From 7f12e5f909a7254030ffbe808c09b9a7af85ac03 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 02:50:22 +0000 Subject: [PATCH 1/4] Fix Fitting Room overflow and add Save as Outfit to Extractor - Fixed Fitting Room page overflow by constraining height and using ScrollArea for the Generated Looks section. - Added "Save as Outfit" checkbox and input to the Refine stage of the Extractor dialog. - Installed @radix-ui/react-checkbox and added Shadcn Checkbox component. - Updated Dockerfile to include example-data directory in the final image. --- Dockerfile | 1 + bun.lock | 1 + package.json | 1 + src/ui/components/ExtractorDialog.tsx | 35 ++++++++++++++++- src/ui/components/extractor/RefineStage.tsx | 43 ++++++++++++++++++++- src/ui/components/ui/checkbox.tsx | 30 ++++++++++++++ src/ui/pages/FittingRoom.tsx | 7 ++-- 7 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 src/ui/components/ui/checkbox.tsx diff --git a/Dockerfile b/Dockerfile index f147eac..57a72cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,7 @@ COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/bunfig.toml ./bunfig.toml COPY --from=builder /app/public ./public COPY --from=builder /app/prisma.config.ts ./prisma.config.ts +COPY --from=builder /app/example-data ./example-data # Create data directory RUN mkdir -p data diff --git a/bun.lock b/bun.lock index 0c98846..20eb8ff 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "@prisma/adapter-libsql": "7.3.0", "@prisma/client": "^7.3.0", "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-scroll-area": "^1.2.10", diff --git a/package.json b/package.json index 39e3d17..7953427 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@prisma/adapter-libsql": "7.3.0", "@prisma/client": "^7.3.0", "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.8", "@radix-ui/react-scroll-area": "^1.2.10", diff --git a/src/ui/components/ExtractorDialog.tsx b/src/ui/components/ExtractorDialog.tsx index 9e63890..2188b4c 100644 --- a/src/ui/components/ExtractorDialog.tsx +++ b/src/ui/components/ExtractorDialog.tsx @@ -34,6 +34,10 @@ export const ExtractorDialog: React.FC = ({ const [results, setResults] = useState([]) const [isRefineComplete, setIsRefineComplete] = useState(false) + // Save as Outfit state + const [saveAsOutfit, setSaveAsOutfit] = useState(false) + const [outfitName, setOutfitName] = useState('') + // Refs for cleanup const analyzeController = useRef(null) const refineController = useRef(null) @@ -48,6 +52,8 @@ export const ExtractorDialog: React.FC = ({ setSelectedIndices([]) setResults([]) setIsRefineComplete(false) + setSaveAsOutfit(false) + setOutfitName('') analyzeController.current?.abort() refineController.current?.abort() } @@ -194,7 +200,30 @@ export const ExtractorDialog: React.FC = ({ const handleClose = () => onOpenChange(false) - const handleDone = () => { + const handleDone = async () => { + if (saveAsOutfit) { + if (!outfitName.trim()) { + toast.error('Please enter an outfit name') + return + } + try { + const equipmentIds = results.map((r) => r.id) + const res = await client.outfits.$post({ + json: { + name: outfitName, + equipmentIds, + }, + }) + if (res.ok) { + toast.success('Outfit created') + } else { + toast.error('Failed to create outfit') + } + } catch (e) { + console.error(e) + toast.error('Failed to create outfit') + } + } onSuccess(results) handleClose() } @@ -234,6 +263,10 @@ export const ExtractorDialog: React.FC = ({ selectedCandidates={candidates.filter((_, i) => selectedIndices.includes(i))} results={results} isComplete={isRefineComplete} + saveAsOutfit={saveAsOutfit} + setSaveAsOutfit={setSaveAsOutfit} + outfitName={outfitName} + setOutfitName={setOutfitName} /> )} diff --git a/src/ui/components/extractor/RefineStage.tsx b/src/ui/components/extractor/RefineStage.tsx index a5d9c76..7bd3495 100644 --- a/src/ui/components/extractor/RefineStage.tsx +++ b/src/ui/components/extractor/RefineStage.tsx @@ -2,24 +2,35 @@ import { CheckCircle2, Loader2 } from 'lucide-react' import React from 'react' import { cn } from '../../lib/utils' import { CandidateAsset, ExtractedAsset } from '../../types' +import { Checkbox } from '../ui/checkbox' +import { Input } from '../ui/input' +import { Label } from '../ui/label' import { ScrollArea } from '../ui/scroll-area' interface RefineStageProps { selectedCandidates: CandidateAsset[] results: ExtractedAsset[] isComplete: boolean + saveAsOutfit: boolean + setSaveAsOutfit: (value: boolean) => void + outfitName: string + setOutfitName: (value: string) => void } export const RefineStage: React.FC = ({ selectedCandidates, results, isComplete, + saveAsOutfit, + setSaveAsOutfit, + outfitName, + setOutfitName, }) => { return (
{/* Grid */} - -
+ +
{selectedCandidates.map((candidate, index) => { const isFinished = index < results.length const isProcessing = index === results.length && !isComplete @@ -114,6 +125,34 @@ export const RefineStage: React.FC = ({ })}
+ + {isComplete && ( +
+
+ setSaveAsOutfit(c === true)} + /> + +
+ {saveAsOutfit && ( + setOutfitName(e.target.value)} + className="max-w-xs animate-in fade-in zoom-in duration-300" + autoFocus + /> + )} +
+ )} +
) } diff --git a/src/ui/pages/FittingRoom.tsx b/src/ui/pages/FittingRoom.tsx index ee5fb50..19fe2e6 100644 --- a/src/ui/pages/FittingRoom.tsx +++ b/src/ui/pages/FittingRoom.tsx @@ -10,13 +10,13 @@ import { LookDetailsDialog } from '../components/LookDetailsDialog' import { PageHeader } from '../components/PageHeader' import { Button } from '../components/ui/button' import { Card } from '../components/ui/card' -import { ScrollArea } from '../components/ui/scroll-area' import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, } from '../components/ui/dropdown-menu' +import { ScrollArea } from '../components/ui/scroll-area' // Types - Define locally to avoid complex type extraction if client inference is tricky // But we should try to use inference @@ -122,7 +122,7 @@ export default function FittingRoom() { {character.name}

{character.name}

diff --git a/src/ui/pages/Poses.tsx b/src/ui/pages/Poses.tsx index 77c2ef0..4497b76 100644 --- a/src/ui/pages/Poses.tsx +++ b/src/ui/pages/Poses.tsx @@ -111,6 +111,7 @@ export default function Poses() { } }} /> +
) }