Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@togglecorp/toggle-request": "^1.0.0-beta.3",
"@turf/bbox": "^6.5.0",
"@turf/buffer": "^6.5.0",
"diff": "^8.0.2",
"exceljs": "^4.3.0",
"file-saver": "^2.0.5",
"html-to-image": "^1.11.11",
Expand Down
12 changes: 9 additions & 3 deletions app/src/components/PerExportModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ import {

import Link from '#components/Link';
import { type components } from '#generated/types';
import { useRequest } from '#utils/restRequest';
import {
type GoApiBody,
useRequest,
} from '#utils/restRequest';

import i18n from './i18n.json';

type ExportStatusEnum = components<'read'>['schemas']['ExportStatusEnum'];
type ExportBody = GoApiBody<'/api/v2/pdf-export/', 'POST'>;

const EXPORT_STATUS_PENDING = 0 satisfies ExportStatusEnum;
const EXPORT_STATUS_COMPLETED = 1 satisfies ExportStatusEnum;
Expand All @@ -45,8 +49,10 @@ function PerExportModal(props: Props) {
export_id: Number(perId),
export_type: 'per' as const,
per_country: Number(countryId),
is_pga: false,
}),
is_pga: undefined,
version: undefined,
diff: undefined,
} satisfies ExportBody),
[perId, countryId],
);

