Skip to content

Commit

Permalink
add prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
timarney committed Feb 3, 2025
1 parent a23eeb8 commit 903299d
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 58 deletions.
118 changes: 118 additions & 0 deletions components/clientComponents/forms/SaveAndResume/ConfirmDownload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { useCallback, useState } from "react";
import * as AlertDialog from "@radix-ui/react-alert-dialog";
import { useTranslation } from "@i18n/client";
import Markdown from "markdown-to-jsx";

import { type Language } from "@lib/types/form-builder-types";
import { Button } from "@clientComponents/globals";
import { useGCFormsContext } from "@lib/hooks/useGCFormContext";
import { slugify } from "@lib/client/clientHelpers";

export type handleCloseType = (value: boolean) => void;

declare global {
interface Window {
showSaveFilePicker: ({}) => Promise<FileSystemFileHandle>;
createWritable: () => Promise<FileSystemWritableFileStream>;
}
}

async function promptToSave(fileName: string, data: string) {
const handle = await window?.showSaveFilePicker({
suggestedName: fileName,
types: [
{
description: "Form Progress (Save to resume later)",
accept: { "text/plain": [".txt"] },
},
],
});

const writable = await handle.createWritable();
await writable.write(data);
await writable.close();
}

export const ConfirmDownload = ({
open,
handleClose,
formId,
formTitleEn,
formTitleFr,
language,
}: {
open: boolean;
handleClose: handleCloseType;
formId: string;
formTitleEn: string;
formTitleFr: string;
language: Language;
}) => {
const { t } = useTranslation("form-builder");

const { getProgressData } = useGCFormsContext();
const [saving, setSaving] = useState(false);

const handleSave = useCallback(async () => {
try {
setSaving(true);

const title = language === "en" ? formTitleEn : formTitleFr;

const fileName = `${slugify(title)}-${formId}.txt`;

const data = btoa(JSON.stringify(getProgressData()));

if (!window?.showSaveFilePicker) {
const downloadLink = document.createElement("a");
const blob = new Blob([data], { type: "text/plain" });
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = fileName;
downloadLink.click();
URL.revokeObjectURL(downloadLink.href);
} else {
await promptToSave(fileName, data);
}
setSaving(false);
} catch (error) {
setSaving(false);
}
}, [formId, formTitleEn, formTitleFr, getProgressData, language]);

return (
<AlertDialog.Root open={open}>
<AlertDialog.Portal>
<AlertDialog.Overlay className="fixed inset-0 z-[200] h-screen w-screen bg-gray-500/70" />
<AlertDialog.Content className="absolute left-1/2 top-1/2 z-[201] w-full max-w-xl -translate-x-1/2 -translate-y-1/2 rounded-2xl bg-white p-4 shadow-2xl">
<AlertDialog.Title className="text-2xl font-extrabold leading-tight">
{t("saveAndResume.prompt.title")}
</AlertDialog.Title>
<AlertDialog.Description className="pb-6">
<Markdown options={{ forceBlock: false }}>
{t("saveAndResume.prompt.description")}
</Markdown>
</AlertDialog.Description>
<div style={{ display: "flex", gap: 15, justifyContent: "flex-end" }}>
<AlertDialog.Cancel asChild>
<Button onClick={() => handleClose(false)} theme="secondary">
{t("saveAndResume.prompt.cancel")}
</Button>
</AlertDialog.Cancel>
<AlertDialog.Action asChild>
<Button
disabled={saving}
onClick={() => {
handleSave();
handleClose(true);
}}
theme="primary"
>
{t("saveAndResume.prompt.okay")}
</Button>
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
);
};
74 changes: 16 additions & 58 deletions components/clientComponents/forms/SaveAndResume/SaveAndResume.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,10 @@
import { useCallback, useState } from "react";
import { useGCFormsContext } from "@lib/hooks/useGCFormContext";
import { LinkButton } from "@serverComponents/globals/Buttons/LinkButton";
import { SubmitButton as DownloadProgress } from "@clientComponents/globals/Buttons/SubmitButton";
import { useState } from "react";
import { useTranslation } from "@i18n/client";
import { Language } from "@lib/types/form-builder-types";
import { slugify } from "@lib/client/clientHelpers";

declare global {
interface Window {
showSaveFilePicker: ({}) => Promise<FileSystemFileHandle>;
createWritable: () => Promise<FileSystemWritableFileStream>;
}
}

async function promptToSave(fileName: string, data: string) {
const handle = await window?.showSaveFilePicker({
suggestedName: fileName,
types: [
{
description: "Form Progress (Save to resume later)",
accept: { "text/plain": [".txt"] },
},
],
});

const writable = await handle.createWritable();
await writable.write(data);
await writable.close();
}
import { type Language } from "@lib/types/form-builder-types";
import { LinkButton } from "@serverComponents/globals/Buttons/LinkButton";
import { SubmitButton as DownloadProgress } from "@clientComponents/globals/Buttons/SubmitButton";
import { ConfirmDownload } from "./ConfirmDownload";

export const SaveAndResume = ({
formId,
Expand All @@ -40,50 +17,31 @@ export const SaveAndResume = ({
formTitleFr: string;
language: Language;
}) => {
const { getProgressData } = useGCFormsContext();
const { t } = useTranslation(["review", "common"]);
const [saving, setSaving] = useState(false);

const handleSave = useCallback(async () => {
try {
setSaving(true);

const title = language === "en" ? formTitleEn : formTitleFr;

const fileName = `${slugify(title)}.txt`;

const data = btoa(JSON.stringify(getProgressData()));

if (!window?.showSaveFilePicker) {
const downloadLink = document.createElement("a");
const blob = new Blob([data], { type: "text/plain" });
downloadLink.href = URL.createObjectURL(blob);
downloadLink.download = fileName;
downloadLink.click();
URL.revokeObjectURL(downloadLink.href);
} else {
await promptToSave(fileName, data);
}
setSaving(false);
} catch (error) {
setSaving(false);
}
}, [formTitleEn, formTitleFr, getProgressData, language]);
const [confirm, setConfirm] = useState(false);

return (
<div className="sticky bottom-0 z-50 mt-10 flex border-t-2 border-gcds-blue-900 bg-gcds-blue-100 p-4">
<DownloadProgress
className="mr-4"
type="button"
loading={saving}
loading={confirm}
theme="secondary"
onClick={handleSave}
onClick={() => setConfirm(true)}
>
{t("saveAndResume.saveBtn")}
</DownloadProgress>
<LinkButton.Secondary href={`/${language}/id/${formId}/resume`}>
{t("saveAndResume.resumeBtn")}
</LinkButton.Secondary>
<ConfirmDownload
formId={formId}
formTitleEn={formTitleEn}
formTitleFr={formTitleFr}
language={language}
open={confirm}
handleClose={() => setConfirm(false)}
/>
</div>
);
};
6 changes: 6 additions & 0 deletions i18n/translations/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@
"title": "Start again",
"description": "Fill out a blank form"
}
},
"prompt": {
"title": "Save your progress",
"description": "Warning you are responsible for protecting the downloaded file. If you lose it, you will not be able to resume your form.",
"okay": "Okay",
"cancel": "Cancel"
}
}
}
6 changes: 6 additions & 0 deletions i18n/translations/fr/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,12 @@
"title": "Start again FR",
"description": "Fill out a blank form FR"
}
},
"prompt": {
"title": "Save your progress [FR]",
"description": "Warning you are responsible for protecting the downloaded file. If you lose it, you will not be able to resume your form. [FR]",
"okay": "Okay [FR]",
"cancel": "Cancel [FR]"
}
}
}

0 comments on commit 903299d

Please sign in to comment.