Skip to content

Commit

Permalink
issue #311: draft: react hook form to simplify data management
Browse files Browse the repository at this point in the history
  • Loading branch information
k-allagbe committed Nov 15, 2024
1 parent d266264 commit 071af13
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 176 deletions.
17 changes: 17 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 @@ -31,6 +31,7 @@
"next": "14.2.15",
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.53.2",
"react-zoom-pan-pinch": "^3.6.1",
"swiper": "^11.1.14",
"ts-node": "^10.9.2",
Expand Down
22 changes: 10 additions & 12 deletions src/app/label-data-validation/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
import Dummy from "@/components/Dummy";
import ImageViewer from "@/components/ImageViewer";
import OrganizationForm from "@/components/OrganizationForm";
import {
FieldStatus,
Organization,
} from "@/components/OrganizationInformation";
import { Organization } from "@/components/OrganizationInformation";
import { InputStatus } from "@/components/StatusInput";
import {
HorizontalNonLinearStepper,
StepComponentProps,
Expand All @@ -26,44 +24,44 @@ function LabelDataValidationPage() {
{
name: {
value: "GreenGrow Inc.",
status: FieldStatus.Unverified,
status: InputStatus.Unverified,
errorMessage: null,
},
address: {
value: "123 Green Road, Farmville, State, 12345",
status: FieldStatus.Unverified,
status: InputStatus.Unverified,
errorMessage: null,
},
website: {
value: "https://www.greengrow.com",
status: FieldStatus.Unverified,
status: InputStatus.Unverified,
errorMessage: null,
},
phoneNumber: {
value: "123-456-7890",
status: FieldStatus.Unverified,
status: InputStatus.Unverified,
errorMessage: null,
},
},
{
name: {
value: "GreenGrow Inc.",
status: FieldStatus.Unverified,
status: InputStatus.Unverified,
errorMessage: null,
},
address: {
value: "123 Green Road, Farmville, State, 12345",
status: FieldStatus.Unverified,
status: InputStatus.Unverified,
errorMessage: null,
},
website: {
value: "https://www.greengrow.com",
status: FieldStatus.Unverified,
status: InputStatus.Unverified,
errorMessage: null,
},
phoneNumber: {
value: "123-456-7890",
status: FieldStatus.Unverified,
status: InputStatus.Unverified,
errorMessage: null,
},
},
Expand Down
78 changes: 37 additions & 41 deletions src/components/OrganizationForm.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Box, Divider, Typography } from "@mui/material";
import { useEffect } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form";
import OrganizationInformation, {
FieldStatus,
Organization,
} from "./OrganizationInformation";
import { InputStatus } from "./StatusInput";
import { StepComponentProps, StepStatus } from "./stepper";

export interface OrganizationsProps extends StepComponentProps {
Expand All @@ -17,51 +18,46 @@ const OrganizationForm: React.FC<OrganizationsProps> = ({
organizations,
setOrganizations,
}) => {
const statusIsVerified = (status: FieldStatus) =>
status === FieldStatus.Verified;
const methods = useForm({
defaultValues: { organizations },
});

const watchedValues = useWatch({
control: methods.control,
name: "organizations",
});

useEffect(() => {
const isAllChecked = organizations.every(
(org) =>
statusIsVerified(org.name.status) &&
statusIsVerified(org.address.status) &&
statusIsVerified(org.website.status) &&
statusIsVerified(org.phoneNumber.status),
);
setStatus(isAllChecked ? StepStatus.Completed : StepStatus.Incomplete);
}, [organizations, setStatus]);
console.log("watchedValues", watchedValues);
if (watchedValues) {
setOrganizations(watchedValues);

const isAllChecked = watchedValues.every(
(org: Organization) =>
org &&
[org.name, org.address, org.website, org.phoneNumber].every(
(field) => field.status === InputStatus.Verified,
),
);

const setOrganization = (
index: number,
updatedOrg: React.SetStateAction<Organization>,
) => {
setOrganizations((prevOrganizations) => {
const newOrgs = [...prevOrganizations];
newOrgs[index] =
typeof updatedOrg === "function"
? updatedOrg(newOrgs[index])
: updatedOrg;
return newOrgs;
});
};
setStatus(isAllChecked ? StepStatus.Completed : StepStatus.Incomplete);
}
}, [watchedValues, setOrganizations, setStatus]);

return (
<div>
<Typography variant="h6">{title}</Typography>
<Box className="flex flex-col gap-4 p-6">
{organizations.map((organization, index) => (
<div key={index}>
<OrganizationInformation
organization={organization}
setOrganization={(updatedOrg) =>
setOrganization(index, updatedOrg)
}
/>
{index < organizations.length - 1 && <Divider />}
</div>
))}
</Box>
</div>
<FormProvider {...methods}>
<div>
<Typography variant="h6">{title}</Typography>
<Box className="flex flex-col gap-4 p-6">
{organizations.map((_, index) => (
<div key={index}>
<OrganizationInformation index={index} />
{index < organizations.length - 1 && <Divider />}
</div>
))}
</Box>
</div>
</FormProvider>
);
};

