esercizio
This commit is contained in:
parent
14ffafe801
commit
77758f2c29
40
package-lock.json
generated
40
package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "better-tictactoe-front-end",
|
"name": "better-tictactoe-front-end",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@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",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
@ -3399,6 +3400,32 @@
|
|||||||
"url": "https://github.com/sponsors/gregberge"
|
"url": "https://github.com/sponsors/gregberge"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/query-core": {
|
||||||
|
"version": "5.90.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.9.tgz",
|
||||||
|
"integrity": "sha512-UFOCQzi6pRGeVTVlPNwNdnAvT35zugcIydqjvFUzG62dvz2iVjElmNp/hJkUoM5eqbUPfSU/GJIr/wbvD8bTUw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/react-query": {
|
||||||
|
"version": "5.90.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.9.tgz",
|
||||||
|
"integrity": "sha512-Zke2AaXiaSfnG8jqPZR52m8SsclKT2d9//AgE/QIzyNvbpj/Q2ln+FsZjb1j69bJZUouBvX2tg9PHirkTm8arw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/query-core": "5.90.9"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18 || ^19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "8.19.0",
|
"version": "8.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz",
|
||||||
@ -19048,6 +19075,19 @@
|
|||||||
"loader-utils": "^2.0.0"
|
"loader-utils": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@tanstack/query-core": {
|
||||||
|
"version": "5.90.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.9.tgz",
|
||||||
|
"integrity": "sha512-UFOCQzi6pRGeVTVlPNwNdnAvT35zugcIydqjvFUzG62dvz2iVjElmNp/hJkUoM5eqbUPfSU/GJIr/wbvD8bTUw=="
|
||||||
|
},
|
||||||
|
"@tanstack/react-query": {
|
||||||
|
"version": "5.90.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.9.tgz",
|
||||||
|
"integrity": "sha512-Zke2AaXiaSfnG8jqPZR52m8SsclKT2d9//AgE/QIzyNvbpj/Q2ln+FsZjb1j69bJZUouBvX2tg9PHirkTm8arw==",
|
||||||
|
"requires": {
|
||||||
|
"@tanstack/query-core": "5.90.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@testing-library/dom": {
|
"@testing-library/dom": {
|
||||||
"version": "8.19.0",
|
"version": "8.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz",
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@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",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
|||||||
19
src/App.tsx
19
src/App.tsx
@ -1,16 +1,18 @@
|
|||||||
import { Routes, Route, Outlet, Link } from "react-router-dom";
|
import { Routes, Route, Outlet, Link } from "react-router-dom";
|
||||||
|
import CheckAgeForm from "./pages/CheckAgeForm";
|
||||||
import { CheckName } from './pages/CheckName';
|
import { CheckName } from './pages/CheckName';
|
||||||
import { Home } from './pages/Home';
|
import { Home } from './pages/Home';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Layout />}>
|
<Route path="/" element={<Layout />}>
|
||||||
<Route index element={<Home />} />
|
<Route index element={<Home />} />
|
||||||
<Route path="check-name" element={<CheckName />} />
|
<Route path="check-name" element={<CheckName />} />
|
||||||
<Route path="*" element={<NoMatch />} />
|
<Route path="check-age-form" element={<CheckAgeForm />} />
|
||||||
</Route>
|
<Route path="*" element={<NoMatch />} />
|
||||||
</Routes>
|
</Route>
|
||||||
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +27,9 @@ function Layout() {
|
|||||||
<li>
|
<li>
|
||||||
<Link to="/check-name">Check Name</Link>
|
<Link to="/check-name">Check Name</Link>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link to="/check-age-form">Check age form</Link>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<hr />
|
<hr />
|
||||||
|
|||||||
@ -3,11 +3,21 @@ import './index.css';
|
|||||||
import App from './App';
|
import App from './App';
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
import { BrowserRouter } from "react-router-dom";
|
import { BrowserRouter } from "react-router-dom";
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
|
||||||
|
const queryClient = new QueryClient()
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement
|
document.getElementById('root') as HTMLElement
|
||||||
);
|
);
|
||||||
root.render(<BrowserRouter><App /></BrowserRouter>);
|
|
||||||
|
root.render(
|
||||||
|
<BrowserRouter>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<App />
|
||||||
|
</QueryClientProvider>
|
||||||
|
</BrowserRouter>
|
||||||
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
// to log results (for example: reportWebVitals(console.log))
|
||||||
|
|||||||
@ -3,7 +3,7 @@ interface ValidationError {
|
|||||||
property: string;
|
property: string;
|
||||||
value?: any;
|
value?: any;
|
||||||
constraints?: {
|
constraints?: {
|
||||||
[type: string]: string;
|
[type: string]: string;
|
||||||
};
|
};
|
||||||
children?: ValidationError[];
|
children?: ValidationError[];
|
||||||
contexts?: {
|
contexts?: {
|
||||||
@ -31,3 +31,12 @@ interface BaseResponseError extends BaseResponseInteface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type BaseResponse = BaseResponseSuccess | BaseResponseError;
|
export type BaseResponse = BaseResponseSuccess | BaseResponseError;
|
||||||
|
|
||||||
|
|
||||||
|
export type FormState = {
|
||||||
|
name: string
|
||||||
|
age: number
|
||||||
|
married?: boolean
|
||||||
|
dateOfBirth: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
150
src/pages/CheckAgeForm.tsx
Normal file
150
src/pages/CheckAgeForm.tsx
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { BaseResponse, FormState } from "../interfaces";
|
||||||
|
|
||||||
|
|
||||||
|
const CheckAgeForm = () => {
|
||||||
|
const [form, setForm] = useState<FormState>({
|
||||||
|
name: "",
|
||||||
|
age: 0,
|
||||||
|
dateOfBirth: new Date()
|
||||||
|
})
|
||||||
|
const validate = useMutation({
|
||||||
|
mutationFn: async (data: FormState): 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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSand = async () => {
|
||||||
|
await validate.mutateAsync({
|
||||||
|
...form
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const handleReset = () => {
|
||||||
|
validate.reset()
|
||||||
|
setForm({
|
||||||
|
name: "",
|
||||||
|
age: 0,
|
||||||
|
dateOfBirth: new Date()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAbove18 = form.age >= 18
|
||||||
|
|
||||||
|
if (validate.isError) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>ERRORE INVIO DATI</h1>
|
||||||
|
<button onClick={handleReset}>RIPROVA</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validate.isPending) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>INVIO IN CORSO</h1>
|
||||||
|
<button onClick={handleReset}>ANNULLA</button>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="name"
|
||||||
|
value={form.name}
|
||||||
|
onChange={(e) => {
|
||||||
|
setForm((prev) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
name: e.target.value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="age"
|
||||||
|
value={form.age}
|
||||||
|
onChange={(e) => {
|
||||||
|
setForm((prev) => {
|
||||||
|
const age = Number.parseInt(e.target.value) ?? 0
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
age
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
(isAbove18) && (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
placeholder="married?"
|
||||||
|
checked={form.married}
|
||||||
|
onChange={() => {
|
||||||
|
setForm((prev) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
married: !(prev.married ?? false)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<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}
|
||||||
|
>
|
||||||
|
VALIDA
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CheckAgeForm
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user