@deskwork/studio 0.10.2 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/editorial-skills-catalogue.d.ts +15 -1
- package/dist/lib/editorial-skills-catalogue.d.ts.map +1 -1
- package/dist/lib/editorial-skills-catalogue.js +66 -59
- package/dist/lib/editorial-skills-catalogue.js.map +1 -1
- package/dist/lib/entry-resolver.d.ts +22 -0
- package/dist/lib/entry-resolver.d.ts.map +1 -0
- package/dist/lib/entry-resolver.js +42 -0
- package/dist/lib/entry-resolver.js.map +1 -0
- package/dist/lib/stage-affordances.d.ts +31 -0
- package/dist/lib/stage-affordances.d.ts.map +1 -0
- package/dist/lib/stage-affordances.js +37 -0
- package/dist/lib/stage-affordances.js.map +1 -0
- package/dist/pages/dashboard/affordances.d.ts +41 -0
- package/dist/pages/dashboard/affordances.d.ts.map +1 -0
- package/dist/pages/dashboard/affordances.js +87 -0
- package/dist/pages/dashboard/affordances.js.map +1 -0
- package/dist/pages/dashboard/affordances.ts +95 -0
- package/dist/pages/dashboard/data.d.ts +24 -0
- package/dist/pages/dashboard/data.d.ts.map +1 -0
- package/dist/pages/dashboard/data.js +49 -0
- package/dist/pages/dashboard/data.js.map +1 -0
- package/dist/pages/dashboard/data.ts +56 -0
- package/dist/pages/dashboard/header.d.ts +13 -0
- package/dist/pages/dashboard/header.d.ts.map +1 -0
- package/dist/pages/dashboard/header.js +70 -0
- package/dist/pages/dashboard/header.js.map +1 -0
- package/dist/pages/dashboard/header.ts +80 -0
- package/dist/pages/dashboard/section.d.ts +37 -0
- package/dist/pages/dashboard/section.d.ts.map +1 -0
- package/dist/pages/dashboard/section.js +117 -0
- package/dist/pages/dashboard/section.js.map +1 -0
- package/dist/pages/dashboard/section.ts +132 -0
- package/dist/pages/dashboard.d.ts +30 -21
- package/dist/pages/dashboard.d.ts.map +1 -1
- package/dist/pages/dashboard.js +34 -799
- package/dist/pages/dashboard.js.map +1 -1
- package/dist/pages/dashboard.ts +44 -980
- package/dist/pages/entry-review.d.ts +25 -0
- package/dist/pages/entry-review.d.ts.map +1 -0
- package/dist/pages/entry-review.js +148 -0
- package/dist/pages/entry-review.js.map +1 -0
- package/dist/pages/entry-review.ts +185 -0
- package/dist/pages/help.d.ts +10 -5
- package/dist/pages/help.d.ts.map +1 -1
- package/dist/pages/help.js +113 -99
- package/dist/pages/help.js.map +1 -1
- package/dist/pages/help.ts +113 -99
- package/dist/pages/index.d.ts +13 -1
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +39 -24
- package/dist/pages/index.js.map +1 -1
- package/dist/pages/index.ts +43 -27
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +23 -3
- package/dist/server.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-row affordance helpers for the dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline-redesign Task 34. Buttons are static HTML — they link to
|
|
5
|
+
* the entry's review surface (`/dev/editorial-review/<uuid>`) or
|
|
6
|
+
* carry a `data-copy` payload that the existing studio client copies
|
|
7
|
+
* to the clipboard. No new backend handlers are wired here; the
|
|
8
|
+
* universal-verb skills are the canonical action path.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { html, unsafe, type RawHtml } from '../html.ts';
|
|
12
|
+
import type { Entry, Stage, ReviewState } from '@deskwork/core/schema/entry';
|
|
13
|
+
|
|
14
|
+
const REVIEW_STATE_LABEL: Record<ReviewState, string> = {
|
|
15
|
+
'in-review': 'in review',
|
|
16
|
+
iterating: 'iterating',
|
|
17
|
+
approved: 'approved',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Render the reviewState badge. When the entry has no reviewState
|
|
22
|
+
* (most pre-review stages), render an em-dash placeholder so the row
|
|
23
|
+
* stays grid-aligned with sibling rows that DO carry a badge.
|
|
24
|
+
*/
|
|
25
|
+
export function renderReviewStateBadge(state: ReviewState | undefined): RawHtml {
|
|
26
|
+
if (state === undefined) {
|
|
27
|
+
return unsafe('<span class="er-stamp er-stamp-none" data-review-state="none">—</span>');
|
|
28
|
+
}
|
|
29
|
+
return unsafe(html`<span class="er-stamp er-stamp-${state}" data-review-state="${state}">${REVIEW_STATE_LABEL[state]}</span>`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Iteration count for the entry's current stage. The sidecar's
|
|
34
|
+
* `iterationByStage` records every stage the entry has touched; this
|
|
35
|
+
* surfaces the count for the stage the entry is currently in. Defaults
|
|
36
|
+
* to 0 when the bucket is missing (a brand-new entry on its first tick
|
|
37
|
+
* before any iterate has fired).
|
|
38
|
+
*/
|
|
39
|
+
export function iterationForCurrentStage(entry: Entry): number {
|
|
40
|
+
return entry.iterationByStage[entry.currentStage] ?? 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Build the per-row action strip. Affordances vary by stage:
|
|
45
|
+
*
|
|
46
|
+
* - Linear pipeline stages (Ideas / Planned / Outlining / Drafting /
|
|
47
|
+
* Final): "open →" link to the review surface, plus an "iterate"
|
|
48
|
+
* copy-CLI button when reviewState is `iterating` and an
|
|
49
|
+
* "approve" copy-CLI button when reviewState is `approved`.
|
|
50
|
+
* - Published: "view →" (read-only review surface).
|
|
51
|
+
* - Blocked / Cancelled: "induct →" copy-CLI to bring the entry back.
|
|
52
|
+
*
|
|
53
|
+
* Each button's behavior is parked behind a `data-copy` attribute so
|
|
54
|
+
* the existing studio client (editorial-studio-client.ts) handles
|
|
55
|
+
* clipboard wiring without new server handlers.
|
|
56
|
+
*/
|
|
57
|
+
export function renderRowActions(entry: Entry): RawHtml {
|
|
58
|
+
const buttons: string[] = [];
|
|
59
|
+
const stage = entry.currentStage;
|
|
60
|
+
const reviewLink = `/dev/editorial-review/${entry.uuid}`;
|
|
61
|
+
|
|
62
|
+
if (isLinearActiveStage(stage)) {
|
|
63
|
+
buttons.push(html`<a class="er-btn er-btn-small" href="${reviewLink}"
|
|
64
|
+
title="open the review surface for ${entry.slug}">open →</a>`);
|
|
65
|
+
if (entry.reviewState === 'iterating') {
|
|
66
|
+
buttons.push(html`<button class="er-btn er-btn-small er-btn-primary er-copy-btn" type="button"
|
|
67
|
+
data-copy="/deskwork:iterate ${entry.slug}"
|
|
68
|
+
title="operator clicked Iterate — run the iterate skill in Claude Code">iterate →</button>`);
|
|
69
|
+
}
|
|
70
|
+
if (entry.reviewState === 'approved') {
|
|
71
|
+
buttons.push(html`<button class="er-btn er-btn-small er-btn-approve er-copy-btn" type="button"
|
|
72
|
+
data-copy="/deskwork:approve ${entry.slug}"
|
|
73
|
+
title="approved — graduate to the next stage">approve →</button>`);
|
|
74
|
+
}
|
|
75
|
+
} else if (stage === 'Published') {
|
|
76
|
+
buttons.push(html`<a class="er-btn er-btn-small" href="${reviewLink}"
|
|
77
|
+
title="read-only review surface for the published entry">view →</a>`);
|
|
78
|
+
} else if (stage === 'Blocked' || stage === 'Cancelled') {
|
|
79
|
+
buttons.push(html`<button class="er-btn er-btn-small er-copy-btn" type="button"
|
|
80
|
+
data-copy="/deskwork:induct ${entry.slug}"
|
|
81
|
+
title="bring this entry back into the pipeline">induct →</button>`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return unsafe(`<span class="er-calendar-action">${buttons.join('')}</span>`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isLinearActiveStage(stage: Stage): boolean {
|
|
88
|
+
return (
|
|
89
|
+
stage === 'Ideas' ||
|
|
90
|
+
stage === 'Planned' ||
|
|
91
|
+
stage === 'Outlining' ||
|
|
92
|
+
stage === 'Drafting' ||
|
|
93
|
+
stage === 'Final'
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard data loader. Reads every sidecar under
|
|
3
|
+
* `<projectRoot>/.deskwork/entries/*.json` and groups them by
|
|
4
|
+
* `currentStage` so the renderer can iterate the eight canonical
|
|
5
|
+
* stage sections without re-walking the disk per stage.
|
|
6
|
+
*
|
|
7
|
+
* Pipeline-redesign Task 34. Replaces the legacy
|
|
8
|
+
* `loadDashboardData` (calendar.md + workflow store) with a
|
|
9
|
+
* sidecar-only reader.
|
|
10
|
+
*/
|
|
11
|
+
import type { Entry, Stage } from '@deskwork/core/schema/entry';
|
|
12
|
+
/**
|
|
13
|
+
* The eight canonical stages, in display order. Linear pipeline
|
|
14
|
+
* (Ideas → Published) first, then off-pipeline (Blocked, Cancelled)
|
|
15
|
+
* pinned at the bottom so the visual flow reads top-down through the
|
|
16
|
+
* normal lifecycle.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DASHBOARD_STAGE_ORDER: readonly Stage[];
|
|
19
|
+
export interface DashboardData {
|
|
20
|
+
readonly entries: readonly Entry[];
|
|
21
|
+
readonly byStage: ReadonlyMap<Stage, readonly Entry[]>;
|
|
22
|
+
}
|
|
23
|
+
export declare function loadDashboardData(projectRoot: string): Promise<DashboardData>;
|
|
24
|
+
//# sourceMappingURL=data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../src/pages/dashboard/data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAEhE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,KAAK,EASxC,CAAC;AAEX,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,KAAK,EAAE,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,KAAK,EAAE,SAAS,KAAK,EAAE,CAAC,CAAC;CACxD;AAiBD,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAInF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard data loader. Reads every sidecar under
|
|
3
|
+
* `<projectRoot>/.deskwork/entries/*.json` and groups them by
|
|
4
|
+
* `currentStage` so the renderer can iterate the eight canonical
|
|
5
|
+
* stage sections without re-walking the disk per stage.
|
|
6
|
+
*
|
|
7
|
+
* Pipeline-redesign Task 34. Replaces the legacy
|
|
8
|
+
* `loadDashboardData` (calendar.md + workflow store) with a
|
|
9
|
+
* sidecar-only reader.
|
|
10
|
+
*/
|
|
11
|
+
import { readAllSidecars } from '@deskwork/core/sidecar';
|
|
12
|
+
/**
|
|
13
|
+
* The eight canonical stages, in display order. Linear pipeline
|
|
14
|
+
* (Ideas → Published) first, then off-pipeline (Blocked, Cancelled)
|
|
15
|
+
* pinned at the bottom so the visual flow reads top-down through the
|
|
16
|
+
* normal lifecycle.
|
|
17
|
+
*/
|
|
18
|
+
export const DASHBOARD_STAGE_ORDER = [
|
|
19
|
+
'Ideas',
|
|
20
|
+
'Planned',
|
|
21
|
+
'Outlining',
|
|
22
|
+
'Drafting',
|
|
23
|
+
'Final',
|
|
24
|
+
'Published',
|
|
25
|
+
'Blocked',
|
|
26
|
+
'Cancelled',
|
|
27
|
+
];
|
|
28
|
+
function bucketize(entries) {
|
|
29
|
+
const out = new Map();
|
|
30
|
+
for (const stage of DASHBOARD_STAGE_ORDER)
|
|
31
|
+
out.set(stage, []);
|
|
32
|
+
for (const e of entries) {
|
|
33
|
+
const bucket = out.get(e.currentStage);
|
|
34
|
+
if (bucket !== undefined)
|
|
35
|
+
bucket.push(e);
|
|
36
|
+
}
|
|
37
|
+
// Sort each bucket by slug — hierarchical entries cluster under
|
|
38
|
+
// their ancestor (display-only ordering; storage stays flat).
|
|
39
|
+
for (const bucket of out.values()) {
|
|
40
|
+
bucket.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
export async function loadDashboardData(projectRoot) {
|
|
45
|
+
const entries = await readAllSidecars(projectRoot);
|
|
46
|
+
const byStage = bucketize(entries);
|
|
47
|
+
return { entries, byStage };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../../../src/pages/dashboard/data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAqB;IACrD,OAAO;IACP,SAAS;IACT,WAAW;IACX,UAAU;IACV,OAAO;IACP,WAAW;IACX,SAAS;IACT,WAAW;CACH,CAAC;AAOX,SAAS,SAAS,CAAC,OAAyB;IAC1C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,KAAK,MAAM,KAAK,IAAI,qBAAqB;QAAE,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QACvC,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,gEAAgE;IAChE,8DAA8D;IAC9D,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,WAAmB;IACzD,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard data loader. Reads every sidecar under
|
|
3
|
+
* `<projectRoot>/.deskwork/entries/*.json` and groups them by
|
|
4
|
+
* `currentStage` so the renderer can iterate the eight canonical
|
|
5
|
+
* stage sections without re-walking the disk per stage.
|
|
6
|
+
*
|
|
7
|
+
* Pipeline-redesign Task 34. Replaces the legacy
|
|
8
|
+
* `loadDashboardData` (calendar.md + workflow store) with a
|
|
9
|
+
* sidecar-only reader.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readAllSidecars } from '@deskwork/core/sidecar';
|
|
13
|
+
import type { Entry, Stage } from '@deskwork/core/schema/entry';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The eight canonical stages, in display order. Linear pipeline
|
|
17
|
+
* (Ideas → Published) first, then off-pipeline (Blocked, Cancelled)
|
|
18
|
+
* pinned at the bottom so the visual flow reads top-down through the
|
|
19
|
+
* normal lifecycle.
|
|
20
|
+
*/
|
|
21
|
+
export const DASHBOARD_STAGE_ORDER: readonly Stage[] = [
|
|
22
|
+
'Ideas',
|
|
23
|
+
'Planned',
|
|
24
|
+
'Outlining',
|
|
25
|
+
'Drafting',
|
|
26
|
+
'Final',
|
|
27
|
+
'Published',
|
|
28
|
+
'Blocked',
|
|
29
|
+
'Cancelled',
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
export interface DashboardData {
|
|
33
|
+
readonly entries: readonly Entry[];
|
|
34
|
+
readonly byStage: ReadonlyMap<Stage, readonly Entry[]>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function bucketize(entries: readonly Entry[]): Map<Stage, Entry[]> {
|
|
38
|
+
const out = new Map<Stage, Entry[]>();
|
|
39
|
+
for (const stage of DASHBOARD_STAGE_ORDER) out.set(stage, []);
|
|
40
|
+
for (const e of entries) {
|
|
41
|
+
const bucket = out.get(e.currentStage);
|
|
42
|
+
if (bucket !== undefined) bucket.push(e);
|
|
43
|
+
}
|
|
44
|
+
// Sort each bucket by slug — hierarchical entries cluster under
|
|
45
|
+
// their ancestor (display-only ordering; storage stays flat).
|
|
46
|
+
for (const bucket of out.values()) {
|
|
47
|
+
bucket.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function loadDashboardData(projectRoot: string): Promise<DashboardData> {
|
|
53
|
+
const entries = await readAllSidecars(projectRoot);
|
|
54
|
+
const byStage = bucketize(entries);
|
|
55
|
+
return { entries, byStage };
|
|
56
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard masthead + filter strip.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline-redesign Task 34. The masthead reads from sidecar-derived
|
|
5
|
+
* counts (total entries, in-review entries) instead of the legacy
|
|
6
|
+
* workflow store. The filter strip exposes one chip per stage so
|
|
7
|
+
* operators can collapse to a single section quickly.
|
|
8
|
+
*/
|
|
9
|
+
import type { DashboardData } from './data.ts';
|
|
10
|
+
import { type RawHtml } from '../html.ts';
|
|
11
|
+
export declare function renderHeader(data: DashboardData, projectRoot: string, now: Date): RawHtml;
|
|
12
|
+
export declare function renderFilterStrip(): RawHtml;
|
|
13
|
+
//# sourceMappingURL=header.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../../src/pages/dashboard/header.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAuBxD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,aAAa,EACnB,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,IAAI,GACR,OAAO,CA0BT;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAa3C"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard masthead + filter strip.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline-redesign Task 34. The masthead reads from sidecar-derived
|
|
5
|
+
* counts (total entries, in-review entries) instead of the legacy
|
|
6
|
+
* workflow store. The filter strip exposes one chip per stage so
|
|
7
|
+
* operators can collapse to a single section quickly.
|
|
8
|
+
*/
|
|
9
|
+
import { DASHBOARD_STAGE_ORDER } from "./data.js";
|
|
10
|
+
import { html, unsafe } from "../html.js";
|
|
11
|
+
const MONTH_NAMES = [
|
|
12
|
+
'January', 'February', 'March', 'April', 'May', 'June',
|
|
13
|
+
'July', 'August', 'September', 'October', 'November', 'December',
|
|
14
|
+
];
|
|
15
|
+
function reviewActiveCount(data) {
|
|
16
|
+
let n = 0;
|
|
17
|
+
for (const entry of data.entries) {
|
|
18
|
+
if (entry.reviewState === 'in-review' || entry.reviewState === 'iterating')
|
|
19
|
+
n++;
|
|
20
|
+
}
|
|
21
|
+
return n;
|
|
22
|
+
}
|
|
23
|
+
function approvedCount(data) {
|
|
24
|
+
let n = 0;
|
|
25
|
+
for (const entry of data.entries) {
|
|
26
|
+
if (entry.reviewState === 'approved')
|
|
27
|
+
n++;
|
|
28
|
+
}
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
export function renderHeader(data, projectRoot, now) {
|
|
32
|
+
const volume = '01';
|
|
33
|
+
const issueNum = String(data.entries.length).padStart(2, '0');
|
|
34
|
+
const issueDate = `${now.getDate()} ${MONTH_NAMES[now.getMonth()]} ${now.getFullYear()}`;
|
|
35
|
+
return unsafe(html `
|
|
36
|
+
<header class="er-pagehead er-pagehead--centered">
|
|
37
|
+
<p class="er-pagehead__kicker">
|
|
38
|
+
Vol. ${volume} · № ${issueNum} · Press-check
|
|
39
|
+
</p>
|
|
40
|
+
<h1 class="er-pagehead__title">
|
|
41
|
+
Editorial <em>Studio</em>
|
|
42
|
+
</h1>
|
|
43
|
+
<p class="er-pagehead__deck">
|
|
44
|
+
Project: <code>${projectRoot}</code>
|
|
45
|
+
· <a class="er-link-marginalia" href="/dev/editorial-help">the manual</a>
|
|
46
|
+
</p>
|
|
47
|
+
<p class="er-pagehead__meta">
|
|
48
|
+
<span>${issueDate}</span>
|
|
49
|
+
<span class="sep">·</span>
|
|
50
|
+
<span>${data.entries.length} on the calendar</span>
|
|
51
|
+
<span class="sep">·</span>
|
|
52
|
+
<span>${reviewActiveCount(data)} in review</span>
|
|
53
|
+
<span class="sep">·</span>
|
|
54
|
+
<span>${approvedCount(data)} approved</span>
|
|
55
|
+
</p>
|
|
56
|
+
</header>`);
|
|
57
|
+
}
|
|
58
|
+
export function renderFilterStrip() {
|
|
59
|
+
return unsafe(html `
|
|
60
|
+
<section class="er-filter" data-filter-strip>
|
|
61
|
+
<span class="er-filter-label">Find</span>
|
|
62
|
+
<input type="search" data-filter-input placeholder="slug, title…" autocomplete="off" />
|
|
63
|
+
<span class="er-filter-label er-filter-label--gap">Stage</span>
|
|
64
|
+
<div class="er-chips" role="tablist">
|
|
65
|
+
<button class="er-chip" aria-pressed="true" data-stage-chip="all">all</button>
|
|
66
|
+
${DASHBOARD_STAGE_ORDER.map((s) => unsafe(html `<button class="er-chip" data-stage-chip="${s}">${s.toLowerCase()}</button>`))}
|
|
67
|
+
</div>
|
|
68
|
+
</section>`);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header.js","sourceRoot":"","sources":["../../../src/pages/dashboard/header.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,GAAG;IAClB,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM;IACtD,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU;CACjE,CAAC;AAEF,SAAS,iBAAiB,CAAC,IAAmB;IAC5C,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,WAAW,KAAK,WAAW,IAAI,KAAK,CAAC,WAAW,KAAK,WAAW;YAAE,CAAC,EAAE,CAAC;IAClF,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,aAAa,CAAC,IAAmB;IACxC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,WAAW,KAAK,UAAU;YAAE,CAAC,EAAE,CAAC;IAC5C,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,IAAmB,EACnB,WAAmB,EACnB,GAAS;IAET,MAAM,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;IACzF,OAAO,MAAM,CAAC,IAAI,CAAA;;;aAGP,MAAM,eAAe,QAAQ;;;;;;uBAMnB,WAAW;;;;cAIpB,SAAS;;cAET,IAAI,CAAC,OAAO,CAAC,MAAM;;cAEnB,iBAAiB,CAAC,IAAI,CAAC;;cAEvB,aAAa,CAAC,IAAI,CAAC;;YAErB,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAA;;;;;;;UAOV,qBAAqB,CAAC,GAAG,CACzB,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAA,4CAA4C,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,WAAW,CAAC,CAChG;;eAEM,CAAC,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard masthead + filter strip.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline-redesign Task 34. The masthead reads from sidecar-derived
|
|
5
|
+
* counts (total entries, in-review entries) instead of the legacy
|
|
6
|
+
* workflow store. The filter strip exposes one chip per stage so
|
|
7
|
+
* operators can collapse to a single section quickly.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { DashboardData } from './data.ts';
|
|
11
|
+
import { DASHBOARD_STAGE_ORDER } from './data.ts';
|
|
12
|
+
import { html, unsafe, type RawHtml } from '../html.ts';
|
|
13
|
+
|
|
14
|
+
const MONTH_NAMES = [
|
|
15
|
+
'January', 'February', 'March', 'April', 'May', 'June',
|
|
16
|
+
'July', 'August', 'September', 'October', 'November', 'December',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function reviewActiveCount(data: DashboardData): number {
|
|
20
|
+
let n = 0;
|
|
21
|
+
for (const entry of data.entries) {
|
|
22
|
+
if (entry.reviewState === 'in-review' || entry.reviewState === 'iterating') n++;
|
|
23
|
+
}
|
|
24
|
+
return n;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function approvedCount(data: DashboardData): number {
|
|
28
|
+
let n = 0;
|
|
29
|
+
for (const entry of data.entries) {
|
|
30
|
+
if (entry.reviewState === 'approved') n++;
|
|
31
|
+
}
|
|
32
|
+
return n;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function renderHeader(
|
|
36
|
+
data: DashboardData,
|
|
37
|
+
projectRoot: string,
|
|
38
|
+
now: Date,
|
|
39
|
+
): RawHtml {
|
|
40
|
+
const volume = '01';
|
|
41
|
+
const issueNum = String(data.entries.length).padStart(2, '0');
|
|
42
|
+
const issueDate = `${now.getDate()} ${MONTH_NAMES[now.getMonth()]} ${now.getFullYear()}`;
|
|
43
|
+
return unsafe(html`
|
|
44
|
+
<header class="er-pagehead er-pagehead--centered">
|
|
45
|
+
<p class="er-pagehead__kicker">
|
|
46
|
+
Vol. ${volume} · № ${issueNum} · Press-check
|
|
47
|
+
</p>
|
|
48
|
+
<h1 class="er-pagehead__title">
|
|
49
|
+
Editorial <em>Studio</em>
|
|
50
|
+
</h1>
|
|
51
|
+
<p class="er-pagehead__deck">
|
|
52
|
+
Project: <code>${projectRoot}</code>
|
|
53
|
+
· <a class="er-link-marginalia" href="/dev/editorial-help">the manual</a>
|
|
54
|
+
</p>
|
|
55
|
+
<p class="er-pagehead__meta">
|
|
56
|
+
<span>${issueDate}</span>
|
|
57
|
+
<span class="sep">·</span>
|
|
58
|
+
<span>${data.entries.length} on the calendar</span>
|
|
59
|
+
<span class="sep">·</span>
|
|
60
|
+
<span>${reviewActiveCount(data)} in review</span>
|
|
61
|
+
<span class="sep">·</span>
|
|
62
|
+
<span>${approvedCount(data)} approved</span>
|
|
63
|
+
</p>
|
|
64
|
+
</header>`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function renderFilterStrip(): RawHtml {
|
|
68
|
+
return unsafe(html`
|
|
69
|
+
<section class="er-filter" data-filter-strip>
|
|
70
|
+
<span class="er-filter-label">Find</span>
|
|
71
|
+
<input type="search" data-filter-input placeholder="slug, title…" autocomplete="off" />
|
|
72
|
+
<span class="er-filter-label er-filter-label--gap">Stage</span>
|
|
73
|
+
<div class="er-chips" role="tablist">
|
|
74
|
+
<button class="er-chip" aria-pressed="true" data-stage-chip="all">all</button>
|
|
75
|
+
${DASHBOARD_STAGE_ORDER.map(
|
|
76
|
+
(s) => unsafe(html`<button class="er-chip" data-stage-chip="${s}">${s.toLowerCase()}</button>`),
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
</section>`);
|
|
80
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-stage section renderer.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline-redesign Task 34. Each of the eight stage sections renders
|
|
5
|
+
* with a section heading (stage name + entry count) and either a list
|
|
6
|
+
* of rows or an empty-state placeholder. Per-row HTML carries the
|
|
7
|
+
* sidecar-derived state inline (iteration count + reviewState badge)
|
|
8
|
+
* so an operator can see at a glance where each entry sits without
|
|
9
|
+
* opening it.
|
|
10
|
+
*/
|
|
11
|
+
import { type RawHtml } from '../html.ts';
|
|
12
|
+
import type { Entry, Stage } from '@deskwork/core/schema/entry';
|
|
13
|
+
/**
|
|
14
|
+
* Render one entry as a single dashboard row. Carries inline:
|
|
15
|
+
* - slug (linked to the review surface)
|
|
16
|
+
* - title
|
|
17
|
+
* - iteration count for the entry's currentStage
|
|
18
|
+
* - reviewState badge (or an em-dash placeholder)
|
|
19
|
+
* - updatedAt timestamp
|
|
20
|
+
* - per-stage action buttons
|
|
21
|
+
*/
|
|
22
|
+
export declare function renderRow(entry: Entry, index: number): RawHtml;
|
|
23
|
+
/**
|
|
24
|
+
* Render one full stage section: heading + ornaments + count + rows.
|
|
25
|
+
* Empty stages render the placeholder message — keeping the section
|
|
26
|
+
* visible (rather than collapsing it) so the operator sees that the
|
|
27
|
+
* stage exists and is currently empty.
|
|
28
|
+
*/
|
|
29
|
+
export declare function renderStageSection(stage: Stage, entries: readonly Entry[]): RawHtml;
|
|
30
|
+
/**
|
|
31
|
+
* Render the reserved Distribution placeholder. Stays a separate
|
|
32
|
+
* sibling of the stage sections — distribution records (shortform
|
|
33
|
+
* cross-posts) are tracked under their own model and the dashboard
|
|
34
|
+
* surfaces only a placeholder here until that integration lands.
|
|
35
|
+
*/
|
|
36
|
+
export declare function renderDistributionPlaceholder(): RawHtml;
|
|
37
|
+
//# sourceMappingURL=section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../../../src/pages/dashboard/section.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AA6BhE;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CA8B9D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,KAAK,EAAE,GAAG,OAAO,CAkBnF;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAWvD"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-stage section renderer.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline-redesign Task 34. Each of the eight stage sections renders
|
|
5
|
+
* with a section heading (stage name + entry count) and either a list
|
|
6
|
+
* of rows or an empty-state placeholder. Per-row HTML carries the
|
|
7
|
+
* sidecar-derived state inline (iteration count + reviewState badge)
|
|
8
|
+
* so an operator can see at a glance where each entry sits without
|
|
9
|
+
* opening it.
|
|
10
|
+
*/
|
|
11
|
+
import { html, unsafe } from "../html.js";
|
|
12
|
+
import { iterationForCurrentStage, renderReviewStateBadge, renderRowActions, } from "./affordances.js";
|
|
13
|
+
const STAGE_ORNAMENTS = {
|
|
14
|
+
Ideas: '◇',
|
|
15
|
+
Planned: '§',
|
|
16
|
+
Outlining: '⊹',
|
|
17
|
+
Drafting: '✎',
|
|
18
|
+
Final: '※',
|
|
19
|
+
Published: '✓',
|
|
20
|
+
Blocked: '⊘',
|
|
21
|
+
Cancelled: '✗',
|
|
22
|
+
};
|
|
23
|
+
const STAGE_EMPTY_MESSAGES = {
|
|
24
|
+
Ideas: 'No open ideas. Run /deskwork:add to capture one.',
|
|
25
|
+
Planned: 'Nothing planned. /deskwork:approve <slug> to graduate an idea.',
|
|
26
|
+
Outlining: 'Nothing in outlining.',
|
|
27
|
+
Drafting: 'No posts in drafting.',
|
|
28
|
+
Final: 'Nothing in final review.',
|
|
29
|
+
Published: 'No published posts yet.',
|
|
30
|
+
Blocked: 'Nothing blocked.',
|
|
31
|
+
Cancelled: 'No cancelled entries.',
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Render one entry as a single dashboard row. Carries inline:
|
|
35
|
+
* - slug (linked to the review surface)
|
|
36
|
+
* - title
|
|
37
|
+
* - iteration count for the entry's currentStage
|
|
38
|
+
* - reviewState badge (or an em-dash placeholder)
|
|
39
|
+
* - updatedAt timestamp
|
|
40
|
+
* - per-stage action buttons
|
|
41
|
+
*/
|
|
42
|
+
export function renderRow(entry, index) {
|
|
43
|
+
const iteration = iterationForCurrentStage(entry);
|
|
44
|
+
const reviewLink = `/dev/editorial-review/${entry.uuid}`;
|
|
45
|
+
const search = [entry.slug, entry.title, entry.keywords.join(' ')].join(' ').toLowerCase();
|
|
46
|
+
// Hierarchical entries (slugs containing `/`) get a visual indent
|
|
47
|
+
// marker the CSS layer reads. Storage stays flat; this is display-only.
|
|
48
|
+
const depth = entry.slug.split('/').length - 1;
|
|
49
|
+
const depthAttrs = depth > 0
|
|
50
|
+
? unsafe(html ` data-depth="${depth}" style="--er-row-depth: ${depth}"`)
|
|
51
|
+
: '';
|
|
52
|
+
return unsafe(html `
|
|
53
|
+
<div class="er-calendar-row-wrap" data-row-wrap data-search="${search}"${depthAttrs}>
|
|
54
|
+
<div class="er-calendar-row" data-stage="${entry.currentStage}"
|
|
55
|
+
data-uuid="${entry.uuid}" data-slug="${entry.slug}" data-search="${search}">
|
|
56
|
+
<span class="er-row-num">№ ${String(index + 1).padStart(2, '0')}</span>
|
|
57
|
+
<div class="er-calendar-body">
|
|
58
|
+
<span class="er-row-slug"><a href="${reviewLink}"
|
|
59
|
+
title="open the review surface">${entry.slug}</a></span>
|
|
60
|
+
<span class="er-calendar-title">${entry.title}</span>
|
|
61
|
+
<span class="er-calendar-meta er-calendar-meta-iteration"
|
|
62
|
+
data-iteration="${iteration}">iteration: ${iteration}</span>
|
|
63
|
+
<span class="er-calendar-meta er-calendar-meta-updated"
|
|
64
|
+
title="${entry.updatedAt}">${formatDate(entry.updatedAt)}</span>
|
|
65
|
+
</div>
|
|
66
|
+
<span class="er-calendar-status">${renderReviewStateBadge(entry.reviewState)}</span>
|
|
67
|
+
${renderRowActions(entry)}
|
|
68
|
+
</div>
|
|
69
|
+
</div>`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Render one full stage section: heading + ornaments + count + rows.
|
|
73
|
+
* Empty stages render the placeholder message — keeping the section
|
|
74
|
+
* visible (rather than collapsing it) so the operator sees that the
|
|
75
|
+
* stage exists and is currently empty.
|
|
76
|
+
*/
|
|
77
|
+
export function renderStageSection(stage, entries) {
|
|
78
|
+
const body = entries.length === 0
|
|
79
|
+
? unsafe(html `<div class="er-empty" data-empty-stage="${stage}"
|
|
80
|
+
style="padding: 1rem 0.25rem; font-size: 0.95rem;">
|
|
81
|
+
${STAGE_EMPTY_MESSAGES[stage]}
|
|
82
|
+
</div>`)
|
|
83
|
+
: unsafe(entries.map((e, i) => renderRow(e, i).__raw).join(''));
|
|
84
|
+
return unsafe(html `
|
|
85
|
+
<section class="er-section" id="stage-${stage.toLowerCase()}" data-stage-section="${stage}">
|
|
86
|
+
<h2 class="er-section-head">
|
|
87
|
+
<span>${stage}</span>
|
|
88
|
+
<span class="ornament">${STAGE_ORNAMENTS[stage]}</span>
|
|
89
|
+
<span class="count">№ ${entries.length}</span>
|
|
90
|
+
</h2>
|
|
91
|
+
${body}
|
|
92
|
+
</section>`);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Render the reserved Distribution placeholder. Stays a separate
|
|
96
|
+
* sibling of the stage sections — distribution records (shortform
|
|
97
|
+
* cross-posts) are tracked under their own model and the dashboard
|
|
98
|
+
* surfaces only a placeholder here until that integration lands.
|
|
99
|
+
*/
|
|
100
|
+
export function renderDistributionPlaceholder() {
|
|
101
|
+
return unsafe(html `
|
|
102
|
+
<section class="er-section" id="stage-distribution" data-stage-section="Distribution">
|
|
103
|
+
<h2 class="er-section-head">
|
|
104
|
+
<span>Distribution</span>
|
|
105
|
+
<span class="ornament">⌘</span>
|
|
106
|
+
</h2>
|
|
107
|
+
<div class="er-empty" style="padding: 1rem 0.25rem; font-size: 0.95rem;">
|
|
108
|
+
Reserved for shortform DistributionRecords — separate model.
|
|
109
|
+
</div>
|
|
110
|
+
</section>`);
|
|
111
|
+
}
|
|
112
|
+
function formatDate(iso) {
|
|
113
|
+
// Trim to YYYY-MM-DD for compact display. Full timestamp is on the
|
|
114
|
+
// <span title="...">.
|
|
115
|
+
return iso.slice(0, 10);
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=section.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.js","sourceRoot":"","sources":["../../../src/pages/dashboard/section.ts"],"names":[],"mappings":"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"}
|