frontend to backend communication working, starting on locations page

This commit is contained in:
2021-12-08 23:11:50 -05:00
parent ce38e21dca
commit 41f6b5873c
18 changed files with 341 additions and 124 deletions

21
frontend/apis/backend.js Normal file
View File

@@ -0,0 +1,21 @@
import React, {useState, useEffect, useContext } from 'react';
import APIContext from '../../App';
import { Text } from '@mantine/core'
import useSwr from 'swr';
function BackendAPI() {
const serverConfig = useContext(APIContext);
return (
<>
<Text>This is the homepage!</Text>
</>
);
}
export default BackendAPI;

View File

@@ -10672,11 +10672,6 @@
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz",
"integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA=="
},
"ky": {
"version": "0.28.7",
"resolved": "https://registry.npmjs.org/ky/-/ky-0.28.7.tgz",
"integrity": "sha512-a23i6qSr/ep15vdtw/zyEQIDLoUaKDg9Jf04CYl/0ns/wXNYna26zJpI+MeIFaPeDvkrjLPrKtKOiiI3IE53RQ=="
},
"language-subtag-registry": {
"version": "0.3.21",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz",

View File

@@ -13,7 +13,6 @@
"@testing-library/user-event": "^13.5.0",
"base-64": "^1.0.0",
"dayjs": "^1.10.7",
"ky": "^0.28.7",
"npm-check-updates": "^12.0.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",

View File

@@ -1,112 +1,88 @@
import React, {useState, useEffect, useContext, createContext} from 'react';
import base64 from 'base-64';
import Home from './components/pages/Home';
import React, {useState, useEffect, createContext} from 'react';
import HomePage from './components/pages/HomePage';
import Locations from './components/pages/Locations'
import { Modal, Button, Text, Group, TextInput, Loader, AppShell } from '@mantine/core';
import { useDebouncedValue, useLocalStorageValue } from '@mantine/hooks';
import { useNotifications } from '@mantine/notifications';
import { backendAPI } from './services/backend-api';
import SideBar from './components/SideBar';
import AppHeader from './components/AppHeader';
const defaultSettings = {"serverURL": window.location.href.slice(0, -1), "Timezone": "America/Detroit"}
const APIContext = createContext();
function App() {
// Main nav/sidebar appshell openend
const [shellOpened, setShellOpened] = useState(false)
const [portModalOpen, setPortModalOpen] = useState(false)
const [authModalOpen, setAuthModalOpen] = useState(false)
const [isLoading, setIsLoading] = useState(true)
const [currentPage, setCurrentPage] = useState("home")
// json.stringify({"serverURL": window.location.href.slice(0, -1), "Timezone": "America/Detroit"})
const [serverConfigLS, setServerConfigLS] = useLocalStorageValue({key: 'serverConfig', defaultValue: JSON.stringify(defaultSettings)})
const [serverConfig, setServerConfig] = useState(defaultSettings)
const [serverConfig, setServerConfig] = useState({})
const [fullServerURL] = useDebouncedValue(serverConfig["serverURL"], 800);
const notifications = useNotifications();
//headers.append("Authorization", "Basic " + base64.encode("user:password"));
const user = "admin"
const password = "password"
useEffect(() => {
console.log("Checking local storage for server settings: ", JSON.parse(serverConfigLS))
if(JSON.parse(serverConfigLS)) {
setServerConfig(JSON.parse(serverConfigLS))
}
}, [serverConfigLS, setServerConfig])
useEffect(() => {
setPortModalOpen(false)
const lastChar = fullServerURL[fullServerURL.length - 1]
let newURL = fullServerURL
if (lastChar === "/") {
newURL = fullServerURL.slice(0, -1)
}
console.log("Auto-discover URL attempted: ", `${newURL}/config`)
fetch(`${newURL}/config`)
.then(response => {
console.log("RESPONSE: ", response)
if (!response.ok) {
console.log("Server not responding as expected, this should not happen!")
setPortModalOpen(true)
} else {
response.json().then(data => {
console.log("Connected to Server! ", data)
setServerConfig({...serverConfig, "Timezone": data["Timezone"], "BasicAuth": data["BasicAuth"]})
setServerConfigLS(JSON.stringify({...serverConfig, "Timezone": data["Timezone"], "BasicAuth": data["BasicAuth"]}))
setPortModalOpen(false)
setIsLoading(false)
}).catch(err => {
console.log("Server URL is incorrect, please change! ", err)
setPortModalOpen(true)
})
}
setIsLoading(true)
async function fetchSettings() {
backendAPI.get('/config').then(results => {
console.log("CONFIG: ", results.data)
results.data.baseURL = results.config.baseURL
console.log("CONFIG: ", results.data)
setServerConfig(results.data)
notifications.showNotification({
title: 'Backend Notice',
message: 'Config fetched from backend!',
color: "green"
})
setIsLoading(false)
}).catch(err => {
console.log("Server URL is incorrect, please change! ", err)
setPortModalOpen(true)
notifications.showNotification({
title: 'Backend Notice',
message: `Failed to connect to backend! ${err}`,
autoClose: false,
color: "red",
})
setIsLoading(false)
})
}, [fullServerURL])
useEffect(() => {
console.log("Loading Server Config: ", serverConfig)
if (serverConfig["BasicAuth"]) {
console.log("Looks like auth is required!")
//setAuthModalOpen(true)
}
}, [serverConfig])
fetchSettings();
}, [])
function showPage() {
switch (currentPage) {
case "home":
return <HomePage />
case "locations":
return <Locations />
default:
break;
}
}
return (
<APIContext.Provider value={{...serverConfig}}>
<AppShell
navbarOffsetBreakpoint="sm" // navbarOffsetBreakpoint controls when navbar should no longer be offset with padding-left
fixed // fixed prop on AppShell will be automatically added to Header and Navbar
navbar={<SideBar opened={shellOpened}/>}
header={<AppHeader opened={shellOpened} setOpened={setShellOpened}/>}
>
<Text>Resize app to see responsive navbar in action</Text>
</AppShell>
<Modal
opened={portModalOpen}
onClose={() => setPortModalOpen(false)}
title="Communication Failed, server URL may not be what was expected!"
>
<Text>Guessed Server URL: {serverConfig["serverURL"]} does not appear to be correct, please enter correct url.</Text>
<TextInput label="Server URL" value={serverConfig["serverURL"]} onChange={(e) => setServerConfig({...serverConfig, "serverURL": e.currentTarget.value})} required></TextInput>
</Modal>
<Modal opened={authModalOpen} onClose={() => setAuthModalOpen(false)} title="Please Login!">
<TextInput label="UserName" value={serverConfig["serverURL"]} onChange={(e) => setServerConfig({"ServerURL": e.currentTarget.value})} required></TextInput>
<TextInput label="UserName" type="password" value={serverConfig["serverURL"]} onChange={(e) => setServerConfig({"ServerURL": e.currentTarget.value})} required></TextInput>
</Modal>
{isLoading ? <Group position="center"><Loader size="xl"></Loader></Group> : <div>Welcome to goInventorize!</div>}
<AppShell
navbarOffsetBreakpoint="sm" // navbarOffsetBreakpoint controls when navbar should no longer be offset with padding-left
fixed // fixed prop on AppShell will be automatically added to Header and Navbar
navbar={<SideBar opened={shellOpened} setCurrentPage={setCurrentPage}/>}
header={<AppHeader opened={shellOpened} setOpened={setShellOpened}/>}
>
{showPage()}
</AppShell>
</APIContext.Provider>
);

View File

@@ -21,7 +21,7 @@ function AppHeader(props) {
/>
</MediaQuery>
<Group>
<ThemeIcon size="xl"><BsHouseDoor /></ThemeIcon>
<BsHouseDoor size={30} />
<Title><Text inherit variant="gradient" gradient={{ from: 'indigo', to: 'cyan', deg: 45 }}>goInventorize</Text></Title>
</Group>

View File

@@ -1,12 +1,26 @@
import React, {useState } from 'react';
import { Navbar, Text, Group, ThemeIcon, Button } from '@mantine/core';
import { Navbar, Text, Group, ThemeIcon, Button, UnstyledButton } from '@mantine/core';
import { createStyles } from '@mantine/styles';
import { BsMap } from 'react-icons/bs'
import {BrowserRouter as Router, Routes, Route, Link} from 'react-router-dom'
const useStyles = createStyles((theme) => ({
button: {
display: 'block',
width: '100%',
padding: theme.spacing.xs,
borderRadius: theme.radius.sm,
color: theme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.black,
backgroundColor: 'transparent',
'&:hover': {
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
},
},
}));
function SideBar(props) {
const { classes } = useStyles();
return (
<Navbar
@@ -17,14 +31,13 @@ function SideBar(props) {
hidden={!props.opened}
width={{ base: 200, breakpoints: { sm: '100%', lg: 300 } }}
>
<Router>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
<Button component={Link} to="/locations" leftIcon={<ThemeIcon><BsMap /></ThemeIcon>}>
<Text>Locations</Text>
</Button>
</Router>
<UnstyledButton className={classes.button} onClick={() => props.setCurrentPage("locations")} >
<Group>
<BsMap />
<Text>Locations</Text>
</Group>
</UnstyledButton>
</Navbar>
)

View File

@@ -1,10 +1,10 @@
import React, {useState, useEffect, useContext, createContext} from 'react';
import APIContext from '../../App';
import { Text } from '@mantine/core'
function App() {
function HomePage() {
const [opened, setOpened] = useState(false);
const serverConfig = useContext(APIContext);
@@ -12,9 +12,10 @@ function App() {
return (
<>
<Text>This is the homepage!</Text>
</>
);
}
export default App;
export default HomePage;

View File

@@ -0,0 +1,70 @@
import React, {useState, useEffect, useContext, createContext} from 'react';
import APIContext from '../../App';
import { Text, Loader, Center, Card, Image, Badge, Button, SimpleGrid, Group } from '@mantine/core'
import { useNotifications } from '@mantine/notifications';
import { backendAPI } from '../../services/backend-api';
function LocationsPage() {
// const [opened, setOpened] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [locations, setLocations] = useState([])
const [baseURL, setBaseURL] = useState("")
const serverConfig = useContext(APIContext);
const notifications = useNotifications();
useEffect(() => {
setIsLoading(true)
async function fetchSettings() {
backendAPI.get('/locations').then(results => {
console.log("CONFIG: ", serverConfig)
//console.log("baseurl: ", results.config.baseURL)
setBaseURL(results.config.baseURL)
console.log("URL", `${results.data[0]}`)
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 (
<>
<Center>{ isLoading && <Loader size="xl" variant="bars" />}</Center>
<SimpleGrid cols={4} spacing="xl">
{ locations.map((location, idx) =>
<Card key={idx} shadow="sm" padding="md">
<Card.Section>
<Image src={`${serverConfig.baseURL}/photos/locations/${location.Name}/${location.CoverPhoto}`}></Image>
</Card.Section>
<Group position="apart">
<Text weight={500}>{location.Name}</Text>
<Badge color="pink" variant="light">
On Sale
</Badge>
</Group>
<Text size="sm">{location.Description}</Text>
</Card>
)}
</SimpleGrid>
</>
);
}
export default LocationsPage;

View File

@@ -2,14 +2,17 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { NotificationsProvider } from '@mantine/notifications';
//setup api
const backendPort = process.env.REACT_APP_BACKEND_PORT
console.log("BACKEND PORT: ", backendPort)
//const backendPort = process.env.REACT_APP_BACKEND_PORT
//console.log("BACKEND PORT: ", backendPort)
ReactDOM.render(
<React.StrictMode>
<App />
<React.StrictMode>
<NotificationsProvider>
<App />
</NotificationsProvider>
</React.StrictMode>,
document.getElementById('root')
);

View File

@@ -1,12 +1,13 @@
import ky from 'ky';
import axios from 'axios';
const url = 'https://sindresorhus.com';
const backendPort = 3500
const backendAPI = ky.create({
headers: {
rainbow: 'rainbow',
unicorn: 'unicorn'
}
});
let baseURL = ""
if (process.env.NODE_ENV !== 'production') {
baseURL = 'http://localhost:3500'
}
export const backendAPI = axios.create({
baseURL: baseURL
})