@builderos/create-agent-os 0.0.2
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 +39 -0
- package/bin/cli.js +133 -0
- package/package.json +40 -0
- package/src/template/App.tsx +68 -0
- package/src/template/agent-os/commands/create-tasks/1-get-spec-requirements.md +19 -0
- package/src/template/agent-os/commands/create-tasks/2-create-tasks-list.md +234 -0
- package/src/template/agent-os/commands/create-tasks/create-tasks.md +254 -0
- package/src/template/agent-os/commands/design-screen/design-screen.md +32 -0
- package/src/template/agent-os/commands/design-shell/design-shell.md +34 -0
- package/src/template/agent-os/commands/design-tokens/design-tokens.md +36 -0
- package/src/template/agent-os/commands/export-product/export-product.md +44 -0
- package/src/template/agent-os/commands/implement-tasks/1-determine-tasks.md +13 -0
- package/src/template/agent-os/commands/implement-tasks/2-implement-tasks.md +63 -0
- package/src/template/agent-os/commands/implement-tasks/3-verify-implementation.md +113 -0
- package/src/template/agent-os/commands/implement-tasks/implement-tasks.md +207 -0
- package/src/template/agent-os/commands/initialize-design/initialize-design.md +54 -0
- package/src/template/agent-os/commands/orchestrate-tasks/orchestrate-tasks.md +180 -0
- package/src/template/agent-os/commands/plan-product/1-product-concept.md +53 -0
- package/src/template/agent-os/commands/plan-product/2-create-mission.md +78 -0
- package/src/template/agent-os/commands/plan-product/3-create-roadmap.md +73 -0
- package/src/template/agent-os/commands/plan-product/4-create-tech-stack.md +46 -0
- package/src/template/agent-os/commands/plan-product/plan-product.md +241 -0
- package/src/template/agent-os/commands/sample-data/sample-data.md +51 -0
- package/src/template/agent-os/commands/scaffold-implementation/scaffold-implementation.md +36 -0
- package/src/template/agent-os/commands/screenshot-design/screenshot-design.md +21 -0
- package/src/template/agent-os/commands/shape-spec/1-initialize-spec.md +95 -0
- package/src/template/agent-os/commands/shape-spec/2-shape-spec.md +300 -0
- package/src/template/agent-os/commands/shape-spec/shape-spec.md +40 -0
- package/src/template/agent-os/commands/write-spec/write-spec.md +134 -0
- package/src/template/agent-os/config.yml +13 -0
- package/src/template/agent-os/product/mission.md +29 -0
- package/src/template/agent-os/product/roadmap.md +9 -0
- package/src/template/agent-os/product/tech-stack.md +14 -0
- package/src/template/agent-os/specs/README.md +1 -0
- package/src/template/agent-os/standards/backend/api.md +10 -0
- package/src/template/agent-os/standards/backend/migrations.md +9 -0
- package/src/template/agent-os/standards/backend/models.md +10 -0
- package/src/template/agent-os/standards/backend/queries.md +9 -0
- package/src/template/agent-os/standards/frontend/accessibility.md +10 -0
- package/src/template/agent-os/standards/frontend/components.md +11 -0
- package/src/template/agent-os/standards/frontend/css.md +7 -0
- package/src/template/agent-os/standards/frontend/responsive.md +11 -0
- package/src/template/agent-os/standards/global/coding-style.md +10 -0
- package/src/template/agent-os/standards/global/commenting.md +5 -0
- package/src/template/agent-os/standards/global/conventions.md +11 -0
- package/src/template/agent-os/standards/global/error-handling.md +9 -0
- package/src/template/agent-os/standards/global/tech-stack.md +31 -0
- package/src/template/agent-os/standards/global/validation.md +11 -0
- package/src/template/agent-os/standards/testing/test-writing.md +9 -0
- package/src/template/agent-os-ui/README.md +73 -0
- package/src/template/agent-os-ui/package-lock.json +5028 -0
- package/src/template/agent-os-ui/package.json +52 -0
- package/src/template/agent-os-ui/postcss.config.js +6 -0
- package/src/template/agent-os-ui/src/components/AgentShell.tsx +31 -0
- package/src/template/agent-os-ui/src/components/AgentSidebar.tsx +65 -0
- package/src/template/agent-os-ui/src/components/GuidanceCard.tsx +75 -0
- package/src/template/agent-os-ui/src/components/MarkdownViewer.tsx +25 -0
- package/src/template/agent-os-ui/src/components/PromptButton.tsx +28 -0
- package/src/template/agent-os-ui/src/components/StatusItem.tsx +45 -0
- package/src/template/agent-os-ui/src/components/ThemeToggle.tsx +72 -0
- package/src/template/agent-os-ui/src/index.ts +11 -0
- package/src/template/agent-os-ui/src/style.css +3 -0
- package/src/template/agent-os-ui/tailwind.config.js +50 -0
- package/src/template/agent-os-ui/tsconfig.json +33 -0
- package/src/template/agent-os-ui/vite.config.ts +32 -0
- package/src/template/control-center/backend/backend.log +2 -0
- package/src/template/control-center/backend/index.js +228 -0
- package/src/template/control-center/backend/package-lock.json +951 -0
- package/src/template/control-center/backend/package.json +19 -0
- package/src/template/control-center/frontend/README.md +73 -0
- package/src/template/control-center/frontend/eslint.config.js +23 -0
- package/src/template/control-center/frontend/index.html +21 -0
- package/src/template/control-center/frontend/package-lock.json +5752 -0
- package/src/template/control-center/frontend/package.json +42 -0
- package/src/template/control-center/frontend/public/runtime-config.json +11 -0
- package/src/template/control-center/frontend/public/vite.svg +1 -0
- package/src/template/control-center/frontend/src/App.css +42 -0
- package/src/template/control-center/frontend/src/App.tsx +738 -0
- package/src/template/control-center/frontend/src/assets/react.svg +1 -0
- package/src/template/control-center/frontend/src/components/ThemeToggle.tsx +64 -0
- package/src/template/control-center/frontend/src/components/ui/ToastContext.tsx +81 -0
- package/src/template/control-center/frontend/src/index.css +194 -0
- package/src/template/control-center/frontend/src/main.tsx +14 -0
- package/src/template/control-center/frontend/src/vite-env.d.ts +1 -0
- package/src/template/control-center/frontend/tsconfig.app.json +28 -0
- package/src/template/control-center/frontend/tsconfig.json +7 -0
- package/src/template/control-center/frontend/tsconfig.node.json +26 -0
- package/src/template/control-center/frontend/vite.config.ts +22 -0
- package/src/template/design/.claude/commands/design-os/data-model.md +122 -0
- package/src/template/design/.claude/commands/design-os/design-screen.md +309 -0
- package/src/template/design/.claude/commands/design-os/design-shell.md +238 -0
- package/src/template/design/.claude/commands/design-os/design-tokens.md +166 -0
- package/src/template/design/.claude/commands/design-os/export-product.md +1105 -0
- package/src/template/design/.claude/commands/design-os/product-roadmap.md +121 -0
- package/src/template/design/.claude/commands/design-os/product-vision.md +99 -0
- package/src/template/design/.claude/commands/design-os/sample-data.md +263 -0
- package/src/template/design/.claude/commands/design-os/screenshot-design.md +112 -0
- package/src/template/design/.claude/commands/design-os/shape-section.md +138 -0
- package/src/template/design/.claude/skills/frontend-design/SKILL.md +42 -0
- package/src/template/design/.github/CODE_OF_CONDUCT.md +5 -0
- package/src/template/design/.github/CONTRIBUTING.md +51 -0
- package/src/template/design/.github/ISSUE_TEMPLATE/config.yml +22 -0
- package/src/template/design/.github/PULL_REQUEST_TEMPLATE.md +20 -0
- package/src/template/design/.github/SECURITY.yml +5 -0
- package/src/template/design/.github/SUPPORT.md +19 -0
- package/src/template/design/.github/workflows/pr-decline.yml +135 -0
- package/src/template/design/.github/workflows/stale.yml +25 -0
- package/src/template/design/CHANGELOG.md +13 -0
- package/src/template/design/LICENSE +21 -0
- package/src/template/design/README.md +54 -0
- package/src/template/design/agents.md +218 -0
- package/src/template/design/claude.md +1 -0
- package/src/template/design/components.json +22 -0
- package/src/template/design/docs/codebase-implementation.md +153 -0
- package/src/template/design/docs/design-section.md +135 -0
- package/src/template/design/docs/export.md +149 -0
- package/src/template/design/docs/getting-started.md +59 -0
- package/src/template/design/docs/index.md +56 -0
- package/src/template/design/docs/product-planning.md +113 -0
- package/src/template/design/docs/requirements.md +22 -0
- package/src/template/design/docs/usage.md +62 -0
- package/src/template/design/eslint.config.js +23 -0
- package/src/template/design/index.html +21 -0
- package/src/template/design/package-lock.json +5473 -0
- package/src/template/design/package.json +47 -0
- package/src/template/design/product-plan.zip +0 -0
- package/src/template/design/public/vite.svg +1 -0
- package/src/template/design/src/assets/react.svg +1 -0
- package/src/template/design/src/components/AppLayout.tsx +95 -0
- package/src/template/design/src/components/DataCard.tsx +139 -0
- package/src/template/design/src/components/DataModelPage.tsx +120 -0
- package/src/template/design/src/components/DesignPage.tsx +284 -0
- package/src/template/design/src/components/EmptyState.tsx +155 -0
- package/src/template/design/src/components/ExportPage.tsx +344 -0
- package/src/template/design/src/components/NextPhaseButton.tsx +33 -0
- package/src/template/design/src/components/PhaseNav.tsx +152 -0
- package/src/template/design/src/components/PhaseWarningBanner.tsx +81 -0
- package/src/template/design/src/components/ProductOverviewCard.tsx +102 -0
- package/src/template/design/src/components/ProductPage.tsx +97 -0
- package/src/template/design/src/components/ScreenDesignPage.tsx +370 -0
- package/src/template/design/src/components/ScreenDesignsCard.tsx +49 -0
- package/src/template/design/src/components/SectionPage.tsx +256 -0
- package/src/template/design/src/components/SectionsCard.tsx +47 -0
- package/src/template/design/src/components/SectionsPage.tsx +181 -0
- package/src/template/design/src/components/ShellCard.tsx +85 -0
- package/src/template/design/src/components/ShellDesignPage.tsx +242 -0
- package/src/template/design/src/components/SpecCard.tsx +121 -0
- package/src/template/design/src/components/StepIndicator.tsx +75 -0
- package/src/template/design/src/components/ThemeToggle.tsx +86 -0
- package/src/template/design/src/components/ui/ToastContext.tsx +81 -0
- package/src/template/design/src/components/ui/avatar.tsx +53 -0
- package/src/template/design/src/components/ui/badge.tsx +46 -0
- package/src/template/design/src/components/ui/button.tsx +60 -0
- package/src/template/design/src/components/ui/card.tsx +92 -0
- package/src/template/design/src/components/ui/collapsible.tsx +48 -0
- package/src/template/design/src/components/ui/dialog.tsx +143 -0
- package/src/template/design/src/components/ui/dropdown-menu.tsx +255 -0
- package/src/template/design/src/components/ui/input.tsx +21 -0
- package/src/template/design/src/components/ui/label.tsx +22 -0
- package/src/template/design/src/components/ui/progress.tsx +24 -0
- package/src/template/design/src/components/ui/scroll-area.tsx +18 -0
- package/src/template/design/src/components/ui/select.tsx +67 -0
- package/src/template/design/src/components/ui/separator.tsx +28 -0
- package/src/template/design/src/components/ui/sheet.tsx +137 -0
- package/src/template/design/src/components/ui/skeleton.tsx +13 -0
- package/src/template/design/src/components/ui/switch.tsx +46 -0
- package/src/template/design/src/components/ui/table.tsx +116 -0
- package/src/template/design/src/components/ui/tabs.tsx +64 -0
- package/src/template/design/src/index.css +284 -0
- package/src/template/design/src/lib/data-model-loader.ts +91 -0
- package/src/template/design/src/lib/design-system-loader.ts +101 -0
- package/src/template/design/src/lib/product-loader.ts +221 -0
- package/src/template/design/src/lib/router.tsx +52 -0
- package/src/template/design/src/lib/section-loader.ts +272 -0
- package/src/template/design/src/lib/shell-loader.ts +175 -0
- package/src/template/design/src/lib/utils.ts +6 -0
- package/src/template/design/src/main.tsx +15 -0
- package/src/template/design/src/sections/.gitkeep +0 -0
- package/src/template/design/src/sections/ai-orchestration-engine-oai/OrchestrationEngine.tsx +348 -0
- package/src/template/design/src/sections/core-platform-shell/AppShell.tsx +403 -0
- package/src/template/design/src/sections/gemini-live-integration/GeminiIntegration.tsx +332 -0
- package/src/template/design/src/sections/interactive-2d-canvas/WhiteboardCanvas.tsx +334 -0
- package/src/template/design/src/sections/participation-equity-tracker/EquityTracker.tsx +383 -0
- package/src/template/design/src/sections/persistent-memory-system/PersistentMemory.tsx +308 -0
- package/src/template/design/src/sections/real-time-communication-layer/VideoSession.tsx +342 -0
- package/src/template/design/src/sections/visual-intelligence-agents/VisualAgents.tsx +311 -0
- package/src/template/design/src/types/product.ts +97 -0
- package/src/template/design/src/types/section.ts +33 -0
- package/src/template/design/tsconfig.app.json +34 -0
- package/src/template/design/tsconfig.json +13 -0
- package/src/template/design/tsconfig.node.json +26 -0
- package/src/template/design/vite.config.ts +18 -0
- package/src/template/package.json +27 -0
- package/src/template/vite.config.ts +16 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { Check, AlertTriangle, FileText, FolderTree, ChevronDown, Download, Package, Copy } from 'lucide-react'
|
|
3
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
4
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
|
5
|
+
import { AppLayout } from '@/components/AppLayout'
|
|
6
|
+
import { loadProductData, hasExportZip, getExportZipUrl } from '@/lib/product-loader'
|
|
7
|
+
import { getAllSectionIds, getSectionScreenDesigns } from '@/lib/section-loader'
|
|
8
|
+
import { useToast } from '@/components/ui/ToastContext'
|
|
9
|
+
|
|
10
|
+
export function ExportPage() {
|
|
11
|
+
const productData = useMemo(() => loadProductData(), [])
|
|
12
|
+
|
|
13
|
+
// Get section stats
|
|
14
|
+
const sectionStats = useMemo(() => {
|
|
15
|
+
const allSectionIds = getAllSectionIds()
|
|
16
|
+
const sectionCount = productData.roadmap?.sections.length || 0
|
|
17
|
+
const sectionsWithScreenDesigns = allSectionIds.filter(id => {
|
|
18
|
+
const screenDesigns = getSectionScreenDesigns(id)
|
|
19
|
+
return screenDesigns.length > 0
|
|
20
|
+
}).length
|
|
21
|
+
return { sectionCount, sectionsWithScreenDesigns, allSectionIds }
|
|
22
|
+
}, [productData.roadmap])
|
|
23
|
+
|
|
24
|
+
const hasOverview = !!productData.overview
|
|
25
|
+
const hasRoadmap = !!productData.roadmap
|
|
26
|
+
const hasDataModel = !!productData.dataModel
|
|
27
|
+
const hasDesignSystem = !!productData.designSystem
|
|
28
|
+
const hasShell = !!productData.shell
|
|
29
|
+
const hasSections = sectionStats.sectionsWithScreenDesigns > 0
|
|
30
|
+
|
|
31
|
+
const requiredComplete = hasOverview && hasRoadmap && hasSections
|
|
32
|
+
|
|
33
|
+
// Check for export zip
|
|
34
|
+
const exportZipAvailable = hasExportZip()
|
|
35
|
+
const exportZipUrl = getExportZipUrl()
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<AppLayout>
|
|
39
|
+
<div className="space-y-6">
|
|
40
|
+
{/* Page intro */}
|
|
41
|
+
<div className="mb-8">
|
|
42
|
+
<h1 className="text-2xl font-semibold text-stone-900 dark:text-stone-100 mb-2">
|
|
43
|
+
{exportZipAvailable ? 'Ready for implementation!' : 'Export'}
|
|
44
|
+
</h1>
|
|
45
|
+
<p className="text-stone-600 dark:text-stone-400">
|
|
46
|
+
{exportZipAvailable
|
|
47
|
+
? 'Download your product design package and implement it in your codebase using the provided handoff prompts and assets.'
|
|
48
|
+
: 'Generate a complete handoff package for your development team.'}
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{/* Status - only show if zip not available */}
|
|
53
|
+
{!exportZipAvailable && (
|
|
54
|
+
<Card className="border-stone-200 dark:border-stone-700 shadow-sm">
|
|
55
|
+
<CardHeader>
|
|
56
|
+
<CardTitle className="text-lg font-semibold text-stone-900 dark:text-stone-100 flex items-center gap-2">
|
|
57
|
+
{requiredComplete ? (
|
|
58
|
+
<>
|
|
59
|
+
<div className="w-6 h-6 rounded-full bg-lime-100 dark:bg-lime-900/30 flex items-center justify-center">
|
|
60
|
+
<Check className="w-4 h-4 text-lime-600 dark:text-lime-400" strokeWidth={2.5} />
|
|
61
|
+
</div>
|
|
62
|
+
Ready to Export
|
|
63
|
+
</>
|
|
64
|
+
) : (
|
|
65
|
+
<>
|
|
66
|
+
<div className="w-6 h-6 rounded-full bg-amber-100 dark:bg-amber-900/30 flex items-center justify-center">
|
|
67
|
+
<AlertTriangle className="w-4 h-4 text-amber-600 dark:text-amber-400" strokeWidth={2.5} />
|
|
68
|
+
</div>
|
|
69
|
+
Not Ready
|
|
70
|
+
</>
|
|
71
|
+
)}
|
|
72
|
+
</CardTitle>
|
|
73
|
+
</CardHeader>
|
|
74
|
+
<CardContent>
|
|
75
|
+
<div className="space-y-1">
|
|
76
|
+
<ChecklistItem label="Product Overview" isComplete={hasOverview} />
|
|
77
|
+
<ChecklistItem label="Product Roadmap" isComplete={hasRoadmap} />
|
|
78
|
+
<ChecklistItem label="Data Model" isComplete={hasDataModel} />
|
|
79
|
+
<ChecklistItem label="Design System" isComplete={hasDesignSystem} />
|
|
80
|
+
<ChecklistItem label="Application Shell" isComplete={hasShell} />
|
|
81
|
+
<ChecklistItem
|
|
82
|
+
label={`Sections with screen designs (${sectionStats.sectionsWithScreenDesigns}/${sectionStats.sectionCount})`}
|
|
83
|
+
isComplete={hasSections}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
</CardContent>
|
|
87
|
+
</Card>
|
|
88
|
+
)}
|
|
89
|
+
|
|
90
|
+
{/* Export command */}
|
|
91
|
+
{requiredComplete && (
|
|
92
|
+
<Card className="border-stone-200 dark:border-stone-700 shadow-sm">
|
|
93
|
+
<CardHeader>
|
|
94
|
+
<CardTitle className="text-lg font-semibold text-stone-900 dark:text-stone-100 flex items-center gap-2">
|
|
95
|
+
{exportZipAvailable ? (
|
|
96
|
+
<>
|
|
97
|
+
<div className="w-6 h-6 rounded-full bg-lime-100 dark:bg-lime-900/30 flex items-center justify-center">
|
|
98
|
+
<Check className="w-4 h-4 text-lime-600 dark:text-lime-400" strokeWidth={2.5} />
|
|
99
|
+
</div>
|
|
100
|
+
Export Package is Ready
|
|
101
|
+
</>
|
|
102
|
+
) : (
|
|
103
|
+
'Generate Export Package'
|
|
104
|
+
)}
|
|
105
|
+
</CardTitle>
|
|
106
|
+
</CardHeader>
|
|
107
|
+
<CardContent className="space-y-6">
|
|
108
|
+
{exportZipAvailable && exportZipUrl ? (
|
|
109
|
+
<div className="space-y-4">
|
|
110
|
+
<div className="flex items-center gap-3 p-4 bg-lime-50 dark:bg-lime-900/20 rounded-lg border border-lime-200 dark:border-lime-800">
|
|
111
|
+
<div className="w-10 h-10 rounded-full bg-lime-100 dark:bg-lime-900/40 flex items-center justify-center shrink-0">
|
|
112
|
+
<Package className="w-5 h-5 text-lime-600 dark:text-lime-400" strokeWidth={1.5} />
|
|
113
|
+
</div>
|
|
114
|
+
<div className="flex-1 min-w-0">
|
|
115
|
+
<p className="font-medium text-stone-900 dark:text-stone-100">
|
|
116
|
+
Synced to Agent OS & Ready for Download
|
|
117
|
+
</p>
|
|
118
|
+
<p className="text-sm text-stone-500 dark:text-stone-400">
|
|
119
|
+
Files handover complete. You can also download the zip backup.
|
|
120
|
+
</p>
|
|
121
|
+
</div>
|
|
122
|
+
<a
|
|
123
|
+
href={exportZipUrl}
|
|
124
|
+
download="product-plan.zip"
|
|
125
|
+
className="inline-flex items-center gap-2 px-4 py-2 bg-white hover:bg-stone-50 border border-stone-200 text-stone-700 font-medium text-sm rounded-md transition-colors shrink-0"
|
|
126
|
+
>
|
|
127
|
+
<Download className="w-4 h-4" strokeWidth={2} />
|
|
128
|
+
Backup
|
|
129
|
+
</a>
|
|
130
|
+
</div>
|
|
131
|
+
<p className="text-sm text-stone-500 dark:text-stone-400">
|
|
132
|
+
To regenerate and re-sync, run <code className="font-mono text-stone-700 dark:text-stone-300">/export-product</code> again.
|
|
133
|
+
</p>
|
|
134
|
+
</div>
|
|
135
|
+
) : (
|
|
136
|
+
<div className="space-y-4">
|
|
137
|
+
<p className="text-stone-600 dark:text-stone-400">
|
|
138
|
+
Run the following command to generate the export package and <strong>automatically sync</strong> it to the Agent OS (app/agent-os):
|
|
139
|
+
</p>
|
|
140
|
+
<PromptButton
|
|
141
|
+
command="/export-product"
|
|
142
|
+
prompt="Antigravity, generate the export package and sync it. Read 'agent-os/commands/export-product/export-product.md'."
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
{/* What's included */}
|
|
148
|
+
<div className="pt-4 border-t border-stone-200 dark:border-stone-700">
|
|
149
|
+
<h4 className="text-sm font-medium text-stone-500 dark:text-stone-400 uppercase tracking-wide mb-4 flex items-center gap-2">
|
|
150
|
+
<FolderTree className="w-4 h-4" strokeWidth={1.5} />
|
|
151
|
+
What's Included
|
|
152
|
+
</h4>
|
|
153
|
+
<div className="grid sm:grid-cols-2 gap-4">
|
|
154
|
+
<ExportItem
|
|
155
|
+
title="Ready-to-Use Prompts"
|
|
156
|
+
description="Pre-written prompts to copy/paste into your coding agent."
|
|
157
|
+
items={['one-shot-prompt.md', 'section-prompt.md']}
|
|
158
|
+
/>
|
|
159
|
+
<ExportItem
|
|
160
|
+
title="Instructions"
|
|
161
|
+
description="Detailed implementation guides for your coding agent."
|
|
162
|
+
items={['product-overview.md', 'one-shot-instructions.md', 'incremental/ (milestones)']}
|
|
163
|
+
/>
|
|
164
|
+
<ExportItem
|
|
165
|
+
title="Design System"
|
|
166
|
+
description="Colors, typography, and styling configuration for consistent branding."
|
|
167
|
+
items={['CSS tokens', 'Tailwind config', 'Font setup']}
|
|
168
|
+
/>
|
|
169
|
+
<ExportItem
|
|
170
|
+
title="Data Model"
|
|
171
|
+
description="Entity definitions and sample data for your application."
|
|
172
|
+
items={['TypeScript types', 'Sample data', 'Entity docs']}
|
|
173
|
+
/>
|
|
174
|
+
<ExportItem
|
|
175
|
+
title="Components"
|
|
176
|
+
description="React components and visual references for each section."
|
|
177
|
+
items={['Shell components', 'Section components', 'Screenshots']}
|
|
178
|
+
/>
|
|
179
|
+
<ExportItem
|
|
180
|
+
title="Test Instructions"
|
|
181
|
+
description="Framework-agnostic test specs for TDD implementation."
|
|
182
|
+
items={['tests.md per section', 'User flow tests', 'Empty state tests']}
|
|
183
|
+
/>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
</CardContent>
|
|
187
|
+
</Card>
|
|
188
|
+
)}
|
|
189
|
+
|
|
190
|
+
{/* How to use */}
|
|
191
|
+
<Card className="border-stone-200 dark:border-stone-700 shadow-sm">
|
|
192
|
+
<CardHeader>
|
|
193
|
+
<CardTitle className="text-lg font-semibold text-stone-900 dark:text-stone-100 flex items-center gap-2">
|
|
194
|
+
<FileText className="w-5 h-5 text-stone-500 dark:text-stone-400" strokeWidth={1.5} />
|
|
195
|
+
How to Use the Export
|
|
196
|
+
</CardTitle>
|
|
197
|
+
</CardHeader>
|
|
198
|
+
<CardContent className="space-y-4">
|
|
199
|
+
{/* Option A - Incremental (Recommended) */}
|
|
200
|
+
<Collapsible>
|
|
201
|
+
<CollapsibleTrigger className="flex items-start justify-between w-full text-left group">
|
|
202
|
+
<div className="flex-1">
|
|
203
|
+
<h4 className="font-medium text-stone-900 dark:text-stone-100">
|
|
204
|
+
Option A: Incremental (Recommended)
|
|
205
|
+
</h4>
|
|
206
|
+
<p className="text-sm text-stone-500 dark:text-stone-400 mt-1">
|
|
207
|
+
Build milestone by milestone for better control and easier debugging.
|
|
208
|
+
</p>
|
|
209
|
+
</div>
|
|
210
|
+
<ChevronDown className="w-4 h-4 text-stone-400 dark:text-stone-500 mt-1 shrink-0 transition-transform group-data-[state=open]:rotate-180" strokeWidth={1.5} />
|
|
211
|
+
</CollapsibleTrigger>
|
|
212
|
+
<CollapsibleContent>
|
|
213
|
+
<ol className="text-sm text-stone-600 dark:text-stone-400 space-y-2 list-decimal list-inside mt-4 pl-1">
|
|
214
|
+
<li>Copy the <code className="font-mono text-stone-800 dark:text-stone-200">product-plan/</code> folder into your codebase</li>
|
|
215
|
+
<li>Start with Foundation (<code className="font-mono text-stone-800 dark:text-stone-200">instructions/incremental/01-foundation.md</code>)</li>
|
|
216
|
+
<li>Then Shell (<code className="font-mono text-stone-800 dark:text-stone-200">instructions/incremental/02-shell.md</code>)</li>
|
|
217
|
+
<li>
|
|
218
|
+
For each section:
|
|
219
|
+
<ul className="mt-1.5 ml-5 space-y-1">
|
|
220
|
+
<li className="flex items-center gap-2">
|
|
221
|
+
<span className="w-1 h-1 rounded-full bg-stone-400 dark:bg-stone-500" />
|
|
222
|
+
Open <code className="font-mono text-stone-800 dark:text-stone-200">prompts/section-prompt.md</code>
|
|
223
|
+
</li>
|
|
224
|
+
<li className="flex items-center gap-2">
|
|
225
|
+
<span className="w-1 h-1 rounded-full bg-stone-400 dark:bg-stone-500" />
|
|
226
|
+
Fill in the section variables at the top (SECTION_NAME, SECTION_ID, NN)
|
|
227
|
+
</li>
|
|
228
|
+
<li className="flex items-center gap-2">
|
|
229
|
+
<span className="w-1 h-1 rounded-full bg-stone-400 dark:bg-stone-500" />
|
|
230
|
+
Copy/paste the prompt into your AI coding agent
|
|
231
|
+
</li>
|
|
232
|
+
</ul>
|
|
233
|
+
</li>
|
|
234
|
+
<li>Review and test after each milestone before moving to the next</li>
|
|
235
|
+
</ol>
|
|
236
|
+
</CollapsibleContent>
|
|
237
|
+
</Collapsible>
|
|
238
|
+
|
|
239
|
+
<div className="border-t border-stone-200 dark:border-stone-700" />
|
|
240
|
+
|
|
241
|
+
{/* Option B - One-Shot */}
|
|
242
|
+
<Collapsible>
|
|
243
|
+
<CollapsibleTrigger className="flex items-start justify-between w-full text-left group">
|
|
244
|
+
<div className="flex-1">
|
|
245
|
+
<h4 className="font-medium text-stone-900 dark:text-stone-100">
|
|
246
|
+
Option B: One-Shot
|
|
247
|
+
</h4>
|
|
248
|
+
<p className="text-sm text-stone-500 dark:text-stone-400 mt-1">
|
|
249
|
+
Build the entire app in one session using a pre-written prompt.
|
|
250
|
+
</p>
|
|
251
|
+
</div>
|
|
252
|
+
<ChevronDown className="w-4 h-4 text-stone-400 dark:text-stone-500 mt-1 shrink-0 transition-transform group-data-[state=open]:rotate-180" strokeWidth={1.5} />
|
|
253
|
+
</CollapsibleTrigger>
|
|
254
|
+
<CollapsibleContent>
|
|
255
|
+
<ol className="text-sm text-stone-600 dark:text-stone-400 space-y-2 list-decimal list-inside mt-4 pl-1">
|
|
256
|
+
<li>Copy the <code className="font-mono text-stone-800 dark:text-stone-200">product-plan/</code> folder into your codebase</li>
|
|
257
|
+
<li>Open <code className="font-mono text-stone-800 dark:text-stone-200">prompts/one-shot-prompt.md</code></li>
|
|
258
|
+
<li>Add any additional notes to the prompt (tech stack preferences, etc.)</li>
|
|
259
|
+
<li>Copy/paste the prompt into your AI coding agent</li>
|
|
260
|
+
<li>Answer the agent's clarifying questions about auth, user modeling, etc.</li>
|
|
261
|
+
<li>Let the agent plan and implement everything</li>
|
|
262
|
+
</ol>
|
|
263
|
+
</CollapsibleContent>
|
|
264
|
+
</Collapsible>
|
|
265
|
+
</CardContent>
|
|
266
|
+
</Card>
|
|
267
|
+
</div>
|
|
268
|
+
</AppLayout>
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
interface ChecklistItemProps {
|
|
273
|
+
label: string
|
|
274
|
+
isComplete: boolean
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function ChecklistItem({ label, isComplete }: ChecklistItemProps) {
|
|
278
|
+
return (
|
|
279
|
+
<div className="flex items-center gap-2 py-1">
|
|
280
|
+
{isComplete ? (
|
|
281
|
+
<div className="w-4 h-4 rounded bg-stone-200 dark:bg-stone-700 flex items-center justify-center">
|
|
282
|
+
<Check className="w-2.5 h-2.5 text-stone-600 dark:text-stone-400" strokeWidth={3} />
|
|
283
|
+
</div>
|
|
284
|
+
) : (
|
|
285
|
+
<div className="w-4 h-4 rounded border-2 border-amber-400 dark:border-amber-500" />
|
|
286
|
+
)}
|
|
287
|
+
<span className="text-sm text-stone-700 dark:text-stone-300">
|
|
288
|
+
{label}
|
|
289
|
+
</span>
|
|
290
|
+
</div>
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
interface ExportItemProps {
|
|
295
|
+
title: string
|
|
296
|
+
description: string
|
|
297
|
+
items: string[]
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function ExportItem({ title, description, items }: ExportItemProps) {
|
|
301
|
+
return (
|
|
302
|
+
<div className="bg-stone-50 dark:bg-stone-800/50 rounded-lg p-4">
|
|
303
|
+
<h4 className="font-medium text-stone-900 dark:text-stone-100 mb-1">{title}</h4>
|
|
304
|
+
<p className="text-xs text-stone-500 dark:text-stone-400 mb-3">{description}</p>
|
|
305
|
+
<ul className="text-sm text-stone-600 dark:text-stone-400 space-y-1">
|
|
306
|
+
{items.map((item, index) => (
|
|
307
|
+
<li key={index} className="flex items-center gap-2">
|
|
308
|
+
<span className="w-1 h-1 rounded-full bg-stone-400 dark:bg-stone-500" />
|
|
309
|
+
{item}
|
|
310
|
+
</li>
|
|
311
|
+
))}
|
|
312
|
+
</ul>
|
|
313
|
+
</div>
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function PromptButton({ command, prompt }: { command: string, prompt: string }) {
|
|
318
|
+
const { toast } = useToast()
|
|
319
|
+
|
|
320
|
+
const copyPrompt = () => {
|
|
321
|
+
navigator.clipboard.writeText(prompt)
|
|
322
|
+
toast({
|
|
323
|
+
title: "Prompt Copied!",
|
|
324
|
+
description: "Paste it into the Agent terminal to run the action.",
|
|
325
|
+
type: "success"
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return (
|
|
330
|
+
<div className="bg-stone-100 dark:bg-stone-800 rounded-md px-4 py-2.5 w-full">
|
|
331
|
+
<button
|
|
332
|
+
onClick={copyPrompt}
|
|
333
|
+
className="w-full flex items-center justify-center gap-2 text-stone-700 dark:text-stone-300 hover:text-stone-900 dark:hover:text-stone-100 transition-colors mb-1"
|
|
334
|
+
title="Copy Agent Prompt"
|
|
335
|
+
>
|
|
336
|
+
<span className="text-xs font-medium uppercase tracking-wider text-stone-500">Run Action</span>
|
|
337
|
+
<Copy size={14} />
|
|
338
|
+
</button>
|
|
339
|
+
<code className="block text-center text-sm font-mono text-stone-700 dark:text-stone-300">
|
|
340
|
+
{command}
|
|
341
|
+
</code>
|
|
342
|
+
</div>
|
|
343
|
+
)
|
|
344
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useNavigate } from 'react-router-dom'
|
|
2
|
+
import { FileText, Boxes, Layout, LayoutList, Package, ArrowRight } from 'lucide-react'
|
|
3
|
+
import type { Phase } from './PhaseNav'
|
|
4
|
+
|
|
5
|
+
interface NextPhaseButtonProps {
|
|
6
|
+
nextPhase: Exclude<Phase, 'product'> // Can't navigate "next" to product since it's first
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const phaseConfig: Record<Exclude<Phase, 'product'>, { label: string; icon: typeof FileText; path: string }> = {
|
|
10
|
+
'data-model': { label: 'Data Model', icon: Boxes, path: '/data-model' },
|
|
11
|
+
'design': { label: 'Design', icon: Layout, path: '/design' },
|
|
12
|
+
'sections': { label: 'Sections', icon: LayoutList, path: '/sections' },
|
|
13
|
+
'export': { label: 'Export', icon: Package, path: '/export' },
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function NextPhaseButton({ nextPhase }: NextPhaseButtonProps) {
|
|
17
|
+
const navigate = useNavigate()
|
|
18
|
+
const config = phaseConfig[nextPhase]
|
|
19
|
+
const Icon = config.icon
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<button
|
|
23
|
+
onClick={() => navigate(config.path)}
|
|
24
|
+
className="w-full flex items-center justify-between gap-4 px-6 py-4 bg-stone-900 dark:bg-stone-100 text-stone-100 dark:text-stone-900 rounded-lg hover:bg-stone-800 dark:hover:bg-stone-200 transition-colors group"
|
|
25
|
+
>
|
|
26
|
+
<div className="flex items-center gap-3">
|
|
27
|
+
<Icon className="w-5 h-5" strokeWidth={1.5} />
|
|
28
|
+
<span className="font-medium">Continue to {config.label}</span>
|
|
29
|
+
</div>
|
|
30
|
+
<ArrowRight className="w-5 h-5 transition-transform group-hover:translate-x-1" strokeWidth={1.5} />
|
|
31
|
+
</button>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { useLocation, useNavigate } from 'react-router-dom'
|
|
2
|
+
import { useMemo } from 'react'
|
|
3
|
+
import { FileText, Boxes, Layout, LayoutList, Package } from 'lucide-react'
|
|
4
|
+
import { loadProductData, hasExportZip } from '@/lib/product-loader'
|
|
5
|
+
import { getAllSectionIds, getSectionScreenDesigns, hasSectionSpec } from '@/lib/section-loader'
|
|
6
|
+
|
|
7
|
+
export type Phase = 'product' | 'data-model' | 'design' | 'sections' | 'export'
|
|
8
|
+
|
|
9
|
+
interface PhaseConfig {
|
|
10
|
+
id: Phase
|
|
11
|
+
label: string
|
|
12
|
+
icon: typeof FileText
|
|
13
|
+
path: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const phases: PhaseConfig[] = [
|
|
17
|
+
{ id: 'product', label: 'Product', icon: FileText, path: '/' },
|
|
18
|
+
{ id: 'data-model', label: 'Data Model', icon: Boxes, path: '/data-model' },
|
|
19
|
+
{ id: 'design', label: 'Design', icon: Layout, path: '/design' },
|
|
20
|
+
{ id: 'sections', label: 'Sections', icon: LayoutList, path: '/sections' },
|
|
21
|
+
{ id: 'export', label: 'Export', icon: Package, path: '/export' },
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
export type PhaseStatus = 'completed' | 'current' | 'upcoming'
|
|
25
|
+
|
|
26
|
+
interface PhaseInfo {
|
|
27
|
+
phase: PhaseConfig
|
|
28
|
+
status: PhaseStatus
|
|
29
|
+
isComplete: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function usePhaseStatuses(): PhaseInfo[] {
|
|
33
|
+
const location = useLocation()
|
|
34
|
+
const productData = useMemo(() => loadProductData(), [])
|
|
35
|
+
|
|
36
|
+
// Calculate completion status for each phase
|
|
37
|
+
const hasOverview = !!productData.overview
|
|
38
|
+
const hasRoadmap = !!productData.roadmap
|
|
39
|
+
const hasDataModel = !!productData.dataModel
|
|
40
|
+
const hasDesignSystem = !!productData.designSystem
|
|
41
|
+
const hasShell = !!productData.shell
|
|
42
|
+
|
|
43
|
+
const sectionIds = useMemo(() => getAllSectionIds(), [])
|
|
44
|
+
const sectionsWithScreenDesigns = useMemo(() => {
|
|
45
|
+
return sectionIds.filter(id => getSectionScreenDesigns(id).length > 0 && hasSectionSpec(id)).length
|
|
46
|
+
}, [sectionIds])
|
|
47
|
+
const hasSections = sectionsWithScreenDesigns > 0
|
|
48
|
+
|
|
49
|
+
// Determine current phase from URL
|
|
50
|
+
const currentPath = location.pathname
|
|
51
|
+
let currentPhaseId: Phase = 'product'
|
|
52
|
+
|
|
53
|
+
if (currentPath === '/' || currentPath === '/product') {
|
|
54
|
+
currentPhaseId = 'product'
|
|
55
|
+
} else if (currentPath === '/data-model') {
|
|
56
|
+
currentPhaseId = 'data-model'
|
|
57
|
+
} else if (currentPath === '/design' || currentPath === '/design-system' || currentPath.startsWith('/shell')) {
|
|
58
|
+
currentPhaseId = 'design'
|
|
59
|
+
} else if (currentPath === '/sections' || currentPath.startsWith('/sections/')) {
|
|
60
|
+
currentPhaseId = 'sections'
|
|
61
|
+
} else if (currentPath === '/export') {
|
|
62
|
+
currentPhaseId = 'export'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check if export zip exists
|
|
66
|
+
const exportZipExists = hasExportZip()
|
|
67
|
+
|
|
68
|
+
// Determine completion status
|
|
69
|
+
const phaseComplete: Record<Phase, boolean> = {
|
|
70
|
+
'product': hasOverview && hasRoadmap,
|
|
71
|
+
'data-model': hasDataModel,
|
|
72
|
+
'design': hasDesignSystem || hasShell,
|
|
73
|
+
'sections': hasSections,
|
|
74
|
+
'export': exportZipExists,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return phases.map(phase => {
|
|
78
|
+
const isComplete = phaseComplete[phase.id]
|
|
79
|
+
let status: PhaseStatus
|
|
80
|
+
if (phase.id === currentPhaseId) {
|
|
81
|
+
status = 'current'
|
|
82
|
+
} else if (isComplete) {
|
|
83
|
+
status = 'completed'
|
|
84
|
+
} else {
|
|
85
|
+
status = 'upcoming'
|
|
86
|
+
}
|
|
87
|
+
return { phase, status, isComplete }
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function PhaseNav() {
|
|
92
|
+
const navigate = useNavigate()
|
|
93
|
+
const phaseInfos = usePhaseStatuses()
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<nav className="flex items-center justify-center">
|
|
97
|
+
{phaseInfos.map(({ phase, status, isComplete }, index) => {
|
|
98
|
+
const Icon = phase.icon
|
|
99
|
+
const isFirst = index === 0
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div key={phase.id} className="flex items-center">
|
|
103
|
+
{/* Connector line */}
|
|
104
|
+
{!isFirst && (
|
|
105
|
+
<div
|
|
106
|
+
className={`w-4 sm:w-8 lg:w-12 h-px transition-colors duration-200 ${status === 'upcoming'
|
|
107
|
+
? 'bg-stone-200 dark:bg-stone-700'
|
|
108
|
+
: 'bg-stone-400 dark:bg-stone-500'
|
|
109
|
+
}`}
|
|
110
|
+
/>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
{/* Phase button */}
|
|
114
|
+
<button
|
|
115
|
+
onClick={() => navigate(phase.path)}
|
|
116
|
+
className={`
|
|
117
|
+
group relative flex items-center gap-1.5 sm:gap-2 px-2 sm:px-3 py-1.5 sm:py-2 rounded-lg transition-all duration-200 whitespace-nowrap
|
|
118
|
+
${status === 'current'
|
|
119
|
+
? 'bg-stone-900 dark:bg-stone-100 text-stone-100 dark:text-stone-900 shadow-sm'
|
|
120
|
+
: status === 'completed'
|
|
121
|
+
? 'bg-stone-100 dark:bg-stone-800 text-stone-700 dark:text-stone-300 hover:bg-stone-200 dark:hover:bg-stone-700'
|
|
122
|
+
: 'text-stone-400 dark:text-stone-500 hover:text-stone-600 dark:hover:text-stone-400 hover:bg-stone-50 dark:hover:bg-stone-800/50'
|
|
123
|
+
}
|
|
124
|
+
`}
|
|
125
|
+
>
|
|
126
|
+
<Icon
|
|
127
|
+
className={`w-4 h-4 shrink-0 transition-transform duration-200 group-hover:scale-110 ${status === 'current' ? '' : status === 'completed' ? '' : 'opacity-60'
|
|
128
|
+
}`}
|
|
129
|
+
strokeWidth={1.5}
|
|
130
|
+
/>
|
|
131
|
+
<span className={`text-sm font-medium hidden sm:inline ${status === 'upcoming' ? 'opacity-60' : ''
|
|
132
|
+
}`}>
|
|
133
|
+
{phase.label}
|
|
134
|
+
</span>
|
|
135
|
+
|
|
136
|
+
{/* Completion indicator - check circle at top-left (shows even when current) */}
|
|
137
|
+
{isComplete && (
|
|
138
|
+
<span className="absolute -top-1 -left-1 w-4 h-4 rounded-full bg-lime-500 flex items-center justify-center shadow-sm">
|
|
139
|
+
<svg className="w-2.5 h-2.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
|
|
140
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
|
141
|
+
</svg>
|
|
142
|
+
</span>
|
|
143
|
+
)}
|
|
144
|
+
</button>
|
|
145
|
+
</div>
|
|
146
|
+
)
|
|
147
|
+
})}
|
|
148
|
+
</nav>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { phases }
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo } from 'react'
|
|
2
|
+
import { Link } from 'react-router-dom'
|
|
3
|
+
import { AlertTriangle, X } from 'lucide-react'
|
|
4
|
+
import { loadProductData } from '@/lib/product-loader'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get a storage key based on the product name to track dismissed warnings per product
|
|
8
|
+
*/
|
|
9
|
+
function getStorageKey(productName: string): string {
|
|
10
|
+
const sanitized = productName.toLowerCase().replace(/[^a-z0-9]+/g, '-')
|
|
11
|
+
return `design-os-phase-warning-dismissed-${sanitized}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function PhaseWarningBanner() {
|
|
15
|
+
const productData = useMemo(() => loadProductData(), [])
|
|
16
|
+
const [isDismissed, setIsDismissed] = useState(true) // Start dismissed to avoid flash
|
|
17
|
+
|
|
18
|
+
const hasDataModel = !!productData.dataModel
|
|
19
|
+
const hasDesignSystem = !!(productData.designSystem?.colors || productData.designSystem?.typography)
|
|
20
|
+
const hasShell = !!productData.shell?.spec
|
|
21
|
+
const hasDesign = hasDesignSystem || hasShell
|
|
22
|
+
|
|
23
|
+
const productName = productData.overview?.name || 'default-product'
|
|
24
|
+
const storageKey = getStorageKey(productName)
|
|
25
|
+
|
|
26
|
+
// Check localStorage on mount
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const dismissed = localStorage.getItem(storageKey) === 'true'
|
|
29
|
+
setIsDismissed(dismissed)
|
|
30
|
+
}, [storageKey])
|
|
31
|
+
|
|
32
|
+
const handleDismiss = () => {
|
|
33
|
+
localStorage.setItem(storageKey, 'true')
|
|
34
|
+
setIsDismissed(true)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Don't show if both phases are complete or if dismissed
|
|
38
|
+
if ((hasDataModel && hasDesign) || isDismissed) {
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Build the warning message
|
|
43
|
+
const missingPhases: { name: string; path: string }[] = []
|
|
44
|
+
if (!hasDataModel) {
|
|
45
|
+
missingPhases.push({ name: 'Data Model', path: '/data-model' })
|
|
46
|
+
}
|
|
47
|
+
if (!hasDesign) {
|
|
48
|
+
missingPhases.push({ name: 'Design', path: '/design' })
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg px-4 py-3 mb-6">
|
|
53
|
+
<div className="flex items-start gap-3">
|
|
54
|
+
<AlertTriangle className="w-4 h-4 text-amber-600 dark:text-amber-400 mt-0.5 shrink-0" strokeWidth={2} />
|
|
55
|
+
<div className="flex-1 min-w-0">
|
|
56
|
+
<p className="text-sm text-amber-800 dark:text-amber-200">
|
|
57
|
+
Consider completing{' '}
|
|
58
|
+
{missingPhases.map((phase, index) => (
|
|
59
|
+
<span key={phase.path}>
|
|
60
|
+
{index > 0 && ' and '}
|
|
61
|
+
<Link
|
|
62
|
+
to={phase.path}
|
|
63
|
+
className="font-medium underline hover:no-underline"
|
|
64
|
+
>
|
|
65
|
+
{phase.name}
|
|
66
|
+
</Link>
|
|
67
|
+
</span>
|
|
68
|
+
))}{' '}
|
|
69
|
+
before designing sections.
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
<button
|
|
73
|
+
onClick={handleDismiss}
|
|
74
|
+
className="text-amber-600 dark:text-amber-400 hover:text-amber-800 dark:hover:text-amber-200 transition-colors shrink-0"
|
|
75
|
+
>
|
|
76
|
+
<X className="w-4 h-4" strokeWidth={2} />
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
}
|