From d1698445285a9b0be20d5ecf02d955cbb2500081 Mon Sep 17 00:00:00 2001 From: scottheng96 <44297674+scottheng96@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:21:32 +0800 Subject: [PATCH 1/3] Merge pull request #9568 from opengovsg/chore/revert-number-field-buttons-removal chore: revert hiding of number field stepper buttons --- .../src/templates/Field/Number/NumberField.test.tsx | 10 ---------- .../src/templates/Field/Number/NumberField.tsx | 1 - 2 files changed, 11 deletions(-) diff --git a/apps/frontend/src/templates/Field/Number/NumberField.test.tsx b/apps/frontend/src/templates/Field/Number/NumberField.test.tsx index 299dc210c5..75d928661a 100644 --- a/apps/frontend/src/templates/Field/Number/NumberField.test.tsx +++ b/apps/frontend/src/templates/Field/Number/NumberField.test.tsx @@ -13,16 +13,6 @@ import * as stories from './NumberField.stories' const { ValidationRequired, ValidationOptional } = composeStories(stories) describe('validation required', () => { - it('does not render stepper buttons', () => { - render() - expect( - screen.queryByRole('button', { name: /increment/i, hidden: true }), - ).toBeNull() - expect( - screen.queryByRole('button', { name: /decrement/i, hidden: true }), - ).toBeNull() - }) - it('renders error when field is not filled before submitting', async () => { // Arrange const user = userEvent.setup() diff --git a/apps/frontend/src/templates/Field/Number/NumberField.tsx b/apps/frontend/src/templates/Field/Number/NumberField.tsx index 468952714e..eaa776a0c4 100644 --- a/apps/frontend/src/templates/Field/Number/NumberField.tsx +++ b/apps/frontend/src/templates/Field/Number/NumberField.tsx @@ -34,7 +34,6 @@ export const NumberField = ({ defaultValue="" render={({ field: { value, onChange, ...rest } }) => ( Date: Tue, 9 Jun 2026 12:29:12 +0800 Subject: [PATCH 2/3] fix: add replyTo and X-Formsg-Submission-ID headers to MRF outcome emails (#9570) * feat: include replyTo and submissionId headers to MRF workflow completion email * feat: add reply-To and submissionid to headers for approval emails * fix: formId header, fix empty responses early return for extract email function --- .../multirespondent-submission.service.ts | 12 ++++++++++-- .../multirespondent-submission.utils.ts | 17 +++++++++++++++++ .../src/app/services/mail/mail.service.ts | 12 ++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.service.ts b/apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.service.ts index 3ebe7551dd..a79ed4e9b7 100644 --- a/apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.service.ts +++ b/apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.service.ts @@ -68,6 +68,7 @@ import { reportSubmissionResponseTime } from '../submissions.statsd-client' import { MultirespondentSubmissionContent } from './multirespondent-submission.types' import { + extractEmailAnswersFromResponses, extractRespondentCopyEmailDatas, formatSubmittedStepTimestamp, getEmailFromResponses, @@ -524,13 +525,17 @@ const sendMrfOutcomeEmails = ({ if (isApproval) { return MailService.sendMrfApprovalEmail({ emails: destinationEmails, - formId: form._id, + formId: String(form._id), formTitle: form.title, responseId: submissionId, + submissionId, timestamp: latestSubmissionTimestamp, isRejected, formQuestionAnswers, attachments: emailAttachments, + replyTo: + extractEmailAnswersFromResponses(responses).join(', ') || + undefined, }).orElse((error) => { logger.error({ message: 'Failed to send approval email', @@ -547,12 +552,15 @@ const sendMrfOutcomeEmails = ({ return MailService.sendMrfWorkflowCompletionEmail({ emails: destinationEmails, - formId: form._id, + formId: String(form._id), formTitle: form.title, responseId: submissionId, + submissionId, timestamp: latestSubmissionTimestamp, formQuestionAnswers, attachments: emailAttachments, + replyTo: + extractEmailAnswersFromResponses(responses).join(', ') || undefined, }).orElse((error) => { logger.error({ message: 'Failed to send workflow completion email', diff --git a/apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.utils.ts b/apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.utils.ts index 670ce3b9be..191fbb95d3 100644 --- a/apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.utils.ts +++ b/apps/backend/src/app/modules/submission/multirespondent-submission/multirespondent-submission.utils.ts @@ -109,6 +109,23 @@ export const getEmailFromResponses = ( return field.answer.value } +export const extractEmailAnswersFromResponses = ( + responses: FieldResponsesV3, +): string[] => { + if (!responses) return [] + return Object.values(responses) + .filter( + ( + response, + ): response is Extract< + FieldResponseV3, + { fieldType: BasicField.Email } + > => response.fieldType === BasicField.Email, + ) + .map((response) => response.answer.value) + .filter(Boolean) +} + const getConditionalFieldEmailRecipient = ( form_fields: FormFieldSchema[] | FormFieldDto[], fieldId: string, diff --git a/apps/backend/src/app/services/mail/mail.service.ts b/apps/backend/src/app/services/mail/mail.service.ts index 5d9ba2dcb3..fffbb263d8 100644 --- a/apps/backend/src/app/services/mail/mail.service.ts +++ b/apps/backend/src/app/services/mail/mail.service.ts @@ -1186,17 +1186,21 @@ export class MailService { formId, formTitle, responseId, + submissionId, timestamp, formQuestionAnswers, attachments, + replyTo, }: { emails: string[] formId: string formTitle: string responseId: string + submissionId?: string timestamp: string formQuestionAnswers: QuestionAnswer[] attachments?: Mail.Attachment[] + replyTo?: string }): ResultAsync => { const emailTemplateData: EmailData = { formTitle, @@ -1213,6 +1217,8 @@ export class MailService { attachments, emailType: EmailType.WorkflowCompletion, actionName: 'sendMrfWorkflowCompletionEmail', + submissionId, + replyTo, }) } @@ -1221,19 +1227,23 @@ export class MailService { formId, formTitle, responseId, + submissionId, timestamp, isRejected, formQuestionAnswers, attachments, + replyTo, }: { emails: string[] formId: string formTitle: string responseId: string + submissionId?: string timestamp: string isRejected: boolean formQuestionAnswers: QuestionAnswer[] attachments?: Mail.Attachment[] + replyTo?: string }): ResultAsync => { const outcome = isRejected ? WorkflowOutcome.NOT_APPROVED @@ -1255,6 +1265,8 @@ export class MailService { attachments, emailType: EmailType.WorkflowApproval, actionName: 'sendMrfApprovalEmail', + submissionId, + replyTo, }) } From 476642b76bbfae7a99eed5210a2a216a710a9e5c Mon Sep 17 00:00:00 2001 From: Eliot Lim Date: Tue, 9 Jun 2026 12:29:52 +0800 Subject: [PATCH 3/3] chore: bump version to 7.26.1 --- CHANGELOG.md | 12 ++++++++++++ apps/backend/package.json | 2 +- apps/frontend/package.json | 2 +- package.json | 2 +- packages/react-email-preview/package.json | 2 +- packages/shared/package.json | 2 +- services/form-payment-reconciliation/package.json | 2 +- services/pdf-gen-sparticuz/package.json | 2 +- services/virus-scanner-guardduty/package.json | 2 +- 9 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4fee55818..eba55d67cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [7.26.1](https://github.com/opengovsg/formsg/compare/v7.26.0...v7.26.1) (2026-06-09) + + +### Bug Fixes + +* add replyTo and X-Formsg-Submission-ID headers to MRF outcome emails (#9570) ([#9570](https://github.com/opengovsg/formsg/commit/b7e9c320686c326dc9c6518da4b3a97875cdc980)) + + +### Miscellaneous + +* Merge pull request #9568 from opengovsg/chore/revert-number-field-buttons-removal ([#9568](https://github.com/opengovsg/formsg/commit/d1698445285a9b0be20d5ecf02d955cbb2500081)) + ## [7.26.0](https://github.com/opengovsg/formsg/compare/v7.25.0...v7.26.0) (2026-06-08) diff --git a/apps/backend/package.json b/apps/backend/package.json index 8151795363..b0fa627d90 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -1,6 +1,6 @@ { "name": "formsg-backend", - "version": "7.26.0", + "version": "7.26.1", "private": true, "homepage": ".", "scripts": { diff --git a/apps/frontend/package.json b/apps/frontend/package.json index ddd9a21162..eca79c9e4b 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -1,6 +1,6 @@ { "name": "formsg-frontend", - "version": "7.26.0", + "version": "7.26.1", "private": true, "homepage": ".", "type": "module", diff --git a/package.json b/package.json index 4dfc51e878..7c1b76a94d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "formsg-monorepo", - "version": "7.26.0", + "version": "7.26.1", "private": true, "description": "Form Manager for Government", "homepage": "https://form.gov.sg", diff --git a/packages/react-email-preview/package.json b/packages/react-email-preview/package.json index 2eb79c5cd6..943ec6aaa2 100644 --- a/packages/react-email-preview/package.json +++ b/packages/react-email-preview/package.json @@ -1,6 +1,6 @@ { "name": "formsg-react-email-preview", - "version": "7.26.0", + "version": "7.26.1", "private": true, "scripts": { "build": "email build", diff --git a/packages/shared/package.json b/packages/shared/package.json index 9020dabd23..7eb7e9ea3c 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "formsg-shared", - "version": "7.26.0", + "version": "7.26.1", "private": true, "description": "", "scripts": { diff --git a/services/form-payment-reconciliation/package.json b/services/form-payment-reconciliation/package.json index 505ce17ffa..796a9ab240 100644 --- a/services/form-payment-reconciliation/package.json +++ b/services/form-payment-reconciliation/package.json @@ -1,6 +1,6 @@ { "name": "formsg-payment-reconciliation", - "version": "7.26.0", + "version": "7.26.1", "private": true, "description": "Lambda function for payment reconciliation", "main": "index.js", diff --git a/services/pdf-gen-sparticuz/package.json b/services/pdf-gen-sparticuz/package.json index 8983451457..00d50665a4 100644 --- a/services/pdf-gen-sparticuz/package.json +++ b/services/pdf-gen-sparticuz/package.json @@ -1,6 +1,6 @@ { "name": "formsg-pdf-gen-sparticuz", - "version": "7.26.0", + "version": "7.26.1", "private": true, "description": "", "main": "dist/index.js", diff --git a/services/virus-scanner-guardduty/package.json b/services/virus-scanner-guardduty/package.json index 36d0816be5..df8f3c40ac 100644 --- a/services/virus-scanner-guardduty/package.json +++ b/services/virus-scanner-guardduty/package.json @@ -1,6 +1,6 @@ { "name": "formsg-virus-scanner-guardduty", - "version": "7.26.0", + "version": "7.26.1", "private": true, "description": "", "scripts": {