From 1eea6a43a39720cf0d0c631e44cc8c0a309ad31b Mon Sep 17 00:00:00 2001 From: Shreeyash Shrestha Date: Tue, 16 Sep 2025 11:50:22 +0545 Subject: [PATCH 1/3] feat(dref-translation): remove language mismatch bloker to edit and view dref form --- app/src/views/DrefApplicationForm/index.tsx | 13 +++++++------ app/src/views/DrefFinalReportForm/index.tsx | 4 ++-- app/src/views/DrefOperationalUpdateForm/index.tsx | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/src/views/DrefApplicationForm/index.tsx b/app/src/views/DrefApplicationForm/index.tsx index 755dc09d2..ec6eaed8d 100644 --- a/app/src/views/DrefApplicationForm/index.tsx +++ b/app/src/views/DrefApplicationForm/index.tsx @@ -609,13 +609,14 @@ export function Component() { const disabled = fetchingDref || saveDrefPending; // New DREFs can only be created in English - const nonEnglishCreate = isNotDefined(drefId) && currentLanguage !== 'en'; + // const nonEnglishCreate = isNotDefined(drefId) && currentLanguage !== 'en'; + const languageMismatch = isDefined(drefId) && isDefined(drefResponse) && currentLanguage !== drefResponse?.translation_module_original_language; - const shouldHideForm = nonEnglishCreate - || languageMismatch - || fetchingDref + + const shouldHideForm = fetchingDref + // || nonEnglishCreate || isDefined(drefResponseError); return ( @@ -730,11 +731,11 @@ export function Component() { originalLanguage={drefResponse.translation_module_original_language} /> )} - {nonEnglishCreate && ( + {/* nonEnglishCreate && ( - )} + ) */} {isDefined(drefResponseError) && ( Date: Thu, 18 Sep 2025 16:32:06 +0545 Subject: [PATCH 2/3] feat(dref-translation): update dref status and publish api to approve --- app/src/utils/constants.ts | 3 ++- .../AccountMyFormsDref/ActiveDrefTable/index.tsx | 5 ++++- .../DrefTableActions/index.tsx | 16 ++++++++-------- app/src/views/DrefApplicationForm/index.tsx | 3 ++- app/src/views/DrefFinalReportForm/index.tsx | 1 + .../views/DrefOperationalUpdateForm/index.tsx | 4 +++- go-api | 2 +- 7 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/src/utils/constants.ts b/app/src/utils/constants.ts index 79eee5799..8075e6b3f 100644 --- a/app/src/utils/constants.ts +++ b/app/src/utils/constants.ts @@ -84,7 +84,8 @@ export const FONT_FAMILY_HEADER = 'Montserrat'; // This should not be the same as OperationType. export type DrefStatus = components<'read'>['schemas']['DrefDrefStatusEnumKey']; // const DREF_STATUS_COMPLETED = 1 satisfies DrefStatus; -export const DREF_STATUS_IN_PROGRESS = 0 satisfies DrefStatus; +export const DREF_STATUS_DRAFT = 1 satisfies DrefStatus; +export const DREF_STATUS_APPROVED = 4 satisfies DrefStatus; export type TypeOfDrefEnum = components<'read'>['schemas']['DrefDrefDrefTypeEnumKey']; export const DREF_TYPE_IMMINENT = 0 satisfies TypeOfDrefEnum; diff --git a/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx b/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx index 5e00c6747..e4bc615e0 100644 --- a/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx +++ b/app/src/views/AccountMyFormsDref/ActiveDrefTable/index.tsx @@ -30,6 +30,7 @@ import { import useUserMe from '#hooks/domain/useUserMe'; import useFilterState from '#hooks/useFilterState'; import { + DREF_STATUS_APPROVED, DREF_TYPE_LOAN, type TypeOfDrefEnum, } from '#utils/constants'; @@ -230,13 +231,15 @@ function ActiveDrefTable(props: Props) { const { unpublished_op_update_count, - is_published, + status, has_ops_update, has_final_report, country_details, is_dref_imminent_v2, } = originalDref; + const is_published = status === DREF_STATUS_APPROVED; + const canAddOpsUpdate = (is_published ?? false) && (applicationType === 'DREF' || applicationType === 'OPS_UPDATE') && !has_final_report diff --git a/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx b/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx index 8b16ad1f7..6e3f28989 100644 --- a/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx +++ b/app/src/views/AccountMyFormsDref/DrefTableActions/index.tsx @@ -27,7 +27,7 @@ import Link from '#components/Link'; import useAlert from '#hooks/useAlert'; import useRouting from '#hooks/useRouting'; import { - DREF_STATUS_IN_PROGRESS, + DREF_STATUS_DRAFT, DREF_TYPE_IMMINENT, DREF_TYPE_LOAN, type DrefStatus, @@ -167,7 +167,7 @@ function DrefTableActions(props: Props) { pending: publishDrefPending, } = useLazyRequest({ method: 'POST', - url: '/api/v2/dref/{id}/publish/', + url: '/api/v2/dref/{id}/approve/', pathVariables: { id: String(id) }, // FIXME: typings should be fixed in the server body: () => ({} as never), @@ -198,7 +198,7 @@ function DrefTableActions(props: Props) { pending: publishOpsUpdatePending, } = useLazyRequest({ method: 'POST', - url: '/api/v2/dref-op-update/{id}/publish/', + url: '/api/v2/dref-op-update/{id}/approve/', pathVariables: { id: String(id) }, // FIXME: typings should be fixed in the server body: () => ({} as never), @@ -229,7 +229,7 @@ function DrefTableActions(props: Props) { pending: publishFinalReportPending, } = useLazyRequest({ method: 'POST', - url: '/api/v2/dref-final-report/{id}/publish/', + url: '/api/v2/dref-final-report/{id}/approve/', pathVariables: { id: String(id) }, // FIXME: typings should be fixed in the server body: () => ({} as never), @@ -385,7 +385,7 @@ function DrefTableActions(props: Props) { const canDownloadAllocation = (applicationType === 'DREF' || applicationType === 'OPS_UPDATE'); - const canApprove = status === DREF_STATUS_IN_PROGRESS && hasPermissionToApprove; + const canApprove = status === DREF_STATUS_DRAFT && hasPermissionToApprove; const shouldConfirmImminentAddOpsUpdate = drefType === DREF_TYPE_IMMINENT && isDrefImminentV2; @@ -494,7 +494,7 @@ function DrefTableActions(props: Props) { )} > - {status === DREF_STATUS_IN_PROGRESS && applicationType === 'DREF' && ( + {status === DREF_STATUS_DRAFT && applicationType === 'DREF' && ( )} - {status === DREF_STATUS_IN_PROGRESS && applicationType === 'OPS_UPDATE' && ( + {status === DREF_STATUS_DRAFT && applicationType === 'OPS_UPDATE' && ( )} - {status === DREF_STATUS_IN_PROGRESS && applicationType === 'FINAL_REPORT' && ( + {status === DREF_STATUS_DRAFT && applicationType === 'FINAL_REPORT' && ( formFields, + useCurrentLanguageForMutation: true, onSuccess: (response) => { alert.show( strings.formSaveRequestSuccessMessage, @@ -479,6 +479,7 @@ export function Component() { url: '/api/v2/dref/', method: 'POST', body: (formFields: DrefRequestPostBody) => formFields, + useCurrentLanguageForMutation: true, onSuccess: (response) => { alert.show( strings.formSaveRequestSuccessMessage, diff --git a/app/src/views/DrefFinalReportForm/index.tsx b/app/src/views/DrefFinalReportForm/index.tsx index b863231d1..c8cdfce51 100644 --- a/app/src/views/DrefFinalReportForm/index.tsx +++ b/app/src/views/DrefFinalReportForm/index.tsx @@ -305,6 +305,7 @@ export function Component() { method: 'PATCH', pathVariables: isDefined(finalReportId) ? { id: finalReportId } : undefined, body: (formFields: FinalReportRequestBody) => formFields, + useCurrentLanguageForMutation: true, onSuccess: (response) => { alert.show( strings.formSaveRequestSuccessMessage, diff --git a/app/src/views/DrefOperationalUpdateForm/index.tsx b/app/src/views/DrefOperationalUpdateForm/index.tsx index e02b78f28..254b3ef67 100644 --- a/app/src/views/DrefOperationalUpdateForm/index.tsx +++ b/app/src/views/DrefOperationalUpdateForm/index.tsx @@ -43,6 +43,7 @@ import NonFieldError from '#components/NonFieldError'; import Page from '#components/Page'; import useCurrentLanguage from '#hooks/domain/useCurrentLanguage'; import useAlert from '#hooks/useAlert'; +import { DREF_STATUS_APPROVED } from '#utils/constants'; import { type GoApiResponse, useLazyRequest, @@ -313,7 +314,7 @@ export function Component() { const prevOperationalUpdateId = useMemo(() => { const currentOpsUpdate = drefResponse ?.operational_update_details - ?.find((ou) => !ou.is_published); + ?.find((ou) => !(ou.status === DREF_STATUS_APPROVED)); if (isNotDefined(currentOpsUpdate)) { return undefined; @@ -344,6 +345,7 @@ export function Component() { } = useLazyRequest({ url: '/api/v2/dref-op-update/{id}/', method: 'PATCH', + useCurrentLanguageForMutation: true, pathVariables: isDefined(opsUpdateId) ? { id: opsUpdateId } : undefined, body: (formFields: OpsUpdateRequestBody) => formFields, onSuccess: (response) => { diff --git a/go-api b/go-api index 0b28ce554..6745b93ae 160000 --- a/go-api +++ b/go-api @@ -1 +1 @@ -Subproject commit 0b28ce5542941c45666320b7411c4d9de6f6291a +Subproject commit 6745b93aed93aff3d57751a91d6ee94c651f8112 From dbc9963b5f047bee942f8da5ebfdaa07ce43657b Mon Sep 17 00:00:00 2001 From: Shreeyash Shrestha Date: Thu, 25 Sep 2025 12:36:54 +0545 Subject: [PATCH 3/3] feat(dref-translation): use finalize api in dref table and add view mode in all dref forms --- .../domain/ImageWithCaptionInput/index.tsx | 4 + .../domain/LanguageMismatchMessage/i18n.json | 6 +- .../domain/LanguageMismatchMessage/index.tsx | 17 +-- .../MultiImageWithCaptionInput/index.tsx | 6 +- .../domain/SourceInformationInput/index.tsx | 6 +- app/src/utils/constants.ts | 2 +- .../DrefTableActions/i18n.json | 4 + .../DrefTableActions/index.tsx | 137 +++++++++++++++++- .../DrefApplicationForm/Actions/index.tsx | 24 ++- .../DrefApplicationForm/EventDetail/index.tsx | 35 ++++- .../IndicatorInput/index.tsx | 6 +- .../Operation/InterventionInput/index.tsx | 10 +- .../ActivitiesInput/index.tsx | 5 +- .../Operation/ProposedActionsInput/index.tsx | 6 + .../Operation/RiskSecurityInput/index.tsx | 6 +- .../DrefApplicationForm/Operation/index.tsx | 45 +++++- .../Overview/CopyFieldReportSection/index.tsx | 4 + .../DrefApplicationForm/Overview/index.tsx | 18 +++ .../DrefApplicationForm/Submission/index.tsx | 38 +++++ app/src/views/DrefApplicationForm/i18n.json | 2 +- app/src/views/DrefApplicationForm/index.tsx | 25 ++-- .../Actions/NeedInput/index.tsx | 5 +- .../DrefFinalReportForm/Actions/index.tsx | 16 +- .../DrefFinalReportForm/EventDetail/index.tsx | 15 +- .../IndicatorInput/index.tsx | 4 + .../Operation/InterventionInput/index.tsx | 16 +- .../ActivitiesInput/index.tsx | 5 +- .../Operation/ProposedActionsInput/index.tsx | 7 + .../Operation/RiskSecurityInput/index.tsx | 6 +- .../DrefFinalReportForm/Operation/index.tsx | 41 +++++- .../DrefFinalReportForm/Overview/index.tsx | 16 +- .../DrefFinalReportForm/Submission/index.tsx | 36 +++++ app/src/views/DrefFinalReportForm/i18n.json | 2 +- app/src/views/DrefFinalReportForm/index.tsx | 16 +- .../Actions/NeedInput/index.tsx | 5 +- .../Actions/index.tsx | 16 +- .../EventDetail/index.tsx | 26 +++- .../IndicatorInput/index.tsx | 7 +- .../Operation/InterventionInput/index.tsx | 13 +- .../Operation/RiskSecurityInput/index.tsx | 6 +- .../Operation/index.tsx | 40 ++++- .../Overview/index.tsx | 16 +- .../Submission/index.tsx | 38 +++++ .../views/DrefOperationalUpdateForm/i18n.json | 2 +- .../views/DrefOperationalUpdateForm/index.tsx | 26 +++- app/src/views/FieldReportForm/index.tsx | 1 + app/src/views/FlashUpdateForm/index.tsx | 1 + .../Actions/NeedInput/index.tsx | 5 +- .../OldDrefFinalReportForm/Actions/index.tsx | 16 +- .../EventDetail/index.tsx | 16 +- .../IndicatorInput/index.tsx | 5 +- .../Operation/InterventionInput/index.tsx | 16 +- .../Operation/RiskSecurityInput/index.tsx | 6 +- .../Operation/index.tsx | 36 ++++- .../OldDrefFinalReportForm/Overview/index.tsx | 16 +- .../Submission/index.tsx | 36 +++++ .../views/OldDrefFinalReportForm/index.tsx | 18 ++- app/src/views/PerProcessForm/index.tsx | 1 + app/src/views/ThreeWActivityForm/index.tsx | 1 + .../000051-1758782876113.json | 65 +++++++++ 60 files changed, 937 insertions(+), 88 deletions(-) create mode 100644 translationMigrations/000051-1758782876113.json diff --git a/app/src/components/domain/ImageWithCaptionInput/index.tsx b/app/src/components/domain/ImageWithCaptionInput/index.tsx index 406a54690..ec7c54690 100644 --- a/app/src/components/domain/ImageWithCaptionInput/index.tsx +++ b/app/src/components/domain/ImageWithCaptionInput/index.tsx @@ -30,6 +30,7 @@ interface Props { name: N; url: SupportedPaths; value: Value | null | undefined; + readOnly: boolean; onChange: (value: SetValueArg | undefined, name: N) => void; error: ObjectError | undefined; fileIdToUrlMap: Record; @@ -44,6 +45,7 @@ interface Props { function ImageWithCaptionInput(props: Props) { const { className, + readOnly, name, value, url, @@ -92,6 +94,7 @@ function ImageWithCaptionInput(props: Props) name="id" accept="image/*" value={value?.id} + readOnly={readOnly} onChange={handleFileInputChange} url={url} fileIdToUrlMap={fileIdToUrlMap} @@ -116,6 +119,7 @@ function ImageWithCaptionInput(props: Props) className={styles.captionInput} name="caption" value={value?.caption} + readOnly={readOnly} onChange={setFieldValue} error={error?.caption} placeholder={strings.imageWithCaptionEnterCaption} diff --git a/app/src/components/domain/LanguageMismatchMessage/i18n.json b/app/src/components/domain/LanguageMismatchMessage/i18n.json index cbf3857ce..fd8ba0f29 100644 --- a/app/src/components/domain/LanguageMismatchMessage/i18n.json +++ b/app/src/components/domain/LanguageMismatchMessage/i18n.json @@ -1,8 +1,8 @@ { "namespace": "languageMismatchMessage", "strings": { - "languageMismatchErrorTitle": "Edit not available in selected language!", - "languageMismatchErrorMessage": "This form was originally created in {originalLanguage} and cannot be edited in a different language!", - "languageMismatchHelpMessage": "Please change the language to {originalLanguage} to edit it!" + "languageMismatchErrorTitle": "Edit not available in {selectedLanguage} language!", + "languageMismatchErrorMessage": "This is because form was originally created in {originalLanguage} language and edits cannot be edited in the {selectedLanguage} language.", + "languageMismatchHelpMessage": "To make changes, please switch to the original language of the form." } } diff --git a/app/src/components/domain/LanguageMismatchMessage/index.tsx b/app/src/components/domain/LanguageMismatchMessage/index.tsx index 104a6a567..6e37324f6 100644 --- a/app/src/components/domain/LanguageMismatchMessage/index.tsx +++ b/app/src/components/domain/LanguageMismatchMessage/index.tsx @@ -14,6 +14,7 @@ interface Props { // FIXME: typings should be fixed in the server // this should be of type Language originalLanguage: string | undefined; + selectedLanguage: Language; } function LanguageMismatchMessage(props: Props) { @@ -21,7 +22,8 @@ function LanguageMismatchMessage(props: Props) { const { title = strings.languageMismatchErrorTitle, - originalLanguage = 'en', + originalLanguage, + selectedLanguage, } = props; return ( @@ -32,16 +34,13 @@ function LanguageMismatchMessage(props: Props) { resolveToString( strings.languageMismatchErrorMessage, // FIXME: this should not require cast - { originalLanguage: languageNameMapEn[originalLanguage as Language] ?? '--' }, - ) - } - actions={ - resolveToString( - strings.languageMismatchHelpMessage, - // FIXME: this should not require cast - { originalLanguage: languageNameMapEn[originalLanguage as Language] ?? '--' }, + { + originalLanguage: languageNameMapEn[originalLanguage as Language] ?? '--', + selectedLanguage: languageNameMapEn[selectedLanguage] ?? '--', + }, ) } + actions={strings.languageMismatchHelpMessage} /> ); } diff --git a/app/src/components/domain/MultiImageWithCaptionInput/index.tsx b/app/src/components/domain/MultiImageWithCaptionInput/index.tsx index 9e7ba273a..2f56e65e0 100644 --- a/app/src/components/domain/MultiImageWithCaptionInput/index.tsx +++ b/app/src/components/domain/MultiImageWithCaptionInput/index.tsx @@ -45,6 +45,7 @@ interface Props { label: React.ReactNode; icons?: React.ReactNode; actions?: React.ReactNode; + readOnly?: boolean; disabled?: boolean; } @@ -62,6 +63,7 @@ function MultiImageWithCaptionInput(props: Prop label, icons, actions, + readOnly, disabled, } = props; @@ -139,6 +141,7 @@ function MultiImageWithCaptionInput(props: Prop icons={icons} actions={actions} withoutPreview + readOnly={readOnly} disabled={disabled} > {label} @@ -168,7 +171,7 @@ function MultiImageWithCaptionInput(props: Prop ariaLabel={strings.removeImagesButtonTitle} variant="secondary" spacing="none" - disabled={disabled} + disabled={disabled || readOnly} > @@ -187,6 +190,7 @@ function MultiImageWithCaptionInput(props: Prop onChange={handleCaptionChange} error={imageError?.caption} placeholder={strings.enterCaptionPlaceholder} + readOnly={readOnly} disabled={disabled} /> diff --git a/app/src/components/domain/SourceInformationInput/index.tsx b/app/src/components/domain/SourceInformationInput/index.tsx index 8ee3a0271..33dc8dc73 100644 --- a/app/src/components/domain/SourceInformationInput/index.tsx +++ b/app/src/components/domain/SourceInformationInput/index.tsx @@ -31,6 +31,7 @@ interface Props { onRemove: (index: number) => void; index: number; disabled?: boolean; + readOnly: boolean; } function SourceInformationInput(props: Props) { @@ -41,6 +42,7 @@ function SourceInformationInput(props: Props) { index, onRemove, disabled, + readOnly, } = props; const strings = useTranslation(i18n); @@ -92,6 +94,7 @@ function SourceInformationInput(props: Props) { value={value.source_name} error={error?.source_name} onChange={onFieldChange} + readOnly={readOnly} disabled={disabled} /> @@ -258,7 +263,7 @@ function Actions(props: Props) { onRemove={onNsActionRemove} error={getErrorObject(error?.national_society_actions)} titleDisplayMap={nsActionTitleDisplayMap} - disabled={disabled} + disabled={readOnly || disabled} /> ))} @@ -272,6 +277,7 @@ function Actions(props: Props) {