aggiunta libreria per stile e validazione
This commit is contained in:
parent
77758f2c29
commit
fbd90e1649
1103
package-lock.json
generated
1103
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@ -3,6 +3,12 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@fontsource/roboto": "^5.2.8",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@mui/material": "^7.3.5",
|
||||
"@mui/x-date-pickers": "^8.18.0",
|
||||
"@tanstack/react-query": "^5.90.9",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
@ -11,13 +17,16 @@
|
||||
"@types/node": "^16.18.3",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"date-fns": "^4.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.66.1",
|
||||
"react-router": "^6.4.3",
|
||||
"react-router-dom": "^6.4.3",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^4.9.3",
|
||||
"web-vitals": "^2.1.4"
|
||||
"web-vitals": "^2.1.4",
|
||||
"yup": "^1.7.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
||||
@ -1,48 +1,102 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
import { BaseResponse, FormState } from "../interfaces";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { Controller, SubmitHandler, useForm, useWatch } from "react-hook-form";
|
||||
import { Button, Checkbox, FormControlLabel, Stack, TextField } from "@mui/material";
|
||||
import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import * as yup from "yup";
|
||||
import { differenceInYears, isValid } from "date-fns";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const schema = yup.object({
|
||||
name: yup.string().required("Name is required"),
|
||||
age: yup.number()
|
||||
.typeError("Age must be a number")
|
||||
.positive()
|
||||
.integer()
|
||||
.required("Age is required"),
|
||||
married: yup.boolean()
|
||||
.required(),
|
||||
|
||||
dateOfBirth: yup.date()
|
||||
.typeError("Devi inserire una data valida")
|
||||
.required("DOB is required")
|
||||
.test("dob-match", "Date of Birth does not match the Age provided", function(value) {
|
||||
const { age } = this.parent;
|
||||
|
||||
if (!value || typeof age !== 'number') return true;
|
||||
if (!isValid(value)) return false;
|
||||
|
||||
const calculatedAge = differenceInYears(new Date(), value);
|
||||
return calculatedAge === age;
|
||||
}),
|
||||
}).required();
|
||||
|
||||
type FormSchemaType = yup.InferType<typeof schema>;
|
||||
|
||||
interface BaseResponse {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
const CheckAgeForm = () => {
|
||||
const [form, setForm] = useState<FormState>({
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
reset,
|
||||
trigger,
|
||||
setValue,
|
||||
formState: { errors, isValid, isDirty }
|
||||
} = useForm<FormSchemaType>({
|
||||
mode: "onChange",
|
||||
resolver: yupResolver(schema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
age: 0,
|
||||
married: false,
|
||||
dateOfBirth: new Date()
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
const watchedAge = useWatch({ control, name: "age" });
|
||||
const isAbove18 = (watchedAge || 0) >= 18;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAbove18) {
|
||||
setValue("married", false, { shouldValidate: true });
|
||||
}
|
||||
|
||||
if (control._fields['dateOfBirth']) {
|
||||
trigger("dateOfBirth");
|
||||
}
|
||||
|
||||
}, [watchedAge, isAbove18, control]);
|
||||
|
||||
const validate = useMutation({
|
||||
mutationFn: async (data: FormState): Promise<BaseResponse> => {
|
||||
mutationFn: async (data: FormSchemaType): Promise<BaseResponse> => {
|
||||
const res = await fetch('http://localhost:3001/info/validate-form', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
...data
|
||||
})
|
||||
})
|
||||
|
||||
// TODO: usare axios o altre librerie per avere un minimo di typesafety, qui sto pregando che il risultato sia quello che mi aspetto e sinceramente non so se dia errore fino a quando non prova a chiamare un oggetto annidato
|
||||
const resData: BaseResponse = await res.json()
|
||||
console.log(resData)
|
||||
return resData
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
const resData: BaseResponse = await res.json();
|
||||
return resData;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const onSubmit: SubmitHandler<FormSchemaType> = async (data) => {
|
||||
await validate.mutateAsync(data);
|
||||
};
|
||||
|
||||
const handleSand = async () => {
|
||||
await validate.mutateAsync({
|
||||
...form
|
||||
})
|
||||
}
|
||||
const handleReset = () => {
|
||||
validate.reset()
|
||||
setForm({
|
||||
validate.reset();
|
||||
reset({
|
||||
name: "",
|
||||
age: 0,
|
||||
married: false,
|
||||
dateOfBirth: new Date()
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const isAbove18 = form.age >= 18
|
||||
|
||||
if (validate.isError) {
|
||||
return (
|
||||
@ -55,96 +109,115 @@ const CheckAgeForm = () => {
|
||||
|
||||
if (validate.isPending) {
|
||||
return (
|
||||
<div>
|
||||
<h1>INVIO IN CORSO</h1>
|
||||
<button onClick={handleReset}>ANNULLA</button>
|
||||
</div>
|
||||
<div><h1>INVIO IN CORSO...</h1></div>
|
||||
);
|
||||
}
|
||||
|
||||
if (validate.isSuccess) {
|
||||
return (
|
||||
<div>
|
||||
{validate.data?.success === true && <h1>DATI INVIATI VALIDI</h1>}
|
||||
{validate.data?.success === false && <h1>DATI INVIATI NON VALIDI</h1>}
|
||||
<button onClick={handleReset}>INVIA UN ALTRO VALORE</button>
|
||||
{validate.data?.success ? <div>
|
||||
<h1>DATI VALIDI</h1>
|
||||
<p>
|
||||
{JSON.stringify(validate.data)}
|
||||
</p>
|
||||
</div>
|
||||
: <h1>DATI NON VALIDI</h1>}
|
||||
<button onClick={handleReset}>RESET</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="name"
|
||||
value={form.name}
|
||||
onChange={(e) => {
|
||||
setForm((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
name: e.target.value
|
||||
}
|
||||
});
|
||||
}}
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<Stack sx={{ width: "90%", margin: "auto", gap: 2, mt: 5 }}>
|
||||
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
label="Name"
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<input
|
||||
<Controller
|
||||
name="age"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
label="Age"
|
||||
type="number"
|
||||
placeholder="age"
|
||||
value={form.age}
|
||||
onChange={(e) => {
|
||||
setForm((prev) => {
|
||||
const age = Number.parseInt(e.target.value) ?? 0
|
||||
return {
|
||||
...prev,
|
||||
age
|
||||
}
|
||||
});
|
||||
}}
|
||||
error={!!errors.age}
|
||||
helperText={errors.age?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
{
|
||||
(isAbove18) && (
|
||||
<input
|
||||
type="checkbox"
|
||||
placeholder="married?"
|
||||
checked={form.married}
|
||||
onChange={() => {
|
||||
setForm((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
married: !(prev.married ?? false)
|
||||
<Controller
|
||||
name="dateOfBirth"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<DatePicker
|
||||
{...field}
|
||||
label="Date of birth"
|
||||
slotProps={{
|
||||
textField: {
|
||||
error: !!errors.dateOfBirth,
|
||||
helperText: errors.dateOfBirth?.message
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<input
|
||||
type="date"
|
||||
placeholder="date of birth"
|
||||
value={form.dateOfBirth.toISOString().split("T")[0]}
|
||||
onChange={(e) => {
|
||||
setForm((prev) => {
|
||||
const newDate = new Date(e.target.value);
|
||||
return {
|
||||
...prev,
|
||||
dateOfBirth: newDate,
|
||||
};
|
||||
});
|
||||
}}
|
||||
)}
|
||||
/>
|
||||
|
||||
<button
|
||||
onClick={handleSand}
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Controller
|
||||
name="married"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
checked={!!field.value}
|
||||
onChange={(e) => field.onChange(e.target.checked)}
|
||||
disabled={!isAbove18}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
label={"Married?"}
|
||||
/>
|
||||
|
||||
<Stack
|
||||
sx={{
|
||||
width: "100%",
|
||||
gap: 2,
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end"
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
disabled={!isDirty}
|
||||
variant="outlined"
|
||||
onClick={handleReset}
|
||||
>
|
||||
UNDO
|
||||
</Button>
|
||||
<Button
|
||||
disabled={!isValid}
|
||||
variant="contained"
|
||||
onClick={handleSubmit(onSubmit)}>
|
||||
VALIDA
|
||||
</button>
|
||||
|
||||
</div>
|
||||
)
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</LocalizationProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default CheckAgeForm
|
||||
|
||||
export default CheckAgeForm;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user