- Add ChildrenResponse DTO (Children + Since) in Butter.Dtos.Jobs
- JobManager.GetChildren returns List<JobStatusDto> only (no tuple),
active children filtered via LINQ over activeJobs.Values
- JobsController generates Since timestamp, returns ChildrenResponse
instead of setting x-delta-timestamp header
- JobsService deserializes ChildrenResponse instead of parsing headers
EF Core cannot translate three-level nested Any through many-to-many
navigation (Person→Albums→Assets→SharedWith). Load data with Include
chain and filter visible persons in-memory instead.
- IPersonRepository.GetAllVisible filters persons by asset visibility
- For User level: only returns persons that have at least one album
with a non-deleted asset that is publicly shared, owned by the user,
or explicitly shared with the user
- For Admin/Curator: returns all persons (unchanged behavior)
- PersonController.GetAll extracts user data and uses GetAllVisible
Create PasswordField.razor in Components/Shared with:
- Two-way binding for Password, ConfirmPassword, OldPassword
- Strength bar with color-coded segments
- Requirements checklist (min length, upper/lower, number/special)
- Passwords-do-not-match validation
- Public IsValid property for parent submit-button checks
- ShowOldPassword toggle for change-password vs register flows
Register.razor and User.razor now use the shared component,
eliminating duplicated password validation logic.
Copy password validation UI from User.razor — strength bar,
requirements checklist, passsword match check, submit disabled
until all requirements are met.
- NavigationExtensions: centralized NavigateToLogin/NavigateToLoginForce
helpers that include returnUrl query parameter
- LoginService: SemaphoreSlim guard on Reauthenticate to prevent
concurrent refresh races (timer + API handler)
- MainLayout: expiry-driven keepalive loop — parses JWT, schedules
reauthentication 2 min before token expiry; starts on login,
stops on logout/force-logout
- Login page: reads returnUrl from query via SupplyParameterFromQuery,
navigates there (or /) on success
- All NavigateTo(/login) call sites updated to use returnUrl:
App, MainLayout, NavMenu, Jobs, Settings, AdminUsers
- Cosplayers: fix eternal spinner when logged out (set isLoading=false),
update message to 'No media available' with hint about public content
- EagerFetchMissing fetches children for active roots on parameter set
so the summary never shows the placeholder
- ChildSummary returns null instead of "sub-jobs..." when not fetched
- JobTree polls children for all Level 0 active roots (not just expanded),
so segment progress bars update on collapsed rows too
- Add PollChildren helper to deduplicate fetch logic
- OnExpanded only stops refresh when no active roots remain
- Store UtcNow as fallback when server x-delta-timestamp is missing
- Fix segment colors: share ToCssClass between single and stacked bars,
use text-bg-* classes for proper Bootstrap 5.3 progress-bar override
- Replace status words with Bootstrap icons in child summary
(e.g. "15 Running" → "15 🔃") for compact display
- JobManager.GetChildren now accepts optional 'since' parameter
- Returns only children with LastChange/ModifiedAt > since
- Includes seenIds dedup between active and past children
- Returns response timestamp for next poll
- Controller sets x-delta-timestamp header
- Add ModifiedAt (DateTime?) to JobRecord
- Auto-set on Insert/Update in repository
- Add GetChildrenModifiedSince to interface + implementation
- EF Core migration AddModifiedAtToJobRecord
- Removed job type badge spans from desktop and mobile JobRow
- Colored job-type icons instead (text-info/secondary/primary/etc)
- Desktop launch buttons use matching btn-outline-* colors per job type
- Mobile launch dropdown changed from dropup to dropdown to avoid
clipping into navbar
- Dropdown item icons colored per job type
The Interlocked.Increment(ref failedAssets) for Failed subjobs
caused double-counting: a subjob with N/12 failed assets would
add 1 (failed subjob) + 12 (individual assets) = 13, exceeding
the total asset count. Removed the Increment since subjob's
failedAssets is already the accurate per-asset count.
- Unified Active/Past tabbed view replacing separate sections
- Search by name/message, filter by job type and status
- Status summary strip with colored counts
- Job type badges with distinct colors per EJobType
- Status labels next to status dots
- Compact mobile card layout (d-md-none)
- Tighter tree spacing with lighter indentation
- Scoped Jobs.razor.css extracted from inline styles
Refs #76
- Replace non-functional cosplayer badge with clickable link to cosplayer page
- Move Edit button next to album title in info section
- Add select mode: select/deselect image tiles with check overlays
- Add bulk removal of selected assets (unlink from album)
- Add Select / Done / Remove N action buttons replacing Edit in actions area
- Add scoped CSS for cosplayer link style and tile selection overlays