Переглянути джерело

Separated router from the rest of the application. Added ParcelsListComponent and NewParcelComponent. Secured whole application with Keycloak.

wpfat23-5 2 роки тому
батько
коміт
5caca2283d

+ 20 - 0
src/MainRouter.js

@@ -0,0 +1,20 @@
+import {BrowserRouter, Route, Routes} from "react-router-dom";
+import LoginPage from "./pages/LoginPage";
+import HomePage from "./pages/HomePage";
+import React, {useEffect, useState} from "react";
+
+export default function (props) {
+
+
+
+
+    return (<BrowserRouter>
+        <Routes>
+            <Route path="/" element={<LoginPage />}/>
+            <Route path="/home" element={<HomePage />}/>
+            {/*<Route path="blogs" element={<Blogs />} />*/}
+            {/*<Route path="contact" element={<Contact />} />*/}
+            {/*<Route path="*" element={<NoPage />} />*/}
+        </Routes>
+    </BrowserRouter>)
+}

+ 20 - 0
src/components/NewParcelComponent.js

@@ -0,0 +1,20 @@
+import React from "react";
+import {InputText} from "primereact/inputtext";
+import {Button} from "primereact/button";
+
+export default function (props) {
+
+    return (
+        <div className="w-11 my-2 p-2 surface-200 border-round shadow-3 flex flex-column">
+            <div>
+                <div>Parcel name:</div>
+                <InputText type="text" className="w-full p-inputtext-sm surface-300 "/>
+            </div>
+            <div>
+                <div>Address:</div>
+                <InputText type="text" className="w-full p-inputtext-sm surface-300 "/>
+            </div>
+            <Button className="align-self-end mt-2"><i className="pi pi-check text-green-500 text-xl font-bold"></i> Add</Button>
+        </div>
+    )
+}

+ 27 - 0
src/components/ParcelsListComponent.js

@@ -0,0 +1,27 @@
+import {ScrollPanel} from "primereact/scrollpanel";
+import React from "react";
+import {Button} from "primereact/button";
+
+export default function (props) {
+
+    return (
+        <ScrollPanel className="w-11 my-2 surface-200 max-h-10rem border-round shadow-3 ">
+            {props.parcels.map((parcel, index) =>
+                <div key={index} className="flex flex-row">
+                    <div className="m-1 p-1 w-full surface-300 border-round flex flex-column">
+                        <div className="font-bold">
+                            {parcel.name}
+                        </div>
+                        <div className="flex flex-row">
+                            <div>{parcel.address}</div>
+                        </div>
+                    </div>
+                    <Button
+                        className="text-900 bg-red-200 border-1 border-black-alpha-10 border-round p-1 m-1 flex flex-column align-items-center justify-content-center">
+                        <i className="pi pi-trash"></i>
+                    </Button>
+                </div>
+            )}
+        </ScrollPanel>
+    )
+}

+ 0 - 0
src/css/InputText.css


+ 3 - 13
src/index.js

@@ -10,24 +10,14 @@ import "primereact/resources/primereact.min.css";
 
 //icons
 import "primeicons/primeicons.css";
-import {BrowserRouter, Route, Routes} from "react-router-dom";
-import LoginPage from "./pages/LoginPage";
-import HomePage from "./pages/HomePage";
 import 'primeflex/primeflex.css'
+import MainRouter from "./MainRouter";
+
 
 const root = ReactDOM.createRoot(document.getElementById('root'));
 root.render(
     <React.StrictMode>
-        <BrowserRouter>
-            <Routes>
-                <Route path="/" element={<LoginPage />} />
-                <Route path="/home" element={<HomePage />} />
-                    {/*<Route path="blogs" element={<Blogs />} />*/}
-                    {/*<Route path="contact" element={<Contact />} />*/}
-                    {/*<Route path="*" element={<NoPage />} />*/}
-
-            </Routes>
-        </BrowserRouter>
+        <MainRouter/>
     </React.StrictMode>
 );
 

+ 63 - 11
src/pages/HomePage.js

@@ -5,22 +5,63 @@ import React, {useEffect, useState} from "react";
 import {SPRING_SERVER} from "../config";
 import {Button} from "primereact/button";
 import PathComponent from "../components/PathComponent";
