handleContextMenu (and the shift-click range-selection path) resolved the
target row via props.tasks[index], but index is the row's position in the
sorted/rendered row model (table.getRowModel().rows), not the raw prop
array — the two orders rarely match once the default created_at sort is
applied. This caused right-click (and shift-click) to select the wrong row,
corrupting the count shown in TaskBulkActionsMenu. Both call sites now
resolve the task from the actual row data instead of an index lookup into
the unsorted array, matching the pattern ShotsDataTable.vue already used
correctly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Shot/Asset/Task data tables now keep their header row pinned while scrolling
(both the locked two-pane and single-table modes), matching a common data-
table expectation that was missing everywhere.
Brings Asset and Task browsers to full structural parity with Shot's bounded-
height layout (fixed toolbar, internally-scrolling table) instead of their
previous page-scroll model with a sticky-positioned toolbar. AssetsDataTable
gained the synced frozen/movable-pane scrolling it was missing entirely;
TasksDataTable and both browsers/parent views got the same container model.
The global My Tasks page (/tasks) now reuses TaskTableToolbar and
TasksDataTable instead of its own ad hoc filter selects and TaskList,
including the detail panel, bulk status/assignment context menu, and sticky
header for free. Since it spans multiple projects, TaskTableToolbar gained
an optional Project filter (only rendered when a project list is passed in,
so the project-scoped Tasks page is unaffected); episode/assignee filters
populate once a specific project is chosen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The lucide icon was imported as "Activity as ActivityIcon" but the empty-
state template still referenced <Activity>, which was never registered
under that name — triggering a Vue "failed to resolve component" warning
whenever a project's activity feed was empty.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
get_project_activities, get_task_activities, and get_recent_activities were
gating on "project member OR admin", incorrectly blocking coordinators,
directors, and developers from projects they weren't explicitly added to as
members. Every other project-scoped router (shots.py, assets.py) only
restricts the artist role this way — everyone else has access regardless of
membership. Brought activities.py in line with that convention.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes a real N+1 API-call bug (per-cell project-member fetches in shot/asset
task-status cells, now a shared cached store), replaces a polling-based
in-flight request de-dup with promise memoization, adds markRaw() around
icon components in reactive state, and fixes a deep watcher that re-scanned
the whole projects array just to trigger thumbnail loading.
Introduces useAsyncAction and usePermission composables to cut duplicated
store boilerplate and duplicated role/admin checks, applied only where the
existing code was a clean match rather than forced onto everything. Extracts
assets.ts's shared per-asset optimistic-update/rollback helper without
merging the single and bulk API paths, which hit genuinely different
endpoints. Adds toast-on-error for previously-silent secondary loads and
fixes a dead try/catch in the router's auth-init guard along the way.
Also fixes a thumbnail-loading regression introduced earlier in this same
pass: the new ID-keyed watcher never fired on a repeat visit to the Projects
page when the store already held the same project list, so thumbnails
(local component state, reset per mount) silently stopped loading after the
first visit. Replaced the watcher with a direct call after each fetch.
Table virtualization, splitting the two largest view files, broad store
caching, and moving domain types into types/ were scoped out as separate,
higher-risk follow-ups (documented in frontend_tasks.md).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Tasks list embedded assigned_user_name: undefined with a "will be
resolved if needed" comment that was never followed up on, so every task
showed "Unassigned" regardless of actual assignment. Resolve it from the
project members list, matching how the shot table's own assignment control
does it, and show an avatar next to the name. Also swap the hardcoded
status Badge/variant switch for the same TaskStatusBadge + task-statuses
store the shot table uses, so status colors and labels (including custom
per-project statuses) always match between the table and the panel.
Extracts duplication that built up as shot/asset/task features reached
parity: CheckableCommandItem/ColumnToggleList replace 14+ hand-rolled
checkbox-list blocks, shared toolbar pieces (debounced search, detail-panel
toggle, clear-filters, segmented view/context toggle) replace copy-pasted
markup in the three table toolbars, icon-only buttons standardize on the
icon-sm size, TaskBulkActionsMenu's Assign To submenu matches Set Status,
and a shared DetailPanelOverlay/Header/Loading/Error shell backs all three
detail panels (adding a previously-missing error state to the task panel).
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>
GET /assets/{id} never returned task_details (the schema didn't even
declare the field), unlike GET /shots/{id} which already populated it
correctly. This silently broke the asset detail panel's task list -
surfaced while wiring real task data into it on the frontend.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Phase 1 of frontend_tasks.md - stop showing fabricated/broken UI:
- Dashboard now fetches real stats (projects, tasks, users, pending
approvals, API keys, developer stats, pending reviews, admin
activity) instead of hardcoded numbers. Added services/developer.ts
and services/review.ts wrappers for previously-unused backend
endpoints.
- Wired ActivityFeed into every project's Overview page in place of
the "coming soon" placeholder.
- Registered the missing /projects/:id/technical-specs route (view
and service already existed, just unreachable).
- Fixed AssetDeleteConfirmDialog's raw styled divs to use the shared
Alert component and wired it into AssetBrowser, matching
ShotBrowser's impact-summary + type-to-confirm safety pattern
(asset deletion was previously less safe than shot deletion).
- Fixed a shared bug in both delete dialogs where the impact-summary
section never rendered (watch on the open prop needed
{ immediate: true }).
- Replaced native confirm()/alert() with styled AlertDialog/Dialog in
NoteItem, TaskAttachments, and UserMenu's keyboard-shortcuts item.
- Removed dead-end UI: Google OAuth stub buttons, UserMenu items
pointing at non-existent routes, the /developer/docs dead link, and
the no-op action button on the API Keys placeholder page.
- Removed leftover debug console logging across 8 files.
Added frontend_report.md (full audit) and frontend_tasks.md (phased
checklist) as the reference for this and future phases.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
joinedload("reviewer") used a string instead of a class-bound
attribute (rejected by SQLAlchemy 2.x), and models.submission was the
wrong import path for Submission. Both endpoints 500'd whenever
actually called; surfaced while wiring real dashboard/activity data
into the frontend.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
When the sidebar is collapsed to icon-rail mode, the Columns/Task
Columns checkbox lists had no icon-only representation. Show icon
buttons that open a dropdown menu with checkbox items instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a lock toggle to the shot table toolbar that freezes the
checkbox, thumbnail, and shot name columns. When locked, the table
splits into a fixed left pane and a horizontally scrollable right
pane so the scrollbar spans only the movable columns. Constrain the
table to fill the viewport height so the scrollbar stays at the
screen bottom, and add min-w-0 to the layout to keep horizontal
overflow inside the table instead of the page.
Also fix the asset Task Status filter to render task type groups by
using the object-aware TaskStatusBadge.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Remove wrapper padding from ProjectAssetsView so toolbar sits flush
under the header, matching the shot page
- Add px-4 to AssetsDataTable root div to match ShotsDataTable margin
- Move thumbnail toggle from left filter group to right action area as
an icon-only button, matching the shot toolbar's right-side pattern
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Pass episode_id filter to backend API instead of client-side filtering,
reducing payload size when an episode is selected
- Skip getShot refetch in ShotDetailPanel when initialShot prop is provided
- Fix redundant DB query in list_shots: read project.custom_task_statuses
directly from the already-fetched project object
- Add missing indexes on Task.shot_id, Task.assigned_user_id,
Task.deleted_at and Episode.project_id; add add_perf_indexes.py
migration script to apply them to existing databases
- Center login page layout
- Update CLAUDE.md and AGENTS.md with correct venv path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>