working on view location details page
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, {useState, useEffect } from 'react';
|
||||
import { useAtom } from 'jotai';
|
||||
import HomePage from './components/pages/HomePage';
|
||||
import Locations from './components/pages/Locations';
|
||||
import LocationsPage from './components/pages/LocationsPage';
|
||||
import RoomsPage from './components/pages/RoomsPage';
|
||||
import LocationForm from './components/forms/LocationForm'
|
||||
import { Routes, Route, useNavigate } from "react-router-dom";
|
||||
@@ -16,6 +16,7 @@ import { serverConfigAtom } from './state/main'
|
||||
|
||||
import SideBar from './components/SideBar';
|
||||
import AppHeader from './components/AppHeader';
|
||||
import LocationDetailsPage from './components/pages/LocationDetailsPage';
|
||||
|
||||
|
||||
|
||||
@@ -70,10 +71,11 @@ function App() {
|
||||
header={<AppHeader />}
|
||||
>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="locations" element={<Locations />} />
|
||||
<Route path="rooms" element={<RoomsPage />} />
|
||||
<Route path="locations/new" element={<LocationForm />} />
|
||||
<Route exact path="/" element={<HomePage />} />
|
||||
<Route exact path="locations" element={<LocationsPage />} />
|
||||
<Route exact path="location/:id" element={<LocationDetailsPage />} />
|
||||
<Route exact path="rooms" element={<RoomsPage />} />
|
||||
<Route exact path="locations/new" element={<LocationForm />} />
|
||||
|
||||
</Routes>
|
||||
</AppShell>
|
||||
|
@@ -49,7 +49,7 @@ const useStyles = createStyles((theme) => ({
|
||||
|
||||
|
||||
const sideBarData = [
|
||||
{ label: 'Dashboard', icon: GoDashboard },
|
||||
{ label: 'Dashboard', icon: GoDashboard, link: {label: 'Dashboard', link: '/'}},
|
||||
{
|
||||
label: 'Locations',
|
||||
icon: GoLocation,
|
||||
@@ -99,10 +99,10 @@ function SideBar(props) {
|
||||
<Code sx={{ fontWeight: 700 }}>v3.1.2</Code>
|
||||
</Group>
|
||||
</Navbar.Section> */}
|
||||
|
||||
<Navbar.Section grow className={classes.links} component={ScrollArea}>
|
||||
<div className={classes.linksInner}>{links}</div>
|
||||
</Navbar.Section>
|
||||
|
||||
<Navbar.Section grow className={classes.links} component={ScrollArea}>
|
||||
<div className={classes.linksInner}>{links}</div>
|
||||
</Navbar.Section>
|
||||
|
||||
{/* <Navbar.Section className={classes.footer}>
|
||||
<UserButton
|
||||
@@ -111,7 +111,7 @@ function SideBar(props) {
|
||||
email="anullpointer@yahoo.com"
|
||||
/>
|
||||
</Navbar.Section> */}
|
||||
</Navbar>
|
||||
</Navbar>
|
||||
</MediaQuery>
|
||||
|
||||
</>
|
||||
|
@@ -3,6 +3,7 @@ import { Text, Button, Card, Group, Menu, Image, Badge } from '@mantine/core'
|
||||
import { useAtom } from 'jotai';
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { activePageAtom, roomFilterAtom, serverConfigAtom } from '../../state/main';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +35,7 @@ function LocationCard(props) {
|
||||
return (
|
||||
<Card key={`${idx} - ${location.ID}`} shadow="md" padding="md">
|
||||
<Card.Section>
|
||||
{location.CoverPhoto ? <Image src={`${serverConfig.baseURL}/photos/locations/${location.Name}/${location.CoverPhoto}`}></Image> : <Text>No Photo</Text>}
|
||||
{location.CoverPhoto ? <Image src={`${serverConfig.baseURL}/files/locations/${location.Name}/${location.CoverPhoto}`}></Image> : <Text>No Photo</Text>}
|
||||
<Group position='right' style={{position: 'absolute', top: '5px', right: '5px', backgroundColor: 'grey', borderRadius: '25%'}}>
|
||||
<Menu>
|
||||
<Menu.Label>Edit</Menu.Label>
|
||||
@@ -51,7 +52,7 @@ function LocationCard(props) {
|
||||
</Group>
|
||||
<Text size="sm">{location.Description}</Text>
|
||||
<Group>
|
||||
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Details</Button>
|
||||
<Button component={Link} to={`/location/${location.ID}`} state={{ location: location }}>View Details</Button>
|
||||
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Rooms</Button>
|
||||
</Group>
|
||||
</Card>
|
||||
|
@@ -34,7 +34,7 @@ function RoomCard(props) {
|
||||
return (
|
||||
<Card key={`${idx} - ${room.ID}`} component="a" onClick={(e) => {}} shadow="sm" padding="md">
|
||||
<Card.Section>
|
||||
{room.CoverPhoto ? <Image src={`${serverConfig.baseURL}/photos/locations/${room.CoverPhoto}`}></Image> : <Text>No Photo</Text>}
|
||||
{room.CoverPhoto ? <Image src={`${serverConfig.baseURL}/files/locations/${room.CoverPhoto}`}></Image> : <Text>No Photo</Text>}
|
||||
</Card.Section>
|
||||
<Group position="apart">
|
||||
<Text weight={500}>{room.Name}</Text>
|
||||
|
@@ -1,19 +1,107 @@
|
||||
import React, {useState, useEffect, useContext, createContext} from 'react';
|
||||
import { Text } from '@mantine/core'
|
||||
import { Text, Title, ThemeIcon, Group, Container, Space } from '@mantine/core'
|
||||
import { useAtom } from 'jotai';
|
||||
import { serverConfigAtom } from '../../state/main'
|
||||
import { GoLocation } from 'react-icons/go'
|
||||
import { BsDoorClosed, BsDiagram2 } from 'react-icons/bs'
|
||||
import { useNotifications } from '@mantine/notifications';
|
||||
import { BiCabinet } from 'react-icons/bi'
|
||||
|
||||
import { backendAPI } from '../../services/backend-api';
|
||||
|
||||
|
||||
function HomePage() {
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [serverConfig] = useAtom(serverConfigAtom)
|
||||
const [overview, setOverview] = useState({})
|
||||
|
||||
const notifications = useNotifications();
|
||||
|
||||
useEffect(() => {
|
||||
console.log("LOADING LOCATIONS PAGE!")
|
||||
setIsLoading(true)
|
||||
async function fetchSettings() {
|
||||
backendAPI.get('/overview/all').then(results => {
|
||||
console.log("CONFIG IN LOCATIONS: ", serverConfig)
|
||||
console.log("Overview ", results.data)
|
||||
setOverview(results.data)
|
||||
|
||||
setIsLoading(false)
|
||||
}).catch(err => {
|
||||
notifications.showNotification({
|
||||
title: 'Backend Notice',
|
||||
message: `Failed to fetch statistics from backend! ${err}`,
|
||||
autoClose: false,
|
||||
color: "red",
|
||||
})
|
||||
setIsLoading(false)
|
||||
})
|
||||
|
||||
}
|
||||
fetchSettings();
|
||||
|
||||
|
||||
}, [])
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text>This is the homepage!</Text>
|
||||
<Title order={2}>Your Instance Statistics</Title>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||
<Text>{overview.NumLocations} Location{overview.NumLocations === 1 ? "": "s"}</Text>
|
||||
{overview.hasOwnProperty('LastAddedLocation') && overview.LastAddedLocation !== null &&
|
||||
<>
|
||||
<Text>Last Added Location: </Text>
|
||||
<Text>{overview.LastAddedLocation.Name}</Text>
|
||||
</>
|
||||
}
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><BsDoorClosed /></ThemeIcon>
|
||||
<Text>{overview.NumRooms} Room{overview.NumRooms === 1 ? "": "s"}</Text>
|
||||
{overview.hasOwnProperty('LastAddedRoom') && overview.LastAddedRoom !== null &&
|
||||
<>
|
||||
<Text>Last Added Room: </Text>
|
||||
<Text>{overview.LastAddedRoom.Name}</Text>
|
||||
</>
|
||||
}
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><BiCabinet /></ThemeIcon>
|
||||
<Text>{overview.NumCabinets} Cabinet{overview.NumCabinets === 1 ? "": "s"}</Text>
|
||||
{overview.hasOwnProperty('LastAddedCabinet') && overview.LastAddedCabinet !== null &&
|
||||
<>
|
||||
<Text>Last Added Cabinet: </Text>
|
||||
<Text>{overview.LastAddedCabinet.Name}</Text>
|
||||
</>
|
||||
}
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><BsDiagram2 /></ThemeIcon>
|
||||
<Text>{overview.NumItems} Item{overview.NumItems === 1 ? "": "s"}</Text>
|
||||
{overview.hasOwnProperty('LastAddedItem') && overview.LastAddedItem !== null &&
|
||||
<>
|
||||
<Text>Last Added Item: </Text>
|
||||
<Text>{overview.LastAddedItem.Name}</Text>
|
||||
</>
|
||||
}
|
||||
</Group>
|
||||
</Container>
|
||||
|
||||
</>
|
||||
|
||||
);
|
||||
|
148
frontend/src/components/pages/LocationDetailsPage.js
Normal file
148
frontend/src/components/pages/LocationDetailsPage.js
Normal file
@@ -0,0 +1,148 @@
|
||||
import React, {useState, useEffect } from 'react';
|
||||
import { useAtom } from 'jotai'
|
||||
import { serverConfigAtom } from '../../state/main';
|
||||
import { Loader, Center, Title, Group, Button, Space, Container, ThemeIcon, Text, Image } from '@mantine/core'
|
||||
import { useNavigate, useLocation, useParams } from "react-router-dom";
|
||||
import { useNotifications } from '@mantine/notifications';
|
||||
import { GoLocation } from 'react-icons/go'
|
||||
|
||||
import { backendAPI } from '../../services/backend-api';
|
||||
|
||||
function LocationDetailsPage() {
|
||||
const location = useLocation()
|
||||
|
||||
|
||||
|
||||
// const [opened, setOpened] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [serverConfig] = useAtom(serverConfigAtom)
|
||||
const [locationDetails, setLocationDetails] = useState({})
|
||||
|
||||
let navigate = useNavigate();
|
||||
|
||||
const notifications = useNotifications();
|
||||
|
||||
let routeLocationID = useParams()
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
let locationDetailsProps = {}
|
||||
if (location.state) {
|
||||
if (location.state.hasOwnProperty('location')) {
|
||||
locationDetailsProps = location.state.location
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(locationDetailsProps).length === 0) {
|
||||
console.log("manually fetching location details!")
|
||||
setIsLoading(true)
|
||||
async function fetchSettings() {
|
||||
backendAPI.get(`/locations/${routeLocationID.id}`).then(results => {
|
||||
console.log("CONFIG IN LOCATIONS: ", serverConfig)
|
||||
console.log("LOCATIONS: ", results.data)
|
||||
setLocationDetails(results.data)
|
||||
setIsLoading(false)
|
||||
}).catch(err => {
|
||||
notifications.showNotification({
|
||||
title: 'Backend Notice',
|
||||
message: `Failed to fetch locations from backend! ${err}`,
|
||||
autoClose: false,
|
||||
color: "red",
|
||||
})
|
||||
setIsLoading(false)
|
||||
})
|
||||
|
||||
}
|
||||
fetchSettings();
|
||||
} else {
|
||||
console.log("Using passed state!", locationDetailsProps)
|
||||
setLocationDetails(locationDetailsProps)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}, [])
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Center>{ isLoading && <Loader size="xl" variant="bars" />}</Center>
|
||||
<Center><Title order={1}>{locationDetails.Name}</Title></Center>
|
||||
<Button>Edit Location</Button>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||
<Text>Description: </Text>
|
||||
<Text>{locationDetails.Description}</Text>
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||
<Text>Notes: </Text>
|
||||
<Text>{locationDetails.Notes}</Text>
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||
<Text>Address: </Text>
|
||||
<Text>{locationDetails.Address}</Text>
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||
<Text>Square Feet: </Text>
|
||||
<Text>{locationDetails.SquareFeet}</Text>
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||
<Text>Lat/Long: </Text>
|
||||
<Text>{locationDetails.Latitude}/{locationDetails.Longitude}</Text>
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||
<Text>Date Purchased: </Text>
|
||||
<Text>{locationDetails.DatePurchased}</Text>
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||
<Text>Purchase Price: </Text>
|
||||
<Text>{locationDetails.PurchasePrice}</Text>
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Group>
|
||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||
<Text>Current Value: </Text>
|
||||
<Text>{locationDetails.CurrentValue}</Text>
|
||||
</Group>
|
||||
</Container>
|
||||
<Space h="md" />
|
||||
<Container>
|
||||
<Image src={`${serverConfig.baseURL}/files/locations/${locationDetails.Name}/${locationDetails.CoverPhoto}`} />
|
||||
</Container>
|
||||
</>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default LocationDetailsPage;
|
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Group, Box, Collapse, ThemeIcon, Text, UnstyledButton, createStyles } from '@mantine/core';
|
||||
import { Group, Box, Collapse, ThemeIcon, UnstyledButton, createStyles } from '@mantine/core';
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import { BiChevronLeft, BiChevronRight } from 'react-icons/bi';
|
||||
import { useAtom } from 'jotai';
|
||||
@@ -52,10 +52,11 @@ const useStyles = createStyles((theme) => ({
|
||||
|
||||
|
||||
export function LinksGroup(props) {
|
||||
const { icon: Icon, label, initiallyOpened, links } = props
|
||||
const { icon: Icon, label, initiallyOpened, links, link } = props
|
||||
|
||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||
const hasLinks = Array.isArray(links);
|
||||
const hasLink = (typeof link === "object")
|
||||
const [opened, setOpened] = useState(initiallyOpened || false);
|
||||
// Navigation State
|
||||
const [activePage, setActivePage] = useAtom(activePageAtom)
|
||||
@@ -67,7 +68,7 @@ export function LinksGroup(props) {
|
||||
|
||||
const ChevronIcon = theme.dir === 'ltr' ? BiChevronRight : BiChevronLeft;
|
||||
|
||||
const navigate = (event, location) => {
|
||||
const navigate = (location) => {
|
||||
// event.preventDefault()
|
||||
if (location === "rooms") {
|
||||
setRoomFilter({})
|
||||
@@ -88,36 +89,65 @@ export function LinksGroup(props) {
|
||||
className={activePage === link.label.toLowerCase() ? cx(classes.link, classes.activeLink) : classes.link}
|
||||
to={link.link}
|
||||
key={link.label}
|
||||
onClick={(e) => {navigate(e, link.label.toLowerCase())}}
|
||||
onClick={() => {navigate(link.label.toLowerCase())}}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
</>
|
||||
));
|
||||
|
||||
const buttonContents = () => {
|
||||
return (
|
||||
<Group position="apart" spacing={0}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<ThemeIcon variant="light" size={30}>
|
||||
<Icon size={18} />
|
||||
</ThemeIcon>
|
||||
<Box ml="md">{label}</Box>
|
||||
</Box>
|
||||
{hasLinks && (
|
||||
<ChevronIcon
|
||||
className={classes.chevron}
|
||||
size={14}
|
||||
style={{
|
||||
transform: opened ? `rotate(${theme.dir === 'rtl' ? -90 : 90}deg)` : 'none',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
)
|
||||
}
|
||||
|
||||
const myButton = () => {
|
||||
if (hasLink) {
|
||||
return (
|
||||
<UnstyledButton
|
||||
onClick={hasLinks ? () => setOpened((o) => !o) : () => navigate(link.label.toLowerCase())}
|
||||
className={activePage === link.label.toLowerCase() ? cx(classes.control, classes.activeLink) : classes.control}
|
||||
component={Link}
|
||||
to={link.link}
|
||||
key={link.label}
|
||||
>
|
||||
{buttonContents()}
|
||||
</UnstyledButton>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<UnstyledButton
|
||||
onClick={() => setOpened((o) => !o)}
|
||||
className={classes.control}
|
||||
>
|
||||
{buttonContents()}
|
||||
</UnstyledButton>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<UnstyledButton onClick={() => setOpened((o) => !o)} className={classes.control}>
|
||||
<Group position="apart" spacing={0}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<ThemeIcon variant="light" size={30}>
|
||||
<Icon size={18} />
|
||||
</ThemeIcon>
|
||||
<Box ml="md">{label}</Box>
|
||||
</Box>
|
||||
{hasLinks && (
|
||||
<ChevronIcon
|
||||
className={classes.chevron}
|
||||
size={14}
|
||||
style={{
|
||||
transform: opened ? `rotate(${theme.dir === 'rtl' ? -90 : 90}deg)` : 'none',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
{myButton()}
|
||||
{hasLinks ? <Collapse in={opened}>{items}</Collapse> : null}
|
||||
</>
|
||||
);
|
||||
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user