Initiates real-estate react app

This commit is contained in:
Vazhin Tayeb 2021-01-17 11:06:55 +03:00
parent 627248287e
commit db873560fc
43 changed files with 15393 additions and 0 deletions

View File

@ -0,0 +1,68 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br>
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.<br>
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
### Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
### Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
### Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
### `npm run build` fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUT3DoD4EzVjOECDlLV/skOjH4sNIwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAxMDcwNjU0NTdaFw0yMjAx
MDcwNjU0NTdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCiLrQItvpfLiVGXbe/6tml5PTjKc0az7KReMFFlTc0
3KvvD/7bAfwk2RqPEVF3pUTOJ3rGbYBtYi0GdgQbgiSJuuvbUtL+Pf1RRfNR2XZq
vfcJiytsVGOvD6JiFEFTn1P+7W2zIYlO7MuqGK2f2Fjb6zeEMG/7rRGvo3YIGfx/
vQE6JTWm1HhU0vDpc3pxm2ajW5NjG++7FgoycfQ+BDoQ0XitvWj2b+jt9m2/yPdz
9ptj41mDJWvtrPyJti2/3Bprccv/+qWSZGR7Io9+31e35R85sUpFZS91zTNWyy0Z
CoSgfpqFg/3ul/ROrAYdtfP2U5TJI4GGxaqFV9QP78DrAgMBAAGjUzBRMB0GA1Ud
DgQWBBSgHQxgyIpioCFuWirLuW/ZEwVYvTAfBgNVHSMEGDAWgBSgHQxgyIpioCFu
WirLuW/ZEwVYvTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBF
uZzW4vwdFfeCt8rqUjDcqnz7FM1al2GccHJ7ofDQYJo8gaWuSH2SeJ11eXKDux8M
xP83Nxr4s4rh6DaSybo3wqineMmEJ2F/ikjOMdgvoeTvFV32WAn+UBcmSjvfP6C9
HSSryc13nWS31xJ2SuMHkkBeOn4ZhwiRZYcsduVFFhkBpwzQx2HD0Dr0VaEefig4
pBQy2llCFGF/u6HoGeB87Cjs0rnWguvseYl28hgsX8fkZY+IWbY8OXL+jy0g0LSc
ap9lpqst3WfyFp0UvM7EPjdsxChsSOf4KbC/aHZCqTISRcg8snEHp5Rb6zE8NP/0
k62LJlKMPn0z8dS4JEp0
-----END CERTIFICATE-----

