@donotdev/cli 0.0.19 → 0.0.21

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.
Files changed (128) hide show
  1. package/README.md +31 -0
  2. package/dependencies-matrix.json +205 -50
  3. package/dist/bin/commands/agent-setup.js +2 -2
  4. package/dist/bin/commands/build.js +6 -6
  5. package/dist/bin/commands/bump.js +495 -70
  6. package/dist/bin/commands/cacheout.js +6 -6
  7. package/dist/bin/commands/coach.js +6 -6
  8. package/dist/bin/commands/create-app.js +24 -16
  9. package/dist/bin/commands/create-project.js +114 -18
  10. package/dist/bin/commands/db.js +142136 -0
  11. package/dist/bin/commands/deploy.js +354 -126
  12. package/dist/bin/commands/dev.js +6 -6
  13. package/dist/bin/commands/doctor.js +140 -33
  14. package/dist/bin/commands/emu.js +6 -6
  15. package/dist/bin/commands/format.js +6 -6
  16. package/dist/bin/commands/get-demo.js +11 -6
  17. package/dist/bin/commands/make-admin.js +14210 -13770
  18. package/dist/bin/commands/preview.js +6 -6
  19. package/dist/bin/commands/seed.js +142426 -0
  20. package/dist/bin/commands/setup-cicd.js +8904 -0
  21. package/dist/bin/commands/setup.js +259 -212
  22. package/dist/bin/commands/staging.js +361 -127
  23. package/dist/bin/commands/sync-secrets.js +55 -33
  24. package/dist/bin/commands/type-check.js +16 -10
  25. package/dist/bin/commands/wai.js +6 -6
  26. package/dist/bin/dndev.js +194 -188
  27. package/dist/bin/donotdev.js +139 -189
  28. package/dist/index.js +468 -144
  29. package/package.json +1 -1
  30. package/templates/app-demo/.env.example +1 -0
  31. package/templates/{root-consumer → app-demo}/entities/ExampleEntity.ts.example +15 -9
  32. package/templates/app-demo/index.html.example +1 -1
  33. package/templates/app-demo/public/apple-touch-icon.png.example +0 -0
  34. package/templates/app-demo/public/favicon.svg.example +1 -0
  35. package/templates/app-demo/public/icon-192x192.png.example +0 -0
  36. package/templates/app-demo/public/icon-512x512.png.example +0 -0
  37. package/templates/app-demo/src/App.tsx.example +3 -1
  38. package/templates/app-demo/src/config/app.ts.example +1 -0
  39. package/templates/app-demo/src/entities/booking.ts.example +75 -0
  40. package/templates/app-demo/src/entities/onboarding.ts.example +160 -0
  41. package/templates/app-demo/src/entities/product.ts.example +12 -0
  42. package/templates/app-demo/src/entities/quote.ts.example +70 -0
  43. package/templates/app-demo/src/pages/ChangelogPage.tsx.example +28 -1
  44. package/templates/app-demo/src/pages/ConditionalFormPage.tsx.example +88 -0
  45. package/templates/app-demo/src/pages/DashboardPage.tsx.example +2 -0
  46. package/templates/app-demo/src/pages/HomePage.tsx.example +355 -2
  47. package/templates/app-demo/src/pages/OnboardingPage.tsx.example +47 -0
  48. package/templates/app-demo/src/pages/PricingPage.tsx.example +28 -1
  49. package/templates/app-demo/src/pages/ProductsPage.tsx.example +2 -0
  50. package/templates/app-demo/src/pages/ProfilePage.tsx.example +2 -0
  51. package/templates/app-demo/src/pages/SettingsPage.tsx.example +2 -0
  52. package/templates/app-demo/src/pages/ShowcaseDetailPage.tsx.example +22 -16
  53. package/templates/app-demo/src/pages/ShowcasePage.tsx.example +3 -1
  54. package/templates/app-demo/src/pages/components/ComponentRenderer.tsx.example +147 -51
  55. package/templates/app-demo/src/pages/components/ComponentsData.tsx.example +103 -21
  56. package/templates/app-demo/src/pages/components/componentConfig.ts.example +139 -59
  57. package/templates/app-demo/src/pages/legal/LegalPage.tsx.example +12 -1
  58. package/templates/app-demo/src/pages/legal/PrivacyPage.tsx.example +10 -1
  59. package/templates/app-demo/src/pages/legal/TermsPage.tsx.example +10 -1
  60. package/templates/app-demo/src/themes.css.example +289 -77
  61. package/templates/app-demo/stats.html.example +4949 -0
  62. package/templates/app-dndev/index.html.example +164 -0
  63. package/templates/app-dndev/public/logo.svg.example +1 -0
  64. package/templates/app-dndev/public/manifest.json.example +10 -0
  65. package/templates/app-dndev/src/App.tsx.example +35 -0
  66. package/templates/app-dndev/src/components/CockpitLayout.css.example +181 -0
  67. package/templates/app-dndev/src/components/CockpitLayout.tsx.example +209 -0
  68. package/templates/app-dndev/src/components/Kanban.css.example +385 -0
  69. package/templates/app-dndev/src/components/ModeToggle.tsx.example +32 -0
  70. package/templates/app-dndev/src/components/OverlaySlot.tsx.example +68 -0
  71. package/templates/app-dndev/src/components/TerminalPanel.css.example +228 -0
  72. package/templates/app-dndev/src/components/TerminalPanel.tsx.example +714 -0
  73. package/templates/app-dndev/src/components/markdown-prose.css.example +49 -0
  74. package/templates/app-dndev/src/components/phases/CaptainLog.tsx.example +107 -0
  75. package/templates/app-dndev/src/components/phases/ContextTabs.tsx.example +352 -0
  76. package/templates/app-dndev/src/components/phases/PhaseCard.tsx.example +126 -0
  77. package/templates/app-dndev/src/components/phases/PhaseDetail.tsx.example +147 -0
  78. package/templates/app-dndev/src/components/phases/ReviewPanel.tsx.example +115 -0
  79. package/templates/app-dndev/src/components/phases/phaseData.ts.example +366 -0
  80. package/templates/app-dndev/src/config/app.ts.example +103 -0
  81. package/templates/app-dndev/src/config/commands.ts.example +171 -0
  82. package/templates/app-dndev/src/config/legal.ts.example +170 -0
  83. package/templates/app-dndev/src/config/providers.ts.example +7 -0
  84. package/templates/app-dndev/src/globals.css.example +10 -0
  85. package/templates/app-dndev/src/hooks/useDndevFile.ts.example +144 -0
  86. package/templates/app-dndev/src/main.tsx.example +21 -0
  87. package/templates/app-dndev/src/pages/BoardPage.tsx.example +640 -0
  88. package/templates/app-dndev/src/pages/GrillPage.tsx.example +658 -0
  89. package/templates/app-dndev/src/pages/HomePage.tsx.example +347 -0
  90. package/templates/app-dndev/src/pages/NotFoundPage.tsx.example +33 -0
  91. package/templates/app-dndev/src/pages/PhasesPage.tsx.example +137 -0
  92. package/templates/app-dndev/src/pages/SettingsPage.tsx.example +64 -0
  93. package/templates/app-dndev/src/pages/legal/LegalNoticePage.tsx.example +75 -0
  94. package/templates/app-dndev/src/pages/legal/PrivacyPage.tsx.example +69 -0
  95. package/templates/app-dndev/src/pages/legal/TermsPage.tsx.example +71 -0
  96. package/templates/app-dndev/src/stores/dndevStore.ts.example +386 -0
  97. package/templates/app-dndev/src/themes.css.example +161 -0
  98. package/templates/app-dndev/terminal-sidecar.cjs.example +341 -0
  99. package/templates/app-dndev/tsconfig.json.example +9 -0
  100. package/templates/app-dndev/vite.config.ts.example +24 -0
  101. package/templates/app-vite/index.html.example +1 -1
  102. package/templates/functions-supabase/supabase/functions/.env.example +0 -2
  103. package/templates/root-consumer/.claude/commands/grill.md.example +86 -8
  104. package/templates/root-consumer/.dndev.secrets.example +32 -0
  105. package/templates/root-consumer/.gitignore.example +3 -0
  106. package/templates/root-consumer/AI.md.example +4 -0
  107. package/templates/root-consumer/entities/index.ts.example +2 -5
  108. package/templates/root-consumer/guides/dndev/COMPONENTS_ATOMIC.md.example +4 -0
  109. package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +23 -20
  110. package/templates/root-consumer/guides/dndev/INDEX.md.example +1 -0
  111. package/templates/root-consumer/guides/dndev/SETUP_BILLING.md.example +3 -7
  112. package/templates/root-consumer/guides/dndev/SETUP_CICD.md.example +115 -0
  113. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +41 -0
  114. package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +13 -18
  115. package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +17 -12
  116. package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +185 -251
  117. package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +26 -8
  118. package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +66 -49
  119. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +6 -5
  120. package/templates/root-consumer/guides/wai-way/blueprints/2_entities.md.example +9 -9
  121. package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +1 -1
  122. package/templates/root-consumer/guides/wai-way/blueprints/4_configure.md.example +7 -6
  123. package/templates/root-consumer/guides/wai-way/context_map.json.example +51 -20
  124. package/templates/root-consumer/guides/wai-way/hld_template.md.example +138 -0
  125. package/templates/root-consumer/guides/wai-way/lld_template.md.example +103 -0
  126. package/templates/root-consumer/guides/wai-way/prd_template.md.example +140 -0
  127. /package/templates/{root-consumer → app-demo}/entities/Contact.ts.example +0 -0
  128. /package/templates/{root-consumer → app-demo}/entities/demo.ts.example +0 -0
