Skip to content

Commit

Permalink
issue #393: debounce form fields
Browse files Browse the repository at this point in the history
  • Loading branch information
k-allagbe committed Jan 24, 2025
1 parent 4d3425a commit 3b2950b
Show file tree
Hide file tree
Showing 14 changed files with 150 additions and 90 deletions.
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@mui/icons-material": "^6.4.1",
"@mui/material": "^6.4.1",
"@mui/material-nextjs": "^6.3.1",
"@types/lodash.debounce": "^4.0.9",
"axios": "^1.7.9",
"dotenv": "^16.4.7",
"i18next": "^24.2.1",
Expand Down
7 changes: 1 addition & 6 deletions src/app/label-data-validation/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,9 @@ function LabelDataValidationPage() {
};
}, [uploadedFiles, showAlert, router, storedLabelData, setLabelData]);

const getFiles = () => {
console.log("log uploadedFiles:", uploadedFiles);
return uploadedFiles.map((file) => file.getFile());
};

return (
<LabelDataValidator
files={getFiles()}
files={uploadedFiles.map((file) => file.getFile())}
labelData={labelData}
setLabelData={setLabelData}
loading={loading}
Expand Down
17 changes: 8 additions & 9 deletions src/components/BaseInformationForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FormComponentProps, LabelData, UNITS } from "@/types/types";
import useDebouncedSave from "@/utils/client/useDebouncedSave";
import { Box } from "@mui/material";
import { useEffect } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
Expand All @@ -12,30 +13,28 @@ const BaseInformationForm: React.FC<FormComponentProps> = ({
setLabelData,
}) => {
const { t } = useTranslation("labelDataValidator");
const sectionName = "baseInformation";
const methods = useForm<LabelData>({
defaultValues: labelData,
});

const watchedBaseInformation = useWatch({
control: methods.control,
name: "baseInformation",
name: sectionName,
});

const save = useDebouncedSave(setLabelData);

useEffect(() => {
const currentValues = methods.getValues();
if (JSON.stringify(currentValues) !== JSON.stringify(labelData)) {
methods.reset(labelData);
}
}, [labelData, methods]);

useEffect(() => {
if (watchedBaseInformation) {
setLabelData((prevLabelData) => ({
...prevLabelData,
baseInformation: watchedBaseInformation,
}));
}
}, [watchedBaseInformation, setLabelData]);
save(sectionName, watchedBaseInformation);
}, [watchedBaseInformation, save]);

