From 455572b5f4fe6f9cd38692ff852b0242cdd137ba Mon Sep 17 00:00:00 2001 From: Amit Sharma <74371312+Amit91848@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:49:58 +0530 Subject: [PATCH] feat: Info lost when navigating without warning routing form (#18386) Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> --- apps/web/public/static/locales/en/common.json | 3 ++ .../components/InfoLostWarningDialog.tsx | 46 +++++++++++++++++++ .../components/RoutingNavBar.tsx | 15 ++++++ .../routing-forms/components/SingleForm.tsx | 16 ++++++- .../pages/form-edit/[...appPages].tsx | 7 ++- 5 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 packages/app-store/routing-forms/components/InfoLostWarningDialog.tsx diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index ec7885d63a..21eb02b616 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -2899,6 +2899,9 @@ "calibration": "Calibration", "shortfall": "Shortfall", "routing_form": "Routing Form", + "go_back_and_save": "Go back and save", + "leave_without_saving": "Leave without saving", + "leave_without_saving_description": "Are you sure you want to leave without saving changes to your routing form?", "something_unexpected_occurred": "Something unexpected occurred", "500_error_message": "It's not you, it's us.", "dry_run_mode_active": "You are doing a test booking.", diff --git a/packages/app-store/routing-forms/components/InfoLostWarningDialog.tsx b/packages/app-store/routing-forms/components/InfoLostWarningDialog.tsx new file mode 100644 index 0000000000..1f5ca032d1 --- /dev/null +++ b/packages/app-store/routing-forms/components/InfoLostWarningDialog.tsx @@ -0,0 +1,46 @@ +import { useRouter } from "next/navigation"; +import type { Dispatch, SetStateAction } from "react"; + +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { Dialog, DialogContent, Button, DialogFooter } from "@calcom/ui"; + +interface InfoLostWarningDialog { + isOpenInfoLostDialog: boolean; + setIsOpenInfoLostDialog: Dispatch>; + goToRoute: string; +} + +const InfoLostWarningDialog = (props: InfoLostWarningDialog) => { + const { t } = useLocale(); + const { isOpenInfoLostDialog, setIsOpenInfoLostDialog, goToRoute } = props; + const router = useRouter(); + return ( + + + + + + + + + ); +}; + +export { InfoLostWarningDialog }; diff --git a/packages/app-store/routing-forms/components/RoutingNavBar.tsx b/packages/app-store/routing-forms/components/RoutingNavBar.tsx index e5048132ba..3c02afd411 100644 --- a/packages/app-store/routing-forms/components/RoutingNavBar.tsx +++ b/packages/app-store/routing-forms/components/RoutingNavBar.tsx @@ -1,13 +1,21 @@ +import type { Dispatch, SetStateAction } from "react"; +import type { UseFormReturn } from "react-hook-form"; + import { HorizontalTabs } from "@calcom/ui"; import type { getSerializableForm } from "../lib/getSerializableForm"; +import type { RoutingFormWithResponseCount } from "./SingleForm"; export default function RoutingNavBar({ form, appUrl, + hookForm, + setShowInfoLostDialog, }: { form: Awaited>; appUrl: string; + hookForm: UseFormReturn; + setShowInfoLostDialog: Dispatch>; }) { const tabs = [ { @@ -17,6 +25,13 @@ export default function RoutingNavBar({ { name: "Routing", href: `${appUrl}/route-builder/${form?.id}`, + onClick: () => { + if (hookForm.formState.isDirty) { + setShowInfoLostDialog(true); + } else { + window.location.href = `${appUrl}/route-builder/${form?.id}`; + } + }, }, { name: "Reporting", diff --git a/packages/app-store/routing-forms/components/SingleForm.tsx b/packages/app-store/routing-forms/components/SingleForm.tsx index 8c4d73057e..a7aeeae80e 100644 --- a/packages/app-store/routing-forms/components/SingleForm.tsx +++ b/packages/app-store/routing-forms/components/SingleForm.tsx @@ -44,6 +44,7 @@ import { findMatchingRoute } from "../lib/processRoute"; import type { FormResponse, NonRouterRoute, SerializableForm } from "../types/types"; import { FormAction, FormActionsDropdown, FormActionsProvider } from "./FormActions"; import FormInputFields from "./FormInputFields"; +import { InfoLostWarningDialog } from "./InfoLostWarningDialog"; import RoutingNavBar from "./RoutingNavBar"; import { getServerSidePropsForSingleFormView } from "./getServerSidePropsSingleForm"; @@ -682,6 +683,7 @@ function SingleForm({ form, appUrl, Page, enrichedWithUserProfileForm }: SingleF const [isTestPreviewOpen, setIsTestPreviewOpen] = useState(false); const [skipFirstUpdate, setSkipFirstUpdate] = useState(true); + const [showInfoLostDialog, setShowInfoLostDialog] = useState(false); const hookForm = useFormContext(); useEffect(() => { @@ -944,7 +946,12 @@ function SingleForm({ form, appUrl, Page, enrichedWithUserProfileForm }: SingleF )}
- +
@@ -952,6 +959,13 @@ function SingleForm({ form, appUrl, Page, enrichedWithUserProfileForm }: SingleF + {showInfoLostDialog && ( + + )} { - hookForm.setValue(`${hookFieldNamespace}.options`, updatedOptions); + hookForm.setValue(`${hookFieldNamespace}.options`, updatedOptions, { shouldDirty: true }); }; const handleRemoveOptions = (index: number) => { @@ -206,6 +206,9 @@ function Field({ defaultValue={label || routerField?.label || ""} required {...hookForm.register(`${hookFieldNamespace}.label`)} + onChange={(e) => { + hookForm.setValue(`${hookFieldNamespace}.label`, e.target.value, { shouldDirty: true }); + }} />
@@ -220,7 +223,7 @@ function Field({ // The identifier field will have the same value as the label field until it is changed value={identifier || routerField?.identifier || label || routerField?.label || ""} onChange={(e) => { - hookForm.setValue(`${hookFieldNamespace}.identifier`, e.target.value); + hookForm.setValue(`${hookFieldNamespace}.identifier`, e.target.value, { shouldDirty: true }); }} />