Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pull Request for Point Update Email #366

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/dotnet/HQ.Abstractions/Emails/UpdatedPlanningPointsEmail.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Drawing;

namespace HQ.Abstractions.Emails
{
public class UpdatedPlanningPointsEmail : NotificationEmail
{
public string? StaffName { get; set; }
public List<GetPointsV1.Point> Points { get; set; } = null!;
public string? UpdatedBy { get; set; }
public DateOnly Date { get; set; }

public static new UpdatedPlanningPointsEmail Sample = new()
{
Heading = "Planning Points Updated",
Message = "Your planing points have been updated. Please review them.",
ButtonLabel = "Open HQ",
ButtonUrl = new Uri("http://hq.localhost:4200/dashboard"),
Date = new DateOnly(2024, 7, 3),
Points = new List<GetPointsV1.Point> { new GetPointsV1.Point{
ChargeCode = "p10101",
ProjectName = "HQ"
} },
StaffName = "pshah",
UpdatedBy = "Joe Fabitz"
};
}
}
3 changes: 2 additions & 1 deletion src/dotnet/HQ.Abstractions/Enumerations/EmailMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ namespace HQ.Abstractions.Enumerations;
public enum EmailMessage
{
Notification = 1,
RejectTimeEntry = 2
RejectTimeEntry = 2,
UpdatedPlanningPoints = 3
}
24 changes: 24 additions & 0 deletions src/dotnet/HQ.Email/Views/Emails/HTML/UpdatedPlanningPoints.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@model HQ.Abstractions.Emails.UpdatedPlanningPointsEmail
@{
Layout = "_LayoutEmailNotificationHTML";
}
<mj-text>
<div><strong>Staff:</strong> @Model.StaffName</div>
<div><strong>Updated By :</strong> @Model.UpdatedBy</div>
<div><strong>Date:</strong> @Model.Date</div>
</mj-text>
<mj-table>
<tr style="white;padding:15px 0;color:white;text-align:center;padding:15px 5px;">
<td style="padding: 0 15px 0 0;font-weight:bold;border-bottom:1px solid white;">Charge Code</td>
<td style="padding: 0 15px;font-weight:bold;border-bottom:1px solid white;">Project
</td>
</tr>
@foreach (var modelPoint in Model.Points)
{
<tr style="color:white; text-align:center;">
<td style="padding: 0 15px 0 0;">@modelPoint.ChargeCode</td>
<td style="padding: 0 15px;">@modelPoint.ProjectName</td>
</tr>
}
</mj-table>
<mj-text></mj-text>
38 changes: 38 additions & 0 deletions src/dotnet/HQ.Email/Views/Emails/Text/UpdatedPlanningPoints.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@model HQ.Abstractions.Emails.UpdatedPlanningPointsEmail
@if (!String.IsNullOrEmpty(Model.Heading))
{
<text>@Model.Heading</text>
<text>---</text>
<text></text>
}

@if (!String.IsNullOrEmpty(Model.Message))
{
<text>@Model.Message</text>
<text></text>
}

Details:
Staff: @Model.StaffName
Date: @Model.Date
Updated By: @Model.UpdatedBy

<table>
<tr style="white;padding:15px 0;color:white;text-align:center;padding:15px 5px;">
<td style="padding: 0 15px 0 0;font-weight:bold;border-bottom:1px solid white;">Charge Code</td>
<td style="padding: 0 15px;font-weight:bold;border-bottom:1px solid white;">Project
</td>
</tr>
@foreach (var modelPoint in Model.Points)
{
<tr style="color:white; text-align:center;">
<td style="padding: 0 15px 0 0;">@modelPoint.ChargeCode</td>
<td style="padding: 0 15px;">@modelPoint.ProjectName</td>
</tr>
}
</table>

