[ui] Timesheets chart, task date filters and admin page chrome - #2196
Open
frankrousseau wants to merge 64 commits into
Open
[ui] Timesheets chart, task date filters and admin page chrome#2196frankrousseau wants to merge 64 commits into
frankrousseau wants to merge 64 commits into
Conversation
Add a meta title key to every non production route (merged into the existing meta permission objects, inherited by sub-routes) and render it in the topbar left area. Production pages keep their breadcrumb and the open productions page shows nothing, both simply by carrying no meta title. Remove the in-page PageTitle headers from AssetLibrary, People, Productions and Timesheets now that the title lives in the topbar, and drop their orphaned imports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the Bulma underline look shared by RouteTabs, RouteSectionTabs and the raw tabs markup pages: rounded background-alt rail hugging its content, active tab as a raised pill with a soft shadow and the plain text color, quiet hover on inactive tabs. In dark the background token is darker than the rail and would read as a pressed slot, so the active pill gets its own lighter fill there, hover included. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the dom and grablist mixins with the useGrabList composable and move the getters, ranges and cell helpers to script setup. Fold the four selection checks and the four detail routes into isSelected and getDetailRoute, and the six duration helpers into a single duration function. Scope the scroll watcher to the wrapper and document why it shifts the view by 350px. Two behavior fixes: a day off now wins over logged time on every day of the month (the old check only matched days 10 to 31 because of an unpadded key), and the weekly overtime warning compares logged hours whatever the displayed unit, so it fires in day unit too. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
Convert PeopleTimesheetInfo and TimeSpentTaskList to script setup. The panel derives its level from the route name, builds the close route from a table indexed by level and drops the unreachable fallback branch. The task list computes its grouped and sorted productions in a single computed instead of a watcher feeding component state, and loses the dead .table styles and the row class that had no effect. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
The page kept a local ref for every filter alongside the route value, plus a silent flag to break the feedback loop between the two. Each filter is now a single computed reading the route and pushing to it, the detail level comes from the route name, and a watch on the five loading parameters replaces the manual change detection. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiJw8JgxuBJqvaYo7VzF8Z
The side panel listed the time spent task by task but never summed it, so reading a week total meant adding the rows by hand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiJw8JgxuBJqvaYo7VzF8Z
Both the year combobox and the year columns started at a hardcoded 2018, which gave every studio a handful of empty years to scroll through. They now start at the organisation creation date, falling back to 2018 when it is missing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiJw8JgxuBJqvaYo7VzF8Z
Show the admin lists' empty text when nobody logged time for the period and hide the people count then. Logged hours read in the strong text color at weight 600 with a keyboard focus ring, and the weekend tint now reaches the last row, where the global datatable rule paints the cells directly. The person panel becomes a rounded panel surface filling a transparent side column, with the task rows flattened onto it through the datatable tokens, and the close button leaves the flow to sit in the top right corner at a smaller size. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
The flattened rows made the task list read as part of the panel surface: drop the token override so the list keeps the stripes and borders every other datatable has. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
Only weeks over five working days were flagged, and only as red text. Every column now carries an expected amount: the organisation day rate for a day, five days for a week, and the Monday to Friday working days of the month or year for the coarser levels, days off ignored so the threshold only errs on the lenient side. Cells above it show as a red tinted chip, kept solid when selected. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
The grid gave no way to read a person's workload over the period or the team's load on a given day, and nothing told where today sits among the columns. Totals are summed in raw minutes and converted once, so the rounding does not accumulate: a Total column per person, a footer row per column, and the grand total at their intersection. The cell formatter is now shared by the three. The column matching today is tinted and its header takes the accent colour, at every detail level. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiJw8JgxuBJqvaYo7VzF8Z
The panel and the grid inherited colours painted outside the theme, which read as near black on the light panel background. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiJw8JgxuBJqvaYo7VzF8Z
The totals lived in a tfoot with its own background and border, which stood out from the grid: the row now sits last in the body as a regular datatable row, padded so its text cells match the height of the avatar rows. The grand total leaves the corner cell for the footer line, next to the people count, in the selected unit. The wrapper also gets more room below its scrollbar. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
The floating close button reserved 2em on the name row and the page title is sized for a full-width header, so a two-word name wrapped in the 400px column. Reserve only the 1em the button overhangs the padding by, and set the title to 1.5rem in this panel. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
The header grows a department filter, a person picker and a people switch (with time logged, or everyone active) and splits into a period row and a scope row. The rows align at the top with the Bulma field margin dropped, the export buttons stretch to the 42px of the controls and sit on the row's bottom edge, and the row gap and the grid's top margin follow a 1.5em and 2em rhythm. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
Below the tablet breakpoint the grid rows become cards with a labelled chip per column, the person panel replaces the grid at full width and the export buttons are hidden, as on the admin lists. The filters fold behind a new button that keeps only the detail level, year and unit visible; once unfolded the people filter takes its own line. ButtonSimple gains the filter icon used by that button, and the panel shows the production label that the name widget hides at this width. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
The period heads the panel, with the week range on its own line, and the hours spent and days off become two tiles on the page surface. The task lists keep a square top-left corner under the task type tag so that the tag reads as a tab on the list. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
Every duration cell now says in its title how many hours were logged and how many were expected, and the person panel shows the expected figure next to the logged one, with the days off of the period deducted from the working days; the days off count follows the same rule instead of counting records. The unit, department, people filter and selected person join the production and studio in the query, so a reload or a shared link keeps them, and the detail and close routes carry the whole query. Also: a skeleton holds the hours tile while it loads, the filters toggle exposes aria-expanded, and the week headers show the Monday under the week number. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
Vue condenses the whitespace between the two spans, so the slash hugged the logged number. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
The open-tasks endpoint already accepted start_date and due_date, no UI exposed them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JhSFWVSk5T7fWXE9nD9KJV
Same red as a burned estimation, on tasks with no done date. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JhSFWVSk5T7fWXE9nD9KJV
The selection grid never held more than one task, so isManySelection was dead and the row index it tracked comes back from the task list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JhSFWVSk5T7fWXE9nD9KJV
Infinite scroll kept fetching past the end of the feed, and a failed page left loading.more stuck at true, killing it until a reload. The error and load-more states were tracked but never rendered, and newsStats was stored while nothing read it. Filters other than task type and status were lost on reload, and every change pushed a history entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DD28uafRepf5oX941BPyhT
The unit combobox gains "Salary" for admins: the logged time is converted into money at the person's daily rate, falling back on the salary scale entry of the first of their departments that has one for their position and seniority. The grid, its totals and footer, the person panel with its expected figure, the task list and the CSV export follow; the cell titles keep talking hours. Amounts are whole numbers grouped by thousands according to the user's clock preference rather than the interface language. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
`filter` renders horizontal sliders, and the funnel of KitsuIcon sits behind that name so it could not be reached from the button. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JhSFWVSk5T7fWXE9nD9KJV
Read-only cards for the rows, the secondary filters behind a funnel toggle on the production line, the burndown hidden, and the task panel taking the whole width with a back button to the list. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JhSFWVSk5T7fWXE9nD9KJV
A chart button in the filters row swaps the grid for a bar chart of the column totals, one bar per day, week, month or year of the displayed period, in the selected unit, with the current period marked on its axis label; the view is kept in the URL. The hours conversion moves to a shared helper used by the grid, the panel, the task list, the export and the chart, and the chart.js palette moves to a composable shared with the burndown chart. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
The sliders icon that ButtonSimple mapped to "filter" replaced the Kitsu funnel on every page using that name. The timesheets toggle switches to the lucide funnel, as on All Tasks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Nxfm6CRXGP3BhKDWZ9ZFU
Cards read production and type, then parent and entity next to the thumbnail, status top right and smaller assignees on the right. The hidden cells lost to the cell reset and cluttered the grid. Unfolded, status and type take a line each instead of squeezing the production combobox. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JhSFWVSk5T7fWXE9nD9KJV
The Topbar spec mocks $route without a meta object, so the new pageTitle computed threw on every test. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KV65Eqg5sgZBYAewFwRZT3
The component only declared its name, so the whole Options API block is dropped: a template-and-style SFC needs no script at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert the page to <script setup> and drop the isAuthenticated / user getters, which the template never used. Read the redirect target from useRoute() rather than from the vuex-router-sync mirror in $store.state.route: same value, one hop less. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert the page to <script setup>: getters and head() become computed, the reset-password promise chain becomes async/await. Drop the mounted() resets of isLoading and isSuccess, which only reassigned their initial values. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert the page to <script setup>: data becomes refs, the resetPassword action a store.dispatch, and head() a useHead with a reactive title. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert Asset.vue to script setup on top of the useEntity composable instead of entityMixin. The composable gains the task selection state (currentTask, onTaskSelected) the page needs, and its manager and supervisor checks now use the production-scoped getters like the mixin did, which also fixes canEditTaskDates on the Edit page. Drop the dead members carried over from the mixin (changeTab, onEditClicked, isPreview, thumbnail path, unused getters, format mixin) and the CSS selectors that no longer match any element. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7h7MZgNUtk8cCp247ci6N
Convert the page to <script setup>: data becomes refs, the promise chain async/await, and the password-validity branch an early return instead of an else block. Clear the redirect countdown in onBeforeUnmount: leaving the page before it reached zero left the interval running and pushed the user back to the login page from wherever they had navigated. Drop the mounted() resets of isLoading, isError and secondsLeft, which only reassigned their initial values. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert the page to <script setup>: getters become computed, the logIn promise chain async/await, and the two SSO button labels ternaries. Rename the TwoFAsEnabled state to twoFAsEnabled and drop the unused argument of changedTwoFA. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert the page to <script setup>: getters become computed, the non-reactive productionIndex a plain let, and the search-field ref a useTemplateRef. Merge the single-use sectionPath helper into getPath. Rebuild the name index in the openProductions watcher. It was only built in mounted(), so on a cold load, where the productions arrive after mount, the index stayed empty and the search returned no result. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert the component to <script setup> and reach the socket through getCurrentInstance().appContext.config.globalProperties, the pattern the other migrated components already use, since $socket has no `this` to hang off any more. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert the component to <script setup>: getters become computed, actions
store.dispatch calls, and the five entity caches plain consts, since the
Maps are built once and mutated in place.
Replace the vue-websocket-next `socket: { events }` option, which has no
Composition API equivalent, with a socketEvents map bound through
socket.on in the setup body. The plugin bound them in beforeCreate, so
binding at setup keeps them registered before Main connects the socket
from its own mounted hook. No teardown: App is the root component.
Switch the locale overlay to i18n.global, the way lib/lang.js already
does it, drop the no-op 'task-type:update' handler for a comment saying
why the event is ignored, and give the preview modal a named cancel
handler instead of an inline $store.commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert EntityTaskList, EntityNews, EntityOutputFiles, EntityPreviewFiles and EntityTimeLogs to script setup. The format mixin is replaced by useFormat, the news socket listener is registered and removed explicitly, and the promise chains become async functions. The news socket guard compared against taskTypeId and taskStatusId that never existed on the component, so it now only checks the production. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7h7MZgNUtk8cCp247ci6N
Asset.vue: the dark overrides use the border and background tokens, the redundant dark padding goes away and the two page-header blocks merge. EntityTaskList moves its static row classes out of the class binding. EntityOutputFiles drops a dead thumbnail selector and matches the colspan to the column count. EntityPreviewFiles removes a redundant loading check and merges its duplicated wrapper rules. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7h7MZgNUtk8cCp247ci6N
Convert the component to <script setup>: data becomes refs, the searchData action a store.dispatch, and the search watcher async/await. Rename the input template ref to searchInput and read it with useTemplateRef. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Convert the component to <script setup>: getters become computed and the mapped actions store.dispatch calls. Drop toggleEntities, which nothing called. Make the import and remove helpers read productionId, entityTypeId and the selection from their refs instead of taking them as arguments that shadowed those same names, and rename the catch bindings to err for the same reason. Set the error flag when unshareAssets fails, so the error text passed to DeleteEntities can finally show: the flag existed but nothing ever raised it, leaving failed removals silent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Under 768px the production and section menus keep their thumbnail and icon and hide the selected label. The dropdown entries keep their labels: the mobile rule that hid every production name lived in ProductionName and is now scoped to the topbar's selected line. The section icon gets a min-width so a squeezed row cannot crush it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7h7MZgNUtk8cCp247ci6N
Under 768px the columns stack, the main column scrolls and the two side panels become drawers sliding in from the right, like the news feed. The xyz-in entry animation kept an identity transform on the page that made it the containing block of the fixed drawers: its fill mode is dropped. Tables switch to an opt-in card layout defined once in App.vue: cells with a data-label become label/value lines, card-head cells stack on top, the rest is hidden. The task list, preview files, time logs and output files use it. Empty tabs share a new EmptySection widget with the kanban empty-state style, the concept status filter only shows when concepts exist, and the schedule widget narrows its entity column on mobile. The task list highlight now comes from a selected-task-id prop so closing the drawer clears it; Shot, Sequence and Episode pass it too. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7h7MZgNUtk8cCp247ci6N
The global-search-field-open class only fed a rule resetting a border-radius the wrapper never carries, and appears nowhere else in the sources: drop both, and with them the class object binding. Drop max-height on the results panel too. The inline min-height is (nbResults || 1) * 60px, so it is never below 60px, and min-height wins over max-height. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
The three importFrom helpers repeated the same loading, dispatch, emit, catch and refresh sequence around shareAssets: keep one shareAndRefresh taking the payload and turn them into one-liners. removeSharedEntities stays on its own, it dispatches another action, raises the error flag and clears the selection. Drop the commented-out selected-entities markup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TooF3FPNhsC5XpzvC1QspX
Switch PeopleList to the shared datatable--cards rule: the user cell heads the card, role, departments and expiration become labelled lines, the per-list grid layout is dropped. The global rule also neutralises the sticky first column, zeroes the DepartmentName padding and gets a roomier card and head cell. Hide the new bot buttons under 768px on the bots page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F7h7MZgNUtk8cCp247ci6N
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N3RZPehgBepCt9wxwKqMN
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N3RZPehgBepCt9wxwKqMN
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N3RZPehgBepCt9wxwKqMN
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N3RZPehgBepCt9wxwKqMN
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N3RZPehgBepCt9wxwKqMN
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N3RZPehgBepCt9wxwKqMN
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N3RZPehgBepCt9wxwKqMN
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N3RZPehgBepCt9wxwKqMN
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N3RZPehgBepCt9wxwKqMN
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
user; topbar page title crashes on routes without meta.Solution
datatable--cardsrule in App.vue: opt-in class turns a datatable into label/value cards below 768px (data-labellines,card-headcells first, sticky column neutralised). Applied to the asset page tables and to PeopleList; per-list grid layouts removed.EmptySectionwidget, status combobox hidden without concepts, schedule left column narrowed on mobile.userexposed as a computed in Comment; route meta read guarded in the topbar page title.department:deletereadsdepartment_id, the seven keys translated in every locale.src/lib/timesheet.js; the three per-list mobile card blocks opt intodatatable--cardsand keep only their layout deltas.