+import {ScrollPanel} from "primereact/scrollpanel";
+import {Divider} from "primereact/divider";
+import {Accordion, AccordionTab} from "primereact/accordion";
+import ParcelsListComponent from "../components/ParcelsListComponent";
+import NewParcelComponent from "../components/NewParcelComponent";
+import MarkersComponent from "../components/MarkersComponent";
+import {Navigate, useNavigate} from "react-router-dom";
 
 export default function () {
+    const navigate = useNavigate();
 
     const [positions, setPositions] = useState([[51.746732, 19.450587], [51.744347, 19.451015]]);
+    /*
+        const [authenticated, setAuthenticated] = useState(false)
+    */
+
+    if (!localStorage.getItem("access_token") || !localStorage.getItem("expires_at"))
+    {
+
+        localStorage.removeItem("access_token")
+        localStorage.removeItem("expires_at")
+        return <Navigate to="/" replace/>
+    }else{
+        if (parseInt(localStorage.getItem("expires_at")) < Date.now()){
+            localStorage.removeItem("access_token")
+            localStorage.removeItem("expires_at")
+
+            return <Navigate to="/" replace/>
+        }
+    }
+
+    /*    useEffect(() => {
+            if (localStorage.getItem("access_token") && localStorage.getItem("expires_at") &&
+                localStorage.getItem("expires_at") < Date.now()) {
+                setAuthenticated(true)
+            } else {
+                setAuthenticated(false)
+            }
+        })*/
 
     useEffect(() => {
+
         const params = new URLSearchParams({
-            lon1: 19.45056,
-            lat1: 51.74673,
+            lon1: 19.3037,
+            lat1: 51.8194,
             lon2: 19.46211,
             lat2: 51.74275
         });
 
         const url = SPRING_SERVER + '/route/nodes?' + params;
 
-        fetch(url)
+        fetch(url, {
+            headers: {
+                "Authorization": "Bearer " + localStorage.getItem("access_token"),
+                // 'Content-Type': 'application/x-www-form-urlencoded',
+            }
+        })
             .then(response => response.json())
             .then(data => {
                 setPositions(data)
@@ -29,21 +70,31 @@ export default function () {
     }, []);
 
     const onButtonClick = () => {
-        console.log(positions)
+        localStorage.removeItem("access_token")
+        return navigate("/")
     };
 
+
+    /*if (authenticated === true)*/
     return (
         < >
-            <div className="flex flex-row h-screen">
-                <div className="w-5 bg-orange-500 flex flex-column">
+            <div className="flex flex-row h-screen text-900 ">
+                <div className="w-5 bg-orange-500 flex flex-column align-items-center">
 
-                    <div className="flex flex-row align-self-center mt-3">
-                        <Avatar className="align-self-center" icon="pi pi-user" size="xlarge" shape="circle"
+                    <div className="flex flex-row align-self-center mt-3 surface-200 w-11 border-round p-2 shadow-5">
+                        <Avatar className="align-self-center text-700" icon="pi pi-user" size="xlarge" shape="circle"
                                 style={{fontSize: '2.5rem'}}/>
-                        <div className="font-medium text-500 pl-2 align-self-center">USERNAME</div>
-                        <Button onClick={onButtonClick}></Button>
-
+                        <div className="font-bold text-2xl pl-3 align-self-center">John Doe</div>
                     </div>
+
+                    <Button className="col-11 mt-2" onClick={onButtonClick}><i className="pi pi-sign-out"
+                                                                               style={{fontSize: '1rem'}}></i></Button>
+                    <ParcelsListComponent parcels={[
+                        {'id': 1, 'name': 'package2241', 'address': 'New York 232-421'},
+                        {'id': 2, 'name': 'package12437', 'address': 'New York 232-421'},
+                        {'id': 2, 'name': 'package12437', 'address': 'New York 232-421'},
+                        {'id': 2, 'name': 'package12437', 'address': 'New York 232-421'}]}></ParcelsListComponent>
+                    <NewParcelComponent></NewParcelComponent>
                 </div>
                 <div className="w-full bg-orange-500">
 
@@ -54,6 +105,7 @@ export default function () {
                             url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                         />
                         <PathComponent positions={positions}></PathComponent>
+                        <MarkersComponent positions={positions}></MarkersComponent>
                     </MapContainer>
                 </div>
             </div>

+ 41 - 5
src/pages/LoginPage.js

@@ -1,13 +1,47 @@
 import {Button} from "primereact/button";
 import {InputText} from "primereact/inputtext";
-import {useNavigate} from "react-router-dom";
+import {Navigate, useNavigate} from "react-router-dom";
+import React, {useState} from "react";
 
 export default function () {
     const navigate = useNavigate();
+    const [login, setLogin] = useState("");
+    const [password, setPassword] = useState("");
+
+    if (localStorage.getItem("access_token")) {
+        return <Navigate to="/home" replace/>
+    }
+
 
     function onLoginClick() {
+        fetch('http://localhost:8181/auth/realms/SpringBootKeycloak/protocol/openid-connect/token', {
+            method: 'POST',
+            headers: {
+                'Content-Type': 'application/x-www-form-urlencoded'
+            },
+            body: new URLSearchParams({
+                'username': login,
+                'password': password,
+                'grant_type': 'password',
+                'client_id': 'login-app'
+            })
+        }).then(response => {
+            if (!response.ok) {
+                throw new Error(response.status)
+            } else {
+                return response.json()
+            }
+        })
+            .then((data) => {
+                localStorage.setItem("access_token", data.access_token)
+                localStorage.setItem("expires_at", (parseInt(data.expires_in) * 1000 + Date.now()).toString())
+            })
+            .then(() => {
+                if (localStorage.getItem("access_token")) return navigate("/home")
+            })
+            .catch((e) => console.log(e))
+
 
-        return navigate("/home")
     }
 
     return (
@@ -19,11 +53,13 @@ export default function () {
 
                 <span className="p-input-icon-left py-1 ">
                         <i className="pi pi-user"/>
-                        <InputText placeholder="Username" className="min-w-full max-w-full"/>
+                        <InputText placeholder="Username" onChange={(e) => setLogin(e.target.value)}
+                                   className="min-w-full max-w-full"/>
                 </span>
-                <span className="p-input-icon-left py-1" >
+                <span className="p-input-icon-left py-1">
                         <i className="pi pi-key"/>
-                        <InputText type="password" placeholder="Password" className="min-w-full max-w-full"/>
+                        <InputText type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)}
+                                   className="min-w-full max-w-full"/>
                 </span>
                 <div className="flex justify-content-end pt-2">
                     <Button onClick={onLoginClick}>Login</Button>