updating frontend to new standards
This commit is contained in:
@@ -490,3 +490,90 @@
|
|||||||
{"level":"debug","time":"2023-01-09T22:18:51-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
{"level":"debug","time":"2023-01-09T22:18:51-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
{"logtype":"webserver", "pid":"15420", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
{"logtype":"webserver", "pid":"15420", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
{"logtype":"webserver", "pid":"15420", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
{"logtype":"webserver", "pid":"15420", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"level":"info","time":"2023-01-10T21:30:33-05:00","message":"Configuration loaded successfully..."}
|
||||||
|
{"level":"debug","time":"2023-01-10T21:30:33-05:00","message":"{Timezone:America/New_York Server:{Port:3500 LocationFilesDir:./app/files/} Logger:{Level:debug LoggingFile:./app/log/goInventorize.log} Authentication:{BasicAuth:false UserName:admin Password:password} Development:false}"}
|
||||||
|
{"level":"info","time":"2023-01-10T21:30:33-05:00","message":"Database and Config loaded, starting webserver..."}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"level":"info","time":"2023-01-10T21:31:52-05:00","message":"Getting all Rooms"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/rooms"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:30:28-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:30:32-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"level":"info","time":"2023-01-10T22:30:37-05:00","message":"Getting all Rooms"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/rooms"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:30:38-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:32:19-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"level":"info","time":"2023-01-10T22:32:20-05:00","message":"Getting all Rooms"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/rooms"}
|
||||||
|
{"level":"info","time":"2023-01-10T22:32:23-05:00","message":"Getting all Rooms"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/rooms"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:32:24-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:32:28-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/config"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/overview/all"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:38:41-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:40:26-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:41:33-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"404", "method":"GET", "path":"/files/locations/undefined/undefined"}
|
||||||
|
{"level":"debug","time":"2023-01-10T22:42:00-05:00","message":"Returning Locations: [{ID:1 Name:Location0 Description:This is my test description0 Notes:Notes for my location!0 Address: SquareFeet:2500 Latitude:120N Longitude:120N DatePurchased: PurchasePrice:125000 CurrentValue: CoverPhoto:Location0_cover.png Photos:[] Files:[] Rooms:[]}]"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"200", "method":"GET", "path":"/api/locations"}
|
||||||
|
{"logtype":"webserver", "pid":"14996", "requestid":"", "status":"404", "method":"GET", "path":"/files/locations/undefined/undefined"}
|
||||||
|
@@ -1,23 +1,24 @@
|
|||||||
import React, {useState, useEffect } from 'react';
|
import React, {useState, useEffect } from 'react';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import HomePage from './components/pages/HomePage';
|
import HomePage from './components/pages/HomePage';
|
||||||
import LocationsPage from './components/pages/LocationsPage';
|
import LocationsPage from './components/pages/Locations/LocationsPage';
|
||||||
import RoomsPage from './components/pages/RoomsPage';
|
import RoomsPage from './components/pages/RoomsPage';
|
||||||
import LocationForm from './components/forms/LocationForm'
|
import LocationForm from './components/pages/Locations/NewLocationForm'
|
||||||
import NotFound from './components/pages/NotFound';
|
import NotFound from './components/pages/NotFound';
|
||||||
import { BrowserRouter as Router, Routes, Route} from 'react-router-dom'
|
import { BrowserRouter as Router, Routes, Route} from 'react-router-dom'
|
||||||
import { Modal, Button, Text, Group, TextInput, Loader, AppShell, MediaQuery } from '@mantine/core';
|
import { Loader, AppShell, MediaQuery, Header, Center, Title, Burger, useMantineTheme } from '@mantine/core';
|
||||||
import { useDebouncedValue, useLocalStorageValue } from '@mantine/hooks';
|
// import { useDebouncedValue, useLocalStorageValue } from '@mantine/hooks';
|
||||||
import { showNotification } from '@mantine/notifications';
|
|
||||||
|
|
||||||
import { backendAPI, defaultURLS } from './services/backend-api';
|
import { backendAPI, defaultURLS } from './services/backend-api';
|
||||||
|
|
||||||
import { serverConfigAtom } from './state/state'
|
import { serverConfigAtom, notificationQueueAtom } from './state/state'
|
||||||
|
|
||||||
|
import { generateNotification } from './components/Utilities';
|
||||||
|
|
||||||
|
|
||||||
import SideBar from './components/SideBar';
|
import SideBar from './components/SideBar';
|
||||||
import AppHeader from './components/AppHeader';
|
// import AppHeader from './components/AppHeader';
|
||||||
import LocationDetailsPage from './components/pages/LocationDetailsPage';
|
import LocationDetailsPage from './components/pages/Locations/LocationDetailsPage';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -25,52 +26,73 @@ function App() {
|
|||||||
// Main nav/sidebar appshell openend
|
// Main nav/sidebar appshell openend
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
|
||||||
|
const [opened, setOpened] = useState(false);
|
||||||
|
|
||||||
const [, setServerConfig] = useAtom(serverConfigAtom)
|
const [, setServerConfig] = useAtom(serverConfigAtom)
|
||||||
|
const [, setNotificationQueue] = useAtom(notificationQueueAtom)
|
||||||
|
|
||||||
|
const theme = useMantineTheme();
|
||||||
|
|
||||||
|
|
||||||
// const navigate = useNavigate();
|
// const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// navigate("/")// Reset to homepage on new load anywhere in the app
|
// navigate("/")// Reset to homepage on new load anywhere in the app
|
||||||
setIsLoading(true)
|
|
||||||
async function fetchSettings() {
|
const fetchSettings = async () => {
|
||||||
backendAPI.get('/config').then(results => {
|
setIsLoading(true)
|
||||||
|
try {
|
||||||
|
const results = await backendAPI.get('/config')
|
||||||
// console.log("FULL RESULTS: ", results.config.baseURL)
|
// console.log("FULL RESULTS: ", results.config.baseURL)
|
||||||
results.data.baseURL = defaultURLS.baseURL
|
results.data.baseURL = defaultURLS.baseURL
|
||||||
console.log("CONFIG: ", results.data)
|
console.log("CONFIG: ", results.data)
|
||||||
setServerConfig(results.data)
|
setServerConfig(results.data)
|
||||||
showNotification({
|
const notification = generateNotification('success', 'Config Fetched', 'Config fetched from backend')
|
||||||
title: 'Backend Notice',
|
setNotificationQueue((currentQueue) => ([...currentQueue, notification]))
|
||||||
message: 'Config fetched from backend!',
|
} catch(err) {
|
||||||
color: "green"
|
const notification = generateNotification('error', 'Failed to fetch config', `Failed to fetch config: ${err}`)
|
||||||
})
|
setNotificationQueue((currentQueue) => ([...currentQueue, notification]))
|
||||||
setIsLoading(false)
|
}
|
||||||
}).catch(err => {
|
setIsLoading(false)
|
||||||
showNotification({
|
|
||||||
title: 'Backend Notice',
|
|
||||||
message: `Failed to connect to backend! ${err}`,
|
|
||||||
autoClose: false,
|
|
||||||
color: "red",
|
|
||||||
})
|
|
||||||
setIsLoading(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchSettings();
|
fetchSettings();
|
||||||
|
|
||||||
|
}, [setServerConfig, setNotificationQueue])
|
||||||
}, [])
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{isLoading ? <Center><Loader variant="bars" /></Center> :
|
||||||
<Router>
|
<Router>
|
||||||
<AppShell
|
<AppShell
|
||||||
navbarOffsetBreakpoint="sm" // navbarOffsetBreakpoint controls when navbar should no longer be offset with padding-left
|
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
|
fixed // fixed prop on AppShell will be automatically added to Header and Navbar
|
||||||
navbar={<SideBar />}
|
navbar={<SideBar />}
|
||||||
header={<AppHeader />}
|
header={
|
||||||
|
<Header height={70} p="md">
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center', height: '100%' }}>
|
||||||
|
<MediaQuery largerThan="sm" styles={{ display: 'none' }}>
|
||||||
|
<Burger
|
||||||
|
opened={opened}
|
||||||
|
onClick={() => setOpened((o) => !o)}
|
||||||
|
size="sm"
|
||||||
|
color={theme.colors.gray[6]}
|
||||||
|
mr="xl"
|
||||||
|
/>
|
||||||
|
</MediaQuery>
|
||||||
|
<MediaQuery largerThan="sm" styles={{ display: 'none' }}>
|
||||||
|
<Title order={4}>goInventorize</Title>
|
||||||
|
</MediaQuery>
|
||||||
|
<MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
|
||||||
|
<Title>goInventorize</Title>
|
||||||
|
</MediaQuery>
|
||||||
|
</div>
|
||||||
|
</Header>
|
||||||
|
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='*' element={<NotFound />} />
|
<Route path='*' element={<NotFound />} />
|
||||||
@@ -86,6 +108,8 @@ function App() {
|
|||||||
</Routes>
|
</Routes>
|
||||||
</AppShell>
|
</AppShell>
|
||||||
</Router>
|
</Router>
|
||||||
|
}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
function Notifications() {
|
|
||||||
|
|
||||||
|
|
||||||
return(
|
|
||||||
<></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Notifications;
|
|
@@ -1,38 +0,0 @@
|
|||||||
import React, {useState, useEffect, useContext } from 'react';
|
|
||||||
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/state';
|
|
||||||
|
|
||||||
|
|
||||||
function AppHeader(props) {
|
|
||||||
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 largerThan="sm" styles={{ display: 'none' }}>
|
|
||||||
<Burger
|
|
||||||
opened={menuOpened}
|
|
||||||
onClick={() => setMenuOpened((o) => !o)}
|
|
||||||
size="sm"
|
|
||||||
color={theme.colors.gray[6]}
|
|
||||||
mr="xl"
|
|
||||||
/>
|
|
||||||
</MediaQuery>
|
|
||||||
<Group>
|
|
||||||
<BsHouseDoor size={30} />
|
|
||||||
<Title><Text inherit variant="gradient" gradient={{ from: 'indigo', to: 'cyan', deg: 45 }}>goInventorize</Text></Title>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</Header>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default AppHeader
|
|
32
frontend/src/components/Notifications.js
Normal file
32
frontend/src/components/Notifications.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { useAtom } from 'jotai';
|
||||||
|
import { useResetAtom } from 'jotai/utils'
|
||||||
|
import { notificationQueueAtom, notificationHistoryAtom } from '../state/state.js';
|
||||||
|
import { showNotification } from '@mantine/notifications';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
function Notifications() {
|
||||||
|
const [notificationQueue] = useAtom(notificationQueueAtom)
|
||||||
|
const [,setNotificationHistory] = useAtom(notificationHistoryAtom)
|
||||||
|
const resetNotifications = useResetAtom(notificationQueueAtom)
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (notificationQueue.length > 0) {
|
||||||
|
notificationQueue.forEach(notification => {
|
||||||
|
// setting the full history so user can view it later
|
||||||
|
setNotificationHistory((history) => [...history, notification])
|
||||||
|
showNotification({
|
||||||
|
title: notification.title,
|
||||||
|
message: notification.message,
|
||||||
|
color: notification.color,
|
||||||
|
icon: notification.icon,
|
||||||
|
disallowClose: notification.disallowClose,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
resetNotifications()
|
||||||
|
}
|
||||||
|
}, [notificationQueue, resetNotifications, setNotificationHistory])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Notifications
|
120
frontend/src/components/PhotoDisplay.js
Normal file
120
frontend/src/components/PhotoDisplay.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import {Image, Text, Modal, SimpleGrid, Title, Loader, Center, Card, Group, Badge, Button, Tooltip} from '@mantine/core'
|
||||||
|
import { ExternalLink } from 'tabler-icons-react'
|
||||||
|
import {useEffect, useState } from 'react'
|
||||||
|
// import BackendAPI from '../services/backend'
|
||||||
|
|
||||||
|
const urlPrepend = "https://apps.ts.fedex.com/tsrphotos/"
|
||||||
|
|
||||||
|
function PhotoDisplay(props) {
|
||||||
|
const { photos } = props
|
||||||
|
//const [photosDisplay, setPhotosDisplay] = useState([])
|
||||||
|
const [photosLoading, setPhotosLoading] = useState(false)
|
||||||
|
const [numPhotos, setNumPhotos] = useState(photos.length)
|
||||||
|
// Modal states
|
||||||
|
const [photoOpened, setPhotoOpened] = useState(false)
|
||||||
|
const [photoDetails, setPhotoDetails] = useState({
|
||||||
|
"name": "",
|
||||||
|
"data": null,
|
||||||
|
"locationName": "",
|
||||||
|
})
|
||||||
|
const [rawPhotoArray, setRawPhotoArray] = useState([])
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const createPhotos = async () => {
|
||||||
|
setPhotosLoading(true)
|
||||||
|
if (photos.length > 0) {
|
||||||
|
photos.forEach(async (photo) => {
|
||||||
|
console.log("Loading photo: ", photo.photo_path_txt)
|
||||||
|
const fullUrl = `${urlPrepend}${photo.photo_path_txt}`
|
||||||
|
let newPhoto =
|
||||||
|
<Card key={photo.photo_path_txt} shadow="sm" p="lg" radius="md" withBorder>
|
||||||
|
<Card.Section>
|
||||||
|
<Image radius="md"
|
||||||
|
key={photo.photo_path_txt}
|
||||||
|
style={{ cursor: 'pointer'}}
|
||||||
|
src={`${urlPrepend}${photo.photo_path_txt}`}
|
||||||
|
onClick={() => displayPhoto(fullUrl, photo)}
|
||||||
|
/>
|
||||||
|
</Card.Section>
|
||||||
|
<Group position="apart" mt="md" mb="xs">
|
||||||
|
<Text weight={500}>{photo.photo_nm}</Text>
|
||||||
|
<Badge color="pink" variant="light">
|
||||||
|
{photo.network_location ? photo.network_location.location_nm : "No Location"}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
<Text size="sm" mb="lg" color="dimmed">
|
||||||
|
{photo?.photo_notes_txt ? photo.photo_notes_txt : "No Photo Notes!"}
|
||||||
|
</Text>
|
||||||
|
{photo.latitude_nbr !== 0 && photo.longitude_nbr !== 0 &&
|
||||||
|
<Tooltip label={`${photo.latitude_nbr}, ${photo.longitude_nbr}`}>
|
||||||
|
<Button mb="xl" color="dark" component='a' href={`https://maps.google.com/?q=${photo.latitude_nbr},${photo.longitude_nbr}`} target="_blank" leftIcon={<ExternalLink />}>View Photo in Google Maps</Button>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
</Card>
|
||||||
|
setRawPhotoArray(currArray => [...currArray, newPhoto])
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (photos.length > 0) {
|
||||||
|
console.log("RECEIVED PHOTOS!", photos)
|
||||||
|
setNumPhotos(photos.length)
|
||||||
|
createPhotos()
|
||||||
|
}
|
||||||
|
}, [photos])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (photos.length === rawPhotoArray.length) {
|
||||||
|
console.log("Done loading! ")
|
||||||
|
setPhotosLoading(false)
|
||||||
|
}
|
||||||
|
}, [rawPhotoArray, photos.length])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// For displaying photos full screen
|
||||||
|
const displayPhoto = async (photoUrl, photo) => {
|
||||||
|
|
||||||
|
setPhotoDetails({
|
||||||
|
// data: 'data:image/jpeg;base64,' + photoBinary,
|
||||||
|
data: photoUrl,
|
||||||
|
name: photo.photo_nm,
|
||||||
|
locationName: photo.network_location ? photo.network_location.location_nm : ""
|
||||||
|
})
|
||||||
|
setPhotoOpened(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal opened={photoOpened} onClose={() => setPhotoOpened(false)} title={photoDetails.name} size="full">
|
||||||
|
<Image radius="md"
|
||||||
|
src={photoDetails.data}
|
||||||
|
caption={photoDetails.locationName ? <><Text weight={700}>{photoDetails.locationName}</Text><Text>{photoDetails.name}</Text></>: <Text>{photoDetails.name}</Text>}
|
||||||
|
withPlaceholder
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
<Title mb="lg" order={3}>Photos: {numPhotos}</Title>
|
||||||
|
<SimpleGrid
|
||||||
|
cols={4}
|
||||||
|
breakpoints={[
|
||||||
|
{ maxWidth: 1080, cols: 2, spacing: 'md' },
|
||||||
|
// { maxWidth: 755, cols: 2, spacing: 'sm' },
|
||||||
|
{ maxWidth: 600, cols: 1, spacing: 'sm' },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{rawPhotoArray}
|
||||||
|
</SimpleGrid>
|
||||||
|
{photosLoading && <Center><Loader variant='bars' size="sm" /></Center>}
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PhotoDisplay
|
@@ -13,8 +13,8 @@ const sideBarData = [
|
|||||||
links: [
|
links: [
|
||||||
{ label: 'View Locations', link: '/locations' },
|
{ label: 'View Locations', link: '/locations' },
|
||||||
{ label: 'Add New Location', link: '/locations/new' },
|
{ label: 'Add New Location', link: '/locations/new' },
|
||||||
{ label: 'Outlook', link: '/' },
|
// { label: 'Outlook', link: '/' },
|
||||||
{ label: 'Real time', link: '/' },
|
// { label: 'Real time', link: '/' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -24,11 +24,11 @@ const sideBarData = [
|
|||||||
links: [
|
links: [
|
||||||
{ label: 'View Rooms', link: '/rooms' },
|
{ label: 'View Rooms', link: '/rooms' },
|
||||||
{ label: 'Add New Room', link: '/rooms/new' },
|
{ label: 'Add New Room', link: '/rooms/new' },
|
||||||
{ label: 'Releases schedule', link: '/'},
|
// { label: 'Releases schedule', link: '/'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ label: 'Cabinets', icon: BiCabinet, link: "/" },
|
// { label: 'Cabinets', icon: BiCabinet, link: "/" },
|
||||||
{ label: 'Items', icon: BsDiagram2, link: "/" },
|
// { label: 'Items', icon: BsDiagram2, link: "/" },
|
||||||
{ label: 'Settings', icon: GoGear, link: '/settings' },
|
{ label: 'Settings', icon: GoGear, link: '/settings' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
33
frontend/src/components/Utilities.js
Normal file
33
frontend/src/components/Utilities.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { IconAlertTriangle, IconAlertCircle, IconCheck } from '@tabler/icons'
|
||||||
|
|
||||||
|
|
||||||
|
export const generateNotification = (notificationType, title, message, noClose, id) => {
|
||||||
|
// Generates the notification so you can push it to the queue
|
||||||
|
let baseNotification = {
|
||||||
|
id: id ? id : null,
|
||||||
|
dissallowClose: noClose ? noClose : false,
|
||||||
|
title: title,
|
||||||
|
message: message,
|
||||||
|
color: 'red',
|
||||||
|
icon: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (notificationType) {
|
||||||
|
case "error":
|
||||||
|
baseNotification.color = 'red'
|
||||||
|
baseNotification.icon = <IconAlertTriangle size={16} />
|
||||||
|
break;
|
||||||
|
case "warning":
|
||||||
|
baseNotification.color = 'yellow'
|
||||||
|
baseNotification.icon = <IconAlertCircle size={16} />
|
||||||
|
break;
|
||||||
|
case "success":
|
||||||
|
baseNotification.color = 'green'
|
||||||
|
baseNotification.icon = <IconCheck size={16}/>
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.log("MY NOTIFICATION: ", baseNotification)
|
||||||
|
return baseNotification
|
||||||
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Button, Card, Group, Menu, Image, Badge } from '@mantine/core'
|
import { Text, Button, Card, Group, Menu, Image, Badge } from '@mantine/core'
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { useNavigate } from "react-router-dom";
|
// import { useNavigate } from "react-router-dom";
|
||||||
import { activePageAtom, roomFilterAtom, serverConfigAtom } from '../../state/state';
|
import { serverConfigAtom } from '../../state/state';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
|
||||||
@@ -10,10 +10,6 @@ import { Link } from 'react-router-dom';
|
|||||||
function LocationCard(props) {
|
function LocationCard(props) {
|
||||||
const {location, idx} = props
|
const {location, idx} = props
|
||||||
const [serverConfig] = useAtom(serverConfigAtom)
|
const [serverConfig] = useAtom(serverConfigAtom)
|
||||||
const [, setRoomsFilter] = useAtom(roomFilterAtom)
|
|
||||||
const [, setActivePage] = useAtom(activePageAtom)
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const setRoomNumber = (rooms) => {
|
const setRoomNumber = (rooms) => {
|
||||||
if (rooms === null) {
|
if (rooms === null) {
|
||||||
@@ -25,11 +21,6 @@ function LocationCard(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigateToRooms = (locationID, locationName) => {
|
|
||||||
setActivePage("rooms")
|
|
||||||
setRoomsFilter({"filterType": "location", "locationID": locationID, "commonName": "location", "metadata": locationName})
|
|
||||||
navigate("/rooms")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -51,9 +42,8 @@ function LocationCard(props) {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</Group>
|
</Group>
|
||||||
<Text size="sm">{location.Description}</Text>
|
<Text size="sm">{location.Description}</Text>
|
||||||
<Group>
|
<Group position='right' mt="xl">
|
||||||
<Button component={Link} to={`/locations/${location.ID}`} state={{ location: location }}>View Details</Button>
|
<Button component={Link} to={`/locations/${location.ID}`} state={{ location: location }}>View Location Details</Button>
|
||||||
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Rooms</Button>
|
|
||||||
</Group>
|
</Group>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
@@ -1,148 +1,170 @@
|
|||||||
import React, {useState, useEffect } from 'react';
|
import React, {useState, useEffect } from 'react';
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { serverConfigAtom } from '../../state/state';
|
import { serverConfigAtom } from '../../../state/state';
|
||||||
import { Loader, Center, Title, Group, Button, Space, Container, ThemeIcon, Text, Image } from '@mantine/core'
|
import { BsDoorClosed } from 'react-icons/bs';
|
||||||
import { useNavigate, useLocation, useParams } from "react-router-dom";
|
import { BiEdit } from 'react-icons/bi';
|
||||||
import { useNotifications } from '@mantine/notifications';
|
import { Loader, Center, Title, Group, Button, Space, Container, ThemeIcon, Text, Image, Accordion, Avatar } from '@mantine/core'
|
||||||
import { GoLocation } from 'react-icons/go'
|
import { useNavigate, useLocation, useParams } from "react-router-dom";
|
||||||
|
import { useNotifications } from '@mantine/notifications';
|
||||||
import { backendAPI } from '../../services/backend-api';
|
import { GoLocation } from 'react-icons/go'
|
||||||
|
|
||||||
function LocationDetailsPage() {
|
import { backendAPI } from '../../../services/backend-api';
|
||||||
const location = useLocation()
|
|
||||||
|
function LocationDetailsPage() {
|
||||||
|
const location = useLocation()
|
||||||
|
|
||||||
// const [opened, setOpened] = useState(false);
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
const [serverConfig] = useAtom(serverConfigAtom)
|
// const [opened, setOpened] = useState(false);
|
||||||
const [locationDetails, setLocationDetails] = useState({})
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [serverConfig] = useAtom(serverConfigAtom)
|
||||||
let navigate = useNavigate();
|
const [locationDetails, setLocationDetails] = useState({})
|
||||||
|
|
||||||
const notifications = useNotifications();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
let routeLocationID = useParams()
|
const notifications = useNotifications();
|
||||||
|
|
||||||
|
let routeLocationID = useParams()
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let locationDetailsProps = {}
|
|
||||||
if (location.state) {
|
useEffect(() => {
|
||||||
if (location.state.hasOwnProperty('location')) {
|
let locationDetailsProps = {}
|
||||||
locationDetailsProps = location.state.location
|
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)
|
if (Object.keys(locationDetailsProps).length === 0) {
|
||||||
async function fetchSettings() {
|
console.log("manually fetching location details!")
|
||||||
backendAPI.get(`/locations/${routeLocationID.id}`).then(results => {
|
setIsLoading(true)
|
||||||
console.log("CONFIG IN LOCATIONS: ", serverConfig)
|
async function fetchSettings() {
|
||||||
console.log("LOCATIONS: ", results.data)
|
backendAPI.get(`/locations/${routeLocationID.id}`).then(results => {
|
||||||
setLocationDetails(results.data)
|
console.log("CONFIG IN LOCATIONS: ", serverConfig)
|
||||||
setIsLoading(false)
|
console.log("LOCATIONS: ", results.data)
|
||||||
}).catch(err => {
|
setLocationDetails(results.data)
|
||||||
notifications.showNotification({
|
setIsLoading(false)
|
||||||
title: 'Backend Notice',
|
}).catch(err => {
|
||||||
message: `Failed to fetch locations from backend! ${err}`,
|
notifications.showNotification({
|
||||||
autoClose: false,
|
title: 'Backend Notice',
|
||||||
color: "red",
|
message: `Failed to fetch locations from backend! ${err}`,
|
||||||
})
|
autoClose: false,
|
||||||
setIsLoading(false)
|
color: "red",
|
||||||
})
|
})
|
||||||
|
setIsLoading(false)
|
||||||
}
|
})
|
||||||
fetchSettings();
|
|
||||||
} else {
|
}
|
||||||
console.log("Using passed state!", locationDetailsProps)
|
fetchSettings();
|
||||||
setLocationDetails(locationDetailsProps)
|
} else {
|
||||||
}
|
console.log("Using passed state!", locationDetailsProps)
|
||||||
|
setLocationDetails(locationDetailsProps)
|
||||||
|
}
|
||||||
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Center>{ isLoading && <Loader size="xl" variant="bars" />}</Center>
|
return (
|
||||||
<Center><Title order={1}>{locationDetails.Name}</Title></Center>
|
<>
|
||||||
<Button>Edit Location</Button>
|
<Center>{ isLoading && <Loader size="xl" variant="bars" />}</Center>
|
||||||
<Space h="md" />
|
<Center><Title order={1}>{locationDetails.Name}</Title></Center>
|
||||||
<Container>
|
<Button color="yellow" leftIcon={<BiEdit size={24} />}>Edit Location</Button>
|
||||||
<Group>
|
<Space h="md" />
|
||||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
<Container>
|
||||||
<Text>Description: </Text>
|
<Group>
|
||||||
<Text>{locationDetails.Description}</Text>
|
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||||
</Group>
|
<Text>Description: </Text>
|
||||||
</Container>
|
<Text>{locationDetails.Description}</Text>
|
||||||
<Space h="md" />
|
</Group>
|
||||||
<Container>
|
</Container>
|
||||||
<Group>
|
<Space h="md" />
|
||||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
<Container>
|
||||||
<Text>Notes: </Text>
|
<Group>
|
||||||
<Text>{locationDetails.Notes}</Text>
|
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||||
</Group>
|
<Text>Notes: </Text>
|
||||||
</Container>
|
<Text>{locationDetails.Notes}</Text>
|
||||||
<Space h="md" />
|
</Group>
|
||||||
<Container>
|
</Container>
|
||||||
<Group>
|
<Space h="md" />
|
||||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
<Container>
|
||||||
<Text>Address: </Text>
|
<Group>
|
||||||
<Text>{locationDetails.Address}</Text>
|
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||||
</Group>
|
<Text>Address: </Text>
|
||||||
</Container>
|
<Text>{locationDetails.Address}</Text>
|
||||||
<Space h="md" />
|
</Group>
|
||||||
<Container>
|
</Container>
|
||||||
<Group>
|
<Space h="md" />
|
||||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
<Container>
|
||||||
<Text>Square Feet: </Text>
|
<Group>
|
||||||
<Text>{locationDetails.SquareFeet}</Text>
|
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||||
</Group>
|
<Text>Square Feet: </Text>
|
||||||
</Container>
|
<Text>{locationDetails.SquareFeet}</Text>
|
||||||
<Space h="md" />
|
</Group>
|
||||||
<Container>
|
</Container>
|
||||||
<Group>
|
<Space h="md" />
|
||||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
<Container>
|
||||||
<Text>Lat/Long: </Text>
|
<Group>
|
||||||
<Text>{locationDetails.Latitude}/{locationDetails.Longitude}</Text>
|
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||||
</Group>
|
<Text>Lat/Long: </Text>
|
||||||
</Container>
|
<Text>{locationDetails.Latitude}/{locationDetails.Longitude}</Text>
|
||||||
<Space h="md" />
|
</Group>
|
||||||
<Container>
|
</Container>
|
||||||
<Group>
|
<Space h="md" />
|
||||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
<Container>
|
||||||
<Text>Date Purchased: </Text>
|
<Group>
|
||||||
<Text>{locationDetails.DatePurchased}</Text>
|
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||||
</Group>
|
<Text>Date Purchased: </Text>
|
||||||
</Container>
|
<Text>{locationDetails.DatePurchased}</Text>
|
||||||
<Space h="md" />
|
</Group>
|
||||||
<Container>
|
</Container>
|
||||||
<Group>
|
<Space h="md" />
|
||||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
<Container>
|
||||||
<Text>Purchase Price: </Text>
|
<Group>
|
||||||
<Text>{locationDetails.PurchasePrice}</Text>
|
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||||
</Group>
|
<Text>Purchase Price: </Text>
|
||||||
</Container>
|
<Text>{locationDetails.PurchasePrice}</Text>
|
||||||
<Space h="md" />
|
</Group>
|
||||||
<Container>
|
</Container>
|
||||||
<Group>
|
<Space h="md" />
|
||||||
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
<Container>
|
||||||
<Text>Current Value: </Text>
|
<Group>
|
||||||
<Text>{locationDetails.CurrentValue}</Text>
|
<ThemeIcon variant="light" size={30}><GoLocation /></ThemeIcon>
|
||||||
</Group>
|
<Text>Current Value: </Text>
|
||||||
</Container>
|
<Text>{locationDetails.CurrentValue}</Text>
|
||||||
<Space h="md" />
|
</Group>
|
||||||
<Container>
|
</Container>
|
||||||
<Image src={`${serverConfig.baseURL}/files/locations/${locationDetails.Name}/${locationDetails.CoverPhoto}`} />
|
<Space h="md" />
|
||||||
</Container>
|
<Container>
|
||||||
</>
|
<Image src={`${serverConfig.baseURL}/files/locations/${locationDetails.Name}/${locationDetails.CoverPhoto}`} />
|
||||||
|
</Container>
|
||||||
);
|
<Accordion mt="xl">
|
||||||
}
|
<Accordion.Item value="notifications">
|
||||||
|
<Accordion.Control>
|
||||||
|
<Group noWrap>
|
||||||
|
<Avatar color="blue" radius="xl" size="lg">
|
||||||
|
<BsDoorClosed />
|
||||||
|
</Avatar>
|
||||||
|
<div>
|
||||||
|
<Text>Rooms</Text>
|
||||||
|
<Text size="sm" color="dimmed" weight={400}>
|
||||||
|
Rooms in this Location
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Group>
|
||||||
|
</Accordion.Control>
|
||||||
|
<Accordion.Panel>
|
||||||
|
|
||||||
|
</Accordion.Panel>
|
||||||
|
</Accordion.Item>
|
||||||
|
</Accordion>
|
||||||
|
</>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default LocationDetailsPage;
|
export default LocationDetailsPage;
|
@@ -1,76 +1,76 @@
|
|||||||
import React, {useState, useEffect } from 'react';
|
import React, {useState, useEffect } from 'react';
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { serverConfigAtom } from '../../state/state';
|
import { serverConfigAtom } from '../../../state/state';
|
||||||
import { HiPlus } from 'react-icons/hi'
|
import { HiPlus } from 'react-icons/hi'
|
||||||
import { Loader, Center, SimpleGrid, Title, Group, Button } from '@mantine/core'
|
import { Loader, Center, SimpleGrid, Title, Group, Button } from '@mantine/core'
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useNotifications } from '@mantine/notifications';
|
import { useNotifications } from '@mantine/notifications';
|
||||||
|
|
||||||
|
|
||||||
import { backendAPI } from '../../services/backend-api';
|
import { backendAPI } from '../../../services/backend-api';
|
||||||
import LocationCard from '../cards/LocationCard';
|
import LocationCard from '../../cards/LocationCard';
|
||||||
|
|
||||||
|
|
||||||
function LocationsPage() {
|
function LocationsPage() {
|
||||||
// const [opened, setOpened] = useState(false);
|
// const [opened, setOpened] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [locations, setLocations] = useState([])
|
const [locations, setLocations] = useState([])
|
||||||
const [serverConfig] = useAtom(serverConfigAtom)
|
const [serverConfig] = useAtom(serverConfigAtom)
|
||||||
|
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("LOADING LOCATIONS PAGE!")
|
console.log("LOADING LOCATIONS PAGE!")
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
async function fetchSettings() {
|
async function fetchSettings() {
|
||||||
backendAPI.get('/locations').then(results => {
|
backendAPI.get('/locations').then(results => {
|
||||||
console.log("CONFIG IN LOCATIONS: ", serverConfig)
|
console.log("CONFIG IN LOCATIONS: ", serverConfig)
|
||||||
console.log("LOCATIONS: ", results.data)
|
console.log("LOCATIONS: ", results.data)
|
||||||
setLocations(results.data)
|
setLocations(results.data)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
notifications.showNotification({
|
notifications.showNotification({
|
||||||
title: 'Backend Notice',
|
title: 'Backend Notice',
|
||||||
message: `Failed to fetch locations from backend! ${err}`,
|
message: `Failed to fetch locations from backend! ${err}`,
|
||||||
autoClose: false,
|
autoClose: false,
|
||||||
color: "red",
|
color: "red",
|
||||||
})
|
})
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
fetchSettings();
|
fetchSettings();
|
||||||
|
|
||||||
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Center>{ isLoading && <Loader size="xl" variant="bars" />}</Center>
|
<Center>{ isLoading && <Loader size="xl" variant="bars" />}</Center>
|
||||||
<Center><Title order={1}>Locations</Title></Center>
|
<Center><Title order={1}>Locations</Title></Center>
|
||||||
<Button mb="lg" leftIcon={<HiPlus />} onClick={(e) => {navigate("/locations/new")}}>Add New Location</Button>
|
<Button mb="lg" leftIcon={<HiPlus />} onClick={(e) => {navigate("/locations/new")}}>Add New Location</Button>
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
spacing="md"
|
spacing="md"
|
||||||
cols={4}
|
cols={4}
|
||||||
breakpoints={[
|
breakpoints={[
|
||||||
{ maxWidth: 'sm', cols: 1, spacing: 'md'},
|
{ maxWidth: 'sm', cols: 1, spacing: 'md'},
|
||||||
{ maxWidth: 'md', cols: 3, spacing: "sm"},
|
{ maxWidth: 'md', cols: 3, spacing: "sm"},
|
||||||
{ maxWidth: 'lg', cols: 4, spacing: 'md'}
|
{ maxWidth: 'lg', cols: 4, spacing: 'md'}
|
||||||
]}
|
]}
|
||||||
|
|
||||||
>
|
>
|
||||||
{ locations.map((location, idx) =>
|
{ locations.map((location, idx) =>
|
||||||
<LocationCard location={location} idx={idx}></LocationCard>
|
<LocationCard location={location} idx={idx}></LocationCard>
|
||||||
)}
|
)}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LocationsPage;
|
export default LocationsPage;
|
@@ -1,166 +1,166 @@
|
|||||||
import React, {useState, useEffect} from 'react';
|
import React, {useState, useEffect} from 'react';
|
||||||
import { Text, Title, TextInput, Button, NumberInput, Textarea, Grid, Group, useMantineTheme, MantineTheme, Image, SimpleGrid } from '@mantine/core'
|
import { Text, Title, TextInput, Button, NumberInput, Textarea, Grid, Group, useMantineTheme, MantineTheme, Image, SimpleGrid } from '@mantine/core'
|
||||||
import { DatePicker } from '@mantine/dates';
|
import { DatePicker } from '@mantine/dates';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { serverConfigAtom } from '../../state/state'
|
import { serverConfigAtom } from '../../../state/state'
|
||||||
import { backendAPI } from '../../services/backend-api';
|
import { backendAPI } from '../../../services/backend-api';
|
||||||
import { IMAGE_MIME_TYPE } from '@mantine/dropzone';
|
import { IMAGE_MIME_TYPE } from '@mantine/dropzone';
|
||||||
import CustomDropZone from '../CustomDropZone';
|
import CustomDropZone from '../../CustomDropZone';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function LocationForm(props) {
|
function NewLocationForm(props) {
|
||||||
const {location, modify: bool} = props
|
const {location, modify: bool} = props
|
||||||
const [opened, setOpened] = useState(false);
|
const [opened, setOpened] = useState(false);
|
||||||
const [serverConfig] = useAtom(serverConfigAtom)
|
const [serverConfig] = useAtom(serverConfigAtom)
|
||||||
// Cover Photo
|
// Cover Photo
|
||||||
const [coverPhoto, setCoverPhoto] = useState(null)
|
const [coverPhoto, setCoverPhoto] = useState(null)
|
||||||
// Additional Photos
|
// Additional Photos
|
||||||
const [additionalPhotos, setAdditionalPhotos] = useState([])
|
const [additionalPhotos, setAdditionalPhotos] = useState([])
|
||||||
|
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
|
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
Name: '',
|
Name: '',
|
||||||
Description: '',
|
Description: '',
|
||||||
Notes: '',
|
Notes: '',
|
||||||
Address: '',
|
Address: '',
|
||||||
SquareFeet: '',
|
SquareFeet: '',
|
||||||
Latitude: '',
|
Latitude: '',
|
||||||
Longitude: '',
|
Longitude: '',
|
||||||
DatePurchased: '',
|
DatePurchased: '',
|
||||||
PurchasePrice: '',
|
PurchasePrice: '',
|
||||||
CurrentValue: '',
|
CurrentValue: '',
|
||||||
CoverPhoto: '',
|
CoverPhoto: '',
|
||||||
AdditionalPhotos: [],
|
AdditionalPhotos: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
validate: {
|
validate: {
|
||||||
Name: (value) => (/^\S+/.test(value) ? null : 'Invalid Name')
|
Name: (value) => (/^\S+/.test(value) ? null : 'Invalid Name')
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const submitNewLocation = (values) => {
|
const submitNewLocation = (values) => {
|
||||||
console.log("VALUES: ", values)
|
console.log("VALUES: ", values)
|
||||||
// let additionalPhotos = values.AdditionalPhotos
|
// let additionalPhotos = values.AdditionalPhotos
|
||||||
let formData = new FormData()
|
let formData = new FormData()
|
||||||
for (const [key, value] of Object.entries(values)) {
|
for (const [key, value] of Object.entries(values)) {
|
||||||
if (key === "AdditionalPhotos" && value.length > 0) {
|
if (key === "AdditionalPhotos" && value.length > 0) {
|
||||||
value.forEach(photo => {
|
value.forEach(photo => {
|
||||||
console.log("PHOTO VALUE: ", photo)
|
console.log("PHOTO VALUE: ", photo)
|
||||||
formData.append(key, photo)
|
formData.append(key, photo)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
formData.append(key, value)
|
formData.append(key, value)
|
||||||
}
|
}
|
||||||
backendAPI.post("/locations/new", formData).then((result) => {
|
backendAPI.post("/locations/new", formData).then((result) => {
|
||||||
console.log("STATUS: ", result.status)
|
console.log("STATUS: ", result.status)
|
||||||
console.log("result: ", result.data)
|
console.log("result: ", result.data)
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.log("Error adding new location!", err)
|
console.log("Error adding new location!", err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleFileAccept = (files) => {
|
const handleFileAccept = (files) => {
|
||||||
form.setFieldValue('CoverPhoto', files[0])
|
form.setFieldValue('CoverPhoto', files[0])
|
||||||
const imageUrl = URL.createObjectURL(files[0]);
|
const imageUrl = URL.createObjectURL(files[0]);
|
||||||
setCoverPhoto(
|
setCoverPhoto(
|
||||||
<Image
|
<Image
|
||||||
key={"coverPhoto"}
|
key={"coverPhoto"}
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
imageProps={{ onLoad: () => URL.revokeObjectURL(imageUrl) }}
|
imageProps={{ onLoad: () => URL.revokeObjectURL(imageUrl) }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAdditionalPhotos = (files) => {
|
const handleAdditionalPhotos = (files) => {
|
||||||
setAdditionalPhotos(oldPhotos => [...oldPhotos, ...files])
|
setAdditionalPhotos(oldPhotos => [...oldPhotos, ...files])
|
||||||
// setAdditionalPhotos(files)
|
// setAdditionalPhotos(files)
|
||||||
console.log("FORM VALUES: ",form.values)
|
console.log("FORM VALUES: ",form.values)
|
||||||
const oldValues = form.values['AdditionalPhotos']
|
const oldValues = form.values['AdditionalPhotos']
|
||||||
form.setFieldValue('AdditionalPhotos', [...oldValues, ...files])
|
form.setFieldValue('AdditionalPhotos', [...oldValues, ...files])
|
||||||
console.log("NEW FORM VALUES: ", form.values)
|
console.log("NEW FORM VALUES: ", form.values)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const previews = additionalPhotos.map((file, index) => {
|
const previews = additionalPhotos.map((file, index) => {
|
||||||
console.log("FILE IS: ", file)
|
console.log("FILE IS: ", file)
|
||||||
const imageUrl = URL.createObjectURL(file);
|
const imageUrl = URL.createObjectURL(file);
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
key={index}
|
key={index}
|
||||||
src={imageUrl}
|
src={imageUrl}
|
||||||
imageProps={{ onLoad: () => URL.revokeObjectURL(imageUrl) }}
|
imageProps={{ onLoad: () => URL.revokeObjectURL(imageUrl) }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Location Form</Title>
|
<Title>Location Form</Title>
|
||||||
<form onSubmit={form.onSubmit((values) => submitNewLocation(values))}>
|
<form onSubmit={form.onSubmit((values) => submitNewLocation(values))}>
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|
||||||
<Grid.Col md={6} lg={4} sm={12} xs={12}>
|
<Grid.Col md={6} lg={4} sm={12} xs={12}>
|
||||||
<TextInput label="Location Name" value={form.values.Name} required {...form.getInputProps('Name')}/>
|
<TextInput label="Location Name" value={form.values.Name} required {...form.getInputProps('Name')}/>
|
||||||
<TextInput label="Description" value={form.values.Description} {...form.getInputProps('Description')} />
|
<TextInput label="Description" value={form.values.Description} {...form.getInputProps('Description')} />
|
||||||
<Textarea label="Location Notes" value={form.values.Notes} {...form.getInputProps('Notes')}/>
|
<Textarea label="Location Notes" value={form.values.Notes} {...form.getInputProps('Notes')}/>
|
||||||
<TextInput label="Street Address" value={form.values.Address} {...form.getInputProps('Address')} />
|
<TextInput label="Street Address" value={form.values.Address} {...form.getInputProps('Address')} />
|
||||||
<NumberInput label="Square Feet" value={form.values.SquareFeet} {...form.getInputProps('SquareFeet')} />
|
<NumberInput label="Square Feet" value={form.values.SquareFeet} {...form.getInputProps('SquareFeet')} />
|
||||||
<TextInput label="Latitude" value={form.values.Latitude} {...form.getInputProps('Latitude')} />
|
<TextInput label="Latitude" value={form.values.Latitude} {...form.getInputProps('Latitude')} />
|
||||||
<TextInput label="Longitude" value={form.values.Longitude} {...form.getInputProps('Longitude')}/>
|
<TextInput label="Longitude" value={form.values.Longitude} {...form.getInputProps('Longitude')}/>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
label="Date Purchased"
|
label="Date Purchased"
|
||||||
value={form.values.DatePurchased}
|
value={form.values.DatePurchased}
|
||||||
maxDate={dayjs(new Date()).add(1, 'days').toDate()}
|
maxDate={dayjs(new Date()).add(1, 'days').toDate()}
|
||||||
{...form.getInputProps('DatePurchased')}
|
{...form.getInputProps('DatePurchased')}
|
||||||
/>
|
/>
|
||||||
<TextInput label="Purchase Price" value={form.values.PurchasePrice} {...form.getInputProps('PurchasePrice')}/>
|
<TextInput label="Purchase Price" value={form.values.PurchasePrice} {...form.getInputProps('PurchasePrice')}/>
|
||||||
<TextInput label="Current Value" value={form.values.CurrentValue} {...form.getInputProps('CurrentValue')}/>
|
<TextInput label="Current Value" value={form.values.CurrentValue} {...form.getInputProps('CurrentValue')}/>
|
||||||
<Group><Title order={4}>Location Cover Photo </Title><Text color="red">*</Text></Group>
|
<Group><Title order={4}>Location Cover Photo </Title><Text color="red">*</Text></Group>
|
||||||
|
|
||||||
{coverPhoto ?
|
{coverPhoto ?
|
||||||
coverPhoto
|
coverPhoto
|
||||||
:
|
:
|
||||||
<CustomDropZone
|
<CustomDropZone
|
||||||
uploadText1={"Drag Image Here or click to select File"}
|
uploadText1={"Drag Image Here or click to select File"}
|
||||||
uploadText2={"Single File, max size: 5mb"}
|
uploadText2={"Single File, max size: 5mb"}
|
||||||
uploadFormat={IMAGE_MIME_TYPE}
|
uploadFormat={IMAGE_MIME_TYPE}
|
||||||
maxSize={3 * 1024 ** 2}
|
maxSize={3 * 1024 ** 2}
|
||||||
multipleFiles={false}
|
multipleFiles={false}
|
||||||
returnFiles={handleFileAccept}
|
returnFiles={handleFileAccept}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<Title order={4}>Additional Location Photos</Title>
|
<Title order={4}>Additional Location Photos</Title>
|
||||||
<CustomDropZone
|
<CustomDropZone
|
||||||
uploadText1={"Drag Images Here or click to select Files"}
|
uploadText1={"Drag Images Here or click to select Files"}
|
||||||
uploadText2={"Multiple Files, max size per file: 5mb"}
|
uploadText2={"Multiple Files, max size per file: 5mb"}
|
||||||
uploadFormat={IMAGE_MIME_TYPE}
|
uploadFormat={IMAGE_MIME_TYPE}
|
||||||
maxSize={3 * 1024 ** 2}
|
maxSize={3 * 1024 ** 2}
|
||||||
multipleFiles={true}
|
multipleFiles={true}
|
||||||
returnFiles={handleAdditionalPhotos}
|
returnFiles={handleAdditionalPhotos}
|
||||||
/>
|
/>
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
cols={4}
|
cols={4}
|
||||||
breakpoints={[{ maxWidth: 'sm', cols: 1 }]}
|
breakpoints={[{ maxWidth: 'sm', cols: 1 }]}
|
||||||
mt={previews.length > 0 ? 'xl' : 0}
|
mt={previews.length > 0 ? 'xl' : 0}
|
||||||
>
|
>
|
||||||
{previews}
|
{previews}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
<Button type="submit">Submit</Button>
|
<Button type="submit">Submit</Button>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LocationForm;
|
export default NewLocationForm;
|
@@ -1,8 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { 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 { BiChevronLeft, BiChevronRight } from 'react-icons/bi';
|
import { BiChevronLeft, BiChevronRight } from 'react-icons/bi';
|
||||||
import { NavLink, useLocation } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import { NoEncryption } from '@material-ui/icons';
|
|
||||||
|
|
||||||
const useStyles = createStyles((theme, _params, getRef) => {
|
const useStyles = createStyles((theme, _params, getRef) => {
|
||||||
const icon = getRef('icon');
|
const icon = getRef('icon');
|
||||||
@@ -61,7 +60,6 @@ const useStyles = createStyles((theme, _params, getRef) => {
|
|||||||
|
|
||||||
|
|
||||||
export function LinksGroup({ icon: Icon, label, link, initiallyOpened, links }) {
|
export function LinksGroup({ icon: Icon, label, link, initiallyOpened, links }) {
|
||||||
const mypath = useLocation()
|
|
||||||
const { classes, theme, cx } = useStyles();
|
const { classes, theme, cx } = useStyles();
|
||||||
const hasLinks = Array.isArray(links);
|
const hasLinks = Array.isArray(links);
|
||||||
const [opened, setOpened] = useState(initiallyOpened || false);
|
const [opened, setOpened] = useState(initiallyOpened || false);
|
||||||
@@ -78,9 +76,6 @@ export function LinksGroup({ icon: Icon, label, link, initiallyOpened, links })
|
|||||||
{link.label}
|
{link.label}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
));
|
));
|
||||||
|
|
||||||
console.log("TOP LINK: ", link)
|
|
||||||
|
|
||||||
if (link) {
|
if (link) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@@ -1,3 +1,17 @@
|
|||||||
body {
|
/* body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
} */
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
}
|
}
|
@@ -1,9 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
import './css/custom.css'
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
import { NotificationsProvider } from '@mantine/notifications';
|
import { NotificationsProvider } from '@mantine/notifications';
|
||||||
import { Notifications } from './Notifications.js'
|
import Notifications from './components/Notifications.js'
|
||||||
|
import { MantineProvider } from '@mantine/core';
|
||||||
|
|
||||||
//setup api
|
//setup api
|
||||||
//const backendPort = process.env.REACT_APP_BACKEND_PORT
|
//const backendPort = process.env.REACT_APP_BACKEND_PORT
|
||||||
@@ -11,10 +13,12 @@ import { Notifications } from './Notifications.js'
|
|||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<NotificationsProvider>
|
<MantineProvider withNormalizeCSS withGlobalStyles>
|
||||||
<Notifications />
|
<NotificationsProvider>
|
||||||
<App />
|
<Notifications />
|
||||||
</NotificationsProvider>
|
<App />
|
||||||
|
</NotificationsProvider>
|
||||||
|
</MantineProvider>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
@@ -15,5 +15,5 @@ export const locationFilterAtom = atom({})
|
|||||||
export const roomFilterAtom = atom({})
|
export const roomFilterAtom = atom({})
|
||||||
|
|
||||||
// Notification history and notification stack
|
// Notification history and notification stack
|
||||||
export const notificationHistory = atom([])
|
export const notificationHistoryAtom = atom([])
|
||||||
export const notifications = atomWithReset([])
|
export const notificationQueueAtom = atomWithReset([])
|
||||||
|
Reference in New Issue
Block a user