660 lines
21 KiB
TypeScript
660 lines
21 KiB
TypeScript
import {
|
|
Box,
|
|
Button,
|
|
DialogActions,
|
|
DialogContent,
|
|
DialogTitle,
|
|
FormControlLabel,
|
|
Grid2 as Grid,
|
|
InputAdornment,
|
|
Step,
|
|
StepLabel,
|
|
Stepper,
|
|
Switch,
|
|
Tab,
|
|
Tabs,
|
|
TextField,
|
|
Typography
|
|
} from "@mui/material";
|
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
|
import SearchSelect from "common/SearchSelect";
|
|
import GrainDescriber from "grain/GrainDescriber";
|
|
import { Contract } from "models/Contract";
|
|
import moment from "moment";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { useGlobalState, useTaskAPI, useContractAPI } from "providers";
|
|
import React, { useEffect, useState } from "react";
|
|
import { getGrainUnit } from "utils";
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
close: () => void;
|
|
contract?: Contract;
|
|
//if we do other types of contracts (not grain) the default type should be an input prop
|
|
}
|
|
|
|
interface InputLabels {
|
|
commodityLabel: string;
|
|
sizeLabel: string;
|
|
deliveryLabel: string;
|
|
conversionLabel: string;
|
|
adornment: string;
|
|
}
|
|
|
|
export default function ContractSettings(props: Props) {
|
|
const { open, close, contract } = props;
|
|
const contractAPI = useContractAPI();
|
|
const [name, setName] = useState("");
|
|
const [contractID, setContractID] = useState(""); //id
|
|
const [holder, setHolder] = useState(""); //holder
|
|
const [deliveryStart, setDeliveryStart] = useState(moment().format("YYYY-MM-DD")); //deliverystart
|
|
const [deliveryEnd, setDeliveryEnd] = useState(moment().format("YYYY-MM-DD")); //deliveryend
|
|
const [contractType, setContractType] = useState<pond.ContractType>(1); //contracttype -- defaults to grain
|
|
//const [selectedContractType, setSelectedContractType] = useState<Option | null>(null);
|
|
const [customCommodity, setCustomCommodity] = useState(""); //customcommodity
|
|
const [contractSize, setContractSize] = useState("0"); //contractsize
|
|
const [deliveredAmount, setDeliveredAmount] = useState("0"); //deliveredamount
|
|
const [contractValue, setContractValue] = useState("0"); //contractvalue
|
|
const [commodity, setCommodity] = useState(0); //commodity
|
|
const [useCustom, setUseCustom] = useState(false);
|
|
const [currentTab, setCurrentTab] = useState(0);
|
|
const [contractDate, setContractDate] = useState(moment().format("YYYY-MM-DD"));
|
|
const [deleteWindow, setDeleteWindow] = useState(false);
|
|
const [createDeliveryTask, setCreateDeliveryTask] = useState(false);
|
|
const [conversionValue, setConversionValue] = useState("1");
|
|
const [labels, setLabels] = useState<InputLabels>({
|
|
sizeLabel: "Size",
|
|
commodityLabel: "Commodity",
|
|
conversionLabel: "Primary units per Secondary Unit",
|
|
deliveryLabel: "Delivered",
|
|
adornment: ""
|
|
});
|
|
const taskAPI = useTaskAPI();
|
|
const [{ user }] = useGlobalState();
|
|
|
|
const closeSettings = () => {
|
|
close();
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (contract) {
|
|
setName(contract.name());
|
|
setContractID(contract.settings.contractId);
|
|
setHolder(contract.settings.contractHolder);
|
|
setDeliveryStart(contract.settings.deliveryWindow?.startDate ?? "");
|
|
setDeliveryEnd(contract.settings.deliveryWindow?.endDate ?? "");
|
|
setContractType(contract.settings.type);
|
|
setCustomCommodity(contract.settings.customCommodity);
|
|
setContractSize(contract.sizeInPreferredUnit().toFixed(2));
|
|
setDeliveredAmount(contract.deliveredInPreferredUnit().toFixed(2));
|
|
setContractValue(contract.settings.totalValue.toString());
|
|
setCommodity(contract.settings.commodity);
|
|
setContractDate(contract.settings.contractDate);
|
|
setConversionValue(
|
|
contract.settings.conversionValue > 0 ? contract.settings.conversionValue.toString() : "1"
|
|
);
|
|
setUseCustom(contract.isCustom());
|
|
}
|
|
}, [contract]);
|
|
|
|
useEffect(() => {
|
|
switch (contractType) {
|
|
case pond.ContractType.CONTRACT_TYPE_GRAIN:
|
|
setLabels({
|
|
sizeLabel: "Total Grain on Contract",
|
|
commodityLabel: "Select Grain Type",
|
|
conversionLabel: "Bushels Per Tonne",
|
|
deliveryLabel: "Grain Delivered",
|
|
adornment: getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT ? "mT" : "bu"
|
|
});
|
|
break;
|
|
default:
|
|
setLabels({
|
|
sizeLabel: "Size",
|
|
commodityLabel: "Commodity",
|
|
conversionLabel: "Primary units per Secondary Unit",
|
|
deliveryLabel: "Delivered",
|
|
adornment: ""
|
|
});
|
|
break;
|
|
}
|
|
}, [contractType]);
|
|
|
|
const createTask = (contractKey: string) => {
|
|
let task: pond.TaskSettings = pond.TaskSettings.create({
|
|
userId: user.id(),
|
|
title: name + " Delivery Window",
|
|
start: deliveryStart,
|
|
startTime: "00:00",
|
|
end: deliveryEnd,
|
|
endTime: "00:00",
|
|
objectKey: contractKey,
|
|
description: "The window for delivering grain for the " + name + " contract",
|
|
worker: user.id(),
|
|
complete: false,
|
|
colour: "22194D"
|
|
});
|
|
|
|
taskAPI
|
|
.addTask(task)
|
|
.then(resp => {
|
|
console.log("Task Created");
|
|
})
|
|
.catch(err => {
|
|
console.log("error creating task");
|
|
});
|
|
};
|
|
|
|
const submit = () => {
|
|
const conVal = isNaN(parseFloat(conversionValue)) ? 1 : parseFloat(conversionValue);
|
|
const deliveredVal = isNaN(parseFloat(deliveredAmount))
|
|
? 0
|
|
: Math.round(Contract.toStoredUnit(parseFloat(deliveredAmount), contractType, conVal));
|
|
const sizeVal = isNaN(parseFloat(contractSize))
|
|
? 0
|
|
: Math.round(Contract.toStoredUnit(parseFloat(contractSize), contractType, conVal));
|
|
if (contract) {
|
|
let settings: pond.ContractSettings = contract.settings;
|
|
settings.commodity = commodity;
|
|
settings.contractHolder = holder;
|
|
settings.contractId = contractID;
|
|
settings.customCommodity = customCommodity;
|
|
settings.delivered = deliveredVal;
|
|
settings.deliveryWindow = pond.ContractDelivery.create({
|
|
startDate: deliveryStart,
|
|
endDate: deliveryEnd
|
|
});
|
|
settings.conversionValue = conVal;
|
|
settings.size = sizeVal;
|
|
settings.totalValue = isNaN(parseFloat(contractValue)) ? 0 : parseFloat(contractValue);
|
|
settings.type = contractType;
|
|
settings.contractDate = contractDate;
|
|
|
|
contractAPI
|
|
.updateContract(contract.key(), name, settings)
|
|
.then(resp => {
|
|
console.log("Updated Contract");
|
|
close();
|
|
})
|
|
.catch(err => {
|
|
console.log("Error updating contract");
|
|
});
|
|
} else {
|
|
let settings: pond.ContractSettings = pond.ContractSettings.create();
|
|
settings.commodity = commodity;
|
|
settings.contractHolder = holder;
|
|
settings.contractId = contractID;
|
|
settings.customCommodity = customCommodity;
|
|
//settings.commoditySubtype = variant;
|
|
settings.delivered = deliveredVal;
|
|
settings.deliveryWindow = pond.ContractDelivery.create({
|
|
startDate: deliveryStart,
|
|
endDate: deliveryEnd
|
|
});
|
|
settings.size = sizeVal;
|
|
settings.totalValue = isNaN(parseFloat(contractValue)) ? 0 : parseFloat(contractValue);
|
|
settings.type = contractType;
|
|
settings.conversionValue = conVal;
|
|
settings.contractDate = contractDate;
|
|
|
|
contractAPI
|
|
.addContract(name, settings)
|
|
.then(resp => {
|
|
if (createDeliveryTask) {
|
|
createTask(resp.data.key);
|
|
}
|
|
close();
|
|
})
|
|
.catch(err => {
|
|
console.log("Error adding contract");
|
|
});
|
|
}
|
|
};
|
|
|
|
const deleteContract = () => {
|
|
if (contract) {
|
|
contractAPI
|
|
.removeContract(contract.key())
|
|
.then(resp => {
|
|
console.log("contract deleted");
|
|
close();
|
|
})
|
|
.catch(err => {
|
|
console.log("failed to delete contract");
|
|
});
|
|
}
|
|
};
|
|
|
|
const deleteConfirmation = () => {
|
|
return (
|
|
<ResponsiveDialog
|
|
open={deleteWindow}
|
|
onClose={() => {
|
|
setDeleteWindow(false);
|
|
}}>
|
|
<DialogTitle>Delete Contract</DialogTitle>
|
|
<DialogContent>Are you sure you want to delete this contract</DialogContent>
|
|
<DialogActions>
|
|
<Button
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={() => {
|
|
setDeleteWindow(false);
|
|
}}
|
|
style={{ marginRight: 10 }}>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
variant="contained"
|
|
color="primary"
|
|
style={{ backgroundColor: "red" }}
|
|
onClick={deleteContract}>
|
|
Delete
|
|
</Button>
|
|
</DialogActions>
|
|
</ResponsiveDialog>
|
|
);
|
|
};
|
|
|
|
const stepValid = () => {
|
|
let valid = true;
|
|
if (currentTab === 0) {
|
|
if (name === "" || holder === "" || contractID === "") {
|
|
valid = false;
|
|
}
|
|
} else if (currentTab === 1) {
|
|
if (contractType === pond.ContractType.CONTRACT_TYPE_UNKNOWN) {
|
|
valid = false;
|
|
} else if (
|
|
commodity === 0 ||
|
|
(commodity === Contract.customValue(contractType) &&
|
|
(customCommodity === "" || conversionValue === ""))
|
|
) {
|
|
valid = false;
|
|
}
|
|
}
|
|
return valid;
|
|
};
|
|
|
|
const general = () => {
|
|
return (
|
|
<React.Fragment>
|
|
<Typography style={{ fontSize: 13, margin: 20 }}>
|
|
General information about your contract such as: name, reference number, contract holder
|
|
and delivery window will help organize your contracts for easy reference and deliveries.
|
|
All fields marked with “*” are required but all fields are recommended
|
|
</Typography>
|
|
<TextField
|
|
fullWidth
|
|
margin="dense"
|
|
value={name}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => {
|
|
setName(e.target.value);
|
|
}}
|
|
label={"*Contract Name"}
|
|
/>
|
|
<TextField
|
|
fullWidth
|
|
margin="dense"
|
|
type="date"
|
|
label="Contract Signing Date"
|
|
value={contractDate}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => setContractDate(e.target.value)}
|
|
/>
|
|
<TextField
|
|
fullWidth
|
|
margin="dense"
|
|
value={contractID}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => {
|
|
setContractID(e.target.value);
|
|
}}
|
|
label={"*Reference #"}
|
|
/>
|
|
<TextField
|
|
fullWidth
|
|
margin="dense"
|
|
value={holder}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => {
|
|
setHolder(e.target.value);
|
|
}}
|
|
label={"*Contract Holder"}
|
|
/>
|
|
<Box border={"2px solid grey"} borderRadius={5} padding={2} marginTop={2} marginBottom={2}>
|
|
<Typography>Delivery Window</Typography>
|
|
<TextField
|
|
style={{ width: "45%" }}
|
|
margin="dense"
|
|
type="date"
|
|
label="Start"
|
|
value={deliveryStart}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => setDeliveryStart(e.target.value)}
|
|
/>
|
|
<TextField
|
|
style={{ width: "45%", marginLeft: "10%" }}
|
|
margin="dense"
|
|
type="date"
|
|
label="End"
|
|
value={deliveryEnd}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => setDeliveryEnd(e.target.value)}
|
|
/>
|
|
</Box>
|
|
{!contract && (
|
|
<FormControlLabel
|
|
control={
|
|
<Switch
|
|
checked={createDeliveryTask}
|
|
onChange={(_, checked) => {
|
|
setCreateDeliveryTask(checked);
|
|
}}
|
|
name="Create Task For Delivery Window"
|
|
/>
|
|
}
|
|
//disabled={!canEdit}
|
|
label="Create Task For Delivery Window"
|
|
labelPlacement="end"
|
|
/>
|
|
)}
|
|
<TextField
|
|
fullWidth
|
|
margin="dense"
|
|
type="number"
|
|
value={contractValue}
|
|
InputProps={{
|
|
startAdornment: <InputAdornment position="start">$</InputAdornment>
|
|
}}
|
|
error={isNaN(parseFloat(contractValue))}
|
|
helperText={isNaN(parseFloat(contractValue)) ? "Must be a valid number" : ""}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => {
|
|
setContractValue(e.target.value);
|
|
}}
|
|
label={"Contract Value"}
|
|
/>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
const details = () => {
|
|
return (
|
|
<React.Fragment>
|
|
<Box>
|
|
<Typography
|
|
style={{ fontSize: 13, paddingLeft: 5, paddingRight: 5, margin: 20, marginBottom: 0 }}>
|
|
Select Grain Type, Total Grain on Contract (bu), and Grain Delivered. These settings can
|
|
be changed at anytime. If your grain type is not listed use the “Custom” toggle.
|
|
</Typography>
|
|
<Typography
|
|
style={{
|
|
fontSize: 13,
|
|
padding: 5,
|
|
border: "1px solid grey",
|
|
borderRadius: 5,
|
|
margin: 20
|
|
}}>
|
|
Note: When delivering grain on contract from a field or a bin the Grain Types must match
|
|
</Typography>
|
|
</Box>
|
|
<Grid
|
|
container
|
|
direction="row"
|
|
alignContent="center"
|
|
alignItems="center"
|
|
style={{ marginTop: 10 }}>
|
|
<Grid size={8}>
|
|
<SearchSelect
|
|
label="Contract Type*"
|
|
selected={Contract.toTypeOption(contractType)}
|
|
changeSelection={option => {
|
|
if (option) {
|
|
setContractType(option.value);
|
|
}
|
|
}}
|
|
options={Contract.typeOptions()}
|
|
/>
|
|
</Grid>
|
|
<Grid size={4}>
|
|
<FormControlLabel
|
|
control={
|
|
<Switch
|
|
checked={useCustom}
|
|
onChange={(_, checked) => {
|
|
setUseCustom(checked);
|
|
setCustomCommodity("");
|
|
if (checked) {
|
|
setCommodity(Contract.customValue(contractType));
|
|
} else {
|
|
setCommodity(0);
|
|
}
|
|
}}
|
|
name="Custom"
|
|
/>
|
|
}
|
|
//disabled={!canEdit}
|
|
label="Custom"
|
|
labelPlacement="start"
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
{!useCustom ? (
|
|
<SearchSelect
|
|
style={{ marginTop: 10 }}
|
|
label={labels.commodityLabel + "*"}
|
|
selected={Contract.toCommodityOption(contractType, commodity)}
|
|
changeSelection={option => {
|
|
if (option) {
|
|
let c = 0;
|
|
let conversion = 1;
|
|
if (contractType) {
|
|
switch (contractType) {
|
|
case pond.ContractType.CONTRACT_TYPE_GRAIN:
|
|
c = pond.Grain[option.value as keyof typeof pond.Grain];
|
|
conversion = GrainDescriber(c).bushelsPerTonne;
|
|
break;
|
|
default:
|
|
c = option.value;
|
|
}
|
|
}
|
|
setCommodity(c);
|
|
setConversionValue(conversion.toFixed(2));
|
|
}
|
|
}}
|
|
options={Contract.commodityOptionsForType(contractType)}
|
|
/>
|
|
) : (
|
|
<React.Fragment>
|
|
<TextField
|
|
fullWidth
|
|
margin="dense"
|
|
label="Custom*"
|
|
value={customCommodity}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => setCustomCommodity(e.target.value)}
|
|
/>
|
|
<TextField
|
|
fullWidth
|
|
margin="dense"
|
|
type="number"
|
|
label={labels.conversionLabel}
|
|
value={conversionValue}
|
|
InputLabelProps={{ shrink: true }}
|
|
error={isNaN(parseFloat(conversionValue))}
|
|
helperText={isNaN(parseFloat(conversionValue)) ? "Must be a valid number" : ""}
|
|
onChange={e => setConversionValue(e.target.value)}
|
|
/>
|
|
</React.Fragment>
|
|
)}
|
|
{/* <TextField
|
|
fullWidth
|
|
margin="dense"
|
|
type="variant"
|
|
label="Variant"
|
|
value={variant}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => setVariant(e.target.value)}
|
|
/> */}
|
|
<TextField
|
|
fullWidth
|
|
margin="dense"
|
|
type="number"
|
|
InputProps={{
|
|
endAdornment: <InputAdornment position="end">{labels.adornment}</InputAdornment>
|
|
}}
|
|
value={contractSize}
|
|
error={isNaN(parseFloat(contractSize))}
|
|
helperText={isNaN(parseFloat(contractSize)) ? "Must be a valid number" : ""}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => {
|
|
setContractSize(e.target.value);
|
|
}}
|
|
label={labels.sizeLabel}
|
|
/>
|
|
<TextField
|
|
fullWidth
|
|
margin="dense"
|
|
type="number"
|
|
InputProps={{
|
|
endAdornment: <InputAdornment position="end">{labels.adornment}</InputAdornment>
|
|
}}
|
|
value={deliveredAmount}
|
|
error={isNaN(parseFloat(deliveredAmount))}
|
|
helperText={isNaN(parseFloat(deliveredAmount)) ? "Must be a valid number" : ""}
|
|
InputLabelProps={{ shrink: true }}
|
|
onChange={e => {
|
|
setDeliveredAmount(e.target.value);
|
|
}}
|
|
label={labels.deliveryLabel}
|
|
/>
|
|
</React.Fragment>
|
|
);
|
|
};
|
|
|
|
const stepper = () => {
|
|
if (contract === undefined) {
|
|
return (
|
|
<Stepper activeStep={currentTab}>
|
|
<Step key={"general"}>
|
|
<StepLabel>General</StepLabel>
|
|
</Step>
|
|
<Step key={"details"}>
|
|
<StepLabel>Commodity</StepLabel>
|
|
</Step>
|
|
</Stepper>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box display="flex" justifyContent="center" width="100%">
|
|
<Tabs
|
|
value={currentTab}
|
|
onChange={(_, value) => setCurrentTab(value)}
|
|
indicatorColor="primary"
|
|
textColor="primary"
|
|
variant="fullWidth"
|
|
aria-label="bin tabs"
|
|
//classes={{ root: classes.tabs }}
|
|
>
|
|
<Tab label="General" />
|
|
<Tab label="Commodity" />
|
|
</Tabs>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const stepperContent = (step: number) => {
|
|
switch (step) {
|
|
case 1:
|
|
return details();
|
|
default:
|
|
return general();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<ResponsiveDialog open={open} onClose={closeSettings}>
|
|
<DialogTitle>Contract Settings</DialogTitle>
|
|
<DialogContent>
|
|
{/* <Tabs
|
|
value={currentTab}
|
|
onChange={(_, value) => setCurrentTab(value)}>
|
|
<Tab label="General" />
|
|
<Tab label="Commodity" />
|
|
</Tabs>
|
|
<TabPanelMine value={currentTab} index={0}>
|
|
{general()}
|
|
</TabPanelMine>
|
|
<TabPanelMine value={currentTab} index={1}>
|
|
{details()}
|
|
</TabPanelMine> */}
|
|
{stepper()}
|
|
{stepperContent(currentTab)}
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Grid container direction="row-reverse" justifyContent="space-between">
|
|
<Grid>
|
|
<Button
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={close}
|
|
style={{ marginRight: 10 }}>
|
|
Cancel
|
|
</Button>
|
|
{!contract && currentTab > 0 && (
|
|
<Button
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={() => {
|
|
setCurrentTab(currentTab - 1);
|
|
}}
|
|
style={{ marginRight: 10 }}>
|
|
Back
|
|
</Button>
|
|
)}
|
|
{(contract || currentTab === 1) && (
|
|
<Button
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={submit}
|
|
style={{ marginRight: 10 }}
|
|
disabled={!stepValid()}>
|
|
Submit
|
|
</Button>
|
|
)}
|
|
{!contract && currentTab < 1 && (
|
|
<Button
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={() => {
|
|
setCurrentTab(currentTab + 1);
|
|
}}
|
|
style={{ marginRight: 10 }}
|
|
disabled={!stepValid()}>
|
|
Next
|
|
</Button>
|
|
)}
|
|
</Grid>
|
|
{contract && (
|
|
<Grid>
|
|
<Button
|
|
variant="contained"
|
|
style={{ backgroundColor: "red" }}
|
|
onClick={() => {
|
|
setDeleteWindow(true);
|
|
}}>
|
|
Delete
|
|
</Button>
|
|
</Grid>
|
|
)}
|
|
</Grid>
|
|
</DialogActions>
|
|
</ResponsiveDialog>
|
|
{deleteConfirmation()}
|
|
</React.Fragment>
|
|
);
|
|
}
|