--- phase: 42-wallpapers-social-format-conversion-voice plan: 06 subsystem: ui tags: [react, typescript, drag-drop, format-conversion, deep-link, SSE, FormData, multipart, shadcn, lucide] # Dependency graph requires: - phase: 42-wallpapers-social-format-conversion-voice plan: 03 provides: POST /api/companies/:companyId/convert (multipart), GET /api/system/converters, ConvertBundle type - phase: 40-content-job-infra provides: useContentJob hook, getContentJobAsset, SSE job tracking pattern provides: - ui/src/api/convert.ts — submitConvertJob (multipart) and getConverterCapabilities API client - ui/src/components/ConvertPanel.tsx — drag-drop + grouped format chips + AI fallback notice + download - ui/src/pages/ConvertPage.tsx — /convert route with deep-link URL param reading - ui/src/App.tsx — three route variants /convert, /convert/:src, /convert/:src/:tgt affects: - Any future phase adding nav links to /convert # Tech tracking tech-stack: added: [] patterns: - Direct fetch (not api.post) for multipart submissions that need per-status-code inspection (422 vs 202) - Direct EventSource connection for pre-submitted jobs (bypasses useContentJob.submit which re-submits) - FORMAT_GROUPS constant exported from ConvertPanel for reuse in ConvertPage validation - normalizeFormatParam helper: toLowerCase + allowlist check, silently returns undefined for invalid params key-files: created: - ui/src/api/convert.ts - ui/src/components/ConvertPanel.tsx - ui/src/pages/ConvertPage.tsx modified: - ui/src/App.tsx key-decisions: - "Direct fetch used in submitConvertJob (not api.postForm) to inspect 422 vs 202 status before throwing — api.request() throws on !res.ok, but 422 is a valid business response with MIME error body" - "Direct EventSource in ConvertPanel for already-submitted jobs — useContentJob.submit() calls submitContentJob internally and cannot track an existing jobId without re-submitting" - "FORMAT_GROUPS exported from ConvertPanel so ConvertPage can import the same constant for allowlist validation without duplicating the list" - "AI fallback notice shown per target format group (Images/AV/Docs/Data) based on capabilities, not per individual format pair — matches CONV-08 spec that all paths fall to AI bridge when direct converter unavailable" patterns-established: - "Format chip role=radio within role=radiogroup — matches accessibility spec from 42-UI-SPEC.md" - "Drop zone uses motion-safe: Tailwind variant to disable transition for prefers-reduced-motion" requirements-completed: [CONV-01, CONV-02, CONV-03, CONV-04, CONV-05, CONV-06, CONV-07, CONV-08, CONV-09] # Metrics duration: 12min completed: 2026-04-04 --- # Phase 42 Plan 06: Format Conversion UI Summary **Drag-drop ConvertPanel with grouped format chips, AI fallback notice, MIME error display, SSE job tracking, and deep-link /convert/:src/:tgt routing** ## Performance - **Duration:** 12 min - **Started:** 2026-04-04T22:17:33Z - **Completed:** 2026-04-04T22:29:00Z - **Tasks:** 2 - **Files modified:** 4 (3 created, 1 modified) ## Accomplishments - Created convert.ts API client: submitConvertJob with direct fetch for 422/202 status differentiation; getConverterCapabilities for startup capability probe - Built ConvertPanel with all three visual zones: ConvertSourceZone (drag-drop with idle/dragover/error states), ConvertTargetSelector (grouped chips with AI fallback notice), ConvertActionBar (disabled until ready, download after done) - FORMAT_GROUPS constant drives chip rendering and is exported for ConvertPage format validation - ConvertPage reads URL params, normalizes to lowercase, silently ignores invalid format strings - Three route variants wired in App.tsx: /convert, /convert/:sourceFormat, /convert/:sourceFormat/:targetFormat ## Task Commits 1. **Task 1: Create convert API client and ConvertPanel component** - `c0040f66` (feat) 2. **Task 2: Create ConvertPage and wire routes in App.tsx** - `b5587c03` (feat) ## Files Created/Modified - `ui/src/api/convert.ts` - submitConvertJob (multipart POST, 422/202 handling), getConverterCapabilities - `ui/src/components/ConvertPanel.tsx` - Full ConvertPanel: drag-drop zone, FORMAT_GROUPS chips, AI fallback notice, SSE progress, download - `ui/src/pages/ConvertPage.tsx` - Route page reading sourceFormat/targetFormat URL params with case-insensitive normalization - `ui/src/App.tsx` - Added ConvertPage lazy import and three /convert route variants inside boardRoutes() ## Decisions Made - **Direct fetch in submitConvertJob** — api.request() throws on any !res.ok response, but 422 is a valid business response carrying the MIME mismatch error body. Using raw fetch lets us return it as a ConvertMimeError value instead of catching and re-parsing an ApiError. - **Direct EventSource for pre-submitted jobs** — useContentJob.submit() calls submitContentJob internally; it has no "track existing job" API. Since submitConvertJob already dispatched the convert job via the multipart route (not contentJobs route), a standalone EventSource connection is used to track progress. - **FORMAT_GROUPS exported from ConvertPanel** — ConvertPage needs the same allowlist for normalizeFormatParam validation. Exporting from ConvertPanel avoids duplication and keeps them in sync. - **AI fallback notice per group** — The capabilities object returns converter availability per category (imageConverter, audioVideoConverter, docConverter, dataConverter), not per format pair. Showing the notice at group level matches the granularity available from /api/system/converters. ## Deviations from Plan None — plan executed exactly as written. ## Known Stubs None — all conversion flows are fully wired to real backend endpoints. ## Issues Encountered None — tsc compiles cleanly for all new/modified files. 17 pre-existing errors in unrelated files (AgentConfigForm, useKeyboardShortcuts, useNexusMode, usePiperTts, useVadRecorder, ContentStudio, PersonalAssistant) were present before this plan and not introduced here. ## User Setup Required None — no external service configuration required. ## Next Phase Readiness - All CONV requirements fulfilled (CONV-01 through CONV-09) - /convert, /convert/:src, and /convert/:src/:tgt routes live and navigable - Phase 42 is complete — all 6 plans executed --- *Phase: 42-wallpapers-social-format-conversion-voice* *Completed: 2026-04-04* ## Self-Check: PASSED - `ui/src/api/convert.ts` — FOUND - `ui/src/components/ConvertPanel.tsx` — FOUND - `ui/src/pages/ConvertPage.tsx` — FOUND - `.planning/phases/42-wallpapers-social-format-conversion-voice/42-06-SUMMARY.md` — FOUND - Task 1 commit `c0040f66` — FOUND - Task 2 commit `b5587c03` — FOUND