mirror of
https://github.com/ditkrg/AuthorizationServerDemos.git
synced 2026-01-22 22:47:02 +00:00
Initiates real-estate react app
This commit is contained in:
parent
627248287e
commit
db873560fc
68
React/real-estate/README.md
Normal file
68
React/real-estate/README.md
Normal 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 can’t go back!**
|
||||
|
||||
If you aren’t 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 you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t 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
|
||||
21
React/real-estate/cert.pem
Normal file
21
React/real-estate/cert.pem
Normal 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
27
React/real-estate/key.pem
Normal 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-----
|
||||
30
React/real-estate/keytmp.pem
Normal file
30
React/real-estate/keytmp.pem
Normal 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
13765
React/real-estate/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
40
React/real-estate/package.json
Normal file
40
React/real-estate/package.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
React/real-estate/public/favicon.ico
Normal file
BIN
React/real-estate/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
27
React/real-estate/public/index.html
Normal file
27
React/real-estate/public/index.html
Normal 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>
|
||||
48
React/real-estate/src/App.js
Normal file
48
React/real-estate/src/App.js
Normal 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;
|
||||
41
React/real-estate/src/actions/authActions.js
Normal file
41
React/real-estate/src/actions/authActions.js
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
5
React/real-estate/src/actions/types.js
Normal file
5
React/real-estate/src/actions/types.js
Normal 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'
|
||||
399
React/real-estate/src/images/logo.svg
Normal file
399
React/real-estate/src/images/logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 101 KiB |
90
React/real-estate/src/index.css
Normal file
90
React/real-estate/src/index.css
Normal 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;
|
||||
}
|
||||
}
|
||||
6
React/real-estate/src/index.js
Normal file
6
React/real-estate/src/index.js
Normal 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"));
|
||||
15
React/real-estate/src/pages/RegisterVehicle.js
Normal file
15
React/real-estate/src/pages/RegisterVehicle.js
Normal 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;
|
||||
15
React/real-estate/src/pages/YourVehicles.js
Normal file
15
React/real-estate/src/pages/YourVehicles.js
Normal 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;
|
||||
12
React/real-estate/src/pages/components/Button.js
Normal file
12
React/real-estate/src/pages/components/Button.js
Normal 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;
|
||||
14
React/real-estate/src/pages/components/Card.js
Normal file
14
React/real-estate/src/pages/components/Card.js
Normal 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;
|
||||
12
React/real-estate/src/pages/components/Heading1.js
Normal file
12
React/real-estate/src/pages/components/Heading1.js
Normal 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;
|
||||
14
React/real-estate/src/pages/components/Heading3.js
Normal file
14
React/real-estate/src/pages/components/Heading3.js
Normal 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;
|
||||
34
React/real-estate/src/pages/components/LoginWindow.js
Normal file
34
React/real-estate/src/pages/components/LoginWindow.js
Normal 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;
|
||||
14
React/real-estate/src/pages/components/Main.js
Normal file
14
React/real-estate/src/pages/components/Main.js
Normal 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;
|
||||
84
React/real-estate/src/pages/components/Navbar.js
Normal file
84
React/real-estate/src/pages/components/Navbar.js
Normal 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;
|
||||
@ -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;
|
||||
98
React/real-estate/src/pages/components/VehiclesMain.js
Normal file
98
React/real-estate/src/pages/components/VehiclesMain.js
Normal 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;
|
||||
39
React/real-estate/src/pages/components/inputs/InputSelect.js
Normal file
39
React/real-estate/src/pages/components/inputs/InputSelect.js
Normal 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;
|
||||
27
React/real-estate/src/pages/components/inputs/InputText.js
Normal file
27
React/real-estate/src/pages/components/inputs/InputText.js
Normal 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;
|
||||
13
React/real-estate/src/pages/components/table/ColumnName.js
Normal file
13
React/real-estate/src/pages/components/table/ColumnName.js
Normal 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;
|
||||
15
React/real-estate/src/pages/components/table/EditBtns.js
Normal file
15
React/real-estate/src/pages/components/table/EditBtns.js
Normal 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;
|
||||
51
React/real-estate/src/pages/components/table/Table.js
Normal file
51
React/real-estate/src/pages/components/table/Table.js
Normal 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;
|
||||
12
React/real-estate/src/pages/components/table/TableData.js
Normal file
12
React/real-estate/src/pages/components/table/TableData.js
Normal 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;
|
||||
21
React/real-estate/src/pages/home.js
Normal file
21
React/real-estate/src/pages/home.js
Normal 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;
|
||||
21
React/real-estate/src/pages/login.js
Normal file
21
React/real-estate/src/pages/login.js
Normal 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;
|
||||
22
React/real-estate/src/pages/signin-oidc.js
Normal file
22
React/real-estate/src/pages/signin-oidc.js
Normal 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
|
||||
22
React/real-estate/src/pages/signout-oidc.js
Normal file
22
React/real-estate/src/pages/signout-oidc.js
Normal 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
|
||||
38
React/real-estate/src/reducers/authReducer.js
Normal file
38
React/real-estate/src/reducers/authReducer.js
Normal 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
|
||||
}
|
||||
}
|
||||
6
React/real-estate/src/reducers/index.js
Normal file
6
React/real-estate/src/reducers/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { combineReducers } from 'redux';
|
||||
import authReducer from './authReducer';
|
||||
|
||||
export default combineReducers({
|
||||
auth: authReducer
|
||||
})
|
||||
17
React/real-estate/src/services/apiService.js
Normal file
17
React/real-estate/src/services/apiService.js
Normal 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
|
||||
}
|
||||
46
React/real-estate/src/services/userService.js
Normal file
46
React/real-estate/src/services/userService.js
Normal 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
|
||||
13
React/real-estate/src/store.js
Normal file
13
React/real-estate/src/store.js
Normal 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;
|
||||
54
React/real-estate/src/utils/authProvider.js
Normal file
54
React/real-estate/src/utils/authProvider.js
Normal 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)
|
||||
)
|
||||
}
|
||||
5
React/real-estate/src/utils/axiosHeaders.js
Normal file
5
React/real-estate/src/utils/axiosHeaders.js
Normal file
@ -0,0 +1,5 @@
|
||||
import axios from 'axios'
|
||||
|
||||
export function setAuthHeader(token) {
|
||||
axios.defaults.headers.common['Authorization'] = token ? 'Bearer ' + token : ''
|
||||
}
|
||||
13
React/real-estate/src/utils/protectedRoute.js
Normal file
13
React/real-estate/src/utils/protectedRoute.js
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user