Skip to main content

Lookbook

Your generations live at /lookbook. Browse, zoom, select, and send designs to production.

Overview

Your generated designs live here. Each card shows:
  • Thumbnail with zoom on click
  • Generation date
  • Design prompt
  • Status indicator (pending → completed → in production)

Generation Flow

  1. Chat creates generation → saved to generations table
  2. AI returns image URLimage_url updated
  3. User views in gallery → sorted newest first
  4. User selects designs → multi-select for bulk actions

Database Schema

-- generations table
create table public.generations (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade,
  project_id uuid references public.projects(id) on delete set null,
  thread_id uuid references public.threads(id) on delete set null,
  prompt text,
  image_url text,
  provider text, -- 'replicate' | 'fal'
  model text,    -- 'nano-banana' | 'nano-banana-pro'
  status text default 'pending',
  created_at timestamptz default now()
);

Multi-Select Actions

Add to Collection

Group selected designs into a named collection

Send to Production

Create production order with selected designs

Delete

Remove selected generations permanently

Zoom Modal

Click any generation to view full-size:
// ImageCarousel component
<Dialog open={!!selectedGen} onOpenChange={() => setSelectedGen(null)}>
  <DialogContent className="max-w-4xl">
    <img 
      src={selectedGen?.imageUrl}
      alt={selectedGen?.prompt}
      className="w-full h-auto rounded-lg"
    />
    <div className="mt-4 space-y-2">
      <p className="text-sm text-muted-foreground">
        {selectedGen?.prompt}
      </p>
      <p className="text-xs text-muted-foreground">
        Generated {formatDate(selectedGen?.createdAt)}
      </p>
    </div>
  </DialogContent>
</Dialog>

Production Orders

From the gallery, you can send designs to production:
  1. Select designs (one or more)
  2. Click “Send to Production”
  3. Choose garment (pulls from Shopify or mocks)
  4. Select sizes and quantities
  5. Checkout via Stripe
// Creating production order
const createOrder = async () => {
  const response = await fetch("/api/production", {
    method: "POST",
    body: JSON.stringify({
      designUrl: selectedGen.imageUrl,
      garmentId: garment.id,
      sizes: selectedSizes,
      quantities: quantities,
    }),
  });
  // Redirects to Stripe checkout
};
  • By project: Show only designs from current project
  • By date range: Filter by creation date
  • By status: pending, completed, in_production
// Filter state
const [filters, setFilters] = useState({
  projectId: null,
  status: null,
  dateRange: null,
});

const filteredGenerations = generations.filter(gen => {
  if (filters.projectId && gen.projectId !== filters.projectId) return false;
  if (filters.status && gen.status !== filters.status) return false;
  return true;
});

Realtime Updates

New generations appear automatically via Supabase Realtime:
// In AppProvider
useEffect(() => {
  const channel = supabase
    .channel('generations')
    .on('postgres_changes', {
      event: 'INSERT',
      schema: 'public',
      table: 'generations',
      filter: `user_id=eq.${userId}`,
    }, (payload) => {
      setGenerations(prev => [payload.new as Generation, ...prev]);
    })
    .on('postgres_changes', {
      event: 'UPDATE',
      schema: 'public',
      table: 'generations',
      filter: `user_id=eq.${userId}`,
    }, (payload) => {
      setGenerations(prev => 
        prev.map(g => g.id === payload.new.id ? payload.new as Generation : g)
      );
    })
    .subscribe();

  return () => { supabase.removeChannel(channel); };
}, [userId]);

Generation Detail Page

/generations/[id] shows full detail:
  • Full-size image
  • Complete prompt
  • Model used (nano-banana / nano-banana-pro)
  • Provider (Replicate / fal.ai)
  • Reference images used
  • Thread context (links to chat)
  • Quick actions (production, collection, share)

Export Options

Download PNG

Save high-resolution image to device

Copy Link

Copy shareable URL (public if enabled)

Performance

Gallery uses virtual scrolling for large collections:
  • Lazy loading: Images load on scroll into view
  • Pagination: 50 items per page
  • Blur placeholder: Low-res preview while loading
// Image with blur placeholder
<Image
  src={gen.imageUrl}
  alt={gen.prompt}
  width={300}
  height={300}
  placeholder="blur"
  blurDataURL={gen.thumbUrl || PLACEHOLDER_BLUR}
  className="object-cover"
/>