Files
LinkDesk/frontend_tasks.md
T
indigo cd3628a255 Phase 2: asset/shot feature parity, task actions, member management
Phase 2 of frontend_tasks.md - close feature gaps between domains:

- Asset user-assignment popover, ported from shot's EditableTaskStatus,
  wired through columns.ts and AssetBrowser.vue.
- Asset column-locking toggle: two-pane frozen-column layout ported
  into AssetsDataTable.vue (adapted for asset's page-scroll layout,
  which has no bounded-height container like shot's).
- Task table row-actions menu (View Details + Reassign only - no
  Delete/Edit, since no backend support exists for either).
- Real select-task/create-task behavior on asset and shot detail
  panels: clicking a task swaps in the actual TaskDetailPanel in
  place; "Add Task" opens a task-type picker that creates real tasks
  via the existing createAssetTask/createShotTask services.
- create-note/upload-reference/publish-version implemented via a
  task-picker that deep-links into TaskDetailPanel's Notes/
  Attachments/Submissions tabs (new initialTab prop), reusing the
  already-working task-level components instead of building three new
  bespoke forms. Also fixed shot's pre-existing dead "Add Note"/
  "Upload Reference" buttons the same way.
- Consolidated ProjectMembersManager.vue and ProjectMemberManagement.vue
  into one component, combining remove-confirmation and approved-user
  filtering with toast feedback and the shared Select/Dialog UI kit.
- Wired ProjectDetailView's "Manage Members" to navigate to the
  project's Settings > Team tab.

Also fixed a bug in this session's own new code: TaskBrowser.vue's
row-click handler is a no-op by design, so the new row-actions menu
needed to emit row-double-click (which actually opens the panel)
instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 08:45:06 +08:00

11 KiB
Raw Blame History

Frontend Improvement Tasks

Derived from frontend_report.md. Checklist form for tracking progress — check items off as they land. Ordered by phase (do Phase 1 before Phase 2, etc.); within a phase, order doesn't matter much.


Phase 1 — Truth-in-UI & dead code (done)

  • Wire views/DashboardView.vue stats to real API data where an endpoint exists (projects, active projects, overdue tasks, total users, pending approvals, API keys, developer stats, pending reviews, admin system activity). Added services/developer.ts and services/review.ts wrappers.
  • Replace fixed-lookup episode progress with real computed progress — components/episode/EpisodeCard.vue:117-128 and components/episode/EpisodeList.vue:267-278deferred: backend has no completed_shots/progress field yet (backend/schemas/episode.py, backend/routers/episodes.py)
  • Fix task-status usage counts always showing 0 — components/settings/CustomTaskStatusManager.vue:275-295deferred: needs a new GET /projects/{id}/task-statuses/counts backend endpoint
  • Register the missing route: /projects/:projectId/technical-specs in router/index.ts
  • Delete confirmed-dead files (or finish wiring them in):
    • components/shot/ShotColumnVisibilityControl.vue — deleted
    • components/asset/ColumnVisibilityControl.vue — deleted
    • components/project/ShotsTable.vue — deleted
    • components/examples/FileUploadExample.vue — deleted
    • views/GlobalSettingsView.vue — deleted
    • views/HomeView.vue — deleted
    • components/asset/AssetDeleteConfirmDialog.vue — fixed (raw divs → shared Alert component) and wired into AssetBrowser.vue, mirroring ShotBrowser.vue's pattern
    • components/activity/ActivityFeed.vue — wired into ProjectOverviewView.vue in place of "coming soon" text; TaskActivityTimeline.vue — deleted (no taskId available at that call site)
  • Replace native confirm()/alert() with styled dialogs:
    • components/task/NoteItem.vue:178AlertDialog
    • components/task/TaskAttachments.vue:201AlertDialog
    • components/layout/UserMenu.vue keyboard-shortcuts alert()Dialog
  • Remove leftover debug logging (all 8 files/line-ranges)
  • Removed Google OAuth stub buttons (LoginView.vue, RegisterView.vue) — no OAuth backend exists, so the buttons were pure dead ends
  • /developer/api-keys, /developer/projects, /developer/tasks, /developer/analytics, views/ReviewsView.vueleft as-is: their empty states are honest ("No API keys" etc.), not misleading, so out of scope for a truth-in-UI pass. Only fixed the one dead action button on APIKeysView.vue (removed action-text/@action since it did nothing).
  • Removed dead-end UserMenu items (/settings/preferences, /help, notifications toggle) and the /developer/docs dead link (AppSidebar.vue, DashboardView.vue) — no real destination existed for any of them

Bonus fixes surfaced during verification (approved mid-implementation):

  • Fixed two pre-existing backend bugs that 500'd once real data was wired up: backend/routers/reviews.py (joinedload("reviewer") string → joinedload(Review.reviewer)) and backend/utils/activity.py (wrong models.submission import path → models.task).
  • Fixed a pre-existing bug shared by both ShotDeleteConfirmDialog.vue and AssetDeleteConfirmDialog.vue: their watch(() => props.open, ...) lacked { immediate: true }, so the impact-summary section never rendered since the dialog only mounts once already open. Deletion itself worked fine either way — this was purely cosmetic, but it was the whole point of the "impact summary" safety feature.

Phase 2 — Close feature gaps (asset vs. shot parity) (done)

  • Add user-assignment popover to components/asset/EditableTaskStatus.vue — ported from the shot version; wired through asset/columns.ts and AssetBrowser.vue
  • Add column-locking toggle to components/asset/AssetTableToolbar.vue — two-pane frozen-column layout ported into AssetsDataTable.vue (adapted for asset's page-scroll layout, no vertical scroll-sync needed unlike shot's bounded-height container)
  • Add row-actions ("…") menu to components/task/columns.ts — "View Details" + "Reassign" (no Delete/Edit — no backend deleteTask/task-edit-form exists)
  • Implement the asset detail-panel actions in AssetBrowser.vue/AssetDetailPanel.vue: create task (task-type picker), select task (opens TaskDetailPanel in-place), create note/upload reference/publish version (task picker → deep-links into TaskDetailPanel's Notes/Attachments/Submissions tabs via new initialTab prop)
  • Implement the same for ShotBrowser.vue/ShotDetailPanel.vue (also fixed the pre-existing dead "Add Note"/"Upload Reference" buttons there)
  • Consolidate components/project/ProjectMembersManager.vue and components/project/ProjectMemberManagement.vue into one component (kept ProjectMemberManagement.vue, deleted the other) — combined remove-confirmation + approved-user filtering + real avatars from one with toast feedback + shared Select/Dialog UI-kit from the other
  • Wire the consolidated member-management component into ProjectDetailView.vue:149-151 — navigates to the project Settings "Team" tab (mirrors the existing manageTechnicalSpecs pattern) rather than a duplicate dialog

Bugs found and fixed during verification (approved mid-implementation):

  • GET /assets/{id} never returned task_details (schema didn't even declare the field) — unlike GET /shots/{id}, which already did. Broke the asset detail panel's task list and, transitively, every new create-task/note/reference/version feature. Fixed in backend/schemas/asset.py + backend/routers/assets.py.
  • TaskBrowser.vue's handleRowClick is a no-op by design (single click reserved for selection) — the new row-actions menu's "View Details"/"Reassign" needed to emit row-double-click instead, which is what actually opens the panel.

Phase 3 — Unify controls

  • Extract ColumnToggleList component (built on DropdownMenuCheckboxItem, not hand-rolled div+Check) and point at it from:
    • components/ui/sidebar/SidebarColumnSwitch.vue
    • components/shot/ShotTableToolbar.vue
    • components/asset/AssetTableToolbar.vue
    • components/task/TaskTableToolbar.vue
  • Extract shared EntityTableToolbar (search, column-visibility trigger, detail-panel toggle, clear-filters) to de-duplicate the three toolbar files
  • Extract SegmentedToggle component for the three hand-built view-mode switches (shot grid/list/table, asset grid/list, task all/shots/assets)
  • Standardize delete confirmation on the shot pattern (Dialog + Alert + impact summary + type-to-confirm) for any cascading delete; fix asset's dialog to match or consciously keep it lighter
  • Swap hand-rolled checkbox-divs for real Checkbox/DropdownMenuCheckboxItem in:
    • SidebarColumnSwitch.vue
    • ShotTableToolbar.vue
    • AssetTableToolbar.vue
    • TaskTableToolbar.vue
    • ShotTaskStatusFilter.vue
    • asset/TaskStatusFilter.vue
  • Replace ad hoc size="sm" class="h-8 w-8 p-0" with size="icon-sm" across toolbar/browser icon buttons
  • Decide one convention (icon+label vs. icon-only+tooltip) for primary "create" CTAs and apply consistently (shot/asset create buttons currently lack tooltips project/episode create buttons have)
  • Standardize row-actions/bulk-actions on DropdownMenu/DropdownMenuItem; replace ad hoc <button> list in components/task/TaskBulkActionsMenu.vue
  • Extract DetailPanelShell (header/close button, loading state, error state, Tabs scaffold, slide-in transition) for ShotDetailPanel.vue, AssetDetailPanel.vue, TaskDetailPanel.vue to share; add missing error state to Task's panel

Phase 4 — Architecture & tooling investment

  • Add a shared useAsyncAction-style composable (isLoading/error/run(fn)) and adopt it across stores/*.ts to cut repeated try/catch/finally boilerplate
  • Replace stores/taskStatuses.ts's polling-based in-flight request de-dup (100ms setInterval loop, lines 96-117) with promise memoization
  • Extend a similar TTL/cache strategy to stores/assets.ts, stores/tasks.ts, stores/projects.ts, stores/episodes.ts (currently no caching — every view re-fetches on mount)
  • De-duplicate assets.ts's optimistic-update/rollback logic between single (lines 156-234) and bulk (lines 236-356) task-status updates into one parameterized function
  • Add markRaw() around icon components stored in reactive state — stores/projects.ts assignProjectIcon
  • Audit deep: true watchers, especially views/ProjectsView.vue:770 (watches the full, non-markRaw'd projects array)
  • Fix N+1 project-member fetch: hoist getProjectMembers() call out of EditableTaskStatus.vue (mounted per row × task-type column) into a shared store/cache or parent-passed prop
  • Add table virtualization (e.g. @tanstack/vue-virtual) to ShotsDataTable.vue, AssetsDataTable.vue, TasksDataTable.vue
  • Introduce a shared EmptyState component and standardize on Skeleton for loading states (currently: 5 files use Skeleton, 62 use ad hoc spinners; 31 files hand-roll empty-state text)
  • Add toast-on-error for currently-silent secondary loads: episodes/task-types/task-statuses/project-context in ShotBrowser.vue (lines 474-508), stores/notifications.ts:fetchStats, router auth-init failure (router/index.ts:190-196)
  • Centralize a usePermission()/can() composable to consolidate role/admin checks currently duplicated across ~20 components outside the router guard
  • Consider splitting the largest files into smaller pieces: views/project/ShotDetailView.vue (1658 lines), views/admin/DeletedItemsManagementView.vue (1279 lines), and evaluate a shared "entity browser" composable for ShotBrowser.vue/AssetBrowser.vue's ~70% structural overlap
  • Move ad hoc domain types out of services/*.ts into types/ for discoverability (currently only auth.ts, notification.ts, activity.ts live in types/)

Phase 5 — Testing & quality gates

  • Add Vitest + Vue Test Utils
  • Write first tests for the highest-risk untested logic: stores/assets.ts optimistic-update/rollback, stores/auth.ts token lifecycle
  • Add an ESLint config (flag any, unused locals, enforce consistent import/style rules)
  • Re-enable noUnusedLocals in tsconfig.json (currently explicitly disabled)
  • Introduce a typed ApiError helper to replace ad hoc err.response?.data?.detail access at the 104 catch (err: any) sites (fix incrementally as files are touched, not all at once)
  • Accessibility fixes: remove checkbox-inside-SelectItem anti-pattern (also fixes the double-toggle bug), add keyboard support to hand-rolled clickable <div>s (SubmissionCard.vue, EditableTaskStatus.vue)

Full context, rationale, and file-by-file findings for every item above are in frontend_report.md.