feat: add buttons - open task in dashboard, open dashboard
This commit is contained in:
@@ -60,6 +60,12 @@ describe("parseDashboardInboundMessage", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("parses createTask", () => {
|
||||
expect(parseDashboardInboundMessage({ type: "createTask" })).toEqual(
|
||||
ok({ type: "createTask" } satisfies DashboardInboundMessage),
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects non-objects", () => {
|
||||
const result = parseDashboardInboundMessage(null);
|
||||
expect(result.isErr()).toBe(true);
|
||||
|
||||
@@ -9,7 +9,8 @@ export type DashboardInboundMessage =
|
||||
| { type: "setFilter"; filter: TaskFilter }
|
||||
| { type: "setGroupBy"; groupBy: GroupBy }
|
||||
| { type: "saveDescription"; id: string; description: string }
|
||||
| { type: "refresh" };
|
||||
| { type: "refresh" }
|
||||
| { type: "createTask" };
|
||||
|
||||
const GROUP_BY_VALUES: ReadonlySet<string> = new Set([
|
||||
"none",
|
||||
@@ -44,6 +45,7 @@ export function parseDashboardInboundMessage(
|
||||
switch (type) {
|
||||
case "ready":
|
||||
case "refresh":
|
||||
case "createTask":
|
||||
return ok({ type });
|
||||
|
||||
case "selectTask": {
|
||||
|
||||
@@ -50,16 +50,6 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
tree,
|
||||
onTasksMutated: () => TaskDashboardPanel.refreshIfOpen(),
|
||||
});
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand("xboctFlow.openDashboard", () => {
|
||||
TaskDashboardPanel.show({
|
||||
repo,
|
||||
config,
|
||||
onTasksMutated: () => tree.refresh(),
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { appError, AppErrorVariant } from "@/error";
|
||||
import { TaskLocation } from "@/model/taskLocation";
|
||||
import { TaskStatus } from "@/model/task";
|
||||
import { IConfigProvider, ITaskRepository } from "@/ports";
|
||||
import { TaskDashboardPanel } from "@/views/taskDashboardPanel";
|
||||
import { TaskTreeProvider } from "@/views/taskTreeProvider";
|
||||
import { presentError } from "./presentError";
|
||||
import { resolveTaskRef } from "./resolveTaskRef";
|
||||
@@ -21,6 +22,14 @@ export type TaskCommandDeps = {
|
||||
onTasksMutated?: () => void;
|
||||
};
|
||||
|
||||
function dashboardDeps(deps: TaskCommandDeps) {
|
||||
return {
|
||||
repo: deps.repo,
|
||||
config: deps.config,
|
||||
onTasksMutated: () => deps.tree.refresh(),
|
||||
};
|
||||
}
|
||||
|
||||
function notifyMutated(deps: TaskCommandDeps): void {
|
||||
deps.tree.refresh();
|
||||
deps.onTasksMutated?.();
|
||||
@@ -177,6 +186,25 @@ export async function runChangeStatus(
|
||||
);
|
||||
}
|
||||
|
||||
export async function runOpenInDashboard(
|
||||
deps: TaskCommandDeps,
|
||||
item?: unknown,
|
||||
): Promise<void> {
|
||||
const ref = await resolveTaskRef(deps.repo, deps.config, item);
|
||||
if (ref === undefined) {
|
||||
if (resolveTaskLocations(deps.config).length === 0) {
|
||||
presentError(appError(AppErrorVariant.NO_FOLDER));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
TaskDashboardPanel.show(dashboardDeps(deps), { selectedId: ref.id });
|
||||
}
|
||||
|
||||
export function runOpenDashboard(deps: TaskCommandDeps): void {
|
||||
TaskDashboardPanel.show(dashboardDeps(deps));
|
||||
}
|
||||
|
||||
/** Register all task command adapters on the extension host. */
|
||||
export function registerTaskCommands(
|
||||
context: vscode.ExtensionContext,
|
||||
@@ -198,5 +226,12 @@ export function registerTaskCommands(
|
||||
"xboctFlow.changeStatus",
|
||||
(item?: unknown) => runChangeStatus(deps, item),
|
||||
),
|
||||
vscode.commands.registerCommand("xboctFlow.openDashboard", () =>
|
||||
runOpenDashboard(deps),
|
||||
),
|
||||
vscode.commands.registerCommand(
|
||||
"xboctFlow.openInDashboard",
|
||||
(item?: unknown) => runOpenInDashboard(deps, item),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -199,6 +199,7 @@ export function getTaskDashboardHtml(
|
||||
<option value="status">Status</option>
|
||||
<option value="priority">Priority</option>
|
||||
</select>
|
||||
<button id="createTask" type="button" title="Create task">+ Task</button>
|
||||
<button id="resetFilters" type="button" class="secondary" title="Reset filters">Reset</button>
|
||||
<button id="refresh" type="button" class="secondary" title="Refresh">↻</button>
|
||||
</div>
|
||||
@@ -208,7 +209,7 @@ export function getTaskDashboardHtml(
|
||||
<div id="list"></div>
|
||||
</aside>
|
||||
<main>
|
||||
<div id="empty">Select a task or create one from the sidebar.</div>
|
||||
<div id="empty">Select a task or create one with «+ Task».</div>
|
||||
<div id="detail">
|
||||
<h1 id="detail-title"></h1>
|
||||
<div id="detail-meta"></div>
|
||||
@@ -251,6 +252,7 @@ export function getTaskDashboardHtml(
|
||||
query: document.getElementById("query"),
|
||||
groupBy: document.getElementById("groupBy"),
|
||||
refresh: document.getElementById("refresh"),
|
||||
createTask: document.getElementById("createTask"),
|
||||
resetFilters: document.getElementById("resetFilters"),
|
||||
statusFilters: document.getElementById("status-filters"),
|
||||
priorityFilters: document.getElementById("priority-filters"),
|
||||
@@ -437,6 +439,10 @@ export function getTaskDashboardHtml(
|
||||
post({ type: "refresh" });
|
||||
});
|
||||
|
||||
el.createTask.addEventListener("click", () => {
|
||||
post({ type: "createTask" });
|
||||
});
|
||||
|
||||
el.resetFilters.addEventListener("click", () => {
|
||||
resetFilters();
|
||||
});
|
||||
|
||||
@@ -23,6 +23,11 @@ export type TaskDashboardDeps = {
|
||||
onTasksMutated?: () => void;
|
||||
};
|
||||
|
||||
export type TaskDashboardShowOptions = {
|
||||
/** Pre-select this task after load (e.g. open from tree). */
|
||||
selectedId?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Host adapter for the Task Dashboard webview.
|
||||
* Owns panel lifecycle and message wiring; business rules stay in use-cases.
|
||||
@@ -30,16 +35,23 @@ export type TaskDashboardDeps = {
|
||||
export class TaskDashboardPanel {
|
||||
static #current: TaskDashboardPanel | undefined;
|
||||
|
||||
static show(deps: TaskDashboardDeps): void {
|
||||
static show(
|
||||
deps: TaskDashboardDeps,
|
||||
options: TaskDashboardShowOptions = {},
|
||||
): void {
|
||||
if (TaskDashboardPanel.#current) {
|
||||
TaskDashboardPanel.#current.#panel.reveal(vscode.ViewColumn.One);
|
||||
void TaskDashboardPanel.#current.reloadTasks();
|
||||
const current = TaskDashboardPanel.#current;
|
||||
if (options.selectedId !== undefined) {
|
||||
current.#selectedId = options.selectedId;
|
||||
}
|
||||
current.#panel.reveal(vscode.ViewColumn.One);
|
||||
void current.reloadTasks();
|
||||
return;
|
||||
}
|
||||
|
||||
const panel = vscode.window.createWebviewPanel(
|
||||
"xboctFlow.dashboard",
|
||||
"Task Dashboard",
|
||||
"XBOCT flow",
|
||||
vscode.ViewColumn.One,
|
||||
{
|
||||
enableScripts: true,
|
||||
@@ -47,7 +59,11 @@ export class TaskDashboardPanel {
|
||||
},
|
||||
);
|
||||
|
||||
TaskDashboardPanel.#current = new TaskDashboardPanel(panel, deps);
|
||||
TaskDashboardPanel.#current = new TaskDashboardPanel(
|
||||
panel,
|
||||
deps,
|
||||
options.selectedId,
|
||||
);
|
||||
}
|
||||
|
||||
static refreshIfOpen(): void {
|
||||
@@ -63,9 +79,14 @@ export class TaskDashboardPanel {
|
||||
#selectedId: string | undefined;
|
||||
#disposed = false;
|
||||
|
||||
constructor(panel: vscode.WebviewPanel, deps: TaskDashboardDeps) {
|
||||
constructor(
|
||||
panel: vscode.WebviewPanel,
|
||||
deps: TaskDashboardDeps,
|
||||
selectedId?: string,
|
||||
) {
|
||||
this.#panel = panel;
|
||||
this.#deps = deps;
|
||||
this.#selectedId = selectedId;
|
||||
|
||||
const nonce = createNonce();
|
||||
this.#panel.webview.html = getTaskDashboardHtml(this.#panel.webview, nonce);
|
||||
@@ -140,6 +161,11 @@ export class TaskDashboardPanel {
|
||||
case "saveDescription":
|
||||
await this.#saveDescription(message.id, message.description);
|
||||
return;
|
||||
|
||||
case "createTask":
|
||||
await vscode.commands.executeCommand("xboctFlow.createTask");
|
||||
await this.reloadTasks();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user