@@ -0,0 +1,347 @@
1
+ /**
2
+ * @fileoverview Project Hub — 5-second read, zero noise
3
+ *
4
+ * Section 1: "DoNotDev — WAI-WAY Cockpit"
5
+ * Left column: 5 phase cards (shared PhaseCardList)
6
+ * Right column: Agent status + Grill + Board columns
7
+ *
8
+ * Section 2: Actions grid (all commands)
9
+ */
10
+
11
+ import { useState, useEffect } from 'react';
12
+ import {
13
+ LayoutDashboard,
14
+ Flame,
15
+ Inbox,
16
+ Clock,
17
+ Eye,
18
+ CheckCircle2,
19
+ RefreshCw,
20
+ Terminal,
21
+ } from 'lucide-react';
22
+
23
+ import {
24
+ Badge,
25
+ Card,
26
+ Grid,
27
+ Section,
28
+ Stack,
29
+ Text,
30
+ } from '@donotdev/components';
31
+ import type { PageMeta } from '@donotdev/core';
32
+ import { PageContainer, useNavigate } from '@donotdev/ui';
33
+
34
+ import { useDndevFile } from '../hooks/useDndevFile';
35
+ import { useDoNotDashStore, COLUMNS } from '../stores/dndevStore';
36
+ import { getActions } from '../config/commands';
37
+ import { PhaseCardList } from '../components/phases/PhaseCard';
38
+ import { DEFAULT_PROTOCOL } from '../components/phases/phaseData';
39
+
40
+ import type { ProtocolData } from '../components/phases/phaseData';
41
+ import type { TabMode, CardColumn } from '../stores/dndevStore';
42
+
43
+ export const meta: PageMeta = {
44
+ icon: <LayoutDashboard />,
45
+ title: 'Dashboard',
46
+ };
47
+
48
+ // ============================================================================
49
+ // TYPES
50
+ // ============================================================================
51
+
52
+ interface GrillItem {
53
+ severity?: 'blocker' | 'warn' | 'note';
54
+ status?: string;
55
+ }
56
+
57
+ interface GrillReport {
58
+ generated?: string;
59
+ gitSha?: string;
60
+ verdict?: string;
61
+ target?: string;
62
+ items?: GrillItem[];
63
+ }
64
+
65
+ interface GrillHistoryEntry {
66
+ date?: string;
67
+ gitSha?: string;
68
+ verdict?: string;
69
+ blockers?: number;
70
+ warnings?: number;
71
+ notes?: number;
72
+ total?: number;
73
+ resolved?: number;
74
+ carried?: number;
75
+ }
76
+
77
+ // ============================================================================
78
+ // CONSTANTS
79
+ // ============================================================================
80
+
81
+ const VERDICT_VARIANT: Record<string, 'success' | 'destructive' | 'warning' | 'muted'> = {
82
+ SHIP: 'success',
83
+ READY: 'success',
84
+ 'DO NOT SHIP': 'destructive',
85
+ CONDITIONAL: 'warning',
86
+ };
87
+
88
+ const COLUMN_ICON: Record<CardColumn, typeof Inbox> = {
89
+ backlog: Inbox,
90
+ in_progress: Clock,
91
+ review: Eye,
92
+ done: CheckCircle2,
93
+ };
94
+
95
+ const COLUMN_VARIANT: Record<CardColumn, 'muted' | 'warning' | 'accent' | 'success'> = {
96
+ backlog: 'muted',
97
+ in_progress: 'warning',
98
+ review: 'accent',
99
+ done: 'success',
100
+ };
101
+
102
+ // ============================================================================
103
+ // HELPERS
104
+ // ============================================================================
105
+
106
+ function formatDaysAgo(days: number | null): string {
107
+ if (days === null) return '';
108
+ if (days === 0) return 'Today';
109
+ if (days === 1) return 'Yesterday';
110
+ return `${days}d ago`;
111
+ }
112
+
113
+ // ============================================================================
114
+ // PAGE
115
+ // ============================================================================
116
+
117
+ export default function HomePage() {
118
+ const navigate = useNavigate();
119
+
120
+ // --- Data ---
121
+ const { data: protocol } = useDndevFile<ProtocolData>(
122
+ '.dndev/protocol.json',
123
+ (raw) => raw as ProtocolData,
124
+ { fallback: DEFAULT_PROTOCOL },
125
+ );
126
+ const { data: grill } = useDndevFile<GrillReport>(
127
+ '.dndev/grill-report.json',
128
+ (raw) => raw as GrillReport,
129
+ { fallback: {} },
130
+ );
131
+ const { data: grillHistory } = useDndevFile<GrillHistoryEntry[]>(
132
+ '.dndev/grill-history.json',
133
+ (raw) => raw as GrillHistoryEntry[],
134
+ { fallback: [] },
135
+ );
136
+
137
+ const proto = protocol ?? DEFAULT_PROTOCOL;
138
+ const cards = useDoNotDashStore((s) => s.cards);
139
+ const sessions = useDoNotDashStore((s) => s.sessions);
140
+ const actions = getActions();
141
+
142
+ // --- Git HEAD for staleness detection ---
143
+ const [currentSha, setCurrentSha] = useState<string | null>(null);
144
+ useEffect(() => {
145
+ fetch('/api/dndev/git/head')
146
+ .then((r) => r.json())
147
+ .then((d: { commit?: string }) => setCurrentSha(d.commit ?? null))
148
+ .catch(() => setCurrentSha(null));
149
+ }, []);
150
+
151
+ // --- Grill stats ---
152
+ const grillHasRun = !!grill?.generated;
153
+ const grillItems = grill?.items ?? [];
154
+ const grillVerdict = grill?.verdict ?? null;
155
+ const grillTotal = grillItems.length;
156
+ const grillFixed = grillItems.filter((i) => i.status === 'fixed' || i.status === 'resolved').length;
157
+ const grillPending = grillItems.filter((i) => !i.status || i.status === 'pending').length;
158
+
159
+ // --- Grill staleness ---
160
+ const grillDaysAgo = grillHasRun
161
+ ? Math.floor((Date.now() - new Date(grill!.generated!).getTime()) / 86_400_000)
162
+ : null;
163
+ const grillCodeChanged = grillHasRun && currentSha && grill?.gitSha
164
+ ? currentSha !== grill.gitSha
165
+ : false;
166
+ const grillIsStale = (grillDaysAgo !== null && grillDaysAgo > 7) || grillCodeChanged;
167
+
168
+ // --- Grill history (last 5, newest first) ---
169
+ const history = (grillHistory ?? []).slice(-5).reverse();
170
+
171
+ // --- Board counts ---
172
+ const columnCounts: Record<CardColumn, number> = {
173
+ backlog: 0,
174
+ in_progress: 0,
175
+ review: 0,
176
+ done: 0,
177
+ };
178
+ for (const card of cards) {
179
+ if (card.column in columnCounts) columnCounts[card.column]++;
180
+ }
181
+
182
+ // --- Agent detection ---
183
+ const agentConnected = sessions['ai-cli']?.isConnected ?? false;
184
+
185
+ function runAction(command: string, target: TabMode) {
186
+ useDoNotDashStore.getState().injectPrompt(command, { mode: target });
187
+ }
188
+
189
+ return (
190
+ <PageContainer>
191
+ {/* ================================================================ */}
192
+ {/* SECTION 1: WAI-WAY Cockpit */}
193
+ {/* ================================================================ */}
194
+ <Section title="DoNotDev — WAI-WAY Cockpit" gridCols={[1, 1, 2, 2]} gridGap="medium">
195
+ {/* Left: 5 phase cards */}
196
+ <PhaseCardList
197
+ protocol={proto}
198
+ onSelectPhase={() => navigate('/phases')}
199
+ />
200
+
201
+ {/* Right: Agent status + Grill + Board */}
202
+ <Stack gap="tight">
203
+
204
+ {/* Agent Status */}
205
+ {!agentConnected && (
206
+ <Card variant="warning">
207
+ <Stack direction="row" align="center" gap="tight">
208
+ <Terminal size={16} />
209
+ <Stack gap="none" flex="1">
210
+ <Text level="small" weight="semibold">No AI Agent detected</Text>
211
+ <Text level="caption" variant="muted">
212
+ Start Claude, Gemini, or any CLI agent in the terminal to unlock AI-powered workflows.
213
+ </Text>
214
+ </Stack>
215
+ </Stack>
216
+ </Card>
217
+ )}
218
+
219
+ {/* Grill Card */}
220
+ <Card onClick={() => navigate('/grill')} clickable>
221
+ <Stack gap="tight">
222
+ {/* Header */}
223
+ <Stack direction="row" align="center" justify="between" nowrap>
224
+ <Stack direction="row" align="center" gap="tight">
225
+ <Flame size={16} />
226
+ <Text level="small" weight="semibold">Grill</Text>
227
+ </Stack>
228
+ {grillHasRun && (
229
+ <Badge variant={VERDICT_VARIANT[grillVerdict!] ?? 'muted'}>{grillVerdict}</Badge>
230
+ )}
231
+ </Stack>
232
+
233
+ {grillHasRun ? (
234
+ <Stack gap="tight">
235
+ {/* Current report summary */}
236
+ <Stack direction="row" align="center" gap="tight" wrap="wrap">
237
+ <Text as="span" level="caption" weight="semibold">{grillTotal} items</Text>
238
+ {grillFixed > 0 && (
239
+ <Badge variant="success">
240
+ <CheckCircle2 size={10} /> {grillFixed} fixed
241
+ </Badge>
242
+ )}
243
+ {grillPending > 0 && (
244
+ <Badge variant="muted">{grillPending} pending</Badge>
245
+ )}
246
+ <Text as="span" level="caption" variant="muted">
247
+ · {formatDaysAgo(grillDaysAgo)} · {grill?.gitSha ?? ''}
248
+ </Text>
249
+ </Stack>
250
+
251
+ {/* Staleness nudge */}
252
+ {grillIsStale && (
253
+ <Badge variant="warning">
254
+ <RefreshCw size={10} />
255
+ {grillCodeChanged ? 'Code changed since last grill' : 'Stale — re-grill recommended'}
256
+ </Badge>
257
+ )}
258
+
259
+ {/* History timeline (last 5 runs) */}
260
+ {history.length > 0 && (
261
+ <Stack gap="none">
262
+ {history.map((h, i) => (
263
+ <Stack key={i} direction="row" align="center" gap="tight">
264
+ <Text as="span" level="caption" variant="muted">
265
+ {formatDaysAgo(h.date ? Math.floor((Date.now() - new Date(h.date).getTime()) / 86_400_000) : null)}
266
+ </Text>
267
+ <Text as="span" level="caption" variant="muted">·</Text>
268
+ <Text as="span" level="caption">{h.total ?? 0} items</Text>
269
+ {(h.resolved ?? 0) > 0 && (
270
+ <Badge variant="success">
271
+ <CheckCircle2 size={10} /> {h.resolved} fixed
272
+ </Badge>
273
+ )}
274
+ {(h.carried ?? 0) > 0 && (
275
+ <Text as="span" level="caption" variant="muted">{h.carried} carried</Text>
276
+ )}
277
+ <Text as="span" level="caption" variant="muted">
278
+ · {h.gitSha ?? ''}
279
+ </Text>
280
+ </Stack>
281
+ ))}
282
+ </Stack>
283
+ )}
284
+ </Stack>
285
+ ) : (
286
+ <Text level="caption" variant="muted">
287
+ Staff-engineer code review with security depth. Run /grill to analyze.
288
+ </Text>
289
+ )}
290
+ </Stack>
291
+ </Card>
292
+
293
+ {/* Board — 4 column cards */}
294
+ <Stack gap="tight">
295
+ <Stack direction="row" align="center" gap="tight">
296
+ <Text level="small" weight="semibold">Board</Text>
297
+ <Text as="span" level="caption" variant="muted">
298
+ {cards.length === 0 ? 'No tickets yet' : `${cards.length} total`}
299
+ </Text>
300
+ </Stack>
301
+ <Grid cols={[2, 2, 4, 4]} gap="tight">
302
+ {COLUMNS.map((col) => {
303
+ const Icon = COLUMN_ICON[col.id];
304
+ const count = columnCounts[col.id];
305
+ return (
306
+ <Card
307
+ key={col.id}
308
+ variant={count > 0 ? COLUMN_VARIANT[col.id] : 'muted'}
309
+ onClick={() => navigate('/board')}
310
+ clickable
311
+ >
312
+ <Stack align="center" gap="none">
313
+ <Icon size={16} />
314
+ <Text level="h4" weight="bold">{count}</Text>
315
+ <Text level="caption" variant="muted">{col.label}</Text>
316
+ </Stack>
317
+ </Card>
318
+ );
319
+ })}
320
+ </Grid>
321
+ </Stack>
322
+
323
+ </Stack>
324
+ </Section>
325
+
326
+ {/* ================================================================ */}
327
+ {/* SECTION 2: Actions */}
328
+ {/* ================================================================ */}
329
+ <Section title="Actions" gridCols={[2, 2, 4, 4]} gridGap="medium">
330
+ {actions.map((action) => {
331
+ const Icon = action.icon;
332
+ return (
333
+ <Card key={action.id} onClick={() => runAction(action.command, action.target)} clickable>
334
+ <Stack gap="tight">
335
+ <Stack direction="row" align="center" gap="tight">
336
+ <Icon size={16} />
337
+ <Text level="small" weight="semibold">{action.label}</Text>
338
+ </Stack>
339
+ <Text level="caption" variant="muted">{action.description}</Text>
340
+ </Stack>
341
+ </Card>
342
+ );
343
+ })}
344
+ </Section>
345
+ </PageContainer>
346
+ );
347
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @fileoverview Not found page component
3
+ * @description 404 error page for the application
4
+ * @version 0.0.1
5
+ * @since 0.0.1
6
+ * @author AMBROISE PARK Consulting
7
+ */
8
+
9
+ /**
10
+ * Not found page translation namespace
11
+ *
12
+ * @version 0.0.1
13
+ * @since 0.0.1
14
+ * @author AMBROISE PARK Consulting
15
+ */
16
+ export const NAMESPACE = 'dndev';
17
+
18
+ import type { PageMeta } from '@donotdev/core';
19
+
20
+ /**
21
+ * Not found page metadata configuration
22
+ *
23
+ * @version 0.0.1
24
+ * @since 0.0.1
25
+ * @author AMBROISE PARK Consulting
26
+ */
27
+ export const meta: PageMeta = {
28
+ namespace: NAMESPACE,
29
+ route: '/404',
30
+ hideFromMenu: true,
31
+ };
32
+
33
+ export { NotFoundPage as default } from '@donotdev/ui';
@@ -0,0 +1,137 @@
1
+ /**
2
+ * @fileoverview WAI-WAY Operator Cockpit — phase control center
3
+ *
4
+ * Collapsible "DoNotDev — WAI-WAY Phases" section with shared PhaseCardList.
5
+ * Selected phase opens PhaseDetail + ContextTabs below.
6
+ * ReviewPanel auto-opens on pendingReview. CaptainLog at bottom.
7
+ */
8
+
9
+ import { useEffect, useState } from 'react';
10
+ import { GitBranch, Loader2 } from 'lucide-react';
11
+
12
+ import { Section, Stack } from '@donotdev/components';
13
+ import type { PageMeta } from '@donotdev/core';
14
+ import { PageContainer } from '@donotdev/ui';
15
+
16
+ import { ContextTabs } from '../components/phases/ContextTabs';
17
+ import { PhaseCardList } from '../components/phases/PhaseCard';
18
+ import { PhaseDetail } from '../components/phases/PhaseDetail';
19
+ import { ReviewPanel } from '../components/phases/ReviewPanel';
20
+ import {
21
+ DEFAULT_PROTOCOL,
22
+ parseLessons,
23
+ parseProgress,
24
+ } from '../components/phases/phaseData';
25
+ import { useDndevFile } from '../hooks/useDndevFile';
26
+
27
+ import type {
28
+ CaptainLogData,
29
+ Lesson,
30
+ ProgressSection,
31
+ ProtocolData,
32
+ } from '../components/phases/phaseData';
33
+
34
+ export const meta: PageMeta = {
35
+ icon: <GitBranch />,
36
+ title: 'Phases',
37
+ };
38
+
39
+ export default function PhasesPage() {
40
+ // ---- Data sources (all HMR-reactive) ----
41
+ const { data: protocol, loading: protocolLoading } = useDndevFile<ProtocolData>(
42
+ '.dndev/protocol.json',
43
+ (raw) => raw as ProtocolData,
44
+ { fallback: DEFAULT_PROTOCOL },
45
+ );
46
+ const { data: progressSections, loading: progressLoading, reload: reloadProgress } = useDndevFile<ProgressSection[]>(
47
+ '.dndev/implementation.md',
48
+ (raw) => parseProgress(raw as string),
49
+ { fallback: [] },
50
+ );
51
+ const { data: captainLog } = useDndevFile<CaptainLogData>(
52
+ '.dndev/captain-log.json',
53
+ (raw) => raw as CaptainLogData,
54
+ { fallback: { sessions: [] } },
55
+ );
56
+ const { data: lessons } = useDndevFile<Lesson[]>(
57
+ '.dndev/LESSONS.md',
58
+ (raw) => parseLessons(raw as string),
59
+ { fallback: [] },
60
+ );
61
+
62
+ // ---- UI state ----
63
+ const proto = protocol ?? DEFAULT_PROTOCOL;
64
+ const [selectedPhase, setSelectedPhase] = useState<number>(Math.max(0, proto.currentPhase));
65
+ const [reviewOpen, setReviewOpen] = useState(false);
66
+
67
+ useEffect(() => {
68
+ if (proto.currentPhase >= 0) setSelectedPhase(proto.currentPhase);
69
+ }, [proto.currentPhase]);
70
+
71
+ useEffect(() => {
72
+ if (proto.pendingReview) setReviewOpen(true);
73
+ }, [proto.pendingReview]);
74
+
75
+ // ---- Checkbox toggle ----
76
+ async function handleCheckboxToggle(line: number, currentChecked: boolean) {
77
+ try {
78
+ await fetch('/api/dndev/progress/toggle', {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify({ line, checked: !currentChecked }),
82
+ });
83
+ await reloadProgress();
84
+ } catch {
85
+ // Silent
86
+ }
87
+ }
88
+
89
+ // ---- Loading ----
90
+ if (protocolLoading || progressLoading) {
91
+ return (
92
+ <PageContainer>
93
+ <Section title="DoNotDev — WAI-WAY Phases">
94
+ <Stack align="center">
95
+ <Loader2 size={24} style={{ animation: 'spin 1.5s linear infinite' }} />
96
+ </Stack>
97
+ </Section>
98
+ </PageContainer>
99
+ );
100
+ }
101
+
102
+ return (
103
+ <PageContainer>
104
+ {/* Phase cards (left) + detail (right) */}
105
+ <Section title="DoNotDev — WAI-WAY Phases" gridCols={["1fr", "1fr", "1fr 2fr", "1fr 2fr"]}>
106
+ <PhaseCardList
107
+ protocol={proto}
108
+ progressSections={progressSections ?? []}
109
+ selectedPhase={selectedPhase}
110
+ onSelectPhase={setSelectedPhase}
111
+ />
112
+ <PhaseDetail
113
+ phaseId={selectedPhase}
114
+ protocol={proto}
115
+ progressSections={progressSections ?? []}
116
+ onOpenReview={() => setReviewOpen(true)}
117
+ />
118
+ </Section>
119
+
120
+ {/* Context: progress, specs, lessons, log */}
121
+ <ContextTabs
122
+ protocol={proto}
123
+ progressSections={progressSections ?? []}
124
+ lessonsCount={(lessons ?? []).length}
125
+ captainLog={captainLog ?? { sessions: [] }}
126
+ onCheckboxToggle={handleCheckboxToggle}
127
+ />
128
+
129
+ {/* Review sheet */}
130
+ <ReviewPanel
131
+ protocol={proto}
132
+ open={reviewOpen}
133
+ onOpenChange={setReviewOpen}
134
+ />
135
+ </PageContainer>
136
+ );
137
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @fileoverview Settings page — project configuration and preferences
3
+ *
4
+ * Project info, mode toggle, terminal defaults.
5
+ * Layout: bottom-bar (observer page).
6
+ */
7
+
8
+ import { Settings } from 'lucide-react';
9
+
10
+ import { Stack, Card, Text, Section } from '@donotdev/components';
11
+ import type { PageMeta } from '@donotdev/core';
12
+ import { PageContainer } from '@donotdev/ui';
13
+
14
+ import { useDoNotDashStore } from '../stores/dndevStore';
15
+ import { ModeToggle } from '../components/ModeToggle';
16
+
17
+ export const meta: PageMeta = {
18
+ icon: <Settings />,
19
+ title: 'Settings',
20
+ };
21
+
22
+ export default function SettingsPage() {
23
+ const mode = useDoNotDashStore((s) => s.mode);
24
+
25
+ return (
26
+ <PageContainer>
27
+ <Stack gap="tight">
28
+ <Section title="Project">
29
+ <Card
30
+ title="Mode"
31
+ subtitle={`Currently in ${mode.toUpperCase()} mode`}
32
+ content={<ModeToggle />}
33
+ />
34
+ </Section>
35
+
36
+ <Section title="Terminal">
37
+ <Card
38
+ title="Terminal Defaults"
39
+ subtitle="Terminal connects to PTY sidecar on port 24681"
40
+ content={
41
+ <Stack gap="tight">
42
+ <Text level="caption" variant="muted">
43
+ Default tab: AI CLI | Shell: system default | Font: JetBrains Mono 13px
44
+ </Text>
45
+ </Stack>
46
+ }
47
+ />
48
+ </Section>
49
+
50
+ <Section title="About">
51
+ <Card
52
+ title="DnDev Cockpit"
53
+ subtitle="AI-driven development cockpit"
54
+ content={
55
+ <Text level="caption" variant="muted">
56
+ WAI-WAY protocol orchestration, project health monitoring, code review, task management.
57
+ </Text>
58
+ }
59
+ />
60
+ </Section>
61
+ </Stack>
62
+ </PageContainer>
63
+ );
64
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * @fileoverview Legal Notice Page
3
+ * @description Legal notice page using the reusable template
4
+ *
5
+ * @version 0.0.1
6
+ * @since 0.0.1
7
+ * @author AMBROISE PARK Consulting
8
+ */
9
+
10
+ import { Building } from 'lucide-react';
11
+
12
+ import { type PageMeta } from '@donotdev/core';
13
+ import { LegalNoticeTemplate } from '@donotdev/templates';
14
+ import { PageContainer } from '@donotdev/ui';
15
+
16
+ import { legalConfig } from '../../config/legal';
17
+
18
+ /**
19
+ * Legal Notice page translation namespace
20
+ *
21
+ * @version 0.0.1
22
+ * @since 0.0.1
23
+ * @author AMBROISE PARK Consulting
24
+ */
25
+ export const NAMESPACE = 'legalNotice';
26
+
27
+ /**
28
+ * Legal Notice page metadata configuration
29
+ *
30
+ * @version 0.0.1
31
+ * @since 0.0.1
32
+ * @author AMBROISE PARK Consulting
33
+ */
34
+ export const meta: PageMeta = {
35
+ namespace: NAMESPACE,
36
+ icon: <Building />,
37
+ hideFromMenu: true,
38
+ };
39
+
40
+ /**
41
+ * Legal Notice Page Component
42
+ *
43
+ * @version 0.0.1
44
+ * @since 0.0.1
45
+ * @author AMBROISE PARK Consulting
46
+ *
47
+ * Uses the reusable LegalNoticeTemplate with centralized legal config
48
+ */
49
+ function LegalNoticePage() {
50
+ return (
51
+ <PageContainer>
52
+ <LegalNoticeTemplate
53
+ publisherName={legalConfig.company.name}
54
+ publisherType="company"
55
+ legalStatus={legalConfig.company.legalStatus}
56
+ registrationNumber={legalConfig.company.registrationNumber}
57
+ vatNumber={legalConfig.company.vatNumber}
58
+ shareCapital={legalConfig.company.shareCapital}
59
+ registeredOffice={legalConfig.contact.address}
60
+ email={legalConfig.contact.email}
61
+ phone={legalConfig.contact.phone}
62
+ directorName={legalConfig.director.name}
63
+ directorRole={legalConfig.director.role}
64
+ hostingProvider={legalConfig.hosting.provider}
65
+ hostingAddress={legalConfig.hosting.address}
66
+ hostingContact={legalConfig.hosting.contact}
67
+ sections={legalConfig.sections.legalNotice}
68
+ lastUpdated={legalConfig.lastUpdated?.legalNotice}
69
+ />
70
+ </PageContainer>
71
+ );
72
+ }
73
+
74
+ export default LegalNoticePage;
75
+