Skip to content

Commit cf7bfe9

Browse files
Merge pull request #769 from TransactionProcessing/task/#645_product_performance
Product Performance report integration
2 parents 7b99788 + 4d4b047 commit cf7bfe9

13 files changed

Lines changed: 781 additions & 604 deletions

File tree

EstateManagementUI.BlazorServer/Components/Pages/Reporting/ProductPerformance.razor

Lines changed: 18 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
@using EstateManagementUI.BlazorServer.Factories
33
@using EstateManagementUI.BusinessLogic.Requests
44
@rendermode InteractiveServer
5+
@inherits AuthorizedComponentBase
56
@inject IMediator Mediator
67
@inject NavigationManager Navigation
78
@inject ILogger<ProductPerformance> Logger
@@ -67,9 +68,9 @@
6768
<div class="flex items-end">
6869
<button @onclick="LoadData" class="btn btn-primary">
6970
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
70-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
71+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path>
7172
</svg>
72-
Refresh
73+
Apply Filters
7374
</button>
7475
</div>
7576
</div>
@@ -86,7 +87,7 @@
8687
</div>
8788
<div class="info-box-content">
8889
<span class="info-box-text">Total Products</span>
89-
<span class="info-box-number">@totalProducts</span>
90+
<span class="info-box-number">@performanceData.Summary.TotalProducts</span>
9091
</div>
9192
</div>
9293

@@ -98,7 +99,7 @@
9899
</div>
99100
<div class="info-box-content">
100101
<span class="info-box-text">Total Transactions</span>
101-
<span class="info-box-number">@totalTransactions.ToString("N0")</span>
102+
<span class="info-box-number">@performanceData.Summary.TotalCount.ToString("N0")</span>
102103
</div>
103104
</div>
104105

@@ -110,7 +111,7 @@
110111
</div>
111112
<div class="info-box-content">
112113
<span class="info-box-text">Total Value</span>
113-
<span class="info-box-number">@totalValue.ToString("C")</span>
114+
<span class="info-box-number">@performanceData.Summary.TotalValue.ToString("C")</span>
114115
</div>
115116
</div>
116117

