adding the transaction page
This commit is contained in:
parent
975f3bba54
commit
97bbd41492
7 changed files with 371 additions and 15 deletions
|
|
@ -50,7 +50,7 @@ import {
|
||||||
// import FanPicker from "fans/fanPicker";
|
// import FanPicker from "fans/fanPicker";
|
||||||
import { GrainOptions, ToGrainOption } from "grain";
|
import { GrainOptions, ToGrainOption } from "grain";
|
||||||
import GrainDescriber from "grain/GrainDescriber";
|
import GrainDescriber from "grain/GrainDescriber";
|
||||||
// import GrainTransaction from "grain/GrainTransaction";
|
import GrainTransaction from "grain/GrainTransaction";
|
||||||
import { GetGrainUseOptions, ToGrainUseOption } from "grain/GrainUse";
|
import { GetGrainUseOptions, ToGrainUseOption } from "grain/GrainUse";
|
||||||
import { useMobile, useSnackbar } from "hooks";
|
import { useMobile, useSnackbar } from "hooks";
|
||||||
import { clone, cloneDeep } from "lodash";
|
import { clone, cloneDeep } from "lodash";
|
||||||
|
|
@ -424,6 +424,7 @@ export default function BinSettings(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateBin = () => {
|
const updateBin = () => {
|
||||||
|
console.log("bin settings update")
|
||||||
// If initial moisture is updated, so does the timestamp
|
// If initial moisture is updated, so does the timestamp
|
||||||
if (form.inventory?.initialMoisture !== bin?.settings.inventory?.initialMoisture) {
|
if (form.inventory?.initialMoisture !== bin?.settings.inventory?.initialMoisture) {
|
||||||
if (form.inventory) form.inventory.initialTimestamp = moment().format();
|
if (form.inventory) form.inventory.initialTimestamp = moment().format();
|
||||||
|
|
@ -450,7 +451,7 @@ export default function BinSettings(props: Props) {
|
||||||
form.lowTemp = lowTempC;
|
form.lowTemp = lowTempC;
|
||||||
form.autoGrainNode = autoTopNode;
|
form.autoGrainNode = autoTopNode;
|
||||||
if (form.inventory) form.inventory.inventoryControl = inventoryControl;
|
if (form.inventory) form.inventory.inventoryControl = inventoryControl;
|
||||||
|
console.log("bin api update")
|
||||||
binAPI
|
binAPI
|
||||||
.updateBin(bin.key(), form)
|
.updateBin(bin.key(), form)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
@ -473,19 +474,18 @@ export default function BinSettings(props: Props) {
|
||||||
newBin.settings.inventory.grainBushels = bin.settings.inventory?.grainBushels ?? 0; //but also need to use the old inventory amount so the diff is applied correctly
|
newBin.settings.inventory.grainBushels = bin.settings.inventory?.grainBushels ?? 0; //but also need to use the old inventory amount so the diff is applied correctly
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
// <GrainTransaction
|
<GrainTransaction
|
||||||
// open={grainUpdate}
|
open={grainUpdate}
|
||||||
// mainObject={newBin}
|
mainObject={newBin}
|
||||||
// grainAdjustment={grainDiff}
|
grainAdjustment={grainDiff}
|
||||||
// close={() => {
|
close={() => {
|
||||||
// setGrainUpdate(false);
|
setGrainUpdate(false);
|
||||||
// setGrainDiff(0);
|
setGrainDiff(0);
|
||||||
// }}
|
}}
|
||||||
// callback={() => {
|
callback={() => {
|
||||||
// close(true);
|
close(true);
|
||||||
// }}
|
}}
|
||||||
// />
|
/>
|
||||||
null
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,7 @@ export default function GrainTransaction(props: Props) {
|
||||||
|
|
||||||
//create a transaction to enter into the table
|
//create a transaction to enter into the table
|
||||||
const createTransaction = (finalVal: number) => {
|
const createTransaction = (finalVal: number) => {
|
||||||
|
console.log("create transaction")
|
||||||
let dest = selectedDestination;
|
let dest = selectedDestination;
|
||||||
let source = selectedSource;
|
let source = selectedSource;
|
||||||
let bpt = 1;
|
let bpt = 1;
|
||||||
|
|
@ -248,13 +249,16 @@ export default function GrainTransaction(props: Props) {
|
||||||
grainTransaction.subtype = obj.subtype();
|
grainTransaction.subtype = obj.subtype();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log(newTransaction)
|
||||||
transactionAPI.addTransaction(newTransaction, imageIDs).then(resp => {
|
transactionAPI.addTransaction(newTransaction, imageIDs).then(resp => {
|
||||||
|
console.log(resp)
|
||||||
newTransaction.key = resp.data.key;
|
newTransaction.key = resp.data.key;
|
||||||
closeDialogs(true, newTransaction);
|
closeDialogs(true, newTransaction);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateInventory = () => {
|
const updateInventory = () => {
|
||||||
|
console.log("update inventory")
|
||||||
//this is the amount to add to the destination
|
//this is the amount to add to the destination
|
||||||
let destinationAdd = Math.abs(
|
let destinationAdd = Math.abs(
|
||||||
isNaN(parseFloat(grainChangeVal)) ? 1 : parseFloat(grainChangeVal)
|
isNaN(parseFloat(grainChangeVal)) ? 1 : parseFloat(grainChangeVal)
|
||||||
|
|
|
||||||
19
src/models/Transaction.ts
Normal file
19
src/models/Transaction.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import { or } from "utils/types";
|
||||||
|
|
||||||
|
export class Transaction {
|
||||||
|
public transaction: pond.Transaction = pond.Transaction.create();
|
||||||
|
|
||||||
|
public static create(pb?: pond.Transaction): Transaction {
|
||||||
|
let my = new Transaction();
|
||||||
|
if (pb) {
|
||||||
|
my.transaction = pond.Transaction.fromObject(cloneDeep(or(pb, {})));
|
||||||
|
}
|
||||||
|
return my;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static any(data: any): Transaction {
|
||||||
|
return Transaction.create(pond.Transaction.fromObject(cloneDeep(data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ import Site from "pages/Site";
|
||||||
import Heaters from "pages/Heaters";
|
import Heaters from "pages/Heaters";
|
||||||
import Heater from "pages/Heater";
|
import Heater from "pages/Heater";
|
||||||
import Tasks from "pages/Tasks";
|
import Tasks from "pages/Tasks";
|
||||||
|
import Transactions from "pages/Transactions";
|
||||||
//import Site from "pages/Site";
|
//import Site from "pages/Site";
|
||||||
|
|
||||||
const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
|
const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
|
||||||
|
|
@ -301,6 +302,7 @@ export default function Router(props: Props) {
|
||||||
{/* <Route index element={<Typography>Hello!</Typography>} /> */}
|
{/* <Route index element={<Typography>Hello!</Typography>} /> */}
|
||||||
<Route path="users" element={<Users/>} />
|
<Route path="users" element={<Users/>} />
|
||||||
<Route path="tasks" element={<Tasks />} />
|
<Route path="tasks" element={<Tasks />} />
|
||||||
|
<Route path="transactions" element={<Transactions />} />
|
||||||
|
|
||||||
{/* Map pages */}
|
{/* Map pages */}
|
||||||
<Route path="visualFarm" element={<FieldMap />} />
|
<Route path="visualFarm" element={<FieldMap />} />
|
||||||
|
|
|
||||||
165
src/pages/Transactions.tsx
Normal file
165
src/pages/Transactions.tsx
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import PageContainer from "./PageContainer";
|
||||||
|
import { Box, Grid2 as Grid, TextField, Typography } from "@mui/material";
|
||||||
|
import { Moment } from "moment";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import SearchSelect, { Option } from "common/SearchSelect";
|
||||||
|
import TransactionViewer from "transactions/transactionViewer";
|
||||||
|
import { TransactionOptions } from "objects/ObjectDescriber";
|
||||||
|
import { GetDefaultDateRange } from "common/time/DateRange";
|
||||||
|
import TimeBar from "common/time/TimeBar";
|
||||||
|
import { useTransactionAPI } from "providers";
|
||||||
|
import { Transaction } from "models/Transaction";
|
||||||
|
import { makeStyles } from "@mui/styles";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(() => {
|
||||||
|
return ({
|
||||||
|
page: {
|
||||||
|
padding: 10
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//this page will have the dropdown selectors to set the variables for query
|
||||||
|
export default function Transactions() {
|
||||||
|
const classes = useStyles();
|
||||||
|
const defaultDateRange = GetDefaultDateRange();
|
||||||
|
const [startDate, setStartDate] = useState<Moment>(defaultDateRange.start);
|
||||||
|
const [endDate, setEndDate] = useState<Moment>(defaultDateRange.end);
|
||||||
|
//these two variables are the object type
|
||||||
|
const [fromObject, setFromObject] = useState<pond.ObjectType | undefined>(undefined);
|
||||||
|
const [toObject, setToObject] = useState<pond.ObjectType | undefined>(undefined);
|
||||||
|
//these are simply for controlling what is shown in the select boxes
|
||||||
|
const [selectedFromObject, setSelectedFromObject] = useState<Option | null>();
|
||||||
|
const [selectedToObject, setSelectedToObject] = useState<Option | null>();
|
||||||
|
const transactionOptions = TransactionOptions();
|
||||||
|
const transactionAPI = useTransactionAPI();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [totalTransactions, setTotalTransactions] = useState<Transaction[]>([]);
|
||||||
|
const [filteredTransactions, setFilteredTransactions] = useState<Transaction[]>([]);
|
||||||
|
const [objectNames, setObjectNames] = useState<Map<string, string>>(new Map());
|
||||||
|
const [fromName, setFromName] = useState("");
|
||||||
|
const [toName, setToName] = useState("");
|
||||||
|
|
||||||
|
const updateDateRange = (newStartDate: any, newEndDate: any) => {
|
||||||
|
let range = GetDefaultDateRange();
|
||||||
|
range.start = newStartDate;
|
||||||
|
range.end = newEndDate;
|
||||||
|
setStartDate(newStartDate);
|
||||||
|
setEndDate(newEndDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
//useEffect to filter the transactions to show based on the dropdown for object and text boxes for name entry
|
||||||
|
useEffect(() => {
|
||||||
|
let temp: Transaction[] = [];
|
||||||
|
totalTransactions.forEach(trans => {
|
||||||
|
if (
|
||||||
|
(!fromObject || trans.transaction.fromObject === fromObject) &&
|
||||||
|
(!toObject || trans.transaction.toObject === toObject) &&
|
||||||
|
(fromName === "" ||
|
||||||
|
objectNames
|
||||||
|
.get(trans.transaction.fromKey)
|
||||||
|
?.toLowerCase()
|
||||||
|
.includes(fromName.toLowerCase())) &&
|
||||||
|
(toName === "" ||
|
||||||
|
objectNames
|
||||||
|
.get(trans.transaction.toKey)
|
||||||
|
?.toLowerCase()
|
||||||
|
.includes(toName.toLowerCase()))
|
||||||
|
) {
|
||||||
|
temp.push(trans);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setFilteredTransactions(temp);
|
||||||
|
}, [fromObject, toObject, totalTransactions, fromName, toName, objectNames]);
|
||||||
|
|
||||||
|
//useEffect to get the transactions when the timeframe changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoading) {
|
||||||
|
setIsLoading(true);
|
||||||
|
transactionAPI
|
||||||
|
.transactionPageData(startDate.toISOString(), endDate.toISOString())
|
||||||
|
.then(resp => {
|
||||||
|
setTotalTransactions(
|
||||||
|
resp.data.transactions.map((t: pond.Transaction) => Transaction.create(t))
|
||||||
|
);
|
||||||
|
let tempMap: Map<string, string> = new Map();
|
||||||
|
Object.keys(resp.data.nameMap).forEach(key => {
|
||||||
|
tempMap.set(key, resp.data.nameMap[key]);
|
||||||
|
});
|
||||||
|
setObjectNames(tempMap);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log("There was a problem loading your inventory transactions");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [transactionAPI, startDate, endDate]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<Box className={classes.page}>
|
||||||
|
<TimeBar startDate={startDate} endDate={endDate} updateDateRange={updateDateRange} />
|
||||||
|
<Typography>Type</Typography>
|
||||||
|
<Grid container direction="row" spacing={2} style={{ margin: 10 }}>
|
||||||
|
<Grid>
|
||||||
|
<SearchSelect
|
||||||
|
style={{ width: 175 }}
|
||||||
|
label="From"
|
||||||
|
selected={selectedFromObject}
|
||||||
|
options={transactionOptions}
|
||||||
|
changeSelection={op => {
|
||||||
|
setSelectedFromObject(op);
|
||||||
|
setFromObject(op?.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<SearchSelect
|
||||||
|
style={{ width: 175 }}
|
||||||
|
label="To"
|
||||||
|
selected={selectedToObject}
|
||||||
|
options={transactionOptions}
|
||||||
|
changeSelection={op => {
|
||||||
|
setSelectedToObject(op);
|
||||||
|
setToObject(op?.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Typography>Name</Typography>
|
||||||
|
<Grid container direction="row" spacing={2} style={{ margin: 10 }}>
|
||||||
|
<Grid>
|
||||||
|
<TextField
|
||||||
|
label="From"
|
||||||
|
variant="outlined"
|
||||||
|
style={{ width: 175 }}
|
||||||
|
value={fromName}
|
||||||
|
onChange={e => {
|
||||||
|
setFromName(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<TextField
|
||||||
|
label="To"
|
||||||
|
variant="outlined"
|
||||||
|
style={{ width: 175 }}
|
||||||
|
value={toName}
|
||||||
|
onChange={e => {
|
||||||
|
setToName(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<TransactionViewer
|
||||||
|
transactions={filteredTransactions}
|
||||||
|
loading={isLoading}
|
||||||
|
objectNames={objectNames}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
42
src/transactions/transactionDataDisplay.tsx
Normal file
42
src/transactions/transactionDataDisplay.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import GrainDescriber from "grain/GrainDescriber";
|
||||||
|
import { Transaction } from "models/Transaction";
|
||||||
|
import { pond } from "protobuf-ts/pond";
|
||||||
|
import React from "react";
|
||||||
|
import { getGrainUnit } from "utils";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
transaction: Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TransactionDataDisplay(props: Props) {
|
||||||
|
const { transaction } = props;
|
||||||
|
|
||||||
|
const display = () => {
|
||||||
|
let transactionData = transaction.transaction.transaction;
|
||||||
|
if (transactionData) {
|
||||||
|
if (transactionData.grainTransaction) {
|
||||||
|
let gt = transactionData.grainTransaction;
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Typography>
|
||||||
|
Grain:{" "}
|
||||||
|
{gt.grainType === pond.Grain.GRAIN_CUSTOM
|
||||||
|
? gt.customGrain
|
||||||
|
: GrainDescriber(gt.grainType).name}
|
||||||
|
</Typography>
|
||||||
|
<Typography>Variant: {gt.subtype}</Typography>
|
||||||
|
<Typography>
|
||||||
|
{getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT && gt.bushelsPerTonne > 1
|
||||||
|
? "Weight: " + gt.bushels / gt.bushelsPerTonne + " mT"
|
||||||
|
: "Bushels: " + gt.bushels}
|
||||||
|
</Typography>
|
||||||
|
<Typography>Message: {gt.message}</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return <React.Fragment>{display()}</React.Fragment>;
|
||||||
|
}
|
||||||
124
src/transactions/transactionViewer.tsx
Normal file
124
src/transactions/transactionViewer.tsx
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
import ResponsiveTable, { Column } from "common/ResponsiveTable";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
//import { getTableIcons } from "common/ResponsiveTable";
|
||||||
|
//import MaterialTable, { Column, MTableToolbar } from "material-table";
|
||||||
|
import { Transaction } from "models/Transaction";
|
||||||
|
import moment from "moment";
|
||||||
|
import ObjectDescriber from "objects/ObjectDescriber";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import TransactionDataDisplay from "./transactionDataDisplay";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
transactions: Transaction[];
|
||||||
|
loading?: boolean;
|
||||||
|
objectNames?: Map<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TransactionViewer(props: Props) {
|
||||||
|
const { transactions, loading, objectNames } = props;
|
||||||
|
const [displayedRows, setDisplayedRows] = useState<Transaction[]>([])
|
||||||
|
const [tablePage, setTablePage] = useState(0)
|
||||||
|
const [pageSize, setPageSize] = useState(10)
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
// let rows: Transaction[] = []
|
||||||
|
// transactions.forEach((transaction, index) => {
|
||||||
|
// if (index >= tablePage * pageSize && index < (tablePage + 1) * pageSize){
|
||||||
|
// rows.push(transaction)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
let clone: Transaction[] = cloneDeep(transactions)
|
||||||
|
let rows = clone.splice(tablePage * pageSize, pageSize)
|
||||||
|
console.log(transactions)
|
||||||
|
console.log(rows)
|
||||||
|
setDisplayedRows(rows)
|
||||||
|
},[transactions, tablePage, pageSize])
|
||||||
|
|
||||||
|
const columns: Column<Transaction>[] = [
|
||||||
|
{
|
||||||
|
title: "Date",
|
||||||
|
render: row => {
|
||||||
|
return <Typography>{moment(row.transaction.timestamp).format("YYYY-MM-DD")}</Typography>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "From",
|
||||||
|
render: row => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Typography>{ObjectDescriber(row.transaction.fromObject).name}</Typography>
|
||||||
|
<Typography>
|
||||||
|
{objectNames ? objectNames.get(row.transaction.fromKey) : row.transaction.fromKey}
|
||||||
|
</Typography>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "To",
|
||||||
|
render: row => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Typography>{ObjectDescriber(row.transaction.toObject).name}</Typography>
|
||||||
|
<Typography>
|
||||||
|
{objectNames ? objectNames.get(row.transaction.toKey) : row.transaction.toKey}
|
||||||
|
</Typography>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Transaction",
|
||||||
|
render: row => {
|
||||||
|
return <TransactionDataDisplay transaction={row} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const myTable = () => {
|
||||||
|
return (
|
||||||
|
<ResponsiveTable<Transaction>
|
||||||
|
page={tablePage}
|
||||||
|
pageSize={pageSize}
|
||||||
|
rows={displayedRows}
|
||||||
|
total={transactions.length}
|
||||||
|
setPage={page => setTablePage(page)}
|
||||||
|
handleRowsPerPageChange={() => {}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
// return (
|
||||||
|
// <MaterialTable
|
||||||
|
// isLoading={loading}
|
||||||
|
// key="devicesList"
|
||||||
|
// icons={getTableIcons()}
|
||||||
|
// columns={columns}
|
||||||
|
// data={transactions}
|
||||||
|
// options={{
|
||||||
|
// paging: true,
|
||||||
|
// pageSize: 10,
|
||||||
|
// pageSizeOptions: [5, 10, 20],
|
||||||
|
// padding: "dense",
|
||||||
|
// showTitle: true,
|
||||||
|
// toolbar: true,
|
||||||
|
// sorting: true,
|
||||||
|
// columnsButton: true,
|
||||||
|
// search: false
|
||||||
|
// }}
|
||||||
|
// localization={{
|
||||||
|
// body: { emptyDataSourceMessage: loading ? "" : "No Transactions found" }
|
||||||
|
// }}
|
||||||
|
// components={{
|
||||||
|
// Toolbar: props => (
|
||||||
|
// <React.Fragment>
|
||||||
|
// <MTableToolbar {...props} />
|
||||||
|
// </React.Fragment>
|
||||||
|
// )
|
||||||
|
// }}
|
||||||
|
// title="Inventory Transactions"
|
||||||
|
// />
|
||||||
|
// );
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Box>{myTable()}</Box>;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue