Skip to content

Commit

Permalink
Merge pull request #294 from GregFinzer/feature/bulkEmailFull
Browse files Browse the repository at this point in the history
Feature/bulk email full
  • Loading branch information
GregFinzer authored Sep 20, 2023
2 parents da3fec7 + d846cb8 commit 4eb7584
Show file tree
Hide file tree
Showing 33 changed files with 1,910 additions and 171 deletions.
132 changes: 71 additions & 61 deletions BedBrigade.Client/Pages/Administration/Admin/Email.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,78 +2,88 @@
@page "/administration/admin/email"
@attribute [Authorize(Roles = RoleNames.CanSendBulkEmail)]

<h3>Send Email</h3>

<EditForm Model="@email" OnValidSubmit="@HandleSubmit">
<div class="form-group">
<label for="to">To:</label>
<InputText id="to" class="form-control" @bind-Value="email.ToAddress" />
</div>

<div class="form-group">
<label for="subject">Subject:</label>
<InputText id="subject" class="form-control" @bind-Value="email.Subject" />
</div>

<div class="form-group">
<label for="body">Body:</label>
<InputTextArea id="body" class="form-control" @bind-Value="email.Body"></InputTextArea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</EditForm>

<p>&nbsp;</p>

@if (isSuccess)
@if (Model == null || Model.Locations == null || Model.Locations.Count == 0 || Model.CurrentLocationId == 0)
{
<div class="alert alert-success alert-dismissible fade show" role="alert">
@message
</div>
<span class="spinner-border text-success m-5"></span>

<span>Loading ....</span>
}
else if (isFailure)
else
{
<div class="alert alert-danger alert-dismissible fade show" role="alert">
@message
</div>
}
<div class="container my-5 ">
<h1>Send Email</h1>
<EditForm Model="@Model" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator/>

<div class="form-group mb-3">
<label for="emailRecipientOptionDropdown">To</label>
<SfDropDownList ID="emailRecipientOptionDropdown" TItem="EnumNameValue<EmailRecipientOption>" TValue="EmailRecipientOption" DataSource=@Model.EmailRecipientOptions @bind-Value=@Model.CurrentEmailRecipientOption Enabled="true" Placeholder="Email Recipient Option" FloatLabelType="FloatLabelType.Auto">
<DropDownListFieldSettings Text="Name" Value="Value" />
<DropDownListEvents TValue="EmailRecipientOption" TItem="EnumNameValue<EmailRecipientOption>" ValueChange="EmailRecipientChangeEvent"></DropDownListEvents>
</SfDropDownList>
</div>

@if (Model.ShowLocationDropdown)
{
<div class="form-group mb-3">
<label for="locationDropdown">Location</label>
<SfDropDownList ID="locationDropdown" TValue="int" TItem="Location" Placeholder="Select a location" DataSource="@Model.Locations" @bind-Value="Model.CurrentLocationId">
<DropDownListFieldSettings Text="Name" Value="LocationId"></DropDownListFieldSettings>
<DropDownListEvents TValue="int" TItem="Location" ValueChange="LocationChangeEvent"></DropDownListEvents>
</SfDropDownList>
</div>
}

@if (Model.ShowEventDropdown)
{
<div class="form-group mb-3">
<label for="events" class="formlabel">Location Available Events&nbsp;(@Model.Schedules.Count)</label>
<SfDropDownList ID="events" TValue="int" TItem="Schedule" PopupHeight="350px" PopupWidth="350px" Placeholder="Select an event" DataSource="@Model.Schedules">
<DropDownListFieldSettings Value="ScheduleId" Text="EventSelect"></DropDownListFieldSettings>
<DropDownListEvents TValue="int" TItem="Schedule" ValueChange="ScheduleChangeEvent"></DropDownListEvents>
</SfDropDownList>
</div>
}

@code {
private bool isSuccess;
private bool isFailure;
private string message;
private EmailQueue email = new EmailQueue();

public void ShowSuccess(string successMessage)
{
isSuccess = true;
isFailure = false;
message = successMessage;
}
<div class="form-group mb-3">
<label for="subject">Subject:</label>
<InputText id="subject" class="form-control" @bind-Value="Model.Subject" maxlength="100" />
<div style="font-size: smaller">
<ValidationMessage For="@(() => Model.Subject)"></ValidationMessage>
</div>
</div>

public void ShowFailure(string failureMessage)
{
isFailure = true;
isSuccess = false;
message = failureMessage;
}
<div class="form-group mb-3">
<label for="body">Body:</label>
<InputTextArea id="body" class="form-control" style="height:240px" @bind-Value="Model.Body" maxlength="4000"></InputTextArea>
<div style="font-size: smaller">
<ValidationMessage For="@(() => Model.Body)"></ValidationMessage>
</div>
</div>

@if (showPlan)
{
<div class="alert alert-warning alert-dismissible fade show" role="alert">
@message
</div>
}


private async Task HandleSubmit()
{
email.EmailQueueId = 0;
var result = await EmailQueueLogic.QueueEmail(email);
<button type="submit" class="btn btn-primary">Submit</button>
</EditForm>

<p>&nbsp;</p>

if (result.Success)
@if (isSuccess)
{
ShowSuccess("Email successfully queued.");
<div class="alert alert-success alert-dismissible fade show" role="alert">
@message
</div>
}
else
else if (isFailure)
{
ShowFailure("Email failed to queue. " + result.Message);
<div class="alert alert-danger alert-dismissible fade show" role="alert">
@message
</div>
}
}


</div>
}
109 changes: 109 additions & 0 deletions BedBrigade.Client/Pages/Administration/Admin/Email.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using BedBrigade.Common;
using BedBrigade.Data.Models;
using BedBrigade.Data.Services;
using Microsoft.AspNetCore.Components;
using Syncfusion.Blazor.DropDowns;