27
React/real-estate/key.pem Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAoi60CLb6Xy4lRl23v+rZpeT04ynNGs+ykXjBRZU3NNyr7w/+
2wH8JNkajxFRd6VEzid6xm2AbWItBnYEG4Ikibrr21LS/j39UUXzUdl2ar33CYsr
bFRjrw+iYhRBU59T/u1tsyGJTuzLqhitn9hY2+s3hDBv+60Rr6N2CBn8f70BOiU1
ptR4VNLw6XN6cZtmo1uTYxvvuxYKMnH0PgQ6ENF4rb1o9m/o7fZtv8j3c/abY+NZ
gyVr7az8ibYtv9waa3HL//qlkmRkeyKPft9Xt+UfObFKRWUvdc0zVsstGQqEoH6a
hYP97pf0TqwGHbXz9lOUySOBhsWqhVfUD+/A6wIDAQABAoIBAATc258LRXSHIKz8
cF77vqzfsYwCG9k68wKzmS/p6m7WUv1nAGA2rgW40LgLb+LjfK2lt2OtstUUxX7V
GQhuHYRXq2y3DgZ7e1Xtf/8rQwauTXLmzlWALkD4egjwzIiiVVVmbwyY69IG/ZBL
DyGkzf1CDzcScLkeFlKq1wYlKVH3H43rlhpP0+VvsVkPMdv3rO/N6tZGD4Dh3wY4
Pnf6RQcQVcolD7l2reLxJW7CIBJbnRFuSmtWkRNR+nii0KPxrRFMOLWRpPQlMVs3
Ho0x0C+qFghvYTHyQnIPaCsveAyeaQos4ltKSBiYT0+nABdpI39A43DOuTJIcOs4
2G4/FokCgYEA1/8sDm9zZeFUX/86dVfixOeWpP6xKqp/XlyvxHbSqLLfn8WaJ9dF
azmZWo40o7z2/gTPAVaN3PEnuU3KPCWBVWSFm3bHLzuslXF/Z5l8uqiFvhZNcNNq
mGCqMmPxqxgkIMF/aqs+0uj2DkiKvEgWeb7AfyDYPHY/tPpKlqUe8WcCgYEAwDgW
uUX6xnastpkeMV/fVl6CA5pdxVUyf4cqMMqdcFza7+dgHLSLX6jDr81zph57UzyC
MJ4RUPznhVYSFGtIRLRq83ZDrd2J4obGO0RPzbj2JxiF5o+fmo73uJDtJkZo2EPX
50OVXsJa6IES3FiGNxJlAV+TkxnzXjOqJGQ77d0CgYAQqC7/lfyyOKRHGumpLvPf
93QYmUOUZL2Zy1AKuJcaR2ETcyumMuW5lSGMaDcLAYGSqVBFbCVYiohjs+oKpWHJ
8gyTpp9JtzJh+/S1SpLh2ikmNZYXQ1aPFbVKWYbDiQMQO5vV4AriPPDsvQ9l4bFK
BjnWE/RZBU14aWzjHSy7tQKBgCJ+rk1Haq6NA4etSMbRjrHgOfLNlABivrI2HnMt
GT3d3AhrfEsKd2yOS7fT5eos1DLmy/JPm4nuKNo7zPjPG+QAgKT6V0DvXdxCFXbz
VmLzy3DOrNDoe1rwRzJfB7/zqMMOwcMl1Ltxo4DQEQNr+4IMkgXCEii24n7IRMNN
HZgdAoGBALzFDienCJmCUwpYXSLcN6ZqNYSDNQNgjwkWoRjpCYrzpRrcTxLF5o8L
Gz6QTXbkYOTZ0UaE9AlR6Aci/4o6RfucLsmbc0EvvyVbrVoX7i3rnH4HOK+RuAvj
7KlMPWiIQx4KciuUr8gHCufeCDb1RGvbRPIege8XGnJlQCf4s9sh
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIxVNNPfT6hrwCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECPLxxQXt0qGZBIIEyE+8XcltU9Fv
SsFNOcp3/9saZi7sw0IIitUIF5QU2O2qG8qCQ5hU+OJkFNxwXNgBkwLvvRVS3YAH
5kkQ6S5GdoHJhw9LaSycPXYF+bOHt9IIMHmBjMKuXnX3eDL7dXwFf+EHE2HzIjN4
mjweE7eSgvuLLZfPsC6/izuCHVc66R6ZsJjQhzCakSPoGiUlZ4JcExDZA3q/NeyH
VIYy8dZ4dx1SXiLDd4r7FamRM+WxgNeCCPdZOyVlP0hlOCw1y7tH6I0oG3Ktzrj+
o/SV6e6KwQYGs2tETNTrPT0ykkMNUpGRdsx+nTZ2Dy4CVNnqwrZqVxwbVFW3kSQy
E1pP08stVbC7IbGpYWuIx2BgK4G96x+sFE8HIxUINOocVZgJQvhoGbW2EJynywZ3
tUpz9zNZTyCtKpw7iTbeLa0RVcuqJdP3YKVTYSZCOWO72TMc5WdQ1mqGwRH+i3ze
ik1YZNRBNMkLL4UZLcwuA+WfGjkWyIpyuqcqq2IlILaVCGoyWhmOWSug3ezmZTt7
eO1mgPvTUhShjlI/3DqFA2hBpq51k6MZUniKPNGcMAdyluVdw3m1MM623vocUZn6
b4ZQWldDXYTjSl3yaxjrY154z85HQJGon6Frk2kWX0PIgyNJ4S8JBYn9UvMbxp4I
bh2ZZOfIm3R4FyEX+gHpZ1NZH03NImxCK6ND7Ncz9kOPuMokjCPLuZS5XvCpLrvU
wDalfVV5TQrt1s9Ir7viZDY4xxNA6FD1rd7eA+2ZHwh0t88l7b02IMQ7hNQ0Ptn9
pt3v7xfV4w+ujRk9tFfXv9Nju/hKH6dHpPqhZP1Q1iTf1J2YX++8UCVfgNJSo4e+
ORAhvy5ZFfUeiAgtYTlbwSwDlTgGxYQ43ge7zp/B3/BgtUvvWBlZ5PNl/iTvpFUV
NeYRWB0HuzFOehDnB3ZsstjPP+ly3yoV6Xef9+aNp08HL966i41+ttNLW8r0W1yQ
taYLV3O2BtQwmQFogfaL5gO44fOdmr4aqiSrKGfK72iX+6qqu/b37ucUUVZUpzuA
jnoz1ELXUl35f4iW+yC7orIe3jv7n4BiZj/1iHlnqyStFYZlGXFXCb2ieBxHyso5
HbHEKa4vcJ0uZR26pIkweOTi1IiB5gJ6Dlw7alDTtl2bIzZ3JJ8sg2m9Y93ewWld
8Qapg19tP/ULOv2PpN1PEo62Gyb9WBzue5yTamXsfxupHbsS7nAoi4MVxFwQZYBV
/+HiZRYtDYSbn43A/Wo2gS908pRrNWRFBfDd/bzztAiotPJpMYamHDc7w08SEaM4
J4tujBIdC8KsewwDwnUvMUev8TXxDMbdjijZcDuaPaUOhPoppiaWbRTtoXKbxUam
SdTWuT3dXROLgkgBVD2wyLKWoRKD4SIW1n1gwD6MUr1wnJsss0u2iqSDZvaW9Nsu
K1UhAMCbbCG1WSoENaN2tuj+A0Hpk7mEoFZ9yKopipCsMfOouizG5kKWfu4vwkn2
xVCNyW6oAwKtM5fPo+Vh7U62t2AEHlQePaGfzWek+B0ptoVLFa6v4UPxrVXO2CTg
lseyJH6z2+UV9F0vCWIV/PkaAaVaCeP0q7qTG0E1Wqo7SuGq+q0AOessDeEgfO7m
91L5j36PkAKLvjpwgHl4AQ==
-----END ENCRYPTED PRIVATE KEY-----

