Generated by Spark: Create seed data templates for different project types (e-commerce, blog, dashboard)

This commit is contained in:
2026-01-16 23:57:29 +00:00
committed by GitHub
parent d2e29363f5
commit 90e3cb9c4d
13 changed files with 2729 additions and 1 deletions

View File

@@ -32,6 +32,7 @@ const componentMap: Record<string, React.LazyExoticComponent<any>> = {
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 })))

View File

@@ -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<string, any> = {}
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 (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<h2 className="text-2xl font-bold mb-2">Template Explorer</h2>
<p className="text-muted-foreground">
Browse and inspect template structures
</p>
</div>
<Button onClick={exportCurrentData} variant="outline">
<Download className="mr-2" size={16} />
Export Current Data
</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="space-y-2">
{templates.map((template) => (
<Card
key={template.id}
className={`cursor-pointer transition-colors ${
selectedTemplate === template.id ? 'border-primary bg-accent/50' : 'hover:bg-accent/20'
}`}
onClick={() => setSelectedTemplate(template.id)}
>
<CardHeader className="p-4">
<div className="flex items-center gap-2">
<span className="text-2xl">{template.icon}</span>
<CardTitle className="text-sm">{template.name}</CardTitle>
</div>
</CardHeader>
</Card>
))}
</div>
<Card className="col-span-3">
<CardHeader>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<span className="text-4xl">{currentTemplate.icon}</span>
<div>
<CardTitle>{currentTemplate.name}</CardTitle>
<CardDescription>{currentTemplate.description}</CardDescription>
</div>
</div>
<Button onClick={downloadJSON} variant="outline" size="sm">
<Download className="mr-2" size={16} />
Download
</Button>
</div>
</CardHeader>
<CardContent>
<Tabs defaultValue="overview">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="structure">Structure</TabsTrigger>
<TabsTrigger value="json">JSON</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-4">
<div>
<h3 className="font-semibold mb-2">Features</h3>
<div className="flex flex-wrap gap-2">
{currentTemplate.features.map((feature, idx) => (
<Badge key={idx} variant="secondary">
{feature}
</Badge>
))}
</div>
</div>
<div className="grid grid-cols-2 gap-4">
{Object.entries(currentTemplate.data).map(([key, value]) => (
<Card key={key}>
<CardHeader className="p-4">
<CardTitle className="text-sm">{key.replace('project-', '')}</CardTitle>
<CardDescription>
{Array.isArray(value) ? `${value.length} items` : 'Configuration'}
</CardDescription>
</CardHeader>
</Card>
))}
</div>
</TabsContent>
<TabsContent value="structure" className="space-y-4">
<ScrollArea className="h-[500px]">
{Object.entries(currentTemplate.data).map(([key, value]) => (
<div key={key} className="mb-4 p-4 border rounded-lg">
<div className="flex items-center justify-between mb-2">
<h3 className="font-semibold">{key}</h3>
<Badge variant="outline">
{Array.isArray(value) ? `${value.length} items` : 'object'}
</Badge>
</div>
{Array.isArray(value) && value.length > 0 && (
<div className="text-sm text-muted-foreground">
{value.slice(0, 3).map((item: any, idx: number) => (
<div key={idx} className="py-1">
{item.name || item.title || item.id}
</div>
))}
{value.length > 3 && (
<div className="py-1 italic">... and {value.length - 3} more</div>
)}
</div>
)}
</div>
))}
</ScrollArea>
</TabsContent>
<TabsContent value="json">
<div className="relative">
<Button
size="sm"
variant="ghost"
className="absolute right-2 top-2 z-10"
onClick={() => copyToClipboard(JSON.stringify(currentTemplate.data, null, 2))}
>
<Copy size={16} />
</Button>
<ScrollArea className="h-[500px]">
<pre className="text-xs p-4 bg-muted rounded-lg">
{JSON.stringify(currentTemplate.data, null, 2)}
</pre>
</ScrollArea>
</div>
</TabsContent>
</Tabs>
</CardContent>
</Card>
</div>
</div>
)
}

View File

