Skip to content

Web Application Architecture

The Web Application is the user-facing frontend of the Sartiq platform, providing an intuitive interface for fashion brands to manage their products, configure shootings, and review AI-generated imagery.


Overview

Aspect Details
Framework Next.js 14 with App Router
UI Library React 18
Language TypeScript
Styling Tailwind CSS
State Management Zustand + React Query
Port 3000

Architecture Diagram

flowchart TB
    subgraph Browser["Browser"]
        UI[React Components]
        Stores[Zustand Stores]
        RQ[React Query Cache]
        WS[WebSocket Client]
    end

    subgraph NextJS["Next.js Server"]
        Pages[App Router Pages]
        API[API Routes]
        SSR[Server Components]
    end

    subgraph External["External"]
        Backend[Backend API]
        CDN[CDN]
    end

    UI --> Stores
    UI --> RQ
    UI --> WS
    Pages --> SSR
    Pages --> API
    RQ -->|fetch| Backend
    WS -->|events| Backend
    API -->|proxy| Backend
    CDN -->|content| UI

Key Features

E-Commerce Generator

The main generation interface where users configure and execute image generation.

Capabilities:

  • Product selection from catalog
  • Subject assignment
  • Style and guideline configuration
  • Shot type selection
  • Real-time generation progress
  • Side-by-side comparison views

Review and manage generated images.

Capabilities:

  • Grid and list views
  • Filtering and sorting
  • Image approval workflow
  • Revision requests
  • Bulk operations
  • Export functionality

Asset Management

Manage products, subjects, styles, guidelines, and other assets.

Modules:

  • Products: Upload, categorize, and manage product catalog
  • Subjects: Browse and select AI models (base images)
  • Styles: View and configure visual styles
  • Guidelines: Configure technical specs (background, resolution, format, shot types)
  • Shootings: Organize generation sessions

Admin Dashboard

Platform administration for internal users.

Features:

  • User management
  • Organization management
  • System monitoring
  • Configuration management

Page Structure

app/
├── (auth)/
│   ├── login/
│   └── signup/
├── (dashboard)/
│   ├── products/
│   │   ├── page.tsx          # Product list
│   │   └── [id]/page.tsx     # Product detail
│   ├── subjects/
│   ├── shootings/
│   │   ├── page.tsx          # Shooting list
│   │   └── [id]/
│   │       ├── page.tsx      # Shooting detail
│   │       └── looks/[lookId]/
│   ├── gallery/
│   │   ├── page.tsx          # Gallery grid
│   │   └── [id]/page.tsx     # Image detail
│   └── generator/
│       └── page.tsx          # E-Commerce Generator
├── admin/
│   ├── users/
│   ├── organizations/
│   └── settings/
└── api/
    └── [...proxy]/           # Backend proxy routes

State Management

Zustand Stores

Global application state managed with Zustand.

flowchart TB
    subgraph Stores [Zustand Stores]
        Auth[authStore]
        UI[uiStore]
        Gen[generatorStore]
        Gallery[galleryStore]
    end

    subgraph Components [Components]
        C1[Header]
        C2[Generator]
        C3[Gallery]
        C4[Sidebar]
    end

    Auth --> C1
    Auth --> C4
    UI --> C1
    UI --> C4
    Gen --> C2
    Gallery --> C3

Key Stores:

Store Purpose
authStore User authentication state, session management
uiStore UI state (sidebar, modals, theme)
generatorStore Generator configuration and state
galleryStore Gallery filters and selection

React Query

Server state management for API data.

Patterns:

  • Automatic caching and invalidation
  • Optimistic updates for better UX
  • Background refetching
  • Pagination support
// Example: Products query
const { data: products, isLoading } = useQuery({
  queryKey: ['products', filters],
  queryFn: () => api.products.list(filters),
});

// Example: Mutation with optimistic update
const mutation = useMutation({
  mutationFn: api.products.update,
  onMutate: async (newProduct) => {
    await queryClient.cancelQueries(['products']);
    const previous = queryClient.getQueryData(['products']);
    queryClient.setQueryData(['products'], (old) => /* optimistic update */);
    return { previous };
  },
  onError: (err, variables, context) => {
    queryClient.setQueryData(['products'], context.previous);
  },
  onSettled: () => {
    queryClient.invalidateQueries(['products']);
  },
});

