From 90e3cb9c4d0d7fbc0526bbda3a62f7267c90e950 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 16 Jan 2026 23:57:29 +0000 Subject: [PATCH] Generated by Spark: Create seed data templates for different project types (e-commerce, blog, dashboard) --- src/App.tsx | 1 + src/components/TemplateExplorer.tsx | 198 +++++++ src/components/TemplateSelector.tsx | 173 ++++++ src/components/index.ts | 2 + src/config/pages.json | 11 +- src/config/seed-templates/README.md | 262 +++++++++ src/config/seed-templates/blog.json | 672 ++++++++++++++++++++++ src/config/seed-templates/dashboard.json | 617 ++++++++++++++++++++ src/config/seed-templates/e-commerce.json | 635 ++++++++++++++++++++ src/config/seed-templates/index.ts | 3 + src/hooks/data/index.ts | 2 + src/hooks/data/use-seed-templates.ts | 153 +++++ src/hooks/index.ts | 1 + 13 files changed, 2729 insertions(+), 1 deletion(-) create mode 100644 src/components/TemplateExplorer.tsx create mode 100644 src/components/TemplateSelector.tsx create mode 100644 src/config/seed-templates/README.md create mode 100644 src/config/seed-templates/blog.json create mode 100644 src/config/seed-templates/dashboard.json create mode 100644 src/config/seed-templates/e-commerce.json create mode 100644 src/config/seed-templates/index.ts create mode 100644 src/hooks/data/use-seed-templates.ts diff --git a/src/App.tsx b/src/App.tsx index c0cace0..0de10e2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -32,6 +32,7 @@ const componentMap: Record> = { PWASettings: lazy(() => import('@/components/PWASettings').then(m => ({ default: m.PWASettings }))), FaviconDesigner: lazy(() => import('@/components/FaviconDesigner').then(m => ({ default: m.FaviconDesigner }))), FeatureIdeaCloud: lazy(() => import('@/components/FeatureIdeaCloud').then(m => ({ default: m.FeatureIdeaCloud }))), + TemplateSelector: lazy(() => import('@/components/TemplateSelector').then(m => ({ default: m.TemplateSelector }))), } const GlobalSearch = lazy(() => import('@/components/GlobalSearch').then(m => ({ default: m.GlobalSearch }))) diff --git a/src/components/TemplateExplorer.tsx b/src/components/TemplateExplorer.tsx new file mode 100644 index 0000000..9d974d7 --- /dev/null +++ b/src/components/TemplateExplorer.tsx @@ -0,0 +1,198 @@ +import { useState } from 'react' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Badge } from '@/components/ui/badge' +import { useSeedTemplates } from '@/hooks/data/use-seed-templates' +import { Copy, Download } from '@phosphor-icons/react' +import { toast } from 'sonner' + +export function TemplateExplorer() { + const { templates } = useSeedTemplates() + const [selectedTemplate, setSelectedTemplate] = useState(templates[0]?.id || 'default') + + const currentTemplate = templates.find(t => t.id === selectedTemplate) + + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text) + toast.success('Copied to clipboard') + } + + const downloadJSON = () => { + if (!currentTemplate) return + + const dataStr = JSON.stringify(currentTemplate.data, null, 2) + const blob = new Blob([dataStr], { type: 'application/json' }) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = `${currentTemplate.id}-template.json` + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(url) + + toast.success('Template downloaded') + } + + const exportCurrentData = async () => { + const keys = await window.spark.kv.keys() + const data: Record = {} + + for (const key of keys) { + data[key] = await window.spark.kv.get(key) + } + + const dataStr = JSON.stringify(data, null, 2) + const blob = new Blob([dataStr], { type: 'application/json' }) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = 'current-project-data.json' + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + URL.revokeObjectURL(url) + + toast.success('Current project data exported') + } + + if (!currentTemplate) return null + + return ( +
+
+
+

Template Explorer

+

+ Browse and inspect template structures +

+
+ +
+ +
+
+ {templates.map((template) => ( + setSelectedTemplate(template.id)} + > + +
+ {template.icon} + {template.name} +
+
+
+ ))} +
+ + + +
+
+ {currentTemplate.icon} +
+ {currentTemplate.name} + {currentTemplate.description} +
+
+ +
+
+ + + + Overview + Structure + JSON + + + +
+

Features

+
+ {currentTemplate.features.map((feature, idx) => ( + + {feature} + + ))} +
+
+ +
+ {Object.entries(currentTemplate.data).map(([key, value]) => ( + + + {key.replace('project-', '')} + + {Array.isArray(value) ? `${value.length} items` : 'Configuration'} + + + + ))} +
+
+ + + + {Object.entries(currentTemplate.data).map(([key, value]) => ( +
+
+

{key}

+ + {Array.isArray(value) ? `${value.length} items` : 'object'} + +
+ {Array.isArray(value) && value.length > 0 && ( +
+ {value.slice(0, 3).map((item: any, idx: number) => ( +
+ • {item.name || item.title || item.id} +
+ ))} + {value.length > 3 && ( +
... and {value.length - 3} more
+ )} +
+ )} +
+ ))} +
+
+ + +
+ + +
+                      {JSON.stringify(currentTemplate.data, null, 2)}
+                    
+
+
+
+
+
+
+
+
+ ) +} diff --git a/src/components/TemplateSelector.tsx b/src/components/TemplateSelector.tsx new file mode 100644 index 0000000..1bd7882 --- /dev/null +++ b/src/components/TemplateSelector.tsx @@ -0,0 +1,173 @@ +import { useState } from 'react' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Badge } from '@/components/ui/badge' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { useSeedTemplates, type TemplateType } from '@/hooks/data/use-seed-templates' +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' +import { Alert, AlertDescription } from '@/components/ui/alert' +import { TemplateExplorer } from './TemplateExplorer' +import { toast } from 'sonner' +import { Download, Package, Plus, Trash } from '@phosphor-icons/react' + +export function TemplateSelector() { + const { templates, isLoading, clearAndLoadTemplate, mergeTemplate } = useSeedTemplates() + const [selectedTemplate, setSelectedTemplate] = useState(null) + const [showConfirmDialog, setShowConfirmDialog] = useState(false) + const [actionType, setActionType] = useState<'replace' | 'merge'>('replace') + + const handleSelectTemplate = (templateId: TemplateType, action: 'replace' | 'merge') => { + setSelectedTemplate(templateId) + setActionType(action) + setShowConfirmDialog(true) + } + + const handleConfirmLoad = async () => { + if (!selectedTemplate) return + + setShowConfirmDialog(false) + + const success = actionType === 'replace' + ? await clearAndLoadTemplate(selectedTemplate) + : await mergeTemplate(selectedTemplate) + + if (success) { + toast.success(`Template loaded successfully!`, { + description: `${actionType === 'replace' ? 'Replaced' : 'Merged'} with ${selectedTemplate} template` + }) + window.location.reload() + } else { + toast.error('Failed to load template', { + description: 'Please try again or check the console for errors' + }) + } + } + + return ( + <> + + + Load Templates + Explore Templates + + + +
+

