239 lines
6.6 KiB
TypeScript
239 lines
6.6 KiB
TypeScript
import { createSelector } from "@reduxjs/toolkit";
|
|
import { Box, IconButton, MenuItem, Select } from "@mui/material";
|
|
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
|
|
import { GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
|
|
import { RootState } from "@/app/redux/store";
|
|
import { TABLE_COLUMNS } from "../constants";
|
|
import { selectTransactionStatuses } from "@/app/redux/metadata/selectors";
|
|
import { DataRowBase } from "../types";
|
|
|
|
const TRANSACTION_STATUS_FALLBACK: string[] = [
|
|
"pending",
|
|
"completed",
|
|
"failed",
|
|
"inprogress",
|
|
"error",
|
|
];
|
|
|
|
type StatusChangeHandler = (rowId: number, newStatus: string) => void;
|
|
|
|
const selectEnableStatusActions = (
|
|
_state: RootState,
|
|
enableStatusActions: boolean
|
|
) => enableStatusActions;
|
|
|
|
const selectExtraColumns = (
|
|
_state: RootState,
|
|
_enableStatusActions: boolean,
|
|
extraColumns?: string[] | null
|
|
) => extraColumns ?? null;
|
|
|
|
const selectShowExtraColumns = (
|
|
_state: RootState,
|
|
_enableStatusActions: boolean,
|
|
_extraColumns?: string[] | null,
|
|
showExtraColumns = false
|
|
) => showExtraColumns;
|
|
|
|
const selectLocalRows = (
|
|
_state: RootState,
|
|
_enableStatusActions: boolean,
|
|
_extraColumns?: string[] | null,
|
|
_showExtraColumns?: boolean,
|
|
localRows?: DataRowBase[]
|
|
) => localRows ?? [];
|
|
|
|
const noopStatusChangeHandler: StatusChangeHandler = () => {};
|
|
|
|
const selectStatusChangeHandler = (
|
|
_state: RootState,
|
|
_enableStatusActions: boolean,
|
|
_extraColumns?: string[] | null,
|
|
_showExtraColumns?: boolean,
|
|
_localRows?: DataRowBase[],
|
|
handleStatusChange?: StatusChangeHandler
|
|
) => handleStatusChange ?? noopStatusChangeHandler;
|
|
|
|
export const selectBaseColumns = createSelector(
|
|
[selectEnableStatusActions],
|
|
enableStatusActions => {
|
|
if (!enableStatusActions) {
|
|
return TABLE_COLUMNS;
|
|
}
|
|
|
|
return [
|
|
...TABLE_COLUMNS,
|
|
{
|
|
field: "actions",
|
|
headerName: "Actions",
|
|
width: 160,
|
|
sortable: false,
|
|
filterable: false,
|
|
} as GridColDef,
|
|
];
|
|
}
|
|
);
|
|
|
|
export const selectVisibleColumns = createSelector(
|
|
[selectBaseColumns, selectExtraColumns, selectShowExtraColumns],
|
|
(baseColumns, extraColumns, showExtraColumns) => {
|
|
if (!extraColumns || extraColumns.length === 0) {
|
|
return baseColumns;
|
|
}
|
|
|
|
return showExtraColumns
|
|
? baseColumns
|
|
: baseColumns.filter(col => !extraColumns.includes(col.field));
|
|
}
|
|
);
|
|
|
|
export const selectResolvedTransactionStatuses = createSelector(
|
|
[selectTransactionStatuses],
|
|
statuses => (statuses.length > 0 ? statuses : TRANSACTION_STATUS_FALLBACK)
|
|
);
|
|
|
|
export const selectEnhancedColumns = createSelector(
|
|
[
|
|
selectVisibleColumns,
|
|
selectLocalRows,
|
|
selectStatusChangeHandler,
|
|
selectResolvedTransactionStatuses,
|
|
],
|
|
(
|
|
visibleColumns,
|
|
localRows,
|
|
handleStatusChange,
|
|
resolvedStatusOptions
|
|
): GridColDef[] => {
|
|
return visibleColumns.map(col => {
|
|
if (col.field === "status") {
|
|
return {
|
|
...col,
|
|
renderCell: (params: GridRenderCellParams) => {
|
|
const value = params.value?.toLowerCase();
|
|
let bgColor = "#e0e0e0";
|
|
let textColor = "#000";
|
|
switch (value) {
|
|
case "completed":
|
|
bgColor = "#d0f0c0";
|
|
textColor = "#1b5e20";
|
|
break;
|
|
case "pending":
|
|
bgColor = "#fff4cc";
|
|
textColor = "#9e7700";
|
|
break;
|
|
case "inprogress":
|
|
bgColor = "#cce5ff";
|
|
textColor = "#004085";
|
|
break;
|
|
case "error":
|
|
bgColor = "#ffcdd2";
|
|
textColor = "#c62828";
|
|
break;
|
|
}
|
|
return (
|
|
<Box
|
|
sx={{
|
|
backgroundColor: bgColor,
|
|
color: textColor,
|
|
px: 1.5,
|
|
py: 0.5,
|
|
borderRadius: 1,
|
|
fontWeight: 500,
|
|
textTransform: "capitalize",
|
|
display: "inline-block",
|
|
width: "100%",
|
|
textAlign: "center",
|
|
}}
|
|
>
|
|
{params.value}
|
|
</Box>
|
|
);
|
|
},
|
|
};
|
|
}
|
|
|
|
if (col.field === "userId") {
|
|
return {
|
|
...col,
|
|
headerAlign: "center",
|
|
align: "center",
|
|
renderCell: (params: GridRenderCellParams) => (
|
|
<Box
|
|
sx={{
|
|
display: "grid",
|
|
gridTemplateColumns: "1fr auto",
|
|
alignItems: "center",
|
|
width: "100%",
|
|
px: 1,
|
|
}}
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
<Box
|
|
sx={{
|
|
fontWeight: 500,
|
|
fontSize: "0.875rem",
|
|
color: "text.primary",
|
|
}}
|
|
>
|
|
{params.value}
|
|
</Box>
|
|
<IconButton
|
|
href={`/users/${params.value}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
size="small"
|
|
sx={{ p: 0.5, ml: 1 }}
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
<OpenInNewIcon fontSize="small" />
|
|
</IconButton>
|
|
</Box>
|
|
),
|
|
};
|
|
}
|
|
|
|
if (col.field === "actions") {
|
|
return {
|
|
...col,
|
|
renderCell: (params: GridRenderCellParams) => {
|
|
const currentRow = localRows.find(row => row.id === params.id);
|
|
const options =
|
|
currentRow?.options?.map(option => option.value) ??
|
|
resolvedStatusOptions;
|
|
const uniqueOptions: string[] = Array.from(new Set(options));
|
|
|
|
return (
|
|
<Select<string>
|
|
value={currentRow?.status ?? ""}
|
|
onChange={e =>
|
|
handleStatusChange(
|
|
params.id as number,
|
|
e.target.value as string
|
|
)
|
|
}
|
|
size="small"
|
|
fullWidth
|
|
displayEmpty
|
|
sx={{
|
|
"& .MuiOutlinedInput-notchedOutline": { border: "none" },
|
|
"& .MuiSelect-select": { py: 0.5 },
|
|
}}
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
{uniqueOptions.map(option => (
|
|
<MenuItem key={option} value={option}>
|
|
{option}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
);
|
|
},
|
|
};
|
|
}
|
|
|
|
return col;
|
|
}) as GridColDef[];
|
|
}
|
|
);
|