@@ -122,7 +123,7 @@
122123
</div>
123124
<div class="info-box-content">
124125
<span class="info-box-text">Average per Product</span>
125-
<span class="info-box-number">@averageValuePerProduct.ToString("C")</span>
126+
<span class="info-box-number">@performanceData.Summary.AveragePerProduct.ToString("C")</span>
126127
</div>
127128
</div>
128129
</div>
@@ -149,7 +150,7 @@
149150
</div>
150151
</div>
151152
<div class="card-body">
152-
@if (performanceData != null && performanceData.Any())
153+
@if (performanceData != null && performanceData.ProductDetails.Any())
153154
{
154155
@if (!showChart)
155156
{
@@ -166,33 +167,33 @@
166167
</tr>
167168
</thead>
168169
<tbody>
169-
@foreach (var item in performanceData)
170+
@foreach (var item in performanceData.ProductDetails)
170171
{
171172
<tr>
172173
<td class="font-medium">@item.ProductName</td>
173174
<td class="text-right">@item.TransactionCount.ToString("N0")</td>
174175
<td class="text-right">@item.TransactionValue.ToString("C")</td>
175176
<td class="text-right">
176-
<span class="font-medium text-admin-primary">@item.PercentageContribution.ToString("F2")%</span>
177+
<span class="font-medium text-admin-primary">@item.PercentageOfTotal.ToString("F2")%</span>
177178
</td>
178179
<td>
179180
<div class="w-full bg-gray-200 rounded-full h-2.5">
180181
<div class="bg-admin-primary h-2.5 rounded-full"
181-
style="width: @(item.PercentageContribution)%"></div>
182+
style="width: @(item.PercentageOfTotal)%"></div>
182183
</div>
183184
</td>
184185
</tr>
185186
}
186187
</tbody>
187-
<tfoot>
188+
@* <tfoot>
188189
<tr class="font-bold bg-gray-50">
189190
<td>Total</td>
190191
<td class="text-right">@totalTransactions.ToString("N0")</td>
191192
<td class="text-right">@totalValue.ToString("C")</td>
192193
<td class="text-right">@totalPercentage.ToString("F2")%</td>
193194
<td></td>
194195
</tr>
195-
</tfoot>
196+
</tfoot> *@
196197
</table>
197198
</div>
198199
}
@@ -201,19 +202,19 @@
201202
<!-- Chart View -->
202203
<div class="space-y-4">
203204
<h4 class="font-semibold text-lg mb-4">Transaction Value by Product</h4>
204-
@foreach (var item in performanceData)
205+
@foreach (var item in performanceData.ProductDetails)
205206
{
206207
<div class="mb-4">
207208
<div class="flex justify-between mb-1">
208209
<span class="text-sm font-medium text-gray-700">@item.ProductName</span>
209210
<div class="text-sm text-gray-600">
210211
<span class="font-semibold">@item.TransactionValue.ToString("C")</span>
211-
<span class="text-admin-primary ml-2">(@item.PercentageContribution.ToString("F2")%)</span>
212+
<span class="text-admin-primary ml-2">(@item.PercentageOfTotal.ToString("F2")%)</span>
212213
</div>
213214
</div>
214215
<div class="w-full bg-gray-200 rounded-full h-8">
215216
<div class="bg-gradient-to-r from-admin-primary to-admin-secondary h-8 rounded-full flex items-center justify-end pr-3"
216-
style="width: @(item.PercentageContribution)%">
217+
style="width: @(item.PercentageOfTotal)%">
217218
<span class="text-white text-xs font-bold">@item.TransactionCount.ToString("N0")</span>
218219
</div>
219220
</div>
@@ -223,7 +224,7 @@
223224
}
224225

225226
<!-- Validation Message -->
226-
@if (Math.Abs(totalPercentage - 100) < 0.01m)
227+
@if (Math.Abs(performanceData.ProductDetails.Sum(p=> p.PercentageOfTotal) - 100) < 0.01m)
227228
{
228229
<div class="mt-4 p-3 bg-green-50 border border-green-200 rounded-lg">
229230
<div class="flex items-center text-green-700">
@@ -241,7 +242,7 @@
241242
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
242243
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
243244
</svg>
244-
<span class="text-sm font-medium">Warning: Percentages sum to @totalPercentage.ToString("F2")%</span>
245+
<span class="text-sm font-medium">Warning: Percentages sum to @performanceData.ProductDetails.Sum(p => p.PercentageOfTotal).ToString("F2")%</span>
245246
</div>
246247
</div>
247248
}
@@ -260,92 +261,3 @@
260261
}
261262
</div>
262263

