From d2e29363f501929d637e5a9c6a5066180b149f7d Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 16 Jan 2026 23:45:29 +0000 Subject: [PATCH] Generated by Spark: Now just use said json data as seed data and load it all from database --- SEED_DATA_GUIDE.md | 199 +++++++++++ src/App.tsx | 8 +- src/components/ProjectDashboard.tsx | 3 + src/components/ProjectSettingsDesigner.tsx | 8 + src/components/atoms/SeedDataStatus.tsx | 60 ++++ src/components/atoms/index.ts | 1 + src/components/molecules/SeedDataManager.tsx | 97 +++++ src/components/molecules/index.ts | 1 + src/config/seed-data.json | 351 +++++++++++++++++++ src/hooks/data/use-seed-data.ts | 65 ++++ src/hooks/index.ts | 1 + 11 files changed, 793 insertions(+), 1 deletion(-) create mode 100644 SEED_DATA_GUIDE.md create mode 100644 src/components/atoms/SeedDataStatus.tsx create mode 100644 src/components/molecules/SeedDataManager.tsx create mode 100644 src/config/seed-data.json create mode 100644 src/hooks/data/use-seed-data.ts diff --git a/SEED_DATA_GUIDE.md b/SEED_DATA_GUIDE.md new file mode 100644 index 0000000..ae7712a --- /dev/null +++ b/SEED_DATA_GUIDE.md @@ -0,0 +1,199 @@ +# Seed Data System + +## Overview + +The application now includes a comprehensive seed data system that loads initial data from JSON configuration into the database (KV store) on first launch. + +## Features + +### 1. Automatic Data Loading +- Seed data is automatically loaded on application startup +- Only loads if data doesn't already exist (safe to re-run) +- All data is stored in the KV database for persistence + +### 2. Seed Data Management UI +Navigate to **Settings → Data** tab to access the Seed Data Manager with three options: + +- **Load Seed Data**: Populates database with initial data (only if not already loaded) +- **Reset to Defaults**: Overwrites all data with fresh seed data +- **Clear All Data**: Removes all data from the database (destructive action) + +### 3. Dashboard Integration +The Project Dashboard displays available seed data including: +- Number of pre-configured files +- Number of sample models +- Number of example components +- And more... + +## Seed Data Contents + +The system includes the following pre-configured data: + +### Files (`project-files`) +- Sample Next.js pages (page.tsx, layout.tsx) +- Material UI integration examples +- TypeScript configuration + +### Models (`project-models`) +- User model with authentication fields +- Post model with relationships +- Complete Prisma schema examples + +### Components (`project-components`) +- Button components with variants +- Card components with styling +- Reusable UI elements + +### Workflows (`project-workflows`) +- User registration flow +- Complete workflow with triggers and actions +- Visual workflow nodes and connections + +### Lambdas (`project-lambdas`) +- User data processing function +- HTTP trigger configuration +- Environment variable examples + +### Tests (`project-playwright-tests`, `project-storybook-stories`, `project-unit-tests`) +- E2E test examples +- Component story examples +- Unit test templates + +### Component Trees (`project-component-trees`) +- Application layout tree +- Nested component structures +- Material UI component hierarchies + +## Configuration + +### Adding New Seed Data + +Edit `/src/config/seed-data.json` to add or modify seed data: + +```json +{ + "project-files": [...], + "project-models": [...], + "your-custom-key": [...] +} +``` + +### Seed Data Schema + +Each data type follows the TypeScript interfaces defined in `/src/types/project.ts`: + +- `ProjectFile` +- `PrismaModel` +- `ComponentNode` +- `Workflow` +- `Lambda` +- `PlaywrightTest` +- `StorybookStory` +- `UnitTest` +- `ComponentTree` + +## API Reference + +### Hook: `useSeedData()` + +```typescript +import { useSeedData } from '@/hooks/data/use-seed-data' + +const { isLoaded, isLoading, loadSeedData, resetSeedData, clearAllData } = useSeedData() +``` + +**Returns:** +- `isLoaded`: Boolean indicating if seed data has been loaded +- `isLoading`: Boolean indicating if an operation is in progress +- `loadSeedData()`: Function to load seed data (if not already loaded) +- `resetSeedData()`: Function to reset all data to seed defaults +- `clearAllData()`: Function to clear all data from database + +### Direct KV API + +You can also interact with seed data directly: + +```typescript +// Get specific seed data +const files = await window.spark.kv.get('project-files') + +// Update seed data +await window.spark.kv.set('project-files', updatedFiles) + +// Delete seed data +await window.spark.kv.delete('project-files') +``` + +## Components + +### `` +Management UI for seed data operations (used in Settings → Data tab) + +### `` +Display component showing available seed data (used on Dashboard) + +## Best Practices + +1. **Always use the useKV hook** for reactive state management +2. **Use functional updates** when modifying arrays/objects to prevent data loss +3. **Test seed data** thoroughly before deploying +4. **Document custom seed data** for team members +5. **Keep seed data minimal** - only include essential examples + +## Example: Custom Seed Data + +```typescript +// 1. Add to seed-data.json +{ + "my-custom-data": [ + { "id": "1", "name": "Example 1" }, + { "id": "2", "name": "Example 2" } + ] +} + +// 2. Use in component +import { useKV } from '@github/spark/hooks' + +function MyComponent() { + const [data, setData] = useKV('my-custom-data', []) + + // Always use functional updates + const addItem = (newItem) => { + setData(current => [...current, newItem]) + } + + return ( +
+ {data.map(item => ( +
{item.name}
+ ))} +
+ ) +} +``` + +## Troubleshooting + +### Data Not Loading +- Check browser console for errors +- Verify seed-data.json is valid JSON +- Ensure data keys match KV store keys + +### Data Persisting After Clear +- Hard refresh the browser (Ctrl+Shift+R) +- Check for cached service workers (PWA) +- Verify clearAllData completed successfully + +### Type Errors +- Ensure seed data matches TypeScript interfaces +- Update types in `/src/types/project.ts` if needed +- Run type checking: `npm run type-check` + +## Future Enhancements + +Planned improvements: +- Import/export seed data as JSON files +- Version control for seed data +- Seed data migration system +- Seed data validation UI +- Partial seed data loading diff --git a/src/App.tsx b/src/App.tsx index bed2eec..c0cace0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, lazy, Suspense, useMemo } from 'react' +import { useState, lazy, Suspense, useMemo, useEffect } from 'react' import { Tabs, TabsContent } from '@/components/ui/tabs' import { AppHeader, PageHeader } from '@/components/organisms' import { LoadingFallback } from '@/components/molecules' @@ -6,6 +6,7 @@ import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/componen import { useProjectState } from '@/hooks/use-project-state' import { useFileOperations } from '@/hooks/use-file-operations' import { useKeyboardShortcuts } from '@/hooks/use-keyboard-shortcuts' +import { useSeedData } from '@/hooks/data/use-seed-data' import { getPageConfig, getEnabledPages, getPageShortcuts, resolveProps } from '@/config/page-loader' import { toast } from 'sonner' @@ -74,6 +75,7 @@ function App() { const fileOps = useFileOperations(files, setFiles) const { activeFileId, setActiveFileId, handleFileChange, handleFileAdd, handleFileClose } = fileOps + const { loadSeedData } = useSeedData() const [activeTab, setActiveTab] = useState('dashboard') const [searchOpen, setSearchOpen] = useState(false) @@ -81,6 +83,10 @@ function App() { const [lastSaved] = useState(Date.now()) const [errorCount] = useState(0) + useEffect(() => { + loadSeedData() + }, []) + const pageConfig = useMemo(() => getPageConfig(), []) const enabledPages = useMemo(() => getEnabledPages(featureToggles), [featureToggles]) const shortcuts = useMemo(() => getPageShortcuts(featureToggles), [featureToggles]) diff --git a/src/components/ProjectDashboard.tsx b/src/components/ProjectDashboard.tsx index 4b43870..96163ed 100644 --- a/src/components/ProjectDashboard.tsx +++ b/src/components/ProjectDashboard.tsx @@ -14,6 +14,7 @@ import { Warning } from '@phosphor-icons/react' import { ProjectFile, PrismaModel, ComponentNode, ThemeConfig, PlaywrightTest, StorybookStory, UnitTest, FlaskConfig } from '@/types/project' +import { SeedDataStatus } from '@/components/atoms' interface ProjectDashboardProps { files: ProjectFile[] @@ -131,6 +132,8 @@ export function ProjectDashboard({ /> + + Project Details diff --git a/src/components/ProjectSettingsDesigner.tsx b/src/components/ProjectSettingsDesigner.tsx index 0a4995c..f9a4419 100644 --- a/src/components/ProjectSettingsDesigner.tsx +++ b/src/components/ProjectSettingsDesigner.tsx @@ -11,6 +11,7 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, D import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Plus, Trash, Package, Cube, Code } from '@phosphor-icons/react' import { Badge } from '@/components/ui/badge' +import { SeedDataManager } from '@/components/molecules' interface ProjectSettingsDesignerProps { nextjsConfig: NextJsConfig @@ -134,6 +135,7 @@ export function ProjectSettingsDesigner({ Next.js Config NPM Packages Scripts + Data @@ -504,6 +506,12 @@ export function ProjectSettingsDesigner({ + + +
+ +
+
diff --git a/src/components/atoms/SeedDataStatus.tsx b/src/components/atoms/SeedDataStatus.tsx new file mode 100644 index 0000000..e6d7a4f --- /dev/null +++ b/src/components/atoms/SeedDataStatus.tsx @@ -0,0 +1,60 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' +import { Database, Check, X } from '@phosphor-icons/react' +import seedDataConfig from '@/config/seed-data.json' + +export function SeedDataStatus() { + const dataKeys = Object.keys(seedDataConfig) + + const getDataCount = (key: string): number => { + const data = seedDataConfig[key as keyof typeof seedDataConfig] + return Array.isArray(data) ? data.length : 0 + } + + const getLabelForKey = (key: string): string => { + const labels: Record = { + 'project-files': 'Files', + 'project-models': 'Models', + 'project-components': 'Components', + 'project-workflows': 'Workflows', + 'project-lambdas': 'Lambdas', + 'project-playwright-tests': 'Playwright Tests', + 'project-storybook-stories': 'Storybook Stories', + 'project-unit-tests': 'Unit Tests', + 'project-component-trees': 'Component Trees', + } + return labels[key] || key + } + + return ( + + + + + Seed Data Available + + + Pre-configured data ready to load from database + + + +
+ {dataKeys.map((key) => { + const count = getDataCount(key) + return ( +
+ {getLabelForKey(key)} + + {count} + +
+ ) + })} +
+
+
+ ) +} diff --git a/src/components/atoms/index.ts b/src/components/atoms/index.ts index 6260868..4bcd5e6 100644 --- a/src/components/atoms/index.ts +++ b/src/components/atoms/index.ts @@ -8,3 +8,4 @@ export { EmptyStateIcon } from './EmptyStateIcon' export { TreeIcon } from './TreeIcon' export { FileIcon } from './FileIcon' export { ActionIcon } from './ActionIcon' +export { SeedDataStatus } from './SeedDataStatus' diff --git a/src/components/molecules/SeedDataManager.tsx b/src/components/molecules/SeedDataManager.tsx new file mode 100644 index 0000000..6d4d057 --- /dev/null +++ b/src/components/molecules/SeedDataManager.tsx @@ -0,0 +1,97 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Alert, AlertDescription } from '@/components/ui/alert' +import { useSeedData } from '@/hooks/data/use-seed-data' +import { Database, ArrowClockwise, Trash, CheckCircle, CircleNotch } from '@phosphor-icons/react' + +export function SeedDataManager() { + const { isLoaded, isLoading, loadSeedData, resetSeedData, clearAllData } = useSeedData() + + return ( + + + + + Seed Data Management + + + Load, reset, or clear application seed data from the database + + + + {isLoaded && ( + + + + Seed data is loaded and available + + + )} + +
+
+ + + + + +
+ +
+

Load Seed Data: Populates database with initial data if not already loaded

+

Reset to Defaults: Overwrites all data with fresh seed data

+

Clear All Data: Removes all data from the database (destructive action)

+
+
+
+
+ ) +} diff --git a/src/components/molecules/index.ts b/src/components/molecules/index.ts index 5abf5cf..5dc9740 100644 --- a/src/components/molecules/index.ts +++ b/src/components/molecules/index.ts @@ -13,6 +13,7 @@ export { NavigationGroupHeader } from './NavigationGroupHeader' export { NavigationItem } from './NavigationItem' export { PageHeaderContent } from './PageHeaderContent' export { SaveIndicator } from './SaveIndicator' +export { SeedDataManager } from './SeedDataManager' export { StatCard } from './StatCard' export { ToolbarButton } from './ToolbarButton' export { TreeCard } from './TreeCard' diff --git a/src/config/seed-data.json b/src/config/seed-data.json new file mode 100644 index 0000000..e9f7ba2 --- /dev/null +++ b/src/config/seed-data.json @@ -0,0 +1,351 @@ +{ + "project-files": [ + { + "id": "file-1", + "name": "page.tsx", + "path": "/src/app/page.tsx", + "content": "'use client'\n\nimport { ThemeProvider } from '@mui/material/styles'\nimport CssBaseline from '@mui/material/CssBaseline'\nimport { theme } from '@/theme'\nimport { Box, Typography, Button } from '@mui/material'\n\nexport default function Home() {\n return (\n \n \n \n \n Welcome to Your App\n \n \n \n \n )\n}", + "language": "typescript" + }, + { + "id": "file-2", + "name": "layout.tsx", + "path": "/src/app/layout.tsx", + "content": "export const metadata = {\n title: 'My Next.js App',\n description: 'Generated with CodeForge',\n}\n\nexport default function RootLayout({\n children,\n}: {\n children: React.ReactNode\n}) {\n return (\n \n {children}\n \n )\n}", + "language": "typescript" + } + ], + "project-models": [ + { + "id": "model-1", + "name": "User", + "fields": [ + { + "id": "field-1", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-2", + "name": "email", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-3", + "name": "name", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-4", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-2", + "name": "Post", + "fields": [ + { + "id": "field-5", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-6", + "name": "title", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-7", + "name": "content", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-8", + "name": "published", + "type": "Boolean", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "false" + }, + { + "id": "field-9", + "name": "authorId", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "User" + } + ] + } + ], + "project-components": [ + { + "id": "comp-1", + "type": "Button", + "name": "PrimaryButton", + "props": { + "variant": "contained", + "color": "primary" + }, + "children": [] + }, + { + "id": "comp-2", + "type": "Card", + "name": "UserCard", + "props": { + "elevation": 2 + }, + "children": [ + { + "id": "comp-3", + "type": "CardContent", + "name": "CardContent", + "props": {}, + "children": [] + } + ] + } + ], + "project-workflows": [ + { + "id": "workflow-1", + "name": "User Registration Flow", + "description": "Complete user registration and onboarding workflow", + "nodes": [ + { + "id": "node-1", + "type": "trigger", + "name": "Form Submit", + "position": { "x": 100, "y": 100 }, + "data": { + "label": "Registration Form Submitted" + }, + "config": { + "triggerType": "event" + } + }, + { + "id": "node-2", + "type": "action", + "name": "Validate Input", + "position": { "x": 300, "y": 100 }, + "data": { + "label": "Validate User Data" + } + }, + { + "id": "node-3", + "type": "database", + "name": "Create User", + "position": { "x": 500, "y": 100 }, + "data": { + "label": "Insert User Record" + }, + "config": { + "databaseQuery": "INSERT INTO users" + } + } + ], + "connections": [ + { + "id": "conn-1", + "source": "node-1", + "target": "node-2" + }, + { + "id": "conn-2", + "source": "node-2", + "target": "node-3" + } + ], + "isActive": true, + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ], + "project-lambdas": [ + { + "id": "lambda-1", + "name": "processUserData", + "description": "Process and transform user data", + "code": "export async function handler(event) {\n const { userId, data } = event;\n // Process user data\n return {\n statusCode: 200,\n body: JSON.stringify({ success: true })\n };\n}", + "language": "typescript", + "runtime": "nodejs20.x", + "handler": "index.handler", + "timeout": 30, + "memory": 256, + "environment": { + "NODE_ENV": "production" + }, + "triggers": [ + { + "id": "trigger-1", + "type": "http", + "config": { + "method": "POST", + "path": "/api/process-user" + } + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ], + "project-playwright-tests": [ + { + "id": "test-1", + "name": "User Login Flow", + "description": "Test user authentication flow", + "pageUrl": "/login", + "steps": [ + { + "id": "step-1", + "action": "navigate", + "value": "/login" + }, + { + "id": "step-2", + "action": "fill", + "selector": "#email", + "value": "test@example.com" + }, + { + "id": "step-3", + "action": "fill", + "selector": "#password", + "value": "password123" + }, + { + "id": "step-4", + "action": "click", + "selector": "button[type='submit']" + }, + { + "id": "step-5", + "action": "expect", + "selector": ".dashboard", + "assertion": "toBeVisible" + } + ] + } + ], + "project-storybook-stories": [ + { + "id": "story-1", + "componentName": "Button", + "storyName": "Primary", + "args": { + "variant": "primary", + "children": "Click me", + "disabled": false + }, + "description": "Primary button variant", + "category": "Components" + }, + { + "id": "story-2", + "componentName": "Card", + "storyName": "Default", + "args": { + "title": "Card Title", + "description": "Card description", + "elevation": 2 + }, + "description": "Default card component", + "category": "Layout" + } + ], + "project-unit-tests": [ + { + "id": "test-1", + "name": "Button Component Tests", + "description": "Unit tests for Button component", + "testType": "component", + "targetFile": "/src/components/Button.tsx", + "testCases": [ + { + "id": "case-1", + "description": "renders with correct text", + "assertions": [ + "expect(screen.getByText('Click me')).toBeInTheDocument()" + ] + }, + { + "id": "case-2", + "description": "calls onClick handler when clicked", + "assertions": [ + "expect(handleClick).toHaveBeenCalledTimes(1)" + ] + } + ] + } + ], + "project-component-trees": [ + { + "id": "tree-1", + "name": "Main App Layout", + "description": "Primary application layout tree", + "rootNodes": [ + { + "id": "root-1", + "type": "Container", + "name": "AppContainer", + "props": { + "maxWidth": "xl" + }, + "children": [ + { + "id": "header-1", + "type": "AppBar", + "name": "Header", + "props": { + "position": "sticky" + }, + "children": [] + }, + { + "id": "main-1", + "type": "Box", + "name": "MainContent", + "props": { + "sx": { "py": 4 } + }, + "children": [] + } + ] + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ] +} diff --git a/src/hooks/data/use-seed-data.ts b/src/hooks/data/use-seed-data.ts new file mode 100644 index 0000000..b129c5e --- /dev/null +++ b/src/hooks/data/use-seed-data.ts @@ -0,0 +1,65 @@ +import { useEffect, useState } from 'react' +import seedDataConfig from '@/config/seed-data.json' + +export function useSeedData() { + const [isLoaded, setIsLoaded] = useState(false) + const [isLoading, setIsLoading] = useState(false) + + const loadSeedData = async () => { + if (isLoading || isLoaded) return + + setIsLoading(true) + try { + const keys = await window.spark.kv.keys() + + for (const [key, value] of Object.entries(seedDataConfig)) { + if (!keys.includes(key)) { + await window.spark.kv.set(key, value) + } + } + + setIsLoaded(true) + } catch (error) { + console.error('Failed to load seed data:', error) + } finally { + setIsLoading(false) + } + } + + const resetSeedData = async () => { + setIsLoading(true) + try { + for (const [key, value] of Object.entries(seedDataConfig)) { + await window.spark.kv.set(key, value) + } + setIsLoaded(true) + } catch (error) { + console.error('Failed to reset seed data:', error) + } finally { + setIsLoading(false) + } + } + + const clearAllData = async () => { + setIsLoading(true) + try { + const keys = await window.spark.kv.keys() + for (const key of keys) { + await window.spark.kv.delete(key) + } + setIsLoaded(false) + } catch (error) { + console.error('Failed to clear data:', error) + } finally { + setIsLoading(false) + } + } + + return { + isLoaded, + isLoading, + loadSeedData, + resetSeedData, + clearAllData, + } +} diff --git a/src/hooks/index.ts b/src/hooks/index.ts index ac74174..27c97a2 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -12,3 +12,4 @@ export * from './config/use-feature-flags' export * from './ai/use-ai-generation' +export * from './data/use-seed-data'