more database work, added search route, starting frontend new location form
This commit is contained in:
File diff suppressed because it is too large
Load Diff
113
database.go
113
database.go
@@ -6,71 +6,6 @@ import (
|
|||||||
"github.com/asdine/storm/v3"
|
"github.com/asdine/storm/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Location is a top level component that starts the containerization process
|
|
||||||
type Location struct {
|
|
||||||
ID int `storm:"id,increment,index"`
|
|
||||||
Name string `storm:"unique"`
|
|
||||||
Description string
|
|
||||||
CoverPhoto string // A "cover photo" for the location
|
|
||||||
Photos []string // A list of additional photos for the location
|
|
||||||
Rooms []Room
|
|
||||||
Notes string
|
|
||||||
Address string
|
|
||||||
SquareFeet int
|
|
||||||
Latitude string
|
|
||||||
Longitude string
|
|
||||||
DatePurchased string
|
|
||||||
PurchasePrice string
|
|
||||||
CurrentValue string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Room is a containerized area at a location
|
|
||||||
type Room struct {
|
|
||||||
ID int `storm:"id,increment,index"`
|
|
||||||
Name string
|
|
||||||
Description string
|
|
||||||
CoverPhoto string // A "cover photo" for the room
|
|
||||||
Photos []string // A list of additional photos for the room
|
|
||||||
Paint string // Details about the paint
|
|
||||||
Notes string
|
|
||||||
Cabinets []Cabinet
|
|
||||||
Items []Item
|
|
||||||
SquareFeet int
|
|
||||||
LocationID int //Which location room is assigned to
|
|
||||||
LocationName string // Location name room belongs to
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cabinet is a containerized area in a room
|
|
||||||
type Cabinet struct {
|
|
||||||
ID int `storm:"id, increment"`
|
|
||||||
RoomID int // Room ID that the cabinet is assigned to
|
|
||||||
RoomName string // Room name cabinet belongs to
|
|
||||||
CoverPhoto string // A "Cover photo" for this item
|
|
||||||
Items []Item // List of items in the cabinet
|
|
||||||
Name string
|
|
||||||
Notes string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Item is contained inside a room or cabinet
|
|
||||||
type Item struct {
|
|
||||||
ID int `storm:"id, increment"`
|
|
||||||
LocType string // Can be Room or Cabinet to show where it is stored
|
|
||||||
RoomID int // Room ID that item is assigned to (can also be in a cabinet)
|
|
||||||
RoomName string // Room name that item is assigned to
|
|
||||||
CabinetID int //The cabinet id that an item is assigned to
|
|
||||||
CabinetName string // The name of the cabinet it belongs to
|
|
||||||
CoverPhoto string // A "cover photo" for the item
|
|
||||||
Photos []string // A list of additional photos for the item
|
|
||||||
Count int // If item has duplicates
|
|
||||||
Name string
|
|
||||||
Product string
|
|
||||||
Serial string
|
|
||||||
Warranty string
|
|
||||||
Notes string
|
|
||||||
Files []string //filepath to any files relevant to this item
|
|
||||||
}
|
|
||||||
|
|
||||||
// Location Routes
|
// Location Routes
|
||||||
|
|
||||||
//SetupDatabase initializes the storm/bbolt database
|
//SetupDatabase initializes the storm/bbolt database
|
||||||
@@ -301,3 +236,51 @@ func (s *Server) DeleteItem(item Item) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Misc calls
|
||||||
|
|
||||||
|
type SearchResults struct {
|
||||||
|
Locations []Location `json:"locations"`
|
||||||
|
Rooms []Room `json:"rooms"`
|
||||||
|
Cabinets []Cabinet `json:"cabinets"`
|
||||||
|
Items []Item `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchAllByField searches the entire database (locs, rooms, cabinets, items) by the field and search term passed in
|
||||||
|
func (s *Server) SearchAllByField(fieldFilter string, searchTerm string) (results SearchResults, err error) {
|
||||||
|
s.Log.Info().Msgf("Searching all items for term: %s", searchTerm)
|
||||||
|
var locs []Location
|
||||||
|
var rooms []Room
|
||||||
|
var cabinets []Cabinet
|
||||||
|
var items []Item
|
||||||
|
results = SearchResults{}
|
||||||
|
|
||||||
|
err = s.Database.Find(fieldFilter, searchTerm, &locs)
|
||||||
|
if err != nil && err != storm.ErrNotFound {
|
||||||
|
s.Log.Error().Msgf("Unable to search locations in database with term: %s :error: %s ", searchTerm, err)
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
err = s.Database.Find(fieldFilter, searchTerm, &rooms)
|
||||||
|
if err != nil && err != storm.ErrNotFound {
|
||||||
|
s.Log.Error().Msgf("Unable to search rooms in database with term: %s error: %s ", searchTerm, err)
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
err = s.Database.Find(fieldFilter, searchTerm, &cabinets)
|
||||||
|
if err != nil && err != storm.ErrNotFound {
|
||||||
|
s.Log.Error().Msgf("Unable to search cabinets in database with term: %s error: %s ", searchTerm, err)
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
err = s.Database.Find(fieldFilter, searchTerm, &items)
|
||||||
|
if err != nil && err != storm.ErrNotFound {
|
||||||
|
s.Log.Error().Msgf("Unable to search items in database with term: %s error: %s ", searchTerm, err)
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
results.Locations = locs
|
||||||
|
results.Rooms = rooms
|
||||||
|
results.Cabinets = cabinets
|
||||||
|
results.Items = items
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
67
database_structs.go
Normal file
67
database_structs.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Location is a top level component that starts the containerization process
|
||||||
|
type Location struct {
|
||||||
|
ID int `storm:"id,increment,index"`
|
||||||
|
Name string `storm:"unique"`
|
||||||
|
Description string
|
||||||
|
CoverPhoto string // A "cover photo" for the location
|
||||||
|
Photos []string // A list of additional photos for the location
|
||||||
|
Rooms []Room
|
||||||
|
Notes string
|
||||||
|
Address string
|
||||||
|
SquareFeet int
|
||||||
|
Latitude string
|
||||||
|
Longitude string
|
||||||
|
DatePurchased string
|
||||||
|
PurchasePrice string
|
||||||
|
CurrentValue string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Room is a containerized area at a location
|
||||||
|
type Room struct {
|
||||||
|
ID int `storm:"id,increment,index"`
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
CoverPhoto string // A "cover photo" for the room
|
||||||
|
Photos []string // A list of additional photos for the room
|
||||||
|
Paint string // Details about the paint
|
||||||
|
Notes string
|
||||||
|
Cabinets []Cabinet
|
||||||
|
Items []Item
|
||||||
|
SquareFeet int
|
||||||
|
LocationID int //Which location room is assigned to
|
||||||
|
LocationName string // Location name room belongs to
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cabinet is a containerized area in a room
|
||||||
|
type Cabinet struct {
|
||||||
|
ID int `storm:"id, increment"`
|
||||||
|
RoomID int // Room ID that the cabinet is assigned to
|
||||||
|
RoomName string // Room name cabinet belongs to
|
||||||
|
CoverPhoto string // A "Cover photo" for this item
|
||||||
|
Items []Item // List of items in the cabinet
|
||||||
|
Name string
|
||||||
|
Notes string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item is contained inside a room or cabinet
|
||||||
|
type Item struct {
|
||||||
|
ID int `storm:"id, increment"`
|
||||||
|
LocType string // Can be Room or Cabinet to show where it is stored
|
||||||
|
RoomID int // Room ID that item is assigned to (can also be in a cabinet)
|
||||||
|
RoomName string // Room name that item is assigned to
|
||||||
|
CabinetID int //The cabinet id that an item is assigned to
|
||||||
|
CabinetName string // The name of the cabinet it belongs to
|
||||||
|
CoverPhoto string // A "cover photo" for the item
|
||||||
|
Photos []string // A list of additional photos for the item
|
||||||
|
Count int // If item has duplicates
|
||||||
|
Name string
|
||||||
|
Product string
|
||||||
|
Serial string
|
||||||
|
Warranty string
|
||||||
|
Notes string
|
||||||
|
DatePurchased string
|
||||||
|
Files []string //filepath to any files relevant to this item
|
||||||
|
}
|
37
frontend/package-lock.json
generated
37
frontend/package-lock.json
generated
@@ -2030,11 +2030,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@mantine/core": {
|
"@mantine/core": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mantine/core/-/core-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@mantine/core/-/core-4.1.1.tgz",
|
||||||
"integrity": "sha512-uEHGssKveDgGHhbBrgVqp8o12m0oQGLBKH3D8bkrca1GB905ZHuj8CG+i/ojBhqdalfUwqcYGRpwwALtD+XfJg==",
|
"integrity": "sha512-7eTv/vXKX863ri3c6zVbxozsAeA8B0iN31j3/dKs0K3y5Vd9+cgAy59WQk2RBGRlWdln7gtLnELePyQz+WlXLQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@mantine/styles": "4.1.0",
|
"@mantine/styles": "4.1.1",
|
||||||
"@popperjs/core": "^2.9.3",
|
"@popperjs/core": "^2.9.3",
|
||||||
"@radix-ui/react-scroll-area": "^0.1.1",
|
"@radix-ui/react-scroll-area": "^0.1.1",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
@@ -2043,9 +2043,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@mantine/dates": {
|
"@mantine/dates": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-4.1.1.tgz",
|
||||||
"integrity": "sha512-kdKQ9PRGbYe1YhRlqWVUVGG7IKHjdIWMX3XhSFbWWLkfUxqSG4dhwNeAZIcbHXDZhFT9xIifFlBlDiN7JIOmtA==",
|
"integrity": "sha512-IUATTmne8sE4CwUngDS11zXk9xrpDuyBZSJ2LmHa0aiIm2GUTloPutLyK7zY2umaJVoRok6ji9Nlud7eEEAyYg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@popperjs/core": "^2.9.3",
|
"@popperjs/core": "^2.9.3",
|
||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
@@ -2060,10 +2060,15 @@
|
|||||||
"react-dropzone": "^11.4.2"
|
"react-dropzone": "^11.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@mantine/form": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mantine/form/-/form-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-Kh7lJ+JcLynCxpqUDmc5zpj/Jd/ZLpHCKnGM4TnfhV45lRlGhDQQtteKJ99XneYu2s23EhsLXfBIhD4RadHZ8g=="
|
||||||
|
},
|
||||||
"@mantine/hooks": {
|
"@mantine/hooks": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-4.1.1.tgz",
|
||||||
"integrity": "sha512-bvVvHDDfYqdIxl7n+lBBwSIkcqInv6kK+fxvEfFBOUEzN6KbpJaPdaYXP788x64uFbA7AODQjdtOhk3tMyQA8Q=="
|
"integrity": "sha512-uVLVT1Qc7rt3kXy25efUVc509Uv3HPNPMyJaPEGM+o9RWy0k3Mo5Zp3meVfUL7PmjY179RlHmrGtxprdpiDZaA=="
|
||||||
},
|
},
|
||||||
"@mantine/notifications": {
|
"@mantine/notifications": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
@@ -2075,9 +2080,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@mantine/styles": {
|
"@mantine/styles": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mantine/styles/-/styles-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@mantine/styles/-/styles-4.1.1.tgz",
|
||||||
"integrity": "sha512-mWFNSE5DqE/5C9TDYIkQVbTef8QyWnW8a8MADHl6S52K0iGU8nqw4jnyozB+6y9JEIxdl+sp0o16L2JnAsq7bA==",
|
"integrity": "sha512-ZTfjqugTrqAQatgmdA0R+smUCIwPZCHykxKA5HwQzuJZh6/9TwaPozdTP7yOKAVyjBDnnAQda2PPZ1Gz1PrXqg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@emotion/cache": "^11.7.1",
|
"@emotion/cache": "^11.7.1",
|
||||||
"@emotion/react": "^11.7.1",
|
"@emotion/react": "^11.7.1",
|
||||||
@@ -13172,9 +13177,9 @@
|
|||||||
"integrity": "sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw=="
|
"integrity": "sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw=="
|
||||||
},
|
},
|
||||||
"use-isomorphic-layout-effect": {
|
"use-isomorphic-layout-effect": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz",
|
||||||
"integrity": "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ=="
|
"integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA=="
|
||||||
},
|
},
|
||||||
"use-latest": {
|
"use-latest": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
@@ -3,10 +3,11 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mantine/core": "^4.1.0",
|
"@mantine/core": "^4.1.1",
|
||||||
"@mantine/dates": "^4.1.0",
|
"@mantine/dates": "^4.1.1",
|
||||||
"@mantine/dropzone": "^4.1.0",
|
"@mantine/dropzone": "^4.1.0",
|
||||||
"@mantine/hooks": "^4.1.0",
|
"@mantine/form": "^4.1.1",
|
||||||
|
"@mantine/hooks": "^4.1.1",
|
||||||
"@mantine/notifications": "^4.1.0",
|
"@mantine/notifications": "^4.1.0",
|
||||||
"@testing-library/jest-dom": "^5.16.3",
|
"@testing-library/jest-dom": "^5.16.3",
|
||||||
"@testing-library/react": "^12.1.4",
|
"@testing-library/react": "^12.1.4",
|
||||||
|
61
frontend/src/components/cards/LocationCard.js
Normal file
61
frontend/src/components/cards/LocationCard.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
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/main';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return <Text>0 Rooms</Text>
|
||||||
|
} else if (rooms.length > 1) {
|
||||||
|
return <Text>{rooms.length} Rooms</Text>
|
||||||
|
} else {
|
||||||
|
return <Text>1 Room</Text>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const navigateToRooms = (locationID, locationName) => {
|
||||||
|
setActivePage("rooms")
|
||||||
|
setRoomsFilter({"filterType": "location", "locationID": locationID, "commonName": "location", "metadata": locationName})
|
||||||
|
navigate("/rooms")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card key={`${idx} - ${location.ID}`} shadow="md" padding="md">
|
||||||
|
<Card.Section>
|
||||||
|
{location.CoverPhoto ? <Image src={`${serverConfig.baseURL}/photos/locations/${location.Name}/${location.CoverPhoto}`}></Image> : <Text>No Photo</Text>}
|
||||||
|
<Group position='right' style={{position: 'absolute', top: '5px', right: '5px', backgroundColor: 'grey', borderRadius: '25%'}}>
|
||||||
|
<Menu>
|
||||||
|
<Menu.Label>Edit</Menu.Label>
|
||||||
|
<Menu.Label>Delete</Menu.Label>
|
||||||
|
</Menu>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
</Card.Section>
|
||||||
|
<Group position="apart">
|
||||||
|
<Text weight={500}>{location.Name}</Text>
|
||||||
|
<Badge variant="light">
|
||||||
|
{ setRoomNumber(location.Rooms) }
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
<Text size="sm">{location.Description}</Text>
|
||||||
|
<Group>
|
||||||
|
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Details</Button>
|
||||||
|
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Rooms</Button>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LocationCard;
|
63
frontend/src/components/cards/RoomCard.js
Normal file
63
frontend/src/components/cards/RoomCard.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
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/main';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function RoomCard(props) {
|
||||||
|
const {room, idx} = props
|
||||||
|
const [serverConfig] = useAtom(serverConfigAtom)
|
||||||
|
const [, setRoomsFilter] = useAtom(roomFilterAtom)
|
||||||
|
const [, setActivePage] = useAtom(activePageAtom)
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const setItemAndCabinetNumber = (category, name) => {
|
||||||
|
if (category === null) {
|
||||||
|
return <Text>0 {name}s</Text>
|
||||||
|
} else if (category.length > 1) {
|
||||||
|
return <Text>{category.length} {name}s</Text>
|
||||||
|
} else {
|
||||||
|
return <Text>1 {name}</Text>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const navigateToCabinets = (roomID, roomName) => {
|
||||||
|
setActivePage("cabinets")
|
||||||
|
setRoomsFilter({"filterType": "room", "roomID": roomID, "commonName": "room", "metadata": roomName})
|
||||||
|
navigate("/cabinets")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card key={`${idx} - ${room.ID}`} component="a" onClick={(e) => {}} shadow="sm" padding="md">
|
||||||
|
<Card.Section>
|
||||||
|
{room.CoverPhoto ? <Image src={`${serverConfig.baseURL}/photos/locations/${room.CoverPhoto}`}></Image> : <Text>No Photo</Text>}
|
||||||
|
</Card.Section>
|
||||||
|
<Group position="apart">
|
||||||
|
<Text weight={500}>{room.Name}</Text>
|
||||||
|
<Group>
|
||||||
|
<Badge color="pink" variant="light">
|
||||||
|
{room.LocationName}
|
||||||
|
</Badge>
|
||||||
|
<Badge variant='light'>
|
||||||
|
{setItemAndCabinetNumber(room.Cabinets, "Cabinet")}
|
||||||
|
</Badge>
|
||||||
|
<Badge color='teal' variant='light'>
|
||||||
|
{setItemAndCabinetNumber(room.Items, "Item")}
|
||||||
|
</Badge>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
<Text size="sm">{room.Description}</Text>
|
||||||
|
<Group>
|
||||||
|
<Button onClick={() => console.log("test")}>View Details</Button>
|
||||||
|
<Button onClick={() => navigateToCabinets(room.id, room.Name)}>View Cabinets</Button>
|
||||||
|
<Button onClick={() => console.log("test")}>View Items</Button>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RoomCard;
|
26
frontend/src/components/forms/LocationForm.js
Normal file
26
frontend/src/components/forms/LocationForm.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import React, {useState, useEffect} from 'react';
|
||||||
|
import { Text, Title, TextInput, Button, NumberInput, } from '@mantine/core'
|
||||||
|
import { DatePicker } from '@mantine/dates';
|
||||||
|
import { useForm } from '@mantine/form';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
|
import { serverConfigAtom } from '../../state/main'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function LocationForm(props) {
|
||||||
|
const {location, modify: bool} = props
|
||||||
|
const [opened, setOpened] = useState(false);
|
||||||
|
const [serverConfig] = useAtom(serverConfigAtom)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title>Location Form</Title>
|
||||||
|
<Text>This is the homepage!</Text>
|
||||||
|
</>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LocationForm;
|
@@ -1,5 +1,4 @@
|
|||||||
import React, {useState, useEffect, useContext, createContext} from 'react';
|
import React, {useState, useEffect, useContext, createContext} from 'react';
|
||||||
import APIContext from '../../App';
|
|
||||||
import { Text } from '@mantine/core'
|
import { Text } from '@mantine/core'
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { serverConfigAtom } from '../../state/main'
|
import { serverConfigAtom } from '../../state/main'
|
||||||
|
@@ -1,25 +1,26 @@
|
|||||||
import React, {useState, useEffect, useContext, createContext} from 'react';
|
import React, {useState, useEffect } from 'react';
|
||||||
import { useAtom } from 'jotai'
|
import { useAtom } from 'jotai'
|
||||||
import { activePageAtom, roomFilterAtom, serverConfigAtom } from '../../state/main';
|
import { serverConfigAtom } from '../../state/main';
|
||||||
import { Text, Loader, Center, Card, Image, Badge, Button, SimpleGrid, Group, Title } from '@mantine/core'
|
import { HiPlus } from 'react-icons/hi'
|
||||||
import { useNavigate } from "react-router-dom";
|
import { Loader, Center, SimpleGrid, Title, Group, Button, Divider } from '@mantine/core'
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
|
|
||||||
function LocationsPage(props) {
|
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)
|
||||||
const [, setRoomsFilter] = useAtom(roomFilterAtom)
|
|
||||||
const [, setActivePage] = useAtom(activePageAtom)
|
|
||||||
|
|
||||||
|
|
||||||
const notifications = useNotifications();
|
const notifications = useNotifications();
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("LOADING LOCATIONS PAGE!")
|
console.log("LOADING LOCATIONS PAGE!")
|
||||||
@@ -46,27 +47,12 @@ function LocationsPage(props) {
|
|||||||
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const navigateToRooms = (locationID, locationName) => {
|
|
||||||
setActivePage("rooms")
|
|
||||||
setRoomsFilter({"filterType": "location", "locationID": locationID, "commonName": "location", "metadata": locationName})
|
|
||||||
navigate("/rooms")
|
|
||||||
}
|
|
||||||
|
|
||||||
const setRoomNumber = (location) => {
|
|
||||||
if (location.Rooms === null) {
|
|
||||||
return <Text>0 Rooms</Text>
|
|
||||||
} else if (location.Rooms.length > 1) {
|
|
||||||
return <Text>{location.Rooms.length} Rooms</Text>
|
|
||||||
} else {
|
|
||||||
return <Text>1 Room</Text>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
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 leftIcon={<HiPlus />}>Add New Location</Button>
|
||||||
|
<Divider />
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
spacing="md"
|
spacing="md"
|
||||||
cols={4}
|
cols={4}
|
||||||
@@ -77,25 +63,9 @@ function LocationsPage(props) {
|
|||||||
]}
|
]}
|
||||||
|
|
||||||
>
|
>
|
||||||
{ locations.map((location, idx) =>
|
{ locations.map((location, idx) =>
|
||||||
<Card key={`${idx} - ${location.ID}`} shadow="md" padding="md">
|
<LocationCard location={location} idx={idx}></LocationCard>
|
||||||
<Card.Section>
|
)}
|
||||||
{location.CoverPhoto ? <Image src={`${serverConfig.baseURL}/photos/locations/${location.Name}/${location.CoverPhoto}`}></Image> : <Text>No Photo</Text>}
|
|
||||||
</Card.Section>
|
|
||||||
<Group position="apart">
|
|
||||||
<Text weight={500}>{location.Name}</Text>
|
|
||||||
<Badge variant="light">
|
|
||||||
{ setRoomNumber(location) }
|
|
||||||
</Badge>
|
|
||||||
</Group>
|
|
||||||
<Text size="sm">{location.Description}</Text>
|
|
||||||
<Group>
|
|
||||||
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Details</Button>
|
|
||||||
<Button onClick={() => navigateToRooms(location.ID, location.Name)}>View Rooms</Button>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ import { useLocation } 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 RoomCard from '../cards/RoomCard';
|
||||||
|
|
||||||
|
|
||||||
function RoomsPage(props) {
|
function RoomsPage(props) {
|
||||||
@@ -27,7 +28,7 @@ function RoomsPage(props) {
|
|||||||
console.log("CURRENT STATE: ", state)
|
console.log("CURRENT STATE: ", state)
|
||||||
// if we are filtering by location
|
// if we are filtering by location
|
||||||
if (roomFilter.locationID) {
|
if (roomFilter.locationID) {
|
||||||
url = `rooms/${roomFilter.locationID}`
|
url = `locations/${roomFilter.locationID}/rooms`
|
||||||
}
|
}
|
||||||
backendAPI.get(url).then(results => {
|
backendAPI.get(url).then(results => {
|
||||||
console.log("ROOMS: ", results)
|
console.log("ROOMS: ", results)
|
||||||
@@ -50,10 +51,10 @@ function RoomsPage(props) {
|
|||||||
}, [roomFilter])
|
}, [roomFilter])
|
||||||
|
|
||||||
// Check if filter changes for this page and refresh results
|
// Check if filter changes for this page and refresh results
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
console.log("CURRENT ROOM FILTER: ", roomFilter)
|
// console.log("CURRENT ROOM FILTER: ", roomFilter)
|
||||||
|
|
||||||
}, [roomFilter, setRoomFilter])
|
// }, [roomFilter, setRoomFilter])
|
||||||
|
|
||||||
const removeFilterButton = (
|
const removeFilterButton = (
|
||||||
<ActionIcon size="xs" color="blue" radius="xl" variant="transparent">
|
<ActionIcon size="xs" color="blue" radius="xl" variant="transparent">
|
||||||
@@ -83,30 +84,7 @@ function RoomsPage(props) {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{ rooms.map((room, idx) =>
|
{ rooms.map((room, idx) =>
|
||||||
<Card key={`${room.ID}`} component="a" onClick={(e) => {}} shadow="sm" padding="md">
|
<RoomCard room={room} idx={idx}></RoomCard>
|
||||||
<Card.Section>
|
|
||||||
{room.CoverPhoto ? <Image src={`${serverConfig.baseURL}/photos/locations/${room.CoverPhoto}`}></Image> : <Text>No Photo</Text>}
|
|
||||||
</Card.Section>
|
|
||||||
<Group position="apart">
|
|
||||||
<Text weight={500}>{room.Name}</Text>
|
|
||||||
<Group>
|
|
||||||
<Badge color="pink" variant="light">
|
|
||||||
{room.LocationName}
|
|
||||||
</Badge>
|
|
||||||
<Badge variant='light'>
|
|
||||||
{room.Cabinets === null ? 0 : room.Cabinets.length} Cabinets
|
|
||||||
</Badge>
|
|
||||||
<Badge color='teal' variant='light'>
|
|
||||||
{room.Cabinets === null ? 0 : room.Cabinets.length} Items
|
|
||||||
</Badge>
|
|
||||||
</Group>
|
|
||||||
</Group>
|
|
||||||
<Text size="sm">{room.Description}</Text>
|
|
||||||
<Group>
|
|
||||||
<Button onClick={() => console.log("test")}>View Details</Button>
|
|
||||||
<Button onClick={() => console.log("test")}>View Rooms</Button>
|
|
||||||
</Group>
|
|
||||||
</Card>
|
|
||||||
)}
|
)}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</>
|
</>
|
||||||
|
@@ -19,6 +19,20 @@ func (s *Server) GetAllItemsHandler(c *fiber.Ctx) error {
|
|||||||
return c.Status(fiber.StatusOK).JSON(items)
|
return c.Status(fiber.StatusOK).JSON(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetItemHandler returns a single item from an ID
|
||||||
|
func (s *Server) GetItemHandler(c *fiber.Ctx) error {
|
||||||
|
itemID, err := c.ParamsInt("itemID")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
room, err := s.GetItem(itemID)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Err(err).Msgf("Unable to fetch item with id: %d", itemID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusOK).JSON(room)
|
||||||
|
}
|
||||||
|
|
||||||
// GetAllItemsAtRoomHandler gets all items in a room
|
// GetAllItemsAtRoomHandler gets all items in a room
|
||||||
func (s *Server) GetAllItemsAtRoomHandler(c *fiber.Ctx) error {
|
func (s *Server) GetAllItemsAtRoomHandler(c *fiber.Ctx) error {
|
||||||
roomID, err := c.ParamsInt("roomID")
|
roomID, err := c.ParamsInt("roomID")
|
||||||
|
22
handlers_other.go
Normal file
22
handlers_other.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SearchAllByField searches all of the DB by specified field
|
||||||
|
func (s *Server) SearchAllHandler(c *fiber.Ctx) error {
|
||||||
|
searchField := c.Query("field", "Name")
|
||||||
|
searchTerm := c.Query("term")
|
||||||
|
|
||||||
|
s.Log.Debug().Msgf("Search by term: %s in all items at field: %s", searchTerm, searchField)
|
||||||
|
results, err := s.SearchAllByField(searchField, searchTerm)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Error().Msgf("SearchAllByField failed to return results: %s ", err)
|
||||||
|
return c.Status(fiber.StatusInternalServerError).SendString(fmt.Sprintf("Database failure! error: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(results)
|
||||||
|
}
|
8
main.go
8
main.go
@@ -110,17 +110,19 @@ func main() {
|
|||||||
// Cabinet Routes
|
// Cabinet Routes
|
||||||
server.WebServer.Get("/api/cabinets", server.GetAllCabinetsHandler)
|
server.WebServer.Get("/api/cabinets", server.GetAllCabinetsHandler)
|
||||||
server.WebServer.Get("/api/cabinets/:cabinetID", server.GetCabinetHandler)
|
server.WebServer.Get("/api/cabinets/:cabinetID", server.GetCabinetHandler)
|
||||||
|
server.WebServer.Post("/api/cabinets/:roomID/new", server.AddNewCabinetHandler)
|
||||||
server.WebServer.Get("/api/cabinets/:cabinetID/items", server.GetAllItemsAtCabinetHandler)
|
server.WebServer.Get("/api/cabinets/:cabinetID/items", server.GetAllItemsAtCabinetHandler)
|
||||||
server.WebServer.Delete("/api/cabinets/:cabinetID", server.DeleteCabinetHandler)
|
server.WebServer.Delete("/api/cabinets/:cabinetID", server.DeleteCabinetHandler)
|
||||||
|
|
||||||
// Item Routes
|
// Item Routes
|
||||||
server.WebServer.Get("/api/items", server.GetAllItemsHandler)
|
server.WebServer.Get("/api/items", server.GetAllItemsHandler)
|
||||||
server.WebServer.Get("/api/items/room/:roomID", server.GetAllItemsAtRoomHandler)
|
server.WebServer.Get("/api/items/:itemID", server.GetItemHandler)
|
||||||
server.WebServer.Get("/api/items/cabinet/:cabinetID", server.GetAllItemsAtCabinetHandler)
|
|
||||||
server.WebServer.Post("/api/items/cabinet/:cabinetID/new", server.AddNewItemHandler)
|
server.WebServer.Post("/api/items/cabinet/:cabinetID/new", server.AddNewItemHandler)
|
||||||
server.WebServer.Post("/api/items/room/:roomID/new", server.AddNewItemHandler)
|
server.WebServer.Post("/api/items/room/:roomID/new", server.AddNewItemHandler)
|
||||||
server.WebServer.Delete("/api/items/:itemID", server.DeleteItemHandler)
|
server.WebServer.Delete("/api/items/:itemID", server.DeleteItemHandler)
|
||||||
|
// Misc Routes
|
||||||
|
server.WebServer.Get("/api/search/all", server.SearchAllHandler)
|
||||||
|
|
||||||
|
// Start Server
|
||||||
fmt.Println("Everything ready, starting server! ", fmt.Sprintf(":%s", server.Config.Server.Port))
|
fmt.Println("Everything ready, starting server! ", fmt.Sprintf(":%s", server.Config.Server.Port))
|
||||||
server.WebServer.Listen(fmt.Sprintf(":%s", server.Config.Server.Port))
|
server.WebServer.Listen(fmt.Sprintf(":%s", server.Config.Server.Port))
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user