diff --git a/apps/frontend/src/features/admin-form/assistance/mutations.ts b/apps/frontend/src/features/admin-form/assistance/mutations.ts index 0898c993c8..47b3c3a129 100644 --- a/apps/frontend/src/features/admin-form/assistance/mutations.ts +++ b/apps/frontend/src/features/admin-form/assistance/mutations.ts @@ -1,3 +1,4 @@ +import { useTranslation } from 'react-i18next' import { useMutation, useQueryClient } from 'react-query' import { useParams } from 'react-router-dom' @@ -12,11 +13,13 @@ import { useMagicFormBuilderStore } from '../create/builder-and-design/MagicForm export const useAssistanceMutations = () => { const { formId } = useParams() + const { t } = useTranslation() if (!formId) { - throw new Error('Form ID is required') + throw new Error( + t('features.common.adminFormMutations.errors.missingFormId'), + ) } - const queryClient = useQueryClient() const toast = useToast({ status: 'success', isClosable: true }) @@ -33,7 +36,9 @@ export const useAssistanceMutations = () => { queryClient.invalidateQueries(adminFormKeys.id(formId)) toast.closeAll() toast({ - description: 'Fields created successfully', + description: t( + 'features.adminForm.assistance.toasts.fieldsCreatedSuccess', + ), status: 'success', }) } diff --git a/apps/frontend/src/features/admin-form/common/components/PreviewFormBanner/PreviewFormBanner.tsx b/apps/frontend/src/features/admin-form/common/components/PreviewFormBanner/PreviewFormBanner.tsx index 755b65c081..e315795cee 100644 --- a/apps/frontend/src/features/admin-form/common/components/PreviewFormBanner/PreviewFormBanner.tsx +++ b/apps/frontend/src/features/admin-form/common/components/PreviewFormBanner/PreviewFormBanner.tsx @@ -1,5 +1,5 @@ import { useCallback, useMemo } from 'react' -import { useTranslation } from 'react-i18next' +import { Trans, useTranslation } from 'react-i18next' import { BiArrowBack, BiDotsHorizontalRounded, BiShow } from 'react-icons/bi' import { Link as ReactLink } from 'react-router-dom' import { Waypoint } from 'react-waypoint' @@ -99,7 +99,9 @@ export const PreviewFormBanner = ({ mr={{ base: '0.5rem', md: '1rem' }} /> - {isTemplate ? 'Template Preview' : 'Form Preview'} + {isTemplate + ? t('features.common.previewFormBanner.title.template') + : t('features.common.previewFormBanner.title.form')} {isTemplate ? ( @@ -111,24 +113,30 @@ export const PreviewFormBanner = ({ > - Back to FormSG + {t('features.common.previewFormBanner.actions.backToFormSG')} } /> @@ -165,7 +173,7 @@ export const PreviewFormBanner = ({ isFullWidth={true} {...mobileDrawerButtonProps} > - Use this template + {t('features.common.previewFormBanner.actions.useTemplate')} @@ -184,16 +192,18 @@ export const PreviewFormBanner = ({ {secretEnv === 'production' ? ( - To test your payment form, replicate this form on our{' '} - - testing platform. - + + ), + }} + /> ) : ( - You will not be able to make a test payment, or view submitted - answers or attachments in Form Preview mode. Open your form to - make a test payment or form submission. + {t('features.common.previewFormBanner.payment.nonProduction')} )} @@ -202,8 +212,7 @@ export const PreviewFormBanner = ({ {!(secretEnv === 'production') && ( - You will not be able to view submitted answers or attachments in - Form Preview mode. Open your form to test a form submission. + {t('features.common.previewFormBanner.nonPayment.nonProduction')} )} diff --git a/apps/frontend/src/features/admin-form/common/mutations.ts b/apps/frontend/src/features/admin-form/common/mutations.ts index e9631d8ae9..0b95312f9b 100644 --- a/apps/frontend/src/features/admin-form/common/mutations.ts +++ b/apps/frontend/src/features/admin-form/common/mutations.ts @@ -1,4 +1,5 @@ import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import { useMutation, useQueryClient } from 'react-query' import { useNavigate, useParams } from 'react-router-dom' @@ -79,7 +80,12 @@ enum FormCollaboratorAction { export const useMutateCollaborators = () => { const { formId } = useCollaboratorWizard() - if (!formId) throw new Error('No formId provided to useMutateCollaborators') + const { t } = useTranslation() + if (!formId) { + throw new Error( + t('features.common.adminFormMutations.errors.missingFormId'), + ) + } const navigate = useNavigate() const queryClient = useQueryClient() @@ -103,51 +109,59 @@ export const useMutateCollaborators = () => { [formId, queryClient], ) - const getMappedBadRequestErrorMessage = ( - formCollaboratorAction: FormCollaboratorAction, - originalErrorMessage: string, - ): string => { - let badRequestErrorMessage - switch (formCollaboratorAction) { - case FormCollaboratorAction.ADD: - badRequestErrorMessage = `The collaborator was unable to be added or edited. Please try again or refresh the page.` - break - case FormCollaboratorAction.TRANSFER_OWNERSHIP: - badRequestErrorMessage = originalErrorMessage - break - default: - badRequestErrorMessage = `Sorry, an error occurred. Please refresh the page and try again later.` - } - - return badRequestErrorMessage - } + const getMappedBadRequestErrorMessage = useCallback( + ( + formCollaboratorAction: FormCollaboratorAction, + originalErrorMessage: string, + ): string => { + switch (formCollaboratorAction) { + case FormCollaboratorAction.ADD: + return t( + 'features.common.adminFormMutations.collaborators.errors.badRequestAddOrEdit', + ) + case FormCollaboratorAction.TRANSFER_OWNERSHIP: + return originalErrorMessage + default: + return t( + 'features.common.adminFormMutations.collaborators.errors.badRequestGeneric', + ) + } + }, + [t], + ) - const getMappedDefaultErrorMessage = ( - formCollaboratorAction: FormCollaboratorAction, - ): string => { - let defaultErrorMessage - switch (formCollaboratorAction) { - case FormCollaboratorAction.ADD: - defaultErrorMessage = 'Error adding collaborator.' - break - case FormCollaboratorAction.UPDATE: - defaultErrorMessage = 'Error updating collaborator.' - break - case FormCollaboratorAction.REMOVE: - defaultErrorMessage = 'Error removing collaborator.' - break - case FormCollaboratorAction.REMOVE_SELF: - defaultErrorMessage = 'Error removing self.' - break - case FormCollaboratorAction.TRANSFER_OWNERSHIP: - defaultErrorMessage = 'Error transfering form ownership.' - break - //should not reach - default: - defaultErrorMessage = 'Error.' - } - return defaultErrorMessage - } + const getMappedDefaultErrorMessage = useCallback( + (formCollaboratorAction: FormCollaboratorAction): string => { + switch (formCollaboratorAction) { + case FormCollaboratorAction.ADD: + return t( + 'features.common.adminFormMutations.collaborators.errors.add', + ) + case FormCollaboratorAction.UPDATE: + return t( + 'features.common.adminFormMutations.collaborators.errors.update', + ) + case FormCollaboratorAction.REMOVE: + return t( + 'features.common.adminFormMutations.collaborators.errors.remove', + ) + case FormCollaboratorAction.REMOVE_SELF: + return t( + 'features.common.adminFormMutations.collaborators.errors.removeSelf', + ) + case FormCollaboratorAction.TRANSFER_OWNERSHIP: + return t( + 'features.common.adminFormMutations.collaborators.errors.transferOwnership', + ) + //should not reach + default: + return t( + 'features.common.adminFormMutations.collaborators.errors.generic', + ) + } + }, + [t], + ) const getMappedErrorMessage = useCallback( ( @@ -161,8 +175,15 @@ export const useMutateCollaborators = () => { switch (error.code) { case 422: errorMessage = requestEmail - ? `${requestEmail} is not part of a whitelisted agency` - : `An unexpected error 422 happened` + ? t( + 'features.common.adminFormMutations.collaborators.errors.notWhitelistedAgency', + { + email: requestEmail, + }, + ) + : t( + 'features.common.adminFormMutations.collaborators.errors.unexpected422', + ) break case 400: errorMessage = getMappedBadRequestErrorMessage( @@ -178,7 +199,7 @@ export const useMutateCollaborators = () => { // if error is not of type HttpError return the error message encapsulated in Error object return error.message }, - [], + [getMappedBadRequestErrorMessage, getMappedDefaultErrorMessage, t], ) const handleSuccess = useCallback( @@ -232,7 +253,9 @@ export const useMutateCollaborators = () => { ) if (index === -1) throw new Error( - 'Collaborator to update does not seem to exist. Refresh and try again.', + t( + 'features.common.adminFormMutations.collaborators.collaboratorNotFound', + ), ) const permissionListToUpdate = currentPermissions.slice() // Replace old permissions with new permission. @@ -242,9 +265,13 @@ export const useMutateCollaborators = () => { }, { onSuccess: (newData, { permissionToUpdate }) => { - const toastDescription = `${ - permissionToUpdate.email - } has been updated to the ${permissionsToRole(permissionToUpdate)} role` + const toastDescription = t( + 'features.common.adminFormMutations.collaborators.success.updatedToRole', + { + email: permissionToUpdate.email, + role: permissionsToRole(permissionToUpdate), + }, + ) handleSuccess({ newData, toastDescription }) }, onError: (error: Error, { permissionToUpdate }) => { @@ -264,9 +291,13 @@ export const useMutateCollaborators = () => { }, { onSuccess: (newData, { newPermission }) => { - const toastDescription = `${ - newPermission.email - } has been added as a ${permissionsToRole(newPermission)}` + const toastDescription = t( + 'features.common.adminFormMutations.collaborators.success.addedAs', + { + email: newPermission.email, + role: permissionsToRole(newPermission), + }, + ) handleSuccess({ newData, toastDescription }) }, onError: (error: Error, { newPermission }) => { @@ -288,7 +319,13 @@ export const useMutateCollaborators = () => { { onSuccess: (newData, { permissionToRemove }) => { // TODO: Decide if we want to allow redo (via readding permission) - const toastDescription = `${permissionToRemove.email} has been removed as a collaborator` + + const toastDescription = t( + 'features.common.adminFormMutations.collaborators.success.removed', + { + email: permissionToRemove.email, + }, + ) handleSuccess({ newData, toastDescription }) }, onError: (error: Error) => { @@ -304,7 +341,12 @@ export const useMutateCollaborators = () => { toast.closeAll() // Show toast on success. toast({ - description: `${newData.form.admin.email} is now the owner of this form`, + description: t( + 'features.common.adminFormMutations.collaborators.success.newOwner', + { + email: newData.form.admin.email, + }, + ), }) // Update cached data. @@ -328,8 +370,9 @@ export const useMutateCollaborators = () => { { onSuccess: () => { toast({ - description: - 'You have removed yourself as a collaborator from the form.', + description: t( + 'features.common.adminFormMutations.collaborators.success.removeSelf', + ), }) // Remove all related queries from cache. @@ -356,7 +399,12 @@ export const useMutateCollaborators = () => { export const useMutateFormPage = () => { const { formId } = useParams() - if (!formId) throw new Error('No formId provided') + const { t } = useTranslation() + if (!formId) { + throw new Error( + t('features.common.adminFormMutations.errors.missingFormId'), + ) + } const queryClient = useQueryClient() const toast = useToast({ status: 'success', isClosable: true }) @@ -383,7 +431,9 @@ export const useMutateFormPage = () => { oldData ? { ...oldData, startPage: newData } : undefined, ) toast({ - description: 'The form header and instructions were updated.', + description: t( + 'features.common.adminFormMutations.formPage.headerAndInstructionsUpdated', + ), }) }, onError: handleError, @@ -400,7 +450,9 @@ export const useMutateFormPage = () => { (oldData) => (oldData ? { ...oldData, endPage: newData } : undefined), ) toast({ - description: 'The Thank you page was updated.', + description: t( + 'features.common.adminFormMutations.formPage.thankYouPageUpdated', + ), }) }, onError: handleError, @@ -419,7 +471,9 @@ export const useMutateFormPage = () => { oldData ? { ...oldData, payments_field: newData } : undefined, ) toast({ - description: 'The payment was updated.', + description: t( + 'features.common.adminFormMutations.formPage.paymentUpdated', + ), }) }, onError: handleError, @@ -446,7 +500,9 @@ export const useMutateFormPage = () => { : undefined, ) toast({ - description: 'Payments product was updated.', + description: t( + 'features.common.adminFormMutations.formPage.paymentsProductUpdated', + ), }) }, onError: handleError, @@ -496,6 +552,8 @@ export const usePreviewFormMutations = (formId: string) => { } export const useFormFeedbackMutations = (headers: string[]) => { + const { t } = useTranslation() + const toast = useToast({ status: 'success', isClosable: true }) const handleError = useCallback( @@ -515,7 +573,9 @@ export const useFormFeedbackMutations = (headers: string[]) => { { onSuccess: () => { toast({ - description: 'Form feedback download started', + description: t( + 'features.common.adminFormMutations.downloads.feedbackStarted', + ), }) }, onError: handleError, @@ -526,6 +586,8 @@ export const useFormFeedbackMutations = (headers: string[]) => { } export const useFormIssueMutations = (headers: string[]) => { + const { t } = useTranslation() + const toast = useToast({ status: 'success', isClosable: true }) const handleError = useCallback( @@ -545,7 +607,9 @@ export const useFormIssueMutations = (headers: string[]) => { { onSuccess: () => { toast({ - description: 'Form issues download started', + description: t( + 'features.common.adminFormMutations.downloads.issuesStarted', + ), }) }, onError: handleError, @@ -556,6 +620,8 @@ export const useFormIssueMutations = (headers: string[]) => { } export const useFormRemindersMutations = () => { + const { t } = useTranslation() + const toast = useToast({ status: 'success', isClosable: true }) const handleError = useCallback( (error: Error) => { @@ -587,7 +653,7 @@ export const useFormRemindersMutations = () => { { onSuccess: () => { toast({ - description: 'Your reminder has been sent', + description: t('features.common.adminFormMutations.reminders.sent'), }) }, onError: handleError, diff --git a/apps/frontend/src/i18n/locales/features/admin-form/assistance/en-sg.ts b/apps/frontend/src/i18n/locales/features/admin-form/assistance/en-sg.ts new file mode 100644 index 0000000000..794870c0b6 --- /dev/null +++ b/apps/frontend/src/i18n/locales/features/admin-form/assistance/en-sg.ts @@ -0,0 +1,7 @@ +import { Assistance } from '.' + +export const enSG: Assistance = { + toasts: { + fieldsCreatedSuccess: 'Fields created successfully', + }, +} diff --git a/apps/frontend/src/i18n/locales/features/admin-form/assistance/index.ts b/apps/frontend/src/i18n/locales/features/admin-form/assistance/index.ts new file mode 100644 index 0000000000..9f405e1983 --- /dev/null +++ b/apps/frontend/src/i18n/locales/features/admin-form/assistance/index.ts @@ -0,0 +1,7 @@ +export * from './en-sg' + +export interface Assistance { + toasts: { + fieldsCreatedSuccess: string + } +} diff --git a/apps/frontend/src/i18n/locales/features/admin-form/en-sg.ts b/apps/frontend/src/i18n/locales/features/admin-form/en-sg.ts index c44fb4ba27..1b14be3dd2 100644 --- a/apps/frontend/src/i18n/locales/features/admin-form/en-sg.ts +++ b/apps/frontend/src/i18n/locales/features/admin-form/en-sg.ts @@ -2,6 +2,7 @@ import { enSG as responsesCharts } from './responses/charts' import { enSG as responsesComponents } from './responses/components' import { enSG as responsesIndividualResponse } from './responses/individual-response' import { enSG as responsesResponsesPage } from './responses/responses-page' +import { enSG as assistance } from './assistance' import { enSG as collaborator } from './collaborator' import { enSG as featureTour } from './feature-tour' import { enSG as feedback } from './feedback' @@ -14,6 +15,7 @@ import { enSG as sidebar } from './sidebar' import { enSG as toasts } from './toasts' export const enSG = { + assistance, responses: { charts: responsesCharts, components: responsesComponents, diff --git a/apps/frontend/src/i18n/locales/features/admin-form/index.ts b/apps/frontend/src/i18n/locales/features/admin-form/index.ts index b7bbfc0cbc..62f14ad516 100644 --- a/apps/frontend/src/i18n/locales/features/admin-form/index.ts +++ b/apps/frontend/src/i18n/locales/features/admin-form/index.ts @@ -1,3 +1,4 @@ +export { type Assistance } from './assistance' export { type Collaborator } from './collaborator' export * from './en-sg' export { type FeatureTour } from './feature-tour' diff --git a/apps/frontend/src/i18n/locales/features/common/en-sg.ts b/apps/frontend/src/i18n/locales/features/common/en-sg.ts index 713557d06c..33330ec1a1 100644 --- a/apps/frontend/src/i18n/locales/features/common/en-sg.ts +++ b/apps/frontend/src/i18n/locales/features/common/en-sg.ts @@ -93,6 +93,29 @@ export const enSG: Common = { text: 'Edit form', ariaLabel: 'Click to edit the form', }, + previewFormBanner: { + title: { + template: 'Template Preview', + form: 'Form Preview', + }, + actions: { + backToFormSG: 'Back to FormSG', + useTemplate: 'Use this template', + templatePreviewActions: 'Template preview actions', + returnToDashboard: 'Click to return to the admin dashboard', + useTemplateAria: 'Click to use this template', + }, + payment: { + production: + 'To test your payment form, replicate this form on our testing platform.', + nonProduction: + 'You will not be able to make a test payment, or view submitted answers or attachments in Form Preview mode. Open your form to make a test payment or form submission.', + }, + nonPayment: { + nonProduction: + 'You will not be able to view submitted answers or attachments in Form Preview mode. Open your form to test a form submission.', + }, + }, moreOptions: 'More options', betaBadgeLabel: 'Beta', average: 'Average', @@ -107,4 +130,49 @@ export const enSG: Common = { completed: 'Completed', approved: 'Approved', notApproved: 'Not approved', + adminFormMutations: { + errors: { + missingFormId: 'Form ID is required', + }, + collaborators: { + errors: { + badRequestAddOrEdit: + 'The collaborator was unable to be added or edited. Please try again or refresh the page.', + badRequestGeneric: + 'Sorry, an error occurred. Please refresh the page and try again later.', + add: 'Error adding collaborator.', + update: 'Error updating collaborator.', + remove: 'Error removing collaborator.', + removeSelf: 'Error removing self.', + transferOwnership: 'Error transferring form ownership.', + generic: 'Error.', + notWhitelistedAgency: '{email} is not part of a whitelisted agency', + unexpected422: 'An unexpected error 422 happened', + }, + collaboratorNotFound: + 'Collaborator to update does not seem to exist. Refresh and try again.', + success: { + updatedToRole: '{email} has been updated to the {role} role', + addedAs: '{email} has been added as a {role}', + removed: '{email} has been removed as a collaborator', + newOwner: '{email} is now the owner of this form', + removeSelf: + 'You have removed yourself as a collaborator from the form.', + }, + }, + formPage: { + headerAndInstructionsUpdated: + 'The form header and instructions were updated.', + thankYouPageUpdated: 'The Thank you page was updated.', + paymentUpdated: 'The payment was updated.', + paymentsProductUpdated: 'Payments product was updated.', + }, + downloads: { + feedbackStarted: 'Form feedback download started', + issuesStarted: 'Form issues download started', + }, + reminders: { + sent: 'Your reminder has been sent', + }, + }, } diff --git a/apps/frontend/src/i18n/locales/features/common/index.ts b/apps/frontend/src/i18n/locales/features/common/index.ts index e5cd6d3e9d..c40e77c430 100644 --- a/apps/frontend/src/i18n/locales/features/common/index.ts +++ b/apps/frontend/src/i18n/locales/features/common/index.ts @@ -93,6 +93,26 @@ export interface Common { text: string ariaLabel: string } + previewFormBanner: { + title: { + template: string + form: string + } + actions: { + backToFormSG: string + useTemplate: string + templatePreviewActions: string + returnToDashboard: string + useTemplateAria: string + } + payment: { + production: string + nonProduction: string + } + nonPayment: { + nonProduction: string + } + } moreOptions: string betaBadgeLabel: string average: string @@ -107,4 +127,44 @@ export interface Common { completed: string approved: string notApproved: string + adminFormMutations: { + errors: { + missingFormId: string + } + collaborators: { + errors: { + badRequestAddOrEdit: string + badRequestGeneric: string + add: string + update: string + remove: string + removeSelf: string + transferOwnership: string + generic: string + notWhitelistedAgency: string + unexpected422: string + } + collaboratorNotFound: string + success: { + updatedToRole: string + addedAs: string + removed: string + newOwner: string + removeSelf: string + } + } + formPage: { + headerAndInstructionsUpdated: string + thankYouPageUpdated: string + paymentUpdated: string + paymentsProductUpdated: string + } + downloads: { + feedbackStarted: string + issuesStarted: string + } + reminders: { + sent: string + } + } } diff --git a/apps/frontend/src/i18n/locales/features/index.ts b/apps/frontend/src/i18n/locales/features/index.ts index 473ac08a9f..550a4d7547 100644 --- a/apps/frontend/src/i18n/locales/features/index.ts +++ b/apps/frontend/src/i18n/locales/features/index.ts @@ -1,4 +1,5 @@ export { + type Assistance, type Collaborator, type FeatureTour, type Feedback, diff --git a/apps/frontend/src/i18n/locales/types.ts b/apps/frontend/src/i18n/locales/types.ts index 69f1e20fb4..c5451b6cf7 100644 --- a/apps/frontend/src/i18n/locales/types.ts +++ b/apps/frontend/src/i18n/locales/types.ts @@ -4,6 +4,7 @@ import { Pagination } from './components' import { ValidationConstants } from './constants' import { App, + Assistance, Collaborator, Common, EmergencyContact, @@ -37,6 +38,7 @@ interface Translation { translation: { features: { adminForm?: { + assistance?: Assistance sidebar?: { fields?: Fields headerAndInstructions?: HeaderAndInstructions