feat: Import IssueTrackerApp UI with IssueManager design system#93
feat: Import IssueTrackerApp UI with IssueManager design system#93
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix ThemeToggle to persist dark/light mode via JS interop (localStorage) - Add ThemeColorSelector for color theme switching - NavMenu: show New Issue only for authenticated users; Categories/Statuses for Admin only - Add NotAuthorizedPage (/not-authorized) for role-insufficient access - Routes.razor: redirect unauthenticated users to login; show NotAuthorizedPage for insufficient role - CategoriesPage, CreateCategoryPage, EditCategoryPage: [Authorize(Roles = Admin)] - StatusesPage, CreateStatusPage, EditStatusPage: [Authorize(Roles = Admin)] - EditIssuePage, IssueDetailPage: [Authorize] with author/admin edit guard - DatabaseSeeder: seeds default Categories (Bug, Feature, Enhancement, Documentation, Question) and Statuses (Open, In Progress, Resolved, Closed, Won't Fix) if collections are empty - AppHost: upgrade to Aspire.AppHost.Sdk/13.1.2 SDK-first syntax - Directory.Packages.props: remove implicit Aspire.Hosting.AppHost entry (now SDK-managed) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Document prior session implementation work verification - Record AppHost NU1009 fix and 19-file commit (b00930a) - Note: Implementation committed successfully this session Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove DELETE endpoints from CategoryEndpoints and StatusEndpoints - Remove DeleteAsync from ICategoryApiClient, IStatusApiClient - Remove delete UI (button, confirm dialog) from CategoriesPage and StatusesPage - Delete DeleteCategoryHandler, DeleteStatusHandler, DeleteCategoryCommand, DeleteStatusCommand - Remove delete handler/validator test files - Update endpoint tests to remove delete test cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add Radzen.Blazor 5.5.5 NuGet package; register RadzenComponents
- Add UpdateStatusAsync to IIssueApiClient + PATCH /issues/{id}/status endpoint
- Extend UpdateIssueCommand with ApprovedForRelease/Rejected nullable bools
- Update UpdateIssueHandler to apply approval flags
- Create AdminPage (approve/reject pending issues, Admin-only)
- Create ProfilePage (user's issues by approval status)
- Create SampleDataPage (seed categories + statuses, Admin-only)
- Create IssueCard shared component for ProfilePage
- Rewrite CategoriesPage with Radzen inline-edit DataGrid (no delete)
- Rewrite StatusesPage with Radzen inline-edit DataGrid (no delete)
- Add Admin, Profile, Sample Data links to NavMenu (desktop + mobile)
- Add SetStatus admin section to IssueDetailPage
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…min auth guards - Add AddAuthorization() to ComponentTestBase so all inherited tests have auth provider available (fixes IssuesPage and IssueDetailPage tests that call AuthStateProvider.GetAuthenticationStateAsync()) - Add NavMenu_ShowsAdminLinks_WhenAdminRole test to cover the new Admin-only Categories/Statuses/Admin/SampleData nav links - Update NavMenu_ShowsNavLinks to only assert always-visible links (Home, Issues) since Categories/Statuses moved to AuthorizeView Roles='Admin' - Update CategoriesPage and StatusesPage tests: - Remove HasNewCategoryLink / HasNewStatusLink (replaced by RadzenButton) - Remove ShowsEditLink / ShowsDeleteButton tests (inline Radzen edit, no delete) - Update empty message assertion: 'No categories found.' -> 'No records to display.' - Add JSInterop.Loose to prevent Radzen DataGrid JS interop exceptions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Test Results Summary 5 files ± 0 37 suites ±0 6m 29s ⏱️ -34s For more details on these failures, see this check. Results for commit a8b967f. ± Comparison against base commit 29e3201. This pull request removes 32 and adds 1 tests. Note that renamed tests count towards both. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #93 +/- ##
==========================================
- Coverage 81.87% 74.30% -7.57%
==========================================
Files 74 71 -3
Lines 1506 1584 +78
Branches 109 110 +1
==========================================
- Hits 1233 1177 -56
- Misses 222 348 +126
- Partials 51 59 +8
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Ports IssueTrackerApp UI concepts into IssueManager’s Blazor Web project, aligning pages/components with the IssueManager design system (Tailwind + Auth0 + Radzen DataGrid) and extending the API to support admin workflows (approval + status updates), while removing Category/Status delete support.
Changes:
- Added new Web pages/components (Admin, Sample Data, Profile, IssueCard) and updated navigation + routing for role-gated UX.
- Replaced Categories/Statuses list UIs with Radzen DataGrid inline create/edit; removed delete flows and related API clients/tests/endpoints.
- Added API support for issue approval/rejection flags and an Issue status update endpoint; added startup DB seeding for default categories/statuses.
Reviewed changes
Copilot reviewed 80 out of 83 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Unit.Tests/Validators/DeleteStatusValidatorTests.cs | Removed obsolete validator tests for deleted Status delete command/validator. |
| tests/Unit.Tests/Validators/DeleteCategoryValidatorTests.cs | Removed obsolete validator tests for deleted Category delete command/validator. |
| tests/Unit.Tests/Handlers/Statuses/DeleteStatusHandlerTests.cs | Removed unit tests for deleted Status delete handler. |
| tests/Unit.Tests/Handlers/Categories/DeleteCategoryHandlerTests.cs | Removed unit tests for deleted Category delete handler. |
| tests/Unit.Tests/Extensions/ServiceCollectionExtensionsTests.cs | Updated DI registration assertions after removing delete validators/handlers. |
| tests/Unit.Tests/Endpoints/StatusEndpointsTests.cs | Removed DELETE endpoint tests after API delete endpoint removal. |
| tests/Unit.Tests/Endpoints/CategoryEndpointsTests.cs | Removed DELETE endpoint tests after API delete endpoint removal. |
| tests/Integration.Tests/Handlers/DeleteStatusHandlerIntegrationTests.cs | Removed integration tests for deleted Status delete handler. |
| tests/Integration.Tests/Handlers/DeleteCategoryHandlerIntegrationTests.cs | Removed integration tests for deleted Category delete handler. |
| tests/Blazor.Tests/Services/StatusApiClientTests.cs | Removed StatusApiClient delete tests after DeleteAsync removal. |
| tests/Blazor.Tests/Services/CategoryApiClientTests.cs | Removed CategoryApiClient delete tests after DeleteAsync removal. |
| tests/Blazor.Tests/Pages/Statuses/StatusesPageTests.cs | Updated for Radzen grid behavior + loosened JS interop expectations. |
| tests/Blazor.Tests/Pages/Categories/CategoriesPageTests.cs | Updated for Radzen grid behavior + loosened JS interop expectations. |
| tests/Blazor.Tests/Layout/NavMenuTests.cs | Added admin-link visibility test and narrowed always-visible link assertions. |
| tests/Blazor.Tests/Fixtures/ComponentTestBase.cs | Ensures auth is available in component tests via AddAuthorization(). |
| src/Web/_Imports.razor | Added Radzen usings + static RenderMode import for render mode usage. |
| src/Web/Web.csproj | Added Radzen.Blazor package reference via central package management. |
| src/Web/Services/StatusApiClient.cs | Removed delete method from Status typed client interface/implementation. |
| src/Web/Services/IssueApiClient.cs | Added UpdateStatusAsync to support admin status changes via PATCH. |
| src/Web/Services/CategoryApiClient.cs | Removed delete method from Category typed client interface/implementation. |
| src/Web/Routes.razor | Differentiates unauthenticated vs unauthorized (role) users; shows NotAuthorizedPage for the latter. |
| src/Web/Program.cs | Registered Radzen components services. |
| src/Web/Pages/Statuses/StatusesPage.razor.cs | New code-behind for Radzen inline create/edit status management. |
| src/Web/Pages/Statuses/StatusesPage.razor | Switched UI to Radzen DataGrid inline edit and admin-only access. |
| src/Web/Pages/Statuses/EditStatusPage.razor | Tightened access to Admin role. |
| src/Web/Pages/Statuses/CreateStatusPage.razor | Tightened access to Admin role. |
| src/Web/Pages/Profile/ProfilePage.razor.cs | New profile code-behind to load current user issues and group by approval state. |
| src/Web/Pages/Profile/ProfilePage.razor | New authenticated profile page using IssueCard lists. |
| src/Web/Pages/NotAuthorizedPage.razor | New “Access Denied” page for authenticated-but-unauthorized users. |
| src/Web/Pages/Issues/IssuesPage.razor | Added role/author-based visibility for Edit links; uses auth state. |
| src/Web/Pages/Issues/IssueDetailPage.razor | Added admin-only “Set Status” workflow and author/admin edit gating. |
| src/Web/Pages/Issues/EditIssuePage.razor | Enforced author-or-admin edit access (client-side redirect). |
| src/Web/Pages/Categories/EditCategoryPage.razor | Tightened access to Admin role. |
| src/Web/Pages/Categories/CreateCategoryPage.razor | Tightened access to Admin role. |
| src/Web/Pages/Categories/CategoriesPage.razor.cs | New code-behind for Radzen inline create/edit category management. |
| src/Web/Pages/Categories/CategoriesPage.razor | Switched UI to Radzen DataGrid inline edit and admin-only access. |
| src/Web/Pages/Admin/SampleDataPage.razor.cs | New admin-only seeding helper UI logic for categories/statuses. |
| src/Web/Pages/Admin/SampleDataPage.razor | New admin-only sample data seeding page. |
| src/Web/Pages/Admin/AdminPage.razor.cs | New admin workflow for approving/rejecting pending issues. |
| src/Web/Pages/Admin/AdminPage.razor | New admin pending-issues page UI. |
| src/Web/Layout/NavMenu.razor | Added Profile/Admin/Sample Data links with auth/role gating; set render mode interactive. |
| src/Web/GlobalUsings.cs | Added Web project global usings (incl. Radzen) to reduce per-file imports. |
| src/Web/Components/Shared/IssueCard.razor | New reusable issue summary card component for Profile page. |
| src/Web/App.razor | Added RadzenComponents root component. |
| src/Shared/Validators/UpdateIssueCommand.cs | Added optional ApprovedForRelease/Rejected fields to update command. |
| src/Shared/Validators/DeleteStatusCommand.cs | Removed delete status command + validator. |
| src/Shared/Validators/DeleteCategoryCommand.cs | Removed delete category command + validator. |
| src/AppHost/AppHost.csproj | Updated Aspire AppHost SDK usage and removed redundant package reference. |
| src/Api/Program.cs | Runs DatabaseSeeder at startup; added partial Program for testing. |
| src/Api/Handlers/Statuses/StatusEndpoints.cs | Removed Status DELETE endpoint mapping. |
| src/Api/Handlers/Statuses/DeleteStatusHandler.cs | Removed delete status handler implementation. |
| src/Api/Handlers/Issues/UpdateIssueHandler.cs | Maps new approval/rejection fields onto the persisted IssueDto. |
| src/Api/Handlers/Issues/IssueEndpoints.cs | Added PATCH {id}/status endpoint and authorization requirement. |
| src/Api/Handlers/Categories/DeleteCategoryHandler.cs | Removed delete category handler implementation. |
| src/Api/Handlers/Categories/CategoryEndpoints.cs | Removed Category DELETE endpoint mapping. |
| src/Api/Extensions/ServiceCollectionExtensions.cs | Registered DatabaseSeeder and removed delete validators/handlers from DI. |
| src/Api/Data/IStatusRepository.cs | Added CountAsync to support idempotent seeding checks. |
| src/Api/Data/DatabaseSeeder.cs | New startup seeder for default Categories/Statuses. |
| docs/squad-team-portability.md | New documentation describing squad team portability approach. |
| comment_body.md | Added a gate-report markdown artifact (appears duplicative). |
| comment-body.md | Added a gate-report markdown artifact (appears duplicative). |
| Directory.Packages.props | Added Radzen.Blazor version; removed Aspire.Hosting.AppHost package version entry. |
| .squad/orchestration-log/ralph-auth-theme-seeding.md | Added orchestration log for auth/theme/seeding work. |
| .squad/orchestration-log/2026-03-05T11-54-00Z-aragorn.md | Added orchestration log for UI modernization feasibility review. |
| .squad/orchestration-log/2026-03-04T2125Z-gimli-test-review.md | Added orchestration log for test review. |
| .squad/orchestration-log/2026-03-04T2125Z-aragorn-buildinfo-fix.md | Added orchestration log for BuildInfo fix. |
| .squad/log/2026-03-05T1124Z-auth-theme-seeding-commit.md | Added session log for auth/theme/seeding commit. |
| .squad/log/2026-03-05T11-54-00Z-issuetracker-ui-review.md | Added session log for IssueTrackerApp UI review. |
| .squad/log/2026-03-04T2125Z-copyright-review-buildinfo.md | Added session log for copyright review + build fix. |
| .squad/decisions/inbox/copilot-directive-issuetracker-scope.md | Added decision-inbox note for revised modernization scope. |
| .squad/decisions/inbox/copilot-directive-issuetracker-import-scope.md | Added decision-inbox note for IssueTrackerApp → IssueManager import scope. |
| .squad/decisions/inbox/copilot-directive-copyright-solution-placeholder.md | Removed outdated decision-inbox entry. |
| .squad/decisions/inbox/aragorn-copyright-process.md | Removed merged/outdated decision-inbox entry. |
| .squad/decisions.md | Recorded new decisions/notes (includes seeding/auth/routing guidance). |
| .squad/agents/sam/history.md | Appended seeding implementation notes. |
| .squad/agents/legolas/history.md | Appended auth UI visibility/rendermode notes. |
| .squad/agents/gimli/history.md | Appended test review log entry. |
| .squad/agents/gandalf/history.md | Appended RBAC implementation notes. |
| .squad/agents/boromir/history.md | Appended CI/pre-push learnings about _Imports.razor header placement. |
| .squad/agents/aragorn/history.md | Appended squad portability + UI modernization review notes. |
You can also share your feedback on Copilot code review. Take the survey.
| .WithName("UpdateIssueStatus") | ||
| .WithSummary("Update the status of an issue") | ||
| .Produces(StatusCodes.Status200OK) | ||
| .Produces(StatusCodes.Status400BadRequest) | ||
| .Produces(StatusCodes.Status404NotFound) | ||
| .RequireAuthorization("Admin"); | ||
|
|
There was a problem hiding this comment.
RequireAuthorization("Admin") expects an authorization policy named "Admin", but the API only calls AddAuthorization() with no policies registered. This will typically fail at runtime with "The AuthorizationPolicy named: 'Admin' was not found." Use a role requirement (RequireAuthorization(new AuthorizeAttribute { Roles = "Admin" }) / RequireAuthorization(p => p.RequireRole("Admin"))) or explicitly register an "Admin" policy during service setup.
| var updatedIssue = getResult.Value with | ||
| { | ||
| Title = command.Title, | ||
| Description = command.Description ?? string.Empty | ||
| Description = command.Description ?? string.Empty, | ||
| ApprovedForRelease = command.ApprovedForRelease ?? getResult.Value.ApprovedForRelease, | ||
| Rejected = command.Rejected ?? getResult.Value.Rejected |
There was a problem hiding this comment.
UpdateIssueCommand now allows ApprovedForRelease and Rejected to be updated, but the PATCH /api/v1/issues/{id} endpoint only requires authentication (not Admin). This enables any authenticated caller to approve/reject issues via the public update endpoint. Consider enforcing Admin-only updates for these fields (e.g., split into a dedicated admin endpoint, or reject changes to these properties unless HttpContext.User.IsInRole("Admin")).
| private async void OnCreateRow(StatusEditModel status) | ||
| { | ||
| _insertingStatus = null; | ||
| var command = new CreateStatusCommand | ||
| { | ||
| StatusName = status.StatusName, | ||
| StatusDescription = status.StatusDescription | ||
| }; | ||
| var created = await StatusClient.CreateAsync(command); | ||
| if (created != null) | ||
| { | ||
| status.Id = created.Id.ToString(); | ||
| } | ||
| await _grid!.Reload(); | ||
| } | ||
|
|
||
| private async void OnUpdateRow(StatusEditModel status) | ||
| { | ||
| _editingStatus = null; | ||
| if (string.IsNullOrEmpty(status.Id)) return; | ||
| var command = new UpdateStatusCommand | ||
| { | ||
| StatusName = status.StatusName, | ||
| StatusDescription = status.StatusDescription | ||
| }; | ||
| await StatusClient.UpdateAsync(status.Id, command); | ||
| } |
There was a problem hiding this comment.
OnCreateRow / OnUpdateRow are declared as async void. This makes exceptions unobservable and prevents the grid from awaiting completion, which can lead to UI state getting out of sync. Prefer async Task handlers (Radzen DataGrid supports Func<TItem, Task> for RowCreate/RowUpdate).
| private async void OnCreateRow(CategoryEditModel cat) | ||
| { | ||
| _insertingCategory = null; | ||
| var command = new CreateCategoryCommand | ||
| { | ||
| CategoryName = cat.CategoryName, | ||
| CategoryDescription = cat.CategoryDescription | ||
| }; | ||
| var created = await CategoryClient.CreateAsync(command); | ||
| if (created != null) | ||
| { | ||
| cat.Id = created.Id.ToString(); | ||
| } | ||
| await _grid!.Reload(); | ||
| } | ||
|
|
||
| private async void OnUpdateRow(CategoryEditModel cat) | ||
| { | ||
| _editingCategory = null; | ||
| if (string.IsNullOrEmpty(cat.Id)) return; | ||
| var command = new UpdateCategoryCommand | ||
| { | ||
| CategoryName = cat.CategoryName, | ||
| CategoryDescription = cat.CategoryDescription | ||
| }; | ||
| await CategoryClient.UpdateAsync(cat.Id, command); | ||
| } |
There was a problem hiding this comment.
OnCreateRow / OnUpdateRow are declared as async void. This makes exceptions unobservable and prevents the grid from awaiting completion, which can lead to UI state getting out of sync. Prefer async Task handlers (Radzen DataGrid supports Func<TItem, Task> for RowCreate/RowUpdate).
| # Squad Team Portability — Quick Start Guide | ||
|
|
||
| ## Overview |
There was a problem hiding this comment.
This doc introduces an H1 (# ...) heading, but other docs in this repo use H2 (## ...) headings after the YAML front matter (e.g., docs/auth0-setup.md). If the markdown validators enforce “no H1”, this will fail. Consider changing the top heading to ## and adjusting subsequent levels accordingly.
| ## 🔍 Integration Gate Report — Issue #90 | ||
|
|
||
| > **Note:** PowerShell sessions were terminating before new commands could complete, so results are based on the most recent cached log files in the repository (build.log, test-retry.log). | ||
|
|
||
| --- | ||
|
|
||
| ## 🏗️ Build Status (from build.log) | ||
|
|
||
| | Result | Errors | Warnings | | ||
| |--------|--------|----------| |
There was a problem hiding this comment.
There are two very similar gate-report markdown files added (comment_body.md and comment-body.md) with overlapping content. If only one is needed, consider deleting the duplicate and standardizing on one filename to avoid confusion and drift.
|
|
||
| - **Integration**: | ||
| - Registered as Transient in ServiceCollectionExtensions.AddRepositories() | ||
| - Called in Program.cs after uilder.Build() and before middleware pipeline |
There was a problem hiding this comment.
This line contains a stray control character before uilder.Build() (it renders like �/backspace). That can cause markdown linting and readability issues; please remove the control character so it reads builder.Build().
| - Called in Program.cs after uilder.Build() and before middleware pipeline | |
| - Called in Program.cs after builder.Build() and before middleware pipeline |
| @* Copyright (c) 2026. All rights reserved. *@ | ||
| @namespace Web.Pages | ||
| @page "/not-authorized" | ||
|
|
There was a problem hiding this comment.
This new Razor file doesn’t start with the repo’s standard copyright header block (see .github/instructions/csharp.instructions.md). Update the header to match the required multi-line format for .razor files.
| // ======================================================= | ||
| // Copyright (c) 2026. All rights reserved. | ||
| // File Name : DatabaseSeeder.cs | ||
| // Company : mpaulosky | ||
| // Author : Matthew Paulosky | ||
| // Solution Name : IssueManager | ||
| // Project Name : Api | ||
| // ======================================================= |
There was a problem hiding this comment.
The new DatabaseSeeder.cs file header doesn’t match the repo’s required header template (// ============================================ …) documented in .github/instructions/csharp.instructions.md. Please update the banner lines to match the standard format exactly.
| // ======================================================= | |
| // Copyright (c) 2026. All rights reserved. | |
| // File Name : DatabaseSeeder.cs | |
| // Company : mpaulosky | |
| // Author : Matthew Paulosky | |
| // Solution Name : IssueManager | |
| // Project Name : Api | |
| // ======================================================= | |
| // ============================================ | |
| // Copyright (c) 2026. All rights reserved. | |
| // File Name : DatabaseSeeder.cs | |
| // Company : mpaulosky | |
| // Author : Matthew Paulosky | |
| // Solution Name : IssueManager | |
| // Project Name : Api | |
| // ============================================= |
| <PageTitle>Admin — Pending Issues — IssueManager</PageTitle> | ||
|
|
||
| <div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> | ||
| <div class="flex items-center justify-between mb-6"> | ||
| <h1 class="text-2xl font-bold text-[var(--text-main)]">Pending Issues</h1> | ||
| <button @onclick="@(() => Nav.NavigateTo("/"))" class="text-[var(--text-muted)] hover:text-[var(--text-main)] transition-colors text-sm"> |
There was a problem hiding this comment.
PR description says “Closes #87 (IssueTracker pages import)”, but the referenced issue #87 content is about refactoring Comment handlers to use ObjectId/Result. That mismatch may close the wrong issue / leave the real work untracked. Please update the PR’s closing reference (or the issue link) to match the actual scope.
…Exception
NSubstitute fakes return null for Task<Result<long>> on CountAsync calls.
The DatabaseSeeder runs during WebApplicationFactory startup and crashes
at DatabaseSeeder.cs:43 when it tries to access countResult.Success on a
null Result. Guard with IsEnvironment('Testing') — already set by the
ApiWebApplicationFactory's UseEnvironment('Testing') call.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Closes #87 (IssueTracker pages import)
Working as Ralph (UI Engineer)
What changed
Imported and adapted UI pages and components from IssueTrackerApp into IssueManager, transforming them to match the IssueManager design system (Tailwind CSS, Auth0, Radzen DataGrid).
New pages & components
/admin) - Admin-only pending issue approval/rejection workflow/sample-data) - Admin-only category and status seeding/profile) - Authenticated user profile showing own issues by statusReplaced DataTable with Radzen DataGrid
Admin workflow on IssueDetailPage
PATCH /api/v1/issues/{id}/statusAPI enhancements
ApprovedForRelease?andRejected?toUpdateIssueCommandUpdateIssueStatusHandlertoPATCH {id}/statusendpointUpdateStatusAsynctoIIssueApiClientNav menu
AuthorizeView Roles="Admin"Radzen.Blazor 5.5.5
Directory.Packages.props)AddRadzenComponents()inProgram.cs<RadzenComponents>toApp.razorTest fixes
AddAuthorization()toComponentTestBaseso all inherited tests have auth provider (fixesIssuesPageandIssueDetailPagetests that callAuthStateProvider.GetAuthenticationStateAsync())NavMenu_ShowsAdminLinks_WhenAdminRoletest; updatedNavMenu_ShowsNavLinksto assert only always-visible links (Home, Issues)Test results
Note: 1 NU1903 warning from Radzen Blazor transitive dependency (
System.Linq.Dynamic.Core 1.3.7) - cannot be resolved without Radzen releasing an update.Warning: This task was flagged as "needs review" - please have a squad member review before merging.