449 lines
14 KiB
TypeScript
449 lines
14 KiB
TypeScript
import { Box, Button, Grid2 as Grid } from "@mui/material";
|
|
// import { getTableIcons } from "common/ResponsiveTable";
|
|
import SearchSelect, { Option } from "common/SearchSelect";
|
|
// import MaterialTable, { MTableToolbar } from "material-table";
|
|
// import { Bin, BinYard, Field } from "models";
|
|
import { Bin } from "models";
|
|
// import { Gate } from "models/Gate";
|
|
// import { GrainBag } from "models/GrainBag";
|
|
// import { ObjectHeater } from "models/ObjectHeater";
|
|
import ObjectDescriber, { SearchableObjects } from "objects/ObjectDescriber";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import {
|
|
useBinAPI,
|
|
useBinYardAPI,
|
|
// useFieldAPI,
|
|
useGateAPI,
|
|
useGlobalState,
|
|
// useGrainBagAPI,
|
|
// useObjectHeaterAPI
|
|
} from "providers";
|
|
import { useCallback, useEffect, useState } from "react";
|
|
import TeamSearch from "teams/TeamSearch";
|
|
import BulkBinSettings from "./bulkEditForms/bulkBinSettings";
|
|
import ResponsiveTable from "common/ResponsiveTable";
|
|
import { cloneDeep } from "lodash";
|
|
//import BulkGateSettings from "./bulkEditForms/bulkGateSettings";
|
|
|
|
interface customButton {
|
|
label: string;
|
|
function: (selectedObjects: any[]) => void;
|
|
}
|
|
|
|
interface Props {
|
|
startingObject?: pond.ObjectType;
|
|
showControls?: boolean;
|
|
preLoadedData?: any[];
|
|
customButtons?: customButton[];
|
|
rowClickFunction?: (rowData: any) => void;
|
|
}
|
|
|
|
export default function ObjectTable(props: Props) {
|
|
const { startingObject, showControls, preLoadedData, customButtons, rowClickFunction } = props;
|
|
const [currentType, setCurrentType] = useState(
|
|
startingObject ?? pond.ObjectType.OBJECT_TYPE_UNKNOWN
|
|
);
|
|
const [selectedType, setSelectedType] = useState<Option | null>();
|
|
const [objectTotal, setObjectTotal] = useState(0);
|
|
const [pageSize, setPageSize] = useState(10); //offset would be determined by the pageSize * tablePage, limit is just pageSize
|
|
const [tablePage, setTablePage] = useState(0);
|
|
const [tableData, setTableData] = useState<any[]>([]);
|
|
const [loadedData, setLoadedData] = useState<any[]>([]);
|
|
//const [selectedObjects, setSelectedObjects] = useState<any[]>([]);
|
|
const [selectedObjects, setSelectedObjects] = useState<Map<string, any>>(new Map());
|
|
//const [currentDirection, setCurrentDirection] = useState<"asc" | "desc">("asc");
|
|
const [currentSearchText, setCurrentSearchText] = useState<string>("");
|
|
//const [currentOrder, setCurrentOrder] = useState<string | undefined>();
|
|
//const [isNumerical, setIsNumerical] = useState<boolean | undefined>();
|
|
const [{ user, as }] = useGlobalState();
|
|
const [selectedUser, setSelectedUser] = useState<string>(as ?? user.id());
|
|
const [selectedPageRows, setSelectedPageRows] = useState<Map<number, number[]>>(new Map())//key is the page value is an array of row indexes for that page
|
|
const [tableLoading, setTableLoading] = useState(false);
|
|
|
|
const objectExtension = ObjectDescriber(currentType);
|
|
const columns = objectExtension.getTableColumns ? objectExtension.getTableColumns(user) : objectExtension.tableColumns;
|
|
//the api's to load all the objects available to look at in this table
|
|
const binAPI = useBinAPI();
|
|
const binyardAPI = useBinYardAPI();
|
|
// const fieldAPI = useFieldAPI();
|
|
const gateAPI = useGateAPI();
|
|
// const grainBagAPI = useGrainBagAPI();
|
|
// const heaterAPI = useObjectHeaterAPI();
|
|
|
|
const load = useCallback(() => {
|
|
if (tableLoading) return;
|
|
/**
|
|
* switch case to use the correct api to load the object that was selected.
|
|
* In the future create a generic object api that takes the object type as a variable along with
|
|
* the rest that the backend then uses to load from the correct place
|
|
*/
|
|
//add a new case for each object that will be viewable in the table
|
|
let search;
|
|
if (currentSearchText) {
|
|
search = currentSearchText?.replace(/\s/g, "<->");
|
|
}
|
|
if (currentType !== pond.ObjectType.OBJECT_TYPE_UNKNOWN) {
|
|
setTableLoading(true);
|
|
}
|
|
switch (currentType) {
|
|
case pond.ObjectType.OBJECT_TYPE_BIN:
|
|
binAPI
|
|
.listBins(
|
|
pageSize,
|
|
pageSize * tablePage,
|
|
"asc",
|
|
"name",
|
|
search,
|
|
selectedUser,
|
|
undefined
|
|
)
|
|
.then(resp => {
|
|
//setLoadedData(resp.data.bins.map(b => Bin.create(b)))
|
|
setTableData(resp.data.bins.map(b => Bin.create(b)));
|
|
setObjectTotal(resp.data.total);
|
|
setTableLoading(false);
|
|
});
|
|
break;
|
|
// case pond.ObjectType.OBJECT_TYPE_BINYARD:
|
|
// binyardAPI
|
|
// .listBinYards(
|
|
// pageSize,
|
|
// pageSize * tablePage,
|
|
// currentDirection,
|
|
// currentOrder,
|
|
// search,
|
|
// undefined,
|
|
// selectedUser
|
|
// )
|
|
// .then(resp => {
|
|
// setTableData(resp.data.yard.map(b => BinYard.create(b)));
|
|
// setObjectTotal(resp.data.total);
|
|
// setTableLoading(false);
|
|
// });
|
|
// break;
|
|
// case pond.ObjectType.OBJECT_TYPE_FIELD:
|
|
// fieldAPI
|
|
// .listFields(
|
|
// pageSize,
|
|
// pageSize * tablePage,
|
|
// currentDirection,
|
|
// currentOrder,
|
|
// search,
|
|
// selectedUser
|
|
// )
|
|
// .then(resp => {
|
|
// setTableData(resp.data.fields.map(f => Field.create(f)));
|
|
// setObjectTotal(resp.data.total);
|
|
// setTableLoading(false);
|
|
// });
|
|
// break;
|
|
// case pond.ObjectType.OBJECT_TYPE_GATE:
|
|
// gateAPI
|
|
// .listGates(
|
|
// pageSize,
|
|
// pageSize * tablePage,
|
|
// currentDirection,
|
|
// currentOrder,
|
|
// search,
|
|
// undefined,
|
|
// undefined,
|
|
// undefined,
|
|
// isNumerical,
|
|
// selectedUser
|
|
// )
|
|
// .then(resp => {
|
|
// setTableData(resp.data.gates.map(g => Gate.create(g)));
|
|
// setObjectTotal(resp.data.total);
|
|
// setTableLoading(false);
|
|
// });
|
|
// break;
|
|
// case pond.ObjectType.OBJECT_TYPE_GRAIN_BAG:
|
|
// grainBagAPI
|
|
// .listGrainBags(
|
|
// pageSize,
|
|
// pageSize * tablePage,
|
|
// currentDirection,
|
|
// currentOrder,
|
|
// search,
|
|
// undefined,
|
|
// undefined,
|
|
// isNumerical,
|
|
// selectedUser
|
|
// )
|
|
// .then(resp => {
|
|
// setTableData(resp.data.grainBags.map(gb => GrainBag.create(gb)));
|
|
// setObjectTotal(resp.data.total);
|
|
// setTableLoading(false);
|
|
// });
|
|
// break;
|
|
// case pond.ObjectType.OBJECT_TYPE_HEATER:
|
|
// heaterAPI
|
|
// .listObjectHeaters(
|
|
// pageSize,
|
|
// pageSize * tablePage,
|
|
// currentDirection,
|
|
// currentOrder,
|
|
// search,
|
|
// undefined,
|
|
// selectedUser,
|
|
// undefined,
|
|
// undefined,
|
|
// isNumerical
|
|
// )
|
|
// .then(resp => {
|
|
// setTableData(resp.data.heaters.map(h => ObjectHeater.create(h)));
|
|
// setObjectTotal(resp.data.total);
|
|
// setTableLoading(false);
|
|
// });
|
|
// break;
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [
|
|
//currentDirection,
|
|
//currentOrder,
|
|
currentType,
|
|
selectedUser,
|
|
//isNumerical,
|
|
pageSize,
|
|
tablePage,
|
|
currentSearchText,
|
|
binAPI,
|
|
binyardAPI,
|
|
// fieldAPI,
|
|
gateAPI,
|
|
// grainBagAPI,
|
|
// heaterAPI
|
|
]);
|
|
|
|
useEffect(() => {
|
|
if (preLoadedData && currentSearchText === "") {
|
|
setLoadedData(preLoadedData);
|
|
setObjectTotal(preLoadedData.length);
|
|
} else {
|
|
load();
|
|
}
|
|
}, [load, preLoadedData, currentSearchText]);
|
|
|
|
useEffect(()=>{
|
|
let start = pageSize * (tablePage)
|
|
let end = pageSize * (tablePage + 1)
|
|
let currentData: any[] = []
|
|
loadedData.forEach((object,i) => {
|
|
if (i >= start && i < end ) {
|
|
currentData.push(object)
|
|
}
|
|
})
|
|
setTableData(currentData)
|
|
},[loadedData, pageSize, tablePage])
|
|
|
|
const bulkEditForm = (type: pond.ObjectType) => {
|
|
switch (type) {
|
|
case pond.ObjectType.OBJECT_TYPE_BIN:
|
|
return (
|
|
<BulkBinSettings
|
|
selectedBins={Array.from(selectedObjects.values()) as pond.Bin[]}
|
|
extraButtons={customButtons?.map(b => (
|
|
<Button variant="contained" onClick={() => {b.function(Array.from(selectedObjects.values()))}}>{b.label}</Button>
|
|
))}
|
|
refreshCallback={reLoad => {
|
|
//setSelectedObjects([])
|
|
setSelectedObjects(new Map())
|
|
setSelectedPageRows(new Map())
|
|
if (reLoad) {
|
|
load();
|
|
}
|
|
}}
|
|
/>
|
|
);
|
|
case pond.ObjectType.OBJECT_TYPE_GRAIN_BAG:
|
|
return (
|
|
// <BulkGrainBagSettings
|
|
// selectedBags={selectedObjects as GrainBag[]}
|
|
// refreshCallback={reLoad => {
|
|
// if (reLoad) {
|
|
// load();
|
|
// }
|
|
// }}
|
|
// />
|
|
undefined
|
|
);
|
|
default:
|
|
}
|
|
};
|
|
|
|
// const actionToolbar = () => {
|
|
// return (
|
|
// <Box width="100%" padding={2}>
|
|
// <SearchBar
|
|
// value={currentSearchText ?? ""}
|
|
// onChange={val => {
|
|
// setCurrentSearchText(val);
|
|
// }}
|
|
// />
|
|
// {selectedObjects.length > 0 && <Box paddingTop={2}>{bulkEditForm(currentType)}</Box>}
|
|
// <Grid container direction="row" spacing={2} style={{ marginTop: 2 }}>
|
|
// {customButtons &&
|
|
// selectedObjects.length > 0 &&
|
|
// customButtons.map((button, i) => (
|
|
// <Grid size={{ xs: 3 }} key={i}>
|
|
// <Button
|
|
// style={{ width: "100%" }}
|
|
// variant="contained"
|
|
// color="primary"
|
|
// onClick={() => button.function(selectedObjects)}>
|
|
// {button.label}
|
|
// </Button>
|
|
// </Grid>
|
|
// ))}
|
|
// </Grid>
|
|
// </Box>
|
|
// );
|
|
// };
|
|
|
|
const handleRowsPerPageChange = (event: any) => {
|
|
const newRowsPerPage = parseInt(event.target.value, 10); // Get selected value
|
|
setPageSize(newRowsPerPage);
|
|
setTablePage(0)
|
|
}
|
|
|
|
const rowSelected = (row: any, index: number, checked: boolean) => {
|
|
//when a row is selected add it to the map for the page
|
|
let selected = cloneDeep(selectedObjects)
|
|
let pageMap = cloneDeep(selectedPageRows)
|
|
let pageSelect = pageMap.get(tablePage)
|
|
if(checked){ //add the page/index to the map
|
|
if(pageSelect){
|
|
pageSelect.push(index)
|
|
}else{
|
|
pageMap.set(tablePage, [index])
|
|
}
|
|
//handle the row data to add it to a list of objects that are selected
|
|
selected.set(tablePage + ":" + index, row)
|
|
}else{ //remove the page/index from the map
|
|
if(pageSelect){
|
|
pageSelect.splice(pageSelect.indexOf(index))
|
|
}
|
|
//remove the selected object from the array
|
|
selected.delete(tablePage + ":" + index)
|
|
}
|
|
setSelectedObjects(selected)
|
|
setSelectedPageRows(pageMap)
|
|
}
|
|
|
|
return (
|
|
<Box padding={2}>
|
|
{showControls && (
|
|
<Grid container direction="row" spacing={2} style={{ padding: 10 }}>
|
|
<Grid>
|
|
<SearchSelect
|
|
style={{ width: 200 }}
|
|
label="From"
|
|
selected={selectedType}
|
|
options={SearchableObjects()}
|
|
changeSelection={op => {
|
|
//setSelectedObjects([]);
|
|
setSelectedObjects(new Map());
|
|
setSelectedType(op);
|
|
if (op?.value) {
|
|
setCurrentType(op.value);
|
|
setTablePage(0);
|
|
setCurrentSearchText("");
|
|
}
|
|
}}
|
|
/>
|
|
</Grid>
|
|
<Grid style={{ width: 300 }}>
|
|
<TeamSearch
|
|
label="User/Team"
|
|
loadUsers
|
|
setTeamCallback={user => {
|
|
setSelectedUser(user);
|
|
//dispatch({ key: "as", value: user.toString() });
|
|
}}
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
)}
|
|
|
|
<ResponsiveTable
|
|
rows={tableData}
|
|
columns={columns}
|
|
onRowClick={rowClickFunction}
|
|
total={objectTotal}
|
|
pageSize={pageSize}
|
|
page={tablePage}
|
|
setPage={setTablePage}
|
|
handleRowsPerPageChange={handleRowsPerPageChange}
|
|
setSearchText={setCurrentSearchText}
|
|
customElement={bulkEditForm(currentType)}
|
|
rowSelect={rowSelected}
|
|
selectedRows={selectedPageRows.get(tablePage)}
|
|
/>
|
|
{/* <MaterialTable
|
|
isLoading={tableLoading}
|
|
key="objectList"
|
|
title={ObjectDescriber(currentType).name}
|
|
icons={getTableIcons()}
|
|
columns={ObjectDescriber(currentType, user).tableColumns}
|
|
data={tableData}
|
|
page={tablePage}
|
|
totalCount={objectTotal}
|
|
onRowClick={
|
|
rowClickFunction
|
|
? (_, data) => {
|
|
rowClickFunction(data);
|
|
}
|
|
: undefined
|
|
}
|
|
onChangePage={page => {
|
|
setTablePage(page);
|
|
}}
|
|
onOrderChange={(by, direction) => {
|
|
if (by !== -1) {
|
|
let colName = ObjectDescriber(currentType, user).tableColumns[by].title?.toString();
|
|
let order;
|
|
if (colName) {
|
|
order = ObjectDescriber(currentType, user).tableSort.get(colName);
|
|
setCurrentOrder(order?.order);
|
|
setCurrentDirection(direction);
|
|
setIsNumerical(order?.numerical);
|
|
}
|
|
}
|
|
}}
|
|
options={{
|
|
paging: true,
|
|
pageSize: pageSize,
|
|
pageSizeOptions: [5, 10, 20],
|
|
padding: "dense",
|
|
showTitle: true,
|
|
toolbar: true,
|
|
sorting: true,
|
|
columnsButton: true,
|
|
search: false,
|
|
selection: true
|
|
}}
|
|
onSelectionChange={(data, rowData) => {
|
|
setSelectedObjects(data);
|
|
}}
|
|
actions={[
|
|
{
|
|
tooltip: "Update Selected Objects",
|
|
icon: "edit",
|
|
onClick: (evt, data) => {console.log(data)}
|
|
}
|
|
]}
|
|
onChangeRowsPerPage={pageSize => {
|
|
setPageSize(pageSize);
|
|
}}
|
|
components={{
|
|
Toolbar: props => (
|
|
<React.Fragment>
|
|
<MTableToolbar {...props} />
|
|
{actionToolbar()}
|
|
</React.Fragment>
|
|
)
|
|
}}
|
|
/> */}
|
|
</Box>
|
|
);
|
|
}
|