-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* UI for issuing vouchers * Repositories for products and vouchers * Services * Access to backend through API * Adds product fetch error handling * Show voucher responses * Add error handling to vouchers * Add count of tickets to product * Adds form validation * Adds Issue Vouchers to navigation menu * Add role based access * Copy to clipboard * Readonly textfield * Center div * Refactor from Rider suggestions * Redirect to main page if unauthorized * Addresses reviews * Remove deprecated link * Improved form validation * Small UI changes * Adds autocomplete searchable dropdown and prefix length counter --------- Co-authored-by: Andreas Trøstrup <8415722+Duckth@users.noreply.github.com>
- Loading branch information
1 parent
7de6d87
commit dcc9fe6
Showing
15 changed files
with
404 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
@namespace Components | ||
@using System.ComponentModel.DataAnnotations | ||
@using Shifty.App.Services | ||
@using Shifty.Api.Generated.AnalogCoreV1 | ||
@using Shifty.Api.Generated.AnalogCoreV2 | ||
@using Shared | ||
@using LanguageExt.UnsafeValueAccess | ||
@inject IProductService _productService | ||
@inject IVoucherService _voucherService | ||
@inject ISnackbar Snackbar | ||
@inject IJSRuntime JSRuntime | ||
|
||
<MudContainer Style="margin: 50px 15%;" > | ||
<MudCard Class="mb-auto" MinWidth="280px" Width="40vw"> | ||
<MudCardContent> | ||
<MudText Align="Align.Center" Class="mb-n4">Issue Voucher Form</MudText> | ||
<MudForm @bind-IsValid="@_isFormValid" > | ||
<MudAutocomplete T="ProductDto" | ||
Required="true" | ||
RequiredError="Product is required" | ||
Placeholder="Select product" | ||
ResetValueOnEmptyText="true" | ||
CoerceText="true" | ||
CoerceValue="true" | ||
Label="Product" | ||
SearchFunc="@Products" | ||
ToStringFunc="@_converter" | ||
@bind-Value=_voucherForm.Product/> | ||
|
||
<MudNumericField @bind-Value="_voucherForm.Amount" | ||
Placeholder="1" | ||
Label="Amount" | ||
Variant="Variant.Text" | ||
Required="true" | ||
RequiredError="Product is required" | ||
Min="1" | ||
Max="50" /> | ||
|
||
<MudTextField T="string" | ||
@bind-Value="_voucherForm.Requester" | ||
Label="Requester" | ||
Required="true" | ||
RequiredError="Requester is required" /> | ||
|
||
<MudTextField T="string" | ||
@bind-Value="_voucherForm.Description" | ||
Label="Description" | ||
Required="true" | ||
RequiredError="Description is required" /> | ||
|
||
<MudTextField T="string" | ||
@bind-Value="_voucherForm.Prefix" | ||
Label="Voucher prefix" | ||
Validation="@(new Func<string,string>(prefixValidation))" | ||
Required="true" | ||
Counter="3" | ||
MaxLength="3" | ||
Immediate="true" | ||
RequiredError="Prefix is required" /> | ||
|
||
<MudCardActions> | ||
<MudButton Variant="Variant.Filled" | ||
Color="Color.Primary" | ||
Class="ml-auto" | ||
Disabled="@(!_isFormValid)" | ||
OnClick="@(async () => await IssueVoucher())" | ||
EndIcon="@Icons.Material.Filled.Sell"> | ||
Issue Voucher | ||
</MudButton> | ||
</MudCardActions> | ||
</MudForm> | ||
@if (_showProgressBar) | ||
{ | ||
<MudContainer class="d-flex"> | ||
<LoadingIndicator Height="100px"/> | ||
</MudContainer> | ||
} | ||
@if (_vouchers is not null) | ||
{ | ||
<MudTextField Text="@_voucherCodes" | ||
@ref="_multilineReference" | ||
T="string" | ||
Adornment="Adornment.End" | ||
Style="border-width: 2px; padding: 4px;" | ||
Outlined="true" | ||
AdornmentIcon="@Icons.Material.Outlined.ContentCopy" | ||
OnAdornmentClick="@(async () => await CopyToClipboard())" | ||
Lines="@Math.Min(_vouchers.Count(), 10)" | ||
ReadOnly=true /> | ||
} | ||
</MudCardContent> | ||
</MudCard> | ||
</MudContainer> | ||
|
||
@code | ||
{ | ||
[Parameter] | ||
public System.Security.Claims.ClaimsPrincipal User { get; set; } | ||
private VoucherForm _voucherForm = new(); | ||
private bool _isFormValid = false; | ||
private bool _showProgressBar = false; | ||
private IEnumerable<ProductDto> _products = new List<ProductDto>(); | ||
private IEnumerable<IssueVoucherResponse> _vouchers; | ||
private string _voucherCodes; | ||
private MudTextField<string> _multilineReference; | ||
|
||
private class VoucherForm | ||
{ | ||
[Required] | ||
public string Description { get; set; } | ||
public ProductDto Product { get; set; } | ||
public int Amount { get; set; } = 1; | ||
public string Requester { get; set; } | ||
public string Prefix { get; set; } | ||
} | ||
|
||
protected override async Task OnInitializedAsync() | ||
{ | ||
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.BottomRight; | ||
var result = await _productService.GetProducts(); | ||
|
||
result.Match( | ||
Succ: products => { | ||
_products = products; | ||
_voucherForm.Prefix = User.Claims.Single(el => el.Type.Contains("email")).Value[..3].ToUpper(); | ||
}, | ||
Fail: error => { | ||
Snackbar.Add(error.Message, Severity.Error); | ||
} | ||
); | ||
} | ||
|
||
private async Task<IEnumerable<ProductDto>> Products(string value) | ||
Check warning on line 133 in Shifty.App/Components/Voucher.razor GitHub Actions / dev-deploy / Build codebase / Build webapp / Build and test Webapp
Check warning on line 133 in Shifty.App/Components/Voucher.razor GitHub Actions / dev-deploy / Build codebase / Build webapp / Build and test Webapp
Check warning on line 133 in Shifty.App/Components/Voucher.razor GitHub Actions / prd-deploy / Build codebase / Build webapp / Build and test Webapp
Check warning on line 133 in Shifty.App/Components/Voucher.razor GitHub Actions / prd-deploy / Build codebase / Build webapp / Build and test Webapp
|
||
{ | ||
if (string.IsNullOrEmpty(value)) | ||
return _products; | ||
return _products.Where(x => x.Name.StartsWith(value, StringComparison.InvariantCultureIgnoreCase)); | ||
} | ||
|
||
private async Task CopyToClipboard() | ||
{ | ||
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", _voucherCodes); | ||
Snackbar.Add("Codes copied to clipboard", Severity.Success); | ||
} | ||
|
||
private async Task IssueVoucher() | ||
{ | ||
_showProgressBar = true; | ||
_vouchers = null; | ||
_voucherCodes = null; | ||
var result = await _voucherService.IssueVouchers( | ||
amount: _voucherForm.Amount, | ||
productId: _voucherForm.Product.Id, | ||
description: _voucherForm.Description, | ||
requester: _voucherForm.Requester, | ||
prefix: _voucherForm.Prefix); | ||
|
||
result.Match( | ||
Succ: response => { | ||
_showProgressBar = false; | ||
_vouchers = response; | ||
_voucherCodes = string.Join("\n", _vouchers.Select(response => response.VoucherCode)); | ||
}, | ||
Fail: ex => { | ||
_showProgressBar = false; | ||
Snackbar.Add(ex.Message, Severity.Error); | ||
} | ||
); | ||
} | ||
|
||
Func<ProductDto,string> _converter = p => p != null ? $"{p.Name} - {p.NumberOfTickets} ticket" + (p.NumberOfTickets == 1 ? "" : "s") : ""; | ||
Func<string, string> prefixValidation = str => str.Length == 3 ? null : "Prefix must be 3 letters"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
@page "/Voucher" | ||
@using Components | ||
@inject NavigationManager NavManager | ||
|
||
@if (_user is not null && _user.IsInRole("Board")) | ||
{ | ||
<Voucher User="@_user"/> | ||
} | ||
|
||
@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("/"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using System.Threading.Tasks; | ||
using LanguageExt; | ||
using LanguageExt.Common; | ||
using Shifty.Api.Generated.AnalogCoreV1; | ||
|
||
namespace Shifty.App.Repositories | ||
{ | ||
public interface IProductRepository | ||
{ | ||
public Task<Try<System.Collections.Generic.IEnumerable<ProductDto>>> GetProducts(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using System.Threading.Tasks; | ||
using LanguageExt; | ||
using LanguageExt.Common; | ||
using Shifty.Api.Generated.AnalogCoreV2; | ||
|
||
namespace Shifty.App.Repositories | ||
{ | ||
public interface IVoucherRepository | ||
{ | ||
public Task<Try<System.Collections.Generic.ICollection<IssueVoucherResponse>>> IssueAsync(int amount, int productId, string description, string requester, string prefix); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using LanguageExt; | ||
using LanguageExt.Common; | ||
using Shifty.Api.Generated.AnalogCoreV1; | ||
using Shifty.App.Services; | ||
using static LanguageExt.Prelude; | ||
|
||
namespace Shifty.App.Repositories | ||
{ | ||
public class ProductRepository : IProductRepository | ||
{ | ||
private readonly AnalogCoreV1 _client; | ||
|
||
public ProductRepository(AnalogCoreV1 client) | ||
{ | ||
_client = client; | ||
} | ||
|
||
async Task<Try<IEnumerable<ProductDto>>> IProductRepository.GetProducts() | ||
{ | ||
return await TryAsync(async () => (await _client.ApiV1ProductsAsync()).AsEnumerable()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using LanguageExt; | ||
using LanguageExt.Common; | ||
using Shifty.Api.Generated.AnalogCoreV2; | ||
using static LanguageExt.Prelude; | ||
|
||
namespace Shifty.App.Repositories | ||
{ | ||
public class VoucherRepository : IVoucherRepository | ||
{ | ||
private readonly AnalogCoreV2 _client; | ||
|
||
public VoucherRepository(AnalogCoreV2 client) | ||
{ | ||
_client = client; | ||
} | ||
|
||
public async Task<Try<ICollection<IssueVoucherResponse>>> IssueAsync(int amount, int productId, string description, string reqeuster, string prefix) | ||
{ | ||
var request = new IssueVoucherRequest() | ||
{ | ||
Amount = amount, | ||
Description = description, | ||
ProductId = productId, | ||
Requester = reqeuster, | ||
VoucherPrefix = prefix, | ||
}; | ||
|
||
return await TryAsync(async () => await _client.ApiV2VouchersIssueVouchersAsync(request)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using LanguageExt; | ||
using LanguageExt.Common; | ||
using Shifty.Api.Generated.AnalogCoreV1; | ||
|
||
namespace Shifty.App.Services | ||
{ | ||
public interface IProductService | ||
{ | ||
/// <summary> | ||
/// Gives products available to user | ||
/// </summary> | ||
/// <returns>Collection of products in the form of ProductDtos. The Collection is null if an error happens.</returns> | ||
Task<Try<IEnumerable<ProductDto>>> GetProducts(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using LanguageExt; | ||
using LanguageExt.Common; | ||
using Shifty.Api.Generated.AnalogCoreV2; | ||
|
||
namespace Shifty.App.Services | ||
{ | ||
public interface IVoucherService | ||
{ | ||
Task<Try<ICollection<IssueVoucherResponse>>> IssueVouchers(int amount, int productId, string description, string requester, string prefix); | ||
} | ||
} |
Oops, something went wrong.