--- phase: 3 slug: document-migration-multi-user-isolation status: approved reviewed_at: 2026-05-23 shadcn_initialized: false preset: none created: 2026-05-23 --- # Phase 3 — UI Design Contract > Visual and interaction contract for Phase 3: Document Migration & Multi-User Isolation. > Generated by gsd-ui-researcher, verified by gsd-ui-checker. --- ## Design System | Property | Value | |----------|-------| | Tool | none — raw Tailwind CSS v3 | | Preset | not applicable | | Component library | none | | Icon library | Inline SVG heroicons (stroke, no fill) — matches existing AppSidebar, DropZone, and UploadProgress patterns | | Font | System font stack (Tailwind default: ui-sans-serif, system-ui, sans-serif) | **Source:** Inherited from Phase 2 UI-SPEC (approved 2026-05-22). No design system changes in Phase 3. `tailwind.config.js` has empty `theme.extend` — no custom tokens. --- ## Spacing Scale Identical to Phase 2 — inherited, not re-specified. | Token | Value | Usage | |-------|-------|-------| | xs | 4px | Icon-to-label gaps, badge padding, progress step gaps | | sm | 8px | Compact inline spacing, quota bar label gap | | md | 16px | Default field padding, card internal padding | | lg | 24px | Section vertical gap | | xl | 32px | Upload card top margin | | 2xl | 48px | Page vertical centering padding | | 3xl | 64px | Page-level clearance | Exceptions: - Upload progress bar track height: 8px (`h-2`). Justification: 4px (xs) is too narrow to show color transitions clearly at small viewport widths; 8px is the established browser progress-bar idiom and remains within the scale. - Quota bar track height: 8px (`h-2`). Same justification as upload progress bar. - Quota bar touch/click area (sidebar widget, not interactive): no min-height required — display-only element. **Source:** Phase 2 UI-SPEC approved spacing scale. Phase 3 adds no new interactive controls that require new spacing exceptions. --- ## Typography Identical to Phase 2 — inherited, not re-specified. | Role | Size | Weight | Line Height | Tailwind Class | |------|------|--------|-------------|----------------| | Body | 14px | 400 (regular) | 1.5 | `text-sm` | | Label | 14px | 600 (semibold) | 1.4 | `text-sm font-semibold` | | Heading | 20px | 600 (semibold) | 1.3 | `text-xl font-semibold` | | Display | 24px | 600 (semibold) | 1.2 | `text-2xl font-semibold` | Font weight scale: exactly 2 weights — regular (400) and semibold (600). Do not introduce `font-medium` (500) or `font-bold` (700). Do not use `font-medium` that already appears in `.nav-link` scoped styles — that is a pre-existing pattern in AppSidebar and must not be propagated to new components. **New in Phase 3:** - Quota bar label: `text-sm text-gray-500` — visual subordination via muted color, not reduced size. - Upload step label (beneath progress bar): `text-sm text-gray-400` — subordination via color only; same contrast pattern as existing `UploadProgress.vue` status lines. - Error detail lines in quota rejection: `text-sm text-red-600` — matches Phase 2 inline field error style. All three usages remain within the 4-size scale (Body/Label/Heading/Display). The existing `UploadProgress.vue` pattern of `text-xs` is NOT propagated to new Phase 3 code — all new code uses `text-sm` with color differentiation for hierarchy. **Source:** Phase 2 UI-SPEC. Maximum 4 type sizes enforced — no `text-xs` in new Phase 3 components. --- ## Color Identical to Phase 2 palette — no new colors introduced. Phase 3 adds two state-specific usages within the existing amber and red semantic tokens. | Role | Value | Tailwind Token | Usage | |------|-------|----------------|-------| | Dominant (60%) | #f9fafb | `bg-gray-50` | Page background | | Secondary (30%) | #ffffff | `bg-white` | Cards, upload progress rows, sidebar | | Accent (10%) | #4f46e5 | `indigo-600` / `indigo-700` | Primary CTA buttons only, active nav link text, brand logo text | | Accent subtle | #eef2ff | `indigo-50` | Active nav link background; upload progress bar fill (in-progress step) | | Destructive | #dc2626 | `red-600` | Quota bar at ≥ 95% fill; quota rejection error banner border/text | | Warning | #d97706 | `amber-600` | Quota bar at ≥ 80% fill (below 95%); quota warning text | | Success | #16a34a | `green-600` | Upload complete checkmark icon — existing UploadProgress pattern | | Neutral border | #e5e7eb | `gray-200` | Card borders, progress row borders, quota bar track background | | Muted text | #9ca3af | `gray-400` | Quota bar label secondary text, upload step sub-labels | **Source:** Phase 2 UI-SPEC. CONTEXT.md STORE-04 locks amber at 80%, red at 95%. CONTEXT.md D-07 specifies HTTP 413 response — maps to `red-600` error treatment. Accent reserved for: primary CTA buttons, active sidebar nav link text, DocuVault brand/logo text, in-progress upload step indicator fill (`indigo-50` track fill). Accent is NOT used on: quota bar fills, error banners, or secondary link text. ### Quota Bar Color Logic (State Machine) | Condition | Bar fill color | Label color | |-----------|---------------|-------------| | `usage < 80%` | `bg-indigo-500` | `text-gray-500` | | `80% ≤ usage < 95%` | `bg-amber-500` | `text-amber-600` | | `usage ≥ 95%` | `bg-red-500` | `text-red-600` | Bar track (background): `bg-gray-200` always. Bar fill width: `style="width: {percent}%"` clamped to `max-w-full`. --- ## Upload Flow — Interaction Contract ### Overview The two-step presigned upload (CONTEXT.md D-05) is invisible to the user as a multi-step operation. The user sees a single continuous upload experience from file selection to completion. ### Step Mapping to UI States | Internal Step | User-visible state | |---------------|--------------------| | Step 1: `POST /api/documents/upload-url` | "Preparing upload…" — spinner active, 0% progress | | Step 2: `PUT {presigned_url}` (browser → MinIO) | "Uploading…" — progress bar advances 5% → 90% using XHR `progress` event | | Step 3: `POST /api/documents/{id}/confirm` | "Processing…" — progress bar at 95%, spinner resumes | | Celery enqueued (confirm returns 200) | "Done — classifying…" — progress bar completes to 100%, green checkmark | | Error at any step | Error state (see Error States below) | ### Progress Bar Visual Contract The progress bar is an addition to the existing `UploadProgress.vue` component row. Each upload row in `UploadProgress.vue` gains a progress bar between the filename and the status line: ``` [ filename.pdf (spinner/checkmark/error icon) ] [ ████████████░░░░░░░░░░ 62% ] [ Uploading… ] ``` Bar specifications: - Track: `w-full h-2 bg-gray-100 rounded-full mt-1` - Fill: `h-2 rounded-full transition-all duration-300` + color class from state - In-progress fill color: `bg-indigo-500` - Complete fill color: `bg-green-500` - Error fill color: `bg-red-400` (stops at last % value, does not reset to 0) - Percentage label: `text-sm text-gray-400 text-right mt-1` — shown during upload, hidden after completion ### Upload Progress Values by Step | Step | Progress value | Visual | |------|---------------|--------| | Awaiting upload URL | 0% | Bar at 0, status "Preparing upload…" | | Upload URL received | 5% | Bar jumps to 5% | | XHR progress events | 5% → 90% (linear from XHR `loaded/total`) | Bar animates smoothly | | Confirm call in flight | 92% | Bar at 92%, status "Processing…" | | Confirm returned 200 | 100% | Bar fills green, status "Done — classifying…" | The 5%–90% range is reserved for the XHR PUT. The remaining 10% (90%–100%) covers confirm + classification enqueue. This prevents the bar appearing "stuck" at 100% waiting for the confirm call. ### CORS and MinIO PUT The browser PUTs directly to MinIO over a presigned URL (cross-origin). This is transparent to the user. If the PUT fails due to a network error or CORS rejection, treat it as a generic upload error (see Error States). Do not surface "CORS" in any user-facing copy. --- ## Quota Usage Bar — Sidebar Contract ### Placement in AppSidebar.vue Insert the quota bar between the topics nav section (`.flex-1 nav`) and the bottom settings/admin/identity footer (`.px-3.py-4.border-t`). ``` [ Topics nav section ] [ ────────────────────── ] ← existing border-t border-gray-100 [ Quota bar widget ] ← NEW: Phase 3 [ ────────────────────── ] ← existing border-t border-gray-100 [ Admin / Settings / Footer ] ``` ### Quota Bar Widget Structure ```html