@if (!String.IsNullOrEmpty(Model.ButtonLabel) && Model.ButtonUrl != null)
{
<text>@Model.ButtonLabel: @Model.ButtonUrl</text>
}
24 changes: 24 additions & 0 deletions src/dotnet/HQ.Server/Controllers/EmailTemplateTestControllerV1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,30 @@ public Task<ActionResult> RejectTimeEntrySendEmail([FromForm] string to, Cancell
_emailService.SendEmail(EmailMessage.RejectTimeEntry, RejectTimeEntryEmail.Sample, to, "Reject Time Entry Test", System.Net.Mail.MailPriority.Low, null, ct)
.ToActionResult(new HQResultEndpointProfile());




[HttpGet(nameof(UpdatedPlanningPointsText))]
[ProducesResponseType<ContentResult>(StatusCodes.Status200OK, "text/plain")]
public Task<ActionResult> UpdatedPlanningPointsText(CancellationToken ct = default) =>
GetEmailTemplate(EmailMessageOutput.Text, EmailMessage.UpdatedPlanningPoints, UpdatedPlanningPointsEmail.Sample, ct);

[HttpGet(nameof(UpdatedPlanningPointsMJML))]
[ProducesResponseType<ContentResult>(StatusCodes.Status200OK, "text/plain")]
public Task<ActionResult> UpdatedPlanningPointsMJML(CancellationToken ct = default) =>
GetEmailTemplate(EmailMessageOutput.MJML, EmailMessage.UpdatedPlanningPoints, UpdatedPlanningPointsEmail.Sample, ct);

[HttpGet(nameof(UpdatedPlanningPointsHTML))]
[ProducesResponseType<ContentResult>(StatusCodes.Status200OK, "text/html")]
public Task<ActionResult> UpdatedPlanningPointsHTML(CancellationToken ct = default) =>
GetEmailTemplate(EmailMessageOutput.HTML, EmailMessage.UpdatedPlanningPoints, UpdatedPlanningPointsEmail.Sample, ct);

[HttpPost(nameof(UpdatedPlanningPointsSendEmail))]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public Task<ActionResult> UpdatedPlanningPointsSendEmail([FromForm] string to, CancellationToken ct = default) =>
_emailService.SendEmail(EmailMessage.UpdatedPlanningPoints, UpdatedPlanningPointsEmail.Sample, to, "Updated Points Test", System.Net.Mail.MailPriority.Low, null, ct)
.ToActionResult(new HQResultEndpointProfile());

private async Task<ActionResult> GetEmailTemplate<T>(EmailMessageOutput output, EmailMessage emailMessage, T model, CancellationToken ct = default) where T : BaseEmail
{
var request = new GetEmailTemplateV1.Request<T>()
Expand Down
18 changes: 15 additions & 3 deletions src/dotnet/HQ.Server/Controllers/PointControllerV1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

using FluentResults.Extensions.AspNetCore;

using Hangfire;

using HQ.Abstractions.Common;
using HQ.Abstractions.Points;
using HQ.API;
Expand All @@ -31,13 +33,15 @@ public class PointControllerV1 : ControllerBase
{
private readonly PointServiceV1 _pointService;
private readonly IAuthorizationService _authorizationService;
private readonly IBackgroundJobClient _backgroundJobClient;


public PointControllerV1(PointServiceV1 pointService, IAuthorizationService authorizationService)
public PointControllerV1(PointServiceV1 pointService, IAuthorizationService authorizationService, IBackgroundJobClient backgroundJobClient)
{
_pointService = pointService;
_authorizationService = authorizationService;

_backgroundJobClient = backgroundJobClient;
}

[Authorize(HQAuthorizationPolicies.Staff)]
Expand Down Expand Up @@ -78,8 +82,16 @@ public async Task<ActionResult> UpsertPointV1([FromBody] UpsertPointsV1.Request
{
return Forbid();
}
return await _pointService.UpsertPointV1(request, ct)
.ToActionResult(new HQResultEndpointProfile());
var result = await _pointService.UpsertPointV1(request, ct);

if (request.StaffId.HasValue)
{
if (request.StaffId != User.GetStaffId() && result.IsSuccess)
{
_backgroundJobClient.Enqueue<EmailMessageService>(t => t.SendUpdatedPlanningPointsEmail(request.StaffId.Value, User.GetStaffId()!.Value, request.Date, CancellationToken.None));
}
}
return result.ToActionResult(new HQResultEndpointProfile());
}

}
Expand Down
112 changes: 106 additions & 6 deletions src/dotnet/HQ.Server/Services/EmailMessageService.cs
pshahsancsoft marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
using System.Net.Mail;
using System.Text.RegularExpressions;

using FluentResults;
using FluentResults.Extensions.AspNetCore;

using HQ.Abstractions;
using HQ.Abstractions.Emails;
using HQ.Abstractions.EmailTemplates;
using HQ.Abstractions.Enumerations;
using HQ.Abstractions.Services;
using HQ.API;
using HQ.Server.Data;
using HQ.Server.Services;

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace HQ.Server.Services
{
public class EmailMessageService
Expand Down Expand Up @@ -202,5 +197,110 @@ public async Task SendRejectedTimeSubmissionReminderEmail(Guid staffId, DateOnly

await SendEmail(EmailMessage.Notification, model, staff.Email, "[HQ] Rejected Time Reminder", MailPriority.High, null, ct);
}

public async Task SendUpdatedPlanningPointsEmail(Guid staffId, Guid modifiedByStaffId, DateOnly date, CancellationToken ct)
{
//date in order to pull this weeks planning points
var today = date;
var startDate = today.GetPeriodStartDate(Period.Week);

// GetPiontsV1 request object
var getPointsRequest = new GetPointsV1.Request
{
StaffId = staffId,
Date = startDate
};

//get name of user who modified points
var modifyingUser = await _context.Staff
.AsNoTracking()
.SingleOrDefaultAsync(t => t.Id == modifiedByStaffId, ct);

//get name of staff whose points were modified
var staff = await _context.Staff
.AsNoTracking()
.SingleOrDefaultAsync(t => t.Id == staffId, ct);


if (staff == null || String.IsNullOrEmpty(staff.Email))
{
return;
}

var records = _context.Points
.AsNoTracking()
.Include(t => t.ChargeCode)
.Include(t => t.ChargeCode.Project)
.OrderByDescending(t => t.CreatedAt)
.AsQueryable();

var endDate = date.GetPeriodEndDate(Period.Week);


records = records
.Where(p => p.StaffId == staffId && p.Date == startDate);
var points = await records.ToListAsync(ct);
var times = _context.Times.AsNoTracking().AsQueryable().Where(t => t.StaffId == staffId && t.Date >= startDate && t.Date <= endDate);
var pointTime = 4m; // A point represents 4 hours of logged work
var timesDictionary = await times
.GroupBy(x => x.ChargeCodeId)
.ToDictionaryAsync(g => g.Key, g => g.Sum(x => x.Hours));

var _points = points.Select(t => new GetPointsV1.Point
{
ChargeCodeId = t.ChargeCodeId,
Id = t.Id,
Sequence = t.Sequence,
ChargeCode = t.ChargeCode?.Code,
ProjectName = t.ChargeCode?.Project?.Name,
ProjectId = t.ChargeCode?.Project?.Id,
}).OrderBy(t => t.Sequence).ToList();

for (int i = 0; i < 10; i++)
{
var sequence = i + 1;
var point = _points.Find(p => p.Sequence == sequence);


if (point == null)
{
var nullPoint = new GetPointsV1.Point
{
ChargeCodeId = null,
Id = null,
Sequence = sequence,
Completed = false,
ChargeCode = null,
ProjectName = null,
ProjectId = null
};
_points.Insert(i, nullPoint);
}
else
{
if (!point.ChargeCodeId.HasValue) continue;
var hours = timesDictionary.GetValueOrDefault(point.ChargeCodeId.Value, 0);
if (hours < pointTime) continue;
point.Completed = true;
timesDictionary[point.ChargeCodeId.Value] -= pointTime;
}


var pointsFinal = _points;

//set model data for the email which will be sent to user
var model = new UpdatedPlanningPointsEmail()
{
StaffName = staff.Name,
UpdatedBy = modifyingUser?.Name,
ButtonLabel = "Open HQ",
ButtonUrl = _options.CurrentValue.WebUrl,
Date = date,
Points = pointsFinal
};
//sends email
await SendEmail(EmailMessage.UpdatedPlanningPoints, model, staff.Email, "[HQ] Planning Points Updated", MailPriority.High, null, ct);
}
}
}
}