Skip to content
Snippets Groups Projects
Commit ed27828b authored by Tonge, Marcus A (UG - Comp Sci & Elec Eng)'s avatar Tonge, Marcus A (UG - Comp Sci & Elec Eng)
Browse files

Admin dashboard

parent 71719c84
No related branches found
No related tags found
No related merge requests found
...@@ -3,6 +3,7 @@ import React from "react"; ...@@ -3,6 +3,7 @@ import React from "react";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom"; import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";
import LoginPage from "../../pages/auth/login/LoginPage"; import LoginPage from "../../pages/auth/login/LoginPage";
import HomePage from "../../pages/home/HomePage"; import HomePage from "../../pages/home/HomePage";
import AdminPage from "../../pages/admin/admin";
import BookingsPage from "../../pages/bookings/bookings"; import BookingsPage from "../../pages/bookings/bookings";
import NotificationsPage from "../../pages/notifications/notifications"; import NotificationsPage from "../../pages/notifications/notifications";
import ParkingAreasPage from "../../pages/parking_areas/parking_areas"; import ParkingAreasPage from "../../pages/parking_areas/parking_areas";
...@@ -61,6 +62,13 @@ const MainRoutes = [ ...@@ -61,6 +62,13 @@ const MainRoutes = [
component: ParkingAreasPage, component: ParkingAreasPage,
protected: true protected: true
}, },
{
name: "Admin",
path: "/admin",
exact: true,
component: < AdminPage />,
protected: false
},
]; ];
......
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import {
Box,
Button,
Container,
Heading,
VStack,
Text,
useDisclosure,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
ModalFooter,
FormControl,
FormLabel,
Input,
NumberInput,
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper,
useToast,
} from "@chakra-ui/react";
import { CloseIcon } from "@chakra-ui/icons";
const AdminPage = () => {
const token = localStorage.getItem('token');
const navigate = useNavigate();
const [isLoading, setLoading] = useState(true);
const isAdmin = async () => {
const response = await fetch(`${baseUrl}/admin`);
setLoading(false);
if (!response.ok) {
navigate('/');
}
}
const baseUrl = (process.env.REACT_APP_AUTH_SERVICE_ENDPOINT);
useEffect(() => {
isAdmin();
}, []);
return isLoading ? <Text>Loading</Text> : <AdminPageView />
}
const AdminPageView = () => {
const toast = useToast();
const navigate = useNavigate();
const [locations, setLocations] = useState([]);
const { isOpen, onOpen, onClose } = useDisclosure();
const [newLocation, setNewLocation] = useState({
title: "",
address: "",
latitude: 0,
longitude: 0,
spaces: 0,
});
// Load locations when the page is loaded
useEffect(() => {
loadLocations();
}, []);
// Fetch locations from the server
const loadLocations = async () => {
// const response = await fetch("/api/locations");
// const data = await response.json();
const data = [
{
_id: 1,
title: "sd",
address: "sd",
latitude: 0,
longitude: 0,
spaces: 0,
},
{
_id: 2,
title: "sd",
address: "sd",
latitude: 0,
longitude: 0,
spaces: 0,
}
];
setLocations(data);
};
// Add a new location
const addLocation = async (location) => {
const { title, address, latitude, longitude, spaces } = location;
console.log(location);
try {
const response = await fetch("/api/locations", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(newLocation),
});
if (!response.ok) {
throw new Error("Failed to add location");
}
setNewLocation({
title: title,
address: address,
latitude: latitude,
longitude: longitude,
spaces: spaces,
});
toast({
title: "Added new location.",
status: "success",
duration: 2000,
isClosable: true,
});
} catch (error) {
toast({
title: error.message ?? "Failed to add location.",
status: "error",
duration: 2000,
isClosable: true,
});
}
loadLocations();
onClose();
};
// Delete a location
const deleteLocation = async (id) => {
try {
const response = await fetch(`/api/locations/${id}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
if (!response.ok) {
throw new Error("Failed to delete location");
}
setLocations((prevLocations) =>
prevLocations.filter(
(location) => location._id !== id
)
);
toast({
title: "Location deleted.",
status: "success",
duration: 2000,
isClosable: true,
});
} catch (error) {
toast({
title: "Failed to delete location.",
status: "error",
duration: 2000,
isClosable: true,
});
}
loadLocations();
};
return (
<Box>
<Box bg="gray.100" py="4" px="2" textAlign="center">
<Heading as="h1" size="xl">
Admin Panel
</Heading>
<Button onClick={() => {
localStorage.removeItem('token');
navigate("/");
}}>
Sign Out
</Button>
</Box>
<br />
<Container p={4} align={"center"} borderWidth={1} >
<VStack spacing="6">
<Box w="100%">
<Text fontSize="2xl" fontWeight="semibold">
Location Management
</Text>
<Button colorScheme="blue" size="sm" onClick={onOpen}>
Add New Location
</Button>
</Box>
<Box minW={"50vw"}>
{locations.map((location) => (
<Box
key={`parent${location._id}`}
py={1}>
<Box
key={location._id}
p={4}
display={"flex"}
flexDir={"row"}
justifyContent={"space-between"}
borderWidth={1}
borderRadius={4}
>
<div>
<Text mr={4} fontSize={18}>
<strong></strong>
<strong>Title:</strong> {location.title}
</Text>
<Text flex="1">
<strong>Address:</strong> {location.address}
</Text>
<Text flex="1">
<strong>Longitude:</strong> {location.longitude}
</Text>
<Text flex="1">
<strong>Latitude:</strong> {location.latitude}
</Text>
<Text flex="1">
<strong>Spaces:</strong> {location.spaces}
</Text>
</div>
<Button
key={`delete:${location._id}`}
rightIcon={<CloseIcon />}
aria-label="Delete location"
onClick={() => deleteLocation(location._id)}
>
Delete
</Button>
</Box>
</Box>
))}
</Box>
</VStack>
</Container>
<Modal
isOpen={isOpen}
onClose={onClose}
size="md"
closeOnOverlayClick={false}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Add Location / Parking Area</ModalHeader>
<ModalCloseButton />
<ModalBody>
<VStack spacing="4">
<FormControl id="title">
<FormLabel>Title</FormLabel>
<Input
type="text"
value={newLocation.title}
onChange={(e) =>
setNewLocation({ ...newLocation, title: e.target.value })
}
/>
</FormControl>
<FormControl id="address">
<FormLabel>Address</FormLabel>
<Input
type="text"
value={newLocation.address}
onChange={(e) =>
setNewLocation({ ...newLocation, address: e.target.value })
}
/>
</FormControl>
<FormControl id="latitude">
<FormLabel>Latitude</FormLabel>
<Input
type="number"
step="0.000001"
value={newLocation.latitude}
onChange={(e) =>
setNewLocation({
...newLocation,
latitude: parseFloat(e.target.value),
})
}
/>
</FormControl>
<FormControl id="longitude">
<FormLabel>Longitude</FormLabel>
<Input
type="number"
step="0.000001"
value={newLocation.longitude}
onChange={(e) =>
setNewLocation({
...newLocation,
longitude: parseFloat(e.target.value),
})
}
/>
</FormControl>
<FormControl id="spaces">
<FormLabel>Spaces</FormLabel>
<NumberInput
value={newLocation.spaces}
onChange={(value) => {
const intValue = parseInt(value);
if (!isNaN(intValue) && intValue > 0) {
setNewLocation({
...newLocation,
spaces: intValue,
});
}
}
}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</FormControl>
</VStack>
</ModalBody>
<ModalFooter>
<Button variant="ghost" mr="2" onClick={onClose}>
Cancel
</Button>
<Button colorScheme="blue" onClick={() => addLocation(newLocation)}>
Add Location
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</Box>
);
};
export default AdminPage;
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment