diff --git a/package-lock.json b/package-lock.json
index dda1c90..9bcdb3f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42,7 +42,7 @@
"mui-tel-input": "^7.0.0",
"notistack": "^3.0.1",
"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",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1",
@@ -10911,7 +10911,7 @@
},
"node_modules/protobuf-ts": {
"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#90d01d04404d6b38d3200634e194c17bae8a0737",
"dependencies": {
"protobufjs": "^6.8.8"
}
diff --git a/package.json b/package.json
index ee0e9b7..a8e73f7 100644
--- a/package.json
+++ b/package.json
@@ -54,7 +54,7 @@
"mui-tel-input": "^7.0.0",
"notistack": "^3.0.1",
"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",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1",
diff --git a/src/cableEstimator/cableQuote.tsx b/src/cableEstimator/cableQuote.tsx
index ae37870..af6ccee 100644
--- a/src/cableEstimator/cableQuote.tsx
+++ b/src/cableEstimator/cableQuote.tsx
@@ -27,7 +27,7 @@ export default function CableQuote(props: Props) {
});
const pdfDoc = () => {
- return } />;
+ return ]} />;
};
return (
diff --git a/src/common/pdfTemplates/Agriculture/agPDFTemplate.tsx b/src/common/pdfTemplates/Agriculture/agPDFTemplate.tsx
index 5b1285f..9a79783 100644
--- a/src/common/pdfTemplates/Agriculture/agPDFTemplate.tsx
+++ b/src/common/pdfTemplates/Agriculture/agPDFTemplate.tsx
@@ -3,7 +3,7 @@ import Header from "./components/header";
import Footer from "./components/footer";
interface Props {
- content: JSX.Element;
+ content: JSX.Element[];
}
export default function AgPDFTemplate(props: Props) {
@@ -27,15 +27,17 @@ export default function AgPDFTemplate(props: Props) {
});
return (
-
+ {props.content.map(page => (
+
- {props.content}
+ {page}
+ ))}
);
}
diff --git a/src/common/pdfTemplates/MiPCA/components/footer.tsx b/src/common/pdfTemplates/MiPCA/components/footer.tsx
new file mode 100644
index 0000000..469e0cc
--- /dev/null
+++ b/src/common/pdfTemplates/MiPCA/components/footer.tsx
@@ -0,0 +1,31 @@
+import { Image, StyleSheet, View } from "@react-pdf/renderer";
+import agFooter from "common/pdfTemplates/Agriculture/components/footerImages/agFooter.png";
+import React from "react";
+
+export default function Footer() {
+ const styles = StyleSheet.create({
+ view: {
+ flexDirection: "row",
+ fontStyle: "normal",
+ fontWeight: 650,
+ borderTop: "1px solid black",
+ padding: 10
+ },
+ label: {
+ width: "20%"
+ },
+ email: {
+ width: "40%"
+ },
+ phone: {
+ width: "40%"
+ }
+ });
+ return (
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/common/pdfTemplates/MiPCA/components/header.tsx b/src/common/pdfTemplates/MiPCA/components/header.tsx
new file mode 100644
index 0000000..396ecdb
--- /dev/null
+++ b/src/common/pdfTemplates/MiPCA/components/header.tsx
@@ -0,0 +1,32 @@
+import { Image, StyleSheet, Text, View } from "@react-pdf/renderer";
+import moment from "moment";
+import React from "react";
+import logo from "assets/whitelabels/AdaptiveAgriculture/AgLogoText.png";
+
+export default function Header() {
+ const styles = StyleSheet.create({
+ view: {
+ flexDirection: "row",
+ fontStyle: "normal",
+ fontWeight: 650,
+ borderBottom: "1px solid black"
+ },
+ logo: {
+ width: "55%",
+ textAlign: "left",
+ paddingBottom: 15
+ },
+ estimate: {
+ width: "45%",
+ textAlign: "right"
+ }
+ });
+ return (
+
+
+
+ {moment().format("YYYY-MM-DD")}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/common/pdfTemplates/MiPCA/mipcaPDFTemplate.tsx b/src/common/pdfTemplates/MiPCA/mipcaPDFTemplate.tsx
new file mode 100644
index 0000000..4ad5d2d
--- /dev/null
+++ b/src/common/pdfTemplates/MiPCA/mipcaPDFTemplate.tsx
@@ -0,0 +1,43 @@
+import { Document, Page, StyleSheet, View } from "@react-pdf/renderer";
+import Header from "./components/header";
+import Footer from "./components/footer";
+
+interface Props {
+ content: JSX.Element[];
+}
+
+export default function MiPCAPDFTemplate(props: Props) {
+ const pdfStyle = StyleSheet.create({
+ page: {
+ fontSize: 11,
+ padding: "5%"
+ },
+ header: {
+ flexDirection: "row",
+ height: "8%"
+ },
+ content: {
+ flexDirection: "row",
+ height: "87%"
+ },
+ footer: {
+ flexDirection: "row",
+ height: "5%"
+ }
+ });
+ return (
+
+ {props.content.map(page => (
+
+
+
+
+ {page}
+
+
+
+
+ ))}
+
+ );
+}
diff --git a/src/gate/GateList.tsx b/src/gate/GateList.tsx
index 5c548e2..b590b2b 100644
--- a/src/gate/GateList.tsx
+++ b/src/gate/GateList.tsx
@@ -1,5 +1,5 @@
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 { useNavigate } from "react-router-dom";
import { useMobile } from "hooks";
@@ -13,6 +13,7 @@ import GateSettings from "./GateSettings";
import { useGateAPI, useGlobalState } from "providers";
import { Settings } from "@mui/icons-material";
import { cloneDeep } from "lodash";
+import GateReports from "reports/MiPCA/GateReports";
interface Props {
//gates: Gate[];
@@ -54,6 +55,7 @@ export default function GateList(props: Props) {
const [gates, setGates] = useState([])
const [selectedGate, setSelectedGate] = useState()
const [search, setSearch] = useState("")
+ const [openReports, setOpenReports] = useState(false)
const goToGate = (gate: Gate) => {
let path = "/terminals/" + gate.key;
@@ -208,6 +210,8 @@ export default function GateList(props: Props) {
const gateTable = () => {
return (
+ actions={}
+ title="Terminal Gates"
page={tablePage}
pageSize={pageSize}
columns={isMobile || props.useMobile ? mobileCols() : desktopCols()}
@@ -237,7 +241,8 @@ export default function GateList(props: Props) {
)
};
- return
+ return (
+
{gateTable()}
-
+ {setOpenReports(false)}}/>
+
+ )
}
diff --git a/src/providers/pond/gateAPI.tsx b/src/providers/pond/gateAPI.tsx
index 5b07606..8caf4e8 100644
--- a/src/providers/pond/gateAPI.tsx
+++ b/src/providers/pond/gateAPI.tsx
@@ -80,6 +80,8 @@ export interface IGateInterface {
end: string,
otherTeam?: string
) => Promise>;
+ //new call for getting report data for gates
+ gateReportData: (gates: string[], start: string, end: string, otherTeam?: string) => Promise>
}
export const GateAPIcontext = createContext({} as IGateInterface);
@@ -347,6 +349,24 @@ export default function GateProvider(props: PropsWithChildren) {
);
};
+ const gateReportData = (
+ gates: string[],
+ start: string,
+ end: string,
+ otherTeam?: string
+ ) => {
+ const view = otherTeam ? otherTeam : as
+ let url = pondURL("/reports/gates?gateKeys=" + gates.toString() + "&start=" + start + "&end=" + end + "&as=" + view)
+ return new Promise>((resolve, reject) => {
+ get(url).then(resp => {
+ resp.data = pond.GateReportDataResponse.fromObject(resp.data)
+ return resolve(resp)
+ }).catch(err => {
+ return reject(err)
+ })
+ })
+ }
+
return (
) {
updatePrefs,
listGateAirflow,
listGateMeasurements,
- listGateFlowEvents
+ listGateFlowEvents,
+ gateReportData
}}>
{children}
diff --git a/src/reports/MiPCA/GateReports.tsx b/src/reports/MiPCA/GateReports.tsx
new file mode 100644
index 0000000..fa04419
--- /dev/null
+++ b/src/reports/MiPCA/GateReports.tsx
@@ -0,0 +1,191 @@
+import { Box, Button, CircularProgress, DialogActions, DialogContent, DialogTitle, Typography } from "@mui/material"
+import MiPCAPDFTemplate from "common/pdfTemplates/MiPCA/mipcaPDFTemplate"
+import ResponsiveDialog from "common/ResponsiveDialog"
+import ResponsiveTable from "common/ResponsiveTable"
+import { GetDefaultDateRange } from "common/time/DateRange"
+import DateSelect from "common/time/DateSelect"
+import { cloneDeep } from "lodash"
+import { Gate } from "models/Gate"
+import { Moment } from "moment"
+import { pond } from "protobuf-ts/pond"
+import { useGateAPI } from "providers"
+import React, { useEffect, useState } from "react"
+import GateReportPDF from "./pdfComponents.tsx/GateReportPDF"
+import { PDFViewer, StyleSheet } from "@react-pdf/renderer"
+import { useMobile } from "hooks"
+
+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()
+ const [allGates, setAllGates] = useState([])
+ const [displayedGates, setDisplayedGates] = useState([])
+ const [pagesize, setPageSize] = useState(10)
+ const [page, setPage] = useState(0)
+ const [gateKeys, setGateKeys] = useState([])
+ const [selectedTableRows,setSelectedTableRows] = useState([])
+ const [reportPages, setReportPages] = useState([])
+ const [loadingReportData, setLoadingReportData] = useState(false)
+ const defaultDateRange = GetDefaultDateRange();
+ const [startDate, setStartDate] = useState(defaultDateRange.start);
+ const [endDate, setEndDate] = useState(defaultDateRange.end);
+ const isMobile = useMobile()
+ const pdfStyle = StyleSheet.create({
+ viewerDesktop: {
+ height: 500 * 1.44,
+ width: 500
+ },
+ viewerMobile: {
+ height: "100%",
+ width: "100%"
+ }
+ });
+
+ const closeDialog = () => {
+ setGateKeys([])
+ setSelectedTableRows([])
+ setReportPages([])
+ onClose()
+ }
+
+ //load all of the gates for the user/team
+ useEffect(()=>{
+ if(open){
+ if(gate) {
+ setGateKeys([gate])
+ return
+ } else {
+ gateAPI.listGates(0, 0).then(resp => {
+ if(resp.data.gates){
+ setAllGates(resp.data.gates.map(g => Gate.create(g)))
+ }
+ }).catch(err => {
+ console.log(err)
+ })
+ }
+
+ }
+ },[gate, open])
+
+ useEffect(()=>{
+ let start = page*pagesize
+ let offset = pagesize
+ setDisplayedGates(allGates.slice(start, start+offset))
+ },[allGates, page, pagesize])
+
+ //selection table to select which gates to generate a report
+ const gateTable = () => {
+ return (
+
+ columns={[
+ {
+ title: "Gate",
+ render: row => {row.name}
+ }
+ ]}
+ selectedRows={selectedTableRows}
+ rowSelect={(gate, tableIndex, checked) => {
+ let rowIndexes = cloneDeep(selectedTableRows)
+ let keys = cloneDeep(gateKeys)
+ if(checked){
+ if(!selectedTableRows.includes(tableIndex)) rowIndexes.push(tableIndex)
+ if(!gateKeys.includes(gate.key)) keys.push(gate.key)
+ }else{
+ if(rowIndexes.includes(tableIndex)) rowIndexes.splice(rowIndexes.indexOf(tableIndex), 1)
+ if(keys.includes(gate.key)) keys.splice(keys.indexOf(gate.key), 1)
+ }
+ setSelectedTableRows(rowIndexes)
+ setGateKeys(keys)
+ }}
+ rows={displayedGates}
+ total={allGates.length}
+ pageSize={pagesize}
+ page={page}
+ setPage={(page) => {
+ setPage(page)
+ }}
+ handleRowsPerPageChange={(event) => {
+ setPageSize(event.target.value)
+ }} />
+ )
+ }
+
+ //two text field select boxes to select the start and end dates
+ const dateSelect = () => {
+ return (
+ {
+ setStartDate(start)
+ setEndDate(end)
+ }}
+ label="Build Reports For"
+ />
+ )
+ }
+
+ /**
+ * new api call for gates that takes in an array of gate keys to build the report data for
+ * this call will return an array of the gate report data
+ * in the return loop through the data building a pdf report for each one
+ */
+ const generateReports = () => {
+ setLoadingReportData(true)
+ gateAPI.gateReportData(gateKeys, startDate.toISOString(), endDate.toISOString()).then(resp => {
+ console.log(resp)
+ let pdfPages: JSX.Element[] = []
+ if(resp.data.data){
+ resp.data.data.forEach(reportData => {
+ pdfPages.push()
+ })
+ }
+ setReportPages(pdfPages)
+ }).catch(err => {
+
+ }).finally(()=>{
+ //in the finally set the loading to false
+ setLoadingReportData(false)
+ })
+ }
+
+ return (
+ {closeDialog()}}>
+ Gate Reports
+
+ {loadingReportData ?
+
+
+
+ :
+
+ {reportPages.length > 0 ?
+
+
+
+
+ :
+
+ {dateSelect()}
+ {!gate && gateTable()}
+
+ }
+
+ }
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/reports/MiPCA/pdfComponents.tsx/GateReportPDF.tsx b/src/reports/MiPCA/pdfComponents.tsx/GateReportPDF.tsx
new file mode 100644
index 0000000..8616072
--- /dev/null
+++ b/src/reports/MiPCA/pdfComponents.tsx/GateReportPDF.tsx
@@ -0,0 +1,29 @@
+import { View, StyleSheet } from "@react-pdf/renderer";
+import { pond } from "protobuf-ts/pond";
+
+interface Props {
+ reportData: pond.GateReportData
+}
+
+export default function GateReportPDF(props: Props){
+ const styles = StyleSheet.create({
+ general: {
+ marginTop: 2,
+ flexDirection: "row",
+ borderTop: "1px solid black",
+ borderBottom: "1px solid black",
+ paddingTop: 12,
+ paddingBottom: 12,
+ fontFamily: "Helvetica-Bold",
+ fontSize: 20,
+ textAlign: "center"
+ }
+ });
+ return (
+
+
+
+
+
+ )
+}
\ No newline at end of file