Project Templates

+

+ Start your project with pre-configured templates including models, components, and workflows +

+
+ +
+ {templates.map((template) => ( + + +
+
+ {template.icon} +
+ {template.name} + + {template.description} + +
+
+
+
+ +
+ {template.features.map((feature, idx) => ( + + {feature} + + ))} +
+
+ + +
+
+
+ ))} +
+ + + + + Load Template: Replaces all existing data with the selected template. +
+ Merge: Adds template data to your existing project without removing current data. +
+
+
+ + + + +
+ + + + + + {actionType === 'replace' ? 'Replace Project Data?' : 'Merge Template Data?'} + + + {actionType === 'replace' ? ( + <> + This will delete all existing data and load the{' '} + {selectedTemplate} template. This action cannot be undone. + + ) : ( + <> + This will add the {selectedTemplate} template data to your + existing project without removing current data. + + )} + + + + + + + + + + ) +} diff --git a/src/components/index.ts b/src/components/index.ts index 0279920..7d51b0f 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,3 +1,5 @@ export * from './atoms' export * from './molecules' export * from './organisms' +export * from './TemplateSelector' +export * from './TemplateExplorer' diff --git a/src/config/pages.json b/src/config/pages.json index 8f78c20..7e9e5c2 100644 --- a/src/config/pages.json +++ b/src/config/pages.json @@ -253,13 +253,22 @@ "order": 19, "props": {} }, + { + "id": "templates", + "title": "Templates", + "icon": "Package", + "component": "TemplateSelector", + "enabled": true, + "order": 20, + "props": {} + }, { "id": "features", "title": "Features", "icon": "ToggleRight", "component": "FeatureToggleSettings", "enabled": true, - "order": 20, + "order": 21, "props": { "state": ["features:featureToggles"], "actions": ["onFeaturesChange:setFeatureToggles"] diff --git a/src/config/seed-templates/README.md b/src/config/seed-templates/README.md new file mode 100644 index 0000000..74d58ea --- /dev/null +++ b/src/config/seed-templates/README.md @@ -0,0 +1,262 @@ +# Seed Data Templates + +This directory contains pre-configured project templates that provide complete starting points for different application types. + +## Available Templates + +### 🚀 Default Project +**File:** `../seed-data.json` + +Basic starter template with common components and models for general-purpose applications. + +**Features:** +- Basic User and Post models +- Simple file structure +- User registration workflow +- Foundation components (Button, Card) +- Sample tests and stories + +**Best for:** Learning, prototyping, or building custom applications from scratch + +--- + +### 🛍️ E-Commerce Store +**File:** `e-commerce.json` + +Complete online store setup with product catalog, shopping cart, and order management. + +**Features:** +- **Models:** + - Product (with variants, pricing, inventory) + - Category (hierarchical) + - Order & OrderItem + - Customer (with addresses) + +- **Components:** + - ProductCard + - CartItem + - CheckoutSummary + +- **Workflows:** + - Order processing (payment → inventory → notifications) + +- **Lambda Functions:** + - calculateShipping + - processRefund + +- **Pages:** + - Product listing with Hero + - Shopping cart + - Checkout flow + +**Best for:** Online stores, marketplaces, retail platforms + +--- + +### 📝 Blog Platform +**File:** `blog.json` + +Content-focused blogging platform with author management, comments, and newsletter. + +**Features:** +- **Models:** + - Post (with SEO fields, read time, tags) + - Author (with bio, social links) + - Category + - Comment (with threading support) + - Newsletter subscribers + +- **Components:** + - PostCard (with metadata) + - AuthorCard + - CommentSection + +- **Workflows:** + - Post publishing (draft → SEO → review → publish → notify) + +- **Lambda Functions:** + - generateSEO (auto-generate meta tags) + - sendNewsletter + +- **Pages:** + - Blog grid with featured post + - Individual post view + - Author profile pages + +**Best for:** Blogs, magazines, content sites, news platforms + +--- + +### 📊 Analytics Dashboard +**File:** `dashboard.json` + +Data visualization dashboard with real-time metrics, user management, and reporting. + +**Features:** +- **Models:** + - Metric (time-series data with trends) + - User (roles, status, activity tracking) + - Activity (audit logging) + - Report (generated reports with filters) + - Alert (notification system) + +- **Components:** + - StatCard (with trends and sparklines) + - DataTable (sortable, filterable, exportable) + - ChartCard (multiple chart types) + +- **Workflows:** + - Alert processing (threshold → severity check → notifications) + +- **Lambda Functions:** + - aggregateMetrics (scheduled hourly) + - generateReport (on-demand) + +- **Pages:** + - Main dashboard with stats and charts + - Analytics page with date range picker + - User management with filters + +**Best for:** Admin panels, analytics platforms, SaaS dashboards, monitoring tools + +--- + +## Usage + +### Using the Template Selector UI + +1. Navigate to the **Templates** tab in the application +2. Browse available templates +3. Choose an action: + - **Load Template**: Replaces all existing data (⚠️ destructive) + - **Merge**: Adds template data to existing project + +### Programmatic Usage + +```typescript +import { useSeedTemplates } from '@/hooks/data/use-seed-templates' + +function MyComponent() { + const { templates, loadTemplate, clearAndLoadTemplate, mergeTemplate } = useSeedTemplates() + + // Load template (preserves existing data) + await loadTemplate('e-commerce') + + // Replace all data with template + await clearAndLoadTemplate('blog') + + // Merge template with existing data + await mergeTemplate('dashboard') +} +``` + +### Manual Loading + +```typescript +import ecommerceTemplate from '@/config/seed-templates/e-commerce.json' + +// Load each data type +for (const [key, value] of Object.entries(ecommerceTemplate)) { + await window.spark.kv.set(key, value) +} +``` + +--- + +## Template Structure + +Each template is a JSON file with the following keys: + +```json +{ + "project-files": [], // TypeScript/React files + "project-models": [], // Data models (Prisma-style) + "project-components": [], // UI component definitions + "project-component-trees": [], // Component hierarchies + "project-workflows": [], // Visual workflow definitions + "project-lambdas": [], // Serverless functions + "project-playwright-tests": [],// E2E tests + "project-storybook-stories": [],// Component stories + "project-unit-tests": [] // Unit test definitions +} +``` + +--- + +## Creating Custom Templates + +1. **Export current project data:** + ```typescript + const keys = await window.spark.kv.keys() + const data = {} + for (const key of keys) { + data[key] = await window.spark.kv.get(key) + } + console.log(JSON.stringify(data, null, 2)) + ``` + +2. **Create a new JSON file** in this directory + +3. **Add to the templates list** in `use-seed-templates.ts`: + ```typescript + import myTemplate from '@/config/seed-templates/my-template.json' + + const templates: Template[] = [ + // ...existing templates + { + id: 'my-template', + name: 'My Custom Template', + description: 'Description here', + icon: '🎨', + data: myTemplate, + features: ['Feature 1', 'Feature 2'] + } + ] + ``` + +--- + +## Best Practices + +### When to Load vs Merge + +- **Load (Replace):** Starting a new project, switching project types, or resetting to a clean state +- **Merge:** Adding features from another template, combining template elements, or expanding functionality + +### ID Conventions + +Use prefixed IDs to avoid conflicts: +- `file-ecom-1`, `file-blog-1`, `file-dash-1` +- `model-ecom-1`, `model-blog-1`, `model-dash-1` +- `comp-ecom-1`, `comp-blog-1`, `comp-dash-1` + +### Real-World Data + +Templates include realistic: +- Model field definitions +- Component configurations +- Workflow logic +- Function implementations + +This allows immediate testing and provides clear examples for customization. + +--- + +## Template Maintenance + +When updating templates: + +1. **Validate JSON** structure matches expected schema +2. **Test loading** in a fresh project +3. **Verify IDs** are unique within the template +4. **Check relationships** between models reference valid relations +5. **Update documentation** when adding new features + +--- + +## Support + +For questions or issues with templates: +- Check the main project documentation +- Review existing template structures +- Test in development before production use diff --git a/src/config/seed-templates/blog.json b/src/config/seed-templates/blog.json new file mode 100644 index 0000000..9ff0916 --- /dev/null +++ b/src/config/seed-templates/blog.json @@ -0,0 +1,672 @@ +{ + "project-files": [ + { + "id": "file-blog-1", + "name": "page.tsx", + "path": "/src/app/page.tsx", + "content": "'use client'\n\nimport { BlogGrid } from '@/components/BlogGrid'\nimport { FeaturedPost } from '@/components/FeaturedPost'\nimport { Container, Box } from '@mui/material'\n\nexport default function Home() {\n return (\n \n \n \n \n \n \n )\n}", + "language": "typescript" + }, + { + "id": "file-blog-2", + "name": "post.tsx", + "path": "/src/app/post/[slug]/page.tsx", + "content": "'use client'\n\nimport { PostContent } from '@/components/PostContent'\nimport { PostMeta } from '@/components/PostMeta'\nimport { RelatedPosts } from '@/components/RelatedPosts'\nimport { Container, Box } from '@mui/material'\n\nexport default function PostPage({ params }: { params: { slug: string } }) {\n return (\n \n \n \n \n \n \n \n )\n}", + "language": "typescript" + }, + { + "id": "file-blog-3", + "name": "author.tsx", + "path": "/src/app/author/[id]/page.tsx", + "content": "'use client'\n\nimport { AuthorProfile } from '@/components/AuthorProfile'\nimport { AuthorPosts } from '@/components/AuthorPosts'\nimport { Container } from '@mui/material'\n\nexport default function AuthorPage({ params }: { params: { id: string } }) {\n return (\n \n \n \n \n )\n}", + "language": "typescript" + } + ], + "project-models": [ + { + "id": "model-blog-1", + "name": "Post", + "fields": [ + { + "id": "field-blog-1", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-blog-2", + "name": "title", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-3", + "name": "slug", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-blog-4", + "name": "excerpt", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-5", + "name": "content", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-6", + "name": "featuredImage", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-7", + "name": "published", + "type": "Boolean", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "false" + }, + { + "id": "field-blog-8", + "name": "authorId", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "Author" + }, + { + "id": "field-blog-9", + "name": "categoryId", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false, + "relation": "Category" + }, + { + "id": "field-blog-10", + "name": "tags", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": true + }, + { + "id": "field-blog-11", + "name": "viewCount", + "type": "Int", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "0" + }, + { + "id": "field-blog-12", + "name": "readTime", + "type": "Int", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-13", + "name": "publishedAt", + "type": "DateTime", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-14", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + }, + { + "id": "field-blog-15", + "name": "updatedAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-blog-2", + "name": "Author", + "fields": [ + { + "id": "field-blog-16", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-blog-17", + "name": "name", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-18", + "name": "email", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-blog-19", + "name": "bio", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-20", + "name": "avatar", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-21", + "name": "website", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-22", + "name": "social", + "type": "Json", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-23", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-blog-3", + "name": "Category", + "fields": [ + { + "id": "field-blog-24", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-blog-25", + "name": "name", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-blog-26", + "name": "slug", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-blog-27", + "name": "description", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-28", + "name": "color", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + } + ] + }, + { + "id": "model-blog-4", + "name": "Comment", + "fields": [ + { + "id": "field-blog-29", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-blog-30", + "name": "postId", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "Post" + }, + { + "id": "field-blog-31", + "name": "authorName", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-32", + "name": "authorEmail", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-33", + "name": "content", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-34", + "name": "approved", + "type": "Boolean", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "false" + }, + { + "id": "field-blog-35", + "name": "parentId", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false, + "relation": "Comment" + }, + { + "id": "field-blog-36", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-blog-5", + "name": "Newsletter", + "fields": [ + { + "id": "field-blog-37", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-blog-38", + "name": "email", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-blog-39", + "name": "name", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-blog-40", + "name": "subscribed", + "type": "Boolean", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "true" + }, + { + "id": "field-blog-41", + "name": "subscribedAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + } + ], + "project-components": [ + { + "id": "comp-blog-1", + "type": "PostCard", + "name": "PostCard", + "props": { + "showExcerpt": true, + "showAuthor": true, + "showDate": true, + "showReadTime": true + }, + "children": [] + }, + { + "id": "comp-blog-2", + "type": "AuthorCard", + "name": "AuthorCard", + "props": { + "showBio": true, + "showSocialLinks": true + }, + "children": [] + }, + { + "id": "comp-blog-3", + "type": "CommentSection", + "name": "CommentSection", + "props": { + "allowReplies": true, + "requireModeration": true + }, + "children": [] + } + ], + "project-workflows": [ + { + "id": "workflow-blog-1", + "name": "Post Publishing Workflow", + "description": "Content review and publishing workflow", + "nodes": [ + { + "id": "node-blog-1", + "type": "trigger", + "name": "Draft Created", + "position": { "x": 100, "y": 100 }, + "data": { "label": "Author Creates Draft" }, + "config": { "triggerType": "event" } + }, + { + "id": "node-blog-2", + "type": "action", + "name": "Generate SEO", + "position": { "x": 300, "y": 100 }, + "data": { "label": "Auto-generate meta tags" } + }, + { + "id": "node-blog-3", + "type": "action", + "name": "Calculate Read Time", + "position": { "x": 500, "y": 100 }, + "data": { "label": "Estimate reading time" } + }, + { + "id": "node-blog-4", + "type": "notification", + "name": "Notify Editor", + "position": { "x": 700, "y": 100 }, + "data": { "label": "Send review request" } + }, + { + "id": "node-blog-5", + "type": "condition", + "name": "Approved?", + "position": { "x": 900, "y": 100 }, + "data": { "label": "Editor review" } + }, + { + "id": "node-blog-6", + "type": "action", + "name": "Publish", + "position": { "x": 1100, "y": 50 }, + "data": { "label": "Make post live" } + }, + { + "id": "node-blog-7", + "type": "notification", + "name": "Send to Subscribers", + "position": { "x": 1300, "y": 50 }, + "data": { "label": "Email newsletter" } + } + ], + "connections": [ + { "id": "conn-blog-1", "source": "node-blog-1", "target": "node-blog-2" }, + { "id": "conn-blog-2", "source": "node-blog-2", "target": "node-blog-3" }, + { "id": "conn-blog-3", "source": "node-blog-3", "target": "node-blog-4" }, + { "id": "conn-blog-4", "source": "node-blog-4", "target": "node-blog-5" }, + { "id": "conn-blog-5", "source": "node-blog-5", "target": "node-blog-6", "condition": "approved" }, + { "id": "conn-blog-6", "source": "node-blog-6", "target": "node-blog-7" } + ], + "isActive": true, + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ], + "project-lambdas": [ + { + "id": "lambda-blog-1", + "name": "generateSEO", + "description": "Auto-generate SEO metadata for blog posts", + "code": "export async function handler(event) {\n const { title, content, excerpt } = event;\n const keywords = extractKeywords(content);\n const description = excerpt || content.substring(0, 160);\n return {\n statusCode: 200,\n body: JSON.stringify({\n meta: {\n title: `${title} | My Blog`,\n description,\n keywords\n }\n })\n };\n}", + "language": "typescript", + "runtime": "nodejs20.x", + "handler": "index.handler", + "timeout": 10, + "memory": 256, + "environment": {}, + "triggers": [ + { + "id": "trigger-blog-1", + "type": "http", + "config": { "method": "POST", "path": "/api/seo" } + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + }, + { + "id": "lambda-blog-2", + "name": "sendNewsletter", + "description": "Send email to newsletter subscribers", + "code": "export async function handler(event) {\n const { postId, title, excerpt } = event;\n // Send newsletter emails\n return {\n statusCode: 200,\n body: JSON.stringify({ sent: true })\n };\n}", + "language": "typescript", + "runtime": "nodejs20.x", + "handler": "index.handler", + "timeout": 60, + "memory": 512, + "environment": {}, + "triggers": [ + { + "id": "trigger-blog-2", + "type": "event", + "config": { "event": "post.published" } + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ], + "project-playwright-tests": [ + { + "id": "test-blog-1", + "name": "Read and Comment Flow", + "description": "Test reading a post and leaving a comment", + "pageUrl": "/", + "steps": [ + { "id": "step-blog-1", "action": "navigate", "value": "/" }, + { "id": "step-blog-2", "action": "click", "selector": ".post-card:first-child" }, + { "id": "step-blog-3", "action": "expect", "selector": ".post-content", "assertion": "toBeVisible" }, + { "id": "step-blog-4", "action": "scroll", "selector": ".comment-section" }, + { "id": "step-blog-5", "action": "fill", "selector": "#comment-name", "value": "John Doe" }, + { "id": "step-blog-6", "action": "fill", "selector": "#comment-email", "value": "john@example.com" }, + { "id": "step-blog-7", "action": "fill", "selector": "#comment-text", "value": "Great article!" }, + { "id": "step-blog-8", "action": "click", "selector": "button[data-testid='submit-comment']" }, + { "id": "step-blog-9", "action": "expect", "selector": ".comment-success", "assertion": "toBeVisible" } + ] + } + ], + "project-storybook-stories": [ + { + "id": "story-blog-1", + "componentName": "PostCard", + "storyName": "Default", + "args": { + "title": "Getting Started with React", + "excerpt": "Learn the basics of React development", + "author": "Jane Smith", + "date": "2024-01-15", + "readTime": 5, + "image": "/images/post.jpg" + }, + "description": "Blog post card component", + "category": "Blog" + }, + { + "id": "story-blog-2", + "componentName": "AuthorCard", + "storyName": "WithSocial", + "args": { + "name": "Jane Smith", + "bio": "Tech writer and developer", + "avatar": "/images/author.jpg", + "social": { + "twitter": "@janesmith", + "github": "janesmith" + } + }, + "description": "Author profile card", + "category": "Blog" + } + ], + "project-unit-tests": [ + { + "id": "test-blog-unit-1", + "name": "Post Utilities", + "description": "Test post formatting and utilities", + "testType": "utility", + "targetFile": "/src/utils/post.ts", + "testCases": [ + { + "id": "case-blog-1", + "description": "calculates read time correctly", + "assertions": ["expect(calculateReadTime(content)).toBe(5)"] + }, + { + "id": "case-blog-2", + "description": "generates slug from title", + "assertions": ["expect(slugify('Hello World')).toBe('hello-world')"] + } + ] + } + ], + "project-component-trees": [ + { + "id": "tree-blog-1", + "name": "Blog Layout", + "description": "Main blog layout with sidebar", + "rootNodes": [ + { + "id": "root-blog-1", + "type": "Container", + "name": "BlogContainer", + "props": { "maxWidth": "lg" }, + "children": [ + { + "id": "header-blog-1", + "type": "Box", + "name": "BlogHeader", + "props": { "sx": { "py": 4, "borderBottom": 1 } }, + "children": [] + }, + { + "id": "main-blog-1", + "type": "Grid", + "name": "MainGrid", + "props": { "container": true, "spacing": 4 }, + "children": [ + { + "id": "content-blog-1", + "type": "Grid", + "name": "ContentColumn", + "props": { "item": true, "xs": 12, "md": 8 }, + "children": [] + }, + { + "id": "sidebar-blog-1", + "type": "Grid", + "name": "Sidebar", + "props": { "item": true, "xs": 12, "md": 4 }, + "children": [] + } + ] + } + ] + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ] +} diff --git a/src/config/seed-templates/dashboard.json b/src/config/seed-templates/dashboard.json new file mode 100644 index 0000000..5a4c471 --- /dev/null +++ b/src/config/seed-templates/dashboard.json @@ -0,0 +1,617 @@ +{ + "project-files": [ + { + "id": "file-dash-1", + "name": "page.tsx", + "path": "/src/app/page.tsx", + "content": "'use client'\n\nimport { DashboardStats } from '@/components/DashboardStats'\nimport { RecentActivity } from '@/components/RecentActivity'\nimport { AnalyticsCharts } from '@/components/AnalyticsCharts'\nimport { Grid, Container, Box } from '@mui/material'\n\nexport default function Dashboard() {\n return (\n \n \n \n \n \n \n \n \n \n \n \n \n \n )\n}", + "language": "typescript" + }, + { + "id": "file-dash-2", + "name": "analytics.tsx", + "path": "/src/app/analytics/page.tsx", + "content": "'use client'\n\nimport { AnalyticsDashboard } from '@/components/AnalyticsDashboard'\nimport { DateRangePicker } from '@/components/DateRangePicker'\nimport { Container, Box, Typography } from '@mui/material'\n\nexport default function AnalyticsPage() {\n return (\n \n \n Analytics\n \n \n \n \n )\n}", + "language": "typescript" + }, + { + "id": "file-dash-3", + "name": "users.tsx", + "path": "/src/app/users/page.tsx", + "content": "'use client'\n\nimport { UserTable } from '@/components/UserTable'\nimport { UserFilters } from '@/components/UserFilters'\nimport { Container, Box, Typography, Button } from '@mui/material'\nimport { Plus } from '@phosphor-icons/react'\n\nexport default function UsersPage() {\n return (\n \n \n Users\n \n \n \n \n \n )\n}", + "language": "typescript" + } + ], + "project-models": [ + { + "id": "model-dash-1", + "name": "Metric", + "fields": [ + { + "id": "field-dash-1", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-dash-2", + "name": "name", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-3", + "name": "value", + "type": "Float", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-4", + "name": "previousValue", + "type": "Float", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-5", + "name": "unit", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-6", + "name": "category", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-7", + "name": "timestamp", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-dash-2", + "name": "User", + "fields": [ + { + "id": "field-dash-8", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-dash-9", + "name": "email", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-dash-10", + "name": "name", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-11", + "name": "role", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "user" + }, + { + "id": "field-dash-12", + "name": "status", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "active" + }, + { + "id": "field-dash-13", + "name": "avatar", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-14", + "name": "lastLogin", + "type": "DateTime", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-15", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-dash-3", + "name": "Activity", + "fields": [ + { + "id": "field-dash-16", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-dash-17", + "name": "userId", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "User" + }, + { + "id": "field-dash-18", + "name": "action", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-19", + "name": "resource", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-20", + "name": "resourceId", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-21", + "name": "metadata", + "type": "Json", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-22", + "name": "timestamp", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-dash-4", + "name": "Report", + "fields": [ + { + "id": "field-dash-23", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-dash-24", + "name": "name", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-25", + "name": "type", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-26", + "name": "data", + "type": "Json", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-27", + "name": "filters", + "type": "Json", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-28", + "name": "generatedBy", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "User" + }, + { + "id": "field-dash-29", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-dash-5", + "name": "Alert", + "fields": [ + { + "id": "field-dash-30", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-dash-31", + "name": "title", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-32", + "name": "message", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-dash-33", + "name": "severity", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "info" + }, + { + "id": "field-dash-34", + "name": "read", + "type": "Boolean", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "false" + }, + { + "id": "field-dash-35", + "name": "userId", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "User" + }, + { + "id": "field-dash-36", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + } + ], + "project-components": [ + { + "id": "comp-dash-1", + "type": "StatCard", + "name": "StatCard", + "props": { + "showTrend": true, + "showSparkline": true, + "elevation": 2 + }, + "children": [] + }, + { + "id": "comp-dash-2", + "type": "DataTable", + "name": "DataTable", + "props": { + "sortable": true, + "filterable": true, + "paginated": true, + "exportable": true + }, + "children": [] + }, + { + "id": "comp-dash-3", + "type": "ChartCard", + "name": "ChartCard", + "props": { + "chartType": "line", + "showLegend": true, + "interactive": true + }, + "children": [] + } + ], + "project-workflows": [ + { + "id": "workflow-dash-1", + "name": "Alert Processing Workflow", + "description": "Process and route system alerts to users", + "nodes": [ + { + "id": "node-dash-1", + "type": "trigger", + "name": "Metric Threshold", + "position": { "x": 100, "y": 100 }, + "data": { "label": "Threshold Exceeded" }, + "config": { "triggerType": "condition" } + }, + { + "id": "node-dash-2", + "type": "action", + "name": "Create Alert", + "position": { "x": 300, "y": 100 }, + "data": { "label": "Generate alert record" } + }, + { + "id": "node-dash-3", + "type": "condition", + "name": "Check Severity", + "position": { "x": 500, "y": 100 }, + "data": { "label": "Evaluate priority" } + }, + { + "id": "node-dash-4", + "type": "notification", + "name": "Push Notification", + "position": { "x": 700, "y": 50 }, + "data": { "label": "Send push" } + }, + { + "id": "node-dash-5", + "type": "notification", + "name": "Email Notification", + "position": { "x": 700, "y": 100 }, + "data": { "label": "Send email" } + }, + { + "id": "node-dash-6", + "type": "database", + "name": "Log Activity", + "position": { "x": 900, "y": 100 }, + "data": { "label": "Record activity" } + } + ], + "connections": [ + { "id": "conn-dash-1", "source": "node-dash-1", "target": "node-dash-2" }, + { "id": "conn-dash-2", "source": "node-dash-2", "target": "node-dash-3" }, + { "id": "conn-dash-3", "source": "node-dash-3", "target": "node-dash-4", "condition": "critical" }, + { "id": "conn-dash-4", "source": "node-dash-3", "target": "node-dash-5", "condition": "warning" }, + { "id": "conn-dash-5", "source": "node-dash-4", "target": "node-dash-6" }, + { "id": "conn-dash-6", "source": "node-dash-5", "target": "node-dash-6" } + ], + "isActive": true, + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ], + "project-lambdas": [ + { + "id": "lambda-dash-1", + "name": "aggregateMetrics", + "description": "Aggregate and calculate metrics from raw data", + "code": "export async function handler(event) {\n const { timeRange, metricTypes } = event;\n // Aggregate metrics from database\n const metrics = {\n totalUsers: 1234,\n activeUsers: 987,\n revenue: 45678.90,\n conversions: 234\n };\n return {\n statusCode: 200,\n body: JSON.stringify({ metrics })\n };\n}", + "language": "typescript", + "runtime": "nodejs20.x", + "handler": "index.handler", + "timeout": 30, + "memory": 512, + "environment": {}, + "triggers": [ + { + "id": "trigger-dash-1", + "type": "schedule", + "config": { "schedule": "0 * * * *" } + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + }, + { + "id": "lambda-dash-2", + "name": "generateReport", + "description": "Generate analytics reports in various formats", + "code": "export async function handler(event) {\n const { reportType, filters, format } = event;\n // Generate report data\n return {\n statusCode: 200,\n body: JSON.stringify({ reportUrl: '/reports/123.pdf' })\n };\n}", + "language": "typescript", + "runtime": "nodejs20.x", + "handler": "index.handler", + "timeout": 60, + "memory": 1024, + "environment": {}, + "triggers": [ + { + "id": "trigger-dash-2", + "type": "http", + "config": { "method": "POST", "path": "/api/reports" } + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ], + "project-playwright-tests": [ + { + "id": "test-dash-1", + "name": "Dashboard Navigation", + "description": "Test navigating through dashboard sections", + "pageUrl": "/", + "steps": [ + { "id": "step-dash-1", "action": "navigate", "value": "/" }, + { "id": "step-dash-2", "action": "expect", "selector": ".stat-card", "assertion": "toBeVisible" }, + { "id": "step-dash-3", "action": "click", "selector": "a[href='/analytics']" }, + { "id": "step-dash-4", "action": "expect", "selector": ".analytics-chart", "assertion": "toBeVisible" }, + { "id": "step-dash-5", "action": "click", "selector": "a[href='/users']" }, + { "id": "step-dash-6", "action": "expect", "selector": ".user-table", "assertion": "toBeVisible" } + ] + } + ], + "project-storybook-stories": [ + { + "id": "story-dash-1", + "componentName": "StatCard", + "storyName": "WithTrend", + "args": { + "title": "Total Revenue", + "value": "$45,678", + "trend": 12.5, + "trendDirection": "up" + }, + "description": "Stat card with trend indicator", + "category": "Dashboard" + }, + { + "id": "story-dash-2", + "componentName": "ChartCard", + "storyName": "LineChart", + "args": { + "title": "User Growth", + "data": [], + "chartType": "line" + }, + "description": "Chart card with line visualization", + "category": "Dashboard" + } + ], + "project-unit-tests": [ + { + "id": "test-dash-unit-1", + "name": "Metric Calculations", + "description": "Test dashboard metric calculation utilities", + "testType": "utility", + "targetFile": "/src/utils/metrics.ts", + "testCases": [ + { + "id": "case-dash-1", + "description": "calculates percentage change", + "assertions": ["expect(calculateChange(100, 120)).toBe(20)"] + }, + { + "id": "case-dash-2", + "description": "formats large numbers", + "assertions": ["expect(formatNumber(1234567)).toBe('1.23M')"] + } + ] + } + ], + "project-component-trees": [ + { + "id": "tree-dash-1", + "name": "Dashboard Layout", + "description": "Main dashboard layout with sidebar navigation", + "rootNodes": [ + { + "id": "root-dash-1", + "type": "Box", + "name": "DashboardWrapper", + "props": { "sx": { "display": "flex", "minHeight": "100vh" } }, + "children": [ + { + "id": "sidebar-dash-1", + "type": "Drawer", + "name": "Sidebar", + "props": { "variant": "permanent", "sx": { "width": 240 } }, + "children": [] + }, + { + "id": "main-dash-1", + "type": "Box", + "name": "MainContent", + "props": { "sx": { "flexGrow": 1, "p": 3 } }, + "children": [ + { + "id": "appbar-dash-1", + "type": "AppBar", + "name": "TopBar", + "props": { "position": "sticky", "elevation": 0 }, + "children": [] + }, + { + "id": "content-dash-1", + "type": "Container", + "name": "PageContent", + "props": { "maxWidth": "xl", "sx": { "mt": 4 } }, + "children": [] + } + ] + } + ] + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ] +} diff --git a/src/config/seed-templates/e-commerce.json b/src/config/seed-templates/e-commerce.json new file mode 100644 index 0000000..28c6622 --- /dev/null +++ b/src/config/seed-templates/e-commerce.json @@ -0,0 +1,635 @@ +{ + "project-files": [ + { + "id": "file-ecom-1", + "name": "page.tsx", + "path": "/src/app/page.tsx", + "content": "'use client'\n\nimport { ProductGrid } from '@/components/ProductGrid'\nimport { Hero } from '@/components/Hero'\nimport { Box, Container } from '@mui/material'\n\nexport default function Home() {\n return (\n \n \n \n \n \n \n )\n}", + "language": "typescript" + }, + { + "id": "file-ecom-2", + "name": "cart.tsx", + "path": "/src/app/cart/page.tsx", + "content": "'use client'\n\nimport { Cart } from '@/components/Cart'\nimport { Container, Typography, Box } from '@mui/material'\n\nexport default function CartPage() {\n return (\n \n \n Shopping Cart\n \n \n \n )\n}", + "language": "typescript" + }, + { + "id": "file-ecom-3", + "name": "checkout.tsx", + "path": "/src/app/checkout/page.tsx", + "content": "'use client'\n\nimport { CheckoutForm } from '@/components/CheckoutForm'\nimport { Container, Typography } from '@mui/material'\n\nexport default function CheckoutPage() {\n return (\n \n \n Checkout\n \n \n \n )\n}", + "language": "typescript" + } + ], + "project-models": [ + { + "id": "model-ecom-1", + "name": "Product", + "fields": [ + { + "id": "field-ecom-1", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-ecom-2", + "name": "name", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-3", + "name": "description", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-4", + "name": "price", + "type": "Float", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-5", + "name": "salePrice", + "type": "Float", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-6", + "name": "stock", + "type": "Int", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "0" + }, + { + "id": "field-ecom-7", + "name": "sku", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-ecom-8", + "name": "images", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": true + }, + { + "id": "field-ecom-9", + "name": "categoryId", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "Category" + }, + { + "id": "field-ecom-10", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + }, + { + "id": "field-ecom-11", + "name": "updatedAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-ecom-2", + "name": "Category", + "fields": [ + { + "id": "field-ecom-12", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-ecom-13", + "name": "name", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-ecom-14", + "name": "slug", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-ecom-15", + "name": "description", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-16", + "name": "parentId", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false, + "relation": "Category" + } + ] + }, + { + "id": "model-ecom-3", + "name": "Order", + "fields": [ + { + "id": "field-ecom-17", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-ecom-18", + "name": "orderNumber", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-ecom-19", + "name": "customerId", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "Customer" + }, + { + "id": "field-ecom-20", + "name": "status", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "pending" + }, + { + "id": "field-ecom-21", + "name": "subtotal", + "type": "Float", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-22", + "name": "tax", + "type": "Float", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-23", + "name": "shipping", + "type": "Float", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-24", + "name": "total", + "type": "Float", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-25", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + }, + { + "id": "model-ecom-4", + "name": "OrderItem", + "fields": [ + { + "id": "field-ecom-26", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-ecom-27", + "name": "orderId", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "Order" + }, + { + "id": "field-ecom-28", + "name": "productId", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false, + "relation": "Product" + }, + { + "id": "field-ecom-29", + "name": "quantity", + "type": "Int", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-30", + "name": "price", + "type": "Float", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-31", + "name": "total", + "type": "Float", + "isRequired": true, + "isUnique": false, + "isArray": false + } + ] + }, + { + "id": "model-ecom-5", + "name": "Customer", + "fields": [ + { + "id": "field-ecom-32", + "name": "id", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false, + "defaultValue": "uuid()" + }, + { + "id": "field-ecom-33", + "name": "email", + "type": "String", + "isRequired": true, + "isUnique": true, + "isArray": false + }, + { + "id": "field-ecom-34", + "name": "firstName", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-35", + "name": "lastName", + "type": "String", + "isRequired": true, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-36", + "name": "phone", + "type": "String", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-37", + "name": "addresses", + "type": "Json", + "isRequired": false, + "isUnique": false, + "isArray": false + }, + { + "id": "field-ecom-38", + "name": "createdAt", + "type": "DateTime", + "isRequired": true, + "isUnique": false, + "isArray": false, + "defaultValue": "now()" + } + ] + } + ], + "project-components": [ + { + "id": "comp-ecom-1", + "type": "ProductCard", + "name": "ProductCard", + "props": { + "elevation": 1, + "showRating": true, + "showQuickView": true + }, + "children": [] + }, + { + "id": "comp-ecom-2", + "type": "CartItem", + "name": "CartItem", + "props": { + "showImage": true, + "allowQuantityEdit": true + }, + "children": [] + }, + { + "id": "comp-ecom-3", + "type": "CheckoutSummary", + "name": "CheckoutSummary", + "props": { + "showTax": true, + "showShipping": true, + "showPromoCode": true + }, + "children": [] + } + ], + "project-workflows": [ + { + "id": "workflow-ecom-1", + "name": "Order Processing Workflow", + "description": "Complete order fulfillment from payment to shipping", + "nodes": [ + { + "id": "node-ecom-1", + "type": "trigger", + "name": "Order Placed", + "position": { "x": 100, "y": 100 }, + "data": { "label": "Order Created" }, + "config": { "triggerType": "event" } + }, + { + "id": "node-ecom-2", + "type": "action", + "name": "Process Payment", + "position": { "x": 300, "y": 100 }, + "data": { "label": "Charge Customer" } + }, + { + "id": "node-ecom-3", + "type": "condition", + "name": "Payment Success?", + "position": { "x": 500, "y": 100 }, + "data": { "label": "Check Payment" } + }, + { + "id": "node-ecom-4", + "type": "action", + "name": "Update Inventory", + "position": { "x": 700, "y": 50 }, + "data": { "label": "Reduce Stock" } + }, + { + "id": "node-ecom-5", + "type": "notification", + "name": "Send Confirmation", + "position": { "x": 900, "y": 50 }, + "data": { "label": "Email Customer" } + }, + { + "id": "node-ecom-6", + "type": "notification", + "name": "Payment Failed", + "position": { "x": 700, "y": 150 }, + "data": { "label": "Notify Failure" } + } + ], + "connections": [ + { "id": "conn-ecom-1", "source": "node-ecom-1", "target": "node-ecom-2" }, + { "id": "conn-ecom-2", "source": "node-ecom-2", "target": "node-ecom-3" }, + { "id": "conn-ecom-3", "source": "node-ecom-3", "target": "node-ecom-4", "condition": "success" }, + { "id": "conn-ecom-4", "source": "node-ecom-4", "target": "node-ecom-5" }, + { "id": "conn-ecom-5", "source": "node-ecom-3", "target": "node-ecom-6", "condition": "failure" } + ], + "isActive": true, + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ], + "project-lambdas": [ + { + "id": "lambda-ecom-1", + "name": "calculateShipping", + "description": "Calculate shipping costs based on order details", + "code": "export async function handler(event) {\n const { items, destination } = event;\n const weight = items.reduce((sum, item) => sum + item.weight * item.quantity, 0);\n const baseRate = 5.99;\n const weightRate = weight * 0.50;\n const total = baseRate + weightRate;\n return {\n statusCode: 200,\n body: JSON.stringify({ shipping: total })\n };\n}", + "language": "typescript", + "runtime": "nodejs20.x", + "handler": "index.handler", + "timeout": 10, + "memory": 128, + "environment": {}, + "triggers": [ + { + "id": "trigger-ecom-1", + "type": "http", + "config": { "method": "POST", "path": "/api/shipping" } + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + }, + { + "id": "lambda-ecom-2", + "name": "processRefund", + "description": "Handle order refunds and inventory restoration", + "code": "export async function handler(event) {\n const { orderId, reason } = event;\n // Process refund logic\n return {\n statusCode: 200,\n body: JSON.stringify({ refunded: true, orderId })\n };\n}", + "language": "typescript", + "runtime": "nodejs20.x", + "handler": "index.handler", + "timeout": 30, + "memory": 256, + "environment": {}, + "triggers": [ + { + "id": "trigger-ecom-2", + "type": "http", + "config": { "method": "POST", "path": "/api/refunds" } + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ], + "project-playwright-tests": [ + { + "id": "test-ecom-1", + "name": "Complete Purchase Flow", + "description": "Test end-to-end purchase workflow", + "pageUrl": "/", + "steps": [ + { "id": "step-ecom-1", "action": "navigate", "value": "/" }, + { "id": "step-ecom-2", "action": "click", "selector": ".product-card:first-child" }, + { "id": "step-ecom-3", "action": "click", "selector": "button[data-testid='add-to-cart']" }, + { "id": "step-ecom-4", "action": "click", "selector": "a[href='/cart']" }, + { "id": "step-ecom-5", "action": "expect", "selector": ".cart-item", "assertion": "toBeVisible" }, + { "id": "step-ecom-6", "action": "click", "selector": "button[data-testid='checkout']" }, + { "id": "step-ecom-7", "action": "fill", "selector": "#email", "value": "customer@example.com" }, + { "id": "step-ecom-8", "action": "fill", "selector": "#cardNumber", "value": "4242424242424242" }, + { "id": "step-ecom-9", "action": "click", "selector": "button[type='submit']" }, + { "id": "step-ecom-10", "action": "expect", "selector": ".success-message", "assertion": "toBeVisible" } + ] + } + ], + "project-storybook-stories": [ + { + "id": "story-ecom-1", + "componentName": "ProductCard", + "storyName": "Default", + "args": { + "name": "Premium T-Shirt", + "price": 29.99, + "image": "/images/product.jpg", + "rating": 4.5 + }, + "description": "Product card with image and details", + "category": "E-Commerce" + }, + { + "id": "story-ecom-2", + "componentName": "ProductCard", + "storyName": "OnSale", + "args": { + "name": "Premium T-Shirt", + "price": 29.99, + "salePrice": 19.99, + "image": "/images/product.jpg", + "rating": 4.5 + }, + "description": "Product card with sale price", + "category": "E-Commerce" + } + ], + "project-unit-tests": [ + { + "id": "test-ecom-unit-1", + "name": "Cart Calculations", + "description": "Test cart total and tax calculations", + "testType": "utility", + "targetFile": "/src/utils/cart.ts", + "testCases": [ + { + "id": "case-ecom-1", + "description": "calculates subtotal correctly", + "assertions": ["expect(calculateSubtotal(items)).toBe(99.97)"] + }, + { + "id": "case-ecom-2", + "description": "applies tax correctly", + "assertions": ["expect(calculateTax(100, 0.08)).toBe(8.00)"] + } + ] + } + ], + "project-component-trees": [ + { + "id": "tree-ecom-1", + "name": "E-Commerce Layout", + "description": "Main store layout with header, navigation, and footer", + "rootNodes": [ + { + "id": "root-ecom-1", + "type": "Container", + "name": "StoreContainer", + "props": { "maxWidth": "xl" }, + "children": [ + { + "id": "header-ecom-1", + "type": "AppBar", + "name": "StoreHeader", + "props": { "position": "sticky", "elevation": 1 }, + "children": [ + { + "id": "nav-ecom-1", + "type": "Toolbar", + "name": "Navigation", + "props": {}, + "children": [] + } + ] + }, + { + "id": "main-ecom-1", + "type": "Box", + "name": "MainContent", + "props": { "sx": { "minHeight": "80vh" } }, + "children": [] + }, + { + "id": "footer-ecom-1", + "type": "Box", + "name": "Footer", + "props": { "sx": { "py": 6, "bgcolor": "grey.100" } }, + "children": [] + } + ] + } + ], + "createdAt": 1704067200000, + "updatedAt": 1704067200000 + } + ] +} diff --git a/src/config/seed-templates/index.ts b/src/config/seed-templates/index.ts new file mode 100644 index 0000000..279cbc5 --- /dev/null +++ b/src/config/seed-templates/index.ts @@ -0,0 +1,3 @@ +export { default as ecommerceTemplate } from './e-commerce.json' +export { default as blogTemplate } from './blog.json' +export { default as dashboardTemplate } from './dashboard.json' diff --git a/src/hooks/data/index.ts b/src/hooks/data/index.ts index f7cf0f5..e0e7610 100644 --- a/src/hooks/data/index.ts +++ b/src/hooks/data/index.ts @@ -4,3 +4,5 @@ export * from './use-search' export * from './use-debounce' export * from './use-sort' export * from './use-pagination' +export * from './use-seed-data' +export * from './use-seed-templates' diff --git a/src/hooks/data/use-seed-templates.ts b/src/hooks/data/use-seed-templates.ts new file mode 100644 index 0000000..fcc8be7 --- /dev/null +++ b/src/hooks/data/use-seed-templates.ts @@ -0,0 +1,153 @@ +import { useState } from 'react' +import { ecommerceTemplate, blogTemplate, dashboardTemplate } from '@/config/seed-templates' +import defaultTemplate from '@/config/seed-data.json' + +export type TemplateType = 'default' | 'e-commerce' | 'blog' | 'dashboard' + +interface Template { + id: TemplateType + name: string + description: string + icon: string + data: Record + features: string[] +} + +const templates: Template[] = [ + { + id: 'default', + name: 'Default Project', + description: 'Basic starter template with common components', + icon: '🚀', + data: defaultTemplate, + features: ['Basic models', 'Sample components', 'User workflow'] + }, + { + id: 'e-commerce', + name: 'E-Commerce Store', + description: 'Complete online store with products, cart, and checkout', + icon: '🛍️', + data: ecommerceTemplate, + features: [ + 'Product catalog', + 'Shopping cart', + 'Order management', + 'Customer accounts', + 'Payment processing' + ] + }, + { + id: 'blog', + name: 'Blog Platform', + description: 'Content-focused blog with authors, posts, and comments', + icon: '📝', + data: blogTemplate, + features: [ + 'Post management', + 'Author profiles', + 'Comment system', + 'Newsletter', + 'SEO optimization' + ] + }, + { + id: 'dashboard', + name: 'Analytics Dashboard', + description: 'Data visualization dashboard with metrics and reports', + icon: '📊', + data: dashboardTemplate, + features: [ + 'Real-time metrics', + 'Data visualization', + 'User management', + 'Activity logging', + 'Alert system' + ] + } +] + +export function useSeedTemplates() { + const [isLoading, setIsLoading] = useState(false) + + const getTemplates = () => templates + + const getTemplate = (id: TemplateType): Template | undefined => { + return templates.find(t => t.id === id) + } + + const loadTemplate = async (templateId: TemplateType) => { + setIsLoading(true) + try { + const template = getTemplate(templateId) + if (!template) { + throw new Error(`Template ${templateId} not found`) + } + + for (const [key, value] of Object.entries(template.data)) { + await window.spark.kv.set(key, value) + } + + return true + } catch (error) { + console.error('Failed to load template:', error) + return false + } finally { + setIsLoading(false) + } + } + + const clearAndLoadTemplate = async (templateId: TemplateType) => { + setIsLoading(true) + try { + const keys = await window.spark.kv.keys() + for (const key of keys) { + await window.spark.kv.delete(key) + } + + const success = await loadTemplate(templateId) + return success + } catch (error) { + console.error('Failed to clear and load template:', error) + return false + } finally { + setIsLoading(false) + } + } + + const mergeTemplate = async (templateId: TemplateType) => { + setIsLoading(true) + try { + const template = getTemplate(templateId) + if (!template) { + throw new Error(`Template ${templateId} not found`) + } + + for (const [key, value] of Object.entries(template.data)) { + const existingData = await window.spark.kv.get(key) + if (existingData && Array.isArray(existingData) && Array.isArray(value)) { + const mergedData = [...existingData, ...value] + await window.spark.kv.set(key, mergedData) + } else { + await window.spark.kv.set(key, value) + } + } + + return true + } catch (error) { + console.error('Failed to merge template:', error) + return false + } finally { + setIsLoading(false) + } + } + + return { + templates, + isLoading, + getTemplates, + getTemplate, + loadTemplate, + clearAndLoadTemplate, + mergeTemplate + } +} diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 27c97a2..4dbb7f9 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -13,3 +13,4 @@ export * from './config/use-feature-flags' export * from './ai/use-ai-generation' export * from './data/use-seed-data' +export * from './data/use-seed-templates'