From 708fd7fad0d9a99fc340bbbb75372103ea621377 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Sun, 31 May 2026 11:41:32 +0200 Subject: [PATCH] =?UTF-8?q?docs(phase-6.2):=20record=20planning=20complete?= =?UTF-8?q?=20=E2=80=94=204=20plans=20verified,=20state=20updated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ROADMAP.md: progress table → Planned; wave annotations already added by planner - STATE.md: phase 6.2 row → Planned (4 plans, 3 waves); session note added - 06.2-03-PLAN.md: remove incorrect SHARE-03/SHARE-05 from requirements field - 06.2-RESEARCH.md: mark Open Questions section as RESOLVED - 06.2-UI-SPEC.md: add to version control (was untracked) Verification: 0 blockers, 2 cosmetic warnings fixed. Co-Authored-By: Claude Sonnet 4.6 --- .planning/ROADMAP.md | 2 +- .planning/STATE.md | 5 +- .../06.2-03-PLAN.md | 6 +- .../06.2-RESEARCH.md | 2 +- .../06.2-UI-SPEC.md | 319 ++++++++++++++++++ 5 files changed, 326 insertions(+), 8 deletions(-) create mode 100644 .planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-UI-SPEC.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index d3c298d..9b6a182 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -369,4 +369,4 @@ Before any phase is marked complete, all three gates must pass: | 5. Cloud Storage Backends | 12/12 | Complete | 2026-05-30 | | 6. Performance & Production Hardening | 0/TBD | Not started | — | | 6.1. Close v1.0 audit gaps | 2/2 | Complete | 2026-05-30 | -| 6.2. Close v1 sharing + cloud-delete + CSV export gaps | 0/4 | Not started | — | +| 6.2. Close v1 sharing + cloud-delete + CSV export gaps | 0/4 | Planned | — | diff --git a/.planning/STATE.md b/.planning/STATE.md index 7c4a702..aeab19f 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -31,7 +31,7 @@ progress: | 5 | Cloud Storage Backends | ✓ Complete (12/12 plans, UAT 5/6 passed, 3 gaps closed by 05-12) | | 6 | Performance & Production Hardening | Not started | | 6.1 | Close v1.0 audit gaps: SHARE-02/STORE-06/ADMIN-06 | ✓ Complete (2/2 plans) | -| 6.2 | Close v1 sharing + cloud-delete + CSV export gaps | Not started | +| 6.2 | Close v1 sharing + cloud-delete + CSV export gaps | Planned (4 plans, 3 waves) | ## Current Position @@ -197,6 +197,7 @@ _Updated at each phase transition._ | Last session | 2026-05-30 — Phase 5 UAT: 5/6 tests passed; 3 gaps diagnosed (OneDrive unconfigured 500, cloud doc stream opaque 500, DropZone disappeared); gap-closure plan 05-12 created (3 tasks, wave 1) | | Last session | 2026-05-30 — Plan 05-12 executed: OAuth 400 preflight (unconfigured creds), 502 cloud fallback, celery-worker volume mount, upload hint in CloudStorageView; 293 passed / 24 xfailed / 1 pre-existing failure | | Last session | 2026-05-30 — Phase 6.1 executed: 7 share tests + 4 audit tests promoted from xfail stubs; second_auth_user fixture added; 309 passed / 0 failed | -| Next action | Run /gsd:verify-work 6.1 to confirm Phase 6.1 complete | +| Last session | 2026-05-31 — Phase 6.2 planned: 4 plans (3 waves); SHARE-03/SHARE-05 (Plan 02), cloud-delete (Plan 03), ADMIN-06 audit enrichment + CSV + daily exports (Plan 04); verification passed (0 blockers, 2 cosmetic warnings fixed) | +| Next action | Run /gsd:execute-phase 6.2 | | Pending decisions | None | | Resume file | None | diff --git a/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-03-PLAN.md b/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-03-PLAN.md index 55ce86c..32ca5b2 100644 --- a/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-03-PLAN.md +++ b/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-03-PLAN.md @@ -12,10 +12,8 @@ files_modified: - frontend/src/api/client.js - backend/tests/test_documents.py autonomous: true -requirements: - - SHARE-03 - - SHARE-05 - - ADMIN-06 +requirements: [] + # cloud-delete (D-01..D-04) is covered via phase success criteria; no named REQUIREMENTS.md ID must_haves: truths: - "Deleting a cloud document calls the cloud provider's delete_object, not MinIO's" diff --git a/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-RESEARCH.md b/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-RESEARCH.md index 79c559e..07161bc 100644 --- a/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-RESEARCH.md +++ b/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-RESEARCH.md @@ -601,7 +601,7 @@ return StreamingResponse( --- -## Open Questions +## Open Questions (RESOLVED) 1. **Cloud delete partial failure: HTTP status code choice** - What we know: D-03 says return a structured error the frontend can distinguish from hard failure. diff --git a/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-UI-SPEC.md b/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-UI-SPEC.md new file mode 100644 index 0000000..a019097 --- /dev/null +++ b/.planning/phases/06.2-close-v1-sharing-cloud-delete-csv-export-gaps/06.2-UI-SPEC.md @@ -0,0 +1,319 @@ +--- +phase: 6.2 +slug: close-v1-sharing-cloud-delete-csv-export-gaps +status: draft +shadcn_initialized: false +preset: none +created: 2026-05-31 +--- + +# Phase 6.2 — UI Design Contract + +> Visual and interaction contract for Phase 6.2: Close v1 sharing + cloud-delete + CSV export gaps. +> Generated by gsd-ui-researcher. Verified by gsd-ui-checker. + +--- + +## Design System + +| Property | Value | +|----------|-------| +| Tool | none — pure Tailwind CSS v3.4 | +| Preset | not applicable | +| Component library | none (Heroicons inline SVG only) | +| Icon library | Heroicons stroke, w-4 / w-5 sizes (inline SVG, no package) | +| Font | system-ui (browser default stack — no custom font loaded) | + +**Source:** codebase scan — no `components.json`, no component registry, confirmed via directory listing. + +--- + +## Spacing Scale + +Declared values (all multiples of 4, mapped to Tailwind utilities): + +| Token | Value | Tailwind Class | Usage | +|-------|-------|----------------|-------| +| xs | 4px | `gap-1`, `p-1` | Icon gaps, inline badge padding | +| sm | 8px | `gap-2`, `p-2` | Compact element spacing, button icon padding | +| md | 16px | `p-4`, `gap-4` | Default element spacing, card body padding | +| lg | 24px | `p-6`, `gap-6` | Modal body padding, section padding | +| xl | 32px | `p-8` | Empty state vertical padding | +| 2xl | 48px | `py-12` | Page-level empty state vertical rhythm | +| 3xl | 64px | n/a | Not used in this phase | + +**Exceptions:** + +- Icon-only action buttons: `min-h-[44px] min-w-[44px]` touch target — established in DocumentCard and maintained for all new icon buttons. Source: `DocumentCard.vue` line 43. +- Share row inline items: `py-2` (8px vertical) per row — matches existing `ShareModal.vue` recipient list rhythm. +- Permission dropdown in share creation row: no extra spacing; sits inline within the existing `flex gap-2` row. + +--- + +## Typography + +| Role | Size | Weight | Line Height | Tailwind Classes | +|------|------|--------|-------------|------------------| +| Body | 14px | 400 | 1.5 | `text-sm` | +| Label / caption | 12px | 600 | 1.4 | `text-xs font-semibold` | +| Heading (modal title) | 18px | 600 | 1.2 | `text-lg font-semibold` | +| Mono (timestamps, IDs) | 12px | 400 | 1.4 | `text-xs font-mono` | + +**Source:** codebase scan — `ShareModal.vue` uses `text-lg font-semibold` for modal title, `text-sm` for body text, `text-xs` for labels and badges. `AuditLogTab.vue` uses `font-mono text-xs` for timestamps and IP addresses. All four roles are already present in the components being modified. + +--- + +## Color + +| Role | Value | Tailwind Class | Usage | +|------|-------|----------------|-------| +| Dominant (60%) | #F9FAFB | `bg-gray-50` | Page background, table header row | +| Secondary (30%) | #FFFFFF | `bg-white` | Cards, modals, table body, dropdown panels | +| Accent (10%) | #4F46E5 | `bg-indigo-600` / `text-indigo-600` | Primary action buttons, focus rings, topic pills, shared badge | +| Destructive | #EF4444 | `text-red-500` / `text-red-600` / `bg-red-50` | Remove access button, cloud delete failure modal warning text, error inline text | + +**Accent reserved for (explicit list):** + +1. Primary CTA buttons: "Share document", "Apply filters", "Export CSV", "Download" — `bg-indigo-600 hover:bg-indigo-700 text-white` +2. Focus rings on all text inputs and selects: `focus:ring-2 focus:ring-indigo-500` +3. "Shared" indicator pill on DocumentCard: `bg-indigo-50 text-indigo-600` +4. Permission badge when set to "edit" (distinguished from "view" gray): `bg-indigo-50 text-indigo-600` — matches topic pill pattern +5. View/Edit toggle active state: `bg-indigo-50 text-indigo-600` + +**Destructive reserved for:** + +1. "Remove access" inline link in ShareModal recipient row — `text-red-500 hover:text-red-700` +2. Cloud delete failure modal — warning message text `text-red-700`, border accent on the modal (see Component Contracts below) +3. Inline error text under inputs — `text-red-600 text-xs` + +**Source:** codebase scan — colors extracted from `ShareModal.vue`, `DocumentCard.vue`, `AuditLogTab.vue`, `AdminUsersTab.vue`. + +--- + +## Copywriting Contract + +### Permission Dropdown (ShareModal — share creation row) + +| Element | Copy | +|---------|------| +| Dropdown label (aria-label) | "Permission level" | +| Option: view | "Can view" | +| Option: edit | "Can edit" | +| Default selected | "Can view" | + +### View/Edit Toggle (ShareModal — per share row) + +| Element | Copy | +|---------|------| +| Toggle label: view active | "View" | +| Toggle label: edit active | "Edit" | +| Aria-label pattern | "Change permission for {handle}" | +| Optimistic error (toggle fails) | "Failed to update permission." | + +### Cloud Delete Failure Modal + +| Element | Copy | +|---------|------| +| Modal heading | "Cloud delete failed" | +| Body text | "The file could not be deleted from {provider}. Remove it from DocuVault anyway? The file will remain on {provider}." | +| Primary CTA (remove from app) | "Remove from app" | +| Secondary action (cancel) | "Cancel" | +| Aria-label for modal | "Cloud delete warning" | + +**Note:** `{provider}` is replaced at runtime with the cloud provider display name (e.g., "Google Drive", "OneDrive"). If the provider name is unavailable, fall back to "your cloud storage". + +### Audit Log — Daily Exports Section + +| Element | Copy | +|---------|------| +| Section label | "Daily exports" | +| Dropdown label | "Select date" | +| Dropdown placeholder | "Choose a date" | +| Download button | "Download" | +| Empty state (no exports in bucket) | "No daily exports available." | +| Loading state (fetching list) | "Loading exports…" | + +### Audit Log — CSV Export Fix (behavior only, no copy change) + +The "Export CSV" button label is unchanged. The behavior changes from `window.location.href` to `fetch()` + Blob URL. No new copy needed — the button already reads "Export CSV". + +### Audit Log — User Filter Label + +| Element | Copy | +|---------|------| +| Filter field label (was "User") | "User handle" | +| Input placeholder (was "All users") | "All users" | + +### Shared Badge Fix (DocumentCard — no copy change) + +The "Shared" pill copy is unchanged (`Shared`). Only the v-if condition changes from `doc.share_count > 0` to `doc.is_shared`. No copy update. + +### Error States + +| Scenario | Copy | +|----------|------| +| Share creation — user not found | "User not found. Check the handle and try again." (unchanged, already in ShareModal) | +| Share creation — already shared | "This document is already shared with that user." (unchanged) | +| Share creation — generic error | "Something went wrong. Please try again." (unchanged) | +| Permission update failed | "Failed to update permission." | +| Daily export download failed | "Download failed. Please try again." | +| CSV export request failed | "Export failed. Please try again." | +| Cloud delete failure | See modal copy above. | + +### Destructive Actions + +| Action | Confirmation Approach | +|--------|-----------------------| +| Remove access (revoke share) | Optimistic — immediate removal on click, restore on API failure. No confirmation dialog. Matches existing pattern in `ShareModal.vue:handleRevoke()`. | +| Delete cloud document (default) | Browser `window.confirm()` replaced by the cloud delete failure modal only when the API returns `{"cloud_delete_failed": true}`. Normal delete (MinIO or successful cloud delete) keeps the existing `window.confirm()` on `DocumentView.vue`. | +| "Remove from app" (cloud delete failure path) | Confirmed via the cloud delete failure modal primary CTA. Single click on "Remove from app" proceeds without a second confirmation. | + +--- + +## Component Contracts + +### C-1: Permission Dropdown in ShareModal (share creation row) + +**Location:** `frontend/src/components/sharing/ShareModal.vue` — insert between the handle input and the "Share document" button within the `flex gap-2` row. + +**Markup contract:** + +``` + +``` + +- `permission` reactive ref defaults to `"view"` +- Passed as `permission: permission.value` in the `shareDocument()` call +- Width: `shrink-0` — does not expand; native select width for 2 options is sufficient (~90px) +- Sits between handle input (`flex-1`) and Share button (`shrink-0`) + +### C-2: View/Edit Toggle in ShareModal (per share row) + +**Location:** `frontend/src/components/sharing/ShareModal.vue` — replace the static `view` in each recipient row. + +**Visual spec:** + +- Two adjacent pill buttons: "View" and "Edit" +- Active state: `bg-indigo-50 text-indigo-600 font-medium` +- Inactive state: `bg-gray-100 text-gray-600` +- Both use: `text-xs px-2 py-1 rounded-full font-medium transition-colors` +- Wrapper: `flex rounded-full overflow-hidden border border-gray-200` (pill group container) +- Spacing: no gap between the two buttons (joined pills) + +**Interaction:** + +- Clicking the inactive state calls `PATCH /api/shares/{id}` with `{ permission: "view" | "edit" }` +- Optimistic update: toggle state immediately on click, revert on API error +- Loading state: button shows spinner inline while PATCH is in-flight; both toggle buttons `opacity-50 pointer-events-none` during in-flight state +- Error: show `error.value = "Failed to update permission."` below the row (same `text-xs text-red-600 mt-2` pattern) + +### C-3: Cloud Delete Failure Modal + +**Location:** New component `frontend/src/components/documents/CloudDeleteWarningModal.vue` OR inline conditional block in `DocumentView.vue` — inline in DocumentView is preferred (mirrors how the inline delete confirmation panel works in AdminUsersTab). + +**Trigger:** `confirmDelete()` in `DocumentView.vue` receives `{ cloud_delete_failed: true }` from the delete API call. Instead of navigating away, set `showCloudDeleteWarning.value = true`. + +**Visual spec:** + +``` +Fixed overlay: bg-black/40 flex items-center justify-center z-50 +Panel: bg-white rounded-2xl shadow-xl p-6 max-w-sm w-full mx-4 +``` + +- Heading: `text-lg font-semibold text-gray-900 mb-2` — "Cloud delete failed" +- Body: `text-sm text-gray-600 mb-6` — full warning sentence (see Copywriting Contract) +- Warning icon: Heroicons `ExclamationTriangleIcon` w-5 h-5 text-amber-500 inline before heading, or `text-red-600` — use amber-500 (warning, not error) to match the semantic distinction: this is a degraded-success, not a hard failure +- Button row: `flex gap-3 mt-4 justify-end` + - Primary ("Remove from app"): `bg-red-600 hover:bg-red-700 text-white text-sm px-4 py-2 rounded-lg transition-colors` + - Secondary ("Cancel"): `border border-gray-300 text-gray-700 text-sm px-4 py-2 rounded-lg hover:bg-gray-50 transition-colors` +- `role="dialog"` `aria-modal="true"` `aria-labelledby` on the panel +- Click-outside (`@click.self`) closes the modal (same as ShareModal) +- Pressing "Cancel" closes modal; document is NOT deleted. The pending delete is abandoned. +- Pressing "Remove from app" calls `DELETE /api/documents/{id}?remove_only=true`, then navigates to `/` on success. + +**Warning icon Tailwind:** `text-amber-500` (Tailwind `amber-500` = `#F59E0B`) — signals a recoverable warning state distinct from the hard red error color. + +### C-4: Daily Exports Section in AuditLogTab + +**Location:** `frontend/src/components/admin/AuditLogTab.vue` — add as a new section below the existing pagination block. + +**Visual spec:** + +- Section separator: `
` +- Section label: `

Daily exports

` +- Controls row: `
` + - Date select: `