From 55114937a7c354856830700317df462116ea9833 Mon Sep 17 00:00:00 2001
From: johndoe6345789
Date: Sat, 17 Jan 2026 12:30:47 +0000
Subject: [PATCH] Generated by Spark: Load more of UI from JSON declarations
and break up large components into atomic and create hooks as needed
---
src/components/ProjectDashboard.tsx | 230 ++++++------------------
src/components/atoms/CompletionCard.tsx | 38 ++++
src/components/atoms/DetailRow.tsx | 20 +++
src/components/atoms/TipsCard.tsx | 28 +++
src/components/atoms/index.ts | 5 +-
src/hooks/ui/index.ts | 2 +
src/hooks/ui/use-dashboard-metrics.ts | 94 ++++++++++
src/hooks/ui/use-dashboard-tips.ts | 49 +++++
8 files changed, 290 insertions(+), 176 deletions(-)
create mode 100644 src/components/atoms/CompletionCard.tsx
create mode 100644 src/components/atoms/DetailRow.tsx
create mode 100644 src/components/atoms/TipsCard.tsx
create mode 100644 src/hooks/ui/use-dashboard-metrics.ts
create mode 100644 src/hooks/ui/use-dashboard-tips.ts
diff --git a/src/components/ProjectDashboard.tsx b/src/components/ProjectDashboard.tsx
index 36ac56e..78aa6ec 100644
--- a/src/components/ProjectDashboard.tsx
+++ b/src/components/ProjectDashboard.tsx
@@ -1,6 +1,4 @@
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
-import { Badge } from '@/components/ui/badge'
-import { Progress } from '@/components/ui/progress'
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import {
Code,
Database,
@@ -10,12 +8,13 @@ import {
Play,
Cube,
FileText,
- CheckCircle,
- Warning
} from '@phosphor-icons/react'
import { ProjectFile, PrismaModel, ComponentNode, ThemeConfig, PlaywrightTest, StorybookStory, UnitTest, FlaskConfig, NextJsConfig } from '@/types/project'
-import { SeedDataStatus } from '@/components/atoms'
+import { SeedDataStatus, DetailRow, CompletionCard, TipsCard } from '@/components/atoms'
import { GitHubBuildStatus } from '@/components/molecules/GitHubBuildStatus'
+import { StatCard } from '@/components/molecules'
+import { useDashboardMetrics } from '@/hooks/ui/use-dashboard-metrics'
+import { useDashboardTips } from '@/hooks/ui/use-dashboard-tips'
interface ProjectDashboardProps {
files: ProjectFile[]
@@ -29,29 +28,36 @@ interface ProjectDashboardProps {
nextjsConfig: NextJsConfig
}
-export function ProjectDashboard({
- files,
- models,
- components,
- theme,
- playwrightTests,
- storybookStories,
- unitTests,
- flaskConfig,
- nextjsConfig,
-}: ProjectDashboardProps) {
- const totalFiles = files.length
- const totalModels = models.length
- const totalComponents = components.length
- const totalThemeVariants = theme?.variants?.length || 0
- const totalEndpoints = flaskConfig.blueprints.reduce((acc, bp) => acc + bp.endpoints.length, 0)
- const totalTests = playwrightTests.length + storybookStories.length + unitTests.length
+export function ProjectDashboard(props: ProjectDashboardProps) {
+ const {
+ files,
+ models,
+ components,
+ theme,
+ playwrightTests,
+ storybookStories,
+ unitTests,
+ flaskConfig,
+ nextjsConfig,
+ } = props
- const completionScore = calculateCompletionScore({
- files: totalFiles,
- models: totalModels,
- components: totalComponents,
- tests: totalTests,
+ const metrics = useDashboardMetrics({
+ files,
+ models,
+ components,
+ theme,
+ playwrightTests,
+ storybookStories,
+ unitTests,
+ flaskConfig,
+ })
+
+ const tips = useDashboardTips({
+ totalFiles: metrics.totalFiles,
+ totalModels: metrics.totalModels,
+ totalComponents: metrics.totalComponents,
+ totalThemeVariants: metrics.totalThemeVariants,
+ totalTests: metrics.totalTests,
})
return (
@@ -63,74 +69,58 @@ export function ProjectDashboard({
-
-
-
-
- Project Completeness
-
- Overall progress of your application
-
-
-
- {completionScore}%
- = 70 ? 'default' : 'secondary'} className="text-sm">
- {completionScore >= 70 ? 'Ready to Export' : 'In Progress'}
-
-
-
-
- {getCompletionMessage(completionScore)}
-
-
-
+
}
title="Code Files"
- value={totalFiles}
- description={`${totalFiles} file${totalFiles !== 1 ? 's' : ''} in your project`}
+ value={metrics.totalFiles}
+ description={`${metrics.totalFiles} file${metrics.totalFiles !== 1 ? 's' : ''} in your project`}
color="text-blue-500"
/>
}
title="Database Models"
- value={totalModels}
- description={`${totalModels} Prisma model${totalModels !== 1 ? 's' : ''} defined`}
+ value={metrics.totalModels}
+ description={`${metrics.totalModels} Prisma model${metrics.totalModels !== 1 ? 's' : ''} defined`}
color="text-purple-500"
/>
}
title="Components"
- value={totalComponents}
- description={`${totalComponents} component${totalComponents !== 1 ? 's' : ''} in tree`}
+ value={metrics.totalComponents}
+ description={`${metrics.totalComponents} component${metrics.totalComponents !== 1 ? 's' : ''} in tree`}
color="text-green-500"
/>
}
title="Theme Variants"
- value={totalThemeVariants}
- description={`${totalThemeVariants} theme${totalThemeVariants !== 1 ? 's' : ''} configured`}
+ value={metrics.totalThemeVariants}
+ description={`${metrics.totalThemeVariants} theme${metrics.totalThemeVariants !== 1 ? 's' : ''} configured`}
color="text-pink-500"
/>
}
title="API Endpoints"
- value={totalEndpoints}
- description={`${totalEndpoints} Flask endpoint${totalEndpoints !== 1 ? 's' : ''}`}
+ value={metrics.totalEndpoints}
+ description={`${metrics.totalEndpoints} Flask endpoint${metrics.totalEndpoints !== 1 ? 's' : ''}`}
color="text-orange-500"
/>
}
title="Tests"
- value={totalTests}
- description={`${totalTests} test${totalTests !== 1 ? 's' : ''} written`}
+ value={metrics.totalTests}
+ description={`${metrics.totalTests} test${metrics.totalTests !== 1 ? 's' : ''} written`}
color="text-cyan-500"
/>
@@ -152,135 +142,27 @@ export function ProjectDashboard({
}
label="Playwright Tests"
- value={playwrightTests.length}
+ value={metrics.playwrightCount}
/>
}
label="Storybook Stories"
- value={storybookStories.length}
+ value={metrics.storybookCount}
/>
}
label="Unit Tests"
- value={unitTests.length}
+ value={metrics.unitTestCount}
/>
}
label="Flask Blueprints"
- value={flaskConfig.blueprints.length}
+ value={metrics.blueprintCount}
/>
- {(totalModels === 0 || totalFiles === 0) && (
-
-
-
-
- Quick Tips
-
-
-
- {totalFiles === 0 && (
- • Start by creating some code files in the Code Editor tab
- )}
- {totalModels === 0 && (
- • Define your data models in the Models tab to set up your database
- )}
- {totalComponents === 0 && (
- • Build your UI structure in the Components tab
- )}
- {totalThemeVariants <= 1 && (
- • Create additional theme variants (dark mode) in the Styling tab
- )}
- {totalTests === 0 && (
- • Add tests for better code quality and reliability
- )}
-
-
- )}
+
)
}
-
-function StatCard({
- icon,
- title,
- value,
- description,
- color
-}: {
- icon: React.ReactNode
- title: string
- value: number
- description: string
- color: string
-}) {
- return (
-
-
-
-
-
{title}
-
{value}
-
{description}
-
-
{icon}
-
-
-
- )
-}
-
-function DetailRow({
- icon,
- label,
- value
-}: {
- icon: React.ReactNode
- label: string
- value: number
-}) {
- return (
-
-
- {icon}
- {label}
-
-
{value}
-
- )
-}
-
-function calculateCompletionScore(data: {
- files: number
- models: number
- components: number
- tests: number
-}): number {
- const weights = {
- files: 25,
- models: 25,
- components: 25,
- tests: 25,
- }
-
- const scores = {
- files: Math.min(data.files / 5, 1) * weights.files,
- models: Math.min(data.models / 3, 1) * weights.models,
- components: Math.min(data.components / 5, 1) * weights.components,
- tests: Math.min(data.tests / 5, 1) * weights.tests,
- }
-
- return Math.round(
- scores.files + scores.models + scores.components + scores.tests
- )
-}
-
-function getCompletionMessage(score: number): string {
- if (score >= 90) return 'Excellent! Your project is comprehensive and ready to deploy.'
- if (score >= 70) return 'Great progress! Your project has most essential features.'
- if (score >= 50) return 'Good start! Keep adding more features and tests.'
- if (score >= 30) return 'Getting there! Add more components and models.'
- return 'Just starting! Begin by creating models and components.'
-}
diff --git a/src/components/atoms/CompletionCard.tsx b/src/components/atoms/CompletionCard.tsx
new file mode 100644
index 0000000..98ebed8
--- /dev/null
+++ b/src/components/atoms/CompletionCard.tsx
@@ -0,0 +1,38 @@
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
+import { Badge } from '@/components/ui/badge'
+import { Progress } from '@/components/ui/progress'
+import { CheckCircle } from '@phosphor-icons/react'
+
+interface CompletionCardProps {
+ completionScore: number
+ completionMessage: string
+ isReadyToExport: boolean
+}
+
+export function CompletionCard({
+ completionScore,
+ completionMessage,
+ isReadyToExport
+}: CompletionCardProps) {
+ return (
+
+
+
+
+ Project Completeness
+
+ Overall progress of your application
+
+
+
+ {completionScore}%
+
+ {isReadyToExport ? 'Ready to Export' : 'In Progress'}
+
+
+
+ {completionMessage}
+
+
+ )
+}
diff --git a/src/components/atoms/DetailRow.tsx b/src/components/atoms/DetailRow.tsx
new file mode 100644
index 0000000..7d46e46
--- /dev/null
+++ b/src/components/atoms/DetailRow.tsx
@@ -0,0 +1,20 @@
+import { Card, CardContent } from '@/components/ui/card'
+import { Badge } from '@/components/ui/badge'
+
+interface DetailRowProps {
+ icon: React.ReactNode
+ label: string
+ value: number
+}
+
+export function DetailRow({ icon, label, value }: DetailRowProps) {
+ return (
+
+
+ {icon}
+ {label}
+
+
{value}
+
+ )
+}
diff --git a/src/components/atoms/TipsCard.tsx b/src/components/atoms/TipsCard.tsx
new file mode 100644
index 0000000..eaa6794
--- /dev/null
+++ b/src/components/atoms/TipsCard.tsx
@@ -0,0 +1,28 @@
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+import { Warning } from '@phosphor-icons/react'
+
+interface TipsCardProps {
+ tips: Array<{ message: string; show: boolean }>
+}
+
+export function TipsCard({ tips }: TipsCardProps) {
+ const visibleTips = tips.filter(tip => tip.show)
+
+ if (visibleTips.length === 0) return null
+
+ return (
+
+
+
+
+ Quick Tips
+
+
+
+ {visibleTips.map((tip, index) => (
+ • {tip.message}
+ ))}
+
+
+ )
+}
diff --git a/src/components/atoms/index.ts b/src/components/atoms/index.ts
index 500a917..c521c4b 100644
--- a/src/components/atoms/index.ts
+++ b/src/components/atoms/index.ts
@@ -22,5 +22,6 @@ export { BindingIndicator } from './BindingIndicator'
export { StatCard } from './StatCard'
export { LoadingState } from './LoadingState'
export { EmptyState } from './EmptyState'
-
-
+export { DetailRow } from './DetailRow'
+export { CompletionCard } from './CompletionCard'
+export { TipsCard } from './TipsCard'
diff --git a/src/hooks/ui/index.ts b/src/hooks/ui/index.ts
index 6944a71..2e34ed1 100644
--- a/src/hooks/ui/index.ts
+++ b/src/hooks/ui/index.ts
@@ -2,3 +2,5 @@ export { useDataBinding } from './use-data-binding'
export { useEventHandlers } from './use-event-handlers'
export { useSchemaLoader } from './use-schema-loader'
export { useComponentRegistry } from './use-component-registry'
+export { useDashboardMetrics } from './use-dashboard-metrics'
+export { useDashboardTips } from './use-dashboard-tips'
diff --git a/src/hooks/ui/use-dashboard-metrics.ts b/src/hooks/ui/use-dashboard-metrics.ts
new file mode 100644
index 0000000..bcd4ac7
--- /dev/null
+++ b/src/hooks/ui/use-dashboard-metrics.ts
@@ -0,0 +1,94 @@
+import { useMemo } from 'react'
+import { ProjectFile, PrismaModel, ComponentNode, ThemeConfig, PlaywrightTest, StorybookStory, UnitTest, FlaskConfig } from '@/types/project'
+
+interface DashboardMetrics {
+ totalFiles: number
+ totalModels: number
+ totalComponents: number
+ totalThemeVariants: number
+ totalEndpoints: number
+ totalTests: number
+ playwrightCount: number
+ storybookCount: number
+ unitTestCount: number
+ blueprintCount: number
+ completionScore: number
+ completionMessage: string
+ isReadyToExport: boolean
+}
+
+interface UseDashboardMetricsProps {
+ files: ProjectFile[]
+ models: PrismaModel[]
+ components: ComponentNode[]
+ theme: ThemeConfig
+ playwrightTests: PlaywrightTest[]
+ storybookStories: StorybookStory[]
+ unitTests: UnitTest[]
+ flaskConfig: FlaskConfig
+}
+
+function calculateCompletionScore(metrics: {
+ files: number
+ models: number
+ components: number
+ tests: number
+}): number {
+ let score = 0
+ if (metrics.files > 0) score += 25
+ if (metrics.models > 0) score += 25
+ if (metrics.components > 0) score += 25
+ if (metrics.tests > 0) score += 25
+ return score
+}
+
+function getCompletionMessage(score: number): string {
+ if (score >= 90) return 'Excellent! Your project is complete and ready for export.'
+ if (score >= 70) return 'Great progress! Add a few more tests to reach 100%.'
+ if (score >= 50) return 'Making good progress. Keep adding features and tests.'
+ if (score >= 25) return 'Good start! Continue building your application.'
+ return 'Just getting started. Create files, models, and components to begin.'
+}
+
+export function useDashboardMetrics({
+ files,
+ models,
+ components,
+ theme,
+ playwrightTests,
+ storybookStories,
+ unitTests,
+ flaskConfig,
+}: UseDashboardMetricsProps): DashboardMetrics {
+ return useMemo(() => {
+ const totalFiles = files.length
+ const totalModels = models.length
+ const totalComponents = components.length
+ const totalThemeVariants = theme?.variants?.length || 0
+ const totalEndpoints = flaskConfig.blueprints.reduce((acc, bp) => acc + bp.endpoints.length, 0)
+ const totalTests = playwrightTests.length + storybookStories.length + unitTests.length
+
+ const completionScore = calculateCompletionScore({
+ files: totalFiles,
+ models: totalModels,
+ components: totalComponents,
+ tests: totalTests,
+ })
+
+ return {
+ totalFiles,
+ totalModels,
+ totalComponents,
+ totalThemeVariants,
+ totalEndpoints,
+ totalTests,
+ playwrightCount: playwrightTests.length,
+ storybookCount: storybookStories.length,
+ unitTestCount: unitTests.length,
+ blueprintCount: flaskConfig.blueprints.length,
+ completionScore,
+ completionMessage: getCompletionMessage(completionScore),
+ isReadyToExport: completionScore >= 70,
+ }
+ }, [files, models, components, theme, playwrightTests, storybookStories, unitTests, flaskConfig])
+}
diff --git a/src/hooks/ui/use-dashboard-tips.ts b/src/hooks/ui/use-dashboard-tips.ts
new file mode 100644
index 0000000..b1de091
--- /dev/null
+++ b/src/hooks/ui/use-dashboard-tips.ts
@@ -0,0 +1,49 @@
+import { useMemo } from 'react'
+
+interface DashboardTip {
+ message: string
+ show: boolean
+}
+
+interface UseDashboardTipsProps {
+ totalFiles: number
+ totalModels: number
+ totalComponents: number
+ totalThemeVariants: number
+ totalTests: number
+}
+
+export function useDashboardTips({
+ totalFiles,
+ totalModels,
+ totalComponents,
+ totalThemeVariants,
+ totalTests,
+}: UseDashboardTipsProps): DashboardTip[] {
+ return useMemo(() => {
+ const tips: DashboardTip[] = [
+ {
+ message: 'Start by creating some code files in the Code Editor tab',
+ show: totalFiles === 0,
+ },
+ {
+ message: 'Define your data models in the Models tab to set up your database',
+ show: totalModels === 0,
+ },
+ {
+ message: 'Build your UI structure in the Components tab',
+ show: totalComponents === 0,
+ },
+ {
+ message: 'Create additional theme variants (dark mode) in the Styling tab',
+ show: totalThemeVariants <= 1,
+ },
+ {
+ message: 'Add tests for better code quality and reliability',
+ show: totalTests === 0,
+ },
+ ]
+
+ return tips.filter(tip => tip.show)
+ }, [totalFiles, totalModels, totalComponents, totalThemeVariants, totalTests])
+}