263-
@code {
264-
private bool isLoading = true;
265-
private string? errorMessage;
266-
private bool showChart = false;
267-
268-
// Filter states
269-
private DateOnly _startDate = DateOnly.FromDateTime(DateTime.Now.AddDays(-30));
270-
private DateOnly _endDate = DateOnly.FromDateTime(DateTime.Now);
271-
272-
// Data
273-
private List<ProductPerformanceModel>? performanceData;
274-
275-
// KPIs
276-
private int totalProducts = 0;
277-
private int totalTransactions = 0;
278-
private decimal totalValue = 0;
279-
private decimal averageValuePerProduct = 0;
280-
private decimal totalPercentage = 0;
281-
282-
protected override async Task OnInitializedAsync()
283-
{
284-
await LoadData();
285-
}
286-
287-
private async Task LoadData()
288-
{
289-
try
290-
{
291-
isLoading = true;
292-
errorMessage = null;
293-
StateHasChanged();
294-
295-
var correlationId = new CorrelationId(Guid.NewGuid());
296-
var estateId = Guid.Parse("11111111-1111-1111-1111-111111111111");
297-
var accessToken = "stubbed-token";
298-
299-
var startDate = _startDate.ToDateTime(TimeOnly.MinValue);
300-
var endDate = _endDate.ToDateTime(TimeOnly.MaxValue);
301-
302-
var result = await Mediator.Send(new Queries.GetProductPerformanceQuery(
303-
correlationId,
304-
accessToken,
305-
estateId,
306-
startDate,
307-
endDate
308-
));
309-
310-
if (result.IsSuccess && result.Data != null)
311-
{
312-
performanceData = ModelFactory.ConvertFrom(result.Data);
313-
CalculateKPIs();
314-
}
315-
else
316-
{
317-
errorMessage = result.Message ?? "Failed to load product performance data";
318-
}
319-
}
320-
catch (Exception ex)
321-
{
322-
errorMessage = $"Failed to load data: {ex.Message}";
323-
Logger.LogError(ex, "Error loading product performance data");
324-
}
325-
finally
326-
{
327-
isLoading = false;
328-
StateHasChanged();
329-
}
330-
}
331-
332-
private void CalculateKPIs()
333-
{
334-
if (performanceData == null || !performanceData.Any())
335-
{
336-
totalProducts = 0;
337-
totalTransactions = 0;
338-
totalValue = 0;
339-
averageValuePerProduct = 0;
340-
totalPercentage = 0;
341-
return;
342-
}
343-
344-
totalProducts = performanceData.Count;
345-
totalTransactions = performanceData.Sum(p => p.TransactionCount);
346-
totalValue = performanceData.Sum(p => p.TransactionValue);
347-
averageValuePerProduct = totalProducts > 0 ? totalValue / totalProducts : 0;
348-
totalPercentage = performanceData.Sum(p => p.PercentageContribution);
349-
}
350-
}
351-
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using EstateManagementUI.BlazorServer.Factories;
2+
using EstateManagementUI.BlazorServer.Models;
3+
using EstateManagementUI.BlazorServer.Permissions;
4+
using EstateManagementUI.BusinessLogic.Requests;
5+
using SimpleResults;
6+
7+
namespace EstateManagementUI.BlazorServer.Components.Pages.Reporting
8+
{
9+
public partial class ProductPerformance
10+
{
11+
private bool isLoading = true;
12+
private bool showChart = false;
13+
14+
// Filter states
15+
//private DateOnly _startDate = DateOnly.FromDateTime(DateTime.Now.AddDays(-7));
16+
private DateOnly _startDate = DateOnly.FromDateTime(new DateTime(2025, 12, 10));
17+
private DateOnly _endDate = DateOnly.FromDateTime(DateTime.Now);
18+
19+
// Data
20+
private TransactionModels.ProductPerformanceResponse? performanceData;
21+
22+
protected override async Task OnAfterRenderAsync(bool firstRender)
23+
{
24+
if (!firstRender)
25+
{
26+
return;
27+
}
28+
29+
Result result = await OnAfterRender(PermissionSection.Reporting, PermissionFunction.ProductPerformanceReport, this.LoadData);
30+
if (result.IsFailed)
31+
{
32+
return;
33+
}
34+
}
35+
36+
private async Task<Result> LoadData()
37+
{
38+
try
39+
{
40+
isLoading = true;
41+
errorMessage = null;
42+
StateHasChanged();
43+
44+
var correlationId = new CorrelationId(Guid.NewGuid());
45+
var estateId = await this.GetEstateId();
46+
47+
var startDate = _startDate.ToDateTime(TimeOnly.MinValue);
48+
var endDate = _endDate.ToDateTime(TimeOnly.MaxValue);
49+
50+
var result = await Mediator.Send(new TransactionQueries.GetProductPerformanceQuery(
51+
correlationId,
52+
estateId,
53+
startDate,
54+
endDate
55+
));
56+
57+
if (result.IsSuccess && result.Data != null)
58+
{
59+
performanceData = ModelFactory.ConvertFrom(result.Data);
60+
}
61+
else
62+
{
63+
errorMessage = result.Message ?? "Failed to load product performance data";
64+
}
65+
66+
return Result.Success();
67+
}
68+
catch (Exception ex)
69+
{
70+
errorMessage = $"Failed to load product performance data: {ex.Message}";
71+
return Result.Failure(errorMessage);
72+
}
73+
finally
74+
{
75+
isLoading = false;
76+
StateHasChanged();
77+
}
78+
}
79+
}
80+
}

EstateManagementUI.BlazorServer/Components/Pages/Reporting/TransactionSummaryOperator.razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
2828
return;
2929
}
3030

31-
Result result = await OnAfterRender(PermissionSection.Reporting, PermissionFunction.TransactionMerchantSummaryReport, this.LoadData);
31+
Result result = await OnAfterRender(PermissionSection.Reporting, PermissionFunction.TransactionOperatorSummaryReport, this.LoadData);
3232
if (result.IsFailed)
3333
{
3434
return;

0 commit comments

Comments
 (0)