Skip to content

Commit

Permalink
Use global form is dirty
Browse files Browse the repository at this point in the history
  • Loading branch information
MiraGeowerkstatt committed Dec 3, 2024
1 parent 9638840 commit ce70ea4
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 73 deletions.
20 changes: 20 additions & 0 deletions src/client/src/pages/detail/FormDirtyContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { createContext, useContext, useState } from "react";

interface FormDirtyContextType {
isFormDirty: boolean;
setIsFormDirty: (dirty: boolean) => void;
}

const FormDirtyContext = createContext<FormDirtyContextType | undefined>(undefined);

export const FormDirtyProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [isFormDirty, setIsFormDirty] = useState(false);

return <FormDirtyContext.Provider value={{ isFormDirty, setIsFormDirty }}>{children}</FormDirtyContext.Provider>;
};

export const useFormDirty = () => {

Check warning on line 16 in src/client/src/pages/detail/FormDirtyContext.tsx

View workflow job for this annotation

GitHub Actions / Build and run tests

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
const context = useContext(FormDirtyContext);
if (!context) throw new Error("useFormDirty must be used within a FormDirtyProvider");
return context;
};
4 changes: 2 additions & 2 deletions src/client/src/pages/detail/detailHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,28 @@ import {
import DateText from "../../components/legacyComponents/dateText";
import { PromptContext } from "../../components/prompt/promptContext.tsx";
import { DetailHeaderStack } from "../../components/styledComponents.ts";
import { useFormDirty } from "./FormDirtyContext.tsx";

interface DetailHeaderProps {
editingEnabled: boolean;
setEditingEnabled: (editingEnabled: boolean) => void;
editableByCurrentUser: boolean;
borehole: BoreholeV2;
isFormDirty: boolean;
triggerReset: () => void;
}

const DetailHeader = ({
editingEnabled,
setEditingEnabled,
editableByCurrentUser,
isFormDirty,
triggerReset,
borehole,
}: DetailHeaderProps) => {
const history = useHistory();
const dispatch = useDispatch();
const { t } = useTranslation();
const { showPrompt } = useContext(PromptContext);
const { isFormDirty } = useFormDirty();
const auth = useAuth();

const toggleEditing = (editing: boolean) => {
Expand Down
102 changes: 49 additions & 53 deletions src/client/src/pages/detail/detailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ import { DetailPageContent } from "./detailPageContent.tsx";
import { DetailSideNav } from "./detailSideNav.tsx";
import { BoreholeFormInputs } from "./form/borehole/boreholePanelInterfaces.ts";
import { LocationFormInputs, LocationFormSubmission } from "./form/location/locationPanelInterfaces.tsx";
import { FormDirtyProvider } from "./FormDirtyContext.tsx";
import { useLabelingContext } from "./labeling/labelingInterfaces.tsx";
import LabelingPanel from "./labeling/labelingPanel.tsx";
import { SaveBar } from "./saveBar";

export const DetailPage: FC = () => {
const [editingEnabled, setEditingEnabled] = useState(false);
const [loading, setLoading] = useState(true);
const [isFormDirty, setIsFormDirty] = useState(false);
const [editableByCurrentUser, setEditableByCurrentUser] = useState(false);
const [borehole, setBorehole] = useState<BoreholeV2 | null>(null);
const legacyBorehole: Borehole = useSelector((state: ReduxRootState) => state.core_borehole);
Expand Down Expand Up @@ -76,10 +76,6 @@ export const DetailPage: FC = () => {
getAndUpdateBorehole(prepareLocationDataForSubmit(formInputs));
};

const handleDirtyChange = (isDirty: boolean) => {
setIsFormDirty(isDirty);
};

const triggerSubmit = () => {
boreholePanelRef.current?.submit();
locationPanelRef.current?.submit();
Expand Down Expand Up @@ -133,58 +129,58 @@ export const DetailPage: FC = () => {

return (
<>
<DetailHeader
borehole={borehole}
editingEnabled={editingEnabled}
setEditingEnabled={setEditingEnabled}
editableByCurrentUser={editableByCurrentUser}
isFormDirty={isFormDirty}
triggerReset={triggerReset}
/>
<LayoutBox>
<SidebarBox>
<DetailSideNav id={id} />
</SidebarBox>
<Stack width="100%" direction="column">
<Box
sx={{
display: "flex",
flexGrow: 1,
overflow: "auto",
flexDirection: panelPosition === "right" ? "row" : "column",
width: "100%",
}}>
<MainContentBox
<FormDirtyProvider>
<DetailHeader
borehole={borehole}
editingEnabled={editingEnabled}
setEditingEnabled={setEditingEnabled}
editableByCurrentUser={editableByCurrentUser}
triggerReset={triggerReset}
/>
<LayoutBox>
<SidebarBox>
<DetailSideNav id={id} />
</SidebarBox>
<Stack width="100%" direction="column">
<Box
sx={{
width: panelOpen && panelPosition === "right" ? "50%" : "100%",
height: panelOpen && panelPosition === "bottom" ? "50%" : "100%",
display: "flex",
flexGrow: 1,
overflow: "auto",
flexDirection: panelPosition === "right" ? "row" : "column",
width: "100%",
}}>
{editingEnabled && (
<LabelingToggleButton
<MainContentBox
sx={{
width: panelOpen && panelPosition === "right" ? "50%" : "100%",
height: panelOpen && panelPosition === "bottom" ? "50%" : "100%",
}}>
{editingEnabled && (
<LabelingToggleButton
panelOpen={panelOpen}
panelPosition={panelPosition}
onClick={() => togglePanel()}
/>
)}
<DetailPageContent
editingEnabled={editingEnabled}
editableByCurrentUser={editableByCurrentUser}
locationPanelRef={locationPanelRef}
onLocationFormSubmit={onLocationFormSubmit}
boreholePanelRef={boreholePanelRef}
onBoreholeFormSubmit={onBoreholeFormSubmit}
borehole={borehole}
panelOpen={panelOpen}
panelPosition={panelPosition}
onClick={() => togglePanel()}
/>
)}
<DetailPageContent
editingEnabled={editingEnabled}
editableByCurrentUser={editableByCurrentUser}
locationPanelRef={locationPanelRef}
onLocationFormSubmit={onLocationFormSubmit}
boreholePanelRef={boreholePanelRef}
onBoreholeFormSubmit={onBoreholeFormSubmit}
handleDirtyChange={handleDirtyChange}
borehole={borehole}
panelOpen={panelOpen}
/>
</MainContentBox>
{editingEnabled && panelOpen && <LabelingPanel boreholeId={Number(id)} />}
</Box>
{editingEnabled && shouldShowSaveBar && (
<SaveBar triggerSubmit={triggerSubmit} triggerReset={triggerReset} isFormDirty={isFormDirty} />
)}
</Stack>
</LayoutBox>
</MainContentBox>
{editingEnabled && panelOpen && <LabelingPanel boreholeId={Number(id)} />}
</Box>
{editingEnabled && shouldShowSaveBar && (
<SaveBar triggerSubmit={triggerSubmit} triggerReset={triggerReset} />
)}
</Stack>
</LayoutBox>
</FormDirtyProvider>
</>
);
};
4 changes: 0 additions & 4 deletions src/client/src/pages/detail/detailPageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ interface DetailPageContentProps {
boreholePanelRef: RefObject<{ submit: () => void; reset: () => void }>;
onLocationFormSubmit: (data: LocationFormInputs) => void;
onBoreholeFormSubmit: (data: BoreholeFormInputs) => void;
handleDirtyChange: (isDirty: boolean) => void;
borehole: BoreholeV2;
panelOpen: boolean;
}
Expand All @@ -44,7 +43,6 @@ export const DetailPageContent = ({
boreholePanelRef,
onLocationFormSubmit,
onBoreholeFormSubmit,
handleDirtyChange,
borehole,
panelOpen,
}: DetailPageContentProps) => {
Expand Down Expand Up @@ -101,7 +99,6 @@ export const DetailPageContent = ({
editingEnabled={editingEnabled}
onSubmit={onLocationFormSubmit}
borehole={borehole}
onDirtyChange={handleDirtyChange}
labelingPanelOpen={panelOpen}
/>
)}
Expand All @@ -116,7 +113,6 @@ export const DetailPageContent = ({
boreholeId={id}
borehole={borehole}
editingEnabled={editingEnabled}
onDirtyChange={handleDirtyChange}
/>
)}
/>
Expand Down
3 changes: 1 addition & 2 deletions src/client/src/pages/detail/form/borehole/boreholePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Geometry from "./geometry.jsx";
import Sections from "./sections.jsx";

export const BoreholePanel = forwardRef(
({ boreholeId, borehole, editingEnabled, onDirtyChange, onSubmit }: BoreholePanelProps, ref) => {
({ boreholeId, borehole, editingEnabled, onSubmit }: BoreholePanelProps, ref) => {
const { t } = useTranslation();
const history = useHistory();
const location = useLocation();
Expand All @@ -36,7 +36,6 @@ export const BoreholePanel = forwardRef(

UseFormWithSaveBar({
formMethods,
onDirtyChange,
onSubmit,
ref,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export interface BoreholeDetailProps extends BoreholeGeneralProps {
export interface BoreholePanelProps extends BoreholeGeneralProps {
boreholeId: string;
onSubmit: (data: BoreholeFormInputs) => void;
onDirtyChange: (isDirty: boolean) => void;
}

export interface BoreholeFormInputs {
Expand Down
3 changes: 1 addition & 2 deletions src/client/src/pages/detail/form/location/locationPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import NameSegment from "./nameSegment.tsx";
import RestrictionSegment from "./restrictionSegment.tsx";

export const LocationPanel = forwardRef(
({ editingEnabled, onSubmit, onDirtyChange, borehole, labelingPanelOpen }: LocationPanelProps, ref) => {
({ editingEnabled, onSubmit, borehole, labelingPanelOpen }: LocationPanelProps, ref) => {
const [resetKey, setResetKey] = useState(0);
const formMethods = useForm<LocationFormInputs>({
mode: "onChange",
Expand Down Expand Up @@ -44,7 +44,6 @@ export const LocationPanel = forwardRef(

UseFormWithSaveBar({
formMethods,
onDirtyChange,
onSubmit,
ref,
incrementResetKey,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export interface LocationBaseProps {

export interface LocationPanelProps extends LocationBaseProps {
onSubmit: (data: LocationFormInputs) => void;
onDirtyChange: (isDirty: boolean) => void;
labelingPanelOpen: boolean;
ref: RefObject<{ submit: () => void; reset: () => void }>;
}
Expand Down
11 changes: 6 additions & 5 deletions src/client/src/pages/detail/form/useFormWithSaveBar.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { ForwardedRef, useCallback, useEffect, useImperativeHandle } from "react";
import { FieldValues, UseFormReturn } from "react-hook-form";
import { useHistory } from "react-router-dom";
import { useFormDirty } from "../FormDirtyContext.tsx";
import { useBlockNavigation } from "../useBlockNavigation.tsx";

interface UseFormWithSaveBarProps<T extends FieldValues> {
formMethods: UseFormReturn<T>;
onDirtyChange: (isDirty: boolean) => void;
onSubmit: (data: T) => void;
ref: ForwardedRef<unknown>;
incrementResetKey?: () => void;
}

export function UseFormWithSaveBar<T extends FieldValues>({
formMethods,
onDirtyChange,
onSubmit,
incrementResetKey,
ref,
}: UseFormWithSaveBarProps<T>) {
const history = useHistory();
const { handleBlockedNavigation } = useBlockNavigation(formMethods.formState.isDirty);
const { handleBlockedNavigation } = useBlockNavigation();
const { setIsFormDirty } = useFormDirty();

// Block navigation if form is dirty
history.block(nextLocation => {
Expand All @@ -30,13 +30,14 @@ export function UseFormWithSaveBar<T extends FieldValues>({

// Track form dirty state
useEffect(() => {
onDirtyChange(Object.keys(formMethods.formState.dirtyFields).length > 0);
setIsFormDirty(Object.keys(formMethods.formState.dirtyFields).length > 0);
return () => setIsFormDirty(false);
}, [
formMethods.formState.dirtyFields,
formMethods.formState.isDirty,
formMethods,
formMethods.formState,
onDirtyChange,
setIsFormDirty,
]);

// Handle form reset and submit
Expand Down
5 changes: 3 additions & 2 deletions src/client/src/pages/detail/saveBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import { Box, Stack } from "@mui/material";
import { CircleCheck, CircleX } from "lucide-react";
import { theme } from "../../AppTheme.ts";
import { DeleteButton, SaveButton } from "../../components/buttons/buttons.tsx";
import { useFormDirty } from "./FormDirtyContext.tsx";

interface SaveBarProps {
triggerSubmit: () => void;
triggerReset: () => void;
isFormDirty: boolean;
}
export const SaveBar = ({ triggerSubmit, triggerReset, isFormDirty }: SaveBarProps) => {
export const SaveBar = ({ triggerSubmit, triggerReset }: SaveBarProps) => {
const [showSaveFeedback, setShowSaveFeedback] = useState(false);
const { t } = useTranslation();
const location = useLocation();
const { isFormDirty } = useFormDirty();

useEffect(() => {
setShowSaveFeedback(false);
Expand Down
4 changes: 3 additions & 1 deletion src/client/src/pages/detail/useBlockNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import { Trash2, X } from "lucide-react";
import { PromptContext } from "../../components/prompt/promptContext.tsx";
import { useFormDirty } from "./FormDirtyContext.tsx";

interface UseBlockNavigationResult {
handleBlockedNavigation: (nextLocation: string) => boolean;
}

export const useBlockNavigation = (isFormDirty: boolean): UseBlockNavigationResult => {
export const useBlockNavigation = (): UseBlockNavigationResult => {
const [nextLocation, setNextLocation] = useState<string | null>(null);
const [confirmedNavigation, setConfirmedNavigation] = useState(false);
const { isFormDirty } = useFormDirty();
const { showPrompt } = useContext(PromptContext);
const { t } = useTranslation();
const history = useHistory();
Expand Down

0 comments on commit ce70ea4

Please sign in to comment.