diff --git a/.env.example b/.env.example index 10f313b0..42ea1d05 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ # Database Configuration # For development (SQLite): # DATABASE_URL="file:./dev.db" -DATABASE_URL="postgres://62f4097df5e872956ef3438a631f543fae4d5d42215bd0826950ab47ae13d1d8:sk_C9LGde4N8GzIwZvatfrYp@db.prisma.io:5432/postgres?sslmode=require" +DATABASE_URL="postgres://00a2b80f79491981d1bb3b2e9f16ff38e4f8ec8176d81850c1a0fc6b8d07aedb:sk_SAURAAr96utLcyihkDPJ7@db.prisma.io:5432/postgres?sslmode=require" PRISMA_DATABASE_URL="postgres://62f4097df5e872956ef3438a631f543fae4d5d42215bd0826950ab47ae13d1d8:sk_C9LGde4N8GzIwZvatfrYp@db.prisma.io:5432/postgres?sslmode=require" POSTGRES_URL="postgres://62f4097df5e872956ef3438a631f543fae4d5d42215bd0826950ab47ae13d1d8:sk_C9LGde4N8GzIwZvatfrYp@db.prisma.io:5432/postgres?sslmode=require" PRISMA_DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/?api_key=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqd3RfaWQiOjEsInNlY3VyZV9rZXkiOiJza19DOUxHZGU0TjhHekl3WnZhdGZyWXAiLCJhcGlfa2V5IjoiMDFLQVBFN1lQMEdDQzMwQjdEMDFQUkVGWjkiLCJ0ZW5hbnRfaWQiOiI2MmY0MDk3ZGY1ZTg3Mjk1NmVmMzQzOGE2MzFmNTQzZmFlNGQ1ZDQyMjE1YmQwODI2OTUwYWI0N2FlMTNkMWQ4IiwiaW50ZXJuYWxfc2VjcmV0IjoiMTVmYjFkMTAtMDg3Ny00ZWIwLTg2NDktODI0NDFlMjFkMWM4In0.TwVbX50ckjTqPEamd8eD2gR2VE_s0T3dVn4FZ4nhnS8" diff --git a/.github/prompts/plan-storefrontThemeCustomization.prompt.md b/.github/prompts/plan-storefrontThemeCustomization.prompt.md new file mode 100644 index 00000000..3a29310c --- /dev/null +++ b/.github/prompts/plan-storefrontThemeCustomization.prompt.md @@ -0,0 +1,941 @@ +# Plan: Storefront Theme Customization & Template System Enhancement + +**TL;DR**: Enhance StormCom's **existing robust storefront system** (10+ sections, 6 templates) with a **Shopify-inspired unified editor**, **draft/publish workflow**, **drag-and-drop section ordering**, **live preview iframe**, and **template marketplace foundation**. Leverage current [StorefrontConfig](src/types/storefront-config.ts) types, [ThemeTemplates](src/lib/storefront/theme-templates.ts), and [AppearanceEditor](src/app/dashboard/stores/[storeId]/appearance/appearance-editor.tsx) as foundation to avoid rewriting. Build with Next.js 16 Server Actions, shadcn/ui components (Dialog, Drawer, Tabs, Accordion), and @dnd-kit for drag-and-drop. + +## Steps + +### 1. Add Draft/Publish Infrastructure + +Create `StorefrontDraft` model in [schema.prisma](prisma/schema.prisma), add `sectionOrder` field to `StorefrontConfig`, implement draft save/publish Server Actions in new `app/actions/storefront.ts`, and create API routes `POST /api/stores/[id]/storefront/draft` and `POST /api/stores/[id]/storefront/publish` with permission validation (STORE_ADMIN, CONTENT_MANAGER). + +**Database Schema Changes**: +```prisma +model StorefrontDraft { + id String @id @default(cuid()) + storeId String @unique + configJson String // Draft configuration + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + + store Store @relation(fields: [storeId], references: [id], onDelete: Cascade) +} + +model Store { + // ... existing fields + storefrontConfig String? + configVersion Int @default(1) + sectionOrder String? // JSON array: ['hero', 'products', 'testimonials'] + + drafts StorefrontDraft[] +} +``` + +**TypeScript Interface Updates**: +```typescript +// src/types/storefront-config.ts +export interface StorefrontConfig { + version: number; + sectionOrder?: string[]; // ['hero', 'featuredProducts', 'testimonials', etc.] + theme: ThemeSettings; + hero: HeroSection; + // ... existing sections +} +``` + +**Server Actions** (new file): +```typescript +// src/app/actions/storefront.ts +'use server' + +import { getServerSession } from 'next-auth'; +import { authOptions } from '@/lib/auth'; +import { prisma } from '@/lib/prisma'; +import { revalidatePath } from 'next/cache'; + +export async function saveStorefrontDraft(storeId: string, config: StorefrontConfig) { + const session = await getServerSession(authOptions); + // Validate permissions (STORE_ADMIN, CONTENT_MANAGER) + await prisma.storefrontDraft.upsert({ + where: { storeId }, + update: { configJson: JSON.stringify(config) }, + create: { storeId, configJson: JSON.stringify(config) } + }); +} + +export async function publishStorefront(storeId: string) { + const session = await getServerSession(authOptions); + // Validate permissions + const draft = await prisma.storefrontDraft.findUnique({ where: { storeId } }); + await prisma.store.update({ + where: { id: storeId }, + data: { storefrontConfig: draft.configJson } + }); + await prisma.storefrontDraft.delete({ where: { storeId } }); + revalidatePath(`/store/[slug]`, 'page'); +} + +export async function discardDraft(storeId: string) { + await prisma.storefrontDraft.delete({ where: { storeId } }); +} +``` + +**API Routes**: +- `POST /api/stores/[id]/storefront/draft` → Save draft +- `GET /api/stores/[id]/storefront/draft` → Get draft +- `POST /api/stores/[id]/storefront/publish` → Publish draft to live +- `DELETE /api/stores/[id]/storefront/draft` → Discard draft + +### 2. Build Unified Editor Layout + +Replace tab-based [AppearanceEditor](src/app/dashboard/stores/[storeId]/appearance/appearance-editor.tsx) with 3-column layout: `SectionList` (left sidebar with drag handles), `PreviewFrame` (center iframe with breakpoint switcher), `SettingsPanel` (right sidebar with dynamic forms). Use shadcn/ui Drawer for mobile, @dnd-kit/sortable for reordering, and postMessage API for iframe sync. + +**New Editor Structure**: +``` +┌───────────────┬──────────────────────┬─────────────────┐ +│ SectionList │ PreviewFrame │ SettingsPanel │ +│ (240px) │ (flex-1) │ (360px) │ +│ │ │ │ +│ ☰ Hero │ ┌────────────────┐ │ Theme Settings │ +│ ☰ Products │ │ │ │ │ +│ ☰ Badges │ │ [Iframe] │ │ [Dynamic Form] │ +│ ☰ Newsletter │ │ │ │ │ +│ │ │ │ │ │ +│ [+ Add] │ └────────────────┘ │ [Save Draft] │ +│ │ [Desktop▼] [🔄] │ [Publish] │ +└───────────────┴──────────────────────┴─────────────────┘ +``` + +**Component Architecture**: +```typescript +// src/components/storefront-editor/unified-editor.tsx +'use client' + +export function UnifiedEditor({ storeId, initialConfig }: Props) { + const [config, setConfig] = useState(initialConfig); + const [selectedSection, setSelectedSection] = useState('hero'); + const [isDirty, setIsDirty] = useState(false); + + return ( +
+ + + +
+ ); +} +``` + +**Required shadcn/ui Components**: +- `@dnd-kit/core` + `@dnd-kit/sortable` (drag-and-drop) +- `Sheet` or `Drawer` (mobile responsive panels) +- `Select` (breakpoint switcher) +- `Button` (action buttons) +- `ScrollArea` (scrollable panels) + +**Installation**: +```bash +npx shadcn@latest add drawer scroll-area +npm install @dnd-kit/core @dnd-kit/sortable +``` + +### 3. Enhance Template System + +Create `TemplateLibrary` component with visual preview cards for existing 6 templates ([theme-templates.ts](src/lib/storefront/theme-templates.ts)), add "Save as Custom Template" button, implement export/import endpoints (`POST /api/stores/[id]/storefront/export|import`), and build template application wizard with real-time preview using existing `generateThemeCSSVariables` utility. + +**Template Library UI**: +```typescript +// src/components/storefront-editor/template-library.tsx +export function TemplateLibrary({ onSelect }: Props) { + const templates = [ + { id: 'modern', name: 'Modern', preview: '/templates/modern.png' }, + { id: 'classic', name: 'Classic', preview: '/templates/classic.png' }, + { id: 'bold', name: 'Bold', preview: '/templates/bold.png' }, + { id: 'elegant', name: 'Elegant', preview: '/templates/elegant.png' }, + { id: 'minimal', name: 'Minimal', preview: '/templates/minimal.png' }, + { id: 'boutique', name: 'Boutique', preview: '/templates/boutique.png' }, + ]; + + return ( + + + + + + Select a Template +
+ {templates.map(template => ( + + {template.name} + {template.name} + + + ))} +
+
+
+ ); +} +``` + +**Template Application Wizard**: +```typescript +// src/components/storefront-editor/template-wizard.tsx +export function TemplateWizard({ templateId, onComplete }: Props) { + const [step, setStep] = useState(1); + const [customization, setCustomization] = useState({ + primaryColor: '#3b82f6', + storeName: '', + logo: '', + }); + + return ( + + + {step === 1 && } + {step === 2 && } + {step === 3 && } + + + ); +} +``` + +**Export/Import Endpoints**: +```typescript +// GET /api/stores/[id]/storefront/export +export async function GET(req: Request, { params }: { params: { id: string } }) { + const store = await prisma.store.findUnique({ where: { id: params.id } }); + const config = JSON.parse(store.storefrontConfig || '{}'); + + return new Response(JSON.stringify(config, null, 2), { + headers: { + 'Content-Type': 'application/json', + 'Content-Disposition': `attachment; filename="storefront-${store.slug}.json"` + } + }); +} + +// POST /api/stores/[id]/storefront/import +export async function POST(req: Request, { params }: { params: { id: string } }) { + const importedConfig = await req.json(); + // Validate against StorefrontConfigSchema + const validated = storefrontConfigSchema.parse(importedConfig); + + await prisma.store.update({ + where: { id: params.id }, + data: { storefrontConfig: JSON.stringify(validated) } + }); + + return Response.json({ success: true }); +} +``` + +**Required shadcn/ui Components**: +```bash +npx shadcn@latest add dialog card +``` + +### 4. Implement Live Preview + +Build `PreviewFrame` component rendering iframe at `/store/[slug]?preview=draft`, add responsive breakpoint switcher (Desktop 1920px, Tablet 768px, Mobile 375px), implement Inspector Mode (click storefront sections to highlight in editor), and sync changes via `window.postMessage()` with 300ms debounce. + +**PreviewFrame Component**: +```typescript +// src/components/storefront-editor/preview-frame.tsx +'use client' + +export function PreviewFrame({ storeSlug, config, breakpoint }: Props) { + const iframeRef = useRef(null); + + // Sync config changes to iframe + useEffect(() => { + const timer = setTimeout(() => { + iframeRef.current?.contentWindow?.postMessage({ + type: 'CONFIG_UPDATE', + config + }, '*'); + }, 300); // Debounce + + return () => clearTimeout(timer); + }, [config]); + + const breakpoints = { + desktop: '1920px', + tablet: '768px', + mobile: '375px' + }; + + return ( +
+
+ + +
+ +
+