MRB webassembly
This commit is contained in:
64
MesaFabApproval.Client/Pages/AuthenticatedRedirect.razor
Normal file
64
MesaFabApproval.Client/Pages/AuthenticatedRedirect.razor
Normal file
@ -0,0 +1,64 @@
|
||||
@page "/redirect"
|
||||
@attribute [AllowAnonymous]
|
||||
@inject MesaFabApprovalAuthStateProvider authStateProvider
|
||||
@inject IAuthenticationService authService
|
||||
@inject IUserService userService
|
||||
@inject ISnackbar snackbar
|
||||
@inject MesaFabApprovalAuthStateProvider authStateProvider
|
||||
@inject NavigationManager navigationManager
|
||||
|
||||
@code {
|
||||
private string? _jwt;
|
||||
private string? _refreshToken;
|
||||
private string? _redirectPath;
|
||||
|
||||
protected override async Task OnParametersSetAsync() {
|
||||
try {
|
||||
Uri uri = navigationManager.ToAbsoluteUri(navigationManager.Uri);
|
||||
|
||||
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("jwt", out var jwt)) {
|
||||
_jwt = System.Net.WebUtility.UrlDecode(jwt);
|
||||
}
|
||||
|
||||
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("refreshToken", out var refreshToken)) {
|
||||
_refreshToken = System.Net.WebUtility.UrlDecode(refreshToken);
|
||||
}
|
||||
|
||||
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("redirectPath", out var redirectPath)) {
|
||||
_redirectPath = System.Net.WebUtility.UrlDecode(redirectPath);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_jwt) && !string.IsNullOrWhiteSpace(_refreshToken)) {
|
||||
await authService.SetTokens(_jwt, _refreshToken);
|
||||
|
||||
ClaimsPrincipal principal = authService.GetClaimsPrincipalFromJwt(_jwt);
|
||||
|
||||
string loginId = userService.GetLoginIdFromClaimsPrincipal(principal);
|
||||
|
||||
await authService.SetLoginId(loginId);
|
||||
await authService.SetTokens(_jwt, _refreshToken);
|
||||
|
||||
User? user = await userService.GetUserByLoginId(loginId);
|
||||
await authService.SetCurrentUser(user);
|
||||
|
||||
await authStateProvider.StateHasChanged(principal);
|
||||
}
|
||||
|
||||
if (authStateProvider.CurrentUser is not null && !string.IsNullOrWhiteSpace(_redirectPath)) {
|
||||
navigationManager.NavigateTo(_redirectPath);
|
||||
} else {
|
||||
await authStateProvider.Logout();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_redirectPath)) {
|
||||
navigationManager.NavigateTo($"login/{_redirectPath}");
|
||||
} else {
|
||||
navigationManager.NavigateTo("login");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
snackbar.Add($"Redirect failed, because {ex.Message}", Severity.Error);
|
||||
navigationManager.NavigateTo("login");
|
||||
}
|
||||
}
|
||||
}
|
69
MesaFabApproval.Client/Pages/Components/Comments.razor
Normal file
69
MesaFabApproval.Client/Pages/Components/Comments.razor
Normal file
@ -0,0 +1,69 @@
|
||||
@inject ISnackbar snackbar
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudPaper Class="p-2">
|
||||
<MudForm @bind-Errors="@errors">
|
||||
<MudTextField T="string"
|
||||
Label="Comments"
|
||||
Required="true"
|
||||
RequiredError="You must provide a comment"
|
||||
@bind-Value="@comments"
|
||||
@bind-Text="@comments"
|
||||
Immediate="true"
|
||||
AutoGrow
|
||||
AutoFocus/>
|
||||
</MudForm>
|
||||
</MudPaper>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
Class="m1-auto"
|
||||
OnClick=SubmitComments>
|
||||
@if (processing) {
|
||||
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
|
||||
<MudText>Processing</MudText>
|
||||
} else {
|
||||
<MudText>Ok</MudText>
|
||||
}
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Class="grey text-black m1-auto"
|
||||
OnClick=Cancel>
|
||||
Cancel
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string comments { get; set; } = "";
|
||||
|
||||
private string[] errors = { };
|
||||
|
||||
private bool processing = false;
|
||||
|
||||
protected override void OnParametersSet() {
|
||||
comments = string.Empty;
|
||||
}
|
||||
|
||||
private void SubmitComments() {
|
||||
processing = true;
|
||||
try {
|
||||
if (string.IsNullOrWhiteSpace(comments) || comments.Length < 5)
|
||||
throw new Exception("Comments must be at least five characters long");
|
||||
|
||||
MudDialog.Close(DialogResult.Ok(comments));
|
||||
} catch (Exception ex) {
|
||||
snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
processing = false;
|
||||
}
|
||||
|
||||
private void Cancel() {
|
||||
MudDialog.Close(DialogResult.Cancel());
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
@inject ISnackbar snackbar
|
||||
@inject IMRBService mrbService
|
||||
@inject MesaFabApprovalAuthStateProvider authStateProvider
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudPaper Class="p-2">
|
||||
<MudForm @bind-Errors="@errors">
|
||||
<MudTextField T="string"
|
||||
Label="Comments"
|
||||
Required="true"
|
||||
RequiredError="You must provide a comment"
|
||||
@bind-Value="@comments"
|
||||
@bind-Text="@comments"
|
||||
Immediate="true"
|
||||
AutoGrow
|
||||
AutoFocus/>
|
||||
<MudFileUpload T="IReadOnlyList<IBrowserFile>" OnFilesChanged="AddAttachments">
|
||||
<ActivatorContent>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
style="margin: auto;"
|
||||
StartIcon="@Icons.Material.Filled.AttachFile">
|
||||
@if (attachmentUploadInProcess) {
|
||||
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
|
||||
<MudText>Processing</MudText>
|
||||
} else {
|
||||
<MudText>Upload Supporting Document</MudText>
|
||||
}
|
||||
</MudButton>
|
||||
</ActivatorContent>
|
||||
</MudFileUpload>
|
||||
</MudForm>
|
||||
</MudPaper>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
Class="m1-auto"
|
||||
OnClick=SubmitComments>
|
||||
@if (processing) {
|
||||
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
|
||||
<MudText>Processing</MudText>
|
||||
} else {
|
||||
<MudText>Ok</MudText>
|
||||
}
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Class="grey text-black m1-auto"
|
||||
OnClick=Cancel>
|
||||
Cancel
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string comments { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public int actionId { get; set; } = 0;
|
||||
|
||||
private string[] errors = { };
|
||||
|
||||
private bool processing = false;
|
||||
private bool attachmentUploadInProcess = false;
|
||||
|
||||
protected override void OnParametersSet() {
|
||||
comments = string.Empty;
|
||||
}
|
||||
|
||||
private void SubmitComments() {
|
||||
processing = true;
|
||||
try {
|
||||
if (string.IsNullOrWhiteSpace(comments) || comments.Length < 5)
|
||||
throw new Exception("Comments must be at least five characters long");
|
||||
|
||||
MudDialog.Close(DialogResult.Ok(comments));
|
||||
} catch (Exception ex) {
|
||||
snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
processing = false;
|
||||
}
|
||||
|
||||
private void Cancel() {
|
||||
MudDialog.Close(DialogResult.Cancel());
|
||||
}
|
||||
|
||||
private async Task AddAttachments(InputFileChangeEventArgs args) {
|
||||
attachmentUploadInProcess = true;
|
||||
try {
|
||||
if (actionId <= 0)
|
||||
throw new Exception($"{actionId} is not a valid MRB action ID");
|
||||
|
||||
IReadOnlyList<IBrowserFile> attachments = args.GetMultipleFiles();
|
||||
|
||||
if (authStateProvider.CurrentUser is not null) {
|
||||
await mrbService.UploadActionAttachments(attachments, actionId);
|
||||
|
||||
await mrbService.GetAllActionAttachmentsForMRB(actionId, true);
|
||||
|
||||
attachmentUploadInProcess = false;
|
||||
|
||||
snackbar.Add("Attachments successfully uploaded", Severity.Success);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
attachmentUploadInProcess = false;
|
||||
snackbar.Add($"Unable to upload attachments, because {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
261
MesaFabApproval.Client/Pages/Components/MRBActionForm.razor
Normal file
261
MesaFabApproval.Client/Pages/Components/MRBActionForm.razor
Normal file
@ -0,0 +1,261 @@
|
||||
@inject IMRBService mrbService
|
||||
@inject ISnackbar snackbar
|
||||
@inject ICustomerService customerService
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
<MudPaper Class="p-2">
|
||||
<MudForm @bind-Errors="@errors">
|
||||
<MudSelect T="string"
|
||||
Label="Action"
|
||||
Required="true"
|
||||
RequiredError="You must select an action!"
|
||||
@bind-Value="@mrbAction.Action"
|
||||
Text="@mrbAction.Action">
|
||||
<MudSelectItem Value="@("Block")" />
|
||||
<MudSelectItem Value="@("Convert")" />
|
||||
<MudSelectItem Value="@("Recall")" />
|
||||
<MudSelectItem Value="@("Scrap")" />
|
||||
<MudSelectItem Value="@("Unblock")" />
|
||||
<MudSelectItem Value="@("Waiver")" />
|
||||
</MudSelect>
|
||||
@if (mrbAction.Action.Equals("Convert")) {
|
||||
<MudTextField @bind-Value="@mrbAction.ConvertFrom"
|
||||
Label="Convert From"
|
||||
Required
|
||||
RequiredError="Conversion value required!"
|
||||
Text="@mrbAction.ConvertFrom" />
|
||||
<MudTextField @bind-Value="@mrbAction.ConvertTo"
|
||||
Label="Convert To"
|
||||
Required
|
||||
RequiredError="Conversion value required!"
|
||||
Text="@mrbAction.ConvertTo" />
|
||||
}
|
||||
<MudSelect T="string"
|
||||
Label="Affected Customer"
|
||||
Required
|
||||
Variant="Variant.Outlined"
|
||||
AnchorOrigin="Origin.BottomCenter"
|
||||
@bind-Value=mrbAction.Customer
|
||||
Text="@mrbAction.Customer">
|
||||
@foreach (string customer in customerNames) {
|
||||
<MudSelectItem Value="@(customer)" />
|
||||
}
|
||||
</MudSelect>
|
||||
<MudTextField T="int"
|
||||
InputType="@InputType.Number"
|
||||
Label="Qty"
|
||||
Required="true"
|
||||
RequiredError="You must supply a quantity!"
|
||||
@bind-Value=mrbAction.Quantity
|
||||
Text="@mrbAction.Quantity.ToString()" />
|
||||
<MudAutocomplete T="string"
|
||||
Label="Part Number"
|
||||
Variant="Variant.Outlined"
|
||||
AnchorOrigin="Origin.BottomCenter"
|
||||
@bind-Value=mrbAction.PartNumber
|
||||
Text="@mrbAction.PartNumber"
|
||||
SearchFunc="@PartNumberSearch"
|
||||
ResetValueOnEmptyText
|
||||
CoerceText />
|
||||
<MudAutocomplete T="string"
|
||||
Label="Batch Number / Lot Number"
|
||||
Variant="Variant.Outlined"
|
||||
AnchorOrigin="Origin.BottomCenter"
|
||||
@bind-Value=mrbAction.LotNumber
|
||||
Text="@mrbAction.LotNumber"
|
||||
SearchFunc="@LotNumberSearch"
|
||||
ResetValueOnEmptyText
|
||||
CoerceText />
|
||||
@if (mrbAction.Action.Equals("Scrap")) {
|
||||
<MudSelect T="string"
|
||||
Label="Justification"
|
||||
Required
|
||||
RequiredError="Justification required!"
|
||||
Variant="Variant.Outlined"
|
||||
AnchorOrigin="Origin.BottomCenter"
|
||||
@bind-Value=mrbAction.Justification
|
||||
Text="@mrbAction.Justification">
|
||||
<MudSelectItem Value="@("Obsolete")" />
|
||||
<MudSelectItem Value="@("Aged Material")" />
|
||||
<MudSelectItem Value="@("Out of Specification")" />
|
||||
</MudSelect>
|
||||
}
|
||||
</MudForm>
|
||||
</MudPaper>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
Class="m1-auto"
|
||||
OnClick=SaveMRBAction>
|
||||
@if (processingSave) {
|
||||
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
|
||||
<MudText>Processing</MudText>
|
||||
} else {
|
||||
<MudText>Save</MudText>
|
||||
}
|
||||
</MudButton>
|
||||
@if (mrbAction is not null && mrbAction.ActionID > 0) {
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Secondary"
|
||||
Disabled="@(mrbAction is null || (mrbAction is not null && mrbAction.ActionID <= 0))"
|
||||
Class="m1-auto"
|
||||
OnClick=DeleteMRBAction>
|
||||
@if (processingDelete) {
|
||||
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
|
||||
<MudText>Processing</MudText>
|
||||
} else {
|
||||
<MudText>Delete</MudText>
|
||||
}
|
||||
</MudButton>
|
||||
}
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Class="grey text-black m1-auto"
|
||||
OnClick=Cancel>
|
||||
Cancel
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public MRBAction mrbAction { get; set; }
|
||||
|
||||
private IEnumerable<MRBAction>? actions = null;
|
||||
|
||||
private MRBAction lastAction = null;
|
||||
|
||||
private IEnumerable<string> customerNames = new List<string>();
|
||||
|
||||
private bool isVisible { get; set; }
|
||||
private string[] errors = { };
|
||||
private bool processingSave = false;
|
||||
private bool processingDelete = false;
|
||||
|
||||
protected override async Task OnParametersSetAsync() {
|
||||
isVisible = true;
|
||||
|
||||
if (mrbAction is null) {
|
||||
snackbar.Add("MRB action cannot be null", Severity.Warning);
|
||||
MudDialog.Cancel();
|
||||
} else {
|
||||
actions = (await mrbService.GetMRBActionsForMRB(mrbAction.MRBNumber, false)).OrderByDescending(a => a.ActionID);
|
||||
|
||||
if (actions is not null && actions.Count() > 0) {
|
||||
if (string.IsNullOrWhiteSpace(mrbAction.LotNumber))
|
||||
mrbAction.LotNumber = actions.First().LotNumber;
|
||||
if (string.IsNullOrWhiteSpace(mrbAction.PartNumber))
|
||||
mrbAction.PartNumber = actions.First().PartNumber;
|
||||
}
|
||||
}
|
||||
|
||||
if (customerNames is null || customerNames.Count() <= 0)
|
||||
customerNames = await customerService.GetAllCustomerNames();
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private bool FormIsValid() {
|
||||
bool actionIsValid = mrbAction.Action.Equals("Block") || mrbAction.Action.Equals("Convert") ||
|
||||
mrbAction.Action.Equals("Other") || mrbAction.Action.Equals("Recall") || mrbAction.Action.Equals("Scrap") ||
|
||||
mrbAction.Action.Equals("Unblock") || mrbAction.Action.Equals("Waiver");
|
||||
actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Customer) &&
|
||||
!string.IsNullOrWhiteSpace(mrbAction.PartNumber) &&
|
||||
!string.IsNullOrWhiteSpace(mrbAction.LotNumber);
|
||||
|
||||
if (mrbAction.Action.Equals("Scrap"))
|
||||
return actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Justification);
|
||||
|
||||
return actionIsValid;
|
||||
}
|
||||
|
||||
private async void SaveMRBAction() {
|
||||
processingSave = true;
|
||||
try {
|
||||
if (!FormIsValid()) throw new Exception("You must complete the form before saving!");
|
||||
|
||||
if (mrbAction.MRBNumber > 0) {
|
||||
if (mrbAction.ActionID <= 0) {
|
||||
await mrbService.CreateMRBAction(mrbAction);
|
||||
snackbar.Add("MRB action created", Severity.Success);
|
||||
} else {
|
||||
await mrbService.UpdateMRBAction(mrbAction);
|
||||
snackbar.Add("MRB action updated", Severity.Success);
|
||||
}
|
||||
|
||||
actions = (await mrbService.GetMRBActionsForMRB(mrbAction.MRBNumber, true)).OrderByDescending(a => a.ActionID);
|
||||
} else {
|
||||
snackbar.Add("MRB action saved", Severity.Success);
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
|
||||
MudDialog.Close(DialogResult.Ok(mrbAction));
|
||||
} catch (Exception ex) {
|
||||
snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
processingSave = false;
|
||||
}
|
||||
|
||||
private async void DeleteMRBAction() {
|
||||
processingDelete = true;
|
||||
try {
|
||||
if (mrbAction is null) throw new Exception("MRB action cannot be null!");
|
||||
if (mrbAction.ActionID <= 0)
|
||||
throw new Exception("You cannot delete an action before creating it!");
|
||||
if (mrbAction.MRBNumber <= 0)
|
||||
throw new Exception("Invalid MRB number!");
|
||||
|
||||
await mrbService.DeleteMRBAction(mrbAction);
|
||||
snackbar.Add("MRB action successfully deleted", Severity.Success);
|
||||
|
||||
StateHasChanged();
|
||||
|
||||
MudDialog.Close(DialogResult.Ok<MRBAction>(null));
|
||||
} catch (Exception ex) {
|
||||
snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
processingDelete = false;
|
||||
}
|
||||
|
||||
private void Cancel() {
|
||||
MudDialog.Cancel();
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> PartNumberSearch(string value, CancellationToken token) {
|
||||
if (actions is null) return new List<string> { value };
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value)) return new string[0];
|
||||
|
||||
HashSet<string> partNumbers = new();
|
||||
|
||||
partNumbers.Add(value);
|
||||
|
||||
foreach (MRBAction action in actions) {
|
||||
if (action.PartNumber.Contains(value, StringComparison.InvariantCultureIgnoreCase))
|
||||
partNumbers.Add(action.PartNumber);
|
||||
}
|
||||
|
||||
return partNumbers;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> LotNumberSearch(string value, CancellationToken token) {
|
||||
if (actions is null) return new List<string> { value };
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value)) return new string[0];
|
||||
|
||||
HashSet<string> lotNumbers = new();
|
||||
|
||||
lotNumbers.Add(value);
|
||||
|
||||
foreach (MRBAction action in actions) {
|
||||
if (action.LotNumber.Contains(value, StringComparison.InvariantCultureIgnoreCase))
|
||||
lotNumbers.Add(action.LotNumber);
|
||||
}
|
||||
|
||||
return lotNumbers;
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
@inject IApprovalService approvalService
|
||||
@inject ISnackbar snackbar
|
||||
@inject MesaFabApprovalAuthStateProvider authStateProvider
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
@if (availableApprovers is not null) {
|
||||
<MudPaper Class="p-2">
|
||||
<MudSelect T="User"
|
||||
Label="Select a User"
|
||||
Required
|
||||
Variant="Variant.Outlined"
|
||||
AnchorOrigin="Origin.BottomCenter"
|
||||
@bind-Value=selectedUser
|
||||
Text="@(selectedUser is null ? "" : selectedUser.GetFullName())">
|
||||
@foreach (User user in availableApprovers) {
|
||||
<MudSelectItem Value="@user">
|
||||
@user.GetFullName()
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudPaper>
|
||||
}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
Class="m1-auto"
|
||||
OnClick=Submit>
|
||||
<MudText>Submit</MudText>
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Secondary"
|
||||
Class="m1-auto"
|
||||
OnClick=Cancel>
|
||||
<MudText>Cancel</MudText>
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
<MudOverlay Visible=processing DarkBackground="true" AutoClose="false">
|
||||
<MudProgressCircular Color="Color.Info" Size="Size.Medium" Indeterminate="true" />
|
||||
</MudOverlay>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public User selectedUser { get; set; }
|
||||
|
||||
private bool processing = false;
|
||||
|
||||
private HashSet<User> availableApprovers = new();
|
||||
|
||||
protected override async Task OnInitializedAsync() {
|
||||
try {
|
||||
processing = true;
|
||||
|
||||
string roleName = "QA_PRE_APPROVAL";
|
||||
string subRoleName = "QA_PRE_APPROVAL";
|
||||
|
||||
IEnumerable<User> qaApprovers = await GetApprovalGroupMembersForRoleAndSubRole(roleName, subRoleName);
|
||||
|
||||
foreach (User approver in qaApprovers)
|
||||
availableApprovers.Add(approver);
|
||||
|
||||
roleName = "MRB Approver";
|
||||
subRoleName = "MRBApprover";
|
||||
|
||||
IEnumerable<User> mrbApprovers = await GetApprovalGroupMembersForRoleAndSubRole(roleName, subRoleName);
|
||||
|
||||
foreach (User approver in mrbApprovers)
|
||||
availableApprovers.Add(approver);
|
||||
|
||||
selectedUser = availableApprovers.First();
|
||||
|
||||
processing = false;
|
||||
} catch (Exception ex) {
|
||||
processing = false;
|
||||
snackbar.Add($"Unable to get all approvers, because {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void Submit() {
|
||||
MudDialog.Close(DialogResult.Ok(selectedUser));
|
||||
}
|
||||
|
||||
private void Cancel() {
|
||||
MudDialog.Close(DialogResult.Cancel());
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<User>> GetApprovalGroupMembersForRoleAndSubRole(string roleName, string subRoleName) {
|
||||
HashSet<User> members = new();
|
||||
|
||||
int roleId = await approvalService.GetRoleIdForRoleName(roleName);
|
||||
|
||||
if (roleId <= 0) throw new Exception($"could not find {roleName} role ID");
|
||||
|
||||
IEnumerable<SubRole> subRoles = await approvalService.GetSubRolesForSubRoleName(subRoleName, roleId);
|
||||
|
||||
foreach (SubRole subRole in subRoles) {
|
||||
IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
|
||||
|
||||
foreach (User member in subRoleMembers) {
|
||||
members.Add(member);
|
||||
}
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
@attribute [AllowAnonymous]
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
@code {
|
||||
protected override void OnInitialized() {
|
||||
Navigation.NavigateTo("login");
|
||||
}
|
||||
}
|
74
MesaFabApproval.Client/Pages/Components/UserSelector.razor
Normal file
74
MesaFabApproval.Client/Pages/Components/UserSelector.razor
Normal file
@ -0,0 +1,74 @@
|
||||
@inject IUserService userService
|
||||
@inject ISnackbar snackbar
|
||||
@inject MesaFabApprovalAuthStateProvider authStateProvider
|
||||
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
@if (allUsers is not null) {
|
||||
<MudPaper Class="p-2">
|
||||
<MudSelect T="User"
|
||||
Label="Select a User"
|
||||
Required
|
||||
Variant="Variant.Outlined"
|
||||
AnchorOrigin="Origin.BottomCenter"
|
||||
@bind-Value=selectedUser
|
||||
Text="@(selectedUser is null ? "" : selectedUser.GetFullName())">
|
||||
@foreach (User user in allUsers) {
|
||||
<MudSelectItem Value="@user">
|
||||
@user.GetFullName()
|
||||
</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudPaper>
|
||||
}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
Class="m1-auto"
|
||||
OnClick=Submit>
|
||||
<MudText>Submit</MudText>
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Secondary"
|
||||
Class="m1-auto"
|
||||
OnClick=Cancel>
|
||||
<MudText>Cancel</MudText>
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
<MudOverlay Visible=processing DarkBackground="true" AutoClose="false">
|
||||
<MudProgressCircular Color="Color.Info" Size="Size.Medium" Indeterminate="true" />
|
||||
</MudOverlay>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public User selectedUser { get; set; }
|
||||
|
||||
private bool processing = false;
|
||||
|
||||
private IEnumerable<User> allUsers = new List<User>();
|
||||
|
||||
protected override async Task OnInitializedAsync() {
|
||||
try {
|
||||
processing = true;
|
||||
selectedUser = authStateProvider.CurrentUser;
|
||||
allUsers = await userService.GetAllActiveUsers();
|
||||
processing = false;
|
||||
} catch (Exception ex) {
|
||||
processing = false;
|
||||
snackbar.Add($"Unable to get all users, because {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void Submit() {
|
||||
MudDialog.Close(DialogResult.Ok(selectedUser));
|
||||
}
|
||||
|
||||
private void Cancel() {
|
||||
MudDialog.Close(DialogResult.Cancel());
|
||||
}
|
||||
}
|
248
MesaFabApproval.Client/Pages/Dashboard.razor
Normal file
248
MesaFabApproval.Client/Pages/Dashboard.razor
Normal file
@ -0,0 +1,248 @@
|
||||
@page "/"
|
||||
@page "/Dashboard"
|
||||
@inject IConfiguration Configuration
|
||||
@inject MesaFabApprovalAuthStateProvider stateProvider
|
||||
@inject IApprovalService approvalService
|
||||
@inject IMemoryCache cache
|
||||
@inject NavigationManager navigationManager
|
||||
@inject ISnackbar snackbar
|
||||
@inject IMRBService mrbService
|
||||
@inject IECNService ecnService
|
||||
@inject ICAService caService
|
||||
@inject IJSRuntime jsRuntime
|
||||
|
||||
<PageTitle>Dashboard</PageTitle>
|
||||
|
||||
<MudPaper Class="p-2 m-2">
|
||||
<MudText Typo="Typo.h3" Align="Align.Center">Dashboard</MudText>
|
||||
</MudPaper>
|
||||
|
||||
<MudPaper Class="p-2 m-2">
|
||||
<MudTabs Class="p-2" Rounded Centered Color="Color.Info" MinimumTabWidth="50%">
|
||||
<MudTabPanel Text="My Active Approvals" Style="min-width:50%; text-align: center;">
|
||||
@if (stateProvider.CurrentUser is not null && approvalList is not null && !myApprovalsProcessing) {
|
||||
<MudPaper Outlined="true"
|
||||
Class="p-2 m-2 d-flex flex-column justify-center">
|
||||
<MudText Typo="Typo.h4" Align="Align.Center">My Active Approvals</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle" Class="my-2" />
|
||||
<MudTable Items="@approvalList"
|
||||
Class="m-2"
|
||||
Striped
|
||||
SortLabel="Sort by">
|
||||
<HeaderContent>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.IssueID)">
|
||||
Issue ID
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.SubRoleCategoryItem)">
|
||||
Role
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<Approval,object>(x=>x.AssignedDate)">
|
||||
Assigned Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.Step)">
|
||||
Step
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Issue ID">
|
||||
@if (context.IssueID > 0) {
|
||||
<MudLink OnClick="@(() => FollowLink(context.IssueID))">@context.IssueID</MudLink>
|
||||
}
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Role">@context.SubRoleCategoryItem</MudTd>
|
||||
<MudTd DataLabel="Assigned Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.AssignedDate)</MudTd>
|
||||
<MudTd DataLabel="Step">@context.Step</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager />
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
</MudPaper>
|
||||
} else {
|
||||
<MudOverlay Visible DarkBackground="true" AutoClose="false">
|
||||
<MudText Align="Align.Center" Typo="Typo.h3">Processing</MudText>
|
||||
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
|
||||
</MudOverlay>
|
||||
}
|
||||
</MudTabPanel>
|
||||
<MudTabPanel Text="My MRBs" Style="min-width:50%; text-align: center;">
|
||||
@if (stateProvider.CurrentUser is not null && myMRBs is not null && !myMrbsProcessing) {
|
||||
<MudPaper Outlined="true"
|
||||
Class="p-2 m-2 d-flex flex-column justify-center">
|
||||
<MudText Typo="Typo.h4" Align="Align.Center">My MRBs</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle" Class="my-2" />
|
||||
<MudTable Items="@myMRBs"
|
||||
Class="m-2"
|
||||
Striped
|
||||
SortLabel="Sort by"
|
||||
Filter="new Func<MRB, bool>(FilterFuncForMRBTable)">
|
||||
<ToolBarContent>
|
||||
<MudSpacer />
|
||||
<MudTextField @bind-Value="mrbSearchString"
|
||||
Placeholder="Search"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Material.Filled.Search"
|
||||
IconSize="Size.Medium"
|
||||
Class="mt-0" />
|
||||
</ToolBarContent>
|
||||
<HeaderContent>
|
||||
<MudTh>
|
||||
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<MRB,object>(x=>x.MRBNumber)">
|
||||
MRB#
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.Title)">
|
||||
Title
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.SubmittedDate)">
|
||||
Submitted Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.ApprovalDate)">
|
||||
Approval Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.CancelDate)">
|
||||
Cancel Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.CloseDate)">
|
||||
Completed Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="MRB#">
|
||||
<MudLink OnClick="@(() => GoTo($"mrb/{context.MRBNumber}"))">@context.MRBNumber</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Title">@context.Title</MudTd>
|
||||
<MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.SubmittedDate)</MudTd>
|
||||
<MudTd DataLabel="Approval Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ApprovalDate)</MudTd>
|
||||
<MudTd DataLabel="Cancel Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CancelDate)</MudTd>
|
||||
<MudTd DataLabel="Completed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CloseDate)</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager />
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
</MudPaper>
|
||||
} else {
|
||||
<MudOverlay Visible DarkBackground="true" AutoClose="false">
|
||||
<MudText Align="Align.Center" Typo="Typo.h3">Processing</MudText>
|
||||
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
|
||||
</MudOverlay>
|
||||
}
|
||||
</MudTabPanel>
|
||||
</MudTabs>
|
||||
</MudPaper>
|
||||
|
||||
@code {
|
||||
private IEnumerable<Approval> approvalList = new List<Approval>();
|
||||
private IEnumerable<MRB> myMRBs = new List<MRB>();
|
||||
|
||||
private IEnumerable<int> ecnNumbers = new HashSet<int>();
|
||||
private IEnumerable<int> caNumbers = new HashSet<int>();
|
||||
private IEnumerable<int> mrbNumbers = new HashSet<int>();
|
||||
|
||||
private bool myApprovalsProcessing = false;
|
||||
private bool myMrbsProcessing = false;
|
||||
|
||||
private string mrbSearchString = "";
|
||||
|
||||
protected async override Task OnParametersSetAsync() {
|
||||
try {
|
||||
if (stateProvider.CurrentUser is not null) {
|
||||
myApprovalsProcessing = true;
|
||||
approvalList = (await approvalService.GetApprovalsForUserId(stateProvider.CurrentUser.UserID, true))
|
||||
.Where(a => a.CompletedDate > DateTime.Now && a.ItemStatus == 0)
|
||||
.ToList()
|
||||
.OrderByDescending(x => x.AssignedDate);
|
||||
myApprovalsProcessing = false;
|
||||
|
||||
myMrbsProcessing = true;
|
||||
myMRBs = (await mrbService.GetAllMRBs(false)).Where(m => m.OriginatorID == stateProvider.CurrentUser.UserID)
|
||||
.ToList()
|
||||
.OrderByDescending(x => x.SubmittedDate);
|
||||
myMrbsProcessing = false;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
myMrbsProcessing = false;
|
||||
snackbar.Add($"Unable to load the dashboard, because {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task FollowLink(int issueId) {
|
||||
HashSet<Task> tasks = new();
|
||||
|
||||
bool isEcn = false;
|
||||
bool isCa = false;
|
||||
bool isMrb = false;
|
||||
if (ecnNumbers.Contains(issueId))
|
||||
isEcn = true;
|
||||
if (caNumbers.Contains(issueId))
|
||||
isCa = true;
|
||||
if (mrbNumbers.Contains(issueId))
|
||||
isMrb = true;
|
||||
|
||||
if (!isEcn && !isCa && !isMrb) {
|
||||
Task<bool> isEcnTask = ecnService.ECNNumberIsValid(issueId);
|
||||
tasks.Add(isEcnTask);
|
||||
|
||||
Task<bool> isCaTask = caService.CANumberIsValid(issueId);
|
||||
tasks.Add(isCaTask);
|
||||
|
||||
Task<bool> isMrbTask = mrbService.NumberIsValid(issueId);
|
||||
tasks.Add(isMrbTask);
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
if (isEcnTask.Result) {
|
||||
isEcn = true;
|
||||
} else if (isCaTask.Result) {
|
||||
isCa = true;
|
||||
} else if (isMrbTask.Result) {
|
||||
isMrb = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEcn) await GoToExternal($"{Configuration["OldFabApprovalUrl"]}/ECN/Edit?IssueID={issueId}", "");
|
||||
if (isCa) await GoToExternal($"{Configuration["OldFabApprovalUrl"]}/CorrectiveAction/Edit?IssueID={issueId}", "");
|
||||
if (isMrb) GoTo($"mrb/{issueId}");
|
||||
}
|
||||
|
||||
private void GoTo(string page) {
|
||||
cache.Set("redirectUrl", page);
|
||||
navigationManager.NavigateTo("/" + page);
|
||||
}
|
||||
|
||||
private async Task GoToExternal(string url, string content) {
|
||||
IJSObjectReference windowModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/OpenInNewWindow.js");
|
||||
await windowModule.InvokeAsync<object>("OpenInNewWindow", url, content);
|
||||
}
|
||||
|
||||
private bool FilterFuncForMRBTable(MRB mrb) => MRBFilterFunc(mrb, mrbSearchString);
|
||||
|
||||
private bool MRBFilterFunc(MRB mrb, string searchString) {
|
||||
if (string.IsNullOrWhiteSpace(searchString))
|
||||
return true;
|
||||
if (mrb.Title.ToLower().Contains(searchString.Trim().ToLower()))
|
||||
return true;
|
||||
if (mrb.MRBNumber.ToString().Contains(searchString.Trim()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
113
MesaFabApproval.Client/Pages/Login.razor
Normal file
113
MesaFabApproval.Client/Pages/Login.razor
Normal file
@ -0,0 +1,113 @@
|
||||
@page "/login"
|
||||
@page "/login/{redirectUrl}"
|
||||
@page "/login/{redirectUrl}/{redirectUrlSub}"
|
||||
@attribute [AllowAnonymous]
|
||||
@inject MesaFabApprovalAuthStateProvider authStateProvider
|
||||
@inject NavigationManager navManager
|
||||
@inject ISnackbar snackbar
|
||||
|
||||
<MudPaper Class="p-2 m-2">
|
||||
<MudText Typo="Typo.h3" Align="Align.Center">Login</MudText>
|
||||
</MudPaper>
|
||||
|
||||
<MudPaper Class="p-2 m-2">
|
||||
<MudForm @bind-IsValid="@success" @bind-Errors="@errors">
|
||||
<MudTextField T="string"
|
||||
Label="Windows Username"
|
||||
Required="true"
|
||||
RequiredError="Username is required!"
|
||||
Variant="Variant.Outlined"
|
||||
@bind-Value=username
|
||||
Class="m-1"
|
||||
Immediate="true"
|
||||
AutoFocus
|
||||
OnKeyDown=SubmitIfEnter />
|
||||
<MudTextField T="string"
|
||||
Label="Windows Password"
|
||||
Required="true"
|
||||
RequiredError="Password is required!"
|
||||
Variant="Variant.Outlined"
|
||||
@bind-Value=password
|
||||
InputType="InputType.Password"
|
||||
Class="m-1"
|
||||
Immediate="true"
|
||||
OnKeyDown=SubmitIfEnter />
|
||||
<MudButton
|
||||
Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
Disabled="@(!success)"
|
||||
Class="m-1"
|
||||
OnClick=SubmitLogin >
|
||||
@if (processing) {
|
||||
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
|
||||
<MudText>Processing</MudText>
|
||||
} else {
|
||||
<MudText>Log In</MudText>
|
||||
}
|
||||
</MudButton>
|
||||
<MudDivider />
|
||||
@* <MudButton
|
||||
Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
Class="m-1"
|
||||
OnClick="LoginLocal" >
|
||||
@if (processingLocal) {
|
||||
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
|
||||
<MudText>Processing</MudText>
|
||||
} else {
|
||||
<MudText>Log In (SSO)</MudText>
|
||||
}
|
||||
</MudButton> *@
|
||||
</MudForm>
|
||||
</MudPaper>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string? redirectUrl { get; set; }
|
||||
[Parameter]
|
||||
public string? redirectUrlSub { get; set; }
|
||||
private bool success;
|
||||
private bool processing = false;
|
||||
private bool processingLocal = false;
|
||||
private string[] errors = { };
|
||||
private string? username;
|
||||
private string? password;
|
||||
|
||||
private async Task SubmitLogin() {
|
||||
processing = true;
|
||||
if (string.IsNullOrWhiteSpace(username)) snackbar.Add("Username is required!", Severity.Error);
|
||||
else if (string.IsNullOrWhiteSpace(password)) snackbar.Add("Password is required!", Severity.Error);
|
||||
else {
|
||||
await authStateProvider.LoginAsync(username, password);
|
||||
if (!string.IsNullOrWhiteSpace(redirectUrl) && !string.IsNullOrWhiteSpace(redirectUrlSub)) {
|
||||
navManager.NavigateTo($"{redirectUrl}/{redirectUrlSub}");
|
||||
} else if (!string.IsNullOrWhiteSpace(redirectUrl)) {
|
||||
navManager.NavigateTo(redirectUrl);
|
||||
} else {
|
||||
navManager.NavigateTo("dashboard");
|
||||
}
|
||||
}
|
||||
processing = false;
|
||||
}
|
||||
|
||||
private async Task SubmitIfEnter(KeyboardEventArgs e) {
|
||||
if (e.Key == "Enter" && success) {
|
||||
SubmitLogin();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoginLocal() {
|
||||
processingLocal = true;
|
||||
|
||||
await authStateProvider.LoginLocal();
|
||||
if (!string.IsNullOrWhiteSpace(redirectUrl) && !string.IsNullOrWhiteSpace(redirectUrlSub)) {
|
||||
navManager.NavigateTo($"{redirectUrl}/{redirectUrlSub}");
|
||||
} else if (!string.IsNullOrWhiteSpace(redirectUrl)) {
|
||||
navManager.NavigateTo(redirectUrl);
|
||||
} else {
|
||||
navManager.NavigateTo("dashboard");
|
||||
}
|
||||
|
||||
processingLocal = false;
|
||||
}
|
||||
}
|
119
MesaFabApproval.Client/Pages/MRBAll.razor
Normal file
119
MesaFabApproval.Client/Pages/MRBAll.razor
Normal file
@ -0,0 +1,119 @@
|
||||
@page "/mrb/all"
|
||||
@using System.Globalization
|
||||
@inject IMRBService mrbService
|
||||
@inject ISnackbar snackbar
|
||||
@inject IMemoryCache cache
|
||||
@inject NavigationManager navigationManager
|
||||
|
||||
<PageTitle>MRB</PageTitle>
|
||||
|
||||
<MudPaper Class="p-2 m-2">
|
||||
<MudText Typo="Typo.h3" Align="Align.Center">MRB List</MudText>
|
||||
</MudPaper>
|
||||
|
||||
@if (allMrbs is not null && allMrbs.Count() > 0) {
|
||||
<MudTable Items="@allMrbs"
|
||||
Class="m-2"
|
||||
Striped="true"
|
||||
Filter="new Func<MRB,bool>(FilterFuncForTable)"
|
||||
SortLabel="Sort By"
|
||||
Hover="true">
|
||||
<ToolBarContent>
|
||||
<MudSpacer />
|
||||
<MudTextField @bind-Value="searchString"
|
||||
Placeholder="Search"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Material.Filled.Search"
|
||||
IconSize="Size.Medium"
|
||||
Class="mt-0" />
|
||||
</ToolBarContent>
|
||||
<HeaderContent>
|
||||
<MudTh>
|
||||
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<MRB,object>(x=>x.MRBNumber)">
|
||||
MRB#
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.Title)">
|
||||
Title
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.OriginatorName)">
|
||||
Originator
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.SubmittedDate)">
|
||||
Submitted Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.ApprovalDate)">
|
||||
Approval Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.CloseDate)">
|
||||
Closed Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="MRB#">
|
||||
<MudLink OnClick="@(() => GoTo($"mrb/{context.MRBNumber}"))">@context.MRBNumber</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Title">@context.Title</MudTd>
|
||||
<MudTd DataLabel="Originator">@context.OriginatorName</MudTd>
|
||||
<MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.SubmittedDate)</MudTd>
|
||||
<MudTd DataLabel="Approval Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ApprovalDate)</MudTd>
|
||||
<MudTd DataLabel="Closed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CloseDate)</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager />
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
}
|
||||
|
||||
<MudOverlay @bind-Visible=inProcess DarkBackground="true" AutoClose="false">
|
||||
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
|
||||
</MudOverlay>
|
||||
|
||||
@code {
|
||||
private bool inProcess = false;
|
||||
private string searchString = "";
|
||||
private IEnumerable<MRB> allMrbs = new List<MRB>();
|
||||
|
||||
protected override async Task OnParametersSetAsync() {
|
||||
inProcess = true;
|
||||
try {
|
||||
if (mrbService is null) {
|
||||
throw new Exception("MRB service not injected!");
|
||||
} else {
|
||||
allMrbs = await mrbService.GetAllMRBs(false);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
inProcess = false;
|
||||
}
|
||||
|
||||
private bool FilterFuncForTable(MRB mrb) => FilterFunc(mrb, searchString);
|
||||
|
||||
private bool FilterFunc(MRB mrb, string searchString) {
|
||||
if (string.IsNullOrWhiteSpace(searchString))
|
||||
return true;
|
||||
if (mrb.Title.ToLower().Contains(searchString.Trim().ToLower()))
|
||||
return true;
|
||||
if (mrb.OriginatorName.ToLower().Contains(searchString.Trim().ToLower()))
|
||||
return true;
|
||||
if (mrb.MRBNumber.ToString().Contains(searchString.Trim()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void GoTo(string page) {
|
||||
cache.Set("redirectUrl", page);
|
||||
navigationManager.NavigateTo(page);
|
||||
}
|
||||
}
|
1702
MesaFabApproval.Client/Pages/MRBSingle.razor
Normal file
1702
MesaFabApproval.Client/Pages/MRBSingle.razor
Normal file
File diff suppressed because it is too large
Load Diff
119
MesaFabApproval.Client/Pages/PCRBAll.razor
Normal file
119
MesaFabApproval.Client/Pages/PCRBAll.razor
Normal file
@ -0,0 +1,119 @@
|
||||
@page "/pcrb/all"
|
||||
@using System.Globalization
|
||||
@inject IPCRBService pcrbService
|
||||
@inject ISnackbar snackbar
|
||||
@inject IMemoryCache cache
|
||||
@inject NavigationManager navigationManager
|
||||
|
||||
<PageTitle>PCRB</PageTitle>
|
||||
|
||||
<MudPaper Class="p-2 m-2">
|
||||
<MudText Typo="Typo.h3" Align="Align.Center">PCRB List</MudText>
|
||||
</MudPaper>
|
||||
|
||||
@if (allPCRBs is not null && allPCRBs.Count() > 0) {
|
||||
<MudTable Items="@allPCRBs"
|
||||
Class="m-2"
|
||||
Striped="true"
|
||||
Filter="new Func<PCRB,bool>(FilterFuncForTable)"
|
||||
SortLabel="Sort By"
|
||||
Hover="true">
|
||||
<ToolBarContent>
|
||||
<MudSpacer />
|
||||
<MudTextField @bind-Value="searchString"
|
||||
Placeholder="Search"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Material.Filled.Search"
|
||||
IconSize="Size.Medium"
|
||||
Class="mt-0" />
|
||||
</ToolBarContent>
|
||||
<HeaderContent>
|
||||
<MudTh>
|
||||
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<PCRB,object>(x=>x.PlanNumber)">
|
||||
Plan#
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.Title)">
|
||||
Title
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.OwnerName)">
|
||||
Owner
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.InsertTimeStamp)">
|
||||
Submitted Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.LastUpdateDate)">
|
||||
Last Updated
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.ClosedDate)">
|
||||
Closed Date
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Plan#">
|
||||
<MudLink OnClick="@(() => GoTo($"pcrb/{context.PlanNumber.ToString()}"))">@context.PlanNumber</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Title">@context.Title</MudTd>
|
||||
<MudTd DataLabel="Owner">@context.OwnerName</MudTd>
|
||||
<MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.InsertTimeStamp)</MudTd>
|
||||
<MudTd DataLabel="Last Updated">@DateTimeUtilities.GetDateAsStringMinDefault(context.LastUpdateDate)</MudTd>
|
||||
<MudTd DataLabel="Closed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ClosedDate)</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager />
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
}
|
||||
|
||||
<MudOverlay @bind-Visible=inProcess DarkBackground="true" AutoClose="false">
|
||||
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
|
||||
</MudOverlay>
|
||||
|
||||
@code {
|
||||
private bool inProcess = false;
|
||||
private string searchString = "";
|
||||
private IEnumerable<PCRB> allPCRBs = new List<PCRB>();
|
||||
|
||||
protected override async Task OnParametersSetAsync() {
|
||||
inProcess = true;
|
||||
try {
|
||||
if (pcrbService is null) {
|
||||
throw new Exception("PCRB service not injected!");
|
||||
} else {
|
||||
allPCRBs = await pcrbService.GetAllPCRBs(false);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
inProcess = false;
|
||||
}
|
||||
|
||||
private bool FilterFuncForTable(PCRB pcrb) => FilterFunc(pcrb, searchString);
|
||||
|
||||
private bool FilterFunc(PCRB pcrb, string searchString) {
|
||||
if (string.IsNullOrWhiteSpace(searchString))
|
||||
return true;
|
||||
if (pcrb.Title.ToLower().Contains(searchString.Trim().ToLower()))
|
||||
return true;
|
||||
if (pcrb.OwnerName.ToLower().Contains(searchString.Trim().ToLower()))
|
||||
return true;
|
||||
if (pcrb.PlanNumber.ToString().Contains(searchString.Trim()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void GoTo(string page) {
|
||||
cache.Set("redirectUrl", page);
|
||||
navigationManager.NavigateTo(page);
|
||||
}
|
||||
}
|
263
MesaFabApproval.Client/Pages/PCRBSingle.razor
Normal file
263
MesaFabApproval.Client/Pages/PCRBSingle.razor
Normal file
@ -0,0 +1,263 @@
|
||||
@page "/pcrb/{planNumber}"
|
||||
@page "/pcrb/new"
|
||||
@using System.Text
|
||||
|
||||
@inject ISnackbar snackbar
|
||||
@inject IPCRBService pcrbService
|
||||
@inject IUserService userService
|
||||
@inject IMemoryCache cache
|
||||
@inject NavigationManager navigationManager
|
||||
@inject MesaFabApprovalAuthStateProvider authStateProvider
|
||||
|
||||
<PageTitle>PCRB @planNumber</PageTitle>
|
||||
|
||||
<MudPaper Class="p-2 m-2 d-flex flex-row justify-content-between">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.ChevronLeft"
|
||||
Variant="Variant.Outlined"
|
||||
Color="Color.Dark"
|
||||
OnClick="@ReturnToAllPcrbs"
|
||||
Size="Size.Large" />
|
||||
<MudText Typo="Typo.h3" Align="Align.Center">PCRB @planNumber</MudText>
|
||||
<MudPaper Height="100%" Width="0.1%" Square="true" />
|
||||
</MudPaper>
|
||||
|
||||
@if (pcrb is not null) {
|
||||
<MudTimeline Class="mt-2 pt-2" TimelineOrientation="TimelineOrientation.Horizontal"
|
||||
TimelinePosition="TimelinePosition.Bottom">
|
||||
@for (int i = 0; i < PCRB.Stages.Length; i++) {
|
||||
Color color;
|
||||
if (pcrb.CurrentStep > i || pcrb.CurrentStep == (PCRB.Stages.Length - 1)) {
|
||||
color = Color.Success;
|
||||
} else if (pcrb.CurrentStep == i) {
|
||||
color = Color.Info;
|
||||
} else {
|
||||
color = Color.Dark;
|
||||
}
|
||||
|
||||
string stageName = PCRB.Stages[i];
|
||||
|
||||
<MudTimelineItem Color="@color" Variant="Variant.Filled">
|
||||
<MudText Align="Align.Center" Color="@color">@stageName</MudText>
|
||||
</MudTimelineItem>
|
||||
}
|
||||
</MudTimeline>
|
||||
|
||||
bool pcrbIsSubmitted = pcrb.InsertTimeStamp > DateTimeUtilities.MIN_DT;
|
||||
bool userIsOriginator = pcrb.OwnerID == authStateProvider.CurrentUser?.UserID;
|
||||
bool userIsAdmin = authStateProvider.CurrentUser is null ? false : authStateProvider.CurrentUser.IsAdmin;
|
||||
|
||||
<MudPaper Outlined="true"
|
||||
Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-center"
|
||||
Elevation="10">
|
||||
@if (!pcrbIsSubmitted && !string.IsNullOrWhiteSpace(pcrb.Title)) {
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
OnClick=SavePCRB>
|
||||
@if (saveInProcess) {
|
||||
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
|
||||
<MudText>Processing</MudText>
|
||||
} else {
|
||||
<MudText>Save</MudText>
|
||||
}
|
||||
</MudButton>
|
||||
}
|
||||
</MudPaper>
|
||||
|
||||
@if (!pcrbIsSubmitted && GetIncompleteFields().Count() > 0) {
|
||||
IEnumerable<string> incompleteFields = GetIncompleteFields();
|
||||
StringBuilder errorBuilder = new();
|
||||
errorBuilder.Append($"Incomplete fields: {incompleteFields.First()}");
|
||||
for (int i = 1; i < incompleteFields.Count(); i++) {
|
||||
errorBuilder.Append($", {incompleteFields.ElementAt(i)}");
|
||||
}
|
||||
|
||||
<MudPaper Outlined Class="p-2 m-2">
|
||||
<MudText Align="Align.Center" Color="Color.Secondary" Typo="Typo.h6">
|
||||
@errorBuilder.ToString()
|
||||
</MudText>
|
||||
</MudPaper>
|
||||
}
|
||||
|
||||
<MudPaper Outlined="true"
|
||||
Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-start"
|
||||
Elevation="10">
|
||||
<MudTextField @bind-Value=pcrb.PlanNumber
|
||||
Text="@pcrb.PlanNumber.ToString()"
|
||||
T="int"
|
||||
Disabled="true"
|
||||
Label="Plan#"
|
||||
Required
|
||||
Variant="Variant.Outlined" />
|
||||
<MudTextField @bind-Value=pcrb.Title
|
||||
Text="@pcrb.Title"
|
||||
Disabled="@(pcrbIsSubmitted)"
|
||||
T="string"
|
||||
AutoGrow
|
||||
AutoFocus
|
||||
Immediate
|
||||
Clearable
|
||||
Required
|
||||
Variant="Variant.Outlined"
|
||||
Label="Title" />
|
||||
<MudSelect T="User"
|
||||
Label="Originator"
|
||||
Variant="Variant.Outlined"
|
||||
Required
|
||||
Clearable
|
||||
AnchorOrigin="Origin.BottomCenter"
|
||||
ToStringFunc="@UserToNameConverter"
|
||||
Disabled=@(pcrbIsSubmitted)
|
||||
@bind-Value=@selectedOwner>
|
||||
@foreach (User user in allActiveUsers.OrderBy(u => u.LastName)) {
|
||||
<MudSelectItem T="User" Value="@(user)" />
|
||||
}
|
||||
</MudSelect>
|
||||
<MudSelect T="string"
|
||||
Variant="Variant.Outlined"
|
||||
Required
|
||||
Clearable
|
||||
AnchorOrigin="Origin.BottomCenter"
|
||||
Disabled="@pcrbIsSubmitted"
|
||||
@bind-Value="@pcrb.ChangeLevel"
|
||||
Text="@pcrb.ChangeLevel"
|
||||
Label="Change Level">
|
||||
<MudSelectItem Value="@("Global")" />
|
||||
<MudSelectItem Value="@("Other Site + Mesa")" />
|
||||
<MudSelectItem Value="@("Mesa")" />
|
||||
</MudSelect>
|
||||
<MudCheckBox Disabled="@pcrbIsSubmitted"
|
||||
Color="Color.Tertiary"
|
||||
@bind-Value=pcrb.IsITAR
|
||||
Label="Export Controlled"
|
||||
LabelPosition="LabelPosition.Start" />
|
||||
<MudTextField Disabled="true"
|
||||
T="string"
|
||||
Value="@DateTimeUtilities.GetDateAsStringMinDefault(pcrb.InsertTimeStamp)"
|
||||
Label="Submit Date"
|
||||
Variant="Variant.Outlined" />
|
||||
<MudTextField Disabled="true"
|
||||
T="string"
|
||||
Value="@DateTimeUtilities.GetDateAsStringMinDefault(pcrb.LastUpdateDate)"
|
||||
Label="Last Update"
|
||||
Variant="Variant.Outlined" />
|
||||
<MudTextField @bind-Value=pcrb.ChangeDescription
|
||||
Text="@pcrb.ChangeDescription"
|
||||
Disabled="@(pcrbIsSubmitted)"
|
||||
T="string"
|
||||
AutoGrow
|
||||
Immediate
|
||||
Clearable
|
||||
Required
|
||||
Variant="Variant.Outlined"
|
||||
Label="Description" />
|
||||
<MudTextField @bind-Value=pcrb.ReasonForChange
|
||||
Text="@pcrb.ReasonForChange"
|
||||
Disabled="@(pcrbIsSubmitted)"
|
||||
T="string"
|
||||
AutoGrow
|
||||
Immediate
|
||||
Clearable
|
||||
Required
|
||||
Variant="Variant.Outlined"
|
||||
Label="Reason For Change" />
|
||||
</MudPaper>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string planNumber { get; set; } = "";
|
||||
|
||||
private int planNumberInt = 0;
|
||||
private PCRB pcrb = null;
|
||||
|
||||
private IEnumerable<User> allActiveUsers = new List<User>();
|
||||
private User selectedOwner = null;
|
||||
|
||||
private bool processing = false;
|
||||
private bool saveInProcess = false;
|
||||
|
||||
protected override async Task OnParametersSetAsync() {
|
||||
processing = true;
|
||||
try {
|
||||
allActiveUsers = await userService.GetAllActiveUsers();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(planNumber) && Int32.TryParse(planNumber, out planNumberInt)) {
|
||||
pcrb = await pcrbService.GetPCRBByPlanNumber(planNumberInt, false);
|
||||
if (pcrb.OwnerID > 0) selectedOwner = await userService.GetUserByUserId(pcrb.OwnerID);
|
||||
} else {
|
||||
int ownerID = 0;
|
||||
string ownerName = string.Empty;
|
||||
if (authStateProvider.CurrentUser is not null) {
|
||||
selectedOwner = authStateProvider.CurrentUser;
|
||||
ownerID = authStateProvider.CurrentUser.UserID;
|
||||
ownerName = authStateProvider.CurrentUser.GetFullName();
|
||||
}
|
||||
|
||||
pcrb = new() {
|
||||
OwnerID = ownerID,
|
||||
OwnerName = ownerName,
|
||||
CurrentStep = 0
|
||||
};
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
processing = false;
|
||||
}
|
||||
|
||||
private Func<User, string> UserToNameConverter = u => u is null ? string.Empty : u.GetFullName();
|
||||
|
||||
private void ReturnToAllPcrbs() {
|
||||
cache.Set("redirectUrl", $"pcrb/all");
|
||||
navigationManager.NavigateTo("pcrb/all");
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetIncompleteFields() {
|
||||
List<string> incompleteFields = new();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(pcrb.Title))
|
||||
incompleteFields.Add("Title");
|
||||
if (selectedOwner is null || pcrb.OwnerID <= 0 || string.IsNullOrWhiteSpace(pcrb.OwnerName))
|
||||
incompleteFields.Add("Originator");
|
||||
if (string.IsNullOrWhiteSpace(pcrb.ChangeLevel))
|
||||
incompleteFields.Add("Change Level");
|
||||
if (string.IsNullOrWhiteSpace(pcrb.ChangeDescription))
|
||||
incompleteFields.Add("Description");
|
||||
if (string.IsNullOrWhiteSpace(pcrb.ReasonForChange))
|
||||
incompleteFields.Add("Reason For Change");
|
||||
|
||||
return incompleteFields;
|
||||
}
|
||||
|
||||
private async void SavePCRB() {
|
||||
saveInProcess = true;
|
||||
try {
|
||||
if (pcrb is null) throw new Exception("PCRB cannot be null");
|
||||
|
||||
int initialPlanNumber = pcrb.PlanNumber;
|
||||
|
||||
pcrb.OwnerID = selectedOwner.UserID;
|
||||
pcrb.OwnerName = selectedOwner.GetFullName();
|
||||
|
||||
if (initialPlanNumber <= 0) {
|
||||
await pcrbService.CreateNewPCRB(pcrb);
|
||||
} else {
|
||||
await pcrbService.UpdatePCRB(pcrb);
|
||||
}
|
||||
|
||||
pcrb = await pcrbService.GetPCRBByTitle(pcrb.Title, true);
|
||||
|
||||
cache.Set("redirectUrl", $"pcrb/{pcrb.PlanNumber}");
|
||||
|
||||
saveInProcess = false;
|
||||
StateHasChanged();
|
||||
snackbar.Add($"PCRB {pcrb.PlanNumber} successfully saved", Severity.Success);
|
||||
|
||||
if (initialPlanNumber <= 0)
|
||||
navigationManager.NavigateTo($"pcrb/{pcrb.PlanNumber}");
|
||||
} catch (Exception ex) {
|
||||
saveInProcess = false;
|
||||
snackbar.Add($"Unable to save PCRB, because {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user