Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ddbe3fb
email templates
Juwang110 Apr 13, 2026
4058b03
multiple items for email template
Juwang110 Apr 16, 2026
61f6859
logic for sending emails
Juwang110 Apr 20, 2026
83baa1c
tests
Juwang110 Apr 21, 2026
ff7d8e6
Merge branch 'main' into jw/ssf-188-automated-order-lifecycle-emails
Juwang110 Apr 21, 2026
5deb81b
fix bug
Juwang110 Apr 21, 2026
6006842
comments
Juwang110 Apr 29, 2026
dd564f4
Merge branch 'main' into jw/ssf-188-automated-order-lifecycle-emails
Juwang110 Apr 29, 2026
721c0c4
Merge branch 'main' into jw/ssf-188-automated-order-lifecycle-emails
Juwang110 Apr 29, 2026
25664af
Merge branch 'main' into jw/ssf-188-automated-order-lifecycle-emails
Juwang110 Apr 30, 2026
ef55069
Merge branch 'main' into jw/ssf-188-automated-order-lifecycle-emails
Juwang110 May 1, 2026
6ef0e51
Merge branch 'main' into jw/ssf-188-automated-order-lifecycle-emails
Juwang110 May 4, 2026
2470ddb
comments
Juwang110 May 4, 2026
aa7871a
finish test
Juwang110 May 5, 2026
0c8f55a
tests and comments
Juwang110 May 5, 2026
8446636
shipment address instead of mailing
Juwang110 May 6, 2026
0701342
Merge branch 'main' into jw/ssf-188-automated-order-lifecycle-emails
Juwang110 May 6, 2026
1b7750f
food request closed email
Juwang110 May 6, 2026
f658e67
comments
Juwang110 May 7, 2026
0a285e3
Merge branch 'main' into jw/ssf-188-automated-order-lifecycle-emails
Juwang110 May 7, 2026
685bc01
minor refactoring comments
Juwang110 May 13, 2026
4d1417a
Merge branch 'main' into jw/ssf-188-automated-order-lifecycle-emails
Juwang110 May 13, 2026
d77f5b8
some comments
Juwang110 May 13, 2026
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
111 changes: 110 additions & 1 deletion apps/backend/src/emails/emailTemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export type EmailTemplate = {
additionalContent?: string;
};

export const EMAIL_REDIRECT_URL = 'localhost:4200';
export const EMAIL_REDIRECT_URL = 'https://localhost:4200';
// TODO: Change this before production to be the actual ssf email
export const SSF_PARTNER_EMAIL = 'example@gmail.com';

