Skip to content

Commit 3a85bae

Browse files
authored
Merge pull request #3676 from bcgov/feat/3593-2
feat(3593-2): add 'Completed' email template and trigger on edit requ…
2 parents 99a656a + 830bc12 commit 3a85bae

File tree

10 files changed

+194
-25
lines changed

10 files changed

+194
-25
lines changed

app/app/api/private-cloud/provision/[licencePlate]/route.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import { $Enums, DecisionStatus } from '@prisma/client';
1+
import { $Enums, DecisionStatus, RequestType } from '@prisma/client';
22
import { z } from 'zod';
33
import createApiHandler from '@/core/api-handler';
44
import { logger } from '@/core/logging';
55
import prisma from '@/core/prisma';
66
import { NotFoundResponse, OkResponse } from '@/core/responses';
7-
import { sendProvisionedEmails, sendDeleteRequestApprovalEmails } from '@/services/ches/private-cloud/email-handler';
7+
import {
8+
sendProvisionedEmails,
9+
sendDeleteRequestApprovalEmails,
10+
sendEditRequestCompletedEmails,
11+
} from '@/services/ches/private-cloud/email-handler';
812
import { PrivateCloudRequestedProjectWithContacts } from '@/services/nats/private-cloud';
913

1014
const pathParamSchema = z.object({
@@ -72,10 +76,12 @@ export const PUT = apiHandler(async ({ pathParams }) => {
7276
},
7377
});
7478

75-
if (request.type == 'CREATE') {
79+
if (request.type == RequestType.CREATE) {
7680
await sendProvisionedEmails(project as PrivateCloudRequestedProjectWithContacts);
77-
} else if (request.type == 'DELETE') {
81+
} else if (request.type == RequestType.DELETE) {
7882
await sendDeleteRequestApprovalEmails(project as PrivateCloudRequestedProjectWithContacts);
83+
} else if (request.type == RequestType.EDIT) {
84+
await sendEditRequestCompletedEmails(project as PrivateCloudRequestedProjectWithContacts);
7985
}
8086

8187
logger.info(`Successfully marked ${licencePlate} as provisioned.`);

app/app/api/private-cloud/requests/[id]/decision/route.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,7 @@ export const POST = apiHandler(async ({ pathParams, body, session }) => {
5252
// Subscribe users to Mautic
5353
proms.push(subscribeUsersToMautic(users, request.decisionData.cluster, 'Private'));
5454

55-
if (request.type == $Enums.RequestType.EDIT) {
56-
proms.push(sendRequestApprovalEmails(request));
57-
}
55+
proms.push(sendRequestApprovalEmails(request));
5856

5957
await Promise.all(proms);
6058

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import * as React from 'react';
2+
import { samplePrivateProduct } from './_components/Params';
3+
import EditRequestCompleteTemplate from './_templates/private-cloud/EditRequestComplete';
4+
5+
export default function EditRequestComplete() {
6+
return <EditRequestCompleteTemplate product={samplePrivateProduct} />;
7+
}

app/emails/_templates/private-cloud/CreateRequest.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ const NewRequestTemplate = ({ request, userName }: EmailProp) => {
2525
You have requested a new project set for {request.decisionData.name} on the Private Cloud OpenShift platform.
2626
Our administrators have been notified and will review your request.
2727
</Text>
28-
<Text>
29-
Please allow 3-5 minutes for the request to be processed by the backend. If it takes longer, don&apos;t
30-
hesitate to reach out to us.
31-
</Text>
3228
<Button href="https://registry.developer.gov.bc.ca/" className="bg-bcorange rounded-md px-4 py-2 text-white">
3329
View request
3430
</Button>

app/emails/_templates/private-cloud/DeleteRequest.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ const DeleteRequestTemplate = ({ request, userName }: EmailProp) => {
2222
<Text className="">
2323
{`We have received your deletion request for ${request.decisionData.name}. You will receive an email once your request has been processed and completed.`}
2424
</Text>
25-
<Text className="">
26-
Please allow 3-5 minutes for the request to be processed by the backend. If it takes longer, don&apos;t
27-
hesitate to reach out to us.
28-
</Text>
2925
</div>
3026
<div className="pb-6 mt-4 mb-4 border-solid border-0 border-b-1 border-slate-300">
3127
<ProductDetails

app/emails/_templates/private-cloud/EditRequest.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,6 @@ const EditRequestTemplate = ({ request, userName }: EmailProp) => {
3636
? ' Our administrators have been notified and will review your request.'
3737
: ' Your request will be reviewed automatically. Once the provisioning is complete, you will receive a notification email with all the relevant details and updates regarding your request.'}
3838
</Text>
39-
<Text className="">
40-
Please allow 3-5 minutes for the request to be processed by the backend. If it takes longer, don&apos;t
41-
hesitate to reach out to us.
42-
</Text>
4339
<Button
4440
href={`${BASE_URL}/private-cloud/requests/${request.id}/summary`}
4541
className="bg-bcorange rounded-md px-4 py-2 text-white"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Heading, Text } from '@react-email/components';
2+
import * as React from 'react';
3+
import Closing from '@/emails/_components/Closing';
4+
import Layout from '@/emails/_components/layout/Layout';
5+
import NamespaceDetails from '@/emails/_components/NamespaceDetails';
6+
import ProductDetails from '@/emails/_components/ProductDetails';
7+
import { PrivateCloudRequestedProjectWithContacts } from '@/services/nats/private-cloud';
8+
9+
interface EmailProp {
10+
product: PrivateCloudRequestedProjectWithContacts;
11+
}
12+
13+
const EditRequestCompleteTemplate = ({ product }: EmailProp) => {
14+
if (!product) return <></>;
15+
16+
return (
17+
<Layout>
18+
<div className="pb-6 mt-4 mb-4 border-solid border-0 border-b-1 border-slate-300">
19+
<Heading className="text-lg text-black">Your edit request has been completed!</Heading>
20+
<Text>Hi Product Team,</Text>
21+
<Text className="">{`The project set edit request for ${product.name} has been successfully completed.`}</Text>
22+
</div>
23+
<div className="pb-6 mt-4 mb-4 border-solid border-0 border-b-1 border-slate-300">
24+
<ProductDetails
25+
name={product.name}
26+
description={product.description}
27+
ministry={product.ministry}
28+
po={product.projectOwner}
29+
tl1={product.primaryTechnicalLead}
30+
tl2={product.secondaryTechnicalLead}
31+
/>
32+
</div>
33+
<div className="pb-6 mt-4 mb-4 border-solid border-0 border-b-1 border-slate-300">
34+
<NamespaceDetails cluster={product.cluster} licencePlate={product.licencePlate} />
35+
</div>
36+
<div>
37+
<Closing />
38+
</div>
39+
</Layout>
40+
);
41+
};
42+
43+
export default EditRequestCompleteTemplate;

app/emails/_templates/private-cloud/RequestApproval.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { $Enums } from '@prisma/client';
12
import { Button, Heading, Text, Link } from '@react-email/components';
23
import * as React from 'react';
34
import Closing from '@/emails/_components/Closing';
@@ -23,7 +24,7 @@ const RequestApprovalTemplate = ({ request }: EmailProp) => {
2324
const isQuotaUpgraded = isQuotaUpgrade(requested, current);
2425
const requestComment = request.requestComment ?? undefined;
2526

26-
const hasQuotaChanges =
27+
const hasQuotaChanged =
2728
changed.productionQuota || changed.testQuota || changed.developmentQuota || changed.toolsQuota;
2829

2930
return (
@@ -32,13 +33,19 @@ const RequestApprovalTemplate = ({ request }: EmailProp) => {
3233
<Heading className="text-lg text-black">Success! Your request was approved!</Heading>
3334
<Text>Hi Product Team, </Text>
3435
<Text className="">
35-
We are pleased to inform you that your request for {hasQuotaChanges ? 'a resource quota' : 'your product'}{' '}
36-
{!hasQuotaChanges && request.decisionData.name} has been approved on the Private Cloud OpenShift platform. You
37-
can now log in to{' '}
36+
We are pleased to inform you that your
37+
{(request.type === $Enums.RequestType.CREATE || request.type === $Enums.RequestType.DELETE) &&
38+
`request for your product ${!hasQuotaChanged ? request.decisionData.name : ' '}`}
39+
{hasQuotaChanged && 'request for a resource quota'} has been approved on the Private Cloud OpenShift platform.
40+
You can now log in to{' '}
3841
<Link className="mt-0 h-4" href={`https://console.apps.${request.decisionData.cluster}.devops.gov.bc.ca/`}>
3942
OpenShift cluster console{' '}
4043
</Link>{' '}
41-
{hasQuotaChanges ? 'and you will see your new resource quota values.' : 'to manage your product.'}
44+
{hasQuotaChanged ? 'and you will see your new resource quota values.' : 'to manage your product.'}
45+
</Text>
46+
<Text>
47+
Please allow 3-5 minutes for the request to be processed. If it takes longer, don&apos;t hesitate to reach out
48+
to us.
4249
</Text>
4350
<Text className="">
4451
If you have any more questions or need assistance, please reach out to the Platform Services team in the
@@ -93,7 +100,7 @@ const RequestApprovalTemplate = ({ request }: EmailProp) => {
93100
/>
94101
</div>
95102
)}
96-
{hasQuotaChanges && (
103+
{hasQuotaChanged && (
97104
<div className="pb-6 mt-4 mb-4 border-solid border-0 border-b-1 border-slate-300">
98105
<h3 className="mb-0 text-black">Resource quota changes</h3>
99106
<div className="flex flex-row flex-wrap">

app/package-lock.json

Lines changed: 105 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/services/ches/private-cloud/email-handler.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import CreateRequestTemplate from '@/emails/_templates/private-cloud/CreateReque
88
import DeleteApprovalTemplate from '@/emails/_templates/private-cloud/DeleteApproval';
99
import DeleteRequestTemplate from '@/emails/_templates/private-cloud/DeleteRequest';
1010
import EditRequestTemplate from '@/emails/_templates/private-cloud/EditRequest';
11+
import EditRequestCompleteTemplate from '@/emails/_templates/private-cloud/EditRequestComplete';
1112
import ProvisionedTemplate from '@/emails/_templates/private-cloud/Provisioned';
1213
import RequestApprovalTemplate from '@/emails/_templates/private-cloud/RequestApproval';
1314
import RequestRejectionTemplate from '@/emails/_templates/private-cloud/RequestRejection';
@@ -177,3 +178,17 @@ export const sendProvisionedEmails = async (product: PrivateCloudRequestedProjec
177178
logger.error('sendProvisionedEmails:', error);
178179
}
179180
};
181+
182+
export const sendEditRequestCompletedEmails = async (product: PrivateCloudRequestedProjectWithContacts) => {
183+
try {
184+
const email = render(EditRequestCompleteTemplate({ product }), { pretty: true });
185+
186+
await sendEmail({
187+
body: email,
188+
to: [product.projectOwner.email, product.primaryTechnicalLead.email, product.secondaryTechnicalLead?.email],
189+
subject: 'Product edit request has been completed',
190+
});
191+
} catch (error) {
192+
logger.error('sendEditRequestCompletedEmails:', error);
193+
}
194+
};

0 commit comments

Comments
 (0)