diff --git a/modules/rtasks/components/folk-tasks-board.ts b/modules/rtasks/components/folk-tasks-board.ts index 8753529..157b376 100644 --- a/modules/rtasks/components/folk-tasks-board.ts +++ b/modules/rtasks/components/folk-tasks-board.ts @@ -45,6 +45,10 @@ class FolkTasksBoard extends HTMLElement { private _showColumnEditor = false; // Board labels (from server) private _boardLabels: string[] = []; + // Inline workspace creation + private _showCreateWs = false; + // Inline delete confirmation + private _confirmDeleteId: string | null = null; // ClickUp integration state private _cuConnected = false; private _cuTeamName = ''; @@ -434,12 +438,12 @@ class FolkTasksBoard extends HTMLElement { return ''; } - private async createWorkspace() { - const name = prompt("Workspace name:"); + private async createWorkspace(name: string) { if (!name?.trim()) return; if (this.isDemo) { const slug = name.trim().toLowerCase().replace(/\s+/g, "-"); this.workspaces.push({ slug, name: name.trim(), icon: "\u{1F4CB}", task_count: 0, member_count: 1 }); + this._showCreateWs = false; this.render(); return; } @@ -450,7 +454,14 @@ class FolkTasksBoard extends HTMLElement { headers: this.authHeaders({ "Content-Type": "application/json" }), body: JSON.stringify({ name: name.trim() }), }); - if (res.ok) this.loadWorkspaces(); + if (res.ok) { + this._showCreateWs = false; + this.loadWorkspaces(); + } else { + const data = await res.json().catch(() => ({})); + this.error = data.error || `Failed to create workspace (${res.status})`; + this.render(); + } } catch { this.error = "Failed to create workspace"; this.render(); } } @@ -494,7 +505,18 @@ class FolkTasksBoard extends HTMLElement { } catch { this.error = "Failed to update task"; this.render(); } } + private confirmDelete(taskId: string) { + this._confirmDeleteId = taskId; + this.render(); + } + + private cancelDelete() { + this._confirmDeleteId = null; + this.render(); + } + private async deleteTask(taskId: string) { + this._confirmDeleteId = null; if (this.isDemo) { this.tasks = this.tasks.filter(t => t.id !== taskId); if (this.detailTaskId === taskId) this.detailTaskId = null; @@ -866,6 +888,22 @@ class FolkTasksBoard extends HTMLElement { .col-editor__add input { flex: 1; padding: 6px 8px; border-radius: 6px; border: 1px solid var(--rs-border-strong); background: var(--rs-bg-surface-sunken); color: var(--rs-text-primary); font-size: 12px; outline: none; } .col-editor__add button { padding: 6px 14px; border-radius: 6px; border: none; background: var(--rs-primary); color: #fff; cursor: pointer; font-size: 12px; font-weight: 600; } + /* Inline workspace create form */ + .ws-create-form { background: var(--rs-bg-surface); border: 1px solid var(--rs-primary); border-radius: 10px; padding: 14px; margin-bottom: 12px; display: flex; gap: 8px; align-items: center; } + .ws-create-form input { flex: 1; padding: 8px 12px; border-radius: 6px; border: 1px solid var(--rs-border-strong); background: var(--rs-bg-surface-sunken); color: var(--rs-text-primary); font-size: 14px; outline: none; } + .ws-create-form input:focus { border-color: var(--rs-primary-hover); } + .ws-create-form button { padding: 8px 16px; border-radius: 6px; border: none; font-size: 13px; cursor: pointer; font-weight: 600; } + .ws-create-submit { background: var(--rs-primary); color: #fff; } + .ws-create-submit:hover { background: var(--rs-primary-hover); } + .ws-create-cancel { background: transparent; color: var(--rs-text-muted); border: 1px solid var(--rs-border-strong) !important; } + + /* Inline delete confirmation */ + .confirm-delete { display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: #3b1111; border-radius: 6px; margin-top: 6px; } + .confirm-delete span { font-size: 12px; color: #f87171; } + .confirm-delete button { padding: 4px 10px; border-radius: 4px; border: none; font-size: 11px; cursor: pointer; font-weight: 600; } + .confirm-yes { background: #f87171; color: #fff; } + .confirm-no { background: transparent; color: var(--rs-text-muted); border: 1px solid var(--rs-border-strong) !important; } + /* Gear icon */ .gear-btn { padding: 4px 8px; border-radius: 6px; border: 1px solid var(--rs-border-subtle); background: transparent; color: var(--rs-text-muted); cursor: pointer; font-size: 14px; } .gear-btn:hover { color: var(--rs-text-primary); border-color: var(--rs-border-strong); } @@ -912,6 +950,13 @@ class FolkTasksBoard extends HTMLElement { + ${this._showCreateWs ? ` +
+ + + +
+ ` : ''} ${this.renderClickUpPanel()} ${this.workspaces.length > 0 ? `
${this.workspaces.map(ws => ` @@ -1060,6 +1105,7 @@ class FolkTasksBoard extends HTMLElement { return `${fmt}`; })(); const labelFilter = this._filterLabel; + const confirmingDelete = this._confirmDeleteId === task.id; return `
@@ -1072,6 +1118,7 @@ class FolkTasksBoard extends HTMLElement { ${dueDateBadge} ${(task.labels || []).map((l: string) => `${this.esc(l)}`).join("")}
+ ${confirmingDelete ? `
Delete?
` : ''}
`; } @@ -1133,7 +1180,9 @@ class FolkTasksBoard extends HTMLElement { ${task.updated_at ? new Date(task.updated_at).toLocaleString() : new Date(task.updatedAt || Date.now()).toLocaleString()} - + ${this._confirmDeleteId === task.id + ? `
Delete this task?
` + : ``} `; } @@ -1169,7 +1218,25 @@ class FolkTasksBoard extends HTMLElement { this.render(); }); this.shadow.querySelector("#btn-tour")?.addEventListener("click", () => this.startTour()); - this.shadow.getElementById("create-ws")?.addEventListener("click", () => this.createWorkspace()); + this.shadow.getElementById("create-ws")?.addEventListener("click", () => { + this._showCreateWs = !this._showCreateWs; + this.render(); + if (this._showCreateWs) setTimeout(() => this.shadow.getElementById("ws-name-input")?.focus(), 0); + }); + this.shadow.getElementById("ws-create-submit")?.addEventListener("click", () => { + const input = this.shadow.getElementById("ws-name-input") as HTMLInputElement; + if (input?.value?.trim()) this.createWorkspace(input.value.trim()); + }); + this.shadow.getElementById("ws-name-input")?.addEventListener("keydown", (e) => { + if ((e as KeyboardEvent).key === "Enter") { + const input = e.target as HTMLInputElement; + if (input.value?.trim()) this.createWorkspace(input.value.trim()); + } + if ((e as KeyboardEvent).key === "Escape") { this._showCreateWs = false; this.render(); } + }); + this.shadow.getElementById("ws-create-cancel")?.addEventListener("click", () => { + this._showCreateWs = false; this.render(); + }); this.shadow.getElementById("create-task")?.addEventListener("click", () => { this.showCreateForm = !this.showCreateForm; this.render(); @@ -1344,7 +1411,7 @@ class FolkTasksBoard extends HTMLElement { }); // Detail delete this.shadow.getElementById("detail-delete")?.addEventListener("click", () => { - if (this.detailTaskId && confirm("Delete this task?")) this.deleteTask(this.detailTaskId); + if (this.detailTaskId) this.confirmDelete(this.detailTaskId); }); // ── Quick delete on card hover ── @@ -1352,7 +1419,22 @@ class FolkTasksBoard extends HTMLElement { el.addEventListener("click", (e) => { e.stopPropagation(); const taskId = (el as HTMLElement).dataset.quickDelete!; - if (confirm("Delete this task?")) this.deleteTask(taskId); + this.confirmDelete(taskId); + }); + }); + + // ── Inline delete confirmation ── + this.shadow.querySelectorAll("[data-confirm-yes]").forEach(el => { + el.addEventListener("click", (e) => { + e.stopPropagation(); + const taskId = (el as HTMLElement).dataset.confirmYes!; + this.deleteTask(taskId); + }); + }); + this.shadow.querySelectorAll("[data-confirm-no]").forEach(el => { + el.addEventListener("click", (e) => { + e.stopPropagation(); + this.cancelDelete(); }); });