Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Platform]: ot table server #496

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/ui/src/components/OtTable/OtTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import OtTableColumnFilter from "./OtTableColumnFilter";
// import { naLabel } from "../../constants";
import OtTableSearch from "./OtTableSearch";
import { OtTableProps } from "./table.types";
import { OtTableProps } from "./types/tableTypes";
import {
FontAwesomeIconPadded,
OtTableContainer,
Expand All @@ -41,7 +41,7 @@ import {
getDefaultSortObj,
getFilterValueFromObject,
mapTableColumnToTanstackColumns,
} from "./tableUtil";
} from "./utils/tableUtils";
import Tooltip from "../Tooltip";

declare module "@tanstack/table-core" {
Expand Down
355 changes: 355 additions & 0 deletions packages/ui/src/components/OtTable/OtTableSSP.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,355 @@
import { ReactElement, useEffect, useReducer, useState } from "react";
import { Box, CircularProgress, Grid, IconButton, NativeSelect } from "@mui/material";
import {
useReactTable,
getCoreRowModel,
getPaginationRowModel,
flexRender,
PaginationState,
} from "@tanstack/react-table";
import { faAngleLeft, faAngleRight, faBackwardStep } from "@fortawesome/free-solid-svg-icons";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

import OtTableSearch from "./OtTableSearch";
import { INIT_PAGE_SIZE, OtTableSSPProps } from "./types/tableTypes";
import { OtTableContainer, OtTableHeader, OtTH, OtTableHeaderText, OtTD } from "./otTableLayout";
import DataDownloader from "../DataDownloader";
import { getCurrentPagePosition, mapTableColumnToTanstackColumns } from "./utils/tableUtils";
import Tooltip from "../Tooltip";

import { getTableRows } from "./service/tableService";
import { createInitialState, otTableReducer } from "./context/otTableReducer";
import { addRows, setLoading, setNewData, textSearch } from "./context/otTableActions";
import { naLabel } from "../../constants";
import useCursorBatchDownloader from "../../hooks/useCursorBatchDownloader";

function OtTableSSP({
showGlobalFilter = true,
columns = [],
verticalHeaders = false,
query,
variables,
entity,
sectionName,
dataDownloaderFileStem,
dataDownloaderColumns,
dataDownloader,
}: OtTableSSPProps): ReactElement {
const [state, dispatch] = useReducer(otTableReducer, "", createInitialState);
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: INIT_PAGE_SIZE,
});
const mappedColumns = mapTableColumnToTanstackColumns(columns);

const table = useReactTable({
data: state.rows,
columns: mappedColumns,
rowCount: state.count,
state: {
pagination,
},
autoResetPageIndex: false,
// manualPagination: true,
onPaginationChange: onPaginationChange,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});

/**********************************************
* DEFAULT FUNCTION CALLBACK TRIGGERED BY
* REACT TABLE IN ANY PAGE CHANGE EVENT *
* @param:
* updater: @type callback function
**********************************************/

function onPaginationChange(updater) {
const newPagination = updater(pagination);

// switch () {
// case (pagination.pageSize !== newPagination.pageSize): {
// onPageSizeChange(newPagination);
// break;
// }
// case (newPagination.pageIndex > pagination.pageIndex): {
// onPageChange(newPagination);
// break;
// }
// default: {
// setPagination(newPagination);
// break;
// }
// }

if (pagination.pageSize !== newPagination.pageSize) {
onPageSizeChange(newPagination);
} else if (newPagination.pageIndex > pagination.pageIndex) {
onPageChange(newPagination);
} else {
setPagination(newPagination);
}
}

/**********************************************
* FUNCTION FOR PAGE INDEX CHANGE
* CHECK IF MORE DATA IS REQUIRED BY CHECKING
* NUMBER OF ROWS ALREADY FETCHED, PREVENT EXCESSIVE
* API CALLS IN CASE USER PAGINATE BACK AND FORTH
* @param:
* newPagination: @type PaginationState "@tanstack/react-table"
**********************************************/
function onPageChange(newPagination: PaginationState) {
if (needMoreData(pagination.pageSize, newPagination.pageIndex)) {
addNewData(newPagination);
} else {
setPagination(newPagination);
}
}

/**********************************************
* FUNCTION FOR PAGE SIZE CHANGE
* @param:
* newPagination: @type PaginationState "@tanstack/react-table"
**********************************************/
function onPageSizeChange(newPagination: PaginationState) {
setTableData({ newPagination });
}

/**********************************************
* FUNCTION TO FETCH MORE DATA (PAGINATION)
* SETTING LOADING TRUE BEFORE FETCHING
* FETCH DATA AS PER TABLE STATE
* SETS PAGINATION AFTER DATA IS FETCHED TO AVOID
* SHOWING EMPTY ROWS
* @param:
* newPagination: @type PaginationState "@tanstack/react-table"
**********************************************/

function addNewData(newPagination: PaginationState) {
dispatch(setLoading(true));
getTableRows({
query,
variables,
cursor: state.cursor,
size: pagination.pageSize,
freeTextQuery: state.freeTextQuery,
}).then(d => {
dispatch(addRows(d.data[entity][sectionName]));
setPagination(newPagination);
});
}

/**********************************************
* FUNCTION TO FETCH NEW DATA (PAGE SIZE CHANGE OR SEARCH TEXT)
* SETTING LOADING TRUE BEFORE FETCHING
* FETCH DATA AND PASSING CURSOR AS NULL
* @param:
* newPagination: @type PaginationState "@tanstack/react-table"
**********************************************/
function setTableData({ newPagination = pagination, freeTextQuery = state.freeTextQuery }) {
dispatch(setLoading(true));
getTableRows({
query,
variables,
cursor: null,
size: newPagination.pageSize,
freeTextQuery,
}).then(d => {
dispatch(setNewData(d.data[entity][sectionName]));
});
}

/**********************************************
* FUNCTION TO TO CHECK IF MORE DATA IS NEEDED
* IN CASE USER PAGINATE BACKWARDS
* @param:
* pageSize: number
* pageIndex: number
* @return : boolean
**********************************************/
function needMoreData(pageSize: number, pageIndex: number) {
const dataLength = table.options.data.length;
return dataLength < (pageIndex + 1) * pageSize;
}

/*********************************
* STORES ALL DATA IN FOR EXPORT *
*********************************/
const getWholeDataset = useCursorBatchDownloader(
query,
{ ...variables, freeTextQuery: state.freeTextQuery },
`data[${entity}][${sectionName}]`
);

useEffect(() => {
const newPagination = {
pageIndex: 0,
pageSize: pagination.pageSize,
};
setTableData({ newPagination, freeTextQuery: state.freeTextQuery });
}, [state.freeTextQuery]);

return (
<div>
{/* Global Search */}
<Grid container>
{showGlobalFilter && (
<Grid item sm={12} md={4}>
<OtTableSearch
setGlobalSearchTerm={freeTextQuery => {
dispatch(textSearch(freeTextQuery));
}}
/>
</Grid>
)}
{dataDownloader && (
<Grid item sm={12} md={8} sx={{ ml: "auto" }}>
<DataDownloader
columns={dataDownloaderColumns || columns}
rows={getWholeDataset}
fileStem={dataDownloaderFileStem}
query={query}
variables={variables}
/>
</Grid>
)}
</Grid>
{/* Table component container */}
<Box sx={{ w: 1, overflowX: "auto", marginTop: theme => theme.spacing(3) }}>
{/* Table component */}
<OtTableContainer>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<OtTH
key={header.id}
colSpan={header.colSpan}
stickyColumn={header.column.columnDef.sticky}
>
{header.isPlaceholder ? null : (
<>
<OtTableHeader>
<OtTableHeaderText
verticalHeader={
header.column.columnDef.verticalHeader || verticalHeaders
}
onClick={header.column.getToggleSortingHandler()}
sx={{ typography: "subtitle2" }}
>
<Tooltip
style={""}
title={header.column.columnDef.tooltip}
showHelpIcon={!!header.column.columnDef.tooltip}
>
{flexRender(header.column.columnDef.header, header.getContext())}
</Tooltip>
</OtTableHeaderText>
</OtTableHeader>
</>
)}
</OtTH>
);
})}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => {
return (
<tr key={row.id}>
{row.getVisibleCells().map(cell => {
return (
<OtTD key={cell.id} stickyColumn={cell.column.columnDef.sticky}>
<Box sx={{ typography: "body2" }}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
{/* TODO: check NA value */}
{/* {Boolean(flexRender(cell.column.columnDef.cell, cell.getContext())) ||
naLabel} */}
</Box>
</OtTD>
);
})}
</tr>
);
})}
</tbody>
</OtTableContainer>
</Box>

{/* Table footer component container */}
<Box
sx={{
display: "flex",
justifyContent: "end",
alignItems: "center",
padding: theme => `${theme.spacing(2)} 0 `,
}}
>
{state.loading && <CircularProgress sx={{ mx: theme => theme.spacing(2) }} size={25} />}
<div>
<span>Rows per page:</span>
<NativeSelect
disableUnderline
sx={{ pl: theme => theme.spacing(2) }}
value={pagination.pageSize}
onChange={e => {
table.setPageSize(Number(e.target.value));
setPagination({
pageIndex: 0,
pageSize: Number(e.target.value),
});
}}
>
{/* TODO: set page size */}
{[10, 25, 100].map(pageSize => (
<option key={pageSize} value={pageSize}>
{pageSize}
</option>
))}
</NativeSelect>
</div>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: theme => theme.spacing(3),
marginLeft: theme => theme.spacing(3),
}}
>
<div>
<span>
{getCurrentPagePosition(pagination.pageIndex, pagination.pageSize, state.count)}
</span>
</div>

<div className="paginationAction">
<IconButton
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage() || state.loading}
>
<FontAwesomeIcon size="2xs" icon={faBackwardStep} />
</IconButton>
<IconButton
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage() || state.loading}
>
<FontAwesomeIcon size="2xs" icon={faAngleLeft} />
</IconButton>

<IconButton
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage() || state.loading}
>
<FontAwesomeIcon size="2xs" icon={faAngleRight} />
</IconButton>
</div>
</Box>
</Box>
</div>
);
}

export default OtTableSSP;
2 changes: 1 addition & 1 deletion packages/ui/src/components/OtTable/OtTableSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Input, InputAdornment } from "@mui/material";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons";
import useDebounce from "../../hooks/useDebounce";
import { OtTableSearchProps } from "./table.types";
import { OtTableSearchProps } from "./types/tableTypes";

/****************************************
* OT TABLE SEARCH COMPONENT *
Expand Down
Loading
Loading