13765
React/real-estate/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
{
"name": "oidcclient",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.19.0",
"milligram": "^1.4.1",
"oidc-client": "^1.9.1",
"react": "^16.10.0",
"react-dom": "^16.10.0",
"react-icons": "^4.1.0",
"react-redux": "^7.1.1",
"react-responsive": "^8.2.0",
"react-router-dom": "^5.1.0",
"react-scripts": "3.1.2",
"redux": "^4.0.4",
"tailwindcss": "^2.0.2"
},
"scripts": {
"start": "HTTPS=true react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<title>Traffic Police</title>
<style>
body {
text-align: center;
}
code {
text-align: left;
}
</style>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

View File

@ -0,0 +1,48 @@
import React, { useEffect } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import SigninOidc from "./pages/signin-oidc";
import SignoutOidc from "./pages/signout-oidc";
import Home from "./pages/home";
import Login from "./pages/login";
import { Provider } from "react-redux";
import store from "./store";
import userManager, { loadUserFromStorage } from "./services/userService";
import AuthProvider from "./utils/authProvider";
import PrivateRoute from "./utils/protectedRoute";
import "./index.css";
import { storeUser } from "./actions/authActions";
function App() {
useEffect(() => {
// Commented out not to check the localStorage for the user's existence ** Temporary **
// fetch current user from cookies
loadUserFromStorage(store);
}, []);
// Should be removed ** Temporary **
//const logUserIn = () => {
// const user = { profile: { given_name: "John Doe" } };
// console.log(`User logged in!`);
// store.dispatch(storeUser(user));
// };
// ** Temporary **
// logUserIn();
return (
<Provider store={store}>
<AuthProvider userManager={userManager} store={store}>
<Router>
<Switch>
<Route path="/login" component={Login} />
<Route path="/signout-oidc" component={SignoutOidc} />
<Route path="/signin-oidc" component={SigninOidc} />
<PrivateRoute path="/" component={Home} />
</Switch>
</Router>
</AuthProvider>
</Provider>
);
}
export default App;

View File

@ -0,0 +1,41 @@
import {
STORE_USER,
USER_SIGNED_OUT,
USER_EXPIRED,
STORE_USER_ERROR,
LOADING_USER
} from './types'
import { setAuthHeader } from '../utils/axiosHeaders'
export function storeUser(user) {
setAuthHeader(user.access_token)
return {
type: STORE_USER,
payload: user
}
}
export function loadingUser() {
return {
type: LOADING_USER
}
}
export function storeUserError() {
return {
type: STORE_USER_ERROR
}
}
export function userExpired() {
return {
type: USER_EXPIRED
}
}
export function userSignedOut() {
return {
type: USER_SIGNED_OUT
}
}

View File

@ -0,0 +1,5 @@
export const STORE_USER = 'STORE_USER'
export const USER_SIGNED_OUT = 'USER_SIGNED_OUT'
export const USER_EXPIRED = 'USER_EXPIRED'
export const STORE_USER_ERROR = 'STORE_USER_ERROR'
export const LOADING_USER = 'LOADING_USER'

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -0,0 +1,90 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Playfair+Display&display=swap");
:root {
--blue: #2170b9;
--black: #1a1c21;
}
.bg-blue-custom {
background-color: var(--blue);
}
.text-blue-custom {
color: var(--blue);
}
p,
.font-inter {
font-family: "Inter", sans-serif;
}
.font-playfair {
font-family: "Playfair Display", serif;
}
.text-black-custom {
color: var(--black);
}
p,
h1,
h2,
h3,
h4,
h5,
h6 {
text-align: left;
}
button:hover {
background-color: #1d6ab1;
border: none;
}
@media only screen and (max-width: 950px) {
.navbar-list {
position: absolute;
top: 0px;
right: 0px;
flex-direction: column;
align-items: flex-start;
background-color: #dfdfdf;
width: 40vw;
height: 100vh;
padding: 35vh 5vw 35vh 5vw;
}
.navbar-list li a,
.navbar-list li:nth-child(3) {
font-size: 1.5rem !important;
border-bottom: 1px solid var(--blue);
padding-bottom: 5px;
color: var(--blue);
}
.navbar-list li a:hover {
color: var(--blue);
}
.hamburger-menu {
display: block;
z-index: 1000;
}
}
@media only screen and (max-width: 700px) {
.navbar-list {
width: 50vw;
}
}
@media only screen and (max-width: 600px) {
.navbar-list {
width: 65vw;
}
.navbar-list li a,
.navbar-list li:nth-child(3) {
font-size: 1.28rem !important;
}
}

View File

@ -0,0 +1,6 @@
import React from "react";
import ReactDOM from "react-dom";
import "milligram/dist/milligram.min.css";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));

