Skip to content

Commit

Permalink
Adds unused tickets statistics (#34)
Browse files Browse the repository at this point in the history
* Adds unused tickets statistics

* Add API v2 specs

* Fix setting dates twice on load

* Set full object and not each individual property

* Replace lambda function with method signature

* Remove duplicate null check

* Change wording of status 204
  • Loading branch information
A-Guldborg authored Apr 23, 2024
1 parent 11d9448 commit 7cfd6fa
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 2 deletions.
92 changes: 92 additions & 0 deletions Shifty.App/Components/UnusedTickets.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
@namespace Components
@using System.ComponentModel.DataAnnotations
@using Shifty.App.Services
@using Shifty.Api.Generated.AnalogCoreV1
@using Shifty.Api.Generated.AnalogCoreV2
@using Shifty.App.DomainModels
@using Shifty.App.Shared
@using Shared
@using LanguageExt.UnsafeValueAccess
@inject IUnusedTicketsService _unusedTicketsService
@inject ISnackbar Snackbar

<MudContainer MaxWidth="MaxWidth.Medium" Style="margin-top: 20px;">
<MudDataGrid
T="UnusedTicket"
Items="@Items"
Height="500px"
FixedFooter>
<ToolBarContent>
<MudText Typo="Typo.h6">Unused Tickets</MudText>
<MudSpacer />
@if (_loading)
{
<LoadingIndicator Height="64px" />
}
<MudDateRangePicker DateRange="@_queryDateRange" Editable=true DateRangeChanged="LoadUnusedTickets"/>
</ToolBarContent>
<Columns>
<PropertyColumn Property="x => x.ProductId" Title="Product Id" />
<PropertyColumn Property="x => x.ProductName" Title="Product Name" AggregateDefinition="_footerLabel"/>
<PropertyColumn Property="x => x.TicketsLeft" Title="Unused Tickets" AggregateDefinition="_ticketsLeftSum" />
<PropertyColumn Property="x => x.UnusedPurchasesValue" Title="Unused value (DKK)" AggregateDefinition="_valueLeftSum">
<CellTemplate>
@context.Item.UnusedPurchasesValue.ToString("0.00")
</CellTemplate>
</PropertyColumn>
</Columns>
<NoRecordsContent>No records found for the given time period</NoRecordsContent>
</MudDataGrid>
</MudContainer>

@code
{
private IEnumerable<UnusedTicket> Items;
private bool _loading = false;
private DateRange _queryDateRange = new(){ Start = new(DateTime.Today.Year, 1, 1), End = new(DateTime.Today.Year, 12, 31)};

private async Task LoadUnusedTickets(DateRange queryDateRange)
{
_loading = true;
if (queryDateRange.Start is null || queryDateRange.End is null) return;

_queryDateRange = queryDateRange;
var result = await _unusedTicketsService.GetUnusedTickets(queryDateRange.Start.Value, queryDateRange.End.Value);

result.Match(
Succ: res => {
Items = res;
},
Fail: error => {
Snackbar.Add(error.Message, Severity.Error);
Items = new List<UnusedTicket>();
}
);
_loading = false;
}

protected override async Task OnInitializedAsync()
{
await LoadUnusedTickets(_queryDateRange);
}

private AggregateDefinition<UnusedTicket> _ticketsLeftSum = new()
{
Type = AggregateType.Sum
};

private AggregateDefinition<UnusedTicket> _valueLeftSum = new()
{
CustomAggregate = x => {
var sum = x.Sum(t => t.UnusedPurchasesValue);
return sum.ToString("0.00");
},
Type = AggregateType.Custom,
};

private AggregateDefinition<UnusedTicket> _footerLabel = new()
{
CustomAggregate = x => "Total",
Type = AggregateType.Custom,
};
}
2 changes: 1 addition & 1 deletion Shifty.App/Components/UserTable.razor
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@

return result.Match(
Succ: res => {
return new TableData<SimpleUserResponse>(){ Items = res.Users.AsEnumerable(), TotalItems = res.TotalUsers};;
return new TableData<SimpleUserResponse>(){ Items = res.Users.AsEnumerable(), TotalItems = res.TotalUsers};
},
Fail: error => {
Snackbar.Add(error.Message, Severity.Error);
Expand Down
24 changes: 24 additions & 0 deletions Shifty.App/DomainModels/UnusedTicket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Components;
using Shifty.Api.Generated.AnalogCoreV2;

namespace Shifty.App.DomainModels
{
public class UnusedTicket
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int TicketsLeft { get; set; }
public decimal UnusedPurchasesValue { get; set; }

public static UnusedTicket FromDto(UnusedClipsResponse ticket)
{
return new UnusedTicket()
{
ProductId = ticket.ProductId,
ProductName = ticket.ProductName,
TicketsLeft = ticket.TicketsLeft,
UnusedPurchasesValue = ticket.UnusedPurchasesValue,
};
}
}
}
24 changes: 24 additions & 0 deletions Shifty.App/Pages/Statistics.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@page "/Statistics"
@using Components
@inject NavigationManager NavManager

@if (_user is not null && _user.IsInRole("Board"))
{
<UnusedTickets />
}

@code {
[CascadingParameter] public Task<AuthenticationState> AuthTask { get; set; }
private System.Security.Claims.ClaimsPrincipal _user;

protected override async Task OnInitializedAsync()
{
var authState = await AuthTask;
_user = authState.User;

if (_user is null || !_user.IsInRole("Board"))
{
NavManager.NavigateTo("/");
}
}
}
2 changes: 2 additions & 0 deletions Shifty.App/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public static void ConfigureServices(IServiceCollection services, IConfiguration
services.AddScoped<IVoucherRepository, VoucherRepository>();
services.AddScoped<IProductRepository, ProductRepository>();
services.AddScoped<IMenuItemRepository, MenuItemRepository>();
services.AddScoped<IUnusedTicketRepository, UnusedTicketRepository>();
services.AddScoped<CustomAuthStateProvider>();
services.AddScoped<AuthenticationStateProvider>(s => s.GetService<CustomAuthStateProvider>());
services.AddScoped<IAuthenticationService, AuthenticationService>();
Expand All @@ -66,6 +67,7 @@ public static void ConfigureServices(IServiceCollection services, IConfiguration
services.AddScoped<IMenuItemService, MenuItemService>();
services.AddScoped<IUserService, UserService>();
services.AddScoped<IMenuItemService, MenuItemService>();
services.AddScoped<IUnusedTicketsService, UnusedTicketsService>();
services.AddScoped<RequestAuthenticationHandler>();

services.AddMudServices(config =>
Expand Down
15 changes: 15 additions & 0 deletions Shifty.App/Repositories/IUnusedTicketRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using LanguageExt;
using LanguageExt.Common;
using Shifty.Api.Generated.AnalogCoreV1;
using Shifty.Api.Generated.AnalogCoreV2;

namespace Shifty.App.Repositories
{
public interface IUnusedTicketRepository
{
Task<Try<IEnumerable<UnusedClipsResponse>>> GetTickets(UnusedClipsRequest unusedClipsRequest);
}
}
25 changes: 25 additions & 0 deletions Shifty.App/Repositories/UnusedTicketsRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using LanguageExt;
using Shifty.Api.Generated.AnalogCoreV2;
using static LanguageExt.Prelude;

namespace Shifty.App.Repositories
{
public class UnusedTicketRepository : IUnusedTicketRepository
{
private readonly AnalogCoreV2 _client;

public UnusedTicketRepository(AnalogCoreV2 client)
{
_client = client;
}

public async Task<Try<IEnumerable<UnusedClipsResponse>>> GetTickets(UnusedClipsRequest unusedClipsRequest)
{
return await TryAsync(async () => (await _client.ApiV2StatisticsUnusedClipsAsync(unusedClipsRequest)).AsEnumerable());
}
}
}
23 changes: 23 additions & 0 deletions Shifty.App/Services/IUnusedTicketsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using LanguageExt;
using LanguageExt.Common;
using MudBlazor;
using Shifty.Api.Generated.AnalogCoreV1;
using Shifty.Api.Generated.AnalogCoreV2;
using Shifty.App.DomainModels;

namespace Shifty.App.Services
{
public interface IUnusedTicketsService
{
/// <summary>
/// Queries unused tickets
/// </summary>
/// <param name="from">The first date to retrieve unused tickets from</param>
/// <param name="to">The last date to retrieve unused tickets to</param>
/// <returns>A list of unused tickets grouped by product</returns>
Task<Try<IEnumerable<UnusedTicket>>> GetUnusedTickets(DateTimeOffset from, DateTimeOffset to);
}
}
30 changes: 30 additions & 0 deletions Shifty.App/Services/UnusedTicketsService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using LanguageExt;
using Shifty.Api.Generated.AnalogCoreV2;
using Shifty.App.DomainModels;
using Shifty.App.Repositories;

namespace Shifty.App.Services
{
public class UnusedTicketsService : IUnusedTicketsService
{
private readonly IUnusedTicketRepository _unusedTicketRepository;

public UnusedTicketsService(IUnusedTicketRepository unusedTicketRepository)
{
_unusedTicketRepository = unusedTicketRepository;
}

public async Task<Try<IEnumerable<UnusedTicket>>> GetUnusedTickets(DateTimeOffset from, DateTimeOffset to)
{
return await _unusedTicketRepository
.GetTickets(new UnusedClipsRequest(){
StartDate = from,
EndDate = to
})
.Map(x => x.Map(UnusedTicket.FromDto));
}
}
}
6 changes: 5 additions & 1 deletion Shifty.App/Shared/NavMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@

<MudNavMenu>
<MudNavLink Href="" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Home">Home</MudNavLink>
@if (_user is not null && _user.IsInRole("Board"))
@if (_user is not null && (_user.IsInRole("Board") || _user.IsInRole("Manager")))
{
<MudNavLink Href="Voucher" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.Sell">Issue vouchers</MudNavLink>
}
@if (_user is not null && _user.IsInRole("Board"))
{
<MudNavLink Href="Products" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Outlined.Inventory">Product Management</MudNavLink>
<MudNavLink Href="MenuItems" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Outlined.Coffee">Menu Item Management</MudNavLink>
<MudNavLink Href="Users" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.ManageAccounts">Manage users</MudNavLink>
<MudNavLink Href="Statistics" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.Analytics">Statistics</MudNavLink>
}
<MudDivider />
<MudNavLink OnClick="_authenticationService.Logout" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.Logout">Logout</MudNavLink>
Expand Down
Loading

0 comments on commit 7cfd6fa

Please sign in to comment.