return (
<FormProvider {...methods}>
Expand Down
17 changes: 8 additions & 9 deletions src/components/CautionsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FormComponentProps, LabelData } from "@/types/types";
import useDebouncedSave from "@/utils/client/useDebouncedSave";
import { Box } from "@mui/material";
import { useEffect } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
Expand All @@ -12,12 +13,15 @@ const CautionsForm: React.FC<FormComponentProps> = ({
const methods = useForm<LabelData>({
defaultValues: labelData,
});
const sectionName = "cautions";

const watchedCautions = useWatch({
control: methods.control,
name: "cautions",
name: sectionName,
});

const save = useDebouncedSave(setLabelData);

useEffect(() => {
const currentValues = methods.getValues();
if (JSON.stringify(currentValues) !== JSON.stringify(labelData)) {
Expand All @@ -26,18 +30,13 @@ const CautionsForm: React.FC<FormComponentProps> = ({
}, [labelData, methods]);

useEffect(() => {
if (watchedCautions) {
setLabelData((prevLabelData) => ({
...prevLabelData,
cautions: watchedCautions,
}));
}
}, [watchedCautions, setLabelData]);
save(sectionName, watchedCautions);
}, [watchedCautions, save]);

return (
<FormProvider {...methods}>
<Box className="p-4" data-testid="cautions-form">
<VerifiedBilingualTable path={"cautions"} loading={loading} />
<VerifiedBilingualTable path={sectionName} loading={loading} />
</Box>
</FormProvider>
);
Expand Down
15 changes: 7 additions & 8 deletions src/components/GuaranteedAnalysisForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FormComponentProps, LabelData, UNITS } from "@/types/types";
import useDebouncedSave from "@/utils/client/useDebouncedSave";
import { Box, Typography } from "@mui/material";
import { useEffect } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
Expand All @@ -15,12 +16,15 @@ const GuaranteedAnalysisForm: React.FC<FormComponentProps> = ({
const methods = useForm<LabelData>({
defaultValues: labelData,
});
const sectionName = "guaranteedAnalysis";

const watchedGuaranteedAnalysis = useWatch({
control: methods.control,
name: "guaranteedAnalysis",
name: sectionName,
});

const save = useDebouncedSave(setLabelData);

useEffect(() => {
const currentValues = methods.getValues();
if (JSON.stringify(currentValues) !== JSON.stringify(labelData)) {
Expand All @@ -29,13 +33,8 @@ const GuaranteedAnalysisForm: React.FC<FormComponentProps> = ({
}, [labelData, methods]);

useEffect(() => {
if (watchedGuaranteedAnalysis) {
setLabelData((prevLabelData) => ({
...prevLabelData,
guaranteedAnalysis: watchedGuaranteedAnalysis,
}));
}
}, [watchedGuaranteedAnalysis, setLabelData]);
save(sectionName, watchedGuaranteedAnalysis);
}, [watchedGuaranteedAnalysis, save]);

return (
<FormProvider {...methods}>
Expand Down
19 changes: 2 additions & 17 deletions src/components/ImageViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ZoomOutIcon from "@mui/icons-material/ZoomOut";
import { Box, Button } from "@mui/material";
import Tooltip from "@mui/material/Tooltip";
import Image from "next/image";
import { useEffect, useState } from "react";
import { useState } from "react";
import type { ReactZoomPanPinchRef } from "react-zoom-pan-pinch";
import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
import "swiper/css";
Expand All @@ -19,27 +19,12 @@ interface ImageViewerProps {
}

const ImageViewer: React.FC<ImageViewerProps> = ({ imageFiles }) => {
const [imageUrls, setImageUrls] = useState<string[]>([]);
const [swiperInstance, setSwiperInstance] = useState<SwiperClass | null>(
null,
);
const [zoomRefs, setZoomRefs] = useState<ReactZoomPanPinchRef[]>([]);
const [activeIndex, setActiveIndex] = useState(0);

useEffect(() => {
const urls = imageFiles.map((file) => URL.createObjectURL(file));
setImageUrls(urls);
setZoomRefs((prevRefs) =>
Array.from({ length: urls.length }, (_, i) => prevRefs[i] || null),
);
return () => {
urls.forEach((url) => URL.revokeObjectURL(url));
};
}, [imageFiles, setZoomRefs, setImageUrls, setActiveIndex]);

useEffect(() => {
zoomRefs.forEach((ref) => ref?.resetTransform());
}, [imageFiles, zoomRefs]);
const imageUrls = imageFiles.map((file) => URL.createObjectURL(file));

const handleInit = (index: number, ref: ReactZoomPanPinchRef) => {
setZoomRefs((prevRefs) => {
Expand Down
17 changes: 8 additions & 9 deletions src/components/IngredientsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FormComponentProps, LabelData, UNITS } from "@/types/types";
import useDebouncedSave from "@/utils/client/useDebouncedSave";
import { Box } from "@mui/material";
import { useEffect } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
Expand All @@ -12,14 +13,17 @@ function IngredientsForm({
const methods = useForm<LabelData>({
defaultValues: labelData,
});
const sectionName = "ingredients";

const { control } = methods;

const watchedIngredients = useWatch({
control,
name: "ingredients",
name: sectionName,
});

const save = useDebouncedSave(setLabelData);

useEffect(() => {
const currentValues = methods.getValues();
if (JSON.stringify(currentValues) !== JSON.stringify(labelData)) {
Expand All @@ -28,19 +32,14 @@ function IngredientsForm({
}, [labelData, methods]);

useEffect(() => {
if (watchedIngredients) {
setLabelData((prevLabelData) => ({
...prevLabelData,
ingredients: watchedIngredients,
}));
}
}, [watchedIngredients, setLabelData]);
save(sectionName, watchedIngredients);
}, [watchedIngredients, save]);

return (
<FormProvider {...methods}>
<Box className="p-4" data-testid="ingredients-form">
<VerifiedBilingualTable
path={"ingredients"}
path={sectionName}
unitOptions={UNITS.ingredients}
valueColumn
loading={loading}
Expand Down
17 changes: 8 additions & 9 deletions src/components/InstructionsForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { FormComponentProps, LabelData } from "@/types/types";
import useDebouncedSave from "@/utils/client/useDebouncedSave";
import { Box } from "@mui/material";
import { useEffect } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
Expand All @@ -12,12 +13,15 @@ const InstructionsForm: React.FC<FormComponentProps> = ({
const methods = useForm<LabelData>({
defaultValues: labelData,
});
const sectionName = "instructions";

const watchedInstructions = useWatch({
control: methods.control,
name: "instructions",
name: sectionName,
});

const save = useDebouncedSave(setLabelData);

useEffect(() => {
const currentValues = methods.getValues();
if (JSON.stringify(currentValues) !== JSON.stringify(labelData)) {
Expand All @@ -26,18 +30,13 @@ const InstructionsForm: React.FC<FormComponentProps> = ({
}, [labelData, methods]);

useEffect(() => {
if (watchedInstructions) {
setLabelData((prevLabelData) => ({
...prevLabelData,
instructions: watchedInstructions,
}));
}
}, [watchedInstructions, setLabelData]);
save(sectionName, watchedInstructions);
}, [watchedInstructions, save]);

return (
<FormProvider {...methods}>
<Box className="p-4" data-testid="instructions-form">
<VerifiedBilingualTable path={"instructions"} loading={loading} />
<VerifiedBilingualTable path={sectionName} loading={loading} />
</Box>
</FormProvider>
);
Expand Down
3 changes: 1 addition & 2 deletions src/components/LabelDataValidator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ function LabelDataValidator({
setLabelData,
}: LabelDataValidatorProps) {
const { t } = useTranslation("labelDataValidator");
const imageFiles = files;
const { isDownXs, isBetweenXsSm, isBetweenSmMd, isBetweenMdLg } =
useBreakpoints();
const isLgOrBelow =
Expand Down Expand Up @@ -199,7 +198,7 @@ function LabelDataValidator({
className="flex h-[500px] md:h-[720px] lg:size-full justify-center min-w-0 "
data-testid="image-viewer-container"
>
<ImageViewer imageFiles={imageFiles} />
<ImageViewer imageFiles={files} />
</Box>

{isLgOrBelow && (
Expand Down
19 changes: 9 additions & 10 deletions src/components/OrganizationsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Organization,
} from "@/types/types";
import { checkFieldRecord } from "@/utils/client/fieldValidation";
import useDebouncedSave from "@/utils/client/useDebouncedSave";
import AddIcon from "@mui/icons-material/Add";
import DeleteIcon from "@mui/icons-material/Delete";
import DoneAllIcon from "@mui/icons-material/DoneAll";
Expand Down Expand Up @@ -34,17 +35,20 @@ const OrganizationsForm: React.FC<FormComponentProps> = ({
});

const { control, setValue } = methods;
const sectionName = "organizations";

const { fields, append, remove } = useFieldArray({
control,
name: "organizations",
name: sectionName,
});

const watchedOrganizations = useWatch({
control,
name: "organizations",
name: sectionName,
});

const save = useDebouncedSave(setLabelData);

useEffect(() => {
const currentValues = methods.getValues();
if (JSON.stringify(currentValues) !== JSON.stringify(labelData)) {
Expand All @@ -53,19 +57,14 @@ const OrganizationsForm: React.FC<FormComponentProps> = ({
}, [labelData, methods]);

useEffect(() => {
if (watchedOrganizations) {
setLabelData((prevLabelData) => ({
...prevLabelData,
organizations: watchedOrganizations,
}));
}
}, [watchedOrganizations, setLabelData]);
save(sectionName, watchedOrganizations);
}, [watchedOrganizations, save]);

const setAllVerified = useCallback(
(orgIndex: number, verified: boolean) => {
fieldNames.forEach((fieldName) => {
const fieldPath =
`organizations.${orgIndex}.${fieldName}.verified` as FieldPath<LabelData>;
`${sectionName}.${orgIndex}.${fieldName}.verified` as FieldPath<LabelData>;
setValue(fieldPath, verified, {
shouldValidate: true,
shouldDirty: true,
Expand Down
Loading

0 comments on commit 3b2950b

Please sign in to comment.