making frontend responsive starting item backend routes
This commit is contained in:
@@ -4,7 +4,7 @@ import HomePage from './components/pages/HomePage';
|
||||
import Locations from './components/pages/Locations';
|
||||
import RoomsPage from './components/pages/RoomsPage';
|
||||
import { Routes, Route, useNavigate } from "react-router-dom";
|
||||
import { Modal, Button, Text, Group, TextInput, Loader, AppShell } from '@mantine/core';
|
||||
import { Modal, Button, Text, Group, TextInput, Loader, AppShell, MediaQuery } from '@mantine/core';
|
||||
import { useDebouncedValue, useLocalStorageValue } from '@mantine/hooks';
|
||||
import { useNotifications } from '@mantine/notifications';
|
||||
|
||||
@@ -20,13 +20,12 @@ import AppHeader from './components/AppHeader';
|
||||
|
||||
function App() {
|
||||
// Main nav/sidebar appshell openend
|
||||
const [shellOpened, setShellOpened] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
|
||||
const [, setServerConfig] = useAtom(serverConfigAtom)
|
||||
|
||||
const notifications = useNotifications();
|
||||
const navigate = useNavigate();
|
||||
// const navigate = useNavigate();
|
||||
|
||||
|
||||
|
||||
@@ -61,28 +60,13 @@ function App() {
|
||||
|
||||
}, [])
|
||||
|
||||
// function showPage() {
|
||||
// switch (currentPage.path) {
|
||||
// case "home":
|
||||
// return <HomePage />
|
||||
// case "locations":
|
||||
// console.log("RETURNING LOCATIONS: ", currentPage)
|
||||
// return <Locations setCurrentPage={setCurrentPage} id={currentPage.id}/>
|
||||
// case "rooms":
|
||||
// console.log("RETURNING ROOMS: ", currentPage)
|
||||
// return <RoomsPage setCurrentPage={setCurrentPage} id={currentPage.id} />
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
navbarOffsetBreakpoint="sm" // navbarOffsetBreakpoint controls when navbar should no longer be offset with padding-left
|
||||
fixed // fixed prop on AppShell will be automatically added to Header and Navbar
|
||||
navbar={<SideBar opened={shellOpened} />}
|
||||
header={<AppHeader opened={shellOpened} setOpened={setShellOpened}/>}
|
||||
navbar={<SideBar />}
|
||||
header={<AppHeader />}
|
||||
>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
|
@@ -1,22 +1,23 @@
|
||||
import React, {useState, useEffect, useContext } from 'react';
|
||||
import { Header, MediaQuery, Burger, Text, ThemeIcon, Group, Title } from '@mantine/core';
|
||||
import { useMantineTheme } from '@mantine/core';
|
||||
import { useAtom } from 'jotai';
|
||||
import { Header, MediaQuery, Burger, Text, ThemeIcon, Group, Title, createStyles, useMantineTheme } from '@mantine/core';
|
||||
import { BsHouseDoor } from 'react-icons/bs'
|
||||
|
||||
import { menuOpenedAtom } from '../state/main';
|
||||
|
||||
|
||||
function AppHeader(props) {
|
||||
const theme = useMantineTheme();
|
||||
const theme = useMantineTheme()
|
||||
const [menuOpened, setMenuOpened] = useAtom(menuOpenedAtom)
|
||||
|
||||
|
||||
return (
|
||||
<Header height={70} padding="md">
|
||||
{/* You can handle other responsive styles with MediaQuery component or createStyles function */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', height: '100%' }}>
|
||||
<MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
|
||||
<MediaQuery largerThan="sm" styles={{ display: 'none' }}>
|
||||
<Burger
|
||||
opened={props.opened}
|
||||
onClick={() => props.setOpened((o) => !o)}
|
||||
opened={menuOpened}
|
||||
onClick={() => setMenuOpened((o) => !o)}
|
||||
size="sm"
|
||||
color={theme.colors.gray[6]}
|
||||
mr="xl"
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, {useState } from 'react';
|
||||
import { useAtom } from 'jotai';
|
||||
import { activePageAtom, roomFilterAtom } from '../state/main';
|
||||
import { Navbar, Group, Code, ScrollArea, createStyles } from '@mantine/core';
|
||||
import { activePageAtom, menuOpenedAtom, roomFilterAtom } from '../state/main';
|
||||
import { Navbar, Group, ScrollArea, createStyles, MediaQuery } from '@mantine/core';
|
||||
import { LinksGroup } from './sidebar/SidebarLinksGroup';
|
||||
// import { UserButton } from './sidebar/UserButton';
|
||||
import { Link } from 'react-router-dom';
|
||||
@@ -80,6 +80,8 @@ const useStyles = createStyles((theme) => ({
|
||||
function SideBar(props) {
|
||||
const { classes } = useStyles();
|
||||
|
||||
const [menuOpened] = useAtom(menuOpenedAtom)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -88,51 +90,31 @@ function SideBar(props) {
|
||||
const links = sideBarData.map((item) => <LinksGroup {...item} key={item.label} />);
|
||||
|
||||
return (
|
||||
<Navbar height={800} width={{ sm: 300 }} p="md" className={classes.navbar}>
|
||||
{/* <Navbar.Section className={classes.header}>
|
||||
<Group position="apart">
|
||||
<Logo width={120} />
|
||||
<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 className={classes.footer}>
|
||||
<UserButton
|
||||
image="https://images.unsplash.com/photo-1508214751196-bcfd4ca60f91?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=255&q=80"
|
||||
name="Ann Nullpointer"
|
||||
email="anullpointer@yahoo.com"
|
||||
/>
|
||||
</Navbar.Section> */}
|
||||
</Navbar>
|
||||
// <Navbar
|
||||
// padding="md"
|
||||
// // Breakpoint at which navbar will be hidden if hidden prop is true
|
||||
// hiddenBreakpoint="sm"
|
||||
// // Hides navbar when viewport size is less than value specified in hiddenBreakpoint
|
||||
// hidden={!props.opened}
|
||||
// width={{ base: 200, breakpoints: { sm: '100%', lg: 300 } }}
|
||||
// >
|
||||
// <Button leftIcon={<BsMap />} variant="white" className={activePage === "locations" ? classes.activeButton : classes.button} component={Link} to="/locations" onClick={() => setActivePage("locations")} >
|
||||
// Locations
|
||||
// </Button>
|
||||
|
||||
// <Button leftIcon={<BsMap />} variant="white" className={activePage === "rooms" ? classes.activeButton : classes.button} component={Link} to="/rooms" onClick={() => setActivePage("rooms")} >
|
||||
// Rooms
|
||||
// </Button>
|
||||
|
||||
// {/* <UnstyledButton component={Link} to="/rooms" className={activePage === "rooms" ? classes.activeButton : classes.button} onClick={() => changeView("rooms")} >
|
||||
// <Group>
|
||||
// <BsMap />
|
||||
// <Text>Rooms</Text>
|
||||
// </Group>
|
||||
|
||||
// </UnstyledButton> */}
|
||||
|
||||
// </Navbar>
|
||||
<>
|
||||
<MediaQuery smallerThan="md" styles={!menuOpened && { 'display': 'none'}}>
|
||||
<Navbar height={800} width={{ sm: 300 }} p="md" className={classes.navbar}>
|
||||
{/* <Navbar.Section className={classes.header}>
|
||||
<Group position="apart">
|
||||
<Logo width={120} />
|
||||
<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 className={classes.footer}>
|
||||
<UserButton
|
||||
image="https://images.unsplash.com/photo-1508214751196-bcfd4ca60f91?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=255&q=80"
|
||||
name="Ann Nullpointer"
|
||||
email="anullpointer@yahoo.com"
|
||||
/>
|
||||
</Navbar.Section> */}
|
||||
</Navbar>
|
||||
</MediaQuery>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -52,31 +52,50 @@ function LocationsPage(props) {
|
||||
navigate("/rooms")
|
||||
}
|
||||
|
||||
const setRoomNumber = (location) => {
|
||||
if (location.Rooms === null) {
|
||||
return <Text>0 Rooms</Text>
|
||||
} else if (location.Rooms.length > 1) {
|
||||
return <Text>{location.Rooms.length} Rooms</Text>
|
||||
} else {
|
||||
return <Text>1 Room</Text>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Center>{ isLoading && <Loader size="xl" variant="bars" />}</Center>
|
||||
<Center><Title order={1}>Locations</Title></Center>
|
||||
<SimpleGrid cols={4} spacing="xl">
|
||||
{ locations.map((location, idx) =>
|
||||
<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>}
|
||||
</Card.Section>
|
||||
<Group position="apart">
|
||||
<Text weight={500}>{location.Name}</Text>
|
||||
<Badge variant="light">
|
||||
{ location.Rooms !== null && location.Rooms.length !== 0 && location.Rooms.length > 1 ? <Text>{location.Rooms.length} Rooms</Text> : <Text>{location.Rooms.length} Room</Text>}
|
||||
</Badge>
|
||||
</Group>
|
||||
<Text size="sm">{location.Description}</Text>
|
||||
<Group>
|
||||
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Details</Button>
|
||||
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Rooms</Button>
|
||||
</Group>
|
||||
<SimpleGrid
|
||||
spacing="md"
|
||||
cols={4}
|
||||
breakpoints={[
|
||||
{ maxWidth: 'sm', cols: 1, spacing: 'md'},
|
||||
{ maxWidth: 'md', cols: 3, spacing: "sm"},
|
||||
{ maxWidth: 'lg', cols: 4, spacing: 'md'}
|
||||
]}
|
||||
|
||||
>
|
||||
{ locations.map((location, idx) =>
|
||||
<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>}
|
||||
</Card.Section>
|
||||
<Group position="apart">
|
||||
<Text weight={500}>{location.Name}</Text>
|
||||
<Badge variant="light">
|
||||
{ setRoomNumber(location) }
|
||||
</Badge>
|
||||
</Group>
|
||||
<Text size="sm">{location.Description}</Text>
|
||||
<Group>
|
||||
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Details</Button>
|
||||
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Rooms</Button>
|
||||
</Group>
|
||||
|
||||
</Card>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
</SimpleGrid>
|
||||
</>
|
||||
|
||||
|
@@ -73,7 +73,15 @@ function RoomsPage(props) {
|
||||
</Badge>
|
||||
|
||||
}
|
||||
<SimpleGrid cols={4} spacing="xl">
|
||||
<SimpleGrid
|
||||
spacing="md"
|
||||
cols={4}
|
||||
breakpoints={[
|
||||
{ maxWidth: 'sm', cols: 1, spacing: 'md'},
|
||||
{ maxWidth: 'md', cols: 3, spacing: "sm"},
|
||||
{ maxWidth: 'lg', cols: 4, spacing: 'md'}
|
||||
]}
|
||||
>
|
||||
{ rooms.map((room, idx) =>
|
||||
<Card key={`${room.ID}`} component="a" onClick={(e) => {}} shadow="sm" padding="md">
|
||||
<Card.Section>
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Group, Box, Collapse, ThemeIcon, Text, UnstyledButton, createStyles } from '@mantine/core';
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import { BiChevronLeft, BiChevronRight } from 'react-icons/bi';
|
||||
import { useAtom } from 'jotai';
|
||||
import { activePageAtom, roomFilterAtom } from '../../state/main';
|
||||
import { activePageAtom, menuOpenedAtom, roomFilterAtom } from '../../state/main';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
||||
@@ -35,12 +36,13 @@ const useStyles = createStyles((theme) => ({
|
||||
}`,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.colors.gray[0],
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.colors.gray[1],
|
||||
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.colors.gray[6]
|
||||
}
|
||||
},
|
||||
|
||||
activeLink: {
|
||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.colors.gray[3],
|
||||
},
|
||||
|
||||
chevron: {
|
||||
@@ -51,12 +53,17 @@ const useStyles = createStyles((theme) => ({
|
||||
|
||||
export function LinksGroup(props) {
|
||||
const { icon: Icon, label, initiallyOpened, links } = props
|
||||
const { classes, theme } = useStyles();
|
||||
|
||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||
const hasLinks = Array.isArray(links);
|
||||
const [opened, setOpened] = useState(initiallyOpened || false);
|
||||
// Navigation State
|
||||
const [activePage, setActivePage] = useAtom(activePageAtom)
|
||||
const [roomFilter, setRoomFilter] = useAtom(roomFilterAtom)
|
||||
const [menuOpened, setMenuOpened] = useAtom(menuOpenedAtom)
|
||||
|
||||
|
||||
const { classes, theme, cx } = useStyles();
|
||||
|
||||
const ChevronIcon = theme.dir === 'ltr' ? BiChevronRight : BiChevronLeft;
|
||||
|
||||
@@ -66,6 +73,9 @@ export function LinksGroup(props) {
|
||||
setRoomFilter({})
|
||||
}
|
||||
console.log("navigate to: ", location)
|
||||
if (isMobile && menuOpened) {
|
||||
setMenuOpened(false)
|
||||
}
|
||||
setActivePage(location)
|
||||
}
|
||||
|
||||
@@ -75,11 +85,10 @@ export function LinksGroup(props) {
|
||||
<>
|
||||
<Link
|
||||
component={Link}
|
||||
className={activePage === label.toLowerCase() ? classes.link : classes.link}
|
||||
className={activePage === link.label.toLowerCase() ? cx(classes.link, classes.activeLink) : classes.link}
|
||||
to={link.link}
|
||||
// href={link.link}
|
||||
key={link.label}
|
||||
onClick={(e) => {navigate(e, label.toLowerCase())}}
|
||||
onClick={(e) => {navigate(e, link.label.toLowerCase())}}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
|
@@ -3,6 +3,9 @@ import { atom } from 'jotai'
|
||||
|
||||
export const serverConfigAtom = atom({})
|
||||
|
||||
// Size and menu handling
|
||||
export const menuOpenedAtom = atom(false)
|
||||
|
||||
// Pages state
|
||||
export const activePageAtom = atom({})
|
||||
|
||||
|
Reference in New Issue
Block a user