temp commit so i can change branches

This commit is contained in:
csawatzky 2025-10-21 10:40:14 -06:00
parent bced311508
commit 6dbb7c3432
7 changed files with 160 additions and 87 deletions

4
package-lock.json generated
View file

@ -42,7 +42,7 @@
"mui-tel-input": "^7.0.0", "mui-tel-input": "^7.0.0",
"notistack": "^3.0.1", "notistack": "^3.0.1",
"openweathermap-ts": "^1.2.10", "openweathermap-ts": "^1.2.10",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master", "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#mipca_reports",
"query-string": "^9.2.1", "query-string": "^9.2.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",
@ -10911,7 +10911,7 @@
}, },
"node_modules/protobuf-ts": { "node_modules/protobuf-ts": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#d0ac088df3822c10c0497f49173d1679b4e107b0", "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#4aa7c4efefa38e86c21ec08cddcbd7f789b38464",
"dependencies": { "dependencies": {
"protobufjs": "^6.8.8" "protobufjs": "^6.8.8"
} }

View file

@ -54,7 +54,7 @@
"mui-tel-input": "^7.0.0", "mui-tel-input": "^7.0.0",
"notistack": "^3.0.1", "notistack": "^3.0.1",
"openweathermap-ts": "^1.2.10", "openweathermap-ts": "^1.2.10",
"protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#master", "protobuf-ts": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#mipca_reports",
"query-string": "^9.2.1", "query-string": "^9.2.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",

View file

@ -1,5 +1,5 @@
import { Gate } from "models/Gate"; import { Gate } from "models/Gate";
import { Box, Card, IconButton, List, ListItem, ListItemText, Theme, Typography } from "@mui/material"; import { Box, Button, Card, IconButton, List, ListItem, ListItemText, Theme, Typography } from "@mui/material";
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useMobile } from "hooks"; import { useMobile } from "hooks";
@ -13,6 +13,7 @@ import GateSettings from "./GateSettings";
import { useGateAPI, useGlobalState } from "providers"; import { useGateAPI, useGlobalState } from "providers";
import { Settings } from "@mui/icons-material"; import { Settings } from "@mui/icons-material";
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import GateReports from "reports/MiPCA/GateReports";
interface Props { interface Props {
//gates: Gate[]; //gates: Gate[];
@ -54,6 +55,7 @@ export default function GateList(props: Props) {
const [gates, setGates] = useState<Gate[]>([]) const [gates, setGates] = useState<Gate[]>([])
const [selectedGate, setSelectedGate] = useState<Gate | undefined>() const [selectedGate, setSelectedGate] = useState<Gate | undefined>()
const [search, setSearch] = useState("") const [search, setSearch] = useState("")
const [openReports, setOpenReports] = useState(false)
const goToGate = (gate: Gate) => { const goToGate = (gate: Gate) => {
let path = "/terminals/" + gate.key; let path = "/terminals/" + gate.key;
@ -208,6 +210,8 @@ export default function GateList(props: Props) {
const gateTable = () => { const gateTable = () => {
return ( return (
<ResponsiveTable<Gate> <ResponsiveTable<Gate>
actions={<Button onClick={()=>{setOpenReports(true)}}>Generate Reports</Button>}
title="Terminal Gates"
page={tablePage} page={tablePage}
pageSize={pageSize} pageSize={pageSize}
columns={isMobile || props.useMobile ? mobileCols() : desktopCols()} columns={isMobile || props.useMobile ? mobileCols() : desktopCols()}
@ -237,7 +241,8 @@ export default function GateList(props: Props) {
) )
}; };
return <Box> return (
<React.Fragment>
<GateSettings <GateSettings
open={gateDialog} open={gateDialog}
gate={selectedGate} gate={selectedGate}
@ -256,5 +261,7 @@ export default function GateList(props: Props) {
pulse={gates.length < 1} pulse={gates.length < 1}
/> />
{gateTable()} {gateTable()}
</Box> <GateReports open={openReports} onClose={()=>{setOpenReports(false)}}/>
</React.Fragment>
)
} }

View file

@ -241,7 +241,7 @@ export default function MapBase(props: Props) {
homeMarkerAPI homeMarkerAPI
.listHomeMarkers(1, 0, undefined, undefined, undefined, undefined, as) .listHomeMarkers(1, 0, undefined, undefined, undefined, undefined, as)
.then(resp => { .then(resp => {
if (resp.data.homeMarker.length < 1) { if (resp.data.homeMarker === undefined) {
setHomePin(undefined); setHomePin(undefined);
setHaveHome(false); setHaveHome(false);
setStartingView(props.currentView); setStartingView(props.currentView);
@ -271,6 +271,7 @@ export default function MapBase(props: Props) {
} }
}) })
.catch(err => { .catch(err => {
console.log(err)
openSnack("Could not load Home Pin"); openSnack("Could not load Home Pin");
}); });
}, [as, homeMarkerAPI, openSnack, props.ignoreHomeLoad, user]); // eslint-disable-line react-hooks/exhaustive-deps }, [as, homeMarkerAPI, openSnack, props.ignoreHomeLoad, user]); // eslint-disable-line react-hooks/exhaustive-deps

View file

@ -221,24 +221,26 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
.then(resp => { .then(resp => {
let fields: Map<string, Field> = new Map(); let fields: Map<string, Field> = new Map();
let fieldEntries: Result[] = []; let fieldEntries: Result[] = [];
resp.data.fields.forEach(f => { if(resp.data.fields){
let field = Field.any(f); resp.data.fields.forEach(f => {
fields.set(field.key(), field); let field = Field.any(f);
fieldEntries.push({ fields.set(field.key(), field);
id: field.key(), fieldEntries.push({
place_name: field.name(), id: field.key(),
place_type: ["field"], place_name: field.name(),
center: [field.center().longitude, field.center().latitude], place_type: ["field"],
address: "", center: [field.center().longitude, field.center().latitude],
type: "Feature", address: "",
text: field.name(), type: "Feature",
bbox: [0,0,0,0], text: field.name(),
geometry: {type: "Point", coordinates: [field.center().longitude, field.center().latitude]}, bbox: [0,0,0,0],
context: [], geometry: {type: "Point", coordinates: [field.center().longitude, field.center().latitude]},
properties: {}, context: [],
relevance: 0 properties: {},
relevance: 0
});
}); });
}); }
setFields(fields); setFields(fields);
setFieldSearchEntries(fieldEntries); setFieldSearchEntries(fieldEntries);
}) })
@ -314,53 +316,56 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
let bagOp: BagModel[] = []; let bagOp: BagModel[] = [];
let bagMarkerData: Map<string, MarkerData> = new Map(); let bagMarkerData: Map<string, MarkerData> = new Map();
let bagEntries: Result[] = []; let bagEntries: Result[] = [];
resp.data.grainBags.forEach(bag => { if(resp.data.grainBags){
let b = BagModel.create(bag);
bags.set(b.key(), b); resp.data.grainBags.forEach(bag => {
if (bag.settings) { let b = BagModel.create(bag);
if (!b.settings.startLocation?.longitude && !b.settings.endLocation?.longitude) { bags.set(b.key(), b);
bagOp.push(b); if (bag.settings) {
} else { if (!b.settings.startLocation?.longitude && !b.settings.endLocation?.longitude) {
//build the data for the marker bagOp.push(b);
//TODO-CS: think about making the creation of marker data a function in the object model } else {
let mData: MarkerData = { //build the data for the marker
title: b.name(), //TODO-CS: think about making the creation of marker data a function in the object model
longitude: b.centerLocation().longitude, let mData: MarkerData = {
latitude: b.centerLocation().latitude, title: b.name(),
visibleLevels: { max: 13 }, longitude: b.centerLocation().longitude,
colour: b.settings.theme?.color ?? "white", latitude: b.centerLocation().latitude,
clickFunc: (e, i, isMobile) => { visibleLevels: { max: 13 },
clickDelay(); colour: b.settings.theme?.color ?? "white",
closeDrawers(); clickFunc: (e, i, isMobile) => {
setBagDrawer(true); clickDelay();
setIgnoreFeatures(true); closeDrawers();
setObjectKey(b.key()); setBagDrawer(true);
moveMap( setIgnoreFeatures(true);
b.centerLocation().latitude, setObjectKey(b.key());
b.centerLocation().longitude, moveMap(
zoomLevels.far, b.centerLocation().latitude,
isMobile b.centerLocation().longitude,
); zoomLevels.far,
} isMobile
}; );
bagMarkerData.set(b.key(), mData); }
bagEntries.push({ };
center: [b.centerLocation().longitude, b.centerLocation().latitude], bagMarkerData.set(b.key(), mData);
id: b.key(), bagEntries.push({
place_name: b.name(), center: [b.centerLocation().longitude, b.centerLocation().latitude],
place_type: ["grainBag"], id: b.key(),
address: "", place_name: b.name(),
bbox: [0,0,0,0], place_type: ["grainBag"],
context: [], address: "",
geometry: {type: "Point", coordinates: [b.centerLocation().longitude, b.centerLocation().latitude]}, bbox: [0,0,0,0],
properties: {}, context: [],
relevance: 0, geometry: {type: "Point", coordinates: [b.centerLocation().longitude, b.centerLocation().latitude]},
text: "", properties: {},
type: "Feature" relevance: 0,
}); text: "",
type: "Feature"
});
}
} }
} });
}); }
setBagOptions(bagOp); setBagOptions(bagOp);
setGrainBags(bags); setGrainBags(bags);
grainBagsRef.current = bags; grainBagsRef.current = bags;
@ -382,19 +387,21 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
binYardAPI binYardAPI
.listBinYards(400, 0, "asc", undefined, undefined, undefined, as) .listBinYards(400, 0, "asc", undefined, undefined, undefined, as)
.then(resp => { .then(resp => {
resp.data.yard.forEach(yard => { if(resp.data.yard){
if (yard.settings) {
yardMap.set(yard.settings.key, yard.settings); resp.data.yard.forEach(yard => {
if ( if (yard.settings) {
(!yard.settings.latitude && !yard.settings.longitude) || yardMap.set(yard.settings.key, yard.settings);
(yard.settings.latitude === 0 && yard.settings.longitude === 0) if (
) { (!yard.settings.latitude && !yard.settings.longitude) ||
yardOps.push(yard.settings); (yard.settings.latitude === 0 && yard.settings.longitude === 0)
} else { ) {
let mData: MarkerData = { yardOps.push(yard.settings);
title: yard.settings.name, } else {
longitude: yard.settings.longitude, let mData: MarkerData = {
latitude: yard.settings.latitude, title: yard.settings.name,
longitude: yard.settings.longitude,
latitude: yard.settings.latitude,
colour: yard.settings.theme?.color ?? "green", colour: yard.settings.theme?.color ?? "green",
markerIcon: <BinsIcon width={50 * 0.6} height={50 * 0.6} type="light" />, markerIcon: <BinsIcon width={50 * 0.6} height={50 * 0.6} type="light" />,
visibleLevels: { max: 17 }, visibleLevels: { max: 17 },
@ -429,6 +436,7 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
} }
} }
}); });
}
setYardMarkerData(newYardMarkers); setYardMarkerData(newYardMarkers);
setYardSearchEntries(yardEntries); setYardSearchEntries(yardEntries);
setBinYards(yardMap); setBinYards(yardMap);
@ -447,6 +455,9 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
let markerData: Map<string, MarkerData> = new Map(); let markerData: Map<string, MarkerData> = new Map();
let searchEntries: Result[] = []; let searchEntries: Result[] = [];
let binOp: IBin[] = []; let binOp: IBin[] = [];
if(resp.data.bins){
resp.data.bins.forEach(bin => { resp.data.bins.forEach(bin => {
let b = IBin.create(bin); let b = IBin.create(bin);
binMap.set(b.key(), b); binMap.set(b.key(), b);
@ -495,6 +506,7 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
} }
} }
}); });
}
setBinMarkerData(markerData); setBinMarkerData(markerData);
setBinSearchEntries(searchEntries); setBinSearchEntries(searchEntries);
setBins(binMap); setBins(binMap);
@ -514,6 +526,9 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
let searchEntries: Result[] = []; let searchEntries: Result[] = [];
let options: DeviceModel[] = []; let options: DeviceModel[] = [];
if(resp.data.devices){
resp.data.devices.forEach((device, i) => { resp.data.devices.forEach((device, i) => {
//add device to the main map //add device to the main map
let d = DeviceModel.any(device); let d = DeviceModel.any(device);
@ -553,6 +568,7 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
} }
} }
}); });
}
setDevices(map); setDevices(map);
setDeviceOptions(options); setDeviceOptions(options);
setDeviceMarkerData(newDevMarkers); setDeviceMarkerData(newDevMarkers);
@ -725,6 +741,9 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
let fmMap: Map<string, FieldMarker> = new Map(); let fmMap: Map<string, FieldMarker> = new Map();
let markerData: Map<string, MarkerData> = new Map(); let markerData: Map<string, MarkerData> = new Map();
let searchEntries: Result[] = []; let searchEntries: Result[] = [];
if(resp.data.fieldMarker){
resp.data.fieldMarker.forEach(fieldMarker => { resp.data.fieldMarker.forEach(fieldMarker => {
let fm = FieldMarker.create(fieldMarker); let fm = FieldMarker.create(fieldMarker);
fmMap.set(fm.key(), fm); fmMap.set(fm.key(), fm);
@ -760,6 +779,7 @@ import { Result } from "@mapbox/mapbox-gl-geocoder";
place_type: ["fieldMarker"] place_type: ["fieldMarker"]
} as Result); } as Result);
}); });
}
setFieldMarkers(fmMap); setFieldMarkers(fmMap);
setFieldMarkerData(markerData); setFieldMarkerData(markerData);
setFieldMarkerSearchEntries(searchEntries); setFieldMarkerSearchEntries(searchEntries);

View file

@ -81,6 +81,7 @@ export interface IGateInterface {
otherTeam?: string otherTeam?: string
) => Promise<AxiosResponse<pond.ListGateMeasurementsResponse>>; ) => Promise<AxiosResponse<pond.ListGateMeasurementsResponse>>;
//new call for getting report data for gates //new call for getting report data for gates
gateReportData: (gates: string[], start: string, end: string, otherTeam?: string) => Promise<AxiosResponse<pond.GateReportDataResponse>>
} }
export const GateAPIcontext = createContext<IGateInterface>({} as IGateInterface); export const GateAPIcontext = createContext<IGateInterface>({} as IGateInterface);
@ -348,6 +349,24 @@ export default function GateProvider(props: PropsWithChildren<Props>) {
); );
}; };
const gateReportData = (
gates: string[],
start: string,
end: string,
otherTeam?: string
) => {
const view = otherTeam ? otherTeam : as
let url = pondURL("reports/gates?keys=" + gates.toString() + "start=" + start + "&end=" + end + "&as=" + view)
return new Promise<AxiosResponse<pond.GateReportDataResponse>>((resolve, reject) => {
get<pond.GateReportDataResponse>(url).then(resp => {
resp.data = pond.GateReportDataResponse.fromObject(resp.data)
return resolve(resp)
}).catch(err => {
return reject(err)
})
})
}
return ( return (
<GateAPIcontext.Provider <GateAPIcontext.Provider
value={{ value={{
@ -361,7 +380,8 @@ export default function GateProvider(props: PropsWithChildren<Props>) {
updatePrefs, updatePrefs,
listGateAirflow, listGateAirflow,
listGateMeasurements, listGateMeasurements,
listGateFlowEvents listGateFlowEvents,
gateReportData
}}> }}>
{children} {children}
</GateAPIcontext.Provider> </GateAPIcontext.Provider>

View file

@ -1,8 +1,27 @@
import React from "react" import { DialogActions, DialogContent, DialogTitle } from "@mui/material"
import ResponsiveDialog from "common/ResponsiveDialog"
import { useGateAPI } from "providers"
import React, { useEffect } from "react"
export default function GateReports(){ interface Props {
open: boolean
onClose: () => void
gate?: string //if the gate key is passed in just generate for that gate, otherwise give the user a way of selecting which gates
}
export default function GateReports(props: Props){
const {gate, open, onClose} = props
const gateAPI = useGateAPI()
//load all of the gates for the user/team //load all of the gates for the user/team
useEffect(()=>{
if(gate) return //if there was a gate passed in we dont need to load them
gateAPI.listGates(0, 0).then(resp => {
console.log(resp.data)
}).catch(err => {
})
},[gate])
//selection table to select which gates to generate a report //selection table to select which gates to generate a report
@ -13,6 +32,12 @@ export default function GateReports(){
*/ */
return ( return (
<React.Fragment></React.Fragment> <ResponsiveDialog open={open} onClose={onClose}>
<DialogTitle>Gate Reports</DialogTitle>
<DialogContent>
</DialogContent>
<DialogActions></DialogActions>
</ResponsiveDialog>
) )
} }