View File

@ -0,0 +1,15 @@
import React from "react";
import VehicleRegisterForm from "./components/VehicleRegisterForm";
import Navbar from "./components/Navbar";
import Main from "./components/Main";
const RegisterVehicle = () => {
return (
<div>
<Navbar />
<Main Component={VehicleRegisterForm} />
</div>
);
};
export default RegisterVehicle;

View File

@ -0,0 +1,15 @@
import React from "react";
import Navbar from "./components/Navbar";
import Main from "./components/Main";
import VehiclesMain from "./components/VehiclesMain";
const YourVehicles = () => {
return (
<div>
<Navbar />
<Main Component={VehiclesMain} />
</div>
);
};
export default YourVehicles;

View File

@ -0,0 +1,12 @@
import React from "react";
const Button = ({ onClickEvent, text, classes = "" }) => (
<button
onClick={onClickEvent}
className={`bg-blue-custom border-0 font-inter font-semibold rounded text-xl m-0 ${classes}`}
>
{text}
</button>
);
export default Button;

View File

@ -0,0 +1,14 @@
import React from "react";
const Card = ({ children, classes = "", styles = {} }) => {
return (
<div
style={{ ...styles }}
className={`xl:w-1/3 lg:w-5/12 md:w-2/4 sm:w-9/12 w-11/12 flex flex-col justify-center shadow md:px-32 sm:px-28 md:py-36 sm:py-36 px-20 py-16 my-20 ${classes}`}
>
{children}
</div>
);
};
export default Card;

View File

@ -0,0 +1,12 @@
import React from "react";
const Heading1 = ({ text, classes = "", styles = {} }) => (
<h1
className={`font-playfair text-black-custom text-6xl mb-12 ${classes}`}
style={{ ...styles }}
>
{text}
</h1>
);
export default Heading1;

View File

@ -0,0 +1,14 @@
import React from "react";
const Heading3 = ({ children, styles = {} }) => {
return (
<h3
style={{ ...styles }}
className="font-light font-inter text-black-custom my-20 self-start"
>
{children}
</h3>
);
};
export default Heading3;

View File

@ -0,0 +1,34 @@
import React from "react";
import Button from "./Button";
import { storeUser } from "../../actions/authActions";
import store from "../../store";
import Heading1 from "./Heading1";
import Card from "./Card";
// Services
import { signinRedirect } from "../../services/userService";
const LoginWindow = () => {
// const onUserLogin = () => {
// const user = { profile: { given_name: "John Doe" } };
// console.log(`User logged in!`);
// store.dispatch(storeUser(user));
// };
function login() {
signinRedirect();
}
return (
<Card>
<Heading1 text={"Traffic Police Service"} />
<p className="text-black-custom font-light text-3xl mb-20">
Login to your account to view your dashboard and register a new vehicle.
</p>
<Button onClickEvent={login} text={"Login"} />
</Card>
);
};
export default LoginWindow;

