Commit Graph

582 Commits

Author SHA1 Message Date
f4d4b6a75a Merge branch 'develop' into fix/mobile-ui-responsive-fixes 2026-07-08 21:33:43 +00:00
REDCODE
4ff3b24df8 Merge remote-tracking branch 'gitea/develop' into develop 2026-07-08 23:33:05 +02:00
REDCODE
1da7236d5d fix(frontend): sorts setting by alphabetical 2026-07-08 23:32:46 +02:00
MrFastwind
df14b8b227 fix (lactose): remove check from authentication endpoint
ref: #36
2026-07-08 23:04:51 +02:00
MrFastwind
15508013a3 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	Lactose/Controllers/UserController.cs
2026-07-08 22:39:20 +02:00
MrFastwind
cb47336230 fix(Lactose): Change to use UtcNow, even if alredy working without timeline 2026-07-08 22:38:05 +02:00
76cabe058b fix(ui): collapse AdminUsers table columns and actions on mobile (#48)
- Hide Email and Created columns below md breakpoint (d-none d-md-table-cell)
- Hide Last Login below lg breakpoint (d-none d-lg-table-cell)
- Replace action btn-group with dropdown menu on mobile (d-sm-none)
- Reduces table from 7 visible columns to 4 on mobile
2026-07-08 19:51:48 +02:00
33175ffdcb fix(ui): responsive layout fixes for mobile portrait mode (closes #48)
- Settings: stack sidebar vertically on mobile, fix grid minmax 380->280px, use dvh
- AlbumDetail: add mobile breakpoints for grid, restructure header actions
- ImagePreview: switch info bar to column layout on mobile
- User profile: fix col-2/col-2 to responsive col-5/col-7 breakpoints
- AdminUsers: wrap inline editing controls with flex-wrap on mobile
- Jobs: collapse 8 action buttons into dropdown on mobile
- Albums: add flex-wrap to header, hide 'New Album' text on mobile
- NavMenu: responsive authenticated area (column on mobile, btn-group-sm)
- Empty-state messages: m-5 -> m-2 m-sm-5
- Footer: hide on mobile (d-none d-sm-flex)
- Login: remove orphaned 'Remember me' checkbox
2026-07-08 19:49:11 +02:00
54cf5eff2b feat(stats): add percentage and stale counters to completeness cards
- Show percentage of total assets on missing metadata/thumbnail/preview/phash cards
- Add stale counts for thumbnails and previews (generated at wrong resolution vs current settings)
- Add h-100 to all completeness cards for consistent row heights
2026-07-08 19:45:09 +02:00
94bba45f45 Merge branch 'feature/admin-users-page' into develop
Admin user management page (#45)
2026-07-08 19:18:29 +02:00
23ef7e3ded fix(auth): detect banned/deleted user on page load via InitializeAsync
- LoginService.InitializeAsync: after fetching LoggedUser, check IsBanned
  and DeletedAt. If banned/disabled, set ForceLogoutReason, call Logout
  to clear state, and return false
- App.razor: after InitializeAsync completes, check ForceLogoutReason
  and navigate to /login if set. This handles the timing issue where
  MainLayout isnt created yet when the event fires on page load

Refs #45
2026-07-08 19:15:49 +02:00
a84d93882d fix(auth): show ban/disabled message on login and force-logout on refresh
- AuthController: fix HTTP 100 Continue → 403 Forbidden for banned/disabled
  users in both Login and RefreshToken endpoints
- LoginService.Login: return error message from AuthResultDto on failure
  instead of a generic message
- LoginService.Reauthenticate: parse AuthResultDto on refresh failure;
  if "User is banned" or "User is disabled", trigger force logout via
  new ForceLogout event, setting ForceLogoutReason property
- MainLayout: subscribe to ForceLogout event, redirect to /login
- Login.razor: display specific error messages (ban reason, force-logout
  reason) instead of generic "Login failed"

Refs #45
2026-07-08 19:10:53 +02:00
0ec6888809 fix(admin): show inline errors instead of replacing entire page
- Moved error/success alerts inside the main content block (else branch)
  so validation errors (empty email, username, etc.) do not hide the table
- Added dismissible error alert with ClearError method
- Added clearError method to code-behind
- Fixed broken @if(_showCreateForm) guard for create form

Refs #45
2026-07-08 19:00:08 +02:00
977d2325fa feat(admin): add inline editing for username, email, and password
- Username and email columns now have pencil-to-edit with confirm/cancel
- Actions column gains a key button to change password inline
- Validation: empty username/email/password rejected with error message
- Unified CancelFieldEdit clears all edit modes at once

Refs #45
2026-07-08 18:56:52 +02:00
20975a2872 fix(ui): remove broken onfocusout handler from role edit dropdown
- onfocusout on the wrapper span was firing when focus moved to child buttons,
  cancelling the edit before the button onclick handlers ran
- Confirm/cancel now works via explicit button clicks only
- Starting a new edit on a different row implicitly cancels the previous one

Refs #45
2026-07-08 18:54:49 +02:00
61cf886174 fix(user): hash password in Create endpoint instead of storing raw
- UserController.Create was assigning userCreateDto.Password directly without
  hashing via IPasswordHasher, unlike the Update method and AuthController
- Moved duplicate email/username checks before User object instantiation

Refs #45
2026-07-08 18:53:46 +02:00
bf638ca995 fix(admin): add confirm/cancel for role change and prevent admin self-demotion
- Role dropdown now uses explicit confirm (checkmark) / cancel (X) buttons
  instead of auto-committing on selection
- Clicking outside the dropdown cancels the edit (onfocusout)
- Selecting the same role as current is a no-op
- Admins cannot demote themselves below Admin level

Refs #45
2026-07-08 18:52:27 +02:00
a599648d87 feat(admin): add user management page for admins
- Add AdminUsers.razor page at /Users with user listing, ban/unban toggle,
  access level change, delete, and create user functionality
- Extend UserService with GetAllUsersAsync, GetUserByIdAsync,
  UpdateUserAsync, DeleteUserAsync, CreateUserAsync methods
- Add Users link to NavMenu dropdown for admin users
- Fix ban/unban bug in UserController where BannedAt was only updated
  if the user had been previously banned

Closes #45
2026-07-08 18:47:01 +02:00
e5fc1a017e chore: remove debug auth logging middleware from Program.cs 2026-07-08 18:08:48 +02:00
44394daebf fix(AlbumController): exclude deleted assets from search visibility for #11
Album search for User role used asset.IsPubliclyShared and
SharedWith checks without filtering soft-deleted assets. A deleted
public asset would cause its album to appear in search results.

Added asset.DeletedAt == null checks to both filter paths.

Refs: #11
2026-07-08 17:49:08 +02:00
ac76aab168 fix(AssetController): correct search visibility filter for #11
The User-role asset search filter had two bugs:
1. DeletedAt != null was negated — shared non-deleted assets were
   excluded, while shared deleted assets passed the filter.
2. DeletedAt check was applied only to the shared leg — public
   deleted assets leaked through the filter.

Corrected to: not-deleted AND (public OR shared), matching the
logic used by GET /api/asset/{id}.

Refs: #11
2026-07-08 17:47:43 +02:00
4f56acb320 fix(controllers): tighten asset visibility and media access checks
- AlbumController GET {id}: exclude soft-deleted assets for User role
  (public/owned and shared paths) and Curator role (viewed all assets)
- AssetController GET: add explicit parentheses to DeletedAt operator
  grouping in search filter to match intent
- MediaController: deny Curators direct file access to deleted assets;
  extract CanAccessAssetDirectly helper method

Refs: #11
2026-07-08 17:44:03 +02:00
70fe586fc6 fix: force square aspect-ratio in grid mode over inline style
- Inline style aspect-ratio:W/H overrides CSS aspect-ratio:1
- !important needed to force square crop in grid tiles
2026-07-08 16:44:40 +02:00
58bd4ee766 fix: override masonry inline styles with !important for grid mode
- JS masonry sets position/width/left/top/height as inline styles on tiles
- Grid CSS uses !important to override inline styles for proper layout
- Force StateHasChanged on mode switch to ensure re-render
2026-07-08 16:36:46 +02:00
3d789d68e7 refactor: remove list view, add grid/masonry toggle to album detail
- Albums page: drop list mode, keep masonry and grid
- AlbumDetail page: add masonry/grid toggle in header
- Masonry mode uses JS masonryLayout, grid mode uses CSS grid with square tiles
2026-07-08 16:32:30 +02:00
9327a739f8 feat: view mode selector for albums page (masonry/grid/list)
- Button group in header: masonry (columns), grid (square tiles), list (rows)
- CSS-driven mode switching via album-grid.masonry/.grid/.list classes
- List mode: horizontal card with thumbnail left, info right
2026-07-08 16:19:47 +02:00
de731095cc fix: use CSS columns for albums page, keep JS masonry only for album detail
- Albums page: remove masonryLayout JS call, use column-count CSS (reliable, no ordering needed)
- AlbumDetail: keeps JS masonryLayout for left-to-right alphabetical ordering with varying heights
2026-07-08 16:18:03 +02:00
696ea613de revert: switch album detail back to masonry layout 2026-07-08 14:52:38 +02:00
9b17b6b231 fix: faulty migration that could cause lactose crash on migration apply 2026-07-08 14:47:54 +02:00
b1d5356220 feat: justified gallery layout for album detail page
- New justifiedLayout.apply JS function: uniform row heights, automatic widths
- Reads aspect-ratio from tile inline style, fills each row to container width
- AlbumDetail calls justifiedLayout in OnAfterRenderAsync
- Albums page stays on masonryLayout
2026-07-08 14:19:19 +02:00
37dad0d1d7 fix: JS-based masonry for left-to-right item flow with varying heights
- Add masonryLayout.apply JS function using shortest-column-next algorithm
- Both Albums and AlbumDetail call masonryLayout on each render
- Items flow left-to-right in alphabetical order, each keeping its own aspect-ratio height
- Remove CSS column-count in favor of absolute positioning by JS
2026-07-08 14:10:51 +02:00
c5e41f2c05 fix: unlock body scroll on navigation, reorder masonry for left-to-right flow
- Home.DisposeAsync now unlocks body scroll so navigation to other pages works
- Albums and AlbumDetail unlock body scroll on init as defense
- Reorder items with column-major index mapping so CSS columns display left-to-right
- Switch both pages back to column-count CSS for reliable masonry
- Add pageLayout.columnCount JS helper matching media query breakpoints
2026-07-08 14:05:31 +02:00
d45a915ba9 fix: use flexbox with align-items:flex-start for album detail grid
- CSS Grid forces uniform row heights, cropped landscape images next to portraits
- Flexbox with align-items:flex-start preserves each tile's own aspect-ratio height
- align-content:flex-start ensures container height grows for scrolling
2026-07-08 13:58:37 +02:00
b7d1912fc6 fix: switch album detail grid from flexbox to CSS Grid
- Flexbox was collapsing with aspect-ratio tiles, preventing scroll
- CSS Grid with repeat(N, 1fr) fills left-to-right with proper row heights
2026-07-08 13:54:20 +02:00
d51efe8b3c fix: remove content-visibility and contain-intrinsic-size from album detail tiles
- These properties overrode aspect-ratio in flexbox layout, causing all tiles to render at 200px regardless of actual dimensions
2026-07-08 13:52:56 +02:00
48a97beb74 fix: remove preventDefault from album links so they actually navigate
- @onclick:preventDefault was canceling the browser's link navigation
- @onclick:stopPropagation alone suffices to prevent tile click
2026-07-08 13:51:33 +02:00
e94406932b fix: switch album detail page to row-based flexbox masonry
- Replace column-count CSS masonry with flex-wrap layout
- Tiles flow left-to-right first, then wrap to next row
- Responsive column counts: 2/3/4/5 per row
2026-07-08 13:48:36 +02:00
4fe1fb0da2 feat: make album names clickable links in home tile hover and preview
- Add AlbumIds parallel list to AssetPreviewDto
- Populate AlbumIds in AssetsMapper
- Home tile hover: album names link to /albums/{id} with onclick:stopPropagation
- ImagePreview ChildContent: album names link to /albums/{id}
- CSS: tile-info-album and preview-info-album support <a> tags with hover underline
2026-07-08 13:48:17 +02:00
dcc8eba931 fix: scoped CSS deep selectors for preview info text and filename binding
- Use ::deep in ImagePreview.razor.css so preview-info-cosplayer/album styles pierce CSS isolation boundary into ChildContent
- Add missing @ prefix to Filename parameters so they evaluate as C# expressions instead of literal strings
2026-07-08 13:36:55 +02:00
4c90e553af feat: auto-assign album cover from first alphabetically-sorted asset
- Add one-time migration SQL to set CoverAssetId for existing albums
- AlbumController.Create auto-sets cover from first asset if not specified
- CreateAlbumsJob assigns cover images after asset assignment phase
- Stats: add AlbumsMissingCover and CosplayersMissingCover counters
2026-07-08 13:20:11 +02:00
b8f75f9529 refactor: extract ImagePreview shared component from Home and AlbumDetail
- Create ImagePreview.razor component with self-contained url generation, keyboard handling, and download
- Add Filename to AssetPreviewDto and AlbumAssetPreviewDto DTOs
- Update AssetsMapper and AlbumMapper to populate FileName
- Remove duplicate preview overlay HTML/CSS/code from Home.razor and AlbumDetail.razor
- Fix AssetDto.FileName hiding inherited member from AssetPreviewDto
2026-07-08 13:18:23 +02:00
d10e062373 feat: add albums list page and album detail page with inline preview
- Albums.razor: masonry card grid with infinite scroll, create button for Admin/Curator
- AlbumDetail.razor: asset masonry grid with inline preview overlay (prev/next, download)
- AlbumForm.razor: create/edit modal with title, person dropdown, cover image picker
- AssetPicker.razor: modal showing album assets for cover selection
- Fix NavMenu Albums link to /albums
- Add albumObserver JS for independent infinite scroll
2026-07-08 13:11:40 +02:00
3c1d2ef14b feat: add AlbumService and PersonService to WASM client
- AlbumService: CRUD operations for albums via API
- PersonService: GetAllAsync for person dropdown
- Register both as Scoped in DI
2026-07-08 13:11:31 +02:00
b58c8ebce8 fix: album controller Save calls, person list endpoint, media empty path guard
- Add missing Save() calls to AlbumController Update and Delete
- Wire CoverAssetId in AlbumController Create/Update/BulkUpdate
- Sort album assets by OriginalFilename in Get endpoint
- Implement PersonController.GetAll returning non-deleted people
- Guard MediaController GetThumb/GetPreview against empty paths (return 404)
2026-07-08 13:11:27 +02:00
d602d2d34b feat: add CoverAssetId to albums with resolution data in full DTO
- Add CoverAssetId FK and CoverAsset navigation to Album model
- Add AlbumAssetPreviewDto with resolution/thumbnail info
- Extend AlbumPreviewDto/FullDto with PersonName, CoverAssetId, AssetCount
- Add CoverAssetId to AlbumCreateDto and AlbumUpdateDto
- Update AlbumMapper to populate new fields from eager-loaded relations
- Add EF migration for CoverAssetId column
2026-07-08 13:11:20 +02:00
de938e6d1e docs: update AGENTS.md with verified infrastructure gotchas 2026-07-08 12:28:39 +02:00
949dec9bdc feat: add data completeness indicators to statistics page 2026-07-08 12:21:25 +02:00
89c4bfe13f feat: implement seed-based deterministic random ordering for home page
Replace the fixed GUID-based ordering (OrderBy(a => a.Id)) with a
client-generated seed that produces a truly different random order on
each page refresh. The seed is passed to the server and used to
deterministically shuffle all asset IDs via a hash function, ensuring
consistent pagination with no duplicates across pages within a session.

Also fix LoadPreviousPage showing a spinner when already at page 1 by
adding loadedMinPage <= 1 to the guard clause.
2026-07-08 12:10:37 +02:00
7a0b72584c fix: propagate cancellation tokens through linked child job CTS
Replace manual childJobs.ForEach(j => j.Cancel()) with linked
CancellationTokenSources. Master jobs now call LinkParentToken(token)
on each child, so when the master's CTS fires, all children (running
and queued) receive cancellation automatically via their linked token.

Cancelling a master job now:
- Immediately signals all Running children via their linked CTS
- Ensures queued children, when started by JobManager, receive an
  already-cancelled token and bail without processing
- Eliminates the collection-modification-during-ForEach race condition

Affects: PHashJob, ThumbnailJob, PreviewJob, MetadataJob,
FileSystemCrawlJob.
2026-07-08 12:00:27 +02:00
ebfab19df4 Add autorestart for services 2026-07-08 09:56:45 +02:00