- VisibilityModal: add optional CascadeLabel + CascadeEnabled for checkbox
- Albums page: checkbox to cascade album visibility to non-deleted assets
(lower-only: never raise asset visibility)
- PersonForm: cascade checkboxes when editing cosplayer visibility
- Cascade to albums (lower only)
- Cascade to assets in every album (only if album cascade enabled)
- Inject AssetService in Albums page and PersonForm
- Create MilkStream.Client/Components/Shared/VisibilityModal.razor
- Replace duplicated modal markup in AlbumDetail, CosplayerDetail, Albums
- Add Select All/Deselect All to Albums via AlbumGrid ref
- Add Delete, Visibility modal to Albums select mode
- Add mobile dropdown to Albums action buttons
- Add GetCurrentAlbumIds() to AlbumGrid for Select All support
SortFilterBar: wrap search+sort in flex-nowrap row, remove ms-auto
from ActionButtons so wrapped items are left-aligned and clean.
AlbumDetail: add flex-shrink: 0 to action buttons container.
- Add BulkUpdateAlbumsAsync to AlbumService
- Fix AlbumCard border gate from IsAdminOrCurator to CanEdit()
- CosplayerDetail: Delete, Unlink, Visibility buttons in select mode
- CosplayerDetail: Select All / Deselect All button
- CosplayerDetail: visibility breakdown stats in header
- CosplayerDetail: visibility modal for albums
- Private assets → red border (instead of orange)
- Protected assets → orange border
- Deleted assets → greyed out (opacity + grayscale)
- When not in select mode: hide Private and Deleted assets from view
Backend:
- Add DeletedAt to AssetPreviewDto, AlbumAssetPreviewDto, AlbumPreviewDto
- Remove redundant DeletedAt from AssetDto (now inherited from base)
- Add viewerId parameter to ToAssetPreviewDto, ToAlbumPreviewDto,
ToAlbumFullDto, ToPersonDetailedDto mappers
- Conditionally send DeletedAt only when viewer is Admin or asset uploader
- Pass uid/viewerId from all controller/repository call sites
Frontend:
- AlbumCard: show orange (not public) / red (deleted) border in select
mode for admins/curators
- AlbumDetail: same border logic in GetTileClass for asset tiles
- Borders only appear in select mode per REDCODE's feedback
Adds R3/R4 visibility gates to AlbumRepository.FindVisible and
PersonRepository.FindVisible so that 404 is returned when the
requesting user lacks permission to see the entity itself.
Adds comprehensive HTTP tests verifying:
- R2: anonymous asset detail access
- R3: Public/Protected visible to User, Private returns 404 (Admin sees all)
- R4: Private person returns 404 for User, 200 for Admin/Curator
- Update dotnet-tools.json location reference
- Fix EF Core Tools version (both now v10.0.9)
- Remove stale SharedWith.Any() EF Core gotcha (per-user sharing removed)
- Fix index example to use Visibility/UploadedBy columns
- Remove _editAccessLevel == Maintainer guard so the maintainer
assignment section appears when editing any user as admin
- OpenEditModal already loads maintained persons regardless of access level
- Remove _editAccessLevel == Maintainer guard from the maintained
cosplayers markup so it appears for User, Curator, and Admin edits too
- Remove the same guard from OpenEditModal so maintained persons are
loaded regardless of the user's current access level
- Change ?? EAccessLevel.Admin fallback to ?? EAccessLevel.User in UserController.GetAll
- Add .Include(u => u.MaintainedPersons) to UserRepository.GetAll() so MaintainedPersonIds/MaintainedPersonNames populate in list endpoint
- Fix UsersMapper.cs method body indentation (was flush with class)
- Add Maintainer-role visibility tests (68.5-68.8): promote user, test viewing admin, demote back
Test 75 admin assertion: allow null email for server-scanned assets
(Guid.Empty placeholder has no email). Removes all remaining jsonPath
calls that could NPE on null resolved values.
jsonPath throws NullPointerException in GraalVM when the resolved JSON
value is null. Use response.body[n].property directly for all null
assertions in search-list and visibility tests.
Add loading spinner and null-state guard to prevent rendering
before user data arrives. Fix unsafe `user is not {DeletedAt: null}`
pattern that throws NRE on null user.
- Add docker-compose.debug.yml override for Debug build + Development env
- Add UseWebAssemblyDebugging() middleware in Development
- Production path unchanged (HSTS + HTTPS redirect preserved)
Backend:
- Add MaintainerUserIds to PersonUpdateDto/PersonDetailedDto
- Add MaintainedPersonNames to UserInfoDto
- Add IPersonRepository.SetMaintainers + implementation
- PersonRepository.Find/FindVisible now include Maintainers
- PersonController.Update handles maintainer assignment (admin/curator)
- Fix PersonController.Update to save Visibility field
- Allow curators to list all users via UserController.GetAll
Frontend (PersonForm):
- Add maintainer search+select UI (admin/curator only in edit mode)
- Search users client-side, click to add, badge with X to remove
- Saves maintainer IDs on person update
Frontend (CosplayerDetail):
- Show 'Maintained by: user1, user2' in header with clickable links
- Pass maintainer data to PersonForm
Frontend (User page):
- Show 'Maintainer of: cosplayer1, cosplayer2' with links
- Non-admin view: strip Email, Created, Deleted fields
- Fix LoadUser to use route UserId instead of logged-in user
Backend:
- Add MaintainedPersonIds to UserUpdateDto
- Add SetMaintainedPersons to user repository (removes old PersonMaintainer rows, inserts new)
- Wire up in UserController.Update (admin only)
Frontend:
- Add PersonService injection to AdminUsers page
- Searchable cosplayer dropdown in edit modal for Maintainer users
- Click result to add, badge with X to remove
- Zero debounce, saves only on Submit
- Pre-loads currently assigned cosplayer names on modal open