API Integration

Auto-Generated Client

The API client is auto-generated from the Backend's OpenAPI schema.

flowchart LR
    Backend[Backend API] -->|OpenAPI spec| Generator[openapi-typescript]
    Generator -->|types| Types[API Types]
    Generator -->|client| Client[API Client]
    Client -->|used by| App[Application]

Benefits:

  • Type-safe API calls
  • Automatic updates when API changes
  • Reduced boilerplate
  • IntelliSense support

API Client Structure

lib/
├── api/
│   ├── client.ts         # HTTP client setup
│   ├── types.ts          # Generated types
│   ├── products.ts       # Product endpoints
│   ├── shootings.ts      # Shooting endpoints
│   ├── generations.ts    # Generation endpoints
│   └── index.ts          # Exports

File Upload System

The webapp uploads files directly to Cloudflare R2 via presigned URLs, bypassing the Backend for file data transfer.

Upload Architecture

sequenceDiagram
    participant U as User
    participant W as Webapp
    participant B as Backend
    participant R2 as Cloudflare R2

    U->>W: Select files
    W->>W: Validate (type, size)
    W->>B: POST /api/v1/uploads/presigned-url
    B-->>W: {upload_url, file_url, expires_in_seconds}
    W->>R2: PUT file directly to presigned URL
    R2-->>W: 200 OK
    W->>W: Store file_url in upload state

Upload Module Structure

Path Purpose
src/lib/uploads/services/ Core upload orchestration, API client, error handling
src/lib/uploads/hooks/ React hooks (useFileUpload, useUploadProgress, useUploadUrls)
src/lib/uploads/store/ Zustand store for upload state management
src/lib/uploads/types/ TypeScript type definitions
src/lib/uploads/utils/ Retry logic, file validation, helper utilities

Upload Groups

Group Constant Usage
Product images product-images Product cover, back, and reference images
Style images style-images Style reference images
Subject images subject-images Subject base images
Background images background-images Background reference images

State Management

Upload state is managed via a dedicated Zustand store with the following lifecycle:

stateDiagram-v2
    [*] --> pending: addUpload
    pending --> uploading: Start upload
    uploading --> completed: Upload success
    uploading --> error: Upload failed
    uploading --> retrying: Retry triggered
    retrying --> uploading: Retry attempt
    error --> retrying: Manual retry
    completed --> [*]: markUrlsConsumed

Retry & Error Handling

Setting Value
Max attempts 3
Initial delay 1 second
Backoff multiplier 2x
Max delay 10 seconds
Jitter Enabled
Error Type Description
network_error Network connectivity issue
file_too_large Exceeds 50MB limit
invalid_file_type Unsupported MIME type
presigned_url_failed Backend failed to generate presigned URL
upload_failed R2 upload request failed
timeout Upload timed out

Validation

Check Requirement
Max file size 50MB
Allowed types image/jpeg, image/png, image/webp, image/gif, image/avif
Max files per batch 10

Content Delivery

CDN URL Resolution

The webapp resolves relative file paths to full CDN URLs using the NEXT_PUBLIC_CDN_URL environment variable:

Environment NEXT_PUBLIC_CDN_URL
Production https://media.sartiq.com
Staging https://staging-media.sartiq.com
Development http://localhost:9002/shootify-media-dev

CORS Proxy