Expand Down
117 changes: 30 additions & 87 deletions src/components/OrganizationInformation.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { Box } from "@mui/material";
import { Dispatch, SetStateAction, useCallback } from "react";
import StatusInput from "./StatusInput";
import InputWithStatus, { InputStatus } from "./StatusInput";

export enum FieldStatus {
Verified = "verified",
Unverified = "unverified",
Error = "error",
}
type OrganizationInformationProps = {
index: number;
};

type Field = {
value: string;
status: FieldStatus;
status: InputStatus;
errorMessage: string | null;
};

Expand All @@ -21,87 +18,33 @@ export type Organization = {
phoneNumber: Field;
};

function OrganizationInformation({
organization,
setOrganization,
}: {
organization: Organization;
setOrganization: Dispatch<SetStateAction<Organization>>;
}) {
const handleValueChange = useCallback(
(field: keyof Organization, value: SetStateAction<string>) => {
setOrganization((prev) => ({
...prev,
[field]: {
...prev[field],
value,
},
}));
},
[setOrganization],
);

const handleStatusChange = useCallback(
(field: keyof Organization, status: React.SetStateAction<FieldStatus>) => {
setOrganization((prev) => ({
...prev,
[field]: {
...prev[field],
status,
},
}));
},
[setOrganization],
);

const inputFields = [
{
label: "Name",
placeholder: "Enter organization name",
value: organization.name.value,
setValue: (value: SetStateAction<string>) =>
handleValueChange("name", value),
status: organization.name.status,
setStatus: (status: React.SetStateAction<FieldStatus>) =>
handleStatusChange("name", status),
},
{
label: "Address",
placeholder: "Enter address",
value: organization.address.value,
setValue: (value: SetStateAction<string>) =>
handleValueChange("address", value),
status: organization.address.status,
setStatus: (status: React.SetStateAction<FieldStatus>) =>
handleStatusChange("address", status),
},
{
label: "Website",
placeholder: "Enter website",
value: organization.website.value,
setValue: (value: SetStateAction<string>) =>
handleValueChange("website", value),
status: organization.website.status,
setStatus: (status: React.SetStateAction<FieldStatus>) =>
handleStatusChange("website", status),
},
{
label: "Phone Number",
placeholder: "Enter phone number",
value: organization.phoneNumber.value,
setValue: (value: SetStateAction<string>) =>
handleValueChange("phoneNumber", value),
status: organization.phoneNumber.status,
setStatus: (status: React.SetStateAction<FieldStatus>) =>
handleStatusChange("phoneNumber", status),
},
];

function OrganizationInformation({ index }: OrganizationInformationProps) {
return (
<Box className="flex flex-col gap-4 p-4">
{inputFields.map((fieldProps, index) => (
<StatusInput key={index} {...fieldProps} />
))}
<InputWithStatus
label="Name"
placeholder="Enter organization name"
name={`organizations.${index}.name.value`}
statusName={`organizations.${index}.name.status`}
/>
<InputWithStatus
label="Address"
placeholder="Enter address"
name={`organizations.${index}.address.value`}
statusName={`organizations.${index}.address.status`}
/>
<InputWithStatus
label="Website"
placeholder="Enter website"
name={`organizations.${index}.website.value`}
statusName={`organizations.${index}.website.status`}
/>
<InputWithStatus
label="Phone Number"
placeholder="Enter phone number"
name={`organizations.${index}.phoneNumber.value`}
statusName={`organizations.${index}.phoneNumber.status`}
/>
</Box>
);
}
Expand Down
Loading

0 comments on commit 071af13

Please sign in to comment.