diff --git a/src/client/components/createLogEntry.tsx b/src/client/components/createLogEntry.tsx index 13d841262e059e0d175cb2cd9ea07e457dfa3b95..fc6cac351dd5f30a19ad7b1c7172b8cdf6c7dff5 100644 --- a/src/client/components/createLogEntry.tsx +++ b/src/client/components/createLogEntry.tsx @@ -1,93 +1,89 @@ -import * as React from 'react'; -import Form from 'react-bootstrap/Form'; -import Row from 'react-bootstrap/Row'; -import Col from 'react-bootstrap/Col'; -import Button from 'react-bootstrap/Button'; -import { useFormik } from 'formik'; -import * as Yup from 'yup'; -import {useParams} from 'react-router-dom'; - +import React from "react"; +import Form from "react-bootstrap/Form"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import Button from "react-bootstrap/Button"; +import { useFormik } from "formik"; +import * as Yup from "yup"; +import { useParams } from "react-router-dom"; function VehicleInfo() { - const { vehicleId } = useParams<{vehicleId: string}>(); - return ( - <div> - <h3>ID: {vehicleId}</h3> - </div> - ); + const { vehicleId } = useParams<{ vehicleId: string }>(); + return ( + <div> + <h3>ID: {vehicleId}</h3> + </div> + ); } const CreateLogEntry = () => { - const formik = useFormik({ - initialValues: { - firstName: '', - lastName: '', - email: '', - }, - validationSchema: Yup.object({ - firstName: Yup.string() - .max(15, 'Must be 15 characters or less') - .required('Required'), - lastName: Yup.string() - .max(20, 'Must be 20 characters or less') - .required('Required'), - email: Yup.string().email('Invalid email address').required('Required'), - }), - onSubmit: async values => { - console.log(values) - const response = await fetch('/api/logEntry', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(values) // body data type must match "Content-Type" header - }); - console.log(response.json()) + const formik = useFormik({ + initialValues: { + firstName: "", + lastName: "", + email: "", + }, + validationSchema: Yup.object({ + firstName: Yup.string() + .max(15, "Must be 15 characters or less") + .required("Required"), + lastName: Yup.string() + .max(20, "Must be 20 characters or less") + .required("Required"), + email: Yup.string().email("Invalid email address").required("Required"), + }), + onSubmit: async (values) => { + console.log(values); + const response = await fetch("/api/logEntry", { + method: "POST", + credentials: "same-origin", + headers: { + "Content-Type": "application/json", }, - }); - return ( - <div> - <VehicleInfo /> - <Form onSubmit={formik.handleSubmit}> - <Row className="mb-3"> - <Form.Group as={Col} controlId="firstName"> - <Form.Label>First Name</Form.Label> - <Form.Control - type="text" - {...formik.getFieldProps('firstName')} - /> - {formik.touched.firstName && formik.errors.firstName ? ( - <div>{formik.errors.firstName}</div> - ) : null} - </Form.Group> - </Row> - <Row className="mb-3"> - <Form.Group as={Col} controlId="lastName"> - <Form.Label>Last Name</Form.Label> - <Form.Control type="text" {...formik.getFieldProps('lastName')} /> - {formik.touched.lastName && formik.errors.lastName ? ( - <div>{formik.errors.lastName}</div> - ) : null} - </Form.Group> - </Row> + body: JSON.stringify(values), // body data type must match "Content-Type" header + }); + console.log(response.json()); + }, + }); + return ( + <div> + <VehicleInfo /> + <Form onSubmit={formik.handleSubmit}> + <Row className="mb-3"> + <Form.Group as={Col} controlId="firstName"> + <Form.Label>First Name</Form.Label> + <Form.Control type="text" {...formik.getFieldProps("firstName")} /> + {formik.touched.firstName && formik.errors.firstName ? ( + <div>{formik.errors.firstName}</div> + ) : null} + </Form.Group> + </Row> + <Row className="mb-3"> + <Form.Group as={Col} controlId="lastName"> + <Form.Label>Last Name</Form.Label> + <Form.Control type="text" {...formik.getFieldProps("lastName")} /> + {formik.touched.lastName && formik.errors.lastName ? ( + <div>{formik.errors.lastName}</div> + ) : null} + </Form.Group> + </Row> - <Row className="mb-3"> - <Form.Group as={Col} controlId="email"> - <Form.Label>Email Address</Form.Label> - <Form.Control type="email" {...formik.getFieldProps('email')} /> - {formik.touched.email && formik.errors.email ? ( - <div>{formik.errors.email}</div> - ) : null} - </Form.Group> - </Row> + <Row className="mb-3"> + <Form.Group as={Col} controlId="email"> + <Form.Label>Email Address</Form.Label> + <Form.Control type="email" {...formik.getFieldProps("email")} /> + {formik.touched.email && formik.errors.email ? ( + <div>{formik.errors.email}</div> + ) : null} + </Form.Group> + </Row> - <Button variant="primary" type="submit"> - Submit - </Button> - </Form> - </div> - ); + <Button variant="primary" type="submit"> + Submit + </Button> + </Form> + </div> + ); }; -export default CreateLogEntry +export default CreateLogEntry; diff --git a/src/client/components/header.tsx b/src/client/components/header.tsx index 23cec29c561dc43b508f3803e4bbd3df5b62110c..adbc29832beb5a410719144e237e29981d4da04b 100644 --- a/src/client/components/header.tsx +++ b/src/client/components/header.tsx @@ -1,27 +1,26 @@ -import * as React from 'react'; -import Navbar from 'react-bootstrap/Navbar'; -import Container from 'react-bootstrap/Container'; -import Nav from 'react-bootstrap/Nav'; -import { - Link -} from "react-router-dom"; -import vehicleTypes from "./vehicleTypes"; +import React from "react"; +import Navbar from "react-bootstrap/Navbar"; +import Container from "react-bootstrap/Container"; +import Nav from "react-bootstrap/Nav"; +import { Link } from "react-router-dom"; -class Header extends React.Component { - constructor(props: any) { - super(props); - } - render() { - return <Navbar bg="green" variant="dark"> - <Container> - - <Navbar.Brand as={Link} to="/">Navbar</Navbar.Brand> - <Nav className="me-auto"> - <Nav.Link as={Link} to="/vehicleTypes">Types</Nav.Link> - <Nav.Link as={Link} to="/login">Login</Nav.Link> - </Nav> - </Container> - </Navbar>; - } +function Header() { + return ( + <Navbar bg="green" variant="dark"> + <Container> + <Navbar.Brand as={Link} to="/"> + Navbar + </Navbar.Brand> + <Nav className="me-auto"> + <Nav.Link as={Link} to="/vehicleTypes"> + Types + </Nav.Link> + <Nav.Link as={Link} to="/login"> + Login + </Nav.Link> + </Nav> + </Container> + </Navbar> + ); } -export default Header +export default Header; diff --git a/src/client/components/home.tsx b/src/client/components/home.tsx index 884ffe80dffab4c0c8714570e3c154079fb84cfe..5deeca88d6ea0b931c4bf987107ee718d7c1f4ed 100644 --- a/src/client/components/home.tsx +++ b/src/client/components/home.tsx @@ -1,13 +1,12 @@ -import * as React from 'react'; -import { - Link -} from "react-router-dom"; +import * as React from "react"; +import { Link } from "react-router-dom"; -class Home extends React.Component { - render() { - return <div> - <Link to={"/qr/testId"}>QR</Link> - </div>; - } +function Home() { + return ( + <div> + <Link to={"/qr/testId"}>QR</Link> + </div> + ); } -export default Home + +export default Home; diff --git a/src/client/components/vehicleTypes.tsx b/src/client/components/vehicleTypes.tsx index 2d1b8b04187e81016d094e2e88974304c2748cf8..36fb9b34cd9275937c074e7df303cb4e34e1fc1b 100644 --- a/src/client/components/vehicleTypes.tsx +++ b/src/client/components/vehicleTypes.tsx @@ -1,131 +1,175 @@ -import * as React from 'react'; -import Modal from 'react-bootstrap/Modal'; -import Button from 'react-bootstrap/Button'; -import Table from 'react-bootstrap/Table'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faTrash, faPlus } from '@fortawesome/free-solid-svg-icons' +import React, { useState } from "react"; +import Modal from "react-bootstrap/Modal"; +import Button from "react-bootstrap/Button"; +import Table from "react-bootstrap/Table"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faTrash, faPlus } from "@fortawesome/free-solid-svg-icons"; +function VehicleTypes() { + const [vehicleTypes, setVehicleTypes] = useState< + { id: number; name: string; seats: number; image: string }[] + >([]); + const [add, setAdd] = useState(false); + const [removeId, setRemoveId] = useState<number>(null); -class VehicleTypes extends React.Component<{}, {removeId: number, add: boolean, vehicleTypes: { id: number; name: string; seats: number; image: string;}[]}> { - constructor(props: any) { - super(props); - this.state = { - vehicleTypes: [], - removeId: undefined, - add: false - }; - } - reloadData(){ - fetch('/api/vehicleType', { - method: 'GET', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json' - }, - }) - .then(response => response.json()) - .then(data => this.setState({ vehicleTypes: data })); - } + const reloadData = () => { + fetch("/api/vehicleType", { + method: "GET", + credentials: "same-origin", + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => response.json()) + .then((data) => setVehicleTypes(data)); + }; + const addType = (type: any) => { + fetch("/api/vehicleType", { + method: "POST", + credentials: "same-origin", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(type), + }).then(() => reloadData()); + }; + const removeType = () => { + fetch("/api/vehicleType/" + removeId, { + method: "DELETE", + credentials: "same-origin", + headers: { + "Content-Type": "application/json", + }, + }).then(() => { + reloadData(); + setRemoveId(null); + }); + }; + const getRemoveDialog = () => { + return ( + <div className={"modalWrapper"}> + <Modal.Dialog> + <Modal.Header + closeButton + onClick={() => { + setRemoveId(null); + }} + > + <Modal.Title>Remove Vehicle Type</Modal.Title> + </Modal.Header> + <Modal.Body> + <p> + Remove VehicleType " + {vehicleTypes.find((x) => x.id == removeId).name}"? + </p> + </Modal.Body> + <Modal.Footer> + <Button + variant="secondary" + onClick={() => { + setRemoveId(null); + }} + > + Close + </Button> + <Button + variant="danger" + onClick={() => { + removeType(); + }} + > + Delete + </Button> + </Modal.Footer> + </Modal.Dialog> + </div> + ); + }; + const getAddDialog = () => { + return ( + <div className={"modalWrapper"}> + <Modal.Dialog> + <Modal.Header + closeButton + onClick={() => { + setAdd(false); + }} + > + <Modal.Title>New Vehicle Type</Modal.Title> + </Modal.Header> + <Modal.Body> + <p>Add VehicleType?</p> + TODO + </Modal.Body> + <Modal.Footer> + <Button + variant="secondary" + onClick={() => { + setAdd(false); + }} + > + Close + </Button> + <Button + variant="primary" + onClick={() => { + addType({ name: "ABC", seats: 2 }); + }} + > + Add + </Button> + </Modal.Footer> + </Modal.Dialog> + </div> + ); + }; - componentDidMount() { - this.reloadData(); - } - addType(type: any){ - fetch('/api/vehicleType', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(type) - }).then( - ()=>this.reloadData() - ); - } - removeType(){ - fetch('/api/vehicleType/' + this.state.removeId, { - method: 'DELETE', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json' - }, - }).then( - ()=>{ - this.reloadData(); - this.setState({removeId: undefined}); - } - ); - } - getRemoveDialog(){ - const { vehicleTypes, removeId } = this.state; - return ( - <div className={"modalWrapper"}> - <Modal.Dialog> - <Modal.Header closeButton onClick={()=>{this.setState({removeId: undefined})}}> - <Modal.Title>Remove Vehicle Type</Modal.Title> - </Modal.Header> - <Modal.Body> - <p>Remove VehicleType "{vehicleTypes.find(x=>x.id == removeId).name}"?</p> - </Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={()=>{this.setState({removeId: undefined})}}>Close</Button> - <Button variant="danger" onClick={()=>{this.removeType()}}>Delete</Button> - </Modal.Footer> - </Modal.Dialog> - </div>); - } - getAddDialog(){ - return ( - <div className={"modalWrapper"}> - <Modal.Dialog> - <Modal.Header closeButton onClick={()=>{this.setState({add: false})}}> - <Modal.Title>New Vehicle Type</Modal.Title> - </Modal.Header> - <Modal.Body> - <p>Add VehicleType?</p> - TODO - </Modal.Body> - <Modal.Footer> - <Button variant="secondary" onClick={()=>{this.setState({add: false})}}>Close</Button> - <Button variant="primary" onClick={()=>{this.addType({ - name: "ABC", - seats: 2, - })}}>Add</Button> - </Modal.Footer> - </Modal.Dialog> - </div>); - } - - render(){ - const { vehicleTypes, removeId, add } = this.state; - return (<div> - <div className={"menuBarTable"}> - <Button size="sm" variant="dark" title="Add Type"><FontAwesomeIcon className={"text-center"} onClick={()=>{this.setState({add: true})}} icon={faPlus} /></Button> - </div> - <Table striped bordered hover variant="dark"> - <thead> - <tr> - <th>#</th> - <th>Name</th> - <th>Seats</th> - <th> </th> - </tr> - </thead> - <tbody> - {vehicleTypes.map(t => - <tr key={t.id}> - <td>{t.id}</td> - <td>{t.name}</td> - <td>{t.seats}</td> - <td className={"text-center"}><FontAwesomeIcon className={"text-danger text-center iconHover"} onClick={()=>{this.setState({removeId: t.id})}} title="Remove" icon={faTrash} /></td> - </tr> - )} - </tbody> - </Table> - {removeId != undefined && this.getRemoveDialog()} - {add && this.getAddDialog()} - </div>) - } + return ( + <div> + <div className={"menuBarTable"}> + <Button size="sm" variant="dark" title="Add Type"> + <FontAwesomeIcon + className={"text-center"} + onClick={() => { + setAdd(true); + }} + icon={faPlus} + /> + </Button> + </div> + <Table striped bordered hover variant="dark"> + <thead> + <tr> + <th>#</th> + <th>Name</th> + <th>Seats</th> + <th> </th> + </tr> + </thead> + <tbody> + {vehicleTypes.map((t) => ( + <tr key={t.id}> + <td>{t.id}</td> + <td>{t.name}</td> + <td>{t.seats}</td> + <td className={"text-center"}> + <FontAwesomeIcon + className={"text-danger text-center iconHover"} + onClick={() => { + this.setState({ removeId: t.id }); + }} + title="Remove" + icon={faTrash} + /> + </td> + </tr> + ))} + </tbody> + </Table> + {removeId != null && getRemoveDialog()} + {add && getAddDialog()} + </div> + ); } -export default VehicleTypes +export default VehicleTypes; diff --git a/src/client/index.tsx b/src/client/index.tsx index a47ba59d0cd83173124286970a2a21c5dafa2123..df13a59389b83e0828432fdce8d7ce48176631d1 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -1,15 +1,10 @@ -import * as React from 'react'; -import { render } from 'react-dom'; -import AppRouter from './router' -import './css/app'; +import * as React from "react"; +import { render } from "react-dom"; +import AppRouter from "./router"; +import "./css/app"; -class ApplicationComponent extends React.Component { - constructor(props: any){ - super(props); - } - render(){ - return <AppRouter />; - } +function App() { + return <AppRouter />; } -render(<ApplicationComponent />, document.getElementById('app')) +render(<App />, document.getElementById("app")); diff --git a/src/client/router.tsx b/src/client/router.tsx index b5ac7ac8f81cd8569700c7a4daa61588be4f60c5..322111da58fc28d23bde251af1d0388c28b0ac02 100644 --- a/src/client/router.tsx +++ b/src/client/router.tsx @@ -1,32 +1,25 @@ -import * as React from 'react'; -import Header from './components/header'; -import CreateLogEntry from './components/createLogEntry'; -import Home from './components/home'; -import {BrowserRouter, Route, Switch} from 'react-router-dom'; +import * as React from "react"; +import Header from "./components/header"; +import CreateLogEntry from "./components/createLogEntry"; +import Home from "./components/home"; +import { BrowserRouter, Route, Switch } from "react-router-dom"; import VehicleTypes from "./components/vehicleTypes"; -class AppRouter extends React.Component<{}, {}> { - constructor(props: any) { - super(props); - } - - render(){ - return (<BrowserRouter> - <div> - <Header /> - <div className="mainView"> - <Switch> - <Route path={["/", "/home"]} component={Home} exact/> - <Route path="/qr/:vehicleId" component={CreateLogEntry}/> - <Route path="/vehicleTypes" component={VehicleTypes} exact /> - <Route path="/login" exact/> - <Route /> - </Switch> - </div> - </div> - </BrowserRouter>) - } +function AppRouter() { + return ( + <BrowserRouter> + <Header /> + <div className="mainView"> + <Switch> + <Route path={["/", "/home"]} component={Home} exact /> + <Route path="/qr/:vehicleId" component={CreateLogEntry} /> + <Route path="/vehicleTypes" component={VehicleTypes} exact /> + <Route path="/login" exact /> + <Route /> + </Switch> + </div> + </BrowserRouter> + ); } - export default AppRouter;