@mohammad-irfan/create-dashboard-kit 1.0.0
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/LICENSE +21 -0
- package/README.md +153 -0
- package/bin/create-dashboard.js +12 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +223 -0
- package/dist/configBuilder.d.ts +8 -0
- package/dist/configBuilder.js +573 -0
- package/dist/generator.d.ts +9 -0
- package/dist/generator.js +223 -0
- package/dist/presets.d.ts +8 -0
- package/dist/presets.js +72 -0
- package/dist/prompts.d.ts +2 -0
- package/dist/prompts.js +121 -0
- package/dist/templateEngine.d.ts +3 -0
- package/dist/templateEngine.js +31 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.js +1 -0
- package/dist/validation.d.ts +16 -0
- package/dist/validation.js +73 -0
- package/package.json +56 -0
- package/templates/source/components/controls/Button.tsx +69 -0
- package/templates/source/components/controls/Input.tsx +59 -0
- package/templates/source/components/controls/SearchBar.tsx +44 -0
- package/templates/source/components/controls/Select.tsx +61 -0
- package/templates/source/components/controls/ThemeToggle.tsx +23 -0
- package/templates/source/components/feedback/NotificationCenter.tsx +151 -0
- package/templates/source/components/feedback/Skeleton.tsx +22 -0
- package/templates/source/components/feedback/StatusBadge.tsx +35 -0
- package/templates/source/components/feedback/statusBadgeStyles.ts +28 -0
- package/templates/source/components/icons/IconSet.tsx +235 -0
- package/templates/source/components/layout/AppShell.tsx +70 -0
- package/templates/source/components/layout/Header.tsx +90 -0
- package/templates/source/components/layout/PageHeader.tsx +27 -0
- package/templates/source/components/layout/Sidebar.tsx +202 -0
- package/templates/source/components/metrics/MetricRow.tsx +61 -0
- package/templates/source/components/metrics/StatCard.tsx +49 -0
- package/templates/source/components/navigation/NavItem.tsx +75 -0
- package/templates/source/components/navigation/ShortcutPill.tsx +39 -0
- package/templates/source/components/surfaces/EmptyState.tsx +36 -0
- package/templates/source/components/surfaces/HeroSurface.tsx +64 -0
- package/templates/source/components/surfaces/Panel.tsx +43 -0
- package/templates/source/components/surfaces/PriorityCard.tsx +75 -0
- package/templates/source/components/surfaces/QuietListCard.tsx +146 -0
- package/templates/source/design-system/animations.css +25 -0
- package/templates/source/design-system/tokens.css +74 -0
- package/templates/source/design-system/typography.css +34 -0
- package/templates/source/theme/ThemeContext.tsx +82 -0
- package/templates/source/theme/useTheme.ts +10 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
import { resolvePresetTokens } from './presets.js';
|
|
2
|
+
export function buildDashboardConfigFile(options) {
|
|
3
|
+
const tokens = resolvePresetTokens(options.preset, options.primaryColor, options.accentColor);
|
|
4
|
+
const cardRadiusMap = {
|
|
5
|
+
small: 'rounded-lg',
|
|
6
|
+
medium: 'rounded-xl',
|
|
7
|
+
large: 'rounded-2xl',
|
|
8
|
+
};
|
|
9
|
+
const canvasRadiusMap = {
|
|
10
|
+
small: 'sm',
|
|
11
|
+
medium: 'md',
|
|
12
|
+
large: 'lg',
|
|
13
|
+
};
|
|
14
|
+
const cardRadiusClass = cardRadiusMap[options.cardRadius] || 'rounded-2xl';
|
|
15
|
+
const canvasRadiusClass = canvasRadiusMap[options.cardRadius] || 'lg';
|
|
16
|
+
const demoNav = options.preset === 'cafe'
|
|
17
|
+
? `[
|
|
18
|
+
{ id: 'orders', label: 'Active Orders', to: '/orders', icon: 'coffee', badge: 8 },
|
|
19
|
+
{ id: 'kitchen', label: 'Kitchen KDS', to: '/kitchen', icon: 'clipboard', badge: 'Live' },
|
|
20
|
+
{ id: 'menu', label: 'Menu & Recipes', to: '/menu', icon: 'clock' },
|
|
21
|
+
{ id: 'inventory', label: 'Bean & Milk Stock', to: '/inventory', icon: 'check' },
|
|
22
|
+
{ id: 'staff', label: 'Staff Roster', to: '/staff', icon: 'users' },
|
|
23
|
+
{ id: 'sales', label: 'Daily Takings', to: '/sales', icon: 'dollar' },
|
|
24
|
+
{ id: 'settings', label: 'Store Config', to: '/settings', icon: 'settings' },
|
|
25
|
+
]`
|
|
26
|
+
: `[
|
|
27
|
+
{ id: 'overview', label: 'Overview', to: '/dashboard', icon: 'dashboard' },
|
|
28
|
+
{ id: 'projects', label: 'Projects', to: '/projects', icon: 'folder', badge: 12 },
|
|
29
|
+
{ id: 'tasks', label: 'Tasks', to: '/tasks', icon: 'check', badge: 4 },
|
|
30
|
+
{ id: 'team', label: 'Team', to: '/team', icon: 'users' },
|
|
31
|
+
{ id: 'billing', label: 'Billing', to: '/billing', icon: 'dollar' },
|
|
32
|
+
{ id: 'settings', label: 'Settings', to: '/settings', icon: 'settings' },
|
|
33
|
+
]`;
|
|
34
|
+
const demoShortcuts = options.preset === 'cafe'
|
|
35
|
+
? `[
|
|
36
|
+
{ id: 'new-ticket', label: 'Quick Order', to: '/orders/new', icon: 'plus' },
|
|
37
|
+
{ id: 'brew-log', label: 'Calibrate Grinder', to: '/equipment', icon: 'coffee' },
|
|
38
|
+
{ id: 'close-shift', label: 'Z-Report', to: '/reports', icon: 'dollar' },
|
|
39
|
+
]`
|
|
40
|
+
: `[
|
|
41
|
+
{ id: 'new-project', label: 'New Project', to: '/projects/new', icon: 'plus' },
|
|
42
|
+
{ id: 'ai-summary', label: 'AI Summary', to: '/insights', icon: 'sparkles' },
|
|
43
|
+
{ id: 'team-invite', label: 'Invite Member', to: '/team/invite', icon: 'users' },
|
|
44
|
+
]`;
|
|
45
|
+
const demoUser = options.preset === 'cafe'
|
|
46
|
+
? `{
|
|
47
|
+
name: 'Matteo Rossi',
|
|
48
|
+
email: 'headbarista@roastery.cafe',
|
|
49
|
+
initials: 'MR',
|
|
50
|
+
profileRoute: '/settings',
|
|
51
|
+
}`
|
|
52
|
+
: `{
|
|
53
|
+
name: 'Alex Morgan',
|
|
54
|
+
email: 'alex.morgan@workspace.local',
|
|
55
|
+
initials: 'AM',
|
|
56
|
+
profileRoute: '/settings',
|
|
57
|
+
}`;
|
|
58
|
+
return `// ============================================================================
|
|
59
|
+
// ${options.brandName} — Dashboard Configuration
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// Edit this file to customize navigation, branding, colors, and layout shapes.
|
|
62
|
+
// No core components need to be modified.
|
|
63
|
+
|
|
64
|
+
import type { ComponentType, SVGProps } from 'react'
|
|
65
|
+
|
|
66
|
+
export type CanvasRadius = 'none' | 'sm' | 'md' | 'lg' | 'full'
|
|
67
|
+
export type ButtonShape = 'square' | 'rounded' | 'pill'
|
|
68
|
+
|
|
69
|
+
export interface NavItemConfig {
|
|
70
|
+
id: string
|
|
71
|
+
label: string
|
|
72
|
+
to: string
|
|
73
|
+
icon?: string | ComponentType<SVGProps<SVGSVGElement>>
|
|
74
|
+
badge?: string | number
|
|
75
|
+
end?: boolean
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ShortcutConfig {
|
|
79
|
+
id: string
|
|
80
|
+
label: string
|
|
81
|
+
to: string
|
|
82
|
+
icon?: string | ComponentType<SVGProps<SVGSVGElement>>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface UserMenuConfig {
|
|
86
|
+
name: string
|
|
87
|
+
email?: string
|
|
88
|
+
avatarUrl?: string
|
|
89
|
+
initials?: string
|
|
90
|
+
profileRoute: string
|
|
91
|
+
onLogout?: () => void
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface DashboardConfig {
|
|
95
|
+
branding: {
|
|
96
|
+
name: string
|
|
97
|
+
logoUrl?: string
|
|
98
|
+
logoIcon?: ComponentType<SVGProps<SVGSVGElement>>
|
|
99
|
+
tagline?: string
|
|
100
|
+
homeRoute: string
|
|
101
|
+
}
|
|
102
|
+
colors: {
|
|
103
|
+
primary: { light: string; dark: string }
|
|
104
|
+
primaryForeground: { light: string; dark: string }
|
|
105
|
+
background: { light: string; dark: string }
|
|
106
|
+
canvas: { light: string; dark: string }
|
|
107
|
+
surface: { light: string; dark: string }
|
|
108
|
+
surfaceElevated: { light: string; dark: string }
|
|
109
|
+
border: { light: string; dark: string }
|
|
110
|
+
accent: { light: string; dark: string }
|
|
111
|
+
}
|
|
112
|
+
shapes: {
|
|
113
|
+
canvasRadius: CanvasRadius
|
|
114
|
+
cardRadius: string
|
|
115
|
+
buttonShape: ButtonShape
|
|
116
|
+
}
|
|
117
|
+
navigation: NavItemConfig[]
|
|
118
|
+
shortcuts?: ShortcutConfig[]
|
|
119
|
+
userMenu: UserMenuConfig
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const dashboardConfig: DashboardConfig = {
|
|
123
|
+
branding: {
|
|
124
|
+
name: '${options.brandName.replace(/'/g, "\\'")}',
|
|
125
|
+
tagline: '${(options.tagline || tokens.taglineDefault).replace(/'/g, "\\'")}',
|
|
126
|
+
homeRoute: '/dashboard',
|
|
127
|
+
logoUrl: '/assets/logo.svg',
|
|
128
|
+
},
|
|
129
|
+
colors: {
|
|
130
|
+
primary: {
|
|
131
|
+
light: '${tokens.primaryLight}',
|
|
132
|
+
dark: '${tokens.primaryDark}',
|
|
133
|
+
},
|
|
134
|
+
primaryForeground: {
|
|
135
|
+
light: '#ffffff',
|
|
136
|
+
dark: '#ffffff',
|
|
137
|
+
},
|
|
138
|
+
background: {
|
|
139
|
+
light: '${tokens.backgroundLight}',
|
|
140
|
+
dark: '${tokens.backgroundDark}',
|
|
141
|
+
},
|
|
142
|
+
canvas: {
|
|
143
|
+
light: '${tokens.canvasLight}',
|
|
144
|
+
dark: '${tokens.canvasDark}',
|
|
145
|
+
},
|
|
146
|
+
surface: {
|
|
147
|
+
light: '${tokens.surfaceLight}',
|
|
148
|
+
dark: '${tokens.surfaceDark}',
|
|
149
|
+
},
|
|
150
|
+
surfaceElevated: {
|
|
151
|
+
light: '${tokens.surfaceElevatedLight}',
|
|
152
|
+
dark: '${tokens.surfaceElevatedDark}',
|
|
153
|
+
},
|
|
154
|
+
border: {
|
|
155
|
+
light: '${tokens.borderLight}',
|
|
156
|
+
dark: '${tokens.borderDark}',
|
|
157
|
+
},
|
|
158
|
+
accent: {
|
|
159
|
+
light: '${tokens.accentLight}',
|
|
160
|
+
dark: '${tokens.accentDark}',
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
shapes: {
|
|
164
|
+
canvasRadius: '${canvasRadiusClass}',
|
|
165
|
+
cardRadius: '${cardRadiusClass}',
|
|
166
|
+
buttonShape: '${options.buttonShape}',
|
|
167
|
+
},
|
|
168
|
+
navigation: ${demoNav},
|
|
169
|
+
shortcuts: ${demoShortcuts},
|
|
170
|
+
userMenu: ${demoUser},
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function getCanvasRadiusClass(radius: CanvasRadius): string {
|
|
174
|
+
switch (radius) {
|
|
175
|
+
case 'none':
|
|
176
|
+
return 'rounded-none'
|
|
177
|
+
case 'sm':
|
|
178
|
+
return 'sm:rounded-tl-[16px] lg:rounded-tl-[20px]'
|
|
179
|
+
case 'md':
|
|
180
|
+
return 'sm:rounded-tl-[24px] lg:rounded-tl-[32px]'
|
|
181
|
+
case 'lg':
|
|
182
|
+
return 'sm:rounded-tl-[36px] lg:rounded-tl-[48px]'
|
|
183
|
+
case 'full':
|
|
184
|
+
return 'sm:rounded-tl-[60px] lg:rounded-tl-[72px]'
|
|
185
|
+
default:
|
|
186
|
+
return 'sm:rounded-tl-[36px] lg:rounded-tl-[48px]'
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function getButtonShapeClass(shape: ButtonShape): string {
|
|
191
|
+
switch (shape) {
|
|
192
|
+
case 'pill':
|
|
193
|
+
return 'rounded-full'
|
|
194
|
+
case 'rounded':
|
|
195
|
+
return 'rounded-xl'
|
|
196
|
+
case 'square':
|
|
197
|
+
return 'rounded-md'
|
|
198
|
+
default:
|
|
199
|
+
return 'rounded-full'
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
`;
|
|
203
|
+
}
|
|
204
|
+
export function buildTokensCssFile(options) {
|
|
205
|
+
const tokens = resolvePresetTokens(options.preset, options.primaryColor, options.accentColor);
|
|
206
|
+
return `/* ============================================================================
|
|
207
|
+
* ${options.brandName} — Design Tokens
|
|
208
|
+
* ============================================================================
|
|
209
|
+
* Generated semantic design tokens for Light & Dark mode.
|
|
210
|
+
*/
|
|
211
|
+
|
|
212
|
+
:root,
|
|
213
|
+
[data-theme="light"] {
|
|
214
|
+
--background: ${tokens.backgroundLight};
|
|
215
|
+
--foreground: #0f172a;
|
|
216
|
+
--muted-foreground: #64748b;
|
|
217
|
+
--surface: ${tokens.surfaceLight};
|
|
218
|
+
--surface-elevated: ${tokens.surfaceElevatedLight};
|
|
219
|
+
--muted: #eef1f4;
|
|
220
|
+
--border: ${tokens.borderLight};
|
|
221
|
+
--input: #ffffff;
|
|
222
|
+
--canvas-background: ${tokens.canvasLight};
|
|
223
|
+
|
|
224
|
+
/* Brand Primary */
|
|
225
|
+
--primary: ${tokens.primaryLight};
|
|
226
|
+
--primary-foreground: #ffffff;
|
|
227
|
+
--ring: ${tokens.primaryLight};
|
|
228
|
+
|
|
229
|
+
/* Semantic Feedback */
|
|
230
|
+
--success: ${tokens.accentLight};
|
|
231
|
+
--success-fg: ${tokens.accentLight};
|
|
232
|
+
--success-bg: #ecfdf5;
|
|
233
|
+
--warning: #d97706;
|
|
234
|
+
--warning-fg: #b45309;
|
|
235
|
+
--warning-bg: #fffbeb;
|
|
236
|
+
--danger: #dc2626;
|
|
237
|
+
--danger-fg: #b91c1c;
|
|
238
|
+
--danger-bg: #fef2f2;
|
|
239
|
+
--info: #0284c7;
|
|
240
|
+
--info-fg: #0369a1;
|
|
241
|
+
--info-bg: #f0f9ff;
|
|
242
|
+
|
|
243
|
+
color-scheme: light;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
[data-theme="dark"] {
|
|
247
|
+
--background: ${tokens.backgroundDark};
|
|
248
|
+
--foreground: #f3f5f7;
|
|
249
|
+
--muted-foreground: #9aa4b2;
|
|
250
|
+
--surface: ${tokens.surfaceDark};
|
|
251
|
+
--surface-elevated: ${tokens.surfaceElevatedDark};
|
|
252
|
+
--muted: #22262b;
|
|
253
|
+
--border: ${tokens.borderDark};
|
|
254
|
+
--input: #1a1a1a;
|
|
255
|
+
--canvas-background: ${tokens.canvasDark};
|
|
256
|
+
|
|
257
|
+
/* Brand Primary */
|
|
258
|
+
--primary: ${tokens.primaryDark};
|
|
259
|
+
--primary-foreground: #ffffff;
|
|
260
|
+
--ring: ${tokens.primaryDark};
|
|
261
|
+
|
|
262
|
+
/* Semantic Feedback */
|
|
263
|
+
--success: ${tokens.accentDark};
|
|
264
|
+
--success-fg: ${tokens.accentDark};
|
|
265
|
+
--success-bg: rgba(16, 185, 129, 0.15);
|
|
266
|
+
--warning: #f59e0b;
|
|
267
|
+
--warning-fg: #f59e0b;
|
|
268
|
+
--warning-bg: rgba(245, 158, 11, 0.15);
|
|
269
|
+
--danger: #ef4444;
|
|
270
|
+
--danger-fg: #ef4444;
|
|
271
|
+
--danger-bg: rgba(239, 68, 68, 0.15);
|
|
272
|
+
--info: #38bdf8;
|
|
273
|
+
--info-fg: #38bdf8;
|
|
274
|
+
--info-bg: rgba(56, 189, 248, 0.15);
|
|
275
|
+
|
|
276
|
+
color-scheme: dark;
|
|
277
|
+
}
|
|
278
|
+
`;
|
|
279
|
+
}
|
|
280
|
+
export function buildIndexHtml(options) {
|
|
281
|
+
const storageKey = `${options.projectName.replace(/[^a-zA-Z0-9]/g, '_')}_theme`;
|
|
282
|
+
return `<!doctype html>
|
|
283
|
+
<html lang="en">
|
|
284
|
+
<head>
|
|
285
|
+
<meta charset="UTF-8" />
|
|
286
|
+
<link rel="icon" type="image/svg+xml" href="/assets/logo.svg" />
|
|
287
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
288
|
+
<title>${options.brandName}</title>
|
|
289
|
+
<script>
|
|
290
|
+
(function () {
|
|
291
|
+
try {
|
|
292
|
+
var stored = localStorage.getItem('${storageKey}')
|
|
293
|
+
var theme =
|
|
294
|
+
stored === 'light' || stored === 'dark'
|
|
295
|
+
? stored
|
|
296
|
+
: window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
297
|
+
? 'dark'
|
|
298
|
+
: '${options.defaultMode === 'system' ? 'light' : options.defaultMode}'
|
|
299
|
+
document.documentElement.setAttribute('data-theme', theme)
|
|
300
|
+
} catch (e) {
|
|
301
|
+
document.documentElement.setAttribute('data-theme', 'light')
|
|
302
|
+
}
|
|
303
|
+
})()
|
|
304
|
+
</script>
|
|
305
|
+
</head>
|
|
306
|
+
<body class="dashboard-font-base bg-background text-foreground antialiased selection:bg-primary/20">
|
|
307
|
+
<div id="root"></div>
|
|
308
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
309
|
+
</body>
|
|
310
|
+
</html>
|
|
311
|
+
`;
|
|
312
|
+
}
|
|
313
|
+
export function buildLogoSvg(options) {
|
|
314
|
+
const initial = options.brandName.charAt(0).toUpperCase() || 'D';
|
|
315
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
|
316
|
+
<defs>
|
|
317
|
+
<linearGradient id="brandGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
318
|
+
<stop offset="0%" stop-color="${options.primaryColor || '#2563eb'}" />
|
|
319
|
+
<stop offset="100%" stop-color="${options.accentColor || '#059669'}" />
|
|
320
|
+
</linearGradient>
|
|
321
|
+
</defs>
|
|
322
|
+
<rect width="48" height="48" rx="14" fill="url(#brandGrad)" />
|
|
323
|
+
<text x="50%" y="54%" dominant-baseline="middle" text-anchor="middle" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" font-weight="bold" font-size="24" fill="#ffffff">${initial}</text>
|
|
324
|
+
</svg>
|
|
325
|
+
`;
|
|
326
|
+
}
|
|
327
|
+
export function buildAppTsx(options) {
|
|
328
|
+
const storageKey = `${options.projectName.replace(/[^a-zA-Z0-9]/g, '_')}_theme`;
|
|
329
|
+
const defaultTheme = options.defaultMode === 'system' ? 'light' : options.defaultMode;
|
|
330
|
+
if (!options.includeDemo) {
|
|
331
|
+
return `import { useState } from 'react'
|
|
332
|
+
import { ThemeProvider } from './theme/ThemeContext.tsx'
|
|
333
|
+
import { AppShell } from './components/layout/AppShell.tsx'
|
|
334
|
+
import { PageHeader } from './components/layout/PageHeader.tsx'
|
|
335
|
+
import { Panel } from './components/surfaces/Panel.tsx'
|
|
336
|
+
import { EmptyState } from './components/surfaces/EmptyState.tsx'
|
|
337
|
+
import { Button } from './components/controls/Button.tsx'
|
|
338
|
+
import { dashboardConfig } from './config/dashboard.config.ts'
|
|
339
|
+
|
|
340
|
+
export function App() {
|
|
341
|
+
const [currentPath, setCurrentPath] = useState('/dashboard')
|
|
342
|
+
|
|
343
|
+
return (
|
|
344
|
+
<ThemeProvider defaultTheme="${defaultTheme}" storageKey="${storageKey}">
|
|
345
|
+
<AppShell
|
|
346
|
+
config={dashboardConfig}
|
|
347
|
+
currentPath={currentPath}
|
|
348
|
+
onNavigate={(to) => setCurrentPath(to)}
|
|
349
|
+
headerActions={
|
|
350
|
+
<Button variant="primary" size="sm" shape="${options.buttonShape}">
|
|
351
|
+
Create New
|
|
352
|
+
</Button>
|
|
353
|
+
}
|
|
354
|
+
>
|
|
355
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-6">
|
|
356
|
+
<PageHeader
|
|
357
|
+
title="Overview"
|
|
358
|
+
description="${options.brandName} is ready for your product components."
|
|
359
|
+
actions={
|
|
360
|
+
<Button variant="outline" size="sm" shape="${options.buttonShape}">
|
|
361
|
+
Settings
|
|
362
|
+
</Button>
|
|
363
|
+
}
|
|
364
|
+
/>
|
|
365
|
+
|
|
366
|
+
<Panel title="Welcome to your new Dashboard" subtitle="Extracted from JobTrack's signature visual system.">
|
|
367
|
+
<EmptyState
|
|
368
|
+
title="No records yet"
|
|
369
|
+
description="Start building out your domain surfaces, forms, and metric views using the prebuilt components."
|
|
370
|
+
actionLabel="Add First Item"
|
|
371
|
+
onAction={() => alert('Add your business logic here!')}
|
|
372
|
+
/>
|
|
373
|
+
</Panel>
|
|
374
|
+
</div>
|
|
375
|
+
</AppShell>
|
|
376
|
+
</ThemeProvider>
|
|
377
|
+
)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export default App
|
|
381
|
+
`;
|
|
382
|
+
}
|
|
383
|
+
// With Demo Mode
|
|
384
|
+
return `import { useState } from 'react'
|
|
385
|
+
import { ThemeProvider } from './theme/ThemeContext.tsx'
|
|
386
|
+
import { AppShell } from './components/layout/AppShell.tsx'
|
|
387
|
+
import { HeroSurface } from './components/surfaces/HeroSurface.tsx'
|
|
388
|
+
import { PriorityCard } from './components/surfaces/PriorityCard.tsx'
|
|
389
|
+
import { Panel } from './components/surfaces/Panel.tsx'
|
|
390
|
+
import { QuietListCard } from './components/surfaces/QuietListCard.tsx'
|
|
391
|
+
import { MetricRow, type MetricItem } from './components/metrics/MetricRow.tsx'
|
|
392
|
+
import { StatusBadge } from './components/feedback/StatusBadge.tsx'
|
|
393
|
+
import { NotificationCenter } from './components/feedback/NotificationCenter.tsx'
|
|
394
|
+
import { Button } from './components/controls/Button.tsx'
|
|
395
|
+
import { dashboardConfig } from './config/dashboard.config.ts'
|
|
396
|
+
|
|
397
|
+
const DEMO_METRICS: MetricItem[] = ${options.preset === 'cafe'
|
|
398
|
+
? `[
|
|
399
|
+
{ id: '1', label: 'Active Orders', value: '24' },
|
|
400
|
+
{ id: '2', label: 'Daily Takings', value: '$3,420' },
|
|
401
|
+
{ id: '3', label: 'Bean Stock', value: '142kg' },
|
|
402
|
+
{ id: '4', label: 'Open Kitchen Tickets', value: '6' },
|
|
403
|
+
]`
|
|
404
|
+
: `[
|
|
405
|
+
{ id: '1', label: 'Active Projects', value: '18' },
|
|
406
|
+
{ id: '2', label: 'Sprint Velocity', value: '94%' },
|
|
407
|
+
{ id: '3', label: 'Open Tasks', value: '42' },
|
|
408
|
+
{ id: '4', label: 'Team Members', value: '28' },
|
|
409
|
+
]`}
|
|
410
|
+
|
|
411
|
+
export function App() {
|
|
412
|
+
const [currentPath, setCurrentPath] = useState('/dashboard')
|
|
413
|
+
|
|
414
|
+
return (
|
|
415
|
+
<ThemeProvider defaultTheme="${defaultTheme}" storageKey="${storageKey}">
|
|
416
|
+
<AppShell
|
|
417
|
+
config={dashboardConfig}
|
|
418
|
+
currentPath={currentPath}
|
|
419
|
+
onNavigate={(to) => setCurrentPath(to)}
|
|
420
|
+
headerActions={
|
|
421
|
+
<div className="flex items-center gap-2">
|
|
422
|
+
<NotificationCenter
|
|
423
|
+
notifications={[
|
|
424
|
+
{ id: '1', title: 'System initialized', message: 'Ready for operations', timestamp: 'Just now', read: false },
|
|
425
|
+
{ id: '2', title: 'Weekly summary', message: 'Report compiled', timestamp: '1h ago', read: true },
|
|
426
|
+
]}
|
|
427
|
+
/>
|
|
428
|
+
<Button variant="primary" size="sm" shape="${options.buttonShape}">
|
|
429
|
+
+ Action
|
|
430
|
+
</Button>
|
|
431
|
+
</div>
|
|
432
|
+
}
|
|
433
|
+
>
|
|
434
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 space-y-6">
|
|
435
|
+
<HeroSurface
|
|
436
|
+
greeting="${options.preset === 'cafe' ? 'Good Morning' : 'Welcome back'}"
|
|
437
|
+
title="${options.preset === 'cafe' ? 'Matteo Rossi' : 'Alex Morgan'}"
|
|
438
|
+
description="${options.preset === 'cafe' ? 'Artisan roast service is live. 6 tickets currently open in kitchen.' : 'Your team is tracking 18 active deliverables across 4 workstreams.'}"
|
|
439
|
+
statusContext={
|
|
440
|
+
<div className="flex gap-2">
|
|
441
|
+
<StatusBadge variant="info">${options.preset === 'cafe' ? 'Morning Rush' : 'Sprint 42'}</StatusBadge>
|
|
442
|
+
<StatusBadge variant="success">${options.preset === 'cafe' ? 'Optimal Flow' : '94% On Track'}</StatusBadge>
|
|
443
|
+
</div>
|
|
444
|
+
}
|
|
445
|
+
actions={
|
|
446
|
+
<div className="flex items-center gap-2">
|
|
447
|
+
<Button variant="secondary" size="sm" shape="${options.buttonShape}">
|
|
448
|
+
View Reports
|
|
449
|
+
</Button>
|
|
450
|
+
<Button variant="primary" size="sm" shape="${options.buttonShape}">
|
|
451
|
+
New Entry
|
|
452
|
+
</Button>
|
|
453
|
+
</div>
|
|
454
|
+
}
|
|
455
|
+
/>
|
|
456
|
+
|
|
457
|
+
<MetricRow metrics={DEMO_METRICS} />
|
|
458
|
+
|
|
459
|
+
<PriorityCard
|
|
460
|
+
heading="Next Best Action"
|
|
461
|
+
badgeText="Priority"
|
|
462
|
+
title="${options.preset === 'cafe' ? 'Calibrate Espresso Grinders' : 'Quarterly Executive Review'}"
|
|
463
|
+
description="${options.preset === 'cafe' ? 'Morning ambient humidity changed by 8%. Check extraction time for single origin batch.' : 'Review sprint burn-down chart and update customer satisfaction metrics.'}"
|
|
464
|
+
action={
|
|
465
|
+
<Button variant="primary" size="sm" shape="${options.buttonShape}">
|
|
466
|
+
${options.preset === 'cafe' ? 'Open Calibration Log' : 'Review Deck'}
|
|
467
|
+
</Button>
|
|
468
|
+
}
|
|
469
|
+
/>
|
|
470
|
+
|
|
471
|
+
<Panel
|
|
472
|
+
title="${options.preset === 'cafe' ? 'Live Kitchen Tickets' : 'Recent Project Deliverables'}"
|
|
473
|
+
action={<Button variant="ghost" size="sm">View All</Button>}
|
|
474
|
+
>
|
|
475
|
+
<QuietListCard
|
|
476
|
+
title="${options.preset === 'cafe' ? 'Kitchen Queue' : 'Deliverables'}"
|
|
477
|
+
items={[
|
|
478
|
+
{
|
|
479
|
+
id: '1',
|
|
480
|
+
title: '${options.preset === 'cafe' ? 'Table 4 • 2x Oat Flat White, 1x Croissant' : 'Core Architecture Redesign'}',
|
|
481
|
+
subtitle: '${options.preset === 'cafe' ? 'Barista Station • 3 mins ago' : 'Updated by Sarah • 12 mins ago'}',
|
|
482
|
+
avatarText: '${options.preset === 'cafe' ? 'T4' : 'CA'}',
|
|
483
|
+
statusBadge: <StatusBadge variant="info" dot>${options.preset === 'cafe' ? 'Brewing' : 'In Progress'}</StatusBadge>,
|
|
484
|
+
action: <Button variant="outline" size="sm" shape="${options.buttonShape}">Details</Button>,
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
id: '2',
|
|
488
|
+
title: '${options.preset === 'cafe' ? 'Takeaway • 1x Cold Brew, 1x Bag Colombia' : 'Auth Migration to Passkeys'}',
|
|
489
|
+
subtitle: '${options.preset === 'cafe' ? 'Front Counter • 8 mins ago' : 'Reviewed by David • 1h ago'}',
|
|
490
|
+
avatarText: '${options.preset === 'cafe' ? 'TA' : 'AM'}',
|
|
491
|
+
statusBadge: <StatusBadge variant="success" dot>${options.preset === 'cafe' ? 'Ready' : 'Completed'}</StatusBadge>,
|
|
492
|
+
action: <Button variant="outline" size="sm" shape="${options.buttonShape}">Details</Button>,
|
|
493
|
+
},
|
|
494
|
+
]}
|
|
495
|
+
/>
|
|
496
|
+
</Panel>
|
|
497
|
+
</div>
|
|
498
|
+
</AppShell>
|
|
499
|
+
</ThemeProvider>
|
|
500
|
+
)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
export default App
|
|
504
|
+
`;
|
|
505
|
+
}
|
|
506
|
+
export function buildPackageJson(options) {
|
|
507
|
+
const pkgName = options.projectName.split(/[\\/]/).filter(Boolean).pop() || options.projectName;
|
|
508
|
+
return JSON.stringify({
|
|
509
|
+
name: pkgName,
|
|
510
|
+
private: true,
|
|
511
|
+
version: '0.1.0',
|
|
512
|
+
type: 'module',
|
|
513
|
+
scripts: {
|
|
514
|
+
dev: 'vite',
|
|
515
|
+
build: 'tsc -b && vite build',
|
|
516
|
+
preview: 'vite preview',
|
|
517
|
+
test: 'node --test',
|
|
518
|
+
},
|
|
519
|
+
dependencies: {
|
|
520
|
+
'@tailwindcss/vite': '^4.3.3',
|
|
521
|
+
react: '^19.2.8',
|
|
522
|
+
'react-dom': '^19.2.8',
|
|
523
|
+
tailwindcss: '^4.3.3',
|
|
524
|
+
},
|
|
525
|
+
devDependencies: {
|
|
526
|
+
'@types/node': '^24.13.3',
|
|
527
|
+
'@types/react': '^19.2.18',
|
|
528
|
+
'@types/react-dom': '^19.2.4',
|
|
529
|
+
'@vitejs/plugin-react': '^6.1.0',
|
|
530
|
+
typescript: '~6.0.2',
|
|
531
|
+
vite: '^8.2.2',
|
|
532
|
+
},
|
|
533
|
+
}, null, 2);
|
|
534
|
+
}
|
|
535
|
+
export function buildProjectReadme(options) {
|
|
536
|
+
return `# ${options.brandName}
|
|
537
|
+
|
|
538
|
+
Modern dashboard application built with React, TypeScript, and Tailwind CSS, based on the JobTrack design system.
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## Getting Started
|
|
543
|
+
|
|
544
|
+
### 1. Install Dependencies
|
|
545
|
+
\`\`\`bash
|
|
546
|
+
npm install
|
|
547
|
+
\`\`\`
|
|
548
|
+
|
|
549
|
+
### 2. Run Development Server
|
|
550
|
+
\`\`\`bash
|
|
551
|
+
npm run dev
|
|
552
|
+
\`\`\`
|
|
553
|
+
|
|
554
|
+
### 3. Build for Production
|
|
555
|
+
\`\`\`bash
|
|
556
|
+
npm run build
|
|
557
|
+
\`\`\`
|
|
558
|
+
|
|
559
|
+
---
|
|
560
|
+
|
|
561
|
+
## Customizing Your Dashboard
|
|
562
|
+
|
|
563
|
+
All configuration is centralized in:
|
|
564
|
+
\`\`\`
|
|
565
|
+
src/config/dashboard.config.ts
|
|
566
|
+
\`\`\`
|
|
567
|
+
|
|
568
|
+
- **Navigation**: Update the \`navigation\` array with your product routes and icons.
|
|
569
|
+
- **Branding**: Change \`branding.name\` and \`branding.logoUrl\`.
|
|
570
|
+
- **Colors**: Customize \`colors.primary\` and \`colors.accent\`.
|
|
571
|
+
- **Shapes**: Toggle \`shapes.canvasRadius\` (\`'sm'\` | \`'md'\` | \`'lg'\`) and \`shapes.buttonShape\` (\`'pill'\` | \`'rounded'\` | \`'square'\`).
|
|
572
|
+
`;
|
|
573
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GeneratorOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Finds the source directory containing the generic dashboard starter kit components.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveStarterKitSrc(): string;
|
|
6
|
+
export declare function generateProject(options: GeneratorOptions): {
|
|
7
|
+
success: boolean;
|
|
8
|
+
targetDir: string;
|
|
9
|
+
};
|