@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.
- package/README.md +31 -0
- package/dependencies-matrix.json +205 -50
- package/dist/bin/commands/agent-setup.js +2 -2
- package/dist/bin/commands/build.js +6 -6
- package/dist/bin/commands/bump.js +495 -70
- package/dist/bin/commands/cacheout.js +6 -6
- package/dist/bin/commands/coach.js +6 -6
- package/dist/bin/commands/create-app.js +24 -16
- package/dist/bin/commands/create-project.js +114 -18
- package/dist/bin/commands/db.js +142136 -0
- package/dist/bin/commands/deploy.js +354 -126
- package/dist/bin/commands/dev.js +6 -6
- package/dist/bin/commands/doctor.js +140 -33
- package/dist/bin/commands/emu.js +6 -6
- package/dist/bin/commands/format.js +6 -6
- package/dist/bin/commands/get-demo.js +11 -6
- package/dist/bin/commands/make-admin.js +14210 -13770
- package/dist/bin/commands/preview.js +6 -6
- package/dist/bin/commands/seed.js +142426 -0
- package/dist/bin/commands/setup-cicd.js +8904 -0
- package/dist/bin/commands/setup.js +259 -212
- package/dist/bin/commands/staging.js +361 -127
- package/dist/bin/commands/sync-secrets.js +55 -33
- package/dist/bin/commands/type-check.js +16 -10
- package/dist/bin/commands/wai.js +6 -6
- package/dist/bin/dndev.js +194 -188
- package/dist/bin/donotdev.js +139 -189
- package/dist/index.js +468 -144
- package/package.json +1 -1
- package/templates/app-demo/.env.example +1 -0
- package/templates/{root-consumer → app-demo}/entities/ExampleEntity.ts.example +15 -9
- package/templates/app-demo/index.html.example +1 -1
- package/templates/app-demo/public/apple-touch-icon.png.example +0 -0
- package/templates/app-demo/public/favicon.svg.example +1 -0
- package/templates/app-demo/public/icon-192x192.png.example +0 -0
- package/templates/app-demo/public/icon-512x512.png.example +0 -0
- package/templates/app-demo/src/App.tsx.example +3 -1
- package/templates/app-demo/src/config/app.ts.example +1 -0
- package/templates/app-demo/src/entities/booking.ts.example +75 -0
- package/templates/app-demo/src/entities/onboarding.ts.example +160 -0
- package/templates/app-demo/src/entities/product.ts.example +12 -0
- package/templates/app-demo/src/entities/quote.ts.example +70 -0
- package/templates/app-demo/src/pages/ChangelogPage.tsx.example +28 -1
- package/templates/app-demo/src/pages/ConditionalFormPage.tsx.example +88 -0
- package/templates/app-demo/src/pages/DashboardPage.tsx.example +2 -0
- package/templates/app-demo/src/pages/HomePage.tsx.example +355 -2
- package/templates/app-demo/src/pages/OnboardingPage.tsx.example +47 -0
- package/templates/app-demo/src/pages/PricingPage.tsx.example +28 -1
- package/templates/app-demo/src/pages/ProductsPage.tsx.example +2 -0
- package/templates/app-demo/src/pages/ProfilePage.tsx.example +2 -0
- package/templates/app-demo/src/pages/SettingsPage.tsx.example +2 -0
- package/templates/app-demo/src/pages/ShowcaseDetailPage.tsx.example +22 -16
- package/templates/app-demo/src/pages/ShowcasePage.tsx.example +3 -1
- package/templates/app-demo/src/pages/components/ComponentRenderer.tsx.example +147 -51
- package/templates/app-demo/src/pages/components/ComponentsData.tsx.example +103 -21
- package/templates/app-demo/src/pages/components/componentConfig.ts.example +139 -59
- package/templates/app-demo/src/pages/legal/LegalPage.tsx.example +12 -1
- package/templates/app-demo/src/pages/legal/PrivacyPage.tsx.example +10 -1
- package/templates/app-demo/src/pages/legal/TermsPage.tsx.example +10 -1
- package/templates/app-demo/src/themes.css.example +289 -77
- package/templates/app-demo/stats.html.example +4949 -0
- package/templates/app-dndev/index.html.example +164 -0
- package/templates/app-dndev/public/logo.svg.example +1 -0
- package/templates/app-dndev/public/manifest.json.example +10 -0
- package/templates/app-dndev/src/App.tsx.example +35 -0
- package/templates/app-dndev/src/components/CockpitLayout.css.example +181 -0
- package/templates/app-dndev/src/components/CockpitLayout.tsx.example +209 -0
- package/templates/app-dndev/src/components/Kanban.css.example +385 -0
- package/templates/app-dndev/src/components/ModeToggle.tsx.example +32 -0
- package/templates/app-dndev/src/components/OverlaySlot.tsx.example +68 -0
- package/templates/app-dndev/src/components/TerminalPanel.css.example +228 -0
- package/templates/app-dndev/src/components/TerminalPanel.tsx.example +714 -0
- package/templates/app-dndev/src/components/markdown-prose.css.example +49 -0
- package/templates/app-dndev/src/components/phases/CaptainLog.tsx.example +107 -0
- package/templates/app-dndev/src/components/phases/ContextTabs.tsx.example +352 -0
- package/templates/app-dndev/src/components/phases/PhaseCard.tsx.example +126 -0
- package/templates/app-dndev/src/components/phases/PhaseDetail.tsx.example +147 -0
- package/templates/app-dndev/src/components/phases/ReviewPanel.tsx.example +115 -0
- package/templates/app-dndev/src/components/phases/phaseData.ts.example +366 -0
- package/templates/app-dndev/src/config/app.ts.example +103 -0
- package/templates/app-dndev/src/config/commands.ts.example +171 -0
- package/templates/app-dndev/src/config/legal.ts.example +170 -0
- package/templates/app-dndev/src/config/providers.ts.example +7 -0
- package/templates/app-dndev/src/globals.css.example +10 -0
- package/templates/app-dndev/src/hooks/useDndevFile.ts.example +144 -0
- package/templates/app-dndev/src/main.tsx.example +21 -0
- package/templates/app-dndev/src/pages/BoardPage.tsx.example +640 -0
- package/templates/app-dndev/src/pages/GrillPage.tsx.example +658 -0
- package/templates/app-dndev/src/pages/HomePage.tsx.example +347 -0
- package/templates/app-dndev/src/pages/NotFoundPage.tsx.example +33 -0
- package/templates/app-dndev/src/pages/PhasesPage.tsx.example +137 -0
- package/templates/app-dndev/src/pages/SettingsPage.tsx.example +64 -0
- package/templates/app-dndev/src/pages/legal/LegalNoticePage.tsx.example +75 -0
- package/templates/app-dndev/src/pages/legal/PrivacyPage.tsx.example +69 -0
- package/templates/app-dndev/src/pages/legal/TermsPage.tsx.example +71 -0
- package/templates/app-dndev/src/stores/dndevStore.ts.example +386 -0
- package/templates/app-dndev/src/themes.css.example +161 -0
- package/templates/app-dndev/terminal-sidecar.cjs.example +341 -0
- package/templates/app-dndev/tsconfig.json.example +9 -0
- package/templates/app-dndev/vite.config.ts.example +24 -0
- package/templates/app-vite/index.html.example +1 -1
- package/templates/functions-supabase/supabase/functions/.env.example +0 -2
- package/templates/root-consumer/.claude/commands/grill.md.example +86 -8
- package/templates/root-consumer/.dndev.secrets.example +32 -0
- package/templates/root-consumer/.gitignore.example +3 -0
- package/templates/root-consumer/AI.md.example +4 -0
- package/templates/root-consumer/entities/index.ts.example +2 -5
- package/templates/root-consumer/guides/dndev/COMPONENTS_ATOMIC.md.example +4 -0
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +23 -20
- package/templates/root-consumer/guides/dndev/INDEX.md.example +1 -0
- package/templates/root-consumer/guides/dndev/SETUP_BILLING.md.example +3 -7
- package/templates/root-consumer/guides/dndev/SETUP_CICD.md.example +115 -0
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +41 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +13 -18
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +17 -12
- package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +185 -251
- package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +26 -8
- package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +66 -49
- package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +6 -5
- package/templates/root-consumer/guides/wai-way/blueprints/2_entities.md.example +9 -9
- package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +1 -1
- package/templates/root-consumer/guides/wai-way/blueprints/4_configure.md.example +7 -6
- package/templates/root-consumer/guides/wai-way/context_map.json.example +51 -20
- package/templates/root-consumer/guides/wai-way/hld_template.md.example +138 -0
- package/templates/root-consumer/guides/wai-way/lld_template.md.example +103 -0
- package/templates/root-consumer/guides/wai-way/prd_template.md.example +140 -0
- /package/templates/{root-consumer → app-demo}/entities/Contact.ts.example +0 -0
- /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
|
+
|