Expand Down
1 change: 1 addition & 0 deletions app/src/components/domain/Admin2Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ function Admin2Input<const NAME>(props: Props<NAME>) {
>
{value?.map((admin2Id) => (
<ButtonLayout
key={admin2Id}
spacing="2xs"
after={(
<Button
Expand Down
8 changes: 6 additions & 2 deletions app/src/components/domain/DrefExportModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
type TypeOfDrefEnum,
} from '#utils/constants';
import {
type GoApiBody,
useLazyRequest,
useRequest,
} from '#utils/restRequest';
Expand All @@ -33,6 +34,7 @@ import styles from './styles.module.css';

type ExportTypeEnum = components<'read'>['schemas']['ExportTypeEnum'];
type ExportStatusEnum = components<'read'>['schemas']['ExportStatusEnum'];
type ExportBody = GoApiBody<'/api/v2/pdf-export/', 'POST'>;

const EXPORT_STATUS_PENDING = 0 satisfies ExportStatusEnum;
const EXPORT_STATUS_COMPLETED = 1 satisfies ExportStatusEnum;
Expand Down Expand Up @@ -83,9 +85,11 @@ function DrefExportModal(props: Props) {
export_id: id,
export_type: type,
is_pga: includePga,
selector: '#pdf-preview-ready',
// selector: '#pdf-preview-ready',
per_country: undefined,
};
version: undefined,
diff: undefined,
} satisfies ExportBody;
},
[
id,
Expand Down
13 changes: 13 additions & 0 deletions app/src/components/domain/EapExportModal/i18n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"namespace": "eapExportModal",
"strings": {
"exportTitle": "Export EAP",
"preparingExport": "Preparing for export...",
"waitingExport": "Waiting for the export to complete...",
"exportFailed": "Export failed",
"exportSuccessfully": "Export completed successfully!",
"downloadLinkDescription": "Click on the download link below!",
"downloadLinkLabel": "Download PDF",
"failureToExportMessage":"Failed to export PDF."
}
}
197 changes: 197 additions & 0 deletions app/src/components/domain/EapExportModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import {
useEffect,
useMemo,
useState,
} from 'react';
import { DownloadLineIcon } from '@ifrc-go/icons';
import {
Message,
Modal,
} from '@ifrc-go/ui';
import { useTranslation } from '@ifrc-go/ui/hooks';
import {
isDefined,
isNotDefined,
} from '@togglecorp/fujs';

import Link from '#components/Link';
import { type components } from '#generated/types';
import useAlert from '#hooks/useAlert';
import { EAP_TYPE_SIMPLIFIED } from '#utils/constants';
import {
type GoApiBody,
useLazyRequest,
useRequest,
} from '#utils/restRequest';

import i18n from './i18n.json';
import styles from './styles.module.css';

type EapType = components['schemas']['EapEapTypeEnumKey'];
type ExportStatusEnum = components<'read'>['schemas']['ExportStatusEnum'];

type ExportBody = GoApiBody<'/api/v2/pdf-export/', 'POST'>;

const EXPORT_STATUS_PENDING = 0 satisfies ExportStatusEnum;
const EXPORT_STATUS_COMPLETED = 1 satisfies ExportStatusEnum;
const EXPORT_STATUS_ERRORED = 2 satisfies ExportStatusEnum;

interface Props {
eapId: number;
eapType: EapType;
version?: number;
onClose: () => void;
diff?: boolean;
}

function EapExportModal(props: Props) {
const {
eapId,
eapType,
onClose,
version,
diff,
} = props;

const strings = useTranslation(i18n);
const alert = useAlert();

const [exportId, setExportId] = useState<number | undefined>();

const exportTriggerBody = useMemo<ExportBody>(
() => ({
export_id: eapId,
export_type: eapType === EAP_TYPE_SIMPLIFIED ? 'simplified' : 'full',
selector: '#pdf-preview-ready',
is_pga: undefined,
per_country: undefined,
version,
diff,
}),
[eapId, eapType, version, diff],
);

const {
pending: exportPending,
error: exportError,
trigger: triggerExport,
} = useLazyRequest({
method: 'POST',
useCurrentLanguageForMutation: true,
url: '/api/v2/pdf-export/',
body: exportTriggerBody,
onSuccess: (response) => {
if (isDefined(response.id)) {
setExportId(response.id);
}
},
onFailure: () => {
alert.show(
strings.failureToExportMessage,
{ variant: 'danger' },
);
},
});

useEffect(() => {
triggerExport(null);
}, [triggerExport]);

const {
pending: exportStatusPending,
response: exportStatusResponse,
error: exportStatusError,
} = useRequest({
skip: isNotDefined(exportId),
url: '/api/v2/pdf-export/{id}/',
// FIXME: typings should be fixed in the server
pathVariables: isDefined(exportId) ? ({ id: String(exportId) }) : undefined,
shouldPoll: (poll) => {
if (poll?.errored || poll?.value?.status !== EXPORT_STATUS_PENDING) {
return -1;
}

return 5000;
},
});

const exportStatus = useMemo(() => {
if (exportPending) {
return 'PREPARE';
}

if (exportStatusPending || exportStatusResponse?.status === EXPORT_STATUS_PENDING) {
return 'WAITING';
}

if (isDefined(exportStatusError)
|| isDefined(exportError)
|| (isDefined(exportStatusResponse)
&& exportStatusResponse.status === EXPORT_STATUS_ERRORED)
) {
return 'FAILED';
}

if (isDefined(exportStatusResponse)
&& isDefined(exportStatusResponse.status === EXPORT_STATUS_COMPLETED)
&& isDefined(exportStatusResponse.pdf_file)
) {
return 'SUCCESS';
}

return 'NOT_STARTED';
}, [
exportPending,
exportStatusError,
exportError,
exportStatusPending,
exportStatusResponse,
]);

return (
<Modal
heading={strings.exportTitle}
onClose={onClose}
className={styles.drefExportModal}
>
{exportStatus === 'PREPARE' && (
<Message
pending
title={strings.preparingExport}
/>
)}
{exportStatus === 'WAITING' && (
<Message
pending
title={strings.waitingExport}
/>
)}
{exportStatus === 'FAILED' && (
<Message
title={strings.exportFailed}
description={exportError?.value.messageForNotification
?? exportStatusError?.value.messageForNotification}
/>
)}
{exportStatus === 'SUCCESS' && (
<Message
title={strings.exportSuccessfully}
description={strings.downloadLinkDescription}
actions={(
<Link
colorVariant="primary"
styleVariant="outline"
href={exportStatusResponse?.pdf_file}
before={<DownloadLineIcon className={styles.icon} />}
external
>
{strings.downloadLinkLabel}
</Link>
)}
/>
)}
</Modal>
);
}

export default EapExportModal;
5 changes: 5 additions & 0 deletions app/src/components/domain/EapExportModal/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.dref-export-modal {
.icon {
font-size: var(--go-ui-height-icon-multiplier);
}
}
7 changes: 4 additions & 3 deletions app/src/components/domain/ImageWithCaptionInput/i18n.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"namespace": "imageWithCaptionInput",
"strings": {
"imageWithCaptionEnterCaption": "Enter Caption",
"imageWithCaptionPreview": "preview"
"captionInputPlaceholder": "Enter Caption",
"previewFallbackText": "Preview not available",
"defaultLabel": "Select an image"
}
}
}
13 changes: 7 additions & 6 deletions app/src/components/domain/ImageWithCaptionInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ interface Props<NAME> {
error: ObjectError<InputValue> | undefined;
fileIdToUrlMap: Record<number, string>;
setFileIdToUrlMap?: React.Dispatch<React.SetStateAction<Record<number, string>>>;
label: React.ReactNode;
label?: React.ReactNode;
before?: React.ReactNode;
after?: React.ReactNode;
disabled?: boolean;
Expand All @@ -52,6 +52,8 @@ interface Props<NAME> {

// FIXME: Move this to components
function ImageWithCaptionInput<const N extends string | number>(props: Props<N>) {
const strings = useTranslation(i18n);

const {
className,
readOnly,
Expand All @@ -62,15 +64,13 @@ function ImageWithCaptionInput<const N extends string | number>(props: Props<N>)
setFileIdToUrlMap,
onChange,
error: formError,
label,
label = strings.defaultLabel,
before,
after,
disabled,
useCurrentLanguageForMutation,
} = props;

const strings = useTranslation(i18n);

const setFieldValue = useFormObject(
name,
onChange,
Expand Down Expand Up @@ -119,13 +119,14 @@ function ImageWithCaptionInput<const N extends string | number>(props: Props<N>)
// FIXME: Make Go single file input with preview
description={isDefined(fileUrl) ? (
<Image
alt={strings.imageWithCaptionPreview}
alt={strings.previewFallbackText}
src={fileUrl}
size="sm"
/>
) : undefined}
clearable
useCurrentLanguageForMutation={useCurrentLanguageForMutation}
error={error?.id}
>
{label}
</GoSingleFileInput>
Expand All @@ -136,7 +137,7 @@ function ImageWithCaptionInput<const N extends string | number>(props: Props<N>)
readOnly={readOnly}
onChange={setFieldValue}
error={error?.caption}
placeholder={strings.imageWithCaptionEnterCaption}
placeholder={strings.captionInputPlaceholder}
disabled={disabled}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"namespace": "multiImageWithCaptionInput",
"strings": {
"removeImagesButtonTitle": "Remove",
"imagePreviewAlt": "preview",
"enterCaptionPlaceholder": "Enter Caption"
"removeImageButtonTitle": "Remove",
"imagePreviewFallbackText": "Preview not available",
"defaultLabel": "Select images",
"captionInputPlaceholder": "Enter caption"
}
}
}
Loading
Loading