---
phase: 10-ux-interaction
plan: 12
type: execute
wave: 5
depends_on: [10-01, 10-06, 10-07, 10-08, 10-09, 10-10, 10-11]
files_modified:
  - frontend/src/components/storage/StorageBrowser.vue
  - frontend/src/components/layout/AppSidebar.vue
  - frontend/src/components/admin/AdminSidebar.vue
  - frontend/src/components/folders/FolderRow.vue
  - frontend/src/components/folders/FolderTreeItem.vue
  - frontend/src/components/folders/FolderDeleteModal.vue
  - frontend/src/components/documents/DocumentCard.vue
  - frontend/src/components/documents/DocumentPreviewModal.vue
  - frontend/src/components/documents/SearchBar.vue
  - frontend/src/components/sharing/ShareModal.vue
  - frontend/src/components/upload/DropZone.vue
  - frontend/src/components/upload/UploadProgress.vue
  - frontend/src/components/ui/TreeItem.vue
  - frontend/src/components/ui/SearchableModelSelect.vue
  - frontend/src/components/settings/SettingsCloudTab.vue
  - frontend/src/components/cloud/CloudFolderTreeItem.vue
  - frontend/src/components/cloud/CloudProviderTreeItem.vue
  - frontend/src/views/AccountView.vue
  - frontend/src/views/CloudStorageView.vue
  - frontend/src/views/SettingsView.vue
  - frontend/src/views/admin/AdminUsersView.vue
  - frontend/src/views/admin/AdminAuditView.vue
  - frontend/src/views/admin/AdminQuotasView.vue
  - frontend/src/views/admin/AdminAiView.vue
  - frontend/src/views/admin/AdminOverviewView.vue
autonomous: true
requirements: [CODE-05]
must_haves:
  truths:
    - "All inline <svg> blocks (~66 instances across ~29 files) are replaced with <AppIcon name='...' class='...' /> calls"
    - "Each affected file imports AppIcon from the correct relative path"
    - "Visual rendering is unchanged - same classes carry through to the <svg> via $attrs.class"
    - "The full test suite continues to pass post-migration (no regression)"
    - "No inline `<svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">` blocks remain (except in AppIcon.vue itself, AppSpinner.vue spinner, and UploadProgress.vue fill-based icons if they cannot be swapped)"
  artifacts:
    - path: "frontend/src/**/*.vue"
      provides: "Updated to use AppIcon"
  key_links:
    - from: "Each modified .vue file"
      to: "AppIcon.vue"
      via: "import AppIcon from <relative-path>/components/ui/AppIcon.vue"
      pattern: "import AppIcon"
---

<objective>
Complete CODE-05 by replacing every inline `<svg>` block in the frontend with an `<AppIcon name="..." class="..." />` call. This is the final wave of Phase 10 because every previous wave can freely modify its files without worrying about icon migration churn, and any new SVGs introduced earlier in the phase are caught here.

Per RESEARCH.md §Component Inventory §1, the audit found ~66 `<svg>` blocks across ~29 files mapping to ~30 named icons. The full Name -> d-attribute map lives in `AppIcon.vue` (from 10-01).

Output:
- Every affected file imports `AppIcon` from the correct relative path
- Every inline `<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="..." /></svg>` block (or equivalent) is replaced with `<AppIcon name="<assigned-name>" class="<original-classes>" />`
- AppSpinner.vue retains its inline spinner SVG (it is not an icon)
- UploadProgress.vue fill-based icons are swapped for stroke equivalents (checkCircle / exclamationCircle from AppIcon)
- FolderRow.vue's previously fill-based three-dot icon is replaced with `<AppIcon name="dots" />` (stroke variant added in 10-01)
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@CLAUDE.md
@.planning/phases/10-ux-interaction/10-CONTEXT.md
@.planning/phases/10-ux-interaction/10-RESEARCH.md
@.planning/phases/10-ux-interaction/10-PATTERNS.md
@frontend/src/components/ui/AppIcon.vue

<interfaces>
**Migration mapping (from RESEARCH.md §Component Inventory §1):**

For each file, identify each inline `<svg>` block by its `d` attribute, look up the icon name in the AppIcon ICON_PATHS map, then replace.

Example transformation:
```html
<!-- BEFORE -->
<svg class="w-4 h-4 mr-2 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
    d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7..." />
</svg>

