added to transaction api and built the submit function

This commit is contained in:
csawatzky 2025-05-30 14:50:21 -06:00
parent 49bd146de6
commit a083f7d8ed
3 changed files with 97 additions and 16 deletions

2
package-lock.json generated
View file

@ -10864,7 +10864,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#6bf3baa3e9db19f0d7031dda1c3045f6f64c515b", "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#8269078a53ebd9089b32bc7861c3e53698ba4166",
"dependencies": { "dependencies": {
"protobufjs": "^6.8.8" "protobufjs": "^6.8.8"
} }

View file

@ -1,3 +1,4 @@
import { CheckOutlined } from "@mui/icons-material";
import { Autocomplete, Box, Button, Card, DialogActions, DialogContent, DialogTitle, TextField, Typography } from "@mui/material"; import { Autocomplete, Box, Button, Card, DialogActions, DialogContent, DialogTitle, TextField, Typography } from "@mui/material";
import ResponsiveDialog from "common/ResponsiveDialog"; import ResponsiveDialog from "common/ResponsiveDialog";
import ResponsiveTable from "common/ResponsiveTable"; import ResponsiveTable from "common/ResponsiveTable";
@ -9,7 +10,7 @@ import { Transaction } from "models/Transaction";
import moment from "moment"; import moment from "moment";
import ObjectDescriber from "objects/ObjectDescriber"; import ObjectDescriber from "objects/ObjectDescriber";
import { pond } from "protobuf-ts/pond"; import { pond } from "protobuf-ts/pond";
import { useBinAPI, useContractAPI, useFieldAPI, useGrainBagAPI, useTransactionAPI } from "providers"; import { useBinAPI, useContractAPI, useFieldAPI, useGrainBagAPI, useSnackbar, useTransactionAPI } from "providers";
import React from "react"; import React from "react";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import TransactionDataDisplay from "transactions/transactionDataDisplay"; import TransactionDataDisplay from "transactions/transactionDataDisplay";
@ -31,6 +32,7 @@ export default function BinTransactions(props: Props){
const [possibleTransactionMatches, setPossibleTransactionMatches] = useState<Transaction[]>([]) const [possibleTransactionMatches, setPossibleTransactionMatches] = useState<Transaction[]>([])
const [tablePage, setTablePage] = useState(0) const [tablePage, setTablePage] = useState(0)
const [tablePageSize, setTablePageSize] = useState(10) const [tablePageSize, setTablePageSize] = useState(10)
const { openSnack } = useSnackbar();
const [total, setTotal] = useState(0) const [total, setTotal] = useState(0)
const [binOptions, setBinOptions] = useState<Option[]>([]) const [binOptions, setBinOptions] = useState<Option[]>([])
@ -106,8 +108,32 @@ export default function BinTransactions(props: Props){
},[state, bin, loadTransactionsFor]) },[state, bin, loadTransactionsFor])
const submit = () => { const submit = () => {
let transaction = cloneDeep(selectedTransaction)
if(transaction){
if(selectedMatch){
if (transaction.transaction.fromKey === ""){
transaction.transaction.fromObject = selectedMatch.transaction.fromObject
transaction.transaction.fromKey = selectedMatch.transaction.fromKey
}
if (transaction.transaction.toKey === ""){
transaction.transaction.toObject = selectedMatch.transaction.toObject
transaction.transaction.toKey = selectedMatch.transaction.toKey
}
}
//submit approval //submit approval
transactionAPI.submitApproval(transaction.transaction.key, true, transaction.transaction).then(resp => {
//in the response if it succeeded remove (not reject but fully delete it) the matched transaction //in the response if it succeeded remove (not reject but fully delete it) the matched transaction
if(selectedMatch){
transactionAPI.removeTransaction(selectedMatch.transaction.key).then(resp => {
openSnack("Updated and matched transactions")
}).catch(err => {
})
}else{
openSnack("Updated Transaction")
}
})
}
} }
const reject = () => { const reject = () => {
@ -145,9 +171,10 @@ export default function BinTransactions(props: Props){
<DialogContent> <DialogContent>
<Box> <Box>
<Typography>{getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT ? weight + " mT" : bushels + " bushels"} of {grainTransaction.subtype} {grainDisplay} moved</Typography> <Typography>{getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT ? weight + " mT" : bushels + " bushels"} of {grainTransaction.subtype} {grainDisplay} moved</Typography>
<Typography>From</Typography> <Typography margin={1} fontWeight={650}>From:</Typography>
{/*Object type selection*/} {/*Object type selection*/}
<Autocomplete <Autocomplete
sx={{marginY: 1}}
disabled={fromDisabled} disabled={fromDisabled}
id="autoFromTypes" id="autoFromTypes"
value={fromType} value={fromType}
@ -158,7 +185,7 @@ export default function BinTransactions(props: Props){
]} ]}
getOptionLabel={(option: pond.ObjectType) => ObjectDescriber(option).name} getOptionLabel={(option: pond.ObjectType) => ObjectDescriber(option).name}
fullWidth fullWidth
onChange={(e, val) => { onChange={(_, val) => {
setFromType(val ?? pond.ObjectType.OBJECT_TYPE_UNKNOWN) setFromType(val ?? pond.ObjectType.OBJECT_TYPE_UNKNOWN)
//based on what it is changed to need to load data if it is not already loaded //based on what it is changed to need to load data if it is not already loaded
if(val === pond.ObjectType.OBJECT_TYPE_BIN){ if(val === pond.ObjectType.OBJECT_TYPE_BIN){
@ -175,13 +202,14 @@ export default function BinTransactions(props: Props){
/> />
{/* object selection */} {/* object selection */}
<Autocomplete <Autocomplete
sx={{marginY: 1}}
disabled={fromDisabled} disabled={fromDisabled}
id="autoFromObjects" id="autoFromObjects"
value={fromOption} value={fromOption}
options={getOptions(fromType)} options={getOptions(fromType)}
getOptionLabel={(option: Option) => option.label} getOptionLabel={(option: Option) => option.label}
fullWidth fullWidth
onChange={(e, val) => { onChange={(_, val) => {
setFromOption(val ?? undefined) setFromOption(val ?? undefined)
//once one is selected get the transactions for it that are requiring approval //once one is selected get the transactions for it that are requiring approval
transactionAPI.listTransactionsFor(0, 0, "asc", val?.value, pond.TransactionState.TRANSACTION_STATE_APPROVAL_REQUIRED).then(resp => { transactionAPI.listTransactionsFor(0, 0, "asc", val?.value, pond.TransactionState.TRANSACTION_STATE_APPROVAL_REQUIRED).then(resp => {
@ -197,8 +225,9 @@ export default function BinTransactions(props: Props){
}} }}
renderInput={params => <TextField {...params} label="From" />} renderInput={params => <TextField {...params} label="From" />}
/> />
<Typography>To</Typography> <Typography margin={1} fontWeight={650}>To:</Typography>
<Autocomplete <Autocomplete
sx={{marginY: 1}}
disabled={toDisabled} disabled={toDisabled}
id="autoFromTypes" id="autoFromTypes"
value={toType} value={toType}
@ -209,7 +238,7 @@ export default function BinTransactions(props: Props){
]} ]}
getOptionLabel={(option: pond.ObjectType) => ObjectDescriber(option).name} getOptionLabel={(option: pond.ObjectType) => ObjectDescriber(option).name}
fullWidth fullWidth
onChange={(e, val) => { onChange={(_, val) => {
setToType(val ?? pond.ObjectType.OBJECT_TYPE_UNKNOWN) setToType(val ?? pond.ObjectType.OBJECT_TYPE_UNKNOWN)
//based on what it is changed to need to load data if it is not already loaded //based on what it is changed to need to load data if it is not already loaded
if(val === pond.ObjectType.OBJECT_TYPE_BIN){ if(val === pond.ObjectType.OBJECT_TYPE_BIN){
@ -225,13 +254,14 @@ export default function BinTransactions(props: Props){
renderInput={params => <TextField {...params} label="Type" />} renderInput={params => <TextField {...params} label="Type" />}
/> />
<Autocomplete <Autocomplete
sx={{marginY: 1}}
disabled={toDisabled} disabled={toDisabled}
id="autoFromObjects" id="autoFromObjects"
value={toOption} value={toOption}
options={getOptions(toType)} options={getOptions(toType)}
getOptionLabel={(option: Option) => option.label} getOptionLabel={(option: Option) => option.label}
fullWidth fullWidth
onChange={(e, val) => { onChange={(_, val) => {
//once one is selected get the transactions for it that are requiring approval //once one is selected get the transactions for it that are requiring approval
transactionAPI.listTransactionsFor(0, 0, "asc", val?.value, pond.TransactionState.TRANSACTION_STATE_APPROVAL_REQUIRED).then(resp => { transactionAPI.listTransactionsFor(0, 0, "asc", val?.value, pond.TransactionState.TRANSACTION_STATE_APPROVAL_REQUIRED).then(resp => {
let matches: Transaction[] = [] let matches: Transaction[] = []
@ -248,12 +278,41 @@ export default function BinTransactions(props: Props){
renderInput={params => <TextField {...params} label="From" />} renderInput={params => <TextField {...params} label="From" />}
/> />
</Box> </Box>
<Box> <Box paddingTop={1}>
<Typography variant="h6">Possible matching Transactions</Typography>
<Typography>Pick a transaction below if you want to match them together</Typography>
{possibleTransactionMatches.map(t => { {possibleTransactionMatches.map(t => {
// get the display information for the transaction
const grainTransaction = t.transaction.transaction?.grainTransaction ?? pond.GrainTransaction.create()
const bushels = grainTransaction.bushels ?? 0
const weight = bushels / grainTransaction.bushelsPerTonne
let graintype = grainTransaction.grainType ?? pond.Grain.GRAIN_INVALID
const grainDisplay = graintype === pond.Grain.GRAIN_CUSTOM
? grainTransaction.customGrain
: GrainDescriber(grainTransaction.grainType).name
return ( return (
<Typography> <Card sx={{marginTop: 1, padding: 1}}>
{t.transaction.timestamp} <Box display="flex" justifyContent="space-between" height={40}>
<Typography sx={{fontWeight: 650, margin: "auto", marginLeft: 0}}>
{moment(t.transaction.timestamp).fromNow()}
</Typography> </Typography>
<Box>
{selectedMatch?.transaction.key === t.transaction.key && <CheckOutlined htmlColor="green" />}
</Box>
</Box>
<Box display="flex" justifyContent="space-between">
<Typography sx={{margin: "auto", marginLeft: 0}}>{getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT ? weight + " mT" : bushels + " bushels"} of {grainTransaction.subtype} {grainDisplay} moved</Typography>
<Button
variant="contained"
disabled={selectedMatch?.transaction.key === t.transaction.key}
color="primary"
onClick={()=>{
setselectedMatch(t)
}}>
Set Match
</Button>
</Box>
</Card>
) )
})} })}
</Box> </Box>

View file

@ -40,6 +40,8 @@ export interface ITransactionAPIContext {
data: pond.TransactionData, data: pond.TransactionData,
otherTeam?: string otherTeam?: string
) => Promise<AxiosResponse<pond.UpdateTransactionResponse>>; ) => Promise<AxiosResponse<pond.UpdateTransactionResponse>>;
submitApproval: (key: string, approved: boolean, updatedData: pond.Transaction, otherTeam?: string) => Promise<AxiosResponse<pond.SubmitTransactionApprovalResponse>>;
removeTransaction: (key: string, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveTransactionResponse>>
} }
export const TransactionAPIContext = createContext<ITransactionAPIContext>( export const TransactionAPIContext = createContext<ITransactionAPIContext>(
@ -50,7 +52,7 @@ interface Props {}
export default function TransactionProvider(props: PropsWithChildren<Props>) { export default function TransactionProvider(props: PropsWithChildren<Props>) {
const { children } = props; const { children } = props;
const { get, post, put } = useHTTP(); const { get, post, put, del } = useHTTP();
const [{ as }] = useGlobalState(); const [{ as }] = useGlobalState();
const addTransaction = (transaction: pond.Transaction, fileIDs?: string[], otherTeam?: string) => { const addTransaction = (transaction: pond.Transaction, fileIDs?: string[], otherTeam?: string) => {
@ -158,6 +160,24 @@ export default function TransactionProvider(props: PropsWithChildren<Props>) {
return put<pond.TransactionPageDataResponse>(pondURL("/transactions/" + key + "/revoke")); return put<pond.TransactionPageDataResponse>(pondURL("/transactions/" + key + "/revoke"));
}; };
const submitApproval = (key: string, approval: boolean, updatedData: pond.Transaction, otherTeam?: string) => {
const view = otherTeam ? otherTeam : as
let url = "/transactions/" + key + "/approval?approve=" + approval.toString()
if(view){
url = url + "&as=" + view
}
return post<pond.SubmitTransactionApprovalResponse>(url, updatedData)
}
const removeTransaction = (key: string, otherTeam?: string) => {
const view = otherTeam ? otherTeam : as
let url = "/transactions/" + key + "/remove"
if(view){
url = url + "&as=" + view
}
return del<pond.RemoveTransactionResponse>(url)
}
return ( return (
<TransactionAPIContext.Provider <TransactionAPIContext.Provider
value={{ value={{
@ -166,7 +186,9 @@ export default function TransactionProvider(props: PropsWithChildren<Props>) {
listTransactionsFor, listTransactionsFor,
updateTransaction, updateTransaction,
transactionPageData, transactionPageData,
revokeTransaction revokeTransaction,
submitApproval,
removeTransaction
}}> }}>
{children} {children}
</TransactionAPIContext.Provider> </TransactionAPIContext.Provider>