Skip to content

Repository files navigation

SecondLook — QR-Powered Virtual Try-On for Second-Hand Retail

A digital layer for physical consignment and second-hand stores. Every one-of-one garment gets a QR code. Shoppers scan it to virtually try on that exact piece, build a virtual rack, and decide which items deserve a real trip to the fitting room.

Try the rack, not the fitting room.


The problem

Virtual try-on is usually an e-commerce feature. Physical second-hand retail has an unusually strong need for it:

  • every garment is a one-of-one — no restock in another size;
  • one rack contains French sizing, vintage Italian, US sizing, altered cuts, and decades of conventions;
  • shoppers have to physically try many pieces just to decide what is worth considering;
  • fitting rooms become part of discovery rather than the final confirmation step.

The goal is not to replace the fitting room — it is to turn ten fitting-room experiments into three high-confidence candidates.

How it works

Shopkeeper (intake): photograph the garment → run the YouCam readiness pipeline → enter inventory and consignment details → generate a QR tag and attach it to the physical item.

Shopper (scan): upload one reusable photo → scan any QR in the store → see that exact garment on themselves via YouCam Clothes V3 → add promising pieces to a virtual rack → shortlist for the real fitting room.


Getting started

Prerequisites

  • Node.js 20+
  • A YouCam API key (Clothes V3 required; Shoes optional)
  • An OpenRouter API key (for AI garment cleanup and item-analysis autofill)

Install

git clone <repo>
cd youcam-ai-hackathon
npm install

Environment

Copy .env.example to .env.local and fill in your keys:

cp .env.example .env.local
Variable Required Description
YOUCAM_API_KEY Yes YouCam API key — server-side only, never exposed to the browser
YOUCAM_API_BASE_URL Yes YouCam API base URL (default: https://yce-api-01.makeupar.com)
NEXT_PUBLIC_APP_URL Yes Public URL of this app (used for QR code generation)
OPENROUTER_API_KEY Yes OpenRouter API key — server-side only
OPENROUTER_NANOBANANA_MODEL Yes Model for garment photo cleanup (default: google/gemini-2.5-flash-imagerem)
OPENROUTER_VISION_MODEL Yes Model for item-detail autofill (default: google/gemini-3-flash-preview)

Run

npm run dev        # development server at http://localhost:3000
npm run build      # production build
npm start          # serve production build

Test

npm test           # run full test suite (Vitest, no watch)
npm run test:watch # watch mode
npm run lint       # ESLint

Architecture

Tech stack

Layer Technology
Framework Next.js 16 (App Router, Turbopack)
Language TypeScript 5, React 19
Styling CSS Modules
State React useReducer + context (no external state library)
Persistence IndexedDB via idb (locally created items only; session state only for shopper photo)
QR generation qrcode.react
QR scanning @zxing/browser
Testing Vitest + Testing Library + fake-indexeddb
Deployment Vercel (serverless functions for all API routes)

Request flow

All API keys live only on the server. The browser never sees YOUCAM_API_KEY or OPENROUTER_API_KEY.

Browser
  │
  ├── POST /api/youcam/clothes/tasks          → YouCam task creation
  ├── GET  /api/youcam/clothes/tasks/[taskId] → YouCam task poll
  ├── POST /api/youcam/shoes/tasks            → YouCam Shoes task
  ├── GET  /api/youcam/shoes/tasks/[taskId]   → YouCam Shoes poll
  ├── POST /api/openrouter/garment-cleanup    → Nano Banana photo cleanup
  └── POST /api/openrouter/item-analysis      → Vision model item autofill

YouCam task lifecycle (Clothes V3)

YouCam's VTO is asynchronous. The app models the full state machine:

idle → preparing → submitted → processing → success
                                          ↘ error (recoverable / terminal)

For Try the Rack, each item has its own independent task state so one failure does not destroy the rack session.


Project structure

src/
├── app/
│   ├── api/
│   │   ├── openrouter/
│   │   │   ├── garment-cleanup/     # POST: Nano Banana photo enhancement
│   │   │   └── item-analysis/       # POST: vision-model garment attribute autofill
│   │   └── youcam/
│   │       ├── clothes/tasks/       # POST + GET [taskId]: Clothes V3
│   │       └── shoes/tasks/         # POST + GET [taskId]: Shoes VTO
│   ├── items/[id]/                  # Shopper item page (seeded + locally created)
│   ├── shopkeeper/
│   │   ├── intake/                  # Multi-step consignment intake wizard
│   │   └── tags/[id]/               # QR tag preview + print
│   └── shopper/
│       ├── photo/                   # Shopper photo upload
│       ├── rack/                    # Virtual rack + results
│       └── fitting-room/            # Fitting-room shortlist
│
├── components/
│   ├── qr/                          # QR generator + printable tag
│   ├── shopkeeper/                  # Inventory card, status badges
│   ├── shopper/                     # Item hero, rack item card
│   └── vto/                         # VTO result display
│
├── data/                            # Seeded demo garment fixtures
│
├── features/
│   ├── intake/                      # Intake reducer, wizard, steps, photo enhance
│   ├── rack/                        # Rack reducer + results
│   ├── shopper/                     # Shopper session + IndexedDB persistence
│   └── vto/                         # Single-item VTO orchestration
│
├── lib/
│   ├── db/                          # IndexedDB (idb) shopper state store
│   ├── env.ts                       # Server-side env validation helpers
│   ├── fit/                         # Measurement fit estimation
│   ├── image/                       # Canvas rotation utility
│   ├── items/                       # Item resolver (seeded + local)
│   ├── openrouter/                  # garment-cleanup.ts, item-analysis.ts
│   ├── qr/                          # QR ID generation
│   ├── vto/                         # YouCam task client
│   └── youcam/                      # YouCam API wrappers + category routing
│
└── types/                           # Shared TypeScript types (ConsignmentItem, etc.)

Key modules

src/lib/openrouter/garment-cleanup.ts — calls OpenRouter with a fidelity-preserving prompt (Nano Banana model). Accepts ArrayBuffer, returns a base64 data URL. Never logs the API key or raw payloads.

src/lib/openrouter/item-analysis.ts — vision-model adapter that returns structured ItemAnalysis with per-field { value, confidence, source }. Drops malformed suggestions silently.

src/features/intake/item-analysis-policy.ts — confidence policy gate. brand and labelSize require confidence ≥ 0.90 and source visible_text. name/category/color require ≥ 0.80. material requires ≥ 0.85. Never overwrites a non-empty field.

src/lib/image/rotate-image-data-url.ts — browser-only canvas utility. Draws rotated pixels into a new canvas and returns a data URL. Used by PhotoEnhancePanel so the exact rotated pixels flow into YouCam validation and IndexedDB.

src/features/intake/reducer.ts — single useReducer for the full intake draft. Handles photo lifecycle, AI analysis state, VTO readiness, and item patch with field-overwrite protection.

src/lib/db/shopper-store.ts — IndexedDB store (secondlook-demo / shopperState) for locally created items. Shopper photos are never persisted — they live only in React session state.


API routes

YouCam — Clothes V3

Method Route Body Response
POST /api/youcam/clothes/tasks { shopperImageUrl, referenceImageUrl, category } { taskId }
GET /api/youcam/clothes/tasks/[taskId] { status, resultUrl? }

YouCam — Shoes

Method Route Body Response
POST /api/youcam/shoes/tasks { shopperImageUrl, referenceImageUrl } { taskId }
GET /api/youcam/shoes/tasks/[taskId] { status, resultUrl? }

OpenRouter — Garment cleanup

Method Route Body Response
POST /api/openrouter/garment-cleanup multipart/form-data with image file { dataUrl }

Accepted MIME types: image/jpeg, image/png, image/heic, image/heif. Max 10 MB.

OpenRouter — Item analysis

Method Route Body Response
POST /api/openrouter/item-analysis multipart/form-data with image file { analysis: ItemAnalysis }

Data model

No production database. Seeded fixture items live in src/data/. Locally created items are stored in IndexedDB for the current browser only.

type ConsignmentItem = {
  id: string;
  qrId: string;
  brand: string;
  name: string;
  category: string;
  labelSize: string;
  sizeSystem?: string;
  color?: string;
  material?: string;
  condition?: string;
  fitNotes?: string;
  measurements?: { chest?; waist?; hips?; length?; inseam?; other? };
  originalImage?: string;       // original merchant photo — never analyzed after this point
  vtoReferenceImage: string;    // YouCam-ready reference (may be enhanced + rotated)
  vtoProvider: "youcam-clothes-v3" | "youcam-shoes";
  garmentCategory?: "upper_body" | "lower_body" | "full_body";
  vtoReadiness: "unchecked" | "checking" | "ready" | "retake";
  consignorNumber?: string;
  sellingPrice: number;
  currency: string;
  consignorShare?: number;
  storeShare?: number;
  intakeDate?: string;
  expiryDate?: string;
};

Security constraints

  • YOUCAM_API_KEY and OPENROUTER_API_KEY are never exposed to the browser, passed in NEXT_PUBLIC_* variables, logged, or included in thrown errors.
  • Customer photos are not persisted to any database, server filesystem, analytics service, or localStorage. The File lives only in React state for the current session.
  • Shopper measurements are not sent to YouCam or OpenRouter.
  • Item analysis output is never used to modify the VTO reference image.
  • The AI autofill policy never overwrites a field the shopkeeper has already filled in.

Demo items

The app ships a small catalogue of seeded garments for cross-device QR demos (a consistent QR resolves without a database). Routes are slugs like /items/max-mara-wool-coat. Each fixture has static metadata and a bundled reference image.

The intake wizard still works fully for new items — they are saved to IndexedDB and accessible on the same browser session.


Roadmap

  • Persistent inventory backend
  • Consignor profiles and commission ledger
  • Shoes VTO polished flow
  • Background VTO pre-processing at intake
  • AI measurement guidance and size-system normalization
  • Online catalogue / marketplace export from intake data

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages