feat: Info lost when navigating without warning routing form (#18386)
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
This commit is contained in:
co-authored by
Anik Dhabal Babu
parent
2fe4462f62
commit
455572b5f4
@@ -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.",
|
||||
|
||||
@@ -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<SetStateAction<boolean>>;
|
||||
goToRoute: string;
|
||||
}
|
||||
|
||||
const InfoLostWarningDialog = (props: InfoLostWarningDialog) => {
|
||||
const { t } = useLocale();
|
||||
const { isOpenInfoLostDialog, setIsOpenInfoLostDialog, goToRoute } = props;
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Dialog open={isOpenInfoLostDialog} onOpenChange={setIsOpenInfoLostDialog}>
|
||||
<DialogContent
|
||||
title={t("leave_without_saving")}
|
||||
description={`${t("leave_without_saving_description")}`}
|
||||
Icon="circle-alert"
|
||||
enableOverflow
|
||||
type="confirmation">
|
||||
<DialogFooter className="mt-6">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsOpenInfoLostDialog(false);
|
||||
}}
|
||||
color="minimal">
|
||||
{t("go_back_and_save")}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
setIsOpenInfoLostDialog(false);
|
||||
router.replace(goToRoute);
|
||||
}}>
|
||||
{t("leave_without_saving")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export { InfoLostWarningDialog };
|
||||
@@ -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<ReturnType<typeof getSerializableForm>>;
|
||||
appUrl: string;
|
||||
hookForm: UseFormReturn<RoutingFormWithResponseCount>;
|
||||
setShowInfoLostDialog: Dispatch<SetStateAction<boolean>>;
|
||||
}) {
|
||||
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",
|
||||
|
||||
@@ -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<RoutingFormWithResponseCount>();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -944,7 +946,12 @@ function SingleForm({ form, appUrl, Page, enrichedWithUserProfileForm }: SingleF
|
||||
)}
|
||||
</div>
|
||||
<div className="border-subtle bg-muted w-full rounded-md border p-8">
|
||||
<RoutingNavBar appUrl={appUrl} form={form} />
|
||||
<RoutingNavBar
|
||||
appUrl={appUrl}
|
||||
form={form}
|
||||
hookForm={hookForm}
|
||||
setShowInfoLostDialog={setShowInfoLostDialog}
|
||||
/>
|
||||
<Page hookForm={hookForm} form={form} appUrl={appUrl} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -952,6 +959,13 @@ function SingleForm({ form, appUrl, Page, enrichedWithUserProfileForm }: SingleF
|
||||
</ShellMain>
|
||||
</FormActionsProvider>
|
||||
</Form>
|
||||
{showInfoLostDialog && (
|
||||
<InfoLostWarningDialog
|
||||
goToRoute={`${appUrl}/route-builder/${form?.id}`}
|
||||
isOpenInfoLostDialog={showInfoLostDialog}
|
||||
setIsOpenInfoLostDialog={setShowInfoLostDialog}
|
||||
/>
|
||||
)}
|
||||
<TestFormDialog
|
||||
form={uptoDateForm}
|
||||
isTestPreviewOpen={isTestPreviewOpen}
|
||||
|
||||
@@ -91,7 +91,7 @@ function Field({
|
||||
}) || [];
|
||||
|
||||
const setOptions = (updatedOptions: SelectOption[]) => {
|
||||
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 });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-6 w-full">
|
||||
@@ -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 });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user