Overview
A complete graphics and logos management module that replaces the placeholder library tabs with a fully functional, database-backed asset system. Users can upload, organise, and manage graphic assets (team logos, sponsor graphics, badges) through a dedicated management section, then browse and add them to uniform designs directly from the builder.
Features
Graphics Management Pages (/home/assets/graphics)
- Data Table — Paginated, searchable table of all graphics with thumbnail preview, name, category, format, and status columns
- Create / Edit Forms — Drag-and-drop file upload to S3, auto-generated code from name, category assignment, tags, sort order, and active toggle
- Category Manager — Dialog-based CRUD for creating, editing, and deleting graphic categories with slug auto-generation
- Filtering — Server-side filtering by category, file format, and active status with URL-based search params
- Soft Delete / Restore — Graphics and categories support soft-delete with restore capability via database functions
Database Schema
graphic_categories— User-defined categories with name, slug, description, sort order, active flag, and soft-deletegraphics— Assets with name, unique code, category FK, S3 file URL, format, dimensions, tags (array), sort order, and soft-delete- RLS Policies — Authenticated users can read all active records; write operations restricted to creator or super admins
- Indexes — Optimised queries on slug, code, category, format, active status, tags (GIN), and soft-delete filter
- Functions —
soft_delete_graphic,restore_graphic,soft_delete_graphic_category,restore_graphic_category
Builder Library Integration
- Library Tab — The builder's graphics step "Library" tab now shows a live browsable grid of all active graphics from the database
- Search & Filter — Client-side search by name/code and category dropdown filter within the builder
- One-Click Add — Click any graphic to instantly add it to the canvas with auto-detected dimensions (px → mm conversion)
- Loading & Empty States — Spinner during fetch, helpful empty-state message pointing to the management section
Shared Upload Hook
useS3Uploadrefactored frombuilder/_lib/hooks/to~/lib/hooks/for reuse across both the builder and the graphics management form- WebP Support — Added
image/webpto accepted MIME types in both the upload API route and the builder's format mapping
Navigation & i18n
- Sidebar Link — "Logos & Graphics" entry added under Assets in the personal account navigation
- i18n Namespace — New
graphicsnamespace with full translation coverage for the management UI (form labels, table headers, actions, errors, categories) - Path Config —
graphics,graphicsNew, andgraphicDetailroutes registered inpaths.config.ts
Technical Details
Architecture
- Server Actions —
createGraphicAction,updateGraphicAction,deleteGraphicAction,restoreGraphicActionplus matching category actions, all usingenhanceActionwith Zod validation and auth - Service Layer —
GraphicServiceandGraphicCategoryServiceclasses handle Supabase queries with pagination, filtering, code/slug uniqueness checks, and category joins - Zod Schemas —
CreateGraphicSchema,UpdateGraphicSchema,DeleteGraphicSchema,RestoreGraphicSchema(and category equivalents) for input validation - Builder Hook —
useGraphicsLibraryuses React Query (@tanstack/react-query) with 5-minute stale time to fetch graphics with joined category data
File Structure
assets/graphics/
├── page.tsx # List page with data table
├── new/page.tsx # Create form page
├── [id]/page.tsx # Edit form page
├── _components/
│ ├── graphics-data-table.tsx # Paginated table with filters
│ ├── graphic-form.tsx # Create/edit form with S3 upload
│ └── graphic-category-manager.tsx # Category CRUD dialog
└── _lib/
├── schemas/
│ ├── graphic.schema.ts # Zod schemas + types
│ └── graphic-category.schema.ts
└── server/
├── server-actions.ts # Enhanced server actions
├── graphic.service.ts # Database service
└── graphic-category.service.ts
Data Flow
- Admin uploads a graphic via the management form → S3 pre-signed URL → stored in
graphicstable - Builder loads the library tab →
useGraphicsLibraryfetches active graphics with category join via React Query - User searches/filters and clicks a graphic →
handleAddFromLibrarymaps DB fields to builder'sGraphicItemshape and appends to the form field array