From ced146fc4231619eee9f8b2c924ac643eb325c75 Mon Sep 17 00:00:00 2001 From: Greg Finzer Date: Wed, 28 Aug 2024 17:17:04 -0400 Subject: [PATCH 1/2] New Add/Edit Volunteer, Unique Volunteer Email, Fix Global Toast, Email Validation Attribute, Phone Validation Attribute --- .../AdminTasks/AddEditVolunteer.razor | 131 ++ .../AdminTasks/AddEditVolunteer.razor.cs | 136 +++ .../Manage/ManageVolunteers.razor | 151 +++ .../Manage/ManageVolunteers.razor.cs} | 120 +- .../Manage/ManageVolunteers.razor.css | 41 + .../Administration/Manage/Volunteers.razor | 7 - .../Administration/Manage/Volunteers.razor.cs | 16 - .../Components/Pages/BedRequest.razor.cs | 17 +- .../Components/Pages/ContactUs.razor.cs | 17 +- .../Components/Pages/VolunteerSignUp.razor.cs | 17 +- BedBrigade.Client/Components/Routes.razor | 1 + .../Components/ToastComponent.razor | 23 +- BedBrigade.Client/Components/ToastService.cs | 4 +- .../Components/VolunteerGrid.razor | 260 ---- .../Components/VolunteerGrid.razor.css | 43 - .../Logic/CustomEmailValidation.cs | 20 + .../Logic/CustomPhoneValidation.cs | 21 + BedBrigade.Common/Logic/Validation.cs | 2 +- BedBrigade.Common/Models/BedRequest.cs | 4 +- BedBrigade.Common/Models/ContactUs.cs | 7 +- BedBrigade.Common/Models/Donation.cs | 4 +- BedBrigade.Common/Models/User.cs | 3 + BedBrigade.Common/Models/Volunteer.cs | 7 +- BedBrigade.Data/BedBrigade.Data.csproj | 1 + BedBrigade.Data/Data/DataContext.cs | 8 +- BedBrigade.Data/Data/Seeding/Seed.cs | 27 +- ...0828153512_VolunteerEmailIndex.Designer.cs | 1070 +++++++++++++++++ .../20240828153512_VolunteerEmailIndex.cs | 28 + .../Migrations/DataContextModelSnapshot.cs | 73 +- 29 files changed, 1736 insertions(+), 523 deletions(-) create mode 100644 BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor create mode 100644 BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor.cs create mode 100644 BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor rename BedBrigade.Client/Components/{VolunteerGrid.razor.cs => Pages/Administration/Manage/ManageVolunteers.razor.cs} (84%) create mode 100644 BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.css delete mode 100644 BedBrigade.Client/Components/Pages/Administration/Manage/Volunteers.razor delete mode 100644 BedBrigade.Client/Components/Pages/Administration/Manage/Volunteers.razor.cs delete mode 100644 BedBrigade.Client/Components/VolunteerGrid.razor delete mode 100644 BedBrigade.Client/Components/VolunteerGrid.razor.css create mode 100644 BedBrigade.Common/Logic/CustomEmailValidation.cs create mode 100644 BedBrigade.Common/Logic/CustomPhoneValidation.cs create mode 100644 BedBrigade.Data/Migrations/20240828153512_VolunteerEmailIndex.Designer.cs create mode 100644 BedBrigade.Data/Migrations/20240828153512_VolunteerEmailIndex.cs diff --git a/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor b/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor new file mode 100644 index 00000000..eb59999d --- /dev/null +++ b/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor @@ -0,0 +1,131 @@ +@layout Layout.AdminLayout +@page "/administration/admintasks/addeditvolunteer/{LocationId:int}" +@page "/administration/admintasks/addeditvolunteer/{LocationId:int}/{VolunteerId:int}" +@using BedBrigade.Common.Models +@attribute [Authorize(Roles = RoleNames.CanManageVolunteers)] + +@if (Model == null) +{ + +} +else +{ +
+ @if (VolunteerId == null) + { +

Add Volunteer

+ } + else + { +

Edit Volunteer

+ } + + + + +
+ +
+
+
+ Volunteer Information +
+
+ +
+ + + + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+
+ + +
+
+
+
+ +
+
+
+ Delivery Vehicle +
+
+
+
+ @BootstrapHelper.GetBootstrapMessage("help", "Do you have a vehicle that can carry a bed and mattress?", "", false, "compact") +
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+ + @if (!string.IsNullOrEmpty(ErrorMessage)) + { + + } + + + +
+
+} diff --git a/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor.cs b/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor.cs new file mode 100644 index 00000000..80917b90 --- /dev/null +++ b/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor.cs @@ -0,0 +1,136 @@ +using System.Drawing; +using BedBrigade.Data.Services; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using System.Security.Claims; +using BedBrigade.Common.Constants; +using BedBrigade.Common.Logic; +using BedBrigade.Common.Models; +using Serilog; + +namespace BedBrigade.Client.Components.Pages.Administration.AdminTasks +{ + public partial class AddEditVolunteer : ComponentBase + { + [Parameter] public int? VolunteerId { get; set; } + [Parameter] public int? LocationId { get; set; } + + [Inject] private NavigationManager? _navigationManager { get; set; } + [Inject] private ToastService _toastService { get; set; } + [Inject] private IVolunteerDataService _volunteerDataService { get; set; } + [Inject] private ILocationDataService? _svcLocation { get; set; } + [Inject] private AuthenticationStateProvider? _authState { get; set; } + private ClaimsPrincipal? Identity { get; set; } + public string ErrorMessage { get; set; } + public Volunteer? Model { get; set; } + private const string ErrorTitle = "Error"; + protected List? Locations { get; private set; } + public bool CanSetLocation = false; + protected Dictionary DescriptionHtmlAttribute { get; set; } = new Dictionary() + { + { "rows", "5" }, + }; + + protected override async Task OnInitializedAsync() + { + var authState = await _authState.GetAuthenticationStateAsync(); + Identity = authState.User; + + var userName = Identity.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? Defaults.DefaultUserNameAndEmail; + Log.Information($"{userName} went to the Add/Edit Volunteer Page"); + + if (Identity.IsInRole(RoleNames.NationalAdmin)) + { + CanSetLocation = true; + } + + await LoadLocations(); + await LoadVolunteer(); + } + + private async Task LoadLocations() + { + var result = await _svcLocation!.GetAllAsync(); + + if (result.Success) // + { + Locations = result.Data; + } + else + { + _toastService.Error(ErrorTitle, result.Message); + } + } + + private async Task LoadVolunteer() + { + if (VolunteerId != null) + { + var result = await _volunteerDataService.GetByIdAsync(VolunteerId.Value); + + if (result.Success) + { + Model = result.Data; + Model.Phone = StringUtil.ExtractDigits(Model.Phone); + } + else + { + _toastService.Error(ErrorTitle, result.Message); + } + } + else + { + Model = new Volunteer(); + Model.LocationId = LocationId.Value; + } + } + + private async Task HandleValidSubmit() + { + if (await SaveVolunteer()) + { + _navigationManager.NavigateTo("/administration/manage/volunteers"); + } + } + + private async Task SaveVolunteer() + { + ErrorMessage = string.Empty; + + if (VolunteerId != null) + { + var updateResult = await _volunteerDataService.UpdateAsync(Model!); + if (updateResult.Success) + { + _toastService.Success("Success", "Volunteer updated successfully"); + return true; + } + + _toastService.Error(ErrorTitle, updateResult.Message); + return false; + } + + var existingVolunteerResult = await _volunteerDataService.GetByEmail(Model.Email); + if (existingVolunteerResult.Success) + { + ErrorMessage = "A volunteer with this email already exists."; + return false; + } + + var result = await _volunteerDataService.CreateAsync(Model!); + if (result.Success) + { + _toastService.Success("Success", "Volunteer created successfully"); + return true; + } + + _toastService.Error(ErrorTitle, result.Message); + return false; + } + + private void HandleCancel() + { + _navigationManager.NavigateTo("/administration/manage/volunteers"); + } + } +} diff --git a/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor b/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor new file mode 100644 index 00000000..e008f116 --- /dev/null +++ b/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor @@ -0,0 +1,151 @@ +@layout Layout.AdminLayout +@page "/administration/manage/volunteers" +@attribute [Authorize(Roles = RoleNames.CanManageVolunteers)] + +@using FilterType = Syncfusion.Blazor.Grids.FilterType +@using EditMode = Syncfusion.Blazor.Grids.EditMode +@using WrapMode = Syncfusion.Blazor.Grids.WrapMode +@using BedBrigade.Common.Models + + +@if (Volunteers == null) +{ + +} +else +{ +
 
+
+
+

Manage Volunteers

+
+
+  @userRole  + @if (isLocationAdmin) + { + [Location: @userLocationName] + } +
@userName +
+
+ +
+
+
+ + + + + + + @RecordText + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+} + + + + +@ErrorMessage + +@if (NoPaging) +{ + +} + + diff --git a/BedBrigade.Client/Components/VolunteerGrid.razor.cs b/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.cs similarity index 84% rename from BedBrigade.Client/Components/VolunteerGrid.razor.cs rename to BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.cs index 52f229b6..cde424d8 100644 --- a/BedBrigade.Client/Components/VolunteerGrid.razor.cs +++ b/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.cs @@ -1,18 +1,10 @@ -using BedBrigade.Client.Services; -using BedBrigade.Data.Migrations; -using BedBrigade.Data.Services; +using BedBrigade.Data.Services; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using Syncfusion.Blazor.Grids; using Syncfusion.Blazor.Notifications; -using Syncfusion.Blazor.Notifications.Internal; -using Syncfusion.Blazor.RichTextEditor; using System.Security.Claims; - using Action = Syncfusion.Blazor.Grids.Action; -using System.Diagnostics; -using Syncfusion.Blazor; -using System.Threading; using Serilog; using BedBrigade.Common.Logic; using BedBrigade.Common.Constants; @@ -20,15 +12,15 @@ using BedBrigade.Common.Enums; using BedBrigade.Common.Models; -namespace BedBrigade.Client.Components +namespace BedBrigade.Client.Components.Pages.Administration.Manage { - public partial class VolunteerGrid : ComponentBase + public partial class ManageVolunteers : ComponentBase { [Inject] private IVolunteerDataService? _svcVolunteer { get; set; } - [Inject] private IUserDataService? _svcUser { get; set; } + [Inject] private IUserDataService? _svcUser { get; set; } [Inject] private ILocationDataService? _svcLocation { get; set; } [Inject] private AuthenticationStateProvider? _authState { get; set; } - + [Inject] private NavigationManager? _navigationManager { get; set; } [Parameter] public string? Id { get; set; } private const string LastPage = "LastPage"; @@ -36,10 +28,9 @@ public partial class VolunteerGrid : ComponentBase private const string NextPage = "NextPage"; private const string FirstPage = "First"; private ClaimsPrincipal? Identity { get; set; } - protected List? Volunteers { get; set; } - protected Volunteer Volunteer { get; set; } = new Volunteer(); + protected List? Volunteers { get; set; } public List? lstVehicleTypes { get; private set; } - protected List? Locations { get; private set; } + protected List? Locations { get; private set; } protected SfGrid? Grid { get; set; } protected List? ToolBar; protected List? ContextMenu; @@ -60,12 +51,12 @@ public partial class VolunteerGrid : ComponentBase protected string? ToastTitle { get; set; } protected string? ToastContent { get; set; } protected int ToastTimeout { get; set; } = 3000; - + protected string? RecordText { get; set; } = "Loading Volunteers ..."; protected string? Hide { get; private set; } = "true"; public bool NoPaging { get; private set; } public bool OnlyRead { get; private set; } = false; - private string DisplayEmailMessage = "none"; + private bool ShouldDisplayEmailMessage = false; public bool enabledLocationSelector { get; set; } = true; protected DialogSettings DialogParams = new DialogSettings { Width = "900px", MinHeight = "70%" }; @@ -77,7 +68,7 @@ protected override async Task OnInitializedAsync() await LoadLocations(); await LoadVolunteerData(); lstVehicleTypes = EnumHelper.GetVehicleTypeItems(); - + } // Async Init @@ -85,13 +76,13 @@ private async Task LoadUserData() { var authState = await _authState!.GetAuthenticationStateAsync(); Identity = authState.User; - + userLocationId = int.Parse(Identity.Claims.FirstOrDefault(c => c.Type == "LocationId").Value); userName = Identity.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? Defaults.DefaultUserNameAndEmail; Log.Information($"{userName} went to the Manage Volunteers Page"); - if (Identity.IsInRole(RoleNames.NationalAdmin) || Identity.IsInRole(RoleNames.LocationAdmin) ) + if (Identity.IsInRole(RoleNames.NationalAdmin) || Identity.IsInRole(RoleNames.LocationAdmin)) { ToolBar = new List { "Add", "Edit", "Delete", "Print", "Pdf Export", "Excel Export", "Csv Export", "Search", "Reset" }; ContextMenu = new List { "Edit", "Delete", FirstPage, NextPage, PrevPage, LastPage, "AutoFit", "AutoFitAll", "SortAscending", "SortDescending" }; //, "Save", "Cancel", "PdfExport", "ExcelExport", "CsvExport", "FirstPage", "PrevPage", "LastPage", "NextPage" }; @@ -121,7 +112,7 @@ private async Task LoadUserData() userRole = RoleNames.LocationAuthor; isLocationAdmin = true; } - + } // Get User Data @@ -149,10 +140,10 @@ private async Task LoadVolunteerData() { var dataVolunteer = await _svcVolunteer.GetAllAsync(); // get Schedules Volunteers = new List(); - + if (dataVolunteer.Success && dataVolunteer != null) { - + if (dataVolunteer.Data.Count > 0) { Volunteers = dataVolunteer.Data.ToList(); // retrieve existing media records to temp list @@ -160,7 +151,7 @@ private async Task LoadVolunteerData() // Location Filter if (isLocationAdmin) { - Volunteers = Volunteers.FindAll(e => e.LocationId == userLocationId); // Location Filter + Volunteers = Volunteers.FindAll(e => e.LocationId == userLocationId); // Location Filter } } @@ -179,9 +170,9 @@ private async Task LoadVolunteerData() } // OnInit - - + + /// /// On destoring of the grid save its current state @@ -205,25 +196,30 @@ protected async Task OnToolBarClick(Syncfusion.Blazor.Navigations.ClickEventArgs { await Grid.ResetPersistDataAsync(); _state = await Grid.GetPersistDataAsync(); - await _svcUser.SaveGridPersistance(new Persist { GridId = (int)PersistGrid.Volunteer, UserState = _state }); - return; - } + await _svcUser.SaveGridPersistance(new Persist { GridId = (int)PersistGrid.Volunteer, UserState = _state }); } - if (args.Item.Text == "Pdf Export") + else if (args.Item.Text == "Pdf Export") { await PdfExport(); } - if (args.Item.Text == "Excel Export") + else if (args.Item.Text == "Excel Export") { await ExcelExport(); - return; } - if (args.Item.Text == "Csv Export") + else if (args.Item.Text == "Csv Export") { await CsvExportAsync(); - return; } + } + + protected async Task Edit(ActionEventArgs args) + { + var volunteer = (await Grid.GetSelectedRecordsAsync()).FirstOrDefault(); + if (volunteer != null) + { + _navigationManager.NavigateTo($"/administration/admintasks/addeditvolunteer/{userLocationId}/{volunteer.VolunteerId}"); + } } public async Task OnActionBegin(ActionEventArgs args) @@ -243,11 +239,8 @@ public async Task OnActionBegin(ActionEventArgs args) Add(); break; - case Action.Save: - await Save(args); - break; case Action.BeginEdit: - BeginEdit(); + await Edit(args); break; } @@ -270,36 +263,23 @@ private async Task Delete(ActionEventArgs args) args.Cancel = true; } ToastTimeout = 4000; - await ToastObj.ShowAsync(new ToastModel { Title = ToastTitle, Content = ToastContent, Timeout = ToastTimeout}); + await ToastObj.ShowAsync(new ToastModel { Title = ToastTitle, Content = ToastContent, Timeout = ToastTimeout }); } } private void Add() { - HeaderTitle = "Add Volunteer"; - ButtonTitle = "Add Volunteer"; - if (isLocationAdmin) - { - enabledLocationSelector = false; - } - else - { - enabledLocationSelector = true; - } + _navigationManager.NavigateTo($"/administration/admintasks/addeditvolunteer/{userLocationId}"); } - private void ValidateNewEmail() - { - DisplayEmailMessage = ""; - } private async Task Save(ActionEventArgs args) { - DisplayEmailMessage = "none"; + ShouldDisplayEmailMessage = false; Volunteer Volunteer = args.Data; - + Volunteer.Phone = Volunteer.Phone.FormatPhoneNumber(); if (Volunteer.VolunteerId != 0) { @@ -318,6 +298,17 @@ private async Task Save(ActionEventArgs args) } else { + //See if the email already exists + var existingVolunteerResult = await _svcVolunteer.GetByEmail(Volunteer.Email); + + if (existingVolunteerResult.Success && existingVolunteerResult.Data != null) + { + ShouldDisplayEmailMessage =true; + args.Cancel = true; + StateHasChanged(); + return; + } + // new Volunteer var result = await _svcVolunteer.CreateAsync(Volunteer); if (result.Success && result.Data != null) @@ -337,25 +328,10 @@ private async Task Save(ActionEventArgs args) } await Grid.Refresh(); - - } - private void BeginEdit() - { - HeaderTitle = "Update Volunteer"; - ButtonTitle = "Update"; - enabledLocationSelector = false; } - protected async Task Save(Volunteer location) - { - await Grid.EndEditAsync(); - } - protected async Task Cancel() - { - await Grid.CloseEditAsync(); - } protected void DataBound() { @@ -405,7 +381,5 @@ protected async Task CsvExportAsync() { "rows", "5" }, }; - } } - diff --git a/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.css b/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.css new file mode 100644 index 00000000..8f167d92 --- /dev/null +++ b/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.css @@ -0,0 +1,41 @@ +::deep .e-grid td.e-rowcell.e-focus.e-focused { + box-shadow: none; +} + +::deep .e-grid th.e-headercell.e-focus.e-focused { + box-shadow: none; +} + + +::deep .e-toast-container { + margin-top: 450px; +} + + ::deep .e-toast-container .e-toast { + border: 1px solid black; + border-radius: 25px; + } + /*Use the following CSS to customize the default toast’s title properties like font-family, font-size and color.*/ + ::deep .e-toast-container .e-toast .e-toast-message .e-toast-title { + color: deepskyblue; + font-size: 18px; + font-weight: bold; + text-align: center; + } + /*Use the following CSS to customize the default toast’s content properties like font-family, font-size and color.*/ + ::deep .e-toast-container .e-toast .e-toast-message .e-toast-content { + color: black; + font-size: 14px; + font-weight: bold; + background-color: lightskyblue; + padding: 5px; + width: 100%; + height: 50px; + vertical-align: middle; + text-align: center; + } + +::deep .e-dialog .e-dlg-header { + font-size: 1.75rem; + font-weight: 700; +} diff --git a/BedBrigade.Client/Components/Pages/Administration/Manage/Volunteers.razor b/BedBrigade.Client/Components/Pages/Administration/Manage/Volunteers.razor deleted file mode 100644 index 037333d4..00000000 --- a/BedBrigade.Client/Components/Pages/Administration/Manage/Volunteers.razor +++ /dev/null @@ -1,7 +0,0 @@ -@layout Layout.AdminLayout -@page "/administration/manage/volunteers" -@using BedBrigade.Common; -@using BedBrigade.Common.Constants -@attribute [Authorize(Roles = RoleNames.CanManageVolunteers)] - - \ No newline at end of file diff --git a/BedBrigade.Client/Components/Pages/Administration/Manage/Volunteers.razor.cs b/BedBrigade.Client/Components/Pages/Administration/Manage/Volunteers.razor.cs deleted file mode 100644 index 1c823753..00000000 --- a/BedBrigade.Client/Components/Pages/Administration/Manage/Volunteers.razor.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.JSInterop; - -namespace BedBrigade.Client.Components.Pages.Administration.Manage -{ - public partial class Volunteers : ComponentBase - { - [Inject] private IJSRuntime _js { get; set; } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - //Collapse the mobile menu - await _js.InvokeVoidAsync("AddRemoveClass.RemoveClass", "navbarResponsive", "show"); - } - } -} diff --git a/BedBrigade.Client/Components/Pages/BedRequest.razor.cs b/BedBrigade.Client/Components/Pages/BedRequest.razor.cs index ff508279..1c9af69d 100644 --- a/BedBrigade.Client/Components/Pages/BedRequest.razor.cs +++ b/BedBrigade.Client/Components/Pages/BedRequest.razor.cs @@ -123,7 +123,7 @@ private void CheckChildData(string SearchZipCode) // from Search Location Compo private void ShowValidationMessage(string message) { MyValidationMessage = message; - MyValidationDisplay = "block"; + MyValidationDisplay = ""; } private async Task IsValid() { @@ -136,21 +136,6 @@ private async Task IsValid() return false; } - bool isPhoneValid = Validation.IsValidPhoneNumber(newRequest.Phone); - - if (!isPhoneValid) - { - ShowValidationMessage("Please enter a valid phone number."); - return false; - } - - var emailResult = Validation.IsValidEmail(newRequest.Email); - if (!emailResult.IsValid) - { - ShowValidationMessage(emailResult.UserMessage); - return false; - } - string addressMessage = ValidateAddress(); if (!string.IsNullOrEmpty(addressMessage)) diff --git a/BedBrigade.Client/Components/Pages/ContactUs.razor.cs b/BedBrigade.Client/Components/Pages/ContactUs.razor.cs index 0ab9c5b8..3741369d 100644 --- a/BedBrigade.Client/Components/Pages/ContactUs.razor.cs +++ b/BedBrigade.Client/Components/Pages/ContactUs.razor.cs @@ -122,7 +122,7 @@ private void ClearValidationMessage() private void ShowValidationMessage(string message) { MyValidationMessage = message; - MyValidationDisplay = "block"; + MyValidationDisplay = ""; } private bool IsValid() @@ -136,21 +136,6 @@ private bool IsValid() return false; } - bool isPhoneValid = Validation.IsValidPhoneNumber(newRequest.Phone); - - if (!isPhoneValid) - { - ShowValidationMessage("Please enter a valid phone number."); - return false; - } - - var emailResult = Validation.IsValidEmail(newRequest.Email); - if (!emailResult.IsValid) - { - ShowValidationMessage(emailResult.UserMessage); - return false; - } - if (!ValidReCAPTCHA) { ShowValidationMessage("Please check reCAPTCHA"); diff --git a/BedBrigade.Client/Components/Pages/VolunteerSignUp.razor.cs b/BedBrigade.Client/Components/Pages/VolunteerSignUp.razor.cs index 15a72231..aa4ed107 100644 --- a/BedBrigade.Client/Components/Pages/VolunteerSignUp.razor.cs +++ b/BedBrigade.Client/Components/Pages/VolunteerSignUp.razor.cs @@ -214,7 +214,7 @@ private void ClearMessage() private void ShowMessage(string message) { MyMessage = message; - MyMessageDisplay = "block"; + MyMessageDisplay = ""; } private bool IsValid() @@ -229,21 +229,6 @@ private bool IsValid() return false; } - bool isPhoneValid = Validation.IsValidPhoneNumber(newVolunteer.Phone); - - if (!isPhoneValid) - { - ShowMessage("Please enter a valid phone number."); - return false; - } - - var emailResult = Validation.IsValidEmail(newVolunteer.Email); - if (!emailResult.IsValid) - { - ShowMessage(emailResult.UserMessage); - return false; - } - if (SelectedEvent == null) { ShowMessage("Please select an event."); diff --git a/BedBrigade.Client/Components/Routes.razor b/BedBrigade.Client/Components/Routes.razor index b8a80936..b0e95b5f 100644 --- a/BedBrigade.Client/Components/Routes.razor +++ b/BedBrigade.Client/Components/Routes.razor @@ -7,5 +7,6 @@ + diff --git a/BedBrigade.Client/Components/ToastComponent.razor b/BedBrigade.Client/Components/ToastComponent.razor index bf189329..2bb8db9b 100644 --- a/BedBrigade.Client/Components/ToastComponent.razor +++ b/BedBrigade.Client/Components/ToastComponent.razor @@ -1,15 +1,16 @@ @using Syncfusion.Blazor.Notifications; - - - - @ToastOptions.Title - - - @ToastOptions.Content - - + + + + + @ToastOptions.Title + + + @ToastOptions.Content + + @code { @@ -28,7 +29,7 @@ this.ToastOptions.Title = options.Title; this.ToastOptions.Content = options.Content; this.ToastOptions.CssClass = options.CssClass; - this.ToastOptions.Icon = options.Icon; + this.ToastOptions.Icon = options.Icon; this.StateHasChanged(); await Task.Delay(500); await this.ToastOptions.ToastObj.ShowAsync(); diff --git a/BedBrigade.Client/Components/ToastService.cs b/BedBrigade.Client/Components/ToastService.cs index cb286931..d76d76ab 100644 --- a/BedBrigade.Client/Components/ToastService.cs +++ b/BedBrigade.Client/Components/ToastService.cs @@ -17,7 +17,7 @@ public void Success(string title, string content) { Title = title, Content = content, - CssClass = "e-toast-success", + CssClass = "e-toast-success custom-toast", Icon = "e-success toast-icons" }); } @@ -28,7 +28,7 @@ public void Error(string title, string content) { Title = title, Content = content, - CssClass = "e-toast-danger", + CssClass = "e-toast-danger custom-toast", Icon = "e-error toast-icons" }); } diff --git a/BedBrigade.Client/Components/VolunteerGrid.razor b/BedBrigade.Client/Components/VolunteerGrid.razor deleted file mode 100644 index 88c52f21..00000000 --- a/BedBrigade.Client/Components/VolunteerGrid.razor +++ /dev/null @@ -1,260 +0,0 @@ -@using Syncfusion.Blazor.Notifications -@using Syncfusion.Blazor.Grids -@using Syncfusion.Blazor.Inputs -@using Syncfusion.Blazor.Buttons -@using FilterType = Syncfusion.Blazor.Grids.FilterType -@using EditMode = Syncfusion.Blazor.Grids.EditMode -@using WrapMode = Syncfusion.Blazor.Grids.WrapMode -@using BedBrigade.Common; -@using BedBrigade.Common.Logic -@using BedBrigade.Common.Models - - -@if (Volunteers == null) -{ - -} -else -{ -
 
-
-
-

Volunteers

-
-
-  @userRole  - @if (isLocationAdmin) - { - [Location: @userLocationName] - } -
@userName -
-
- -
-
-
- - - - - - - @RecordText - - - - - @{ - @HeaderTitle - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-} - - - - -@ErrorMessage - -@if (NoPaging) -{ - -} - - diff --git a/BedBrigade.Client/Components/VolunteerGrid.razor.css b/BedBrigade.Client/Components/VolunteerGrid.razor.css deleted file mode 100644 index 1ccb9341..00000000 --- a/BedBrigade.Client/Components/VolunteerGrid.razor.css +++ /dev/null @@ -1,43 +0,0 @@ -/***************************************** VolunteerGrid.razor.css ********************************************/ - -::deep .e-grid td.e-rowcell.e-focus.e-focused { - box-shadow: none; -} - -::deep .e-grid th.e-headercell.e-focus.e-focused { - box-shadow: none; -} - - -::deep .e-toast-container { - margin-top: 450px; -} - -::deep .e-toast-container .e-toast { - border: 1px solid black; - border-radius: 25px; -} -/*Use the following CSS to customize the default toast’s title properties like font-family, font-size and color.*/ -::deep .e-toast-container .e-toast .e-toast-message .e-toast-title { - color: deepskyblue; - font-size: 18px; - font-weight: bold; - text-align: center; -} -/*Use the following CSS to customize the default toast’s content properties like font-family, font-size and color.*/ -::deep .e-toast-container .e-toast .e-toast-message .e-toast-content { - color: black; - font-size: 14px; - font-weight: bold; - background-color: lightskyblue; - padding: 5px; - width: 100%; - height: 50px; - vertical-align: middle; - text-align: center; -} - -::deep .e-dialog .e-dlg-header { - font-size: 1.75rem; - font-weight: 700; -} diff --git a/BedBrigade.Common/Logic/CustomEmailValidation.cs b/BedBrigade.Common/Logic/CustomEmailValidation.cs new file mode 100644 index 00000000..da116b7a --- /dev/null +++ b/BedBrigade.Common/Logic/CustomEmailValidation.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; + +namespace BedBrigade.Common.Logic +{ + public class CustomEmailValidationAttribute : ValidationAttribute + { + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + var email = value as string; + var result = Validation.IsValidEmail(email); + + if (!result.IsValid) + { + return new ValidationResult(result.UserMessage); + } + + return ValidationResult.Success; + } + } +} diff --git a/BedBrigade.Common/Logic/CustomPhoneValidation.cs b/BedBrigade.Common/Logic/CustomPhoneValidation.cs new file mode 100644 index 00000000..c9c6db12 --- /dev/null +++ b/BedBrigade.Common/Logic/CustomPhoneValidation.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; + + +namespace BedBrigade.Common.Logic +{ + public class CustomPhoneValidationAttribute : ValidationAttribute + { + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + var phone = value as string; + var result = Validation.IsValidPhoneNumber(phone); + + if (!result) + { + return new ValidationResult("A phone number must be exactly 10 digits and have a valid area code and prefix."); + } + + return ValidationResult.Success; + } + } +} diff --git a/BedBrigade.Common/Logic/Validation.cs b/BedBrigade.Common/Logic/Validation.cs index 27d63fc0..1c77912a 100644 --- a/BedBrigade.Common/Logic/Validation.cs +++ b/BedBrigade.Common/Logic/Validation.cs @@ -5,7 +5,7 @@ namespace BedBrigade.Common.Logic { public static class Validation { - public static Result IsValidEmail(string email) + public static Result IsValidEmail(string? email) { var emailValidation = LibraryFactory.CreateEmailValidation(); return emailValidation.ValidEmail(email, emailValidation.NoConnectOptions); diff --git a/BedBrigade.Common/Models/BedRequest.cs b/BedBrigade.Common/Models/BedRequest.cs index 08451209..daec6487 100644 --- a/BedBrigade.Common/Models/BedRequest.cs +++ b/BedBrigade.Common/Models/BedRequest.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using BedBrigade.Common.Enums; +using BedBrigade.Common.Logic; namespace BedBrigade.Common.Models; @@ -27,11 +28,12 @@ public class BedRequest : BaseEntity, ILocationId, IEmail [Required(ErrorMessage = "Email Address is required.")] [MaxLength(255)] - [EmailInputValidation] + [CustomEmailValidation] public String? Email { get; set; } = string.Empty; [Required(ErrorMessage = "Phone Number is required.")] [MaxLength(14)] + [CustomPhoneValidation] public String? Phone { get; set; } = string.Empty; [Required(ErrorMessage = "Street Address is required.")] diff --git a/BedBrigade.Common/Models/ContactUs.cs b/BedBrigade.Common/Models/ContactUs.cs index 88b20567..ab97c56e 100644 --- a/BedBrigade.Common/Models/ContactUs.cs +++ b/BedBrigade.Common/Models/ContactUs.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using BedBrigade.Common.Enums; +using BedBrigade.Common.Logic; namespace BedBrigade.Common.Models { @@ -24,11 +25,13 @@ public class ContactUs : BaseEntity, ILocationId, IEmail [Required(ErrorMessage = "Email is required")] [MaxLength(255)] - public String Email { get; set; } = string.Empty; + [CustomEmailValidation] + public String Email { get; set; } = string.Empty; [Required(ErrorMessage = "Phone is required")] [MaxLength(14)] - public String Phone { get; set; } = string.Empty; + [CustomPhoneValidation] + public String Phone { get; set; } = string.Empty; [Required(ErrorMessage = "Message is required")] [MaxLength(4000)] diff --git a/BedBrigade.Common/Models/Donation.cs b/BedBrigade.Common/Models/Donation.cs index bab04fa0..05386675 100644 --- a/BedBrigade.Common/Models/Donation.cs +++ b/BedBrigade.Common/Models/Donation.cs @@ -1,3 +1,4 @@ +using BedBrigade.Common.Logic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -15,7 +16,8 @@ public class Donation : BaseEntity, ILocationId [Required] [MaxLength(255)] - public String Email { get; set; } = string.Empty; + [CustomEmailValidation] + public String Email { get; set; } = string.Empty; [Required] [Column(TypeName = "decimal(18,4)")] diff --git a/BedBrigade.Common/Models/User.cs b/BedBrigade.Common/Models/User.cs index 1d1ac113..ec55fe45 100644 --- a/BedBrigade.Common/Models/User.cs +++ b/BedBrigade.Common/Models/User.cs @@ -1,3 +1,4 @@ +using BedBrigade.Common.Logic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -24,6 +25,7 @@ public class User : BaseEntity, ILocationId, IEmail [Required(ErrorMessage = "An email address is required")] [MaxLength(255)] + [CustomEmailValidation] public String Email { get; set; } = string.Empty; [MaxLength(255)] @@ -34,6 +36,7 @@ public class User : BaseEntity, ILocationId, IEmail [MaxLength(14)] [Required(ErrorMessage = "Please enter a phone number")] + [CustomPhoneValidation] public String? Phone { get; set; } = string.Empty; public String? Role { get; set; } = string.Empty; diff --git a/BedBrigade.Common/Models/Volunteer.cs b/BedBrigade.Common/Models/Volunteer.cs index 4edef230..7b6a08ce 100644 --- a/BedBrigade.Common/Models/Volunteer.cs +++ b/BedBrigade.Common/Models/Volunteer.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using BedBrigade.Common.Enums; +using BedBrigade.Common.Logic; namespace BedBrigade.Common.Models { @@ -25,14 +26,14 @@ public class Volunteer : BaseEntity, ILocationId, IEmail public String LastName { get; set; } = string.Empty; [Required(ErrorMessage = "Email Address is required.")] - [Index(IsUnique = true)] - [EmailInputValidation] + [CustomEmailValidation] [MaxLength(255)] public String Email { get; set; } = string.Empty; [Required(ErrorMessage = "Phone Number is required.")] [MaxLength(14)] - public String Phone { get; set; } = string.Empty; + [CustomPhoneValidation] + public String Phone { get; set; } = string.Empty; [MaxLength(80)] public String? OrganizationOrGroup { get; set; } = string.Empty; diff --git a/BedBrigade.Data/BedBrigade.Data.csproj b/BedBrigade.Data/BedBrigade.Data.csproj index 6be19d94..34245f6e 100644 --- a/BedBrigade.Data/BedBrigade.Data.csproj +++ b/BedBrigade.Data/BedBrigade.Data.csproj @@ -56,6 +56,7 @@ + diff --git a/BedBrigade.Data/Data/DataContext.cs b/BedBrigade.Data/Data/DataContext.cs index d757ca20..08c1093a 100644 --- a/BedBrigade.Data/Data/DataContext.cs +++ b/BedBrigade.Data/Data/DataContext.cs @@ -1,8 +1,5 @@ using BedBrigade.Common.Models; -using Microsoft.AspNetCore.Components.Authorization; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; namespace BedBrigade.Data { @@ -53,9 +50,12 @@ private static void CreateIndexes(ModelBuilder modelBuilder) modelBuilder.Entity() .HasIndex(o => o.EventType); - modelBuilder.Entity() .HasIndex(o => o.ScheduleId); + + modelBuilder.Entity() + .HasIndex(e => e.Email) + .IsUnique(); } } } diff --git a/BedBrigade.Data/Data/Seeding/Seed.cs b/BedBrigade.Data/Data/Seeding/Seed.cs index d9c8cbba..f0c6dbb2 100644 --- a/BedBrigade.Data/Data/Seeding/Seed.cs +++ b/BedBrigade.Data/Data/Seeding/Seed.cs @@ -9,6 +9,7 @@ using BedBrigade.Common.Enums; using BedBrigade.Common.Constants; using BedBrigade.Common.Models; +using Bogus; namespace BedBrigade.Data.Seeding; @@ -650,8 +651,6 @@ private static async Task SeedVolunteers(IDbContextFactory contextF { if (await context.Volunteers.AnyAsync()) return; - List FirstNames = new List { "Mike", "Sam", "John", "Luke", "Betty", "Joan", "Sandra", "Elizabeth", "Greg", "Genava" }; - List LastNames = new List { "Smith", "Willams", "Henry", "Cobb", "McAlvy", "Jackson", "Tomkin", "Corey", "Whipple", "Forbrzo" }; List YesOrNo = new List { true, false }; List EmailProviders = new List { "outlook.com", "gmail.com", "yahoo.com", "comcast.com", "cox.com" }; List volunteersFor = context.VolunteersFor.ToList(); @@ -661,15 +660,14 @@ private static async Task SeedVolunteers(IDbContextFactory contextF { locations.Remove(item); } - + for (var i = 0; i <= 100; i++) { - var firstName = FirstNames[new Random().Next(FirstNames.Count - 1)]; - var lastName = LastNames[new Random().Next(LastNames.Count - 1)]; - int firstThree, nextThree, lastFour; + var faker = new Faker(); + var firstName = faker.Name.FirstName(); + var lastName = faker.Name.LastName(); var phoneNumber = GeneratePhoneNumber(); var location = locations[new Random().Next(locations.Count - 1)]; - var volunteeringFor = volunteersFor[new Random().Next(volunteersFor.Count - 1)]; Volunteer volunteer = new() { LocationId = location.LocationId, @@ -705,8 +703,6 @@ private static async Task SeedDonations(IDbContextFactory contextFa { if (await context.Donations.AnyAsync()) return; - List FirstNames = new List { "Mike", "Sam", "John", "Luke", "Betty", "Joan", "Sandra", "Elizabeth", "Greg", "Genava" }; - List LastNames = new List { "Smith", "Willams", "Henry", "Cobb", "McAlvy", "Jackson", "Tomkin", "Corey", "Whipple", "Forbrzo" }; List YesOrNo = new List { true, false }; List EmailProviders = new List { "outlook.com", "gmail.com", "yahoo.com", "comcast.com", "cox.com" }; List locations = await context.Locations.ToListAsync(); @@ -719,8 +715,9 @@ private static async Task SeedDonations(IDbContextFactory contextFa for (var i = 0; i < 100; i++) { var location = locations[new Random().Next(locations.Count - 1)]; - var firstName = FirstNames[new Random().Next(FirstNames.Count - 1)]; - var lastName = LastNames[new Random().Next(LastNames.Count - 1)]; + var faker = new Faker(); + var firstName = faker.Name.FirstName(); + var lastName = faker.Name.LastName(); Donation donation = new() { LocationId = location.LocationId, @@ -758,9 +755,6 @@ private static async Task SeedBedRequests(IDbContextFactory context { if (await context.BedRequests.AnyAsync()) return; - List FirstNames = new List { "Mike", "Sam", "John", "Luke", "Betty", "Joan", "Sandra", "Elizabeth", "Greg", "Genava" }; - List LastNames = new List { "Smith", "Willams", "Henry", "Cobb", "McAlvy", "Jackson", "Tomkin", "Corey", "Whipple", "Forbrzo" }; - List YesOrNo = new List { true, false }; List EmailProviders = new List { "outlook.com", "gmail.com", "yahoo.com", "comcast.com", "cox.com" }; List City = new List { "Columbus", "Cleveland", "Cincinnati", "Canton", "Youngston", "Springfield", "Middletown", "Beavercreek" }; List StreetName = new List { "E. Bella Ln", "W. Chandler Blvd", "25 St.", "4th Ave.", "G Ave.", "Indian Wells CT.", "N. Arizona" }; @@ -774,8 +768,9 @@ private static async Task SeedBedRequests(IDbContextFactory context for (var i = 0; i < 30; i++) { var location = locations[new Random().Next(locations.Count - 1)]; - var firstName = FirstNames[new Random().Next(FirstNames.Count - 1)]; - var lastName = LastNames[new Random().Next(LastNames.Count - 1)]; + var faker = new Faker(); + var firstName = faker.Name.FirstName(); + var lastName = faker.Name.LastName(); BedRequest bedRequest = new() { LocationId = location.LocationId, diff --git a/BedBrigade.Data/Migrations/20240828153512_VolunteerEmailIndex.Designer.cs b/BedBrigade.Data/Migrations/20240828153512_VolunteerEmailIndex.Designer.cs new file mode 100644 index 00000000..17ed359c --- /dev/null +++ b/BedBrigade.Data/Migrations/20240828153512_VolunteerEmailIndex.Designer.cs @@ -0,0 +1,1070 @@ +// +using System; +using BedBrigade.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BedBrigade.Data.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20240828153512_VolunteerEmailIndex")] + partial class VolunteerEmailIndex + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("BedBrigade.Common.Models.BedRequest", b => + { + b.Property("BedRequestId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("BedRequestId")); + + b.Property("AgesGender") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("City") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("DeliveryDate") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("nvarchar(25)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Notes") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("NumberOfBeds") + .HasColumnType("int"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(14) + .HasColumnType("nvarchar(14)"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(5) + .HasColumnType("nvarchar(5)"); + + b.Property("ScheduleId") + .HasColumnType("int"); + + b.Property("SpecialInstructions") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("State") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Street") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("TeamNumber") + .HasColumnType("int"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("BedRequestId"); + + b.HasIndex("LocationId"); + + b.HasIndex("ScheduleId"); + + b.ToTable("BedRequests"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Configuration", b => + { + b.Property("ConfigurationKey") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ConfigurationValue") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Section") + .HasColumnType("int"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("ConfigurationKey"); + + b.ToTable("Configurations"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.ContactUs", b => + { + b.Property("ContactUsId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ContactUsId")); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("nvarchar(25)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(14) + .HasColumnType("nvarchar(14)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("ContactUsId"); + + b.HasIndex("LocationId"); + + b.HasIndex("Status"); + + b.ToTable("ContactUs"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Content", b => + { + b.Property("ContentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ContentId")); + + b.Property("ContentHtml") + .HasColumnType("nvarchar(max)"); + + b.Property("ContentType") + .HasColumnType("int"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("ContentId"); + + b.HasIndex("ContentType"); + + b.HasIndex("LocationId"); + + b.ToTable("Content"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Donation", b => + { + b.Property("DonationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("DonationId")); + + b.Property("Amount") + .HasColumnType("decimal(18,4)"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FirstName") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("LastName") + .HasMaxLength(25) + .HasColumnType("nvarchar(25)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TaxFormSent") + .HasColumnType("bit"); + + b.Property("TransactionId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("DonationId"); + + b.HasIndex("LocationId"); + + b.ToTable("Donations"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.EmailQueue", b => + { + b.Property("EmailQueueId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("EmailQueueId")); + + b.Property("Body") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FailureMessage") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("FirstName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FromAddress") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FromDisplayName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("HtmlBody") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("LockDate") + .HasColumnType("datetime2"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("QueueDate") + .HasColumnType("datetime2"); + + b.Property("ReplyToAddress") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SentDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ToAddress") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ToDisplayName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("EmailQueueId"); + + b.ToTable("EmailQueue"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Location", b => + { + b.Property("LocationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("LocationId")); + + b.Property("Address1") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Address2") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("City") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Latitude") + .HasColumnType("decimal(18,10)"); + + b.Property("Longitude") + .HasColumnType("decimal(18,10)"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Route") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("State") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("LocationId"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Media", b => + { + b.Property("MediaId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("MediaId")); + + b.Property("AltText") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FileName") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FilePath") + .HasMaxLength(260) + .HasColumnType("nvarchar(260)"); + + b.Property("FileSize") + .HasColumnType("int"); + + b.Property("FileStatus") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("FileUse") + .HasColumnType("int"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MediaType") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("MediaId"); + + b.HasIndex("LocationId"); + + b.ToTable("Media"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Role", b => + { + b.Property("RoleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("RoleId")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("RoleId"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Schedule", b => + { + b.Property("ScheduleId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ScheduleId")); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("DeliveryVehiclesRegistered") + .HasColumnType("int"); + + b.Property("EventDateScheduled") + .HasColumnType("datetime2"); + + b.Property("EventDurationHours") + .HasColumnType("int"); + + b.Property("EventName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("EventNote") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("EventStatus") + .HasColumnType("int"); + + b.Property("EventType") + .HasColumnType("int"); + + b.Property("GroupName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("VolunteersMax") + .HasColumnType("int"); + + b.Property("VolunteersRegistered") + .HasColumnType("int"); + + b.HasKey("ScheduleId"); + + b.HasIndex("EventType"); + + b.HasIndex("LocationId"); + + b.ToTable("Schedules"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Template", b => + { + b.Property("TemplateId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("TemplateId")); + + b.Property("ContentHtml") + .HasColumnType("nvarchar(max)"); + + b.Property("ContentType") + .HasColumnType("int"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("TemplateId"); + + b.ToTable("Templates"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.User", b => + { + b.Property("UserName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("FkRole") + .HasColumnType("int"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("nvarchar(25)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PasswordHash") + .HasMaxLength(255) + .HasColumnType("varbinary(255)"); + + b.Property("PasswordSalt") + .HasMaxLength(255) + .HasColumnType("varbinary(255)"); + + b.Property("PersistBedRequest") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("PersistConfig") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("PersistDonation") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("PersistLocation") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("PersistMedia") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("PersistPages") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("PersistUser") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("PersistVolunteers") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(14) + .HasColumnType("nvarchar(14)"); + + b.Property("Role") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("UserName"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Volunteer", b => + { + b.Property("VolunteerId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("VolunteerId")); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("IHaveVolunteeredBefore") + .HasColumnType("bit"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(25) + .HasColumnType("nvarchar(25)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Message") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("OrganizationOrGroup") + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(14) + .HasColumnType("nvarchar(14)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("VehicleType") + .HasColumnType("int"); + + b.HasKey("VolunteerId"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("LocationId"); + + b.ToTable("Volunteers"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.VolunteerEvent", b => + { + b.Property("RegistrationId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("RegistrationId")); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ScheduleId") + .HasColumnType("int"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("VolunteerEventNote") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("VolunteerId") + .HasColumnType("int"); + + b.HasKey("RegistrationId"); + + b.HasIndex("VolunteerId"); + + b.ToTable("VolunteerEvents"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.VolunteerFor", b => + { + b.Property("VolunteerForId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("VolunteerForId")); + + b.Property("CreateDate") + .HasColumnType("datetime2"); + + b.Property("CreateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MachineName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("UpdateDate") + .HasColumnType("datetime2"); + + b.Property("UpdateUser") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("VolunteerForId"); + + b.ToTable("VolunteersFor"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.BedRequest", b => + { + b.HasOne("BedBrigade.Common.Models.Location", null) + .WithMany("BedRequests") + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.ContactUs", b => + { + b.HasOne("BedBrigade.Common.Models.Location", null) + .WithMany("ContactUs") + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Content", b => + { + b.HasOne("BedBrigade.Common.Models.Location", null) + .WithMany("Contents") + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Donation", b => + { + b.HasOne("BedBrigade.Common.Models.Location", null) + .WithMany("Donations") + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Media", b => + { + b.HasOne("BedBrigade.Common.Models.Location", null) + .WithMany("Media") + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Schedule", b => + { + b.HasOne("BedBrigade.Common.Models.Location", null) + .WithMany("Schedules") + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.User", b => + { + b.HasOne("BedBrigade.Common.Models.Location", null) + .WithMany("Users") + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Volunteer", b => + { + b.HasOne("BedBrigade.Common.Models.Location", null) + .WithMany("Volunteers") + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.VolunteerEvent", b => + { + b.HasOne("BedBrigade.Common.Models.Volunteer", "Volunteer") + .WithMany() + .HasForeignKey("VolunteerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Volunteer"); + }); + + modelBuilder.Entity("BedBrigade.Common.Models.Location", b => + { + b.Navigation("BedRequests"); + + b.Navigation("ContactUs"); + + b.Navigation("Contents"); + + b.Navigation("Donations"); + + b.Navigation("Media"); + + b.Navigation("Schedules"); + + b.Navigation("Users"); + + b.Navigation("Volunteers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/BedBrigade.Data/Migrations/20240828153512_VolunteerEmailIndex.cs b/BedBrigade.Data/Migrations/20240828153512_VolunteerEmailIndex.cs new file mode 100644 index 00000000..960fcbc6 --- /dev/null +++ b/BedBrigade.Data/Migrations/20240828153512_VolunteerEmailIndex.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BedBrigade.Data.Migrations +{ + /// + public partial class VolunteerEmailIndex : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_Volunteers_Email", + table: "Volunteers", + column: "Email", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Volunteers_Email", + table: "Volunteers"); + } + } +} diff --git a/BedBrigade.Data/Migrations/DataContextModelSnapshot.cs b/BedBrigade.Data/Migrations/DataContextModelSnapshot.cs index 4eb9bbbd..9248945c 100644 --- a/BedBrigade.Data/Migrations/DataContextModelSnapshot.cs +++ b/BedBrigade.Data/Migrations/DataContextModelSnapshot.cs @@ -17,12 +17,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.7") + .HasAnnotation("ProductVersion", "8.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - modelBuilder.Entity("BedBrigade.Data.Models.BedRequest", b => + modelBuilder.Entity("BedBrigade.Common.Models.BedRequest", b => { b.Property("BedRequestId") .ValueGeneratedOnAdd() @@ -127,7 +127,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("BedRequests"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Configuration", b => + modelBuilder.Entity("BedBrigade.Common.Models.Configuration", b => { b.Property("ConfigurationKey") .HasMaxLength(50) @@ -164,7 +164,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Configurations"); }); - modelBuilder.Entity("BedBrigade.Data.Models.ContactUs", b => + modelBuilder.Entity("BedBrigade.Common.Models.ContactUs", b => { b.Property("ContactUsId") .ValueGeneratedOnAdd() @@ -230,7 +230,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("ContactUs"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Content", b => + modelBuilder.Entity("BedBrigade.Common.Models.Content", b => { b.Property("ContentId") .ValueGeneratedOnAdd() @@ -284,7 +284,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Content"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Donation", b => + modelBuilder.Entity("BedBrigade.Common.Models.Donation", b => { b.Property("DonationId") .ValueGeneratedOnAdd() @@ -343,7 +343,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Donations"); }); - modelBuilder.Entity("BedBrigade.Data.Models.EmailQueue", b => + modelBuilder.Entity("BedBrigade.Common.Models.EmailQueue", b => { b.Property("EmailQueueId") .ValueGeneratedOnAdd() @@ -435,7 +435,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("EmailQueue"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Location", b => + modelBuilder.Entity("BedBrigade.Common.Models.Location", b => { b.Property("LocationId") .ValueGeneratedOnAdd() @@ -503,7 +503,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Locations"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Media", b => + modelBuilder.Entity("BedBrigade.Common.Models.Media", b => { b.Property("MediaId") .ValueGeneratedOnAdd() @@ -565,7 +565,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Media"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Role", b => + modelBuilder.Entity("BedBrigade.Common.Models.Role", b => { b.Property("RoleId") .ValueGeneratedOnAdd() @@ -583,7 +583,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Roles"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Schedule", b => + modelBuilder.Entity("BedBrigade.Common.Models.Schedule", b => { b.Property("ScheduleId") .ValueGeneratedOnAdd() @@ -655,7 +655,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Schedules"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Template", b => + modelBuilder.Entity("BedBrigade.Common.Models.Template", b => { b.Property("TemplateId") .ValueGeneratedOnAdd() @@ -697,7 +697,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Templates"); }); - modelBuilder.Entity("BedBrigade.Data.Models.User", b => + modelBuilder.Entity("BedBrigade.Common.Models.User", b => { b.Property("UserName") .HasMaxLength(50) @@ -805,7 +805,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Users"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Volunteer", b => + modelBuilder.Entity("BedBrigade.Common.Models.Volunteer", b => { b.Property("VolunteerId") .ValueGeneratedOnAdd() @@ -870,12 +870,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("VolunteerId"); + b.HasIndex("Email") + .IsUnique(); + b.HasIndex("LocationId"); b.ToTable("Volunteers"); }); - modelBuilder.Entity("BedBrigade.Data.Models.VolunteerEvent", b => + modelBuilder.Entity("BedBrigade.Common.Models.VolunteerEvent", b => { b.Property("RegistrationId") .ValueGeneratedOnAdd() @@ -921,7 +924,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("VolunteerEvents"); }); - modelBuilder.Entity("BedBrigade.Data.Models.VolunteerFor", b => + modelBuilder.Entity("BedBrigade.Common.Models.VolunteerFor", b => { b.Property("VolunteerForId") .ValueGeneratedOnAdd() @@ -957,81 +960,81 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("VolunteersFor"); }); - modelBuilder.Entity("BedBrigade.Data.Models.BedRequest", b => + modelBuilder.Entity("BedBrigade.Common.Models.BedRequest", b => { - b.HasOne("BedBrigade.Data.Models.Location", null) + b.HasOne("BedBrigade.Common.Models.Location", null) .WithMany("BedRequests") .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("BedBrigade.Data.Models.ContactUs", b => + modelBuilder.Entity("BedBrigade.Common.Models.ContactUs", b => { - b.HasOne("BedBrigade.Data.Models.Location", null) + b.HasOne("BedBrigade.Common.Models.Location", null) .WithMany("ContactUs") .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("BedBrigade.Data.Models.Content", b => + modelBuilder.Entity("BedBrigade.Common.Models.Content", b => { - b.HasOne("BedBrigade.Data.Models.Location", null) + b.HasOne("BedBrigade.Common.Models.Location", null) .WithMany("Contents") .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("BedBrigade.Data.Models.Donation", b => + modelBuilder.Entity("BedBrigade.Common.Models.Donation", b => { - b.HasOne("BedBrigade.Data.Models.Location", null) + b.HasOne("BedBrigade.Common.Models.Location", null) .WithMany("Donations") .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("BedBrigade.Data.Models.Media", b => + modelBuilder.Entity("BedBrigade.Common.Models.Media", b => { - b.HasOne("BedBrigade.Data.Models.Location", null) + b.HasOne("BedBrigade.Common.Models.Location", null) .WithMany("Media") .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("BedBrigade.Data.Models.Schedule", b => + modelBuilder.Entity("BedBrigade.Common.Models.Schedule", b => { - b.HasOne("BedBrigade.Data.Models.Location", null) + b.HasOne("BedBrigade.Common.Models.Location", null) .WithMany("Schedules") .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("BedBrigade.Data.Models.User", b => + modelBuilder.Entity("BedBrigade.Common.Models.User", b => { - b.HasOne("BedBrigade.Data.Models.Location", null) + b.HasOne("BedBrigade.Common.Models.Location", null) .WithMany("Users") .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("BedBrigade.Data.Models.Volunteer", b => + modelBuilder.Entity("BedBrigade.Common.Models.Volunteer", b => { - b.HasOne("BedBrigade.Data.Models.Location", null) + b.HasOne("BedBrigade.Common.Models.Location", null) .WithMany("Volunteers") .HasForeignKey("LocationId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); - modelBuilder.Entity("BedBrigade.Data.Models.VolunteerEvent", b => + modelBuilder.Entity("BedBrigade.Common.Models.VolunteerEvent", b => { - b.HasOne("BedBrigade.Data.Models.Volunteer", "Volunteer") + b.HasOne("BedBrigade.Common.Models.Volunteer", "Volunteer") .WithMany() .HasForeignKey("VolunteerId") .OnDelete(DeleteBehavior.Cascade) @@ -1040,7 +1043,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Volunteer"); }); - modelBuilder.Entity("BedBrigade.Data.Models.Location", b => + modelBuilder.Entity("BedBrigade.Common.Models.Location", b => { b.Navigation("BedRequests"); From 181d7df28210889e9ec942b43dd22a0847054772 Mon Sep 17 00:00:00 2001 From: Greg Finzer Date: Wed, 28 Aug 2024 19:34:29 -0400 Subject: [PATCH 2/2] Custom Validation Attributes do not work --- .../AdminTasks/AddEditVolunteer.razor.cs | 27 ++++++- .../Manage/ManageVolunteers.razor.cs | 74 +------------------ .../Components/Pages/BedRequest.razor.cs | 15 ++++ .../Components/Pages/ContactUs.razor.cs | 15 ++++ .../Components/Pages/VolunteerSignUp.razor.cs | 16 ++++ .../Logic/CustomEmailValidation.cs | 20 ----- .../Logic/CustomPhoneValidation.cs | 21 ------ BedBrigade.Common/Models/BedRequest.cs | 2 - BedBrigade.Common/Models/ContactUs.cs | 2 - BedBrigade.Common/Models/Donation.cs | 1 - BedBrigade.Common/Models/User.cs | 2 - BedBrigade.Common/Models/Volunteer.cs | 2 - 12 files changed, 73 insertions(+), 124 deletions(-) delete mode 100644 BedBrigade.Common/Logic/CustomEmailValidation.cs delete mode 100644 BedBrigade.Common/Logic/CustomPhoneValidation.cs diff --git a/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor.cs b/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor.cs index 80917b90..4cddacab 100644 --- a/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor.cs +++ b/BedBrigade.Client/Components/Pages/Administration/AdminTasks/AddEditVolunteer.razor.cs @@ -7,6 +7,8 @@ using BedBrigade.Common.Logic; using BedBrigade.Common.Models; using Serilog; +using Microsoft.AspNetCore.Components.Forms; +using System.Net; namespace BedBrigade.Client.Components.Pages.Administration.AdminTasks { @@ -30,6 +32,7 @@ public partial class AddEditVolunteer : ComponentBase { { "rows", "5" }, }; + protected override async Task OnInitializedAsync() { @@ -71,7 +74,6 @@ private async Task LoadVolunteer() if (result.Success) { Model = result.Data; - Model.Phone = StringUtil.ExtractDigits(Model.Phone); } else { @@ -87,16 +89,35 @@ private async Task LoadVolunteer() private async Task HandleValidSubmit() { - if (await SaveVolunteer()) + if (IsValid() && await SaveVolunteer()) { _navigationManager.NavigateTo("/administration/manage/volunteers"); } } - private async Task SaveVolunteer() + private bool IsValid() { ErrorMessage = string.Empty; + bool isPhoneValid = Validation.IsValidPhoneNumber(Model.Phone); + if (!isPhoneValid) + { + ErrorMessage = "Phone numbers must be 10 digits with a valid area code and prefix."; + return false; + } + + var emailResult = Validation.IsValidEmail(Model.Email); + if (!emailResult.IsValid) + { + ErrorMessage = emailResult.UserMessage; + return false; + } + + return true; + } + + private async Task SaveVolunteer() + { if (VolunteerId != null) { var updateResult = await _volunteerDataService.UpdateAsync(Model!); diff --git a/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.cs b/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.cs index cde424d8..d6a56d9e 100644 --- a/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.cs +++ b/BedBrigade.Client/Components/Pages/Administration/Manage/ManageVolunteers.razor.cs @@ -43,24 +43,14 @@ public partial class ManageVolunteers : ComponentBase public bool isLocationAdmin = false; private string ErrorMessage = String.Empty; protected string? _state { get; set; } - protected string? HeaderTitle { get; set; } - protected string? ButtonTitle { get; private set; } - protected string? addNeedDisplay { get; private set; } - protected string? editNeedDisplay { get; private set; } protected SfToast? ToastObj { get; set; } protected string? ToastTitle { get; set; } protected string? ToastContent { get; set; } protected int ToastTimeout { get; set; } = 3000; protected string? RecordText { get; set; } = "Loading Volunteers ..."; - protected string? Hide { get; private set; } = "true"; public bool NoPaging { get; private set; } - public bool OnlyRead { get; private set; } = false; private bool ShouldDisplayEmailMessage = false; - public bool enabledLocationSelector { get; set; } = true; - protected DialogSettings DialogParams = new DialogSettings { Width = "900px", MinHeight = "70%" }; - - private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); // = OperationWithTimeout(cancellationTokenSource.Token); protected override async Task OnInitializedAsync() { @@ -196,8 +186,8 @@ protected async Task OnToolBarClick(Syncfusion.Blazor.Navigations.ClickEventArgs { await Grid.ResetPersistDataAsync(); _state = await Grid.GetPersistDataAsync(); - await _svcUser.SaveGridPersistance(new Persist { GridId = (int)PersistGrid.Volunteer, UserState = _state }); } - + await _svcUser.SaveGridPersistance(new Persist { GridId = (int)PersistGrid.Volunteer, UserState = _state }); + } else if (args.Item.Text == "Pdf Export") { await PdfExport(); @@ -275,61 +265,7 @@ private void Add() - private async Task Save(ActionEventArgs args) - { - ShouldDisplayEmailMessage = false; - Volunteer Volunteer = args.Data; - Volunteer.Phone = Volunteer.Phone.FormatPhoneNumber(); - if (Volunteer.VolunteerId != 0) - { - //Update Volunteer Record - var updateResult = await _svcVolunteer.UpdateAsync(Volunteer); - ToastTitle = "Update Volunteer"; - if (updateResult.Success) - { - ToastContent = "Volunteer Updated Successfully!"; - } - else - { - ToastContent = "Unable to update Volunteer!"; - } - await ToastObj.ShowAsync(new ToastModel { Title = ToastTitle, Content = ToastContent, Timeout = ToastTimeout }); - } - else - { - //See if the email already exists - var existingVolunteerResult = await _svcVolunteer.GetByEmail(Volunteer.Email); - - if (existingVolunteerResult.Success && existingVolunteerResult.Data != null) - { - ShouldDisplayEmailMessage =true; - args.Cancel = true; - StateHasChanged(); - return; - } - - // new Volunteer - var result = await _svcVolunteer.CreateAsync(Volunteer); - if (result.Success && result.Data != null) - { - Volunteer location = result.Data; - } - ToastTitle = "Create Volunteer"; - if (Volunteer.VolunteerId != 0) - { - ToastContent = "Volunteer Created Successfully!"; - } - else - { - ToastContent = "Unable to save Volunteer!"; - } - await ToastObj.ShowAsync(new ToastModel { Title = ToastTitle, Content = ToastContent, Timeout = ToastTimeout }); - } - - await Grid.Refresh(); - - } @@ -375,11 +311,7 @@ protected async Task CsvExportAsync() await Grid.ExportToCsvAsync(ExportProperties); } - private string cssClass { get; set; } = "e-outline"; - protected Dictionary DescriptionHtmlAttribute { get; set; } = new Dictionary() - { - { "rows", "5" }, - }; + } } diff --git a/BedBrigade.Client/Components/Pages/BedRequest.razor.cs b/BedBrigade.Client/Components/Pages/BedRequest.razor.cs index 1c9af69d..1c7d53e2 100644 --- a/BedBrigade.Client/Components/Pages/BedRequest.razor.cs +++ b/BedBrigade.Client/Components/Pages/BedRequest.razor.cs @@ -136,6 +136,21 @@ private async Task IsValid() return false; } + bool isPhoneValid = Validation.IsValidPhoneNumber(newRequest.Phone); + + if (!isPhoneValid) + { + ShowValidationMessage("Phone numbers must be 10 digits with a valid area code and prefix."); + return false; + } + + var emailResult = Validation.IsValidEmail(newRequest.Email); + if (!emailResult.IsValid) + { + ShowValidationMessage(emailResult.UserMessage); + return false; + } + string addressMessage = ValidateAddress(); if (!string.IsNullOrEmpty(addressMessage)) diff --git a/BedBrigade.Client/Components/Pages/ContactUs.razor.cs b/BedBrigade.Client/Components/Pages/ContactUs.razor.cs index 3741369d..33357230 100644 --- a/BedBrigade.Client/Components/Pages/ContactUs.razor.cs +++ b/BedBrigade.Client/Components/Pages/ContactUs.razor.cs @@ -136,6 +136,21 @@ private bool IsValid() return false; } + bool isPhoneValid = Validation.IsValidPhoneNumber(newRequest.Phone); + + if (!isPhoneValid) + { + ShowValidationMessage("Please enter a valid phone number."); + return false; + } + + var emailResult = Validation.IsValidEmail(newRequest.Email); + if (!emailResult.IsValid) + { + ShowValidationMessage(emailResult.UserMessage); + return false; + } + if (!ValidReCAPTCHA) { ShowValidationMessage("Please check reCAPTCHA"); diff --git a/BedBrigade.Client/Components/Pages/VolunteerSignUp.razor.cs b/BedBrigade.Client/Components/Pages/VolunteerSignUp.razor.cs index aa4ed107..44a4a8a4 100644 --- a/BedBrigade.Client/Components/Pages/VolunteerSignUp.razor.cs +++ b/BedBrigade.Client/Components/Pages/VolunteerSignUp.razor.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc; using Azure; using BedBrigade.Common.Models; +using System.Net; namespace BedBrigade.Client.Components.Pages { @@ -229,6 +230,21 @@ private bool IsValid() return false; } + bool isPhoneValid = Validation.IsValidPhoneNumber(newVolunteer.Phone); + + if (!isPhoneValid) + { + ShowMessage("Phone numbers must be 10 digits with a valid area code and prefix."); + return false; + } + + var emailResult = Validation.IsValidEmail(newVolunteer.Email); + if (!emailResult.IsValid) + { + ShowMessage(emailResult.UserMessage); + return false; + } + if (SelectedEvent == null) { ShowMessage("Please select an event."); diff --git a/BedBrigade.Common/Logic/CustomEmailValidation.cs b/BedBrigade.Common/Logic/CustomEmailValidation.cs deleted file mode 100644 index da116b7a..00000000 --- a/BedBrigade.Common/Logic/CustomEmailValidation.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace BedBrigade.Common.Logic -{ - public class CustomEmailValidationAttribute : ValidationAttribute - { - protected override ValidationResult IsValid(object value, ValidationContext validationContext) - { - var email = value as string; - var result = Validation.IsValidEmail(email); - - if (!result.IsValid) - { - return new ValidationResult(result.UserMessage); - } - - return ValidationResult.Success; - } - } -} diff --git a/BedBrigade.Common/Logic/CustomPhoneValidation.cs b/BedBrigade.Common/Logic/CustomPhoneValidation.cs deleted file mode 100644 index c9c6db12..00000000 --- a/BedBrigade.Common/Logic/CustomPhoneValidation.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.ComponentModel.DataAnnotations; - - -namespace BedBrigade.Common.Logic -{ - public class CustomPhoneValidationAttribute : ValidationAttribute - { - protected override ValidationResult IsValid(object value, ValidationContext validationContext) - { - var phone = value as string; - var result = Validation.IsValidPhoneNumber(phone); - - if (!result) - { - return new ValidationResult("A phone number must be exactly 10 digits and have a valid area code and prefix."); - } - - return ValidationResult.Success; - } - } -} diff --git a/BedBrigade.Common/Models/BedRequest.cs b/BedBrigade.Common/Models/BedRequest.cs index daec6487..4762e14d 100644 --- a/BedBrigade.Common/Models/BedRequest.cs +++ b/BedBrigade.Common/Models/BedRequest.cs @@ -28,12 +28,10 @@ public class BedRequest : BaseEntity, ILocationId, IEmail [Required(ErrorMessage = "Email Address is required.")] [MaxLength(255)] - [CustomEmailValidation] public String? Email { get; set; } = string.Empty; [Required(ErrorMessage = "Phone Number is required.")] [MaxLength(14)] - [CustomPhoneValidation] public String? Phone { get; set; } = string.Empty; [Required(ErrorMessage = "Street Address is required.")] diff --git a/BedBrigade.Common/Models/ContactUs.cs b/BedBrigade.Common/Models/ContactUs.cs index ab97c56e..fa6eaeff 100644 --- a/BedBrigade.Common/Models/ContactUs.cs +++ b/BedBrigade.Common/Models/ContactUs.cs @@ -25,12 +25,10 @@ public class ContactUs : BaseEntity, ILocationId, IEmail [Required(ErrorMessage = "Email is required")] [MaxLength(255)] - [CustomEmailValidation] public String Email { get; set; } = string.Empty; [Required(ErrorMessage = "Phone is required")] [MaxLength(14)] - [CustomPhoneValidation] public String Phone { get; set; } = string.Empty; [Required(ErrorMessage = "Message is required")] diff --git a/BedBrigade.Common/Models/Donation.cs b/BedBrigade.Common/Models/Donation.cs index 05386675..d7f27753 100644 --- a/BedBrigade.Common/Models/Donation.cs +++ b/BedBrigade.Common/Models/Donation.cs @@ -16,7 +16,6 @@ public class Donation : BaseEntity, ILocationId [Required] [MaxLength(255)] - [CustomEmailValidation] public String Email { get; set; } = string.Empty; [Required] diff --git a/BedBrigade.Common/Models/User.cs b/BedBrigade.Common/Models/User.cs index ec55fe45..50b74b10 100644 --- a/BedBrigade.Common/Models/User.cs +++ b/BedBrigade.Common/Models/User.cs @@ -25,7 +25,6 @@ public class User : BaseEntity, ILocationId, IEmail [Required(ErrorMessage = "An email address is required")] [MaxLength(255)] - [CustomEmailValidation] public String Email { get; set; } = string.Empty; [MaxLength(255)] @@ -36,7 +35,6 @@ public class User : BaseEntity, ILocationId, IEmail [MaxLength(14)] [Required(ErrorMessage = "Please enter a phone number")] - [CustomPhoneValidation] public String? Phone { get; set; } = string.Empty; public String? Role { get; set; } = string.Empty; diff --git a/BedBrigade.Common/Models/Volunteer.cs b/BedBrigade.Common/Models/Volunteer.cs index 7b6a08ce..c9e3f7e7 100644 --- a/BedBrigade.Common/Models/Volunteer.cs +++ b/BedBrigade.Common/Models/Volunteer.cs @@ -26,13 +26,11 @@ public class Volunteer : BaseEntity, ILocationId, IEmail public String LastName { get; set; } = string.Empty; [Required(ErrorMessage = "Email Address is required.")] - [CustomEmailValidation] [MaxLength(255)] public String Email { get; set; } = string.Empty; [Required(ErrorMessage = "Phone Number is required.")] [MaxLength(14)] - [CustomPhoneValidation] public String Phone { get; set; } = string.Empty; [MaxLength(80)]