Closes the coverage-gap audit's sharpest item (H8/D-auth): mountAuth's
return is captured (authGuards) and design-route ownership is passed
explicitly to mountEditorApi (customerId/isAdmin off req.customer/
req.admin — matches the module defaults but survives default drift);
/api/fonts/registered is admin-gated by a host-level requireAdmin
registered ahead of the module route (auth.middleware can't do this:
the module applies it to the whole /designs group, which would lock
guests out of design persistence). mockupsDir/storageDir now threaded
so design-route thumbnails get the mockup backdrop. Dev harness
unchanged: without DATABASE_URL the roster stays open and ownership
resolves to the guest model.
Also fixes a real bug the boot test surfaced: the host hardcoded
001_designs.sql, silently skipping 002_design_family.sql — the store
SELECTs design_family_id unconditionally, so every GET /api/designs
500'd ('column does not exist'). Now applies every *.sql in the
module's migrations dir in filename order (all idempotent).
Verified by booting both modes: auth ON → roster 403 admin_required,
designs unknown-id 404 (was 500), both migrations applied on a fresh
db; auth OFF → roster 200 with the font list, warning logged. NOTE:
goods-editor's ./server export resolves to dist/, which was stale
(built Jul 7 — predating the module's auth option, roster route, and
Aug security fixes); rebuilt from the branch source. Any deploy must
run the module's build, not just npm install the file: dep.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closes the audit's unstyled-classes item: 48 emitted-but-unstyled
classes across InventoryListPanel (tm-admin-inventory__*, mirroring the
tm-admin-list chrome; stock badge ok/low/out on the success/warning/
danger tokens), InventoryDetailPanel (tm-inventory-detail__* — stock
hero with state-colored count, tx history table with signed deltas,
danger zone) and BulkReceiveModal (tm-bulk-receive__* + the wide modal
card). Class-inventory header updated per the file's own convention;
verified by diffing emitted class names against stylesheet selectors
(only template-literal modifier stems and the tm-meta__field-riding
tm-facet-tags wrapper remain, both intentional).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Server (spans the v0.6→v0.8 arc in one file):
- template-manager mounted with the productsStore catalog, PNG-only
mockup multer, facet personalization config (§R10, FACET_KEY-gated,
inert when unset), and the marketing-preview drainer (2s tick, one
render per tick, .unref()).
- auth-manager bootstrap: DATABASE_URL gates the pg Pool, migrations,
and mountAuth (magic-link customer + Google SSO admin + facet);
absent DATABASE_URL logs a warning and the host boots without auth.
- goods-editor design persistence (#23): idempotent 001_designs.sql
applied at boot, db handle passed to mountEditorApi so /api/designs
routes mount; without Postgres the client falls back to
cart-without-design_id.
- reference-protected TTL sweep (#24): before each pass, every
/uploads/ basename referenced by template-manager JSON or persisted
designs is collected and protected, and exports/designs (db-driven
lifecycle, unrecoverable sources) is skipped entirely. Sweep kickoff
moves below the bootstrap (pgPool TDZ). sweep-worker.mjs is the
standalone process variant for production.
Client: main.jsx wraps the tree in AuthProvider (single /api/auth/me
fetch shared by useCustomer/useAdmin, degrades on 404) and mounts
AppRouter. eslint bumps server.js to ecmaVersion 2022 (top-level
await); .env.example documents the full v0.8 env surface.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
index.html now loads /api/fonts/fonts.css, synthesized at request time
by goods-editor's mountEditorApi from the host's fonts/ directory
(populated via fetch-fonts in goods-editor-module — the host-local
fetch script shrinks to a pointer). Shipped synchronously in <head>
on purpose: the module's runtime injector races first paint and the
editor's text measurement gives wrong answers against fallback
metrics until real fonts load.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AppRouter replaces the single-page App as the mounted root: shop routes
(/ and /shop → ShopHome, /shop/:productSlug → ProductPage, /cart →
CartStub, /customize → CustomizeRoute) plus the template-manager admin
surfaces (/admin/templates|products|featured|trash, v0.8 adds
/admin/inventory + RequireAdmin wrapping). App.jsx stays on disk one
release as the documented legacy fallback — nothing imports it.
New shop UI: components/shop/ (ShopHome/Hero/ProductGrid/ProductCard/
ProductPage/ProductGallery/ProductInfo/ProductBuyPanel/CartStub/
ShopHeader/TemplateGalleryForProduct), hooks (useShopProducts/
useShopProduct/useCart/useFacetCatalog), and shop.css + admin.css.
Header gains the module-gated Clear button (onCanClearChange /
onElementCountChange mirror the editor module's history-commit
signals); i18n keys follow. vite proxies /products to the Node server
(template-manager serves product imagery — without it the SPA fallback
returns index.html as an image) and adds a template-manager chunk.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The host no longer carries a runtime product catalog. products.js
shrinks to a legacy single-product stub; the canonical catalog lives in
template-manager's productsStore, seeded ONCE on first boot from the
new seedProducts.js (after that the store under templates/ is
authoritative and the seed is ignored — intentional, so restarts never
rewrite an admin-managed catalog). phoneModels.js and
templateCategories.js are the supporting constant sets, and
docs/template-schema.md documents the v4 schema.
templates/ is the admin-managed store itself (_products_index.json +
products/<id>/product.json, JSON only) — committed so the configured
catalog (sizes, pricing, options built up in the admin UI) survives a
fresh checkout rather than regressing to the seed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
goods-editor moves from the git+ssh tag to file:../goods-editor-module,
joined by file: deps on template-manager and @khalidadil/auth-manager
(scoped name — published to the Gitea registry as 0.2.0; the file:
protocol stays for local development, the key must match the scoped
name). pg lands for the auth/design-persistence Postgres pool and
react-router-dom for the v0.7 routed app.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Merge client and server dependencies into root package.json
- Remove separate client/package.json and server/package.json
- Update server/index.js to serve built client static files
- Simplify Dockerfile to single build + production stage
- Update dev scripts for unified development workflow
- SPA routing serves index.html for non-API routes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 6 - Template System:
- Add TemplateLayer component for background/overlay rendering
- Add SlotPlaceholder component with visual indicators for empty slots
- Add useTemplate hook with auto-crop and drag constraint functions
- Update templates.js with slot definitions for team-sport template
- Integrate template system into DesignCanvas and App
- Add slot upload UI in TemplatesTab sidebar
Phase 9 - PWA Improvements:
- Add Workbox caching rules for HuggingFace LFS, templates, and API
- Change registerType to 'prompt' for update notifications
- Add service worker update handler in main.jsx
- Add refresh prompt UI in PWAInstall component
Phase 10 - Responsive and Accessibility:
- Add responsive CSS media queries for tablet/mobile layouts
- Add OfflineIndicator component with online/offline detection
- Add focus trap and keyboard navigation to PhotoPreEditor
- Add aria labels and screen reader support to modal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added comprehensive README.md with:
- Feature list and tech stack
- Installation and Docker instructions
- Project structure documentation
- API endpoint reference
- Build plan status checklist
- Verified all 10 phases complete
- Code review passed - no security vulnerabilities found
- Ready for production deployment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implemented full editor UI with three-column layout:
- Sidebar with 4 tabs (Upload, Stickers, Text, Templates)
- UploadTab with drag-and-drop file upload, wires to POST /api/upload
- StickersTab with 96 emoji stickers across 6 categories
- TextTab with font picker (20 Google Fonts), size slider, color picker
- TemplatesTab placeholder for future template system
- LayersPanel showing all elements with select/delete
- PropertiesPanel with position, size, rotation controls
Also added:
- Constants for fonts and stickers
- Enhanced CSS with editor-layout, sidebar, properties-panel classes
- Updated App.jsx to integrate all components
- History tracking with 50-state limit in useDesignEditor hook
- Undo: Ctrl/Cmd + Z keyboard shortcut
- Redo: Ctrl/Cmd + Shift + Z or Ctrl/Cmd + Y
- Undo/Redo buttons in canvas header
- History saves state after add, update, delete, reorder operations
- Disabled button states when history is exhausted
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added react-filerobot-image-editor dependency
- PhotoPreEditor component with full editing capabilities
- Crop, filters, adjustments, annotations, watermark tabs
- Opens after image upload, before adding to canvas
- Exports edited image as PNG for canvas use
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added @xenova/transformers dependency
- useBackgroundRemoval hook with RMBG-1.4 model
- Client-side background removal with progress indicator
- Background removal button in properties panel (image elements only)
- ~170MB model cached after first download
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Three-column layout (sidebar/canvas/properties)
- Sidebar with tabs: Upload, Stickers, Text
- Upload tab with drag-and-drop and click-to-upload
- Stickers tab with 6 categories (40+ emojis)
- Text tab with font selector, size slider, color picker
- Properties panel with position, size, rotation controls
- Delete button for selected element
- Responsive layout for mobile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Root package.json with concurrently for dev server
- Client: Vite + React 18 with design tokens and proxy config
- Server: Express with CORS, multer (20MB), Sharp resize
- Upload endpoint with preview generation
- Dockerfile (multi-stage) and docker-compose.yml
- Canvas deferred to Phase 8 (export functionality)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>