From 71c1bf14e451ac6d0daeab49cdfcb253c79531c1 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 8 May 2026 20:47:40 +0200 Subject: [PATCH 01/35] [topbar] align budget icon gray with other section icons Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/tops/TopbarSectionList.vue | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/tops/TopbarSectionList.vue b/src/components/tops/TopbarSectionList.vue index 0c4eaf51c3..8c24019ec4 100644 --- a/src/components/tops/TopbarSectionList.vue +++ b/src/components/tops/TopbarSectionList.vue @@ -288,4 +288,12 @@ hr { margin-right: 0.8em; width: 20px; } + +svg.section-icon { + color: #515151; +} + +.dark svg.section-icon { + color: #ffffff; +} From c5e2161479473bdcfeda4118555267ef3dce7126 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 8 May 2026 20:59:03 +0200 Subject: [PATCH 02/35] [loading] replace spinner with staggered skeleton rows Replace the spinner shown by TableInfo while a list loads with Slack-style skeleton rows that appear one by one and restart in a continuous wave. Three variants are dispatched through TableInfo: - list (default): thumbnail + name + N cell bars + actions; entity lists (asset, shot, sequence, edit, episode) opt into big-cells for validation-shaped blocks - kanban: 4 columns of stacked card placeholders, used by KanbanBoard - grid: rows of name + day-cells + actions, used by PeopleTimesheetList Each list passes the props that match its actual column shape (cells, with-thumbnail, with-actions). The shared cycle/restart and fade-in/out logic lives in a new useSkeletonCycle composable. A --skeleton-rgb variable was added so the placeholder color stays visible against both light and dark backgrounds. Also: source the asset page filter list directly from userFilters so the filter pills stay visible during asset list loading instead of being briefly cleared by LOAD_ASSETS_START. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/lists/AllTaskList.vue | 7 +- src/components/lists/AssetList.vue | 2 +- src/components/lists/AssetTypeList.vue | 7 +- src/components/lists/BackgroundList.vue | 2 +- src/components/lists/CustomActionList.vue | 7 +- src/components/lists/DayOffList.vue | 7 +- src/components/lists/DepartmentList.vue | 7 +- src/components/lists/EditList.vue | 2 +- src/components/lists/EntityTaskList.vue | 7 +- src/components/lists/EpisodeList.vue | 2 +- src/components/lists/EpisodeStatsList.vue | 7 +- src/components/lists/HardwareItemList.vue | 7 +- src/components/lists/KanbanBoard.vue | 2 +- src/components/lists/PeopleList.vue | 2 +- src/components/lists/PeopleTimesheetList.vue | 2 +- src/components/lists/PreviewFileList.vue | 7 +- .../lists/ProductionAssetTypeList.vue | 8 +- src/components/lists/ProjectTemplateList.vue | 7 +- src/components/lists/QuotaShotList.vue | 8 +- src/components/lists/SequenceList.vue | 2 +- src/components/lists/SequenceStatsList.vue | 8 +- src/components/lists/ShotList.vue | 2 +- src/components/lists/SoftwareLicenseList.vue | 6 +- src/components/lists/StatusAutomationList.vue | 6 +- src/components/lists/StudioList.vue | 7 +- src/components/lists/TaskList.vue | 7 +- src/components/lists/TaskStatusList.vue | 7 +- src/components/lists/TaskTypeList.vue | 7 +- src/components/lists/TimeSpentTaskList.vue | 8 +- src/components/lists/TimesheetList.vue | 8 +- src/components/lists/TodosList.vue | 7 +- src/components/pages/Assets.vue | 23 ++- .../widgets/GridLoadingSkeleton.vue | 134 +++++++++++++++ .../widgets/KanbanLoadingSkeleton.vue | 149 +++++++++++++++++ .../widgets/ListLoadingSkeleton.vue | 129 +++++++++++++++ src/components/widgets/TableInfo.vue | 37 +++-- src/composables/skeleton.js | 29 ++++ src/variables.scss | 155 +++++++++--------- 38 files changed, 705 insertions(+), 126 deletions(-) create mode 100644 src/components/widgets/GridLoadingSkeleton.vue create mode 100644 src/components/widgets/KanbanLoadingSkeleton.vue create mode 100644 src/components/widgets/ListLoadingSkeleton.vue create mode 100644 src/composables/skeleton.js diff --git a/src/components/lists/AllTaskList.vue b/src/components/lists/AllTaskList.vue index 450757e733..63cfa8dcd2 100644 --- a/src/components/lists/AllTaskList.vue +++ b/src/components/lists/AllTaskList.vue @@ -142,7 +142,12 @@ {{ $t('main.load_more') }} - +

{{ stats.total }} diff --git a/src/components/lists/AssetList.vue b/src/components/lists/AssetList.vue index cfbbe3e28f..ccfab2bca5 100644 --- a/src/components/lists/AssetList.vue +++ b/src/components/lists/AssetList.vue @@ -561,7 +561,7 @@

{{ $t('assets.empty_list_client') }}

- + - +

{{ entries.length }} {{ $tc('asset_types.number', entries.length) }} diff --git a/src/components/lists/BackgroundList.vue b/src/components/lists/BackgroundList.vue index 782ae23101..bb91ef69ec 100644 --- a/src/components/lists/BackgroundList.vue +++ b/src/components/lists/BackgroundList.vue @@ -43,7 +43,7 @@ - +

{{ entries.length }} diff --git a/src/components/lists/CustomActionList.vue b/src/components/lists/CustomActionList.vue index 2f3bdbc859..049eb66411 100644 --- a/src/components/lists/CustomActionList.vue +++ b/src/components/lists/CustomActionList.vue @@ -57,7 +57,12 @@ - +

{{ entries.length }} {{ $t('custom_actions.number', entries.length) }} diff --git a/src/components/lists/DayOffList.vue b/src/components/lists/DayOffList.vue index 9c19ca73cb..70f83924e8 100644 --- a/src/components/lists/DayOffList.vue +++ b/src/components/lists/DayOffList.vue @@ -61,7 +61,12 @@

{{ $t('days_off.no_days_off') }}

- +

{{ entries.length }} {{ $t('departments.number', entries.length) }}

diff --git a/src/components/lists/EditList.vue b/src/components/lists/EditList.vue index b1588ee0d9..2d238137e2 100644 --- a/src/components/lists/EditList.vue +++ b/src/components/lists/EditList.vue @@ -450,7 +450,7 @@ - +
- +
- +
- +
- +

{{ entries.length }} {{ $t('hardware_items.number', entries.length) }} diff --git a/src/components/lists/KanbanBoard.vue b/src/components/lists/KanbanBoard.vue index 1c2a5b4a86..b48be164d7 100644 --- a/src/components/lists/KanbanBoard.vue +++ b/src/components/lists/KanbanBoard.vue @@ -107,7 +107,7 @@ - + - +

diff --git a/src/components/lists/ProductionAssetTypeList.vue b/src/components/lists/ProductionAssetTypeList.vue index 63cc2b4f3f..c87a727930 100644 --- a/src/components/lists/ProductionAssetTypeList.vue +++ b/src/components/lists/ProductionAssetTypeList.vue @@ -98,7 +98,13 @@
- +
- +

{{ entries.length }} {{ diff --git a/src/components/lists/QuotaShotList.vue b/src/components/lists/QuotaShotList.vue index 5ea2ec4c2d..cc07f82cd9 100644 --- a/src/components/lists/QuotaShotList.vue +++ b/src/components/lists/QuotaShotList.vue @@ -23,7 +23,13 @@ - + diff --git a/src/components/lists/SequenceList.vue b/src/components/lists/SequenceList.vue index a651419d9b..83e5a22859 100644 --- a/src/components/lists/SequenceList.vue +++ b/src/components/lists/SequenceList.vue @@ -446,7 +446,7 @@ - +

- +
- +
- +

{{ entries.length }} {{ $t('software_licenses.number', entries.length) }} diff --git a/src/components/lists/StatusAutomationList.vue b/src/components/lists/StatusAutomationList.vue index 3be6d44065..08f94ecfc3 100644 --- a/src/components/lists/StatusAutomationList.vue +++ b/src/components/lists/StatusAutomationList.vue @@ -97,7 +97,11 @@ - +

{{ entries.length }} diff --git a/src/components/lists/StudioList.vue b/src/components/lists/StudioList.vue index d85bd722fd..43708c11ea 100644 --- a/src/components/lists/StudioList.vue +++ b/src/components/lists/StudioList.vue @@ -29,7 +29,12 @@ - +

{{ entries.length }} {{ $t('studios.number', entries.length, { n: entries.length }) }} diff --git a/src/components/lists/TaskList.vue b/src/components/lists/TaskList.vue index 6b51a6e70a..4ca14fde84 100644 --- a/src/components/lists/TaskList.vue +++ b/src/components/lists/TaskList.vue @@ -257,7 +257,12 @@ - +

- +

{{ entries.length }} {{ $tc('task_status.number', entries.length) }} diff --git a/src/components/lists/TaskTypeList.vue b/src/components/lists/TaskTypeList.vue index 560256e42d..bec1af1cd8 100644 --- a/src/components/lists/TaskTypeList.vue +++ b/src/components/lists/TaskTypeList.vue @@ -69,7 +69,12 @@ - +

{{ entries.length }} {{ $tc('task_types.number', entries.length) }} diff --git a/src/components/lists/TimeSpentTaskList.vue b/src/components/lists/TimeSpentTaskList.vue index c15f203f3f..ac9d28c3dc 100644 --- a/src/components/lists/TimeSpentTaskList.vue +++ b/src/components/lists/TimeSpentTaskList.vue @@ -1,6 +1,12 @@ - From 54f6307e428ac221c8dd6a7c179cfd203c7380ac Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 11 May 2026 23:23:18 +0200 Subject: [PATCH 20/35] [asset-types] Stack rows as cards on mobile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under 768px transform each table row of AssetTypeList into a self contained card so the page no longer relies on horizontal scrolling. - Tag every with a data-label so it can carry its own caption. - Drop , switch every table element to display: block, and render each as a rounded card (var(--background) light / var(--background-alt) dark, 1px border, 12px radius, 0.75em gap). - A `::before { content: attr(data-label) }` pseudo-element prints the small uppercase muted caption above every cell value. The name cell doubles as the card title, so its label is hidden and its weight is bumped. - Hide the action buttons on mobile (`.datatable-row .actions { display : none }`) — the edit / delete flow stays available on desktop. - Hide empty short-name cells via a data-label-less placeholder plus a `:not([data-label])` selector that keeps the desktop column layout intact. - Keep the wrapper's vertical overflow scrolling (only override `overflow-x: visible`) so the list still scrolls inside .fixed-page. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/lists/AssetTypeList.vue | 94 +++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 9 deletions(-) diff --git a/src/components/lists/AssetTypeList.vue b/src/components/lists/AssetTypeList.vue index ba37360564..e7137b28a1 100644 --- a/src/components/lists/AssetTypeList.vue +++ b/src/components/lists/AssetTypeList.vue @@ -18,16 +18,25 @@ - + {{ entry.name }} - + {{ entry.short_name }} - + + - + {{ $t('asset_types.include_all') }} { } @media screen and (max-width: 768px) { - .name { + // Turn each row into a card: the table head disappears, every cell is + // labelled via its data-label attribute, and the actions cell collapses + // to a footer row. Keep the wrapper's overflow-y: auto from the global + // .datatable-wrapper rule so the list still scrolls inside .fixed-page. + :deep(.datatable-wrapper) { + background: transparent; + border: 0; + overflow-x: visible; + } + + .datatable, + .datatable-body { + display: block; + width: 100%; + } + + .datatable-head { + display: none; + } + + .datatable-row { + background: var(--background); + border: 1px solid var(--border); + border-radius: 12px; + display: block; + margin-bottom: 0.75em; + padding: 0.85em 1em; + } + + .dark .datatable-row { + background: var(--background-alt); + } + + .datatable-row td { + border: 0; + display: block; + padding: 0.4em 0; width: auto; - padding: 0.5em; } - .datatable-body td, - .datatable-head th { - padding: 0.5em; + // Cells without a data-label are placeholders to keep the desktop + // table columns aligned — collapse them on mobile so empty fields + // don't leave gaps. + .datatable-row td:not([data-label]):not(.actions):not(.name) { + display: none; + } + + .datatable-row td[data-label]::before { + color: var(--text-alt); + content: attr(data-label); + display: block; + font-size: 0.75em; + letter-spacing: 0.06em; + margin-bottom: 0.2em; + text-transform: uppercase; + } + + // The name doubles as the card title — drop its label and bump the + // font weight. + .datatable-row .name { + font-size: 1.05em; + font-weight: 600; + padding-top: 0; + + &::before { + display: none; + } + } + + .datatable-row .actions { + display: none; } } From 47486730af0e754e3485ad699b0804a0e30c7dbb Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 11 May 2026 23:31:13 +0200 Subject: [PATCH 21/35] [departments] Migrate page and list to Composition API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Page: convert Departments.vue to diff --git a/src/components/pages/TaskStatus.vue b/src/components/pages/TaskStatus.vue index d1b9830bfc..6b1567bb15 100644 --- a/src/components/pages/TaskStatus.vue +++ b/src/components/pages/TaskStatus.vue @@ -46,7 +46,7 @@ :active="modals.del" :is-loading="loading.del" :is-error="errors.del" - :text="deleteText()" + :text="deleteText" :error-text="$t('task_status.delete_error')" @cancel="modals.del = false" @confirm="confirmDeleteTaskStatus" @@ -54,234 +54,207 @@ - From 84888bc2e65ad2013665e94d2ef1718183350ef9 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 11 May 2026 23:59:08 +0200 Subject: [PATCH 24/35] [task-status] Reorder columns and add hover-fly actions Move the short-name (status chip) column before the name column in both the header and the row template, and drop the hardcoded class="name" from TaskStatusCell so it no longer steals the parent list's .name styling. Add class="datatable-row-footer" on the row actions so the edit / delete buttons stay hidden on desktop and slide in sticky-right on row hover, matching the People page behavior. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/cells/TaskStatusCell.vue | 2 +- src/components/lists/TaskStatusList.vue | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/cells/TaskStatusCell.vue b/src/components/cells/TaskStatusCell.vue index d0956ff04d..62e9afdf0f 100644 --- a/src/components/cells/TaskStatusCell.vue +++ b/src/components/cells/TaskStatusCell.vue @@ -1,5 +1,5 @@