View File

@ -0,0 +1,14 @@
import React from "react";
const Main = ({ Component, styles = {} }) => {
return (
<div
className="flex flex-col items-center justify-center h-4/5"
style={{ ...styles, marginTop: "80px" }}
>
<Component />
</div>
);
};
export default Main;

View File

@ -0,0 +1,84 @@
import React, { useState } from "react";
import logo from "../../images/logo.svg";
import { Link } from "react-router-dom";
import { useSelector } from "react-redux";
import { IoMdMenu } from "react-icons/io";
import { useMediaQuery } from "react-responsive";
// Services
import { signoutRedirect } from "../../services/userService";
const Navbar = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const isMobile = useMediaQuery({ query: "(max-width: 950px)" });
const user = useSelector((state) => state.auth.user);
function signOut() {
signoutRedirect();
}
return (
<div
className="w-screen bg-blue-custom flex justify-center items-center fixed top-0 left-0"
style={{ height: "80px" }}
>
<nav className="flex items-center justify-between xl:w-4/5 w-10/12">
<Link to="/">
<img src={logo} alt="logo" className="h-24" />
</Link>
{user ? (
<>
<IoMdMenu
onClick={() =>
isMenuOpen ? setIsMenuOpen(false) : setIsMenuOpen(true)
}
className={`text-5xl text-white hidden hamburger-menu cursor-pointer ${
isMenuOpen ? "text-blue-custom" : ""
}`}
/>
<ul
className="list-none flex items-center justify-between m-0 navbar-list"
style={
isMobile
? isMenuOpen
? { display: "flex" }
: { display: "none" }
: { display: "flex" }
}
>
<li className="m-0">
<Link
to="/"
className="text-white font-inter font-semibold hover:text-white focus:text-white tracking-normal mr-20"
style={{ fontSize: "1.335rem" }}
>
Your Vehicles
</Link>
</li>
<li className="m-0">
<Link
to="/register"
className="text-white font-inter font-semibold hover:text-white focus:text-white tracking-normal mr-20"
style={{ fontSize: "1.335rem" }}
>
Register A Vehicles
</Link>
</li>
<li
onClick={signOut}
className="m-0 text-white font-inter font-semibold hover:text-white focus:text-white cursor-pointer tracking-normal"
style={{ fontSize: "1.335rem" }}
>
Sign Out
</li>
</ul>
</>
) : (
""
)}
</nav>
</div>
);
};
export default Navbar;

View File

@ -0,0 +1,79 @@
import React, { useState } from "react";
import { useSelector } from "react-redux";
// Services
import * as apiService from "../../services/apiService";
// Components
import Card from "./Card";
import Heading3 from "./Heading3";
import Button from "./Button";
import InputText from "./inputs/InputText";
import InputSelect from "./inputs/InputSelect";
const VehicleRegisterForm = () => {
const [model, setModel] = useState("");
const [licensePlate, setlicensePlate] = useState("");
const [color, setColor] = useState("White");
const [type, setType] = useState(1);
const user = useSelector((state) => state.auth.user);
async function registerVehicle() {
await apiService.registerVehicle(
{ model, licensePlate, color, type },
user.access_token
);
// await getVehicles();
}
return (
<Card styles={{ padding: "60px" }} classes="xl:w-2/5">
<form onSubmit={(e) => e.preventDefault()} className="m-0 flex flex-col">
<Heading3 styles={{ marginTop: "0" }}>Register A Vehicle</Heading3>
<div className="flex flex-col">
<InputText
label="Model"
value={model}
name="model"
placeholder="Model"
onChangeEvent={(e) => setModel(e.target.value)}
/>
<InputText
label="License Plate"
value={licensePlate}
name="licensePlate"
placeholder="License Plate"
onChangeEvent={(e) => setlicensePlate(e.target.value)}
/>
<InputText
label="Color"
value={color}
name="color"
placeholder="Color"
onChangeEvent={(e) => setColor(e.target.value)}
/>
<InputSelect
label="Type"
value={type}
name="type"
onChangeEvent={(e) => setType(e.target.value)}
options={[
{ value: "1", text: "Sedan" },
{ value: "2", text: "SUV" },
{ value: "3", text: "Pickup" },
]}
/>
</div>
<Button
text="Register"
onClickEvent={registerVehicle}
classes="mt-10"
/>
</form>
</Card>
);
};
export default VehicleRegisterForm;

