diff --git a/package.json b/package.json
index f64cf39..219ad38 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.1.3",
+ "mapbox-gl": "^2.11.0",
"jwt-decode": "^3.1.2",
"moment": "^2.29.4",
"react": "^18.2.0",
diff --git a/src/App.js b/src/App.js
index 89571cf..0dfb39e 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,10 +1,21 @@
import {
- BrowserRouter as Router, Navigate, Route, Routes
+ BrowserRouter as Router,
+ Navigate,
+ Route,
+ Routes,
} from "react-router-dom";
import "./App.css";
import { RequireAuth } from "./components";
+import {
+ Client,
+ Dashboard,
+ HostAdmin,
+ Login,
+ ManagerAdmin,
+ Signup,
+ ManagerRoom,
+} from "./pages";
import RoleBasedGuard from "./guards/RoleBasedGuard";
-import { Client, Dashboard, HostAdmin, Login, ManagerAdmin, Signup } from "./pages";
import { SYSTEM_ADMIN, HOST, CLIENT } from "./constants/index";
function App() {
@@ -15,31 +26,33 @@ function App() {
} />
} />
} />
+ } />
+
+
+
+ }
+ >
-
-
- }>
-
-
-
-
-
- }>
-
-
-
-
-
- }>
-
+
+
+
+ }
+ >
+
+
+
+ }
+ >
);
diff --git a/src/components/card/card.jsx b/src/components/card/card.jsx
new file mode 100644
index 0000000..47c8219
--- /dev/null
+++ b/src/components/card/card.jsx
@@ -0,0 +1,41 @@
+import * as React from "react";
+import Box from "@mui/material/Box";
+import Card from "@mui/material/Card";
+import CardActions from "@mui/material/CardActions";
+import CardContent from "@mui/material/CardContent";
+import Button from "@mui/material/Button";
+import Typography from "@mui/material/Typography";
+
+const bull = (
+
+ •
+
+);
+
+export default function BasicCard(props) {
+ return (
+
+
+
+ {props.inTitle}
+
+
+ {props.title}
+
+
+ {props.underTitle}
+
+
+ {props.content}
+
a benevolent smile
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/snackbars/index.jsx b/src/components/snackbars/index.jsx
new file mode 100644
index 0000000..152b23f
--- /dev/null
+++ b/src/components/snackbars/index.jsx
@@ -0,0 +1,26 @@
+import * as React from "react";
+import Button from "@mui/material/Button";
+import Snackbar from "@mui/material/Snackbar";
+import IconButton from "@mui/material/IconButton";
+import CloseIcon from "@mui/icons-material/Close";
+
+export default function SimpleSnackbar(props) {
+ const [open, setOpen] = React.useState(false);
+ React.useEffect(() => {
+ setOpen(props.open);
+ }, [props]);
+ const handleClose = () => {
+ setOpen(false);
+ };
+
+ return (
+
+
+
+ );
+}
diff --git a/src/pages/index.js b/src/pages/index.js
index dddba0e..0291beb 100644
--- a/src/pages/index.js
+++ b/src/pages/index.js
@@ -1,6 +1,7 @@
export { default as Login } from "./login";
export { default as Signup } from "./Signup";
export { default as HostAdmin } from "./HostAdmin";
+export { default as ManagerRoom } from "./manager_room";
export { default as ManagerAdmin } from "./ManagerAdmin";
export { default as Dashboard } from "./Dashboard";
export { default as Client } from "./Client";
diff --git a/src/pages/manager_room/create.jsx b/src/pages/manager_room/create.jsx
new file mode 100644
index 0000000..06ca354
--- /dev/null
+++ b/src/pages/manager_room/create.jsx
@@ -0,0 +1,214 @@
+import * as React from "react";
+import Box from "@mui/material/Box";
+import Modal from "@mui/material/Modal";
+import Button from "@mui/material/Button";
+import TextField from "@mui/material/TextField";
+import MenuItem from "@mui/material/MenuItem";
+import { roomAPI } from "../../services/room.api";
+import SimpleSnackbar from "../../components/snackbars";
+const currencies = [
+ {
+ value: "available",
+ label: "Available",
+ },
+ {
+ value: "full",
+ label: "Full",
+ },
+ {
+ value: "pending",
+ label: "Pending",
+ },
+ {
+ value: "maintain",
+ label: "Maintain",
+ },
+];
+const style = {
+ position: "absolute",
+ top: "50%",
+ left: "50%",
+ transform: "translate(-50%, -50%)",
+ width: 400,
+ bgcolor: "background.paper",
+ border: "2px solid #000",
+ boxShadow: 24,
+ pt: 2,
+ px: 4,
+ pb: 3,
+};
+
+export default function CreateModal(props) {
+ const [open, setOpen] = React.useState(false);
+ const [snackOpen, setsnackOpen] = React.useState(false);
+ const [masage, setmasage] = React.useState("");
+ const [name, setname] = React.useState("");
+ const [address, setaddress] = React.useState("");
+ const [capacity, setcapacity] = React.useState(0);
+ const [price, setprice] = React.useState(0);
+ const [lng, setlng] = React.useState(0);
+ const [lat, setlat] = React.useState(0);
+ const [status, setstatus] = React.useState("false");
+ const [description, setdescription] = React.useState("");
+ const [image, setimage] = React.useState("");
+ const [rating, setrating] = React.useState(0);
+ React.useEffect(() => {
+ setOpen(props.show);
+ }, [props]);
+ const handleClose = () => {
+ setOpen(false);
+ };
+ const CreateRoom = async () => {
+ if (
+ name !== "" &&
+ address !== "" &&
+ lng &&
+ lat &&
+ status !== "" &&
+ image !== "" &&
+ rating
+ ) {
+ const newRoom = await roomAPI.create({
+ name,
+ address,
+ capacity,
+ price,
+ status,
+ description,
+ image,
+ rating,
+ location: { lng, lat },
+ is_active: true,
+ });
+ setsnackOpen(true);
+ if (newRoom ? true : false) {
+ setmasage(" Create success");
+ setOpen(!open);
+ window.location.reload(false);
+ } else {
+ setmasage("create fails");
+ }
+ } else {
+ setsnackOpen(true);
+ setmasage(" Check data again");
+ }
+ };
+ return (
+
+
+
+ Create Room
+ {
+ setname(e.target.value);
+ }}
+ variant="outlined"
+ />
+ {
+ setaddress(e.target.value);
+ }}
+ variant="outlined"
+ />
+ {
+ setcapacity(parseInt(e.target.value));
+ }}
+ variant="outlined"
+ />
+ {
+ setprice(parseInt(e.target.value));
+ }}
+ variant="outlined"
+ />
+ {
+ setlng(parseInt(e.target.value));
+ }}
+ style={{ width: "50%", marginTop: "10px" }}
+ label="Longtitude"
+ variant="outlined"
+ />
+ {
+ setlat(parseInt(e.target.value));
+ }}
+ style={{ width: "50%", marginTop: "10px" }}
+ label="Latitude"
+ variant="outlined"
+ />
+ {
+ setdescription(e.target.value);
+ }}
+ label="description"
+ variant="outlined"
+ />
+ {
+ setimage(e.target.value);
+ }}
+ variant="outlined"
+ />
+ {
+ setrating(parseInt(e.target.value));
+ }}
+ style={{ width: "100%", marginTop: "10px" }}
+ label="rating"
+ variant="outlined"
+ />
+ {
+ setstatus(e.target.value);
+ }}
+ style={{ width: "100%", marginTop: "10px" }}
+ label="Status"
+ variant="outlined"
+ >
+ {currencies.map((option) => (
+
+ ))}
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/manager_room/edit.jsx b/src/pages/manager_room/edit.jsx
new file mode 100644
index 0000000..a321af6
--- /dev/null
+++ b/src/pages/manager_room/edit.jsx
@@ -0,0 +1,259 @@
+import * as React from "react";
+import Box from "@mui/material/Box";
+import Modal from "@mui/material/Modal";
+import Button from "@mui/material/Button";
+import TextField from "@mui/material/TextField";
+import MenuItem from "@mui/material/MenuItem";
+import { roomAPI } from "../../services/room.api";
+import SimpleSnackbar from "../../components/snackbars";
+import mapboxgl from "mapbox-gl";
+mapboxgl.accessToken =
+ "pk.eyJ1IjoiY3NpZGUiLCJhIjoiY2xhYTBrMXIwMDF4bzNwcDExMDdmNG10ZCJ9.dJ8utYc12qEivXTKawbZxg";
+const currencies = [
+ {
+ value: "available",
+ label: "Available",
+ },
+ {
+ value: "full",
+ label: "Full",
+ },
+ {
+ value: "pending",
+ label: "Pending",
+ },
+ {
+ value: "maintain",
+ label: "Maintain",
+ },
+];
+const style = {
+ position: "absolute",
+ top: "50%",
+ left: "50%",
+ transform: "translate(-50%, -50%)",
+ width: 400,
+ bgcolor: "background.paper",
+ border: "2px solid #000",
+ boxShadow: 24,
+ pt: 2,
+ px: 4,
+ pb: 3,
+};
+
+export default function EditModal(props) {
+ const [open, setOpen] = React.useState(false);
+ const [snackOpen, setsnackOpen] = React.useState(false);
+ const [masage, setmasage] = React.useState("");
+ const [name, setname] = React.useState("");
+ const [address, setaddress] = React.useState("");
+ const [capacity, setcapacity] = React.useState(0);
+ const [price, setprice] = React.useState(0);
+ const [lng, setlng] = React.useState(-70.9);
+ const [lat, setlat] = React.useState(42.35);
+ const [status, setstatus] = React.useState("false");
+ const [description, setdescription] = React.useState("");
+ const [image, setimage] = React.useState("");
+ const [rating, setrating] = React.useState(0);
+ const mapContainer = React.useRef(null);
+ const map = React.useRef(null);
+ const [zoom, setZoom] = React.useState(9);
+ React.useEffect(() => {
+ setOpen(props.show);
+ setname(props?.data?.name);
+ setaddress(props?.data?.address);
+ setcapacity(props?.data?.capacity);
+ setprice(props?.data?.price);
+ if (props.data?.location) {
+ setlng(parseInt(props.data?.location?.y));
+ setlat(parseInt(props?.data?.location?.x));
+ }
+ setstatus(props?.data?.status);
+ setdescription(props?.data?.description);
+ setimage(props?.data?.image);
+ setrating(props?.data?.rating);
+ map.current = new mapboxgl.Map({
+ container: mapContainer.current,
+ style: "mapbox://styles/mapbox/streets-v11",
+ center: [lng, lat],
+ zoom: zoom,
+ });
+ }, [props, props?.data]);
+
+ const handleClose = () => {
+ setOpen(false);
+ };
+ const EditRoom = async () => {
+ if (
+ name !== "" &&
+ address !== "" &&
+ lng &&
+ lat &&
+ status !== "" &&
+ image !== "" &&
+ rating &&
+ props?.data?.roomId
+ ) {
+ const newRoom = await roomAPI.edit(props?.data?.roomId, {
+ name,
+ address,
+ capacity,
+ price,
+ status,
+ description,
+ image,
+ rating,
+ location: { lng, lat },
+ is_active: true,
+ });
+ setsnackOpen(true);
+ if (newRoom ? true : false) {
+ setmasage(" Create success");
+ setOpen(!open);
+ window.location.reload(false);
+ } else {
+ setmasage("create fails");
+ }
+ } else {
+ setsnackOpen(true);
+ setmasage(" Check data again");
+ }
+ };
+ return (
+
+
+
+ Edit Room
+ {
+ setname(e.target.value);
+ }}
+ variant="outlined"
+ />
+ {
+ setaddress(e.target.value);
+ }}
+ variant="outlined"
+ />
+ {
+ setcapacity(parseInt(e.target.value));
+ }}
+ variant="outlined"
+ />
+ {
+ setprice(parseInt(e.target.value));
+ }}
+ variant="outlined"
+ />
+
+
+ {
+ setdescription(e.target.value);
+ }}
+ label="description"
+ variant="outlined"
+ />
+ {
+ setimage(e.target.value);
+ }}
+ variant="outlined"
+ />
+ {
+ setrating(parseInt(e.target.value));
+ }}
+ style={{ width: "100%", marginTop: "10px" }}
+ label="rating"
+ variant="outlined"
+ />
+ {
+ setstatus(e.target.value);
+ }}
+ style={{ width: "100%", marginTop: "10px" }}
+ label="Status"
+ variant="outlined"
+ >
+ {currencies.map((option) => (
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/manager_room/index.jsx b/src/pages/manager_room/index.jsx
new file mode 100644
index 0000000..01f8495
--- /dev/null
+++ b/src/pages/manager_room/index.jsx
@@ -0,0 +1,53 @@
+import { Button } from "@mui/material";
+import React from "react";
+import CreateModal from "./create";
+import ListRoom from "./list";
+import EditModal from "./edit";
+import { roomAPI } from "../../services/room.api";
+import { useNavigate, Navigate } from "react-router-dom";
+export default function ManagerRoom() {
+ const [modelCreateRoom, setModelCreateRoom] = React.useState(false);
+ const [modelEditRoom, setModelEditRoom] = React.useState(false);
+ const [rooms, setRooms] = React.useState([]);
+ const [room, setRoom] = React.useState([]);
+ React.useEffect(() => {
+ getRooms();
+ }, []);
+ const getRooms = async () => {
+ const room = await roomAPI.getAll(25, 0);
+ setRooms(room.data);
+ };
+ const getRoom = async (e) => {
+ const room = await roomAPI.getDetail(e.target.id);
+ setRoom(room?.data?.response);
+ setModelEditRoom(!modelEditRoom);
+ };
+ return (
+
+ List Room
+
+
+
Total:{rooms.count}
+
+ {
+ setModelCreateRoom(!modelCreateRoom);
+ }}
+ />
+ {
+ setModelEditRoom(!modelEditRoom);
+ }}
+ />
+
+ );
+}
diff --git a/src/pages/manager_room/list.jsx b/src/pages/manager_room/list.jsx
new file mode 100644
index 0000000..cb72d4a
--- /dev/null
+++ b/src/pages/manager_room/list.jsx
@@ -0,0 +1,40 @@
+import React from "react";
+import BasicCard from "../../components/card/card";
+import { roomAPI } from "../../services/room.api";
+import { Button } from "@mui/material";
+import SimpleSnackbar from "../../components/snackbars";
+export default function ListRoom(props) {
+ const [snackOpen, setsnackOpen] = React.useState(false);
+ const [masage, setmasage] = React.useState("");
+ const remove = async (roomId) => {
+ const isDelete = await roomAPI.Remove(roomId);
+ window.location.reload(false);
+ };
+ return (
+
+ {props?.data?.map((room, index) => (
+
+
+
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/src/services/http.js b/src/services/http.js
index b815ad7..3d72133 100644
--- a/src/services/http.js
+++ b/src/services/http.js
@@ -13,6 +13,8 @@ export const http = axios.create({
timeout: 10000,
headers: {
"Content-Type": "application/json",
+ Authorization:
+ "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkbWluMUBnbWFpbC5jb20iLCJpYXQiOjE2NjkzNDMwMDIsImV4cCI6MTY3MTkzNTAwMn0.C4_sUQgsUb7eQtVTOqRPiavuJkPbv15c3L_HcrTJW3c",
},
});
@@ -25,7 +27,7 @@ http.interceptors.request.use(
return config;
}
if (accessToken) {
- config.headers["Authorization"] = `Bearer ${accessToken}`;
+ // config.headers["Authorization"] = `Bearer ${accessToken}`;
}
return config;
},
@@ -51,8 +53,8 @@ http.interceptors.response.use(
refreshToken = refreshToken
? refreshToken
: authAPI.refreshToken().finally(() => {
- refreshToken = null;
- });
+ refreshToken = null;
+ });
return refreshToken
.then((access_token) => {
error.response.config.Authorization = access_token;
diff --git a/src/services/room.api.js b/src/services/room.api.js
new file mode 100644
index 0000000..8dee44d
--- /dev/null
+++ b/src/services/room.api.js
@@ -0,0 +1,19 @@
+import { http } from "./http";
+
+export const roomAPI = {
+ create: (data) => {
+ return http.post("rooms/create", data);
+ },
+ edit: (room_id, data) => {
+ return http.put(`rooms/${room_id}`, data);
+ },
+ getAll: (pageSize, pageNumber) => {
+ return http.get(`rooms?pageNumber=${pageNumber}&pageSize=${pageSize}`);
+ },
+ getDetail: (room_id) => {
+ return http.get(`rooms/${room_id}`);
+ },
+ Remove: (room_id) => {
+ return http.delete(`rooms/${room_id}`);
+ },
+};