@@ -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<TemplateType | null>(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 (
<>
<Tabs defaultValue="templates" className="w-full">
<TabsList>
<TabsTrigger value="templates">Load Templates</TabsTrigger>
<TabsTrigger value="explorer">Explore Templates</TabsTrigger>
</TabsList>
<TabsContent value="templates" className="space-y-6 mt-6">
<div>
<h2 className="text-2xl font-bold mb-2">Project Templates</h2>
<p className="text-muted-foreground">
Start your project with pre-configured templates including models, components, and workflows
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{templates.map((template) => (
<Card key={template.id} className="relative overflow-hidden hover:shadow-lg transition-shadow">
<CardHeader>
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<span className="text-4xl">{template.icon}</span>
<div>
<CardTitle className="text-xl">{template.name}</CardTitle>
<CardDescription className="mt-1">
{template.description}
</CardDescription>
</div>
</div>
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-2">
{template.features.map((feature, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">
{feature}
</Badge>
))}
</div>
<div className="flex gap-2">
<Button
variant="default"
size="sm"
onClick={() => handleSelectTemplate(template.id, 'replace')}
disabled={isLoading}
className="flex-1"
>
<Download className="mr-2" size={16} />
Load Template
</Button>
<Button
variant="outline"
size="sm"
onClick={() => handleSelectTemplate(template.id, 'merge')}
disabled={isLoading}
className="flex-1"
>
<Plus className="mr-2" size={16} />
Merge
</Button>
</div>
</CardContent>
</Card>
))}
</div>
<Alert>
<Package size={16} />
<AlertDescription>
<strong>Load Template:</strong> Replaces all existing data with the selected template.
<br />
<strong>Merge:</strong> Adds template data to your existing project without removing current data.
</AlertDescription>
</Alert>
</TabsContent>
<TabsContent value="explorer" className="mt-6">
<TemplateExplorer />
</TabsContent>
</Tabs>
<Dialog open={showConfirmDialog} onOpenChange={setShowConfirmDialog}>
<DialogContent>
<DialogHeader>
<DialogTitle>
{actionType === 'replace' ? 'Replace Project Data?' : 'Merge Template Data?'}
</DialogTitle>
<DialogDescription>
{actionType === 'replace' ? (
<>
This will <strong className="text-destructive">delete all existing data</strong> and load the{' '}
<strong>{selectedTemplate}</strong> template. This action cannot be undone.
</>
) : (
<>
This will <strong>add</strong> the <strong>{selectedTemplate}</strong> template data to your
existing project without removing current data.
</>
)}
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={() => setShowConfirmDialog(false)}>
Cancel
</Button>
<Button
variant={actionType === 'replace' ? 'destructive' : 'default'}
onClick={handleConfirmLoad}
>
{actionType === 'replace' ? (
<>
<Trash className="mr-2" size={16} />
Replace All Data
</>
) : (
<>
<Plus className="mr-2" size={16} />
Merge Data
</>
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
)
}

View File

@@ -1,3 +1,5 @@
export * from './atoms'
export * from './molecules'
export * from './organisms'
export * from './TemplateSelector'
export * from './TemplateExplorer'

View File

@@ -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"]

View File

@@ -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

View File

@@ -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 <Box>\n <Container maxWidth=\"lg\" sx={{ py: 6 }}>\n <FeaturedPost />\n <BlogGrid />\n </Container>\n </Box>\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 <Container maxWidth=\"md\" sx={{ py: 6 }}>\n <PostMeta />\n <PostContent />\n <Box sx={{ mt: 8 }}>\n <RelatedPosts />\n </Box>\n </Container>\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 <Container maxWidth=\"lg\" sx={{ py: 6 }}>\n <AuthorProfile authorId={params.id} />\n <AuthorPosts authorId={params.id} />\n </Container>\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
}
]
}

View File

@@ -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 <Container maxWidth=\"xl\" sx={{ py: 4 }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <DashboardStats />\n </Grid>\n <Grid item xs={12} lg={8}>\n <AnalyticsCharts />\n </Grid>\n <Grid item xs={12} lg={4}>\n <RecentActivity />\n </Grid>\n </Grid>\n </Container>\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 <Container maxWidth=\"xl\" sx={{ py: 4 }}>\n <Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 4 }}>\n <Typography variant=\"h4\">Analytics</Typography>\n <DateRangePicker />\n </Box>\n <AnalyticsDashboard />\n </Container>\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 <Container maxWidth=\"xl\" sx={{ py: 4 }}>\n <Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 4 }}>\n <Typography variant=\"h4\">Users</Typography>\n <Button variant=\"contained\" startIcon={<Plus />}>\n Add User\n </Button>\n </Box>\n <UserFilters />\n <UserTable />\n </Container>\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
}
]
}

View File

@@ -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 <Box>\n <Hero \n title=\"Summer Collection 2024\"\n subtitle=\"Discover our latest arrivals\"\n ctaText=\"Shop Now\"\n />\n <Container maxWidth=\"xl\" sx={{ py: 6 }}>\n <ProductGrid />\n </Container>\n </Box>\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 <Container maxWidth=\"lg\" sx={{ py: 6 }}>\n <Typography variant=\"h3\" gutterBottom>\n Shopping Cart\n </Typography>\n <Cart />\n </Container>\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 <Container maxWidth=\"md\" sx={{ py: 6 }}>\n <Typography variant=\"h3\" gutterBottom>\n Checkout\n </Typography>\n <CheckoutForm />\n </Container>\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
}
]
}

View File

@@ -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'

View File

@@ -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'

View File

@@ -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<string, any>
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<any[]>(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
}
}

View File

@@ -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'