View File

@ -0,0 +1,98 @@
import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
import { useHistory } from "react-router-dom";
// Services
import * as apiService from "../../services/apiService";
// Components
import Heading1 from "./Heading1";
import Heading3 from "./Heading3";
import Button from "./Button";
import Table from "./table/Table";
const VehiclesMain = () => {
const user = useSelector((state) => state.auth.user);
const history = useHistory();
// Initial state must be null ** Temporary **
const [vehicleData, setVehicleData] = useState([
{
id: Math.floor(Math.random() * 200) + 1,
color: "White",
model: "Toyota Camry",
licensePlate: "14324235",
type: 2,
},
{
id: Math.floor(Math.random() * 200) + 1,
color: "Black",
model: "Honda Accord",
licensePlate: "8765658",
type: 1,
},
{
id: Math.floor(Math.random() * 200) + 1,
color: "Beige",
model: "Toyota Corolla",
licensePlate: "235464",
type: 2,
},
{
id: Math.floor(Math.random() * 200) + 1,
color: "Grey",
model: "Toyota Yaris",
licensePlate: "1878767",
type: 2,
},
{
id: Math.floor(Math.random() * 200) + 1,
color: "White",
model: "Toyota Camry",
licensePlate: "9956443",
type: 1,
},
]);
useEffect(() => {
getVehicles();
// eslint-disable-next-line
}, []);
async function getVehicles() {
console.log(user);
const vehicles = await apiService.getVehiclesFromApi(user.access_token);
setVehicleData(vehicles);
}
return (
<div className="flex flex-col xl:w-2/3 lg:w-4/5 w-11/12">
<Heading1
text={"Traffic Police Service"}
classes="mt-24"
styles={{
marginBottom: "0",
}}
/>
{/* <p>Hello, {user.profile.given_name}.</p> */}
{vehicleData ? (
<>
<Heading3>Your Vehicles:</Heading3>
<Table vehicleData={vehicleData} />
</>
) : (
<h3 className="font-light font-inter text-black-custom my-32 self-center">
No vehicles yet.
</h3>
)}
<Button
text="Register A Vehicle"
classes={vehicleData && "self-start my-24"}
onClickEvent={() => history.push("/register")}
/>
</div>
);
};
export default VehiclesMain;

View File

@ -0,0 +1,39 @@
import React from "react";
const InputSelect = ({
label,
value,
name,
placeholder,
onChangeEvent,
options,
}) => {
return (
<div className="flex flex-col items-start">
<label className="font-inter font-semibold text-2xl text-black-custom mb-4">
{label}
</label>
<select
value={value}
name={name}
onChange={onChangeEvent}
className="font-normal font-inter text-2xl text-black-custom mb-10 cursor-pointer"
style={{
borderRadius: ".25rem",
border: "none",
boxShadow: `0 1px 1px rgba(0, 0, 0, 0.2), 0 0 4px rgba(0, 0, 0, 0.1)`,
background: "none",
appearance: "auto",
}}
>
{options.map((option, i) => (
<option key={i} value={option.value}>
{option.text}
</option>
))}
</select>
</div>
);
};
export default InputSelect;

View File

@ -0,0 +1,27 @@
import React from "react";
const InputText = ({ label, value, name, onChangeEvent, placeholder }) => {
return (
<div className="flex flex-col items-start">
<label className="font-inter font-semibold text-2xl text-black-custom mb-4">
{label}
</label>
<input
value={value}
type="text"
name={name}
onChange={onChangeEvent}
className="font-normal font-inter text-2xl text-black-custom mb-10"
placeholder={placeholder}
autoComplete="off"
style={{
borderRadius: ".25rem",
border: "none",
boxShadow: `0 1px 1px rgba(0, 0, 0, 0.2), 0 0 4px rgba(0, 0, 0, 0.1)`,
}}
/>
</div>
);
};
export default InputText;

View File

@ -0,0 +1,13 @@
import React from "react";
const ColumnName = ({ text }) => (
<th
scope="col"
className="whitespace-nowrap text-2xl font-inter text-black-custom font-semibold text-center"
style={{ padding: "10px 40px 10px 40px" }}
>
{text}
</th>
);
export default ColumnName;

