From 472fe457ed53b214c48c91f371dfeb54bf5015aa Mon Sep 17 00:00:00 2001 From: deranjer Date: Tue, 10 Jan 2023 22:49:59 -0500 Subject: [PATCH] updating frontend to new standards --- app/log/goInventorize.log | 87 +++++ frontend/src/App.js | 84 +++-- frontend/src/Notifications.js | 11 - frontend/src/components/AppHeader.js | 38 -- frontend/src/components/Notifications.js | 32 ++ frontend/src/components/PhotoDisplay.js | 120 +++++++ frontend/src/components/SideBar.js | 10 +- frontend/src/components/Utilities.js | 33 ++ frontend/src/components/cards/LocationCard.js | 18 +- .../{ => Locations}/LocationDetailsPage.js | 316 +++++++++-------- .../pages/{ => Locations}/LocationsPage.js | 150 ++++---- .../Locations/NewLocationForm.js} | 332 +++++++++--------- .../components/sidebar/SidebarLinksGroup.js | 11 +- frontend/src/css/custom.css | 16 +- frontend/src/index.js | 14 +- frontend/src/state/state.js | 4 +- 16 files changed, 774 insertions(+), 502 deletions(-) delete mode 100644 frontend/src/Notifications.js delete mode 100644 frontend/src/components/AppHeader.js create mode 100644 frontend/src/components/Notifications.js create mode 100644 frontend/src/components/PhotoDisplay.js create mode 100644 frontend/src/components/Utilities.js rename frontend/src/components/pages/{ => Locations}/LocationDetailsPage.js (82%) rename frontend/src/components/pages/{ => Locations}/LocationsPage.js (90%) rename frontend/src/components/{forms/LocationForm.js => pages/Locations/NewLocationForm.js} (94%) diff --git a/app/log/goInventorize.log b/app/log/goInventorize.log index 904f552..5091789 100644 --- a/app/log/goInventorize.log +++ b/app/log/goInventorize.log @@ -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:[]}]"} {"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​"}​ +{"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​"}​ diff --git a/frontend/src/App.js b/frontend/src/App.js index 44224b0..00eb211 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,23 +1,24 @@ import React, {useState, useEffect } from 'react'; import { useAtom } from 'jotai'; 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 LocationForm from './components/forms/LocationForm' +import LocationForm from './components/pages/Locations/NewLocationForm' import NotFound from './components/pages/NotFound'; import { BrowserRouter as Router, Routes, Route} from 'react-router-dom' -import { Modal, Button, Text, Group, TextInput, Loader, AppShell, MediaQuery } from '@mantine/core'; -import { useDebouncedValue, useLocalStorageValue } from '@mantine/hooks'; -import { showNotification } from '@mantine/notifications'; +import { Loader, AppShell, MediaQuery, Header, Center, Title, Burger, useMantineTheme } from '@mantine/core'; +// import { useDebouncedValue, useLocalStorageValue } from '@mantine/hooks'; 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 AppHeader from './components/AppHeader'; -import LocationDetailsPage from './components/pages/LocationDetailsPage'; +// import AppHeader from './components/AppHeader'; +import LocationDetailsPage from './components/pages/Locations/LocationDetailsPage'; @@ -25,52 +26,73 @@ function App() { // Main nav/sidebar appshell openend const [isLoading, setIsLoading] = useState(true) + const [opened, setOpened] = useState(false); + const [, setServerConfig] = useAtom(serverConfigAtom) + const [, setNotificationQueue] = useAtom(notificationQueueAtom) + const theme = useMantineTheme(); + // const navigate = useNavigate(); useEffect(() => { // navigate("/")// Reset to homepage on new load anywhere in the app - setIsLoading(true) - async function fetchSettings() { - backendAPI.get('/config').then(results => { + + const fetchSettings = async () => { + setIsLoading(true) + try { + const results = await backendAPI.get('/config') // console.log("FULL RESULTS: ", results.config.baseURL) results.data.baseURL = defaultURLS.baseURL console.log("CONFIG: ", results.data) setServerConfig(results.data) - showNotification({ - title: 'Backend Notice', - message: 'Config fetched from backend!', - color: "green" - }) - setIsLoading(false) - }).catch(err => { - showNotification({ - title: 'Backend Notice', - message: `Failed to connect to backend! ${err}`, - autoClose: false, - color: "red", - }) - setIsLoading(false) - }) - + const notification = generateNotification('success', 'Config Fetched', 'Config fetched from backend') + setNotificationQueue((currentQueue) => ([...currentQueue, notification])) + } catch(err) { + const notification = generateNotification('error', 'Failed to fetch config', `Failed to fetch config: ${err}`) + setNotificationQueue((currentQueue) => ([...currentQueue, notification])) + } + setIsLoading(false) } + fetchSettings(); - - }, []) + }, [setServerConfig, setNotificationQueue]) return ( + <> + {isLoading ?
: } - header={} + header={ +
+
+ + setOpened((o) => !o)} + size="sm" + color={theme.colors.gray[6]} + mr="xl" + /> + + + goInventorize + + + goInventorize + +
+
+ + } > } /> @@ -86,6 +108,8 @@ function App() {
+ } + ); } diff --git a/frontend/src/Notifications.js b/frontend/src/Notifications.js deleted file mode 100644 index eea2b01..0000000 --- a/frontend/src/Notifications.js +++ /dev/null @@ -1,11 +0,0 @@ - - -function Notifications() { - - - return( - <> - ) -} - -export default Notifications; \ No newline at end of file diff --git a/frontend/src/components/AppHeader.js b/frontend/src/components/AppHeader.js deleted file mode 100644 index 6a8b064..0000000 --- a/frontend/src/components/AppHeader.js +++ /dev/null @@ -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 ( -
- {/* You can handle other responsive styles with MediaQuery component or createStyles function */} -
- - setMenuOpened((o) => !o)} - size="sm" - color={theme.colors.gray[6]} - mr="xl" - /> - - - - <Text inherit variant="gradient" gradient={{ from: 'indigo', to: 'cyan', deg: 45 }}>goInventorize</Text> - - -
-
- ) -} - - - -export default AppHeader \ No newline at end of file diff --git a/frontend/src/components/Notifications.js b/frontend/src/components/Notifications.js new file mode 100644 index 0000000..bed9726 --- /dev/null +++ b/frontend/src/components/Notifications.js @@ -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 \ No newline at end of file diff --git a/frontend/src/components/PhotoDisplay.js b/frontend/src/components/PhotoDisplay.js new file mode 100644 index 0000000..d15a26a --- /dev/null +++ b/frontend/src/components/PhotoDisplay.js @@ -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 = + + + displayPhoto(fullUrl, photo)} + /> + + + {photo.photo_nm} + + {photo.network_location ? photo.network_location.location_nm : "No Location"} + + + + {photo?.photo_notes_txt ? photo.photo_notes_txt : "No Photo Notes!"} + + {photo.latitude_nbr !== 0 && photo.longitude_nbr !== 0 && + + + + } + + 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 ( + <> + setPhotoOpened(false)} title={photoDetails.name} size="full"> + {photoDetails.locationName}{photoDetails.name}: {photoDetails.name}} + withPlaceholder + /> + + Photos: {numPhotos} + + {rawPhotoArray} + + {photosLoading &&
} + + + ) +} + +export default PhotoDisplay \ No newline at end of file diff --git a/frontend/src/components/SideBar.js b/frontend/src/components/SideBar.js index 73c8017..f67d6e2 100644 --- a/frontend/src/components/SideBar.js +++ b/frontend/src/components/SideBar.js @@ -13,8 +13,8 @@ const sideBarData = [ links: [ { label: 'View Locations', link: '/locations' }, { label: 'Add New Location', link: '/locations/new' }, - { label: 'Outlook', link: '/' }, - { label: 'Real time', link: '/' }, + // { label: 'Outlook', link: '/' }, + // { label: 'Real time', link: '/' }, ], }, { @@ -24,11 +24,11 @@ const sideBarData = [ links: [ { label: 'View Rooms', link: '/rooms' }, { label: 'Add New Room', link: '/rooms/new' }, - { label: 'Releases schedule', link: '/'}, + // { label: 'Releases schedule', link: '/'}, ], }, - { label: 'Cabinets', icon: BiCabinet, link: "/" }, - { label: 'Items', icon: BsDiagram2, link: "/" }, + // { label: 'Cabinets', icon: BiCabinet, link: "/" }, + // { label: 'Items', icon: BsDiagram2, link: "/" }, { label: 'Settings', icon: GoGear, link: '/settings' }, ]; diff --git a/frontend/src/components/Utilities.js b/frontend/src/components/Utilities.js new file mode 100644 index 0000000..8bec69e --- /dev/null +++ b/frontend/src/components/Utilities.js @@ -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 = + break; + case "warning": + baseNotification.color = 'yellow' + baseNotification.icon = + break; + case "success": + baseNotification.color = 'green' + baseNotification.icon = + break; + default: + break; + } + console.log("MY NOTIFICATION: ", baseNotification) + return baseNotification +} \ No newline at end of file diff --git a/frontend/src/components/cards/LocationCard.js b/frontend/src/components/cards/LocationCard.js index 36af880..2a11e6b 100644 --- a/frontend/src/components/cards/LocationCard.js +++ b/frontend/src/components/cards/LocationCard.js @@ -1,8 +1,8 @@ import React from 'react'; 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/state'; +// import { useNavigate } from "react-router-dom"; +import { serverConfigAtom } from '../../state/state'; import { Link } from 'react-router-dom'; @@ -10,10 +10,6 @@ import { Link } from 'react-router-dom'; function LocationCard(props) { const {location, idx} = props const [serverConfig] = useAtom(serverConfigAtom) - const [, setRoomsFilter] = useAtom(roomFilterAtom) - const [, setActivePage] = useAtom(activePageAtom) - - const navigate = useNavigate(); const setRoomNumber = (rooms) => { 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 ( @@ -51,9 +42,8 @@ function LocationCard(props) { {location.Description} - - - + + ); diff --git a/frontend/src/components/pages/LocationDetailsPage.js b/frontend/src/components/pages/Locations/LocationDetailsPage.js similarity index 82% rename from frontend/src/components/pages/LocationDetailsPage.js rename to frontend/src/components/pages/Locations/LocationDetailsPage.js index d108e0e..5da00e3 100644 --- a/frontend/src/components/pages/LocationDetailsPage.js +++ b/frontend/src/components/pages/Locations/LocationDetailsPage.js @@ -1,148 +1,170 @@ -import React, {useState, useEffect } from 'react'; -import { useAtom } from 'jotai' -import { serverConfigAtom } from '../../state/state'; -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 ( - <> -
{ isLoading && }
-
{locationDetails.Name}
- - - - - - Description: - {locationDetails.Description} - - - - - - - Notes: - {locationDetails.Notes} - - - - - - - Address: - {locationDetails.Address} - - - - - - - Square Feet: - {locationDetails.SquareFeet} - - - - - - - Lat/Long: - {locationDetails.Latitude}/{locationDetails.Longitude} - - - - - - - Date Purchased: - {locationDetails.DatePurchased} - - - - - - - Purchase Price: - {locationDetails.PurchasePrice} - - - - - - - Current Value: - {locationDetails.CurrentValue} - - - - - - - - - ); -} - +import React, {useState, useEffect } from 'react'; +import { useAtom } from 'jotai' +import { serverConfigAtom } from '../../../state/state'; +import { BsDoorClosed } from 'react-icons/bs'; +import { BiEdit } from 'react-icons/bi'; +import { Loader, Center, Title, Group, Button, Space, Container, ThemeIcon, Text, Image, Accordion, Avatar } 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 ( + <> +
{ isLoading && }
+
{locationDetails.Name}
+ + + + + + Description: + {locationDetails.Description} + + + + + + + Notes: + {locationDetails.Notes} + + + + + + + Address: + {locationDetails.Address} + + + + + + + Square Feet: + {locationDetails.SquareFeet} + + + + + + + Lat/Long: + {locationDetails.Latitude}/{locationDetails.Longitude} + + + + + + + Date Purchased: + {locationDetails.DatePurchased} + + + + + + + Purchase Price: + {locationDetails.PurchasePrice} + + + + + + + Current Value: + {locationDetails.CurrentValue} + + + + + + + + + + + + + +
+ Rooms + + Rooms in this Location + +
+
+
+ + + +
+
+ + + ); +} + export default LocationDetailsPage; \ No newline at end of file diff --git a/frontend/src/components/pages/LocationsPage.js b/frontend/src/components/pages/Locations/LocationsPage.js similarity index 90% rename from frontend/src/components/pages/LocationsPage.js rename to frontend/src/components/pages/Locations/LocationsPage.js index 53d89b2..a64f7bf 100644 --- a/frontend/src/components/pages/LocationsPage.js +++ b/frontend/src/components/pages/Locations/LocationsPage.js @@ -1,76 +1,76 @@ -import React, {useState, useEffect } from 'react'; -import { useAtom } from 'jotai' -import { serverConfigAtom } from '../../state/state'; -import { HiPlus } from 'react-icons/hi' -import { Loader, Center, SimpleGrid, Title, Group, Button } from '@mantine/core' -import { useNavigate } from "react-router-dom"; -import { useNotifications } from '@mantine/notifications'; - - -import { backendAPI } from '../../services/backend-api'; -import LocationCard from '../cards/LocationCard'; - - -function LocationsPage() { - // const [opened, setOpened] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [locations, setLocations] = useState([]) - const [serverConfig] = useAtom(serverConfigAtom) - - let navigate = useNavigate(); - - - - const notifications = useNotifications(); - - - useEffect(() => { - console.log("LOADING LOCATIONS PAGE!") - setIsLoading(true) - async function fetchSettings() { - backendAPI.get('/locations').then(results => { - console.log("CONFIG IN LOCATIONS: ", serverConfig) - console.log("LOCATIONS: ", results.data) - setLocations(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(); - - - }, []) - - return ( - <> -
{ isLoading && }
-
Locations
- - - { locations.map((location, idx) => - - )} - - - - ); -} - +import React, {useState, useEffect } from 'react'; +import { useAtom } from 'jotai' +import { serverConfigAtom } from '../../../state/state'; +import { HiPlus } from 'react-icons/hi' +import { Loader, Center, SimpleGrid, Title, Group, Button } from '@mantine/core' +import { useNavigate } from "react-router-dom"; +import { useNotifications } from '@mantine/notifications'; + + +import { backendAPI } from '../../../services/backend-api'; +import LocationCard from '../../cards/LocationCard'; + + +function LocationsPage() { + // const [opened, setOpened] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [locations, setLocations] = useState([]) + const [serverConfig] = useAtom(serverConfigAtom) + + let navigate = useNavigate(); + + + + const notifications = useNotifications(); + + + useEffect(() => { + console.log("LOADING LOCATIONS PAGE!") + setIsLoading(true) + async function fetchSettings() { + backendAPI.get('/locations').then(results => { + console.log("CONFIG IN LOCATIONS: ", serverConfig) + console.log("LOCATIONS: ", results.data) + setLocations(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(); + + + }, []) + + return ( + <> +
{ isLoading && }
+
Locations
+ + + { locations.map((location, idx) => + + )} + + + + ); +} + export default LocationsPage; \ No newline at end of file diff --git a/frontend/src/components/forms/LocationForm.js b/frontend/src/components/pages/Locations/NewLocationForm.js similarity index 94% rename from frontend/src/components/forms/LocationForm.js rename to frontend/src/components/pages/Locations/NewLocationForm.js index 1ce17e6..c7e4403 100644 --- a/frontend/src/components/forms/LocationForm.js +++ b/frontend/src/components/pages/Locations/NewLocationForm.js @@ -1,166 +1,166 @@ -import React, {useState, useEffect} from 'react'; -import { Text, Title, TextInput, Button, NumberInput, Textarea, Grid, Group, useMantineTheme, MantineTheme, Image, SimpleGrid } from '@mantine/core' -import { DatePicker } from '@mantine/dates'; -import dayjs from 'dayjs'; -import { useForm } from '@mantine/form'; -import { useAtom } from 'jotai'; -import { serverConfigAtom } from '../../state/state' -import { backendAPI } from '../../services/backend-api'; -import { IMAGE_MIME_TYPE } from '@mantine/dropzone'; -import CustomDropZone from '../CustomDropZone'; - - - -function LocationForm(props) { - const {location, modify: bool} = props - const [opened, setOpened] = useState(false); - const [serverConfig] = useAtom(serverConfigAtom) - // Cover Photo - const [coverPhoto, setCoverPhoto] = useState(null) - // Additional Photos - const [additionalPhotos, setAdditionalPhotos] = useState([]) - - const theme = useMantineTheme(); - - - const form = useForm({ - initialValues: { - Name: '', - Description: '', - Notes: '', - Address: '', - SquareFeet: '', - Latitude: '', - Longitude: '', - DatePurchased: '', - PurchasePrice: '', - CurrentValue: '', - CoverPhoto: '', - AdditionalPhotos: [], - }, - - validate: { - Name: (value) => (/^\S+/.test(value) ? null : 'Invalid Name') - }, - }); - - - - const submitNewLocation = (values) => { - console.log("VALUES: ", values) - // let additionalPhotos = values.AdditionalPhotos - let formData = new FormData() - for (const [key, value] of Object.entries(values)) { - if (key === "AdditionalPhotos" && value.length > 0) { - value.forEach(photo => { - console.log("PHOTO VALUE: ", photo) - formData.append(key, photo) - }); - } - formData.append(key, value) - } - backendAPI.post("/locations/new", formData).then((result) => { - console.log("STATUS: ", result.status) - console.log("result: ", result.data) - }).catch(err => { - console.log("Error adding new location!", err) - }) - } - - - const handleFileAccept = (files) => { - form.setFieldValue('CoverPhoto', files[0]) - const imageUrl = URL.createObjectURL(files[0]); - setCoverPhoto( - URL.revokeObjectURL(imageUrl) }} - /> - ) - } - - const handleAdditionalPhotos = (files) => { - setAdditionalPhotos(oldPhotos => [...oldPhotos, ...files]) - // setAdditionalPhotos(files) - console.log("FORM VALUES: ",form.values) - const oldValues = form.values['AdditionalPhotos'] - form.setFieldValue('AdditionalPhotos', [...oldValues, ...files]) - console.log("NEW FORM VALUES: ", form.values) - - } - - const previews = additionalPhotos.map((file, index) => { - console.log("FILE IS: ", file) - const imageUrl = URL.createObjectURL(file); - return ( - URL.revokeObjectURL(imageUrl) }} - /> - ) - }) - - return ( - <> - Location Form -
submitNewLocation(values))}> - - - - - -