<!-- AFTER -->
<AppIcon name="tag" class="w-4 h-4 mr-2 shrink-0" />
```

**Special cases:**
- Settings dual-path (cog + cogDot) in AppSidebar -> single `<AppIcon name="cog" class="..." />` (AppIcon handles the array)
- FolderRow dots (fill-based) -> `<AppIcon name="dots" class="w-4 h-4" />` (stroke replacement; ICON_PATHS already includes the stroke version per 10-01)
- UploadProgress fill-based check/error -> `<AppIcon name="checkCircle" class="..." />` / `<AppIcon name="exclamationCircle" class="..." />`
- BreadcrumbBar internal chevronRight (added in 10-03) is already AppIcon - no change needed
- AppSpinner internal spinner SVG -> KEEP (it is a special animation, not an icon)
- DocumentPreviewModal internal spinner SVG (if it uses `<circle>`) -> KEEP

**Per-file inline SVG count (from RESEARCH.md):**

| File | Approx SVG count | Notes |
|------|-----------------|-------|
| frontend/src/components/storage/StorageBrowser.vue | ~6 | plus, folder (x2), folderMove, pencil, trash, share, document |
| frontend/src/components/layout/AppSidebar.vue | ~10 | tag, inbox, cloud, shield, cog (dual-path), logout, chevronRight (x2) |
| frontend/src/components/admin/AdminSidebar.vue | ~6 | home, users, chartBar, lightBulb, clipboardList, logout |
| frontend/src/components/folders/FolderRow.vue | ~3 | folder, dots, pencil, trash |
| frontend/src/components/folders/FolderTreeItem.vue | ~2 | folder, chevronRight |
| frontend/src/components/folders/FolderDeleteModal.vue | 1 | warning |
| frontend/src/components/documents/DocumentCard.vue | ~3 | document, folderMove, trash |
| frontend/src/components/documents/DocumentPreviewModal.vue | 1 | x (plus spinner kept) |
| frontend/src/components/sharing/ShareModal.vue | 1 | x |
| frontend/src/components/upload/DropZone.vue | 1 | upload |
| frontend/src/components/upload/UploadProgress.vue | ~2 | checkCircle, exclamationCircle (fill-swap) |
| frontend/src/components/ui/TreeItem.vue | 1 | chevronRight |
| frontend/src/components/ui/SearchableModelSelect.vue | ~2 | chevronDown, pencilEdit |
| frontend/src/components/settings/SettingsCloudTab.vue | 1 | warning |
| frontend/src/components/cloud/CloudFolderTreeItem.vue | ~2 | folder, fileDoc |
| frontend/src/components/cloud/CloudProviderTreeItem.vue | 1 | cloud |
| frontend/src/views/AccountView.vue | 1 | checkMark |
| frontend/src/views/CloudStorageView.vue | 1 | cloud (now handled in 10-08 via EmptyState; verify no other inline svg) |
| frontend/src/views/SettingsView.vue | ~3 | checkCircle, exclamationCircle, x (x2) |
| frontend/src/views/admin/AdminUsersView.vue | ~3 | copy, check, refresh |
| frontend/src/views/admin/AdminAuditView.vue | possibly 0-1 after 10-08 changes |
| frontend/src/views/admin/AdminQuotasView.vue | 0-1 |
| frontend/src/views/admin/AdminAiView.vue | 0-1 |
| frontend/src/views/admin/AdminOverviewView.vue | 0-1 |

The numbers are approximate; the actual count comes from `grep -c "<svg" <file>` at execution time.

**Identification process:**
For each file:
1. Read the file
2. For each inline `<svg>` block, read the `d` attribute
3. Look up the matching name in ICON_PATHS (from 10-01)
4. Replace the whole `<svg>...</svg>` block with `<AppIcon name="<name>" class="<original class attribute value>" />`
5. Ensure the file imports AppIcon

If an inline SVG's `d` attribute does NOT match any known icon, halt and report it - DO NOT invent a name. Either:
(a) Add the missing name+d to ICON_PATHS in AppIcon.vue (and update the AppIcon test if necessary), OR
(b) Leave that one inline SVG untouched and note it as an exception in the SUMMARY.

**Import path patterns:**
- Files under `frontend/src/components/storage/` -> `import AppIcon from '../ui/AppIcon.vue'`
- Files under `frontend/src/components/layout/` -> `import AppIcon from '../ui/AppIcon.vue'`
- Files under `frontend/src/components/folders/` -> `import AppIcon from '../ui/AppIcon.vue'`
- Files under `frontend/src/components/documents/` -> `import AppIcon from '../ui/AppIcon.vue'`
- Files under `frontend/src/components/cloud/` -> `import AppIcon from '../ui/AppIcon.vue'`
- Files under `frontend/src/components/sharing/` -> `import AppIcon from '../ui/AppIcon.vue'`
- Files under `frontend/src/components/upload/` -> `import AppIcon from '../ui/AppIcon.vue'`
- Files under `frontend/src/components/settings/` -> `import AppIcon from '../ui/AppIcon.vue'`
- Files under `frontend/src/components/admin/` -> `import AppIcon from '../ui/AppIcon.vue'`
- Files under `frontend/src/components/ui/` -> `import AppIcon from './AppIcon.vue'`
- Files under `frontend/src/views/` -> `import AppIcon from '../components/ui/AppIcon.vue'`
- Files under `frontend/src/views/admin/` -> `import AppIcon from '../../components/ui/AppIcon.vue'`

For Options API files, also register: `components: { AppIcon, ... }`.
For `<script setup>` files, the import is enough.
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Migrate sidebar + top-level layout files (StorageBrowser, AppSidebar, AdminSidebar, TreeItem)</name>
  <files>
    frontend/src/components/storage/StorageBrowser.vue,
    frontend/src/components/layout/AppSidebar.vue,
    frontend/src/components/admin/AdminSidebar.vue,
    frontend/src/components/ui/TreeItem.vue
  </files>
  <read_first>
    - All four files (current state)
    - frontend/src/components/ui/AppIcon.vue (to know the available names and their d-values)
    - .planning/phases/10-ux-interaction/10-RESEARCH.md §"Component Inventory §1: SVG Audit" (file-to-icon map)
  </read_first>
  <action>
    For each file: add the AppIcon import (correct relative path). For Options API files (AppSidebar uses Options API, AdminSidebar likely the same, TreeItem may be either), register `AppIcon` in the `components: { ... }` map. For `<script setup>` files (StorageBrowser), only the import is needed.

    Then, for each inline `<svg>` block, replace as described in <interfaces>:

    **StorageBrowser.vue** (approx 6-7 SVGs):
    - "New folder" button SVG (d=`M12 4v16m8-8H4`) -> `<AppIcon name="plus" class="w-4 h-4" />`
    - Folder row icon (d starting `M3 7a2 2 0 012-2h4l2 2h8...`) -> `<AppIcon name="folder" class="w-4 h-4 text-amber-500" />`
    - Inline new-folder row folder icon -> same as above
    - File row pencil (rename) -> `<AppIcon name="pencil" class="..." />`
    - File row trash (delete) -> `<AppIcon name="trash" class="..." />`
    - File row share -> `<AppIcon name="share" class="..." />`
    - File row document icon -> `<AppIcon name="document" class="..." />`
    - File row folder-move (folderMove) -> `<AppIcon name="folderMove" class="..." />`

    **AppSidebar.vue** (approx 10 SVGs):
    - chevronRight x2 -> `<AppIcon name="chevronRight" class="..." />` each
    - tag (All Topics) -> `<AppIcon name="tag" class="..." />`
    - inbox (Shared with me) -> `<AppIcon name="inbox" class="..." />`
    - cloud (Cloud section) -> `<AppIcon name="cloud" class="..." />`
    - shield (Admin) -> `<AppIcon name="shield" class="..." />`
    - cog dual-path (Settings) -> `<AppIcon name="cog" class="..." />` (single tag - AppIcon handles the array)
    - logout (Sign out) -> `<AppIcon name="logout" class="..." />`

    **AdminSidebar.vue** (approx 6 SVGs): home, users, chartBar, lightBulb, clipboardList, logout. Replace each with the matching AppIcon name preserving the original class attribute.

    **TreeItem.vue** (1 SVG): chevronRight -> `<AppIcon name="chevronRight" class="..." />`.

    Preserve all existing classes verbatim. Do not change SVG container element types (e.g., if the SVG was inside a `<button>`, the AppIcon stays inside the `<button>`).
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run; cd frontend && npm run build</automated>
    Expected: full test suite passes; build succeeds.
  </verify>
  <acceptance_criteria>
    - StorageBrowser.vue: `grep -c "<svg" frontend/src/components/storage/StorageBrowser.vue` returns 0 (all SVGs migrated)
    - AppSidebar.vue: `grep -c "<svg" frontend/src/components/layout/AppSidebar.vue` returns 0
    - AdminSidebar.vue: `grep -c "<svg" frontend/src/components/admin/AdminSidebar.vue` returns 0
    - TreeItem.vue: `grep -c "<svg" frontend/src/components/ui/TreeItem.vue` returns 0
    - Each file contains `import AppIcon` from the correct relative path
    - Each file contains `<AppIcon name="..." class="..." />` calls
    - Full test suite passes
    - npm run build exits 0
  </acceptance_criteria>
  <done>4 sidebar/storage files fully migrated; no inline SVGs remain in any of them.</done>
</task>

<task type="auto">
  <name>Task 2: Migrate folder + document + upload + sharing components</name>
  <files>
    frontend/src/components/folders/FolderRow.vue,
    frontend/src/components/folders/FolderTreeItem.vue,
    frontend/src/components/folders/FolderDeleteModal.vue,
    frontend/src/components/documents/DocumentCard.vue,
    frontend/src/components/documents/DocumentPreviewModal.vue,
    frontend/src/components/documents/SearchBar.vue,
    frontend/src/components/sharing/ShareModal.vue,
    frontend/src/components/upload/DropZone.vue,
    frontend/src/components/upload/UploadProgress.vue
  </files>
  <read_first>
    - All nine files (current state)
    - frontend/src/components/ui/AppIcon.vue
    - .planning/phases/10-ux-interaction/10-RESEARCH.md §"Component Inventory §1" (file/icon map)
  </read_first>
  <action>
    For each file, repeat the migration pattern: add `import AppIcon`, register in components if Options API, replace each inline `<svg>` with `<AppIcon name="..." class="..." />`.

    Specific notes:
    - **FolderRow.vue**: the three-dot menu currently uses `fill="currentColor"` - replace with `<AppIcon name="dots" class="w-4 h-4" />` (stroke variant added in 10-01).
    - **FolderDeleteModal.vue**: warning icon -> `<AppIcon name="warning" class="..." />`.
    - **DocumentCard.vue**: document + folderMove + trash -> each with matching AppIcon name.
    - **DocumentPreviewModal.vue**: close `x` button SVG -> `<AppIcon name="x" class="..." />`. KEEP the loading spinner SVG (it uses `<circle>` and is not in the icon map).
    - **SearchBar.vue**: if SearchBar has a search icon inline, replace with `<AppIcon name="search" />`. If it does not have any inline SVG, this file is a no-op for migration (still add the import only if needed).
    - **ShareModal.vue**: `x` close button -> `<AppIcon name="x" class="..." />`.
    - **DropZone.vue**: upload icon -> `<AppIcon name="upload" class="..." />`.
    - **UploadProgress.vue**: fill-based check + error icons -> `<AppIcon name="checkCircle" />` / `<AppIcon name="exclamationCircle" />`. Drop the `fill="currentColor"` styling - AppIcon uses stroke; visual difference at w-5 h-5 is minor and acceptable per RESEARCH.md Open Question 1.
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run; cd frontend && npm run build</automated>
    Expected: tests pass; build succeeds.
  </verify>
  <acceptance_criteria>
    - For each file: `grep -c "<svg" <file>` returns 0 OR 1 (1 is acceptable only for DocumentPreviewModal.vue with the loading spinner)
    - Specifically: `grep -c "<svg" frontend/src/components/documents/DocumentPreviewModal.vue` may be 1 (spinner preserved)
    - All other listed files: `grep -c "<svg" <file>` returns 0
    - Every file contains `import AppIcon` (except SearchBar if no SVGs existed there - confirm by reading)
    - Full test suite passes
    - npm run build exits 0
  </acceptance_criteria>
  <done>9 folder/document/upload/sharing files migrated.</done>
</task>

<task type="auto">
  <name>Task 3: Migrate cloud + settings + UI utility files</name>
  <files>
    frontend/src/components/cloud/CloudFolderTreeItem.vue,
    frontend/src/components/cloud/CloudProviderTreeItem.vue,
    frontend/src/components/settings/SettingsCloudTab.vue,
    frontend/src/components/ui/SearchableModelSelect.vue
  </files>
  <read_first>
    - All four files (current state)
    - frontend/src/components/ui/AppIcon.vue
    - .planning/phases/10-ux-interaction/10-RESEARCH.md §"Component Inventory §1"
  </read_first>
  <action>
    Apply the migration:
    - **CloudFolderTreeItem.vue**: folder + fileDoc -> `<AppIcon name="folder" />` / `<AppIcon name="fileDoc" />`.
    - **CloudProviderTreeItem.vue**: cloud -> `<AppIcon name="cloud" class="..." />`.
    - **SettingsCloudTab.vue**: warning -> `<AppIcon name="warning" class="..." />`.
    - **SearchableModelSelect.vue**: chevronDown + pencilEdit -> `<AppIcon name="chevronDown" />` / `<AppIcon name="pencilEdit" />`.
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run; cd frontend && npm run build</automated>
    Expected: tests pass; build succeeds.
  </verify>
  <acceptance_criteria>
    - Each file: `grep -c "<svg" <file>` returns 0
    - Each file imports AppIcon
    - Test suite + build pass
  </acceptance_criteria>
  <done>4 cloud/settings/UI files migrated.</done>
</task>

<task type="auto">
  <name>Task 4: Migrate view files (AccountView, CloudStorageView, SettingsView, all admin views)</name>
  <files>
    frontend/src/views/AccountView.vue,
    frontend/src/views/CloudStorageView.vue,
    frontend/src/views/SettingsView.vue,
    frontend/src/views/admin/AdminUsersView.vue,
    frontend/src/views/admin/AdminAuditView.vue,
    frontend/src/views/admin/AdminQuotasView.vue,
    frontend/src/views/admin/AdminAiView.vue,
    frontend/src/views/admin/AdminOverviewView.vue
  </files>
  <read_first>
    - All eight files (current state, after 10-08 modifications)
    - frontend/src/components/ui/AppIcon.vue
    - .planning/phases/10-ux-interaction/10-RESEARCH.md §"Component Inventory §1"
  </read_first>
  <action>
    Apply the migration:
    - **AccountView.vue**: checkMark -> `<AppIcon name="checkMark" class="..." />`.
    - **CloudStorageView.vue**: any remaining inline SVG (after 10-08 EmptyState wiring) -> matching AppIcon.
    - **SettingsView.vue**: checkCircle + exclamationCircle + x (x2) -> matching AppIcons.
    - **AdminUsersView.vue**: copy + check + refresh -> matching AppIcons.
    - **AdminAuditView.vue, AdminQuotasView.vue, AdminAiView.vue, AdminOverviewView.vue**: any remaining inline SVGs -> matching AppIcons. If none, this is a no-op (no import needed unless adding it).

    For all views: register `AppIcon` in `components: { ... }` if Options API; or only add the import if `<script setup>`.
  </action>
  <verify>
    <automated>cd frontend && npm run test -- --run; cd frontend && npm run build; grep -rE "<svg fill=\"none\" stroke=\"currentColor\"" frontend/src --include="*.vue" | grep -v AppIcon.vue | grep -v AppSpinner.vue | grep -v DocumentPreviewModal.vue | wc -l</automated>
    Expected: tests pass; build succeeds; grep count returns 0 (all stroke-based outline SVGs migrated).
  </verify>
  <acceptance_criteria>
    - Each view file: `grep -c "<svg" <file>` returns 0
    - `grep -rE "<svg fill=\"none\" stroke=\"currentColor\"" frontend/src --include="*.vue" | grep -v AppIcon.vue | wc -l` returns 0 (only AppIcon.vue should contain stroke-based outline SVGs)
    - The exceptions are: AppSpinner.vue (spinner animation, not an icon) and DocumentPreviewModal.vue (loading spinner with `<circle>`). Both have already been validated NOT to use the stroke + viewBox 0 0 24 24 pattern; if either matches the grep above, halt and confirm with the user before proceeding.
    - npm run build exits 0
    - Full test suite passes
  </acceptance_criteria>
  <done>All view-level files migrated; CODE-05 verifiable by the single grep across the codebase returning 0.</done>
</task>

</tasks>

<verification>
- Codebase-wide check: `grep -rE "<svg fill=\"none\" stroke=\"currentColor\"" frontend/src --include="*.vue" | grep -v "AppIcon.vue"` returns 0 lines (every inline outline SVG migrated)
- `cd frontend && npm run test -- --run` exits 0 (full suite passes)
- `cd frontend && npm run build` exits 0
- Every modified file imports AppIcon from the correct relative path
- ICON_PATHS in AppIcon.vue is the ONLY file containing the canonical d-attribute values for the migrated icons
</verification>

<success_criteria>
The frontend bundle has a single source of truth for icon paths. Visually, the app renders identically to before migration. Adding a new icon requires editing ONE map in AppIcon.vue and using `<AppIcon name="..." />` at consumer sites - no more copy-paste of `<svg ...>` blocks. The grep gate guarantees no future drift back to inline SVGs without violating CODE-05.
</success_criteria>

<output>
Create `.planning/phases/10-ux-interaction/10-12-SUMMARY.md` when done. Include the final `grep -rc "<svg" frontend/src --include="*.vue"` count (broken down by file if not zero) and confirm the grep gate above returns 0.
</output>
