Skip to content

[ui] Timesheets chart, task date filters and admin page chrome - #2196

Open
frankrousseau wants to merge 64 commits into
cgwire:mainfrom
frankrousseau:main
Open

[ui] Timesheets chart, task date filters and admin page chrome#2196
frankrousseau wants to merge 64 commits into
cgwire:mainfrom
frankrousseau:main

Conversation

@frankrousseau

@frankrousseau frankrousseau commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Timesheets: no view of logged time over a period, filters lost on reload, totals and over-expected hours not visible, page unusable on small screens, list and person panel still on Options API.
  • All tasks: no start or due date filters, overdue tasks unflagged, table unreadable below the tablet breakpoint.
  • News feed pagination broken, load states silent, filters not kept in the URL.
  • Production list has no search or grouping and no way to fold closed productions.
  • Tab bars read as buttons and float above their content, admin page titles duplicate the topbar, the global search crowds the mobile topbar.
  • CSV import errors squeezed into one footer line.
  • Asset page, its entity sub-components, the login and error pages, the root and wrapper components, the global search field and the library manage panel still on Options API.
  • Asset page broken on small screens: side column stuck at its desktop width, header under the topbar, tables unreadable, empty tabs each styled their own way, concepts status combobox shown with no concepts.
  • Topbar production and section menus take all the width on mobile.
  • People, bots and guests lists use a per-list grid card layout on mobile; the new bot button stays visible on read-only mobile.
  • Comment component reads an undefined user; topbar page title crashes on routes without meta.
  • Timesheets grid flags over-expected hours without the days off the side panel counts, the year selector lands on the wrong month when jumping to a past year, display filters refetch the person aggregate, and the year floor hides backfilled time logs.
  • Edit page lost the task row highlight, the production footer counts folded closed productions, deleted departments linger in open tabs, seven timesheet strings were English-only.

Solution

  • Timesheets: logged time chart per period, route-driven filters, totals row and footer, over-expected flags, current column marker, panel tiles, mobile layout, salary unit, Composition API migration of the list and panel.
  • All tasks: start and due date filters, overdue flag, single-id selection, card layout below 768px.
  • News: fixed pagination, visible load states, filters synced to the URL.
  • Production list: search on name and code, group-by combobox, collapsible closed section, cards on mobile.
  • Tabs as an underline bar whose rule spans the header row, admin titles moved to the topbar, funnel icon on filter buttons, global search hidden on mobile.
  • Import render modal shows the server error with its line, the rows imported before the failure and the faulty row highlighted.
  • Composition API migration of Asset, EntityTaskList, EntityTimeLogs, EntityPreviewFiles, EntityOutputFiles, EntityNews, Login, ResetPassword, ResetChangePassword, FirstConnection, ServerDown, NotFound, OpenProductions, App, Main, GlobalSearchField and ManageLibrary, followed by a cleanup pass.
  • Shared datatable--cards rule in App.vue: opt-in class turns a datatable into label/value cards below 768px (data-label lines, card-head cells first, sticky column neutralised). Applied to the asset page tables and to PeopleList; per-list grid layouts removed.
  • Asset page: task and concept panels open as slide-in drawers below 1024px, main column scrolls, empty tabs share a new EmptySection widget, status combobox hidden without concepts, schedule left column narrowed on mobile.
  • Topbar production and section menus show only their icon on mobile, dropdown labels kept.
  • New bot buttons hidden below 768px; user exposed as a computed in Comment; route meta read guarded in the topbar page title.
  • Expected hours net of each person's days off at every level (day offs loaded with the grid), month clamp scoped to the current year, element-wise watch on the aggregate sources, year floor extended to the years the year table holds.
  • Task row selection wired on the edit page, footer counts the listed rows only, department:delete reads department_id, the seven keys translated in every locale.
  • Timesheet grid, chart, panel and task list share their column, period, current-column and value helpers in src/lib/timesheet.js; the three per-list mobile card blocks opt into datatable--cards and keep only their layout deltas.

frankrousseau and others added 29 commits September 7, 2026 18:18
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
frankrousseau and others added 28 commits September 7, 2026 18:29
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant