Template out authorized side of application.
parent
ba6a054846
commit
2863bdb149
|
@ -8,14 +8,21 @@
|
||||||
"name": "front-end",
|
"name": "front-end",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||||
|
"@fortawesome/free-brands-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/free-regular-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
"@reduxjs/toolkit": "^2.2.7",
|
"@reduxjs/toolkit": "^2.2.7",
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
"@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",
|
||||||
|
"axios": "^1.7.5",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"react-tooltip": "^5.28.0",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -2565,6 +2572,101 @@
|
||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@floating-ui/core": {
|
||||||
|
"version": "1.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz",
|
||||||
|
"integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/utils": "^0.2.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/dom": {
|
||||||
|
"version": "1.6.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz",
|
||||||
|
"integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/core": "^1.6.0",
|
||||||
|
"@floating-ui/utils": "^0.2.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@floating-ui/utils": {
|
||||||
|
"version": "0.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz",
|
||||||
|
"integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/fontawesome-svg-core": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/free-brands-svg-icons": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-1MPD8lMNW/earme4OQi1IFHtmHUwAKgghXlNwWi9GO7QkTfD+IIaYpIai4m2YJEzqfEji3jFHX1DZI5pbY/biQ==",
|
||||||
|
"license": "(CC-BY-4.0 AND MIT)",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/free-regular-svg-icons": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==",
|
||||||
|
"license": "(CC-BY-4.0 AND MIT)",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/free-solid-svg-icons": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==",
|
||||||
|
"license": "(CC-BY-4.0 AND MIT)",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/react-fontawesome": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
|
||||||
|
"react": ">=16.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.11.14",
|
"version": "0.11.14",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||||
|
@ -5883,6 +5985,31 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz",
|
||||||
|
"integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.15.6",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
|
"proxy-from-env": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/axios/node_modules/form-data": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/axobject-query": {
|
"node_modules/axobject-query": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
|
||||||
|
@ -6618,6 +6745,12 @@
|
||||||
"integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
|
"integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/classnames": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/clean-css": {
|
"node_modules/clean-css": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
|
||||||
|
@ -15897,6 +16030,12 @@
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/psl": {
|
"node_modules/psl": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||||
|
@ -16379,6 +16518,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-tooltip": {
|
||||||
|
"version": "5.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.28.0.tgz",
|
||||||
|
"integrity": "sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/dom": "^1.6.1",
|
||||||
|
"classnames": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.14.0",
|
||||||
|
"react-dom": ">=16.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
|
|
@ -3,14 +3,21 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||||
|
"@fortawesome/free-brands-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/free-regular-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
"@reduxjs/toolkit": "^2.2.7",
|
"@reduxjs/toolkit": "^2.2.7",
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
"@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",
|
||||||
|
"axios": "^1.7.5",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-redux": "^9.1.2",
|
"react-redux": "^9.1.2",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"react-tooltip": "^5.28.0",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1,19 +1,25 @@
|
||||||
import React from "react";
|
import React, {Component} from "react";
|
||||||
import './main.css'
|
import './main.css'
|
||||||
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { selectAuthenticated } from "./stateSlicers/authenticationSlice";
|
|
||||||
|
|
||||||
import FleetManager from "./components/FleetManager";
|
import FleetManager from "./components/FleetManager";
|
||||||
import Login from "./components/Login";
|
import Login from "./components/Login";
|
||||||
|
|
||||||
function App () {
|
export default class App extends Component {
|
||||||
const authed = useSelector(selectAuthenticated);
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
authenticated: true,
|
||||||
|
userId: 1,
|
||||||
|
fullName: "Chris Lindelof"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
return (
|
return (
|
||||||
(authed)
|
(this.state.authenticated)
|
||||||
? <FleetManager />
|
? <FleetManager user={this.state}/>
|
||||||
: <Login />
|
: <Login />
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faChevronUp, faChevronDown, faClose } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import React, { Component } from "react";
|
||||||
|
|
||||||
|
export default class FuelPurchases extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
expanded: false,
|
||||||
|
showModal: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleExpanded = () => {
|
||||||
|
this.setState({
|
||||||
|
expanded: !this.state.expanded
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowModal = (value) => {
|
||||||
|
this.setState({
|
||||||
|
showModal: value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="bg-gray-400 flex flex-row mt-4 px-3 py-1 justify-between border">
|
||||||
|
<div className="text-xl">Fuel Purchases</div>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div onClick={() => this.setShowModal(true)} className="mr-2 px-2 py-1 rounded bg-green-400 hover:cursor-pointer">Add</div>
|
||||||
|
<div className="hover:cursor-pointer" onClick={this.toggleExpanded}>
|
||||||
|
{
|
||||||
|
(this.state.expanded)
|
||||||
|
? <FontAwesomeIcon icon={faChevronUp} />
|
||||||
|
: <FontAwesomeIcon icon={faChevronDown} />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
(this.state.showModal)
|
||||||
|
? (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none"
|
||||||
|
>
|
||||||
|
<div className="relative w-auto my-6 mx-auto min-w-1/2 max-w-3xl">
|
||||||
|
{/*content*/}
|
||||||
|
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none">
|
||||||
|
{/*header*/}
|
||||||
|
<div className="flex items-start justify-between p-5 border-b border-solid border-blueGray-200 rounded-t">
|
||||||
|
<h3 className="text-3xl font-semibold">
|
||||||
|
Add Fuel Purchase
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
className="p-1 ml-auto bg-transparent border-0 text-black opacity-5 float-right text-3xl leading-none font-semibold outline-none focus:outline-none"
|
||||||
|
onClick={() => this.setShowModal(false)}
|
||||||
|
>
|
||||||
|
<span className="bg-transparent text-black opacity-5 h-6 w-6 text-2xl block outline-none focus:outline-none">
|
||||||
|
<FontAwesomeIcon icon={faClose} />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/*body*/}
|
||||||
|
<div className="relative p-6 flex-auto">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<input type="date"/>
|
||||||
|
<div>Date</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<input type="number" step="0.001" className="border" />
|
||||||
|
Fuel Purchased
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<input type="number" step="0.01" className="border" />
|
||||||
|
Total Cost
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/*footer*/}
|
||||||
|
<div className="flex items-center justify-end p-6 border-t border-solid border-blueGray-200 rounded-b">
|
||||||
|
<button
|
||||||
|
className="text-red-500 background-transparent font-bold uppercase px-6 py-2 text-sm outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150"
|
||||||
|
type="button"
|
||||||
|
onClick={() => this.setShowModal(false)}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="bg-emerald-500 text-white active:bg-emerald-600 font-bold uppercase text-sm px-6 py-3 rounded shadow hover:shadow-lg outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150"
|
||||||
|
type="button"
|
||||||
|
onClick={() => this.setShowModal(false)}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="opacity-25 fixed inset-0 z-40 bg-black"></div>
|
||||||
|
</>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import React, {Component} from "react";
|
||||||
|
|
||||||
|
export default class Header extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="w-full border flex items-center justify-between px-4 py-2 text-xl">
|
||||||
|
<div className="font-bold">
|
||||||
|
Fleet Management
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{this.props.user.fullName}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React, {Component} from "react";
|
||||||
|
|
||||||
|
export default class Sidebar extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state={
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
var vehicleList = this.props.companyVehicles.map((vehicle, index) => {
|
||||||
|
return (
|
||||||
|
<div onClick={() => this.props.selectVehicle(vehicle.id)} key={`vehicleList-${index}`} className={`py-2 border-b pl-4 hover:cursor-pointer ${(this.props.selectedVehicle === vehicle.id) ? 'bg-gray-400 text-white': ''}`}>Vehicle {vehicle.name}</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-1/5 flex flex-col border">
|
||||||
|
{vehicleList}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React, {Component} from "react";
|
||||||
|
|
||||||
|
import VehicleName from "./VehicleInformation/VehicleName";
|
||||||
|
import VehicleImage from "./VehicleInformation/VehicleImage";
|
||||||
|
import Registration from "./VehicleInformation/Registration";
|
||||||
|
import VehicleDetails from "./VehicleInformation/VehicleDetails";
|
||||||
|
import ServiceHistory from "./VehicleInformation/ServiceHistory";
|
||||||
|
import FuelPurchases from "./FuelPurchases";
|
||||||
|
|
||||||
|
export default class VehicleInformation extends Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
|
||||||
|
<div className="grow flex flex-col item-center p-4">
|
||||||
|
<VehicleName selectedVehicleId={this.props.selectedVehicle} />
|
||||||
|
|
||||||
|
<div className="flex flex-row mt-4">
|
||||||
|
<VehicleImage selectedVehicleId={this.props.selectedVehicle} />
|
||||||
|
<div className="ml-4 flex flex-row justify-between grow">
|
||||||
|
<VehicleDetails selectedVehicleId={this.props.selectedVehicle} />
|
||||||
|
<Registration selectedVehicleId={this.props.selectedVehicle} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FuelPurchases selectedVehicleId={this.props.selectedVehicle} />
|
||||||
|
<ServiceHistory selectedVehicleId={this.props.selectedVehicle} />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import React, {Component} from "react";
|
||||||
|
import EditableTextDisplay from "../../Informational-Components/EditableTextDisplay";
|
||||||
|
|
||||||
|
export default class Registration extends Component {
|
||||||
|
|
||||||
|
constructor(props){
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
plate: '',
|
||||||
|
expiration: '',
|
||||||
|
locale: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editFieldCallBack = (newValue, label) => {
|
||||||
|
this.setState({
|
||||||
|
[label]: newValue
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col w-1/2">
|
||||||
|
<div className="underline text-xl text-center">Registration</div>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div>Locale: </div>
|
||||||
|
<EditableTextDisplay defaultValue={this.state.locale} fieldType="text" label="locale" callback={this.editFieldCallBack} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div>Plate: </div>
|
||||||
|
<EditableTextDisplay defaultValue={this.state.plate} fieldType="text" label="plate" callback={this.editFieldCallBack} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div>Expiration: </div>
|
||||||
|
<EditableTextDisplay defaultValue={this.state.expiration} fieldType="date" label="expiration" callback={this.editFieldCallBack} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { faChevronUp, faChevronDown } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import React, {Component} from "react";
|
||||||
|
|
||||||
|
export default class ServiceHistory extends Component {
|
||||||
|
|
||||||
|
constructor(props){
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
expanded: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleExpanded = () => {
|
||||||
|
this.setState({
|
||||||
|
expanded: !this.state.expanded
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="bg-gray-400 flex flex-row border mt-4 px-3 py-1 items-center justify-between">
|
||||||
|
<div className="text-xl">Service History</div>
|
||||||
|
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div className="mr-2 px-2 py-1 rounded bg-green-400 hover:cursor-pointer">Add</div>
|
||||||
|
<div className="hover:cursor-pointer" onClick={this.toggleExpanded}>
|
||||||
|
{
|
||||||
|
(this.state.expanded)
|
||||||
|
? <FontAwesomeIcon icon={faChevronUp} />
|
||||||
|
: <FontAwesomeIcon icon={faChevronDown} />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
(this.state.expanded)
|
||||||
|
? <div className="flex flex-col border-l border-r">Service History</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
import React, {Component} from "react";
|
||||||
|
import EditableTextDisplay from "../../Informational-Components/EditableTextDisplay";
|
||||||
|
|
||||||
|
export default class VehicleDetails extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
vin: "1HGBH41JXMN109186",
|
||||||
|
year: 2017,
|
||||||
|
make: "Chevrolet",
|
||||||
|
model: "Silverado"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editedTextCallback = (newValue, label) => {
|
||||||
|
this.setState({
|
||||||
|
[label]: newValue
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col w-1/2">
|
||||||
|
<div className="underline text-xl bold text-center mb-2">Information</div>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div>VIN:</div>
|
||||||
|
<EditableTextDisplay defaultValue={this.state.vin} fieldType="text" label="vin" callback={this.editedTextCallback}/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div>Year: </div>
|
||||||
|
<EditableTextDisplay defaultValue={this.state.year} fieldType="number" label="year" callback={this.editedTextCallback} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div>Make: </div>
|
||||||
|
<EditableTextDisplay defaultValue={this.state.make} fieldType="text" label="make" callback={this.editedTextCallback} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<div>Model: </div>
|
||||||
|
<EditableTextDisplay defaultValue={this.state.model} fieldType="text" label="model" callback={this.editedTextCallback} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React, { Component } from "react";
|
||||||
|
import { Tooltip } from 'react-tooltip'
|
||||||
|
|
||||||
|
export default class VehicleImage extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
imageSet: false,
|
||||||
|
edit: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateImage = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div onClick={this.updateImage} id="vehicleImage" className="w-48 h-48 p-1 flex flex-col justify-center items-center bg-gray-400 border">
|
||||||
|
<div className="z-0">{
|
||||||
|
(this.state.imageSet)
|
||||||
|
? <img src={require('../../../images/car-silhouet-png-21300.png')} alt="Vehicle" />
|
||||||
|
: <div>Image Not Set</div>
|
||||||
|
}</div>
|
||||||
|
</div>
|
||||||
|
<Tooltip anchorSelect="#vehicleImage" place="bottom-end">
|
||||||
|
<div>Click to Edit</div>
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
import React, { Component } from "react"
|
||||||
|
import Loading from "../../Informational-Components/Loading"
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
|
import { faPenToSquare, faSave } from "@fortawesome/free-solid-svg-icons"
|
||||||
|
|
||||||
|
import { Tooltip } from "react-tooltip"
|
||||||
|
|
||||||
|
export default class VehicleName extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
vehicleName: 'Temp Vehicle Name',
|
||||||
|
vehicleNameEdit: '',
|
||||||
|
loading: false,
|
||||||
|
editing: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editVehicleName = () => {
|
||||||
|
this.setState({
|
||||||
|
vehicleNameEdit: this.state.vehicleName,
|
||||||
|
editing: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVehicleName = (event) => {
|
||||||
|
this.setState({
|
||||||
|
vehicleNameEdit: event.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
saveVehicleNameChange = () => {
|
||||||
|
this.setState({
|
||||||
|
loading: true
|
||||||
|
}, () => this.setState({
|
||||||
|
vehicleName: this.state.vehicleNameEdit,
|
||||||
|
editing: false
|
||||||
|
}, () => {
|
||||||
|
this.setState({
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="w-full flex flex-row justify-center text-xl">
|
||||||
|
{
|
||||||
|
(this.state.loading)
|
||||||
|
? <Loading />
|
||||||
|
: (this.state.editing)
|
||||||
|
? <div className="flex flex-row items-center">
|
||||||
|
<input className="px-2 border" type="text" value={this.state.vehicleNameEdit} onChange={this.updateVehicleName}/>
|
||||||
|
<div id="save" className="pl-2 cursor-pointer" onClick={this.saveVehicleNameChange}><FontAwesomeIcon icon={faSave} /></div>
|
||||||
|
<Tooltip anchorSelect="#save" place="bottom">Save changes</Tooltip>
|
||||||
|
</div>
|
||||||
|
: <div className="flex items-center">
|
||||||
|
{this.state.vehicleName}
|
||||||
|
<div id="edit" className="pl-2 hover:cursor-pointer" onClick={this.editVehicleName}>
|
||||||
|
<FontAwesomeIcon icon={faPenToSquare} />
|
||||||
|
<Tooltip anchorSelect="#edit" place="bottom">Edit vehicle name</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,36 @@
|
||||||
export default function FleetManager() {
|
import { Component } from "react";
|
||||||
|
import Header from "./FleetManager-Components/Header";
|
||||||
|
import Sidebar from "./FleetManager-Components/Sidebar";
|
||||||
|
import VehicleInformation from "./FleetManager-Components/VehicleInformation";
|
||||||
|
|
||||||
|
export default class FleetManager extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
companyVehicles: [
|
||||||
|
{id: 1, name: "1"}, {id: 2, name: "2"}, {id: 3, name: "3"}, {id: 4, name: "4"}, {id: 5, name: "5"}
|
||||||
|
],
|
||||||
|
selectedVehicle: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelectedVehicle = (selected) => {
|
||||||
|
this.setState({
|
||||||
|
selectedVehicle: selected
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
return (
|
return (
|
||||||
<div></div>
|
<div className="w-full min-h-screen flex flex-col">
|
||||||
|
<Header user={this.props.user} />
|
||||||
|
<div className="grow flex flex-row">
|
||||||
|
<Sidebar companyVehicles={this.state.companyVehicles} selectedVehicle={this.state.selectedVehicle} selectVehicle={this.updateSelectedVehicle}/>
|
||||||
|
<VehicleInformation selectedVehicle={this.state.selectedVehicle} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
import React, {Component} from "react";
|
||||||
|
import Loading from "./Loading";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faCancel, faSave, faEdit } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
|
export default class EditableTextDisplay extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
editValue: '',
|
||||||
|
loading: false,
|
||||||
|
edit: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edit = () => {
|
||||||
|
this.setState({
|
||||||
|
editValue: this.props.defaultValue,
|
||||||
|
edit: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange = (event) => {
|
||||||
|
this.setState({
|
||||||
|
editValue: event.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel = () => {
|
||||||
|
this.setState({
|
||||||
|
editValue: '',
|
||||||
|
edit: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
propagateChange = () => {
|
||||||
|
this.props.callback(this.state.editValue, this.props.label);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
edit: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
(this.state.loading)
|
||||||
|
? <Loading />
|
||||||
|
: (this.state.edit)
|
||||||
|
? <div className="flex flex-row items-center">
|
||||||
|
<input className="ml-2 border px-2" type={this.props.fieldType} value={this.state.editValue} onChange={this.handleChange} />
|
||||||
|
<div id={`${this.props.label}Cancel`} className="ml-2" onClick={this.cancel}><FontAwesomeIcon icon={faCancel} /></div>
|
||||||
|
<div id={`${this.props.label}Save`} className="ml-2" onClick={this.propagateChange}><FontAwesomeIcon icon={faSave} /></div>
|
||||||
|
<Tooltip anchorSelect={`#${this.props.label}Save`} place="bottom">
|
||||||
|
Save {this.props.label.toUpperCase()}
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip anchorSelect={`#${this.props.label}Cancel`} place="bottom">
|
||||||
|
Cancel {this.props.label.toUpperCase()} Edit
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
: <div className="flex flex-row ml-2">
|
||||||
|
<div>{this.props.defaultValue}</div>
|
||||||
|
<div id={`${this.props.label}Edit`} className="ml-2" onClick={this.edit}><FontAwesomeIcon icon={faEdit} /></div>
|
||||||
|
<Tooltip anchorSelect={`#${this.props.label}Edit`} place="bottom">
|
||||||
|
Edit {this.props.label.toUpperCase()}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
|
|
||||||
|
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
|
export default function Loading() {
|
||||||
|
return (
|
||||||
|
<FontAwesomeIcon icon={faSpinner} spin />
|
||||||
|
)
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
|
@ -3,14 +3,11 @@ import ReactDOM from 'react-dom/client';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
|
|
||||||
import { Provider } from 'react-redux'
|
|
||||||
import store from './store'
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
root.render(
|
root.render(
|
||||||
<Provider store={store}>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</Provider>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { createSlice } from "@reduxjs/toolkit";
|
|
||||||
|
|
||||||
export const authenticationSlice = createSlice({
|
|
||||||
name: 'auth',
|
|
||||||
initialState: {
|
|
||||||
authenticated: false,
|
|
||||||
userId: null,
|
|
||||||
fullName: null
|
|
||||||
},
|
|
||||||
reducers: {
|
|
||||||
authenticated: (state, action) => {
|
|
||||||
state.authenticated = true;
|
|
||||||
state.userId = action.payload.userId;
|
|
||||||
state.fullName = action.payload.fullName;
|
|
||||||
},
|
|
||||||
logout: (state) => {
|
|
||||||
state = {
|
|
||||||
authenticated: false,
|
|
||||||
userId: null,
|
|
||||||
fullName: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export const {authenticated, logout} = authenticationSlice.actions
|
|
||||||
export const selectAuthenticated = (state) => state.authenticated
|
|
||||||
|
|
||||||
export default authenticationSlice.reducer
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { configureStore } from '@reduxjs/toolkit'
|
|
||||||
|
|
||||||
import authenticationReducer from './stateSlicers/authenticationSlice'
|
|
||||||
|
|
||||||
export default configureStore({
|
|
||||||
reducer: {authenticationReducer},
|
|
||||||
})
|
|
Loading…
Reference in New Issue