Skip to content

Commit 52dd1b0

Browse files
authored
Refactor report function, improve auth handling in UI (MWG-Logan#36)
- Renamed and relocated Azure Function for project completion reports (ProjectCompletionReportFunction → ReportFunction) for better organization. - Broadened "active" project filter to include "In Progress" status in ProjectReportingService. - Enhanced Blazor UI to detect 401 Unauthorized responses and show a login prompt with sign-in button. - Updated staticwebapp.config.json to require authentication for all routes (/*) instead of just /api/*. - Minor UI formatting improvements to data grids.
1 parent f092c5d commit 52dd1b0

File tree

4 files changed

+63
-19
lines changed

4 files changed

+63
-19
lines changed

Bezalu.ProjectReporting.API/Functions/ProjectCompletionReportFunction.cs renamed to Bezalu.ProjectReporting.API/Functions/ReportFunction.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
namespace Bezalu.ProjectReporting.API.Functions;
1212

13-
public class ProjectCompletionReportFunction(
14-
ILogger<ProjectCompletionReportFunction> logger,
13+
public class ReportFunction(
14+
ILogger<ReportFunction> logger,
1515
IProjectReportingService reportingService)
1616
{
1717
[Function("GenerateProjectCompletionReport")]

Bezalu.ProjectReporting.API/Services/ProjectReportingService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public async Task<List<ProjectListItem>> GetActiveProjectsAsync(CancellationToke
2323

2424
// Query ConnectWise for projects with "Active" status
2525
var projects = await connectWiseClient.GetListAsync<CWProject>(
26-
"project/projects?conditions=status/name='Active'&orderBy=name",
26+
"project/projects?conditions=status/name contains 'In Progress'&orderBy=name",
2727
cancellationToken) ?? new List<CWProject>();
2828

2929
return projects

Bezalu.ProjectReporting.Web/Pages/Home.razor

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
@page "/"
2+
@using System.Net
23
@inject HttpClient Http
34
@inject NavigationManager Nav
45
@inject IJSRuntime JS
56
@using Markdig
67

78
<PageTitle>Project Reporting</PageTitle>
89

10+
@if (ShowLoginPrompt)
11+
{
12+
<FluentCard Style="padding:0.5rem 1rem; margin-bottom:1rem;">
13+
<FluentStack Orientation="Orientation.Horizontal" Gap="10" VerticalAlignment="VerticalAlignment.Center">
14+
<FluentIcon Value="@(new Icons.Regular.Size20.LockClosed())" />
15+
<FluentStack>
16+
<strong>@LoginPromptText</strong>
17+
<span>Sign in again to continue.</span>
18+
</FluentStack>
19+
<FluentButton Appearance="Appearance.Accent" OnClick="TriggerLogin">Sign in</FluentButton>
20+
</FluentStack>
21+
</FluentCard>
22+
}
23+
924
@if (Report is null)
1025
{
1126
<FluentCard Style="padding:1rem; margin-bottom:1rem;">
1227
<h2>Select a Project</h2>
1328
@if (IsLoadingProjects)
1429
{
15-
<FluentProgressRing/>
30+
<FluentProgressRing />
1631
<p>Loading projects...</p>
1732
}
1833
else if (!string.IsNullOrEmpty(ErrorMessage))
@@ -31,8 +46,8 @@
3146
<PropertyColumn Property="@(p => p.Manager)" Title="Manager" />
3247
<PropertyColumn Property="@(p => p.Company)" Title="Company" />
3348
<TemplateColumn Title="Actions">
34-
<FluentButton Appearance="Appearance.Accent"
35-
OnClick="@(() => GenerateReport(context.ProjectId))"
49+
<FluentButton Appearance="Appearance.Accent"
50+
OnClick="@(() => GenerateReport(context.ProjectId))"
3651
Disabled="IsGeneratingReport">
3752
@(IsGeneratingReport && GeneratingProjectId == context.ProjectId ? "Generating..." : "Generate Report")
3853
</FluentButton>
@@ -82,10 +97,10 @@ else
8297
else
8398
{
8499
<FluentDataGrid Items="@Report.Phases.AsQueryable()" GenerateFooter="false">
85-
<PropertyColumn Property="@(p => p.PhaseName)" Title="Name"/>
86-
<PropertyColumn Property="@(p => p.Status)" Title="Status"/>
87-
<PropertyColumn Property="@(p => p.EstimatedHours)" Title="Est Hrs"/>
88-
<PropertyColumn Property="@(p => p.ActualHours)" Title="Actual Hrs"/>
100+
<PropertyColumn Property="@(p => p.PhaseName)" Title="Name" />
101+
<PropertyColumn Property="@(p => p.Status)" Title="Status" />
102+
<PropertyColumn Property="@(p => p.EstimatedHours)" Title="Est Hrs" />
103+
<PropertyColumn Property="@(p => p.ActualHours)" Title="Actual Hrs" />
89104
</FluentDataGrid>
90105
}
91106
</FluentTab>
@@ -97,11 +112,11 @@ else
97112
else
98113
{
99114
<FluentDataGrid Items="@Report.Tickets.AsQueryable()" GenerateFooter="false">
100-
<PropertyColumn Property="@(t => t.TicketNumber)" Title="Number"/>
101-
<PropertyColumn Property="@(t => t.Summary)" Title="Summary"/>
102-
<PropertyColumn Property="@(t => t.Status)" Title="Status"/>
103-
<PropertyColumn Property="@(t => t.EstimatedHours)" Title="Est Hrs"/>
104-
<PropertyColumn Property="@(t => t.ActualHours)" Title="Actual Hrs"/>
115+
<PropertyColumn Property="@(t => t.TicketNumber)" Title="Number" />
116+
<PropertyColumn Property="@(t => t.Summary)" Title="Summary" />
117+
<PropertyColumn Property="@(t => t.Status)" Title="Status" />
118+
<PropertyColumn Property="@(t => t.EstimatedHours)" Title="Est Hrs" />
119+
<PropertyColumn Property="@(t => t.ActualHours)" Title="Actual Hrs" />
105120
</FluentDataGrid>
106121
}
107122
</FluentTab>
@@ -116,6 +131,8 @@ else
116131
bool IsGeneratingReport;
117132
int GeneratingProjectId;
118133
bool IsPdfLoading;
134+
bool ShowLoginPrompt;
135+
string LoginPromptText = "Please sign in to continue.";
119136
string? ErrorMessage;
120137
string AiSummaryHtml => Report?.AiGeneratedSummary is null ? string.Empty : Markdown.ToHtml(Report.AiGeneratedSummary);
121138

@@ -131,6 +148,11 @@ else
131148
try
132149
{
133150
var response = await Http.GetAsync("api/projects");
151+
if (response.StatusCode == HttpStatusCode.Unauthorized)
152+
{
153+
HandleUnauthorized("Please sign in to load projects.");
154+
return;
155+
}
134156
if (!response.IsSuccessStatusCode)
135157
{
136158
ErrorMessage = $"Failed to load projects: {response.StatusCode}";
@@ -158,6 +180,11 @@ else
158180
{
159181
var req = new ProjectCompletionReportRequest { ProjectId = projectId };
160182
var httpResp = await Http.PostAsJsonAsync("api/reports/project-completion", req);
183+
if (httpResp.StatusCode == HttpStatusCode.Unauthorized)
184+
{
185+
HandleUnauthorized("Please sign in to generate a report.");
186+
return;
187+
}
161188
if (!httpResp.IsSuccessStatusCode)
162189
{
163190
ErrorMessage = $"Failed to generate report: {httpResp.StatusCode}";
@@ -191,6 +218,11 @@ else
191218
try
192219
{
193220
var httpResp = await Http.PostAsJsonAsync("api/reports/project-completion/pdf", Report);
221+
if (httpResp.StatusCode == HttpStatusCode.Unauthorized)
222+
{
223+
HandleUnauthorized("Please sign in to download the PDF.");
224+
return;
225+
}
194226
if (!httpResp.IsSuccessStatusCode)
195227
{
196228
ErrorMessage = $"Failed to generate PDF: {httpResp.StatusCode}";
@@ -210,4 +242,16 @@ else
210242
IsPdfLoading = false;
211243
}
212244
}
245+
246+
void HandleUnauthorized(string message)
247+
{
248+
LoginPromptText = message;
249+
ShowLoginPrompt = true;
250+
ErrorMessage = null;
251+
}
252+
253+
void TriggerLogin()
254+
{
255+
Nav.NavigateTo("/login?post_login_redirect_uri=.referrer", forceLoad: true);
256+
}
213257
}

Bezalu.ProjectReporting.Web/staticwebapp.config.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"routes": [
3-
{
4-
"route": "/api/*",
5-
"allowedRoles": ["authenticated"]
6-
},
73
{
84
"route": "/login",
95
"rewrite": "/.auth/login/aad"
6+
},
7+
{
8+
"route": "/*",
9+
"allowedRoles": ["authenticated"]
1010
}
1111
],
1212
"navigationFallback": {

0 commit comments

Comments
 (0)