From a14e2479f14030df28c675cbd46cdc896506108b Mon Sep 17 00:00:00 2001 From: jeafreezy Date: Thu, 27 Feb 2025 14:38:22 +0100 Subject: [PATCH 1/6] fix: clear local storage on creation --- .../src/app/routes/models/model-details-form.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/src/app/routes/models/model-details-form.tsx b/frontend/src/app/routes/models/model-details-form.tsx index 1af08216..0f20d411 100644 --- a/frontend/src/app/routes/models/model-details-form.tsx +++ b/frontend/src/app/routes/models/model-details-form.tsx @@ -1,6 +1,20 @@ +import { useModelsContext } from "@/app/providers/models-provider"; import { ModelDetailsForm } from "@/features/model-creation/components/"; +import { useEffect } from "react"; +import { useParams } from "react-router-dom"; export const ModelDetailsFormPage = () => { + const { resetState } = useModelsContext(); + const { modelId } = useParams(); + /** + * Reset the state on first load during creation. + * Don't reset it in edit mode. + */ + useEffect(() => { + if (!!modelId) return + resetState() + }, [resetState]); + return (
Date: Thu, 27 Feb 2025 15:40:46 +0100 Subject: [PATCH 2/6] fix/fixed persistent storage --- .../src/app/providers/models-provider.tsx | 57 ++++++++++++------- .../app/routes/models/model-details-form.tsx | 15 +---- .../components/progress-bar.tsx | 13 ++--- 3 files changed, 45 insertions(+), 40 deletions(-) diff --git a/frontend/src/app/providers/models-provider.tsx b/frontend/src/app/providers/models-provider.tsx index 30a8a94c..5355ada7 100644 --- a/frontend/src/app/providers/models-provider.tsx +++ b/frontend/src/app/providers/models-provider.tsx @@ -238,8 +238,8 @@ const ModelsContext = createContext<{ isModelOwner: boolean; }>({ formData: initialFormState, - setFormData: () => {}, - handleChange: () => {}, + setFormData: () => { }, + handleChange: () => { }, createNewTrainingDatasetMutation: {} as UseMutationResult< TTrainingDataset, Error, @@ -254,13 +254,13 @@ const ModelsContext = createContext<{ >, hasLabeledTrainingAreas: false, hasAOIsWithGeometry: false, - resetState: () => {}, + resetState: () => { }, isEditMode: false, modelId: "", getFullPath: () => "", - handleModelCreationAndUpdate: () => {}, + handleModelCreationAndUpdate: () => { }, trainingDatasetCreationInProgress: false, - handleTrainingDatasetCreation: () => {}, + handleTrainingDatasetCreation: () => { }, validateEditMode: false, isPending: false, isError: false, @@ -342,7 +342,7 @@ export const ModelsProvider: React.FC<{ } }, [isError, error, navigate]); - // Fetch and prefill model details and training dataset + // Prefill formData with model details in edit mode. useEffect(() => { if (!isEditMode || isPending || !data || isError) return; @@ -380,6 +380,19 @@ export const ModelsProvider: React.FC<{ }; }, []); + useEffect(() => { + /** + * During model creation, don't reset the state on unmount to keep track of the users progress and to persist the data during page refresh. + * But during edit mode, clean it up on unmount to ensure the forms are on a clean slate. + * Given that there is already an effect that prefills the form above in edit mode, this won't affect edit mode state. + */ + if (!isEditMode) return + // Cleanup the state on component unmount + return () => { + resetState() + }; + }, []); + const createNewTrainingRequestMutation = useCreateModelTrainingRequest({ mutationConfig: { onSuccess: () => { @@ -411,9 +424,9 @@ export const ModelsProvider: React.FC<{ }, }); - const submitTrainingRequest = useCallback(() => { + const submitTrainingRequest = useCallback((id: string) => { createNewTrainingRequestMutation.mutate({ - model: modelId as string, + model: id, input_boundary_width: formData.boundaryWidth, input_contact_spacing: formData.contactSpacing, epochs: formData.epoch, @@ -422,18 +435,21 @@ export const ModelsProvider: React.FC<{ }); }, [formData, modelId]); - const handleModelCreationOrUpdateSuccess = (id?: string) => { - if (isModelOwner) { + const handleModelCreationOrUpdateSuccess = (id: string) => { + + if (isEditMode && isModelOwner) { showSuccessToast( - isEditMode - ? TOAST_NOTIFICATIONS.modelUpdateSuccess - : TOAST_NOTIFICATIONS.modelCreationSuccess, - ); + TOAST_NOTIFICATIONS.modelUpdateSuccess + ) + } else if (!isEditMode) { + showSuccessToast( + TOAST_NOTIFICATIONS.modelCreationSuccess, + ) } - navigate(`${getFullPath(MODELS_ROUTES.CONFIRMATION)}?id=${id ?? modelId}`); + navigate(`${getFullPath(MODELS_ROUTES.CONFIRMATION)}?id=${id}`); // Submit the model for training request - submitTrainingRequest(); + submitTrainingRequest(id); }; const modelCreateMutation = useCreateModel({ @@ -462,7 +478,7 @@ export const ModelsProvider: React.FC<{ // Confirm that all the training areas labels have been fetched. const hasLabeledTrainingAreas = useMemo( () => - formData.trainingAreas.every( + formData.trainingAreas.length > 0 && formData.trainingAreas.every( (aoi: TTrainingAreaFeature) => aoi.properties.label_fetched !== null, ), [formData], @@ -471,7 +487,7 @@ export const ModelsProvider: React.FC<{ // Confirm that all of the training areas have a geometry. const hasAOIsWithGeometry = useMemo( () => - formData.trainingAreas.every( + formData.trainingAreas.length > 0 && formData.trainingAreas.every( (aoi: TTrainingAreaFeature) => aoi.geometry !== null, ), [formData], @@ -487,6 +503,7 @@ export const ModelsProvider: React.FC<{ createNewTrainingDatasetMutation.isPending; const handleModelCreationAndUpdate = () => { + // The user is trying to edit their model. // In this case, send a PATCH request and submit a training request. if (isEditMode && isModelOwner) { @@ -500,7 +517,7 @@ export const ModelsProvider: React.FC<{ // The user is trying to edit another users model training area and settings. // In this case, directly submit a training request. } else if (isEditMode && !isModelOwner) { - handleModelCreationOrUpdateSuccess(); + handleModelCreationOrUpdateSuccess(modelId as string); } else { modelCreateMutation.mutate({ dataset: formData.selectedTrainingDatasetId, @@ -563,4 +580,4 @@ export const ModelsProvider: React.FC<{ ); }; -export const useModelsContext = () => useContext(ModelsContext); +export const useModelsContext = () => useContext(ModelsContext); \ No newline at end of file diff --git a/frontend/src/app/routes/models/model-details-form.tsx b/frontend/src/app/routes/models/model-details-form.tsx index 0f20d411..7d3c32d4 100644 --- a/frontend/src/app/routes/models/model-details-form.tsx +++ b/frontend/src/app/routes/models/model-details-form.tsx @@ -1,19 +1,8 @@ -import { useModelsContext } from "@/app/providers/models-provider"; + import { ModelDetailsForm } from "@/features/model-creation/components/"; -import { useEffect } from "react"; -import { useParams } from "react-router-dom"; export const ModelDetailsFormPage = () => { - const { resetState } = useModelsContext(); - const { modelId } = useParams(); - /** - * Reset the state on first load during creation. - * Don't reset it in edit mode. - */ - useEffect(() => { - if (!!modelId) return - resetState() - }, [resetState]); + return (
= ({ pages, }) => { const navigate = useNavigate(); - const { getFullPath, isModelOwner } = useModelsContext(); + const { getFullPath, isModelOwner, isEditMode } = useModelsContext(); const activeStepRef = useRef(null); const containerRef = useRef(null); @@ -50,11 +50,11 @@ const ProgressBar: React.FC = ({ const isConfirmationPage = currentPath.includes( MODELS_ROUTES.CONFIRMATION, ); - // Disable the model details and training dataset if the user is not the owner of the model + // Disable the model details and training dataset if the user is not the owner of the model and if in edit mode. const disableButton = isLastPage || isConfirmationPage || - (!isModelOwner && [0, 1].includes(index)); + (!isModelOwner && [0, 1].includes(index) && isEditMode); return (