frontend/src/pages/Contract.tsx

326 lines
11 KiB
TypeScript

import {
Box,
Button,
Card,
Drawer,
Grid2 as Grid,
IconButton,
LinearProgress,
Theme,
Typography
} from "@mui/material";
import ContractTransactionGraph from "contract/contractTransactionGraph";
import ContractVisualizer from "contract/contractVisualizer";
import { useMobile, useThemeType } from "hooks";
// import { MatchParams } from "navigation/Routes";
import { useContractAPI } from "providers/pond/contractAPI";
import React, { useEffect, useState } from "react";
// import { Redirect, useRouteMatch } from "react-router";
import PageContainer from "./PageContainer";
import { Contract as IContract } from "models/Contract";
import ObjectControls from "common/ObjectControls";
import ContractActions from "contract/contractActions";
import { useGlobalState, useUserAPI } from "providers";
import { pond } from "protobuf-ts/pond";
import { Scope } from "models";
import { Transaction } from "models/Transaction";
import ContractTransactionTable from "contract/contractTransactionTable";
import GrainTransaction from "grain/GrainTransaction";
import NotesIcon from "@mui/icons-material/Notes";
import Chat from "chat/Chat";
import ContractsIcon from "products/CommonIcons/contractIcon";
import { makeStyles } from "@mui/styles";
import { useParams } from "react-router-dom";
interface TabPanelProps {
children?: React.ReactNode;
index: any;
value: any;
}
function TabPanelMine(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
aria-labelledby={`simple-tab-${index}`}
{...other}>
{value === index && <React.Fragment>{children}</React.Fragment>}
</div>
);
}
const useStyles = makeStyles((theme: Theme) => {
const themeType = useThemeType();
return ({
inactiveButton: {
color: themeType === "light" ? theme.palette.common.black : theme.palette.common.white,
backgroundColor: "transparent",
width: theme.spacing(5),
height: theme.spacing(5),
border: "1px solid",
borderColor: theme.palette.divider
},
activeButton: {
color: themeType === "light" ? theme.palette.common.black : theme.palette.common.white,
backgroundColor: theme.palette.primary.main,
width: theme.spacing(5),
height: theme.spacing(5),
border: "1px solid",
borderColor: theme.palette.divider
},
drawerPaper: {
height: "100%",
width: "30%"
}
});
});
export default function Contract() {
// const match = useRouteMatch<MatchParams>();
// const contractKey = match.params.contractKey;
const contractKey = useParams<{ contractKey: string }>()?.contractKey ?? "";
const classes = useStyles();
const contractAPI = useContractAPI();
const isMobile = useMobile();
const [contract, setContract] = useState<IContract>(IContract.create());
const [validTransactions, setValidTransactions] = useState<Map<string, Transaction>>(new Map());
//have to use this as the array passed to the bar graph, for whatever reason getting the array from the values of the map causes the labels to not appear
const [transArray, setTransArray] = useState<Transaction[]>([]);
const [openGrainTransactionDialog, setOpenGrainTransactionDialog] = useState(false);
const [displayTab, setDisplayTab] = useState(0);
const [openNoteDrawer, setOpenNoteDrawer] = useState(false);
//const [revokedTransactions, setRevokedTransactions] = useState<Transaction[]>([]);
const userAPI = useUserAPI();
const [{ user, as }] = useGlobalState();
const [permissions, setPermissions] = useState<pond.Permission[]>([]);
const [objectMap, setObjectMap] = useState<Map<string, string>>(new Map());
const [loading, setLoading] = useState(false);
// const [invalid, setInvalid] = useState(false);
useEffect(() => {
let key = contractKey;
let kind = "contract";
if (as) {
key = as;
kind = "team";
}
userAPI.getUser(user.id(), { key: key, kind: kind } as Scope).then(resp => {
setPermissions(resp.permissions);
});
}, [as, contractKey, userAPI, user]);
useEffect(() => {
if (!loading) {
setLoading(true);
contractAPI
.getContractPageData(contractKey, as)
.then(resp => {
setContract(IContract.any(resp.data.contract));
//setValidTransactions(resp.data.transactions.map(t => Transaction.create(t)));
let valid: Map<string, Transaction> = new Map();
let tArray: Transaction[] = [];
resp.data.transactions.forEach(transaction => {
let t = Transaction.create(transaction);
if (
t.transaction.state === pond.TransactionState.TRANSACTION_STATE_VALID ||
t.transaction.state === pond.TransactionState.TRANSACTION_STATE_CORRECTION
) {
valid.set(t.transaction.key, t);
tArray.push(t);
}
// else {
// revoked.push(t);
// }
});
setValidTransactions(valid);
setTransArray(tArray);
//setRevokedTransactions(revoked);
let tempMap: Map<string, string> = new Map();
Object.keys(resp.data.nameMap).forEach(key => {
tempMap.set(key, resp.data.nameMap[key]);
});
setObjectMap(tempMap);
setLoading(false);
})
.catch(err => {
//setInvalid(true);
});
}
}, [contractKey, contractAPI, as]); // eslint-disable-line react-hooks/exhaustive-deps
const contractDisplay = () => {
return (
<React.Fragment>
{isMobile ? (
<Grid
container
alignContent="center"
alignItems="center"
direction="row"
style={{ marginBottom: 15 }}>
<Grid size={9}>
<Typography style={{ paddingRight: 20, fontSize: 30, fontWeight: 650 }} noWrap>
{contract.name()}
</Typography>
</Grid>
<Grid size={3}>
<Button
variant="contained"
color="primary"
disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)}
onClick={e => {
setOpenGrainTransactionDialog(true);
}}>
+ Add Delivery
</Button>
</Grid>
</Grid>
) : (
<Box display="flex" flexDirection="row" marginBottom={2}>
<Typography style={{ paddingRight: 20, fontSize: 30, fontWeight: 650 }} noWrap>
{contract.name()}
</Typography>
<Button
variant="contained"
color="primary"
disabled={!permissions.includes(pond.Permission.PERMISSION_WRITE)}
onClick={e => {
setOpenGrainTransactionDialog(true);
}}>
+ Add Delivery
</Button>
</Box>
)}
<Grid container spacing={2} direction={"row"}>
<Grid size={isMobile ? 12 : 4} style={{ height: isMobile ? 300 : 400 }}>
<ContractVisualizer contract={contract} />
</Grid>
<Grid size={isMobile ? 12 : 8} style={{ height: isMobile ? 300 : 400 }}>
<ContractTransactionGraph contract={contract} transactions={transArray} />
</Grid>
<Grid size={12}>
<ContractTransactionTable
permissions={permissions}
contract={contract}
transactions={transArray}
objectNames={objectMap}
transactionRevokedCallback={t => {
let vt = validTransactions;
vt.delete(t);
setValidTransactions(vt);
setTransArray(Array.from(vt.values()));
}}
/>
</Grid>
</Grid>
</React.Fragment>
);
};
return (
<PageContainer>
<ObjectControls
actions={
<ContractActions
contract={contract}
permissions={permissions}
refreshCallback={() => {}}
/>
}
objectButton={
<IconButton
size="small"
style={{
marginTop: "0.3625rem",
marginBottom: "0.3625rem",
marginRight: "0.5rem"
}}
onClick={() => {
setDisplayTab(0);
}}
className={displayTab === 0 ? classes.activeButton : classes.inactiveButton}
component="span">
<ContractsIcon />
</IconButton>
}
notesButton={
<IconButton
onClick={() => {
if (isMobile) {
setDisplayTab(1);
} else {
setOpenNoteDrawer(true);
}
}}
size="small"
style={{
marginTop: "0.3625rem",
marginBottom: "0.3625rem",
marginRight: "0.5rem"
}}
className={displayTab === 1 ? classes.activeButton : classes.inactiveButton}
component="span">
<NotesIcon />
</IconButton>
}
/>
<Box margin={2} marginTop={0}>
{loading ? (
<LinearProgress />
) : (
<React.Fragment>
<TabPanelMine value={displayTab} index={0}>
{contractDisplay()}
</TabPanelMine>
<TabPanelMine value={displayTab} index={1}>
<Typography style={{ paddingRight: 20, fontSize: 30, fontWeight: 650 }}>
{contract.name()} - Notes
</Typography>
<Card style={{ padding: 10, marginTop: 15 }}>
<Chat objectKey={contract.key()} type={pond.NoteType.NOTE_TYPE_CONTRACT} />
</Card>
</TabPanelMine>
</React.Fragment>
)}
</Box>
<GrainTransaction
open={openGrainTransactionDialog}
mainObject={contract}
allowAttachmentUploads
restrictMatching
asDestination
close={() => {
setOpenGrainTransactionDialog(false);
}}
callback={t => {
setOpenGrainTransactionDialog(false);
if (t) {
let transactions = validTransactions;
transactions.set(t.key, Transaction.create(t));
setValidTransactions(transactions);
setTransArray(Array.from(transactions.values()));
}
}}
/>
<Drawer
open={openNoteDrawer}
onClose={() => {
setOpenNoteDrawer(false);
}}
anchor="right"
classes={{ paper: classes.drawerPaper }}>
<Box padding={2}>
<Typography>Notes</Typography>
<Card style={{ padding: 10 }}>
<Chat objectKey={contract.key()} type={pond.NoteType.NOTE_TYPE_CONTRACT} />
</Card>
</Box>
</Drawer>
</PageContainer>
);
}