namespace BedBrigade.Client.Pages.Administration.Admin
{
public partial class Email : ComponentBase
{
[Inject] private IUserDataService _svcUserDataService { get; set; }
[Inject] private ILocationDataService _svcLocationDataService { get; set; }
[Inject] private IScheduleDataService _svcScheduleDataService { get; set; }
[Inject] private IEmailQueueDataService _svcEmailQueueDataService { get; set; }

public BulkEmailModel Model { get; set; } = new();
private bool isSuccess;
private bool isFailure;
private bool showPlan;
private string message;
private bool isNationalAdmin;

protected override async Task OnInitializedAsync()
{
Model.Locations = (await _svcLocationDataService.GetAllAsync()).Data;
var user = (await _svcUserDataService.GetCurrentLoggedInUser()).Data;
Model.Body = (await _svcUserDataService.GetEmailSignature(user.UserName)).Data;
Model.Schedules = (await _svcScheduleDataService.GetFutureSchedulesByLocationId(user.LocationId)).Data;
Model.CurrentLocationId = user.LocationId;
isNationalAdmin = user.LocationId == Constants.NationalLocationId;

if (isNationalAdmin)
{
Model.EmailRecipientOptions = EnumHelper.GetEnumNameValues<EmailRecipientOption>();
}
else
{
Model.EmailRecipientOptions = EnumHelper.GetEnumNameValues<EmailRecipientOption>().Where(x => x.Value != EmailRecipientOption.Everyone).ToList();
}

Model.CurrentEmailRecipientOption = EmailRecipientOption.BedBrigadeLeadersForLocation;
Model.ShowLocationDropdown = isNationalAdmin;
Model.ShowEventDropdown = false;
await BuildPlan();
}

private async Task HandleValidSubmit()
{
var emails = await _svcEmailQueueDataService.GetEmailsToSend(Model.CurrentLocationId, Model.CurrentEmailRecipientOption, Model.CurrentScheduleId);
var result = await EmailQueueLogic.QueueBulkEmail(emails.Data, Model.Subject, Model.Body);
if (result.Success)
{
ShowSuccess("Email successfully queued.");
}
else
{
ShowFailure("Email failed to queue. " + result.Message);
}
}

private async void LocationChangeEvent(ChangeEventArgs<int, Location> args)
{
Model.CurrentLocationId = args.Value;
Model.Schedules = (await _svcScheduleDataService.GetFutureSchedulesByLocationId(Model.CurrentLocationId)).Data;
Model.CurrentScheduleId = 0;
await BuildPlan();
StateHasChanged();
}

private async Task BuildPlan()
{
message = (await _svcEmailQueueDataService.GetSendPlanMessage(Model.CurrentLocationId, Model.CurrentEmailRecipientOption, Model.CurrentScheduleId)).Data;
showPlan = true;
}

private async void ScheduleChangeEvent(ChangeEventArgs<int, Data.Models.Schedule> args)
{
Model.CurrentScheduleId = args.Value;
await BuildPlan();
StateHasChanged();
}

private async void EmailRecipientChangeEvent(ChangeEventArgs<EmailRecipientOption, EnumNameValue<EmailRecipientOption>> args)
{
Model.CurrentEmailRecipientOption = args.Value;
Model.ShowEventDropdown = Model.CurrentEmailRecipientOption.ToString().Contains("Event");
Model.ShowLocationDropdown = isNationalAdmin && (Model.CurrentEmailRecipientOption.ToString().Contains("Location")
|| Model.CurrentEmailRecipientOption.ToString().Contains("Event"));
await BuildPlan();
StateHasChanged();
}

public void ShowSuccess(string successMessage)
{
isSuccess = true;
isFailure = false;
showPlan = false;
message = successMessage;
}

public void ShowFailure(string failureMessage)
{
isFailure = true;
isSuccess = false;
showPlan = false;
message = failureMessage;
}
}
}
43 changes: 43 additions & 0 deletions BedBrigade.Common/DateUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BedBrigade.Common
{
public static class DateUtil
{
public static String MillisecondsToTimeLapse(long milliseconds)
{
TimeSpan ts = TimeSpan.FromMilliseconds(milliseconds);

string result;

if ((long)ts.TotalDays == 1)
result = string.Format("{0:n0} day, {1:n0} hours, {2:n0} minutes, {3:n0} seconds, {4:n0} milliseconds", ts.Days, ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
else if (ts.TotalDays >= 1)
result = string.Format("{0:n0} days, {1:n0} hours, {2:n0} minutes, {3:n0} seconds, {4:n0} milliseconds", ts.Days, ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
else if ((long)ts.TotalHours == 1)
result = string.Format("{0:n0} hr, {1:n0} minutes, {2:n0} seconds, {3:n0} milliseconds", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
else if (ts.TotalHours >= 1)
result = string.Format("{0:n0} hours, {1:n0} minutes, {2:n0} seconds, {3:n0} milliseconds", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
else if ((long)ts.TotalMinutes == 1)
result = string.Format("{0:n0} min, {1:n0} seconds, {2:n0} milliseconds", ts.Minutes, ts.Seconds, ts.Milliseconds);
else if (ts.TotalMinutes >= 1)
result = string.Format("{0:n0} minutes, {1:n0} seconds, {2:n0} milliseconds", ts.Minutes, ts.Seconds, ts.Milliseconds);
else if ((long)ts.TotalSeconds == 1)
result = string.Format("{0:n0} second, {1:n0} milliseconds", ts.Seconds, ts.Milliseconds);
else if (ts.TotalSeconds >= 1)
result = string.Format("{0:n0} seconds, {1:n0} milliseconds", ts.Seconds, ts.Milliseconds);
else
result = string.Format("{0:n0} milliseconds", ts.Milliseconds);

result = result.Replace(" 1 hours", " 1 hour");
result = result.Replace(" 1 minutes", " 1 minute");
result = result.Replace(" 1 seconds", " 1 second");

return result;
}
}
}
35 changes: 35 additions & 0 deletions BedBrigade.Common/EmailRecipientOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BedBrigade.Common
{
public enum EmailRecipientOption
{
[Description("Bed Brigade Leaders Nationwide")]
BedBrigadeLeadersNationwide,
[Description("Bed Brigade Leaders for Location")]
BedBrigadeLeadersForLocation,
[Description("Bed Requestors for Location")]
BedRequestorsForLocation,
[Description("Bed Requestors Who Have NOT Received A Bed for Location")]
BedRequestorsWhoHaveNotRecievedABed,
[Description("Bed Requestors Who Have Received A Bed for Location")]
BedRequestorsWhoHaveRecievedABed,
[Description("Bed Requestors For An Event")]
BedRequestorsForAnEvent,
Everyone,
[Description("Contact Us Requests for Location")]
ContactUsForLocation,
[Description("Volunteers for Location")]
VolunteersForLocation,
[Description("Volunteers With Delivery Vehicles for Location")]
VolunteersWithDeliveryVehicles,
[Description("Volunteers For An Event")]
VolunteersForAnEvent,

}
}
Loading

0 comments on commit 4eb7584

Please sign in to comment.