The webapp includes a CORS proxy route at /api/proxy/image for cross-origin image downloads (e.g., downloading generated images to the user's device):

Setting Value
Max file size 200MB
Supported formats WebP, PNG, JPEG (with ?type= conversion)
Cache 1 year (public, max-age=31536000)
Security SSRF protection, private IP blocking, protocol validation

Real-Time Updates

WebSocket Connection

Real-time updates for generation progress and notifications.

sequenceDiagram
    participant Browser
    participant WebSocket
    participant Backend
    participant Redis

    Browser->>WebSocket: Connect
    WebSocket->>Backend: Authenticate
    Backend-->>WebSocket: Connected

    Note over Backend,Redis: Generation completes
    Backend->>Redis: Publish event
    Redis->>Backend: Event
    Backend->>WebSocket: Push notification
    WebSocket->>Browser: Update UI

Event Types:

Event Description
generation.started Generation task began
generation.progress Progress update
generation.completed Generation finished
generation.failed Generation error
prediction.created New image available

WebSocket Hook

// Simplified WebSocket hook usage
const { isConnected, lastEvent } = useWebSocket({
  onMessage: (event) => {
    if (event.type === 'generation.completed') {
      queryClient.invalidateQueries(['generations', event.generationId]);
    }
  },
});

Authentication Flow

sequenceDiagram
    participant User
    participant App
    participant Backend

    User->>App: Enter credentials
    App->>Backend: POST /auth/login
    Backend-->>App: JWT token + user data
    App->>App: Store token in authStore
    App->>App: Redirect to dashboard

    Note over App,Backend: Subsequent requests
    App->>Backend: GET /api/... (with JWT)
    Backend-->>App: Protected data

Token Management:

  • JWT stored in memory (Zustand)
  • Refresh token in httpOnly cookie
  • Automatic token refresh
  • Logout clears all state

Component Architecture

Component Hierarchy

components/
├── ui/                    # Primitive UI components
│   ├── Button/
│   ├── Input/
│   ├── Modal/
│   └── ...
├── features/              # Feature-specific components
│   ├── products/
│   │   ├── ProductCard.tsx
│   │   ├── ProductGrid.tsx
│   │   └── ProductForm.tsx
│   ├── generator/
│   │   ├── GeneratorPanel.tsx
│   │   ├── ProductSelector.tsx
│   │   └── ProgressTracker.tsx
│   └── gallery/
│       ├── ImageGrid.tsx
│       ├── ImageDetail.tsx
│       └── ApprovalControls.tsx
├── layout/                # Layout components
│   ├── Header.tsx
│   ├── Sidebar.tsx
│   └── PageContainer.tsx
└── shared/                # Shared components
    ├── LoadingSpinner.tsx
    ├── ErrorBoundary.tsx
    └── EmptyState.tsx

Design Patterns

Compound Components:

// Product selection with compound pattern
<ProductSelector>
  <ProductSelector.Grid>
    <ProductSelector.Item product={product} />
  </ProductSelector.Grid>
  <ProductSelector.Pagination />
</ProductSelector>

Render Props:

// Data fetching with render props
<DataLoader query={productsQuery}>
  {(products) => <ProductGrid products={products} />}
</DataLoader>

Styling

Tailwind CSS

Utility-first CSS with custom design tokens.

// tailwind.config.ts
export default {
  theme: {
    extend: {
      colors: {
        primary: { /* brand colors */ },
        secondary: { /* ... */ },
      },
      fontFamily: {
        sans: ['Plus Jakarta Sans', 'sans-serif'],
      },
    },
  },
};

Component Styling Pattern

// cn utility for conditional classes
import { cn } from '@/lib/utils';

function Button({ variant, size, className, ...props }) {
  return (
    <button
      className={cn(
        'rounded-lg font-medium transition-colors',
        variant === 'primary' && 'bg-primary-500 text-white',
        variant === 'secondary' && 'bg-gray-100 text-gray-900',
        size === 'sm' && 'px-3 py-1.5 text-sm',
        size === 'md' && 'px-4 py-2 text-base',
        className
      )}
      {...props}
    />
  );
}

Performance Optimization

Code Splitting

  • Route-based splitting via Next.js
  • Dynamic imports for heavy components
  • Lazy loading for images

Image Optimization

  • Next.js Image component for automatic optimization
  • CDN for generated content
  • Responsive image sizes
  • WebP/AVIF format support

Caching Strategy

  • React Query caching for API data
  • Service Worker for static assets
  • CDN caching for images

Key Directories

Directory Purpose
app/ Next.js App Router pages and layouts
components/ React components
lib/ Utilities, API client, helpers
src/lib/uploads/ File upload system (presigned URLs, state, retry logic)
hooks/ Custom React hooks
stores/ Zustand store definitions
types/ TypeScript type definitions
styles/ Global styles and Tailwind config
public/ Static assets