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": {