Initial implementation of routing
This commit is contained in:
parent
b1675f3229
commit
0d6f7e3e8d
|
@ -22,12 +22,14 @@
|
||||||
"@eslint/js": "^9.9.0",
|
"@eslint/js": "^9.9.0",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@vitejs/plugin-react": "^4.3.1",
|
"@vitejs/plugin-react": "^4.3.1",
|
||||||
"eslint": "^9.9.0",
|
"eslint": "^9.9.0",
|
||||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.9",
|
"eslint-plugin-react-refresh": "^0.4.9",
|
||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
|
"react-router-dom": "^6.26.2",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.5.4",
|
||||||
"typescript-eslint": "^8.0.1",
|
"typescript-eslint": "^8.0.1",
|
||||||
"vite": "^5.4.1"
|
"vite": "^5.4.1"
|
||||||
|
|
|
@ -39,6 +39,9 @@ importers:
|
||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: ^18.3.0
|
specifier: ^18.3.0
|
||||||
version: 18.3.0
|
version: 18.3.0
|
||||||
|
'@types/react-router-dom':
|
||||||
|
specifier: ^5.3.3
|
||||||
|
version: 5.3.3
|
||||||
'@vitejs/plugin-react':
|
'@vitejs/plugin-react':
|
||||||
specifier: ^4.3.1
|
specifier: ^4.3.1
|
||||||
version: 4.3.1(vite@5.4.2)
|
version: 4.3.1(vite@5.4.2)
|
||||||
|
@ -57,6 +60,9 @@ importers:
|
||||||
prettier:
|
prettier:
|
||||||
specifier: ^3.3.3
|
specifier: ^3.3.3
|
||||||
version: 3.3.3
|
version: 3.3.3
|
||||||
|
react-router-dom:
|
||||||
|
specifier: ^6.26.2
|
||||||
|
version: 6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.5.4
|
specifier: ^5.5.4
|
||||||
version: 5.5.4
|
version: 5.5.4
|
||||||
|
@ -517,6 +523,10 @@ packages:
|
||||||
'@popperjs/core@2.11.8':
|
'@popperjs/core@2.11.8':
|
||||||
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
|
||||||
|
|
||||||
|
'@remix-run/router@1.19.2':
|
||||||
|
resolution: {integrity: sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.21.1':
|
'@rollup/rollup-android-arm-eabi@4.21.1':
|
||||||
resolution: {integrity: sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==}
|
resolution: {integrity: sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
|
@ -612,6 +622,9 @@ packages:
|
||||||
'@types/estree@1.0.5':
|
'@types/estree@1.0.5':
|
||||||
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
||||||
|
|
||||||
|
'@types/history@4.7.11':
|
||||||
|
resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==}
|
||||||
|
|
||||||
'@types/parse-json@4.0.2':
|
'@types/parse-json@4.0.2':
|
||||||
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
|
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
|
||||||
|
|
||||||
|
@ -621,6 +634,12 @@ packages:
|
||||||
'@types/react-dom@18.3.0':
|
'@types/react-dom@18.3.0':
|
||||||
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
|
resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}
|
||||||
|
|
||||||
|
'@types/react-router-dom@5.3.3':
|
||||||
|
resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
|
||||||
|
|
||||||
|
'@types/react-router@5.1.20':
|
||||||
|
resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
|
||||||
|
|
||||||
'@types/react-transition-group@4.4.11':
|
'@types/react-transition-group@4.4.11':
|
||||||
resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==}
|
resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==}
|
||||||
|
|
||||||
|
@ -1175,6 +1194,19 @@ packages:
|
||||||
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
|
resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
react-router-dom@6.26.2:
|
||||||
|
resolution: {integrity: sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8'
|
||||||
|
react-dom: '>=16.8'
|
||||||
|
|
||||||
|
react-router@6.26.2:
|
||||||
|
resolution: {integrity: sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8'
|
||||||
|
|
||||||
react-transition-group@4.4.5:
|
react-transition-group@4.4.5:
|
||||||
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1794,6 +1826,8 @@ snapshots:
|
||||||
|
|
||||||
'@popperjs/core@2.11.8': {}
|
'@popperjs/core@2.11.8': {}
|
||||||
|
|
||||||
|
'@remix-run/router@1.19.2': {}
|
||||||
|
|
||||||
'@rollup/rollup-android-arm-eabi@4.21.1':
|
'@rollup/rollup-android-arm-eabi@4.21.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
@ -1865,6 +1899,8 @@ snapshots:
|
||||||
|
|
||||||
'@types/estree@1.0.5': {}
|
'@types/estree@1.0.5': {}
|
||||||
|
|
||||||
|
'@types/history@4.7.11': {}
|
||||||
|
|
||||||
'@types/parse-json@4.0.2': {}
|
'@types/parse-json@4.0.2': {}
|
||||||
|
|
||||||
'@types/prop-types@15.7.12': {}
|
'@types/prop-types@15.7.12': {}
|
||||||
|
@ -1873,6 +1909,17 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 18.3.4
|
'@types/react': 18.3.4
|
||||||
|
|
||||||
|
'@types/react-router-dom@5.3.3':
|
||||||
|
dependencies:
|
||||||
|
'@types/history': 4.7.11
|
||||||
|
'@types/react': 18.3.4
|
||||||
|
'@types/react-router': 5.1.20
|
||||||
|
|
||||||
|
'@types/react-router@5.1.20':
|
||||||
|
dependencies:
|
||||||
|
'@types/history': 4.7.11
|
||||||
|
'@types/react': 18.3.4
|
||||||
|
|
||||||
'@types/react-transition-group@4.4.11':
|
'@types/react-transition-group@4.4.11':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 18.3.4
|
'@types/react': 18.3.4
|
||||||
|
@ -2443,6 +2490,18 @@ snapshots:
|
||||||
|
|
||||||
react-refresh@0.14.2: {}
|
react-refresh@0.14.2: {}
|
||||||
|
|
||||||
|
react-router-dom@6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
'@remix-run/router': 1.19.2
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
react-router: 6.26.2(react@18.3.1)
|
||||||
|
|
||||||
|
react-router@6.26.2(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
'@remix-run/router': 1.19.2
|
||||||
|
react: 18.3.1
|
||||||
|
|
||||||
react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.4
|
'@babel/runtime': 7.25.4
|
||||||
|
|
155
src/App.tsx
155
src/App.tsx
|
@ -1,155 +1,14 @@
|
||||||
import { Alert, Grow, Rating, Typography } from "@mui/material";
|
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
import ButtonRow, { ActionProps } from "./components/ButtonRow";
|
import { Route, Routes } from "react-router-dom";
|
||||||
import ReviewField, { ReviewFieldProps } from "./components/ReviewField";
|
import Home from "./pages/Home";
|
||||||
import { useState } from "react";
|
import NotFound from "./pages/NotFound";
|
||||||
import EndpointDialog from "./components/EndpointDialong";
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [rating, setNewRating] = useState<number | null>(0);
|
|
||||||
const [showAlert, changeAlert] = useState(false);
|
|
||||||
const [alertText, changeAlertText] = useState("");
|
|
||||||
const [secure, setSecure] = useState(false);
|
|
||||||
|
|
||||||
const [showInfo, changeInfo] = useState(false);
|
|
||||||
const [infoText, changeInfoText] = useState("");
|
|
||||||
|
|
||||||
const fields: ReviewFieldProps[] = [
|
|
||||||
{
|
|
||||||
name: "Name",
|
|
||||||
dynamicState: useState(""),
|
|
||||||
validateInput: ({ length }) => length >= 2 && length <= 30,
|
|
||||||
errorText: "Name must be at least 2 characters",
|
|
||||||
maxLength: 30
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Title",
|
|
||||||
dynamicState: useState(""),
|
|
||||||
validateInput: ({ length }) => !length || length <= 50,
|
|
||||||
errorText: "Title must be less than 50 characters",
|
|
||||||
maxLength: 50
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Content",
|
|
||||||
dynamicState: useState(""),
|
|
||||||
expandable: true,
|
|
||||||
validateInput: ({ length }) => !length || length <= 2000,
|
|
||||||
errorText: "Content must be less than 2000 characters",
|
|
||||||
maxLength: 2000
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const clearValues = () => {
|
|
||||||
fields.forEach((field) => {
|
|
||||||
field.dynamicState[1]("");
|
|
||||||
});
|
|
||||||
|
|
||||||
setNewRating(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const showThenHide = async () => {
|
|
||||||
await fetch(endpoint ? `${secure ? "https" : "http"}://${endpoint}/post` : "http://localhost:8080/post", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
rating: rating,
|
|
||||||
username: fields[0].dynamicState[0],
|
|
||||||
title: fields[1].dynamicState[0],
|
|
||||||
content: fields[2].dynamicState[0],
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then(async (response) => {
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.error) {
|
|
||||||
changeAlertText(`${result.error.type}: ${result.error.message}`);
|
|
||||||
changeAlert(true);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
changeAlert(false);
|
|
||||||
}, 2000);
|
|
||||||
} else {
|
|
||||||
changeInfoText(`Success: ${result.message}`);
|
|
||||||
changeInfo(true);
|
|
||||||
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
changeInfo(false);
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
changeAlertText(err.toString());
|
|
||||||
|
|
||||||
changeAlert(true);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
changeAlert(false);
|
|
||||||
}, 2500);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttons: ActionProps[] = [
|
|
||||||
{
|
|
||||||
name: "Clear",
|
|
||||||
type: "outlined",
|
|
||||||
action: clearValues,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Submit",
|
|
||||||
type: "contained",
|
|
||||||
action: showThenHide,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const info = <Alert severity="info">{infoText}</Alert>;
|
|
||||||
|
|
||||||
const alert = <Alert severity="error">{alertText}</Alert>;
|
|
||||||
const [endpoint, setEndpoint] = useState(JSON.parse(localStorage.getItem("apiEndpoint")!) || "");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Routes>
|
||||||
<div id="settings">
|
<Route path="/" element={<Home />} />
|
||||||
<EndpointDialog endpoint={[endpoint, setEndpoint]} secure={[secure, setSecure]}/>
|
<Route path="*" element={<NotFound />} />
|
||||||
</div>
|
</Routes>
|
||||||
|
|
||||||
<div id="app">
|
|
||||||
<Typography variant="h3">Simple Review Client</Typography>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<Rating
|
|
||||||
name="review-rating"
|
|
||||||
precision={0.5}
|
|
||||||
size="large"
|
|
||||||
value={rating}
|
|
||||||
onChange={(event, newRating) => {
|
|
||||||
event
|
|
||||||
setNewRating(newRating);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<ReviewField fields={fields} />
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<ButtonRow buttons={buttons} />
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div id="alert-box">
|
|
||||||
<Grow in={showAlert} mountOnEnter unmountOnExit>
|
|
||||||
{alert}
|
|
||||||
</Grow>
|
|
||||||
|
|
||||||
<Grow in={showInfo} mountOnEnter unmountOnExit>
|
|
||||||
{info}
|
|
||||||
</Grow>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,14 @@ import { createRoot } from "react-dom/client";
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { createTheme } from "@mui/material";
|
import { createTheme } from "@mui/material";
|
||||||
|
import { BrowserRouter } from "react-router-dom";
|
||||||
|
|
||||||
const theme = createTheme({
|
const theme = createTheme({
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
});
|
});
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<BrowserRouter>
|
||||||
<App />
|
<App />
|
||||||
</StrictMode>,
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
|
|
156
src/pages/Home.tsx
Normal file
156
src/pages/Home.tsx
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
import { Alert, Grow, Rating, Typography } from "@mui/material";
|
||||||
|
import "../App.css";
|
||||||
|
import ButtonRow, { ActionProps } from "../components/ButtonRow";
|
||||||
|
import ReviewField, { ReviewFieldProps } from "../components/ReviewField";
|
||||||
|
import { useState } from "react";
|
||||||
|
import EndpointDialog from "../components/EndpointDialong";
|
||||||
|
|
||||||
|
function Home() {
|
||||||
|
const [rating, setNewRating] = useState<number | null>(0);
|
||||||
|
const [showAlert, changeAlert] = useState(false);
|
||||||
|
const [alertText, changeAlertText] = useState("");
|
||||||
|
const [secure, setSecure] = useState(false);
|
||||||
|
|
||||||
|
const [showInfo, changeInfo] = useState(false);
|
||||||
|
const [infoText, changeInfoText] = useState("");
|
||||||
|
|
||||||
|
const fields: ReviewFieldProps[] = [
|
||||||
|
{
|
||||||
|
name: "Name",
|
||||||
|
dynamicState: useState(""),
|
||||||
|
validateInput: ({ length }) => length >= 2 && length <= 30,
|
||||||
|
errorText: "Name must be at least 2 characters",
|
||||||
|
maxLength: 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Title",
|
||||||
|
dynamicState: useState(""),
|
||||||
|
validateInput: ({ length }) => !length || length <= 50,
|
||||||
|
errorText: "Title must be less than 50 characters",
|
||||||
|
maxLength: 50
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Content",
|
||||||
|
dynamicState: useState(""),
|
||||||
|
expandable: true,
|
||||||
|
validateInput: ({ length }) => !length || length <= 2000,
|
||||||
|
errorText: "Content must be less than 2000 characters",
|
||||||
|
maxLength: 2000
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const clearValues = () => {
|
||||||
|
fields.forEach((field) => {
|
||||||
|
field.dynamicState[1]("");
|
||||||
|
});
|
||||||
|
|
||||||
|
setNewRating(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showThenHide = async () => {
|
||||||
|
await fetch(endpoint ? `${secure ? "https" : "http"}://${endpoint}/post` : "http://localhost:8080/post", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
rating: rating,
|
||||||
|
username: fields[0].dynamicState[0],
|
||||||
|
title: fields[1].dynamicState[0],
|
||||||
|
content: fields[2].dynamicState[0],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then(async (response) => {
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
changeAlertText(`${result.error.type}: ${result.error.message}`);
|
||||||
|
changeAlert(true);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
changeAlert(false);
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
changeInfoText(`Success: ${result.message}`);
|
||||||
|
changeInfo(true);
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
changeInfo(false);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
changeAlertText(err.toString());
|
||||||
|
|
||||||
|
changeAlert(true);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
changeAlert(false);
|
||||||
|
}, 2500);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons: ActionProps[] = [
|
||||||
|
{
|
||||||
|
name: "Clear",
|
||||||
|
type: "outlined",
|
||||||
|
action: clearValues,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Submit",
|
||||||
|
type: "contained",
|
||||||
|
action: showThenHide,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const info = <Alert severity="info">{infoText}</Alert>;
|
||||||
|
|
||||||
|
const alert = <Alert severity="error">{alertText}</Alert>;
|
||||||
|
const [endpoint, setEndpoint] = useState(JSON.parse(localStorage.getItem("apiEndpoint")!) || "");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div id="settings">
|
||||||
|
<EndpointDialog endpoint={[endpoint, setEndpoint]} secure={[secure, setSecure]}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="app">
|
||||||
|
<Typography variant="h3">Simple Review Client</Typography>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<Rating
|
||||||
|
name="review-rating"
|
||||||
|
precision={0.5}
|
||||||
|
size="large"
|
||||||
|
value={rating}
|
||||||
|
onChange={(event, newRating) => {
|
||||||
|
event
|
||||||
|
setNewRating(newRating);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<ReviewField fields={fields} />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<ButtonRow buttons={buttons} />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div id="alert-box">
|
||||||
|
<Grow in={showAlert} mountOnEnter unmountOnExit>
|
||||||
|
{alert}
|
||||||
|
</Grow>
|
||||||
|
|
||||||
|
<Grow in={showInfo} mountOnEnter unmountOnExit>
|
||||||
|
{info}
|
||||||
|
</Grow>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Home;
|
24
src/pages/NotFound.tsx
Normal file
24
src/pages/NotFound.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { Button, Typography } from "@mui/material";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
function NotFound() {
|
||||||
|
return (
|
||||||
|
<div id="app">
|
||||||
|
<Typography variant="h3">
|
||||||
|
Not found
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
component={Link}
|
||||||
|
to="/"
|
||||||
|
>
|
||||||
|
Back to home
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotFound;
|
Loading…
Reference in a new issue