Expand Down Expand Up @@ -98,4 +98,113 @@ export const emailTemplates = {
<p>Best regards,<br />The Securing Safe Food Team</p>
`,
}),

pantryRequestMatchedOrder: (params: {
pantryName: string;
items: { quantity: string; product: string }[];
brand: string;
volunteerName: string;
volunteerEmail: string;
}): EmailTemplate => ({
subject: 'Your Securing Safe Food Request Has Been Matched to a Delivery',
bodyHTML: `
<p>Hi ${params.pantryName},</p>
<p>
Good news! Your recent food request through Securing Safe Food has been successfully matched to an order and is now moving forward toward delivery.
</p>
<p><strong>Items you will receive from ${params.brand}:</strong></p>
<ul>
${params.items
.map((item) => `<li>${item.quantity} of ${item.product}</li>`)
.join('')}
</ul>
<p>
To view full order details, delivery updates, and any notes from the coordinating volunteer or food manufacturer, please <a href="${EMAIL_REDIRECT_URL}/login">log into the platform</a>.
</p>
<p>
If any details change on your end or you have updated availability, please update your request in the system or email your coordinator, ${
params.volunteerName
} at <a href="mailto:${params.volunteerEmail}">${
params.volunteerEmail
}</a>.
</p>
<p>
We will continue to keep you informed as the order progresses. We’re excited to help support your pantry and looking forward to this donation!
</p>
<p>Best regards,<br />The Securing Safe Food Team</p>
`,
}),

pantryRequestClosed: (params: {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for this email, can we also send to pantry coordinator ie volunteer here?

client note in the email doc: ensure both pantry coordinator that is assigned to the donation and the pantry is attached to this email.

we should make it so we only send one at a time, so that recipients will not be able to see all other recipients

Copy link
Copy Markdown

@dburkhart07 dburkhart07 May 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i actually went about doing this for #154. I would look at the changes made here into the emails directory and copy those into here. we will need this for changing the food request closed email in this ticket: we should send an email to the pantry user's email (which we already do), bcc'ing all of the volunteers that the request was closed.

pantryName: string;
volunteerName: string;
volunteerEmail: string;
}): EmailTemplate => ({
subject: 'Your Securing Safe Food Request Has Been Completed',
bodyHTML: `
<p>Hi ${params.pantryName},</p>
<p>
Your recent food request through Securing Safe Food has been marked as complete.
We are glad to fulfill your pantry's requests! If you would like to continue receiving
donations, please submit a new food request at any time to ensure there is no interruption
in future deliveries.
</p>
<p>
To submit a new request or view past orders, please log into the platform here:
<a href="${EMAIL_REDIRECT_URL}/login">${EMAIL_REDIRECT_URL}/login</a>
</p>
<p>
If you have any questions or feedback about this order, please do not hesitate to reach out.
You can contact your pantry coordinator, ${params.volunteerName}, at
<a href="mailto:${params.volunteerEmail}">${params.volunteerEmail}</a>.
</p>
<p>Best regards,<br />The Securing Safe Food Team</p>
`,
}),

fmDonationMatchedOrder: (params: {
manufacturerName: string;
items: { quantity: string; product: string }[];
pantryName: string;
pantryAddress: string;
volunteerName: string;
volunteerEmail: string;
}): EmailTemplate => ({
subject:
'Your Securing Safe Food Donation Has Been Matched to a Pantry Order',
bodyHTML: `
<p>Hi ${params.manufacturerName},</p>
<p>
Thank you for your continued partnership with Securing Safe Food. A donation you submitted has now been successfully matched to a pantry request and is moving forward towards fulfillment.
</p>
<p><strong>Matched Items:</strong><br /></p>
<ul>
${params.items
.map((item) => `<li>${item.quantity} of ${item.product}</li>`)
.join('')}
</ul>
<p>
<strong>Recipient Pantry:</strong> ${params.pantryName}<br />
<strong>Address:</strong><br />
${params.pantryAddress}
</p>
<p>
Please <a href="${EMAIL_REDIRECT_URL}/login">log into the platform</a> to review the full delivery details, timelines, and any special handling instructions associated with this shipment.
</p>
<p>
Your support plays a direct role in expanding access to allergen-safe foods, and we truly appreciate your commitment to this work.
</p>
<p>
If you have any questions or need assistance, please contact your coordinator, ${
params.volunteerName
} at <a href="mailto:${params.volunteerEmail}">${
params.volunteerEmail
}</a>.
</p>
<p>
Thank you so much.
</p>
<p>Best regards,<br />The Securing Safe Food Team</p>
`,
}),
};
18 changes: 15 additions & 3 deletions apps/backend/src/foodRequests/request.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from './dtos/matching.dto';
import { FoodManufacturer } from '../foodManufacturers/manufacturers.entity';
import { Pantry } from '../pantries/pantries.entity';
import { AuthenticatedRequest } from '../auth/authenticated-request';

const mockRequestsService = mock<RequestsService>();

Expand Down Expand Up @@ -296,11 +297,22 @@ describe('RequestsController', () => {
it('should call requestsService.closeRequest', async () => {
const requestId = 1;

mockRequestsService.closeRequest.mockResolvedValueOnce(undefined);
mockRequestsService.closeRequest.mockResolvedValueOnce(
foodRequest1 as FoodRequest,
);

const req = { user: { id: 1 } };

await controller.closeRequest(requestId);
const result = await controller.closeRequest(
requestId,
req as AuthenticatedRequest,
);

expect(mockRequestsService.closeRequest).toHaveBeenCalledWith(requestId);
expect(result).toEqual(foodRequest1);
expect(mockRequestsService.closeRequest).toHaveBeenCalledWith(
requestId,
1,
);
});
});
});
7 changes: 5 additions & 2 deletions apps/backend/src/foodRequests/request.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
ValidationPipe,
Patch,
Delete,
Req,
} from '@nestjs/common';
import { AuthenticatedRequest } from '../auth/authenticated-request';
import { ApiBody } from '@nestjs/swagger';
import { RequestsService } from './request.service';
import { FoodRequest } from './request.entity';
Expand Down Expand Up @@ -124,7 +126,8 @@ export class RequestsController {
@Patch('/:requestId/close')
async closeRequest(
@Param('requestId', ParseIntPipe) requestId: number,
): Promise<void> {
await this.requestsService.closeRequest(requestId);
@Req() req: AuthenticatedRequest,
): Promise<FoodRequest> {
return this.requestsService.closeRequest(requestId, req.user.id);
}
}
4 changes: 4 additions & 0 deletions apps/backend/src/foodRequests/request.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { Pantry } from '../pantries/pantries.entity';
import { FoodManufacturer } from '../foodManufacturers/manufacturers.entity';
import { DonationItem } from '../donationItems/donationItems.entity';
import { EmailsModule } from '../emails/email.module';
import { User } from '../users/users.entity';
import { UsersModule } from '../users/users.module';

@Module({
imports: [
Expand All @@ -18,9 +20,11 @@ import { EmailsModule } from '../emails/email.module';
Pantry,
FoodManufacturer,
DonationItem,
User,
]),
AuthModule,
EmailsModule,
UsersModule,
],
controllers: [RequestsController],
providers: [RequestsService],
Expand Down
Loading
Loading