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",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"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",
|
"@tanstack/react-query": "^5.90.9",
|
||||||
"@testing-library/jest-dom": "^5.16.5",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@testing-library/react": "^13.4.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
@ -11,13 +17,16 @@
|
|||||||
"@types/node": "^16.18.3",
|
"@types/node": "^16.18.3",
|
||||||
"@types/react": "^18.0.25",
|
"@types/react": "^18.0.25",
|
||||||
"@types/react-dom": "^18.0.9",
|
"@types/react-dom": "^18.0.9",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
"react-hook-form": "^7.66.1",
|
||||||
"react-router": "^6.4.3",
|
"react-router": "^6.4.3",
|
||||||
"react-router-dom": "^6.4.3",
|
"react-router-dom": "^6.4.3",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"typescript": "^4.9.3",
|
"typescript": "^4.9.3",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4",
|
||||||
|
"yup": "^1.7.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
|||||||
@ -1,48 +1,102 @@
|
|||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
import { useState } from "react";
|
import { Controller, SubmitHandler, useForm, useWatch } from "react-hook-form";
|
||||||
import { BaseResponse, FormState } from "../interfaces";
|
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 CheckAgeForm = () => {
|
||||||
const [form, setForm] = useState<FormState>({
|
const {
|
||||||
name: "",
|
control,
|
||||||
age: 0,
|
handleSubmit,
|
||||||
dateOfBirth: new Date()
|
reset,
|
||||||
})
|
trigger,
|
||||||
const validate = useMutation({
|
setValue,
|
||||||
mutationFn: async (data: FormState): Promise<BaseResponse> => {
|
formState: { errors, isValid, isDirty }
|
||||||
const res = await fetch('http://localhost:3001/info/validate-form', {
|
} = useForm<FormSchemaType>({
|
||||||
method: 'POST',
|
mode: "onChange",
|
||||||
headers: {
|
resolver: yupResolver(schema),
|
||||||
'Content-Type': 'application/json'
|
defaultValues: {
|
||||||
},
|
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleSand = async () => {
|
|
||||||
await validate.mutateAsync({
|
|
||||||
...form
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const handleReset = () => {
|
|
||||||
validate.reset()
|
|
||||||
setForm({
|
|
||||||
name: "",
|
name: "",
|
||||||
age: 0,
|
age: 0,
|
||||||
|
married: false,
|
||||||
dateOfBirth: new Date()
|
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: FormSchemaType): Promise<BaseResponse> => {
|
||||||
|
const res = await fetch('http://localhost:3001/info/validate-form', {
|
||||||
|
method: 'POST',
|
||||||
|
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 handleReset = () => {
|
||||||
|
validate.reset();
|
||||||
|
reset({
|
||||||
|
name: "",
|
||||||
|
age: 0,
|
||||||
|
married: false,
|
||||||
|
dateOfBirth: new Date()
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const isAbove18 = form.age >= 18
|
|
||||||
|
|
||||||
if (validate.isError) {
|
if (validate.isError) {
|
||||||
return (
|
return (
|
||||||
@ -55,96 +109,115 @@ const CheckAgeForm = () => {
|
|||||||
|
|
||||||
if (validate.isPending) {
|
if (validate.isPending) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div><h1>INVIO IN CORSO...</h1></div>
|
||||||
<h1>INVIO IN CORSO</h1>
|
|
||||||
<button onClick={handleReset}>ANNULLA</button>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validate.isSuccess) {
|
if (validate.isSuccess) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{validate.data?.success === true && <h1>DATI INVIATI VALIDI</h1>}
|
{validate.data?.success ? <div>
|
||||||
{validate.data?.success === false && <h1>DATI INVIATI NON VALIDI</h1>}
|
<h1>DATI VALIDI</h1>
|
||||||
<button onClick={handleReset}>INVIA UN ALTRO VALORE</button>
|
<p>
|
||||||
|
{JSON.stringify(validate.data)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
: <h1>DATI NON VALIDI</h1>}
|
||||||
|
<button onClick={handleReset}>RESET</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||||
<input
|
<Stack sx={{ width: "90%", margin: "auto", gap: 2, mt: 5 }}>
|
||||||
type="text"
|
|
||||||
placeholder="name"
|
|
||||||
value={form.name}
|
|
||||||
onChange={(e) => {
|
|
||||||
setForm((prev) => {
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
name: e.target.value
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<input
|
<Controller
|
||||||
type="number"
|
name="name"
|
||||||
placeholder="age"
|
control={control}
|
||||||
value={form.age}
|
render={({ field }) => (
|
||||||
onChange={(e) => {
|
<TextField
|
||||||
setForm((prev) => {
|
{...field}
|
||||||
const age = Number.parseInt(e.target.value) ?? 0
|
label="Name"
|
||||||
return {
|
error={!!errors.name}
|
||||||
...prev,
|
helperText={errors.name?.message}
|
||||||
age
|
/>
|
||||||
}
|
)}
|
||||||
});
|
/>
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{
|
<Controller
|
||||||
(isAbove18) && (
|
name="age"
|
||||||
<input
|
control={control}
|
||||||
type="checkbox"
|
render={({ field }) => (
|
||||||
placeholder="married?"
|
<TextField
|
||||||
checked={form.married}
|
{...field}
|
||||||
onChange={() => {
|
label="Age"
|
||||||
setForm((prev) => {
|
type="number"
|
||||||
return {
|
error={!!errors.age}
|
||||||
...prev,
|
helperText={errors.age?.message}
|
||||||
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
|
<FormControlLabel
|
||||||
onClick={handleSand}
|
control={
|
||||||
>
|
<Controller
|
||||||
VALIDA
|
name="married"
|
||||||
</button>
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={!!field.value}
|
||||||
|
onChange={(e) => field.onChange(e.target.checked)}
|
||||||
|
disabled={!isAbove18}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={"Married?"}
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
<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>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</LocalizationProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CheckAgeForm
|
export default CheckAgeForm;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user