feat: Add archive action to CategoriesPage (admin-only)#138
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds an admin-only archive action for Categories in the Web UI/client and extends Issue listing to support filtering by Status and Category across Shared contracts, API, and Web.
Changes:
- Add
ArchiveAsynctoICategoryApiClientand wire a new Archive button + confirmation dialog intoCategoriesPage. - Add
StatusName/CategoryNamefilters toListIssuesQuery, API endpoint/handler/repository, and WebIssueApiClient/IssuesPage. - Update API unit tests to match the expanded
IIssueRepository.GetAllAsyncsignature.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Api.Tests.Unit/Handlers/Issues/ListIssuesHandlerTests.cs | Updates mocks/expectations for expanded repository filter parameters. |
| tests/Api.Tests.Unit/Endpoints/IssueEndpointsTests.cs | Updates endpoint test mock signature for issue listing. |
| src/Web/Components/Features/Issues/IssuesPage.razor | Passes status/category filter values into the issue list API call. |
| src/Web/Components/Features/Issues/IssueApiClient.cs | Extends GetAllAsync API to include status/category query params. |
| src/Web/Components/Features/Categories/CategoryApiClient.cs | Adds ArchiveAsync calling DELETE /api/v1/categories/{id}. |
| src/Web/Components/Features/Categories/CategoriesPage.razor.cs | Adds archive dialog state + confirm/cancel handlers and optimistic removal. |
| src/Web/Components/Features/Categories/CategoriesPage.razor | Adds Archive button (admin-only) and confirmation dialog markup. |
| src/Shared/Contracts/ListIssuesQuery.cs | Adds StatusName and CategoryName filter properties. |
| src/Api/Handlers/Issues/ListIssuesHandler.cs | Passes new filters to IIssueRepository.GetAllAsync. |
| src/Api/Handlers/Issues/IssueEndpoints.cs | Accepts statusName and categoryName query params and maps them onto ListIssuesQuery. |
| src/Api/Data/IssueRepository.cs | Adds Mongo filters for status/category via regex. |
| src/Api/Data/Interfaces/IIssueRepository.cs | Expands GetAllAsync signature to include status/category filter params. |
| /// <summary> | ||
| /// Gets or sets the status name for filtering by status. | ||
| /// </summary> | ||
| public string? StatusName { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the category name for filtering by category. | ||
| /// </summary> | ||
| public string? CategoryName { get; init; } |
There was a problem hiding this comment.
The PR description focuses on adding an admin-only archive action for Categories, but this diff also introduces new Issue list filtering parameters (statusName, categoryName) across Shared contracts, API endpoints/repository, and Web UI/client. Either update the PR description to include this additional scope or split the Issue filtering work into a separate PR to keep review/rollback isolated.
| <AuthorizeView Policy="Admin"> | ||
| <Authorized> | ||
| <RadzenButton Icon="archive" ButtonStyle="ButtonStyle.Danger" Size="ButtonSize.Small" | ||
| Click="@(() => HandleArchive(cat.Id, cat.CategoryName))" @onclick:stopPropagation="true" | ||
| Class="ms-1" /> | ||
| </Authorized> | ||
| </AuthorizeView> |
There was a problem hiding this comment.
AuthorizeView is using Policy="Admin", but the Web app appears to use role-based checks ([Authorize(Roles = "Admin")], <AuthorizeView Roles="Admin">) and no "Admin" policy is configured. This can hide the Archive button for admins or throw at runtime when the policy can't be found. Use Roles="Admin" here (or define an Admin authorization policy in AuthExtensions).
| /// <summary> | ||
| /// Gets or sets the status name for filtering by status. | ||
| /// </summary> | ||
| public string? StatusName { get; init; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the category name for filtering by category. | ||
| /// </summary> | ||
| public string? CategoryName { get; init; } |
There was a problem hiding this comment.
New query fields StatusName and CategoryName were added, but ListIssuesQueryValidator currently only constrains SearchTerm/AuthorName length. To keep input validation consistent (and avoid extremely long values being passed through to Mongo regex filters), add MaximumLength rules for StatusName and CategoryName similar to the existing ones.
| /// <param name="categoryName">Optional category name to filter by.</param> | ||
| /// <param name="cancellationToken">Cancellation token.</param> | ||
| Task<PaginatedResponse<IssueDto>> GetAllAsync(int page = 1, int pageSize = 20, string? searchTerm = null, string? authorName = null, CancellationToken cancellationToken = default); | ||
| Task<PaginatedResponse<IssueDto>> GetAllAsync(int page = 1, int pageSize = 20, string? searchTerm = null, string? authorName = null, string? statusName = null, string? categoryName = null, CancellationToken cancellationToken = default); |
There was a problem hiding this comment.
IIssueApiClient.GetAllAsync signature has changed (added statusName and categoryName). There are still call sites in the repo (e.g. tests/Web.Tests.Bunit/Components/Features/Issues/IssuesPageTests.cs) using the old 5-parameter overload, which will fail to compile. Update those mocks/calls to include the new optional parameters (or switch to named arguments) so the solution builds/tests pass.
| if (!string.IsNullOrWhiteSpace(statusName)) | ||
| { | ||
| url += $"&statusName={Uri.EscapeDataString(statusName)}"; | ||
| } | ||
| if (!string.IsNullOrWhiteSpace(categoryName)) | ||
| { | ||
| url += $"&categoryName={Uri.EscapeDataString(categoryName)}"; | ||
| } |
There was a problem hiding this comment.
GetAllAsync now appends statusName and categoryName to the query string, but the existing unit tests for IssueApiClient only cover searchTerm and authorName. Add tests that assert these new parameters are included/escaped correctly when provided (and omitted when null/whitespace).
| if (!string.IsNullOrWhiteSpace(statusName)) | ||
| { | ||
| filters.Add(filterBuilder.Regex(x => x.Status.StatusName, new BsonRegularExpression(statusName, "i"))); | ||
| } | ||
|
|
||
| if (!string.IsNullOrWhiteSpace(categoryName)) | ||
| { | ||
| filters.Add(filterBuilder.Regex(x => x.Category.CategoryName, new BsonRegularExpression(categoryName, "i"))); | ||
| } |
There was a problem hiding this comment.
Filtering by statusName/categoryName was added to the Mongo query, but there are no integration tests asserting that these new filters actually constrain results. Add IssueRepositoryTests coverage that creates issues with different embedded StatusDto.StatusName / CategoryDto.CategoryName values and verifies GetAllAsync(..., statusName: ..., categoryName: ...) returns only matching items.
df51e09 to
e6f43d9
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #138 +/- ##
==========================================
- Coverage 55.39% 54.55% -0.84%
==========================================
Files 128 128
Lines 2939 2984 +45
Branches 332 336 +4
==========================================
Hits 1628 1628
- Misses 1068 1109 +41
- Partials 243 247 +4
🚀 New features to boost your workflow:
|
- Add ArchiveAsync method to ICategoryApiClient interface
- Implement ArchiveAsync in CategoryApiClient (DELETE /api/v1/categories/{id})
- Add Archive button to CategoriesPage visible only to admins
- Add ConfirmDialog for archive confirmation
- Implement optimistic UI update after successful archive
- Use confirmation message: 'Archive '{CategoryName}'? It will no longer appear in issue forms.'
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ArchiveAsync method to IStatusApiClient interface and implementation - Add archive button (admin-only) to StatusesPage with confirmation dialog - Display confirmation message warning about status reassignment - Optimistically remove archived status from local list on success - Follow existing UI patterns from IssueDetailPage and CategoriesPage Working as Legolas (Frontend Developer) Closes #123 Depends on #121 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ge bunit tests AuthorizeView in CategoriesPage and StatusesPage requires IAuthorizationPolicyProvider. Add _ctx.AddAuthorization() to test constructors so row templates render correctly when the grid has data. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
e6f43d9 to
d69bd95
Compare
🏗️ PR Added to Squad Triage QueueThis PR has been labeled with Next steps:
|
Summary
Adds admin-only Archive button with confirmation dialog to CategoriesPage.
Changes
ArchiveAsyncmethod toICategoryApiClientinterfaceArchiveAsyncinCategoryApiClient(DELETE /api/v1/categories/{id})ConfirmDialogfor archive confirmationTesting
Tested locally (API endpoint may return 404 until PR #134 for #120 is merged).
Working as Legolas (Frontend Developer)
Closes #124
Depends on #120