View File

@ -0,0 +1,15 @@
import React from "react";
import { FiSettings, FiTrash } from "react-icons/fi";
import { BsThreeDots } from "react-icons/bs";
const EditBtns = () => {
return (
<div className="flex">
<FiTrash className="mx-3 cursor-pointer" />
<FiSettings className="mx-3 cursor-pointer" />
<BsThreeDots className="mx-3 text-3xl cursor-pointer" />
</div>
);
};
export default EditBtns;

View File

@ -0,0 +1,51 @@
import React from "react";
import ColumnName from "./ColumnName";
import EditBtns from "./EditBtns";
import TableData from "./TableData";
const Table = ({ vehicleData }) => {
function getType(type) {
// eslint-disable-next-line
switch (type) {
case 1:
return "Sedan";
case 2:
return "SUV";
case 3:
return "Pickup";
}
}
return (
<div className="shadow overflow-x-auto border-b border-gray-200 sm:rounded w-full">
<table className="min-w-full divide-y divide-gray-200 mb-0">
<thead className="bg-gray-50">
<tr>
<ColumnName text={"Model"} />
<ColumnName text={"License Plate"} />
<ColumnName text={"Color"} />
<ColumnName text={"Type"} />
<th scope="col" className="relative px-6 py-3">
<span className="sr-only">Actions</span>
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{vehicleData.map((vehicle) => (
<tr key={vehicle.id}>
<TableData text={vehicle.model} />
<TableData text={vehicle.licensePlate} />
<TableData text={vehicle.color} />
<TableData text={getType(vehicle.type)} />
<TableData Component={EditBtns} />
</tr>
))}
{/* <!-- More items... --> */}
</tbody>
</table>
</div>
);
};
export default Table;

View File

@ -0,0 +1,12 @@
import React from "react";
const TableData = ({ text, Component }) => (
<td
className="whitespace-nowrap text-2xl font-inter text-black-custom font-normal text-center"
style={{ padding: "10px 40px 10px 40px" }}
>
{text ? text : <Component />}
</td>
);
export default TableData;

View File

@ -0,0 +1,21 @@
import React from "react";
import { Switch, Route, BrowserRouter as Router } from "react-router-dom";
import RegisterVehicle from "./RegisterVehicle";
import YourVehicles from "./YourVehicles";
function Home() {
return (
<Router>
<Switch>
<Route path="/register">
<RegisterVehicle />
</Route>
<Route path="/">
<YourVehicles />
</Route>
</Switch>
</Router>
);
}
export default Home;

View File

@ -0,0 +1,21 @@
import React from "react";
import { Redirect } from "react-router-dom";
import { useSelector } from "react-redux";
import Navbar from "./components/Navbar";
import Main from "./components/Main";
import LoginWindow from "./components/LoginWindow";
function Login() {
const user = useSelector((state) => state.auth.user);
return user ? (
<Redirect to={"/"} />
) : (
<div>
<Navbar />
<Main Component={LoginWindow} />
</div>
);
}
export default Login;

View File

@ -0,0 +1,22 @@
import React, { useEffect } from 'react'
import { signinRedirectCallback } from '../services/userService'
import { useHistory } from 'react-router-dom'
function SigninOidc() {
const history = useHistory()
useEffect(() => {
async function signinAsync() {
await signinRedirectCallback()
history.push('/')
}
signinAsync()
}, [history])
return (
<div>
Redirecting...
</div>
)
}
export default SigninOidc

View File

@ -0,0 +1,22 @@
import React, { useEffect } from 'react'
import { signoutRedirectCallback } from '../services/userService'
import { useHistory } from 'react-router-dom'
function SignoutOidc() {
const history = useHistory()
useEffect(() => {
async function signoutAsync() {
await signoutRedirectCallback()
history.push('/')
}
signoutAsync()
}, [history])
return (
<div>
Redirecting...
</div>
)
}
export default SignoutOidc

View File

@ -0,0 +1,38 @@
import {
USER_SIGNED_OUT,
STORE_USER_ERROR,
USER_EXPIRED,
STORE_USER,
LOADING_USER
} from '../actions/types'
const initialState = {
user: null,
isLoadingUser: false
};
export default function (state = initialState, action) {
switch (action.type) {
case STORE_USER:
return {
...state,
isLoadingUser: false,
user: action.payload
}
case LOADING_USER:
return {
...state,
isLoadingUser: true
}
case USER_EXPIRED:
case STORE_USER_ERROR:
case USER_SIGNED_OUT:
return {
...state,
user: null,
isLoadingUser: false
}
default:
return state
}
}

View File

@ -0,0 +1,6 @@
import { combineReducers } from 'redux';
import authReducer from './authReducer';
export default combineReducers({
auth: authReducer
})

View File

@ -0,0 +1,17 @@
import axios from 'axios'
async function getVehiclesFromApi(access_token) {
const response = await axios.get(`https://localhost:6001/api/Vehicles`, { headers: { 'Authorization': `Bearer ${access_token}` } });
return response.data;
}
async function registerVehicle(vehicle, access_token) {
console.log(vehicle);
const response = await axios.post(`https://localhost:6001/api/Vehicles`, vehicle, { headers: { 'Authorization': `Bearer ${access_token}` } });
return response.data;
}
export {
getVehiclesFromApi,
registerVehicle
}

View File

@ -0,0 +1,46 @@
import { UserManager } from 'oidc-client';
import { storeUserError, storeUser } from '../actions/authActions'
const config = {
authority: "https://localhost:10000",
client_id: "traffic-police-react-app",
redirect_uri: "https://localhost:3000/signin-oidc",
response_type: "code",
scope: "openid profile traffic-police-api",
post_logout_redirect_uri: "https://localhost:3000/signout-oidc",
};
const userManager = new UserManager(config)
export async function loadUserFromStorage(store) {
try {
let user = await userManager.getUser()
if (!user) { return store.dispatch(storeUserError()) }
store.dispatch(storeUser(user))
} catch (e) {
console.error(`User not found: ${e}`)
store.dispatch(storeUserError())
}
}
export function signinRedirect() {
return userManager.signinRedirect()
}
export function signinRedirectCallback() {
return userManager.signinRedirectCallback()
}
export function signoutRedirect() {
userManager.clearStaleState()
userManager.removeUser()
return userManager.signoutRedirect()
}
export function signoutRedirectCallback() {
userManager.clearStaleState()
userManager.removeUser()
return userManager.signoutRedirectCallback()
}
export default userManager

View File

@ -0,0 +1,13 @@
import { createStore, compose } from 'redux';
import rootReducer from './reducers'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const initialState = {}
const store = createStore(
rootReducer,
initialState,
composeEnhancers()
)
export default store;

View File

@ -0,0 +1,54 @@
import React, { useEffect, useRef } from 'react';
import { storeUser } from '../actions/authActions'
import { setAuthHeader } from './axiosHeaders';
export default function AuthProvider({ userManager: manager, store, children }) {
let userManager = useRef();
useEffect(() => {
userManager.current = manager
const onUserLoaded = (user) => {
console.log(`user loaded: ${user}`)
store.dispatch(storeUser(user))
}
const onUserUnloaded = () => {
setAuthHeader(null)
console.log(`user unloaded`)
}
const onAccessTokenExpiring = () => {
console.log(`user token expiring`)
}
const onAccessTokenExpired = () => {
console.log(`user token expired`)
}
const onUserSignedOut = () => {
console.log(`user signed out`)
}
// events for user
userManager.current.events.addUserLoaded(onUserLoaded)
userManager.current.events.addUserUnloaded(onUserUnloaded)
userManager.current.events.addAccessTokenExpiring(onAccessTokenExpiring)
userManager.current.events.addAccessTokenExpired(onAccessTokenExpired)
userManager.current.events.addUserSignedOut(onUserSignedOut)
// Specify how to clean up after this effect:
return function cleanup() {
userManager.current.events.removeUserLoaded(onUserLoaded);
userManager.current.events.removeUserUnloaded(onUserUnloaded);
userManager.current.events.removeAccessTokenExpiring(onAccessTokenExpiring)
userManager.current.events.removeAccessTokenExpired(onAccessTokenExpired)
userManager.current.events.removeUserSignedOut(onUserSignedOut)
};
}, [manager, store]);
return (
React.Children.only(children)
)
}

View File

@ -0,0 +1,5 @@
import axios from 'axios'
export function setAuthHeader(token) {
axios.defaults.headers.common['Authorization'] = token ? 'Bearer ' + token : ''
}

View File

@ -0,0 +1,13 @@
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
import { useSelector } from 'react-redux'
function ProtectedRoute({ children, component: Component, ...rest }) {
const user = useSelector(state => state.auth.user)
return user
? (<Route {...rest} component={Component} />)
: (<Redirect to={'/login'} />)
}
export default ProtectedRoute