UNPKG

@judo/components

Version:
1 lines 94.4 kB
{"version":3,"file":"components.mjs","sources":["../../src/dialog/ConfirmationDialog.tsx","../../../components-api/dist/esm/components-api.mjs","../../../utilities/dist/esm/utilities.mjs","../../../theme/dist/esm/theme.mjs","../../../data-api-common/dist/esm/data-api-common.mjs","../../src/DropdownButton.tsx","../../src/TrinaryLogicCombobox.tsx","../../src/dialog/FilterDialog.tsx","../../src/dialog/PageDialog.tsx","../../src/CustomTablePagination.tsx","../../src/dialog/hooks.tsx","../../src/dialog/RangeDialog.tsx","../../src/dialog/DialogProvider.tsx","../../src/table/table-row-actions.tsx","../../src/AggregationInput.tsx","../../src/CustomBreadcrumb.tsx","../../src/Hero.tsx","../../src/Logo.tsx","../../src/MenuTree.tsx","../../src/PageHeader.tsx"],"sourcesContent":["import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, Button } from '@mui/material';\nimport { useEffect, useRef } from 'react';\nimport type { ConfirmationDialogProps } from '@judo/components-api';\n\nexport const ConfirmationDialog = ({\n confirmationMessage,\n title,\n resolve,\n open,\n handleClose,\n}: ConfirmationDialogProps) => {\n const descriptionElementRef = useRef<HTMLElement>(null);\n useEffect(() => {\n if (open) {\n const { current: descriptionElement } = descriptionElementRef;\n if (descriptionElement !== null) {\n descriptionElement.focus();\n }\n }\n }, [open]);\n\n const cancel = () => {\n handleClose();\n resolve(false);\n };\n\n const ok = () => {\n handleClose();\n resolve(true);\n };\n\n return (\n <Dialog open={open} onClose={handleClose} scroll=\"paper\" fullWidth={true} maxWidth={'xs'}>\n {title && <DialogTitle id=\"scroll-dialog-title\">{title}</DialogTitle>}\n <DialogContent dividers={!!title}>\n <DialogContentText id=\"scroll-dialog-description\" ref={descriptionElementRef} tabIndex={-1}>\n {confirmationMessage}\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={cancel}>\n No\n </Button>\n <Button variant=\"text\" onClick={ok}>\n Yes\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","const TRINARY_LOGIC = new Map([\n [null, 'Unknown'],\n [true, 'Yes'],\n [false, 'No'],\n]);\n\nvar FilterType;\n(function (FilterType) {\n FilterType[FilterType[\"boolean\"] = 0] = \"boolean\";\n FilterType[FilterType[\"numeric\"] = 1] = \"numeric\";\n FilterType[FilterType[\"string\"] = 2] = \"string\";\n FilterType[FilterType[\"enumeration\"] = 3] = \"enumeration\";\n FilterType[FilterType[\"date\"] = 4] = \"date\";\n FilterType[FilterType[\"dateTime\"] = 5] = \"dateTime\";\n // time,\n FilterType[FilterType[\"trinaryLogic\"] = 6] = \"trinaryLogic\";\n})(FilterType || (FilterType = {}));\n\nexport { FilterType, TRINARY_LOGIC };\n","const e=e=>null!=e,t=e=>e?e.toISOString().substring(0,10):e,r=(t,r,s)=>{if(!t||\"boolean\"!=typeof t.isAxiosError||!0!==t.isAxiosError)return void a(r);const n=t.response;if(e(n))if(n?.status)if(422===n?.status)r(o(t.response?.data).message,{variant:\"error\",preventDuplicate:!0,persist:!0});else{if(400===n?.status)return i(n.data,r,s);r(o(n?.data).message,{variant:\"error\",preventDuplicate:!0,persist:!0})}else a(r);else a(r)},i=(t,r,i)=>{if(e(t[0].location)){if(r(\"Please make sure all fields are filled in correctly.\",{variant:\"error\",preventDuplicate:!0,persist:!i?.duration,autoHideDuration:i?.duration??void 0}),void 0!==i?.setValidation){let e=new Map;t.forEach((t=>e.set(t.location,t.code))),i?.setValidation(e)}}else r(t[0].code,{variant:\"error\",preventDuplicate:!0,persist:!i?.duration,autoHideDuration:i?.duration??void 0})},a=e=>{e(\"Something went wrong. Please contact with the system admins.\",{variant:\"error\",preventDuplicate:!0,persist:!0})},o=e=>({message:e.code}),s=(e,t)=>{if(e.some((e=>e.filterOption.attributeName===t)))return e.filter((e=>e.filterOption.attributeName===t&&e.filterBy.value)).map((e=>({value:e.filterBy.value,operator:e.filterBy.operator})))},n=(e,...t)=>{let r={};for(const i of t)r[i]=s(e,i);return r};export{t as dateToJudoDateString,r as errorHandling,e as exists,n as mapAllFiltersToQueryCustomizerProperties,s as mapFiltersToQueryCustomizerProperty};\n//# sourceMappingURL=utilities.mjs.map\n","import{createTheme as e,alpha as o}from\"@mui/material\";const i={marginTop:8,marginBottom:8,display:\"flex\",flexDirection:\"column\",alignItems:\"center\"},t=e({palette:{mode:\"light\",primary:{main:\"#3C4166FF\"},secondary:{main:\"#E7501DFF\"},text:{primary:\"#17191DFF\",secondary:\"#434448FF\"},background:{default:\"#FAFAFAFF\"},subtitleColor:{main:\"#8c8c8c\"}},typography:{h5:{fontWeight:500,fontSize:26,letterSpacing:.5}},shape:{borderRadius:8},components:{MuiTab:{defaultProps:{disableRipple:!0}}},mixins:{toolbar:{minHeight:48}}}),r={...t,components:{MuiAppBar:{styleOverrides:{colorPrimary:{backgroundColor:t.palette.background.default}}},MuiButton:{defaultProps:{variant:\"contained\",size:\"small\"},styleOverrides:{root:{textTransform:\"none\",borderRadius:\"20px 20px 20px 20px\",paddingLeft:15,paddingRight:15},contained:{boxShadow:\"none\",\"&:active\":{boxShadow:\"none\"}},outlined:{border:\"2px solid\",fontWeight:\"bold\",\"&:hover\":{border:\"2px solid\",background:o(t.palette.primary.main,.15)}}}},MuiInputLabel:{styleOverrides:{root:{color:t.palette.subtitleColor.main,fontWeight:500,fontSize:16,\"&.Mui-focused\":{color:t.palette.subtitleColor.main}}}},MuiFilledInput:{styleOverrides:{input:{\"&:-webkit-autofill\":{WebkitBackgroundClip:\"text !important\"}},root:{background:\"inherit\",\"&.Mui-focused\":{background:\"inherit\"},\"&:hover:not(.Mui-disabled):not(.Mui-focused)\":{background:o(t.palette.secondary.main,.1)},\"&:hover:not(.Mui-disabled):before\":{borderBottom:\"none\"},\"&.Mui-focused .MuiInputAdornment-root .MuiSvgIcon-root\":{color:t.palette.secondary.main}}}},MuiTextField:{defaultProps:{fullWidth:!0,variant:\"filled\",color:\"secondary\"},styleOverrides:{root:{background:\"white\",\"& .MuiFilledInput-underline:before\":{borderBottom:\"none\"},\"&.Mui-readOnly:not(.Mui-disabled):not(.Mui-focused)\":{borderBottom:`1px solid ${o(t.palette.primary.main,.2)}`,background:\"transparent\"},\"&:not(.Mui-readOnly):not(.Mui-disabled)\":{boxShadow:\"0px 0px 8px 2px rgba(0,0,0,0.05)\",borderRadius:\"8px 8px 0 0\"}}}},MuiSelect:{styleOverrides:{filled:{\"&:focus\":{backgroundColor:\"white\"}}}},MuiDataGrid:{styleOverrides:{root:{border:\"none\"},toolbarContainer:{padding:\"8px 8px 4px 8px\"}}},MuiPaper:{styleOverrides:{rounded:{boxShadow:\"0px 0px 8px 1px rgba(0,0,0,0.05)\",borderRadius:16,padding:8}}},MuiDrawer:{styleOverrides:{paper:{border:\"none\",boxShadow:\"0px 0px 8px 1px rgba(0,0,0,0.05)\"}}},MuiTabs:{styleOverrides:{root:{marginLeft:t.spacing(1)},indicator:{height:3,borderTopLeftRadius:3,borderTopRightRadius:3,backgroundColor:t.palette.common.white}}},MuiTab:{styleOverrides:{root:{textTransform:\"none\",margin:\"0 16px\",minWidth:0,padding:0,[t.breakpoints.up(\"md\")]:{padding:0,minWidth:0}}}},MuiIconButton:{styleOverrides:{root:{color:t.palette.secondary.main,padding:t.spacing(1)}}},MuiTooltip:{styleOverrides:{tooltip:{borderRadius:4}}},MuiDivider:{styleOverrides:{root:{backgroundColor:\"rgb(255,255,255,0.15)\"},middle:{marginTop:8,marginBottom:8}}},MuiListItemButton:{defaultProps:{disableTouchRipple:!0}},MuiListItemText:{styleOverrides:{primary:{fontSize:14,fontWeight:t.typography.fontWeightMedium}}},MuiListItemIcon:{styleOverrides:{root:{color:\"inherit\",minWidth:\"auto\",marginRight:t.spacing(2),\"& svg\":{fontSize:20}}}},MuiAvatar:{styleOverrides:{root:{width:32,height:32}}},MuiCard:{styleOverrides:{root:{width:\"100%\",length:\"100%\",padding:10}}}}};export{i as mainContainerPadding,r as theme,t as themeBase};\n//# sourceMappingURL=theme.mjs.map\n","var a,e,l,r;!function(a){a.equals=\"equals\"}(a||(a={})),function(a){a.equals=\"equals\",a.notEquals=\"notEquals\"}(e||(e={})),function(a){a.lessThan=\"lessThan\",a.greaterThan=\"greaterThan\",a.lessOrEqual=\"lessOrEqual\",a.greaterOrEqual=\"greaterOrEqual\",a.equal=\"equal\",a.notEqual=\"notEqual\"}(l||(l={})),function(a){a.lessThan=\"lessThan\",a.greaterThan=\"greaterThan\",a.lessOrEqual=\"lessOrEqual\",a.greaterOrEqual=\"greaterOrEqual\",a.equal=\"equal\",a.notEqual=\"notEqual\",a.matches=\"matches\",a.like=\"like\"}(r||(r={}));export{a as _BooleanOperation,e as _EnumerationOperation,l as _NumericOperation,r as _StringOperation};\n//# sourceMappingURL=data-api-common.mjs.map\n","import { Button, ClickAwayListener, Grow, MenuItem, MenuList, Paper, Popper } from '@mui/material';\nimport { useState, useRef, useEffect } from 'react';\nimport type { ReactNode, KeyboardEvent, SyntheticEvent } from 'react';\nimport { KeyboardArrowDown } from '@mui/icons-material';\n\ninterface DropdownMenuItem {\n disabled?: boolean;\n visible?: boolean;\n label?: string;\n onClick: () => void;\n startIcon?: ReactNode;\n endIcon?: ReactNode;\n}\n\ninterface DropdownButtonProps {\n children?: ReactNode;\n id?: string | undefined;\n menuItems: DropdownMenuItem[];\n disabled?: boolean;\n showDropdownIcon?: boolean;\n fullWidth?: boolean;\n variant?: 'text' | 'outlined' | 'contained' | undefined;\n}\n\nexport function DropdownButton({\n children,\n id,\n menuItems,\n disabled = false,\n showDropdownIcon = true,\n fullWidth = false,\n variant = 'contained',\n}: DropdownButtonProps) {\n const [open, setOpen] = useState(false);\n const anchorRef = useRef<HTMLButtonElement>(null);\n\n const handleToggle = () => {\n setOpen((prevOpen) => !prevOpen);\n };\n\n const handleClose = (event: Event | SyntheticEvent) => {\n if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {\n return;\n }\n\n setOpen(false);\n };\n\n function handleListKeyDown(event: KeyboardEvent) {\n if (event.key === 'Tab') {\n event.preventDefault();\n setOpen(false);\n } else if (event.key === 'Escape') {\n setOpen(false);\n }\n }\n\n // return focus to the button when we transitioned from !open -> open\n const prevOpen = useRef(open);\n useEffect(() => {\n if (prevOpen.current && !open) {\n anchorRef.current!.focus();\n }\n\n prevOpen.current = open;\n }, [open]);\n\n return (\n <>\n <Button\n ref={anchorRef}\n id={id}\n onClick={handleToggle}\n endIcon={showDropdownIcon && <KeyboardArrowDown />}\n disabled={disabled}\n fullWidth={fullWidth}\n variant={variant}\n >\n {children}\n </Button>\n <Popper\n open={open}\n anchorEl={anchorRef.current}\n placement=\"bottom\"\n transition\n style={{ zIndex: 1400, minWidth: anchorRef.current?.scrollWidth }}\n >\n {({ TransitionProps, placement }) => (\n <Grow\n {...TransitionProps}\n style={{\n transformOrigin: placement === 'bottom-start' ? 'left top' : 'left bottom',\n }}\n >\n <Paper>\n <ClickAwayListener onClickAway={handleClose}>\n <MenuList autoFocusItem={open} onKeyDown={handleListKeyDown}>\n {menuItems\n .filter((menuItem) => menuItem.visible ?? true)\n .map((menuItem, index) => {\n return (\n <MenuItem\n id={menuItem.label ?? '' + index}\n disabled={menuItem.disabled ?? false}\n onClick={(event) => {\n handleClose(event);\n menuItem.onClick();\n }}\n >\n {menuItem.startIcon}\n {menuItem.label}\n {menuItem.endIcon}\n </MenuItem>\n );\n })}\n </MenuList>\n </ClickAwayListener>\n </Paper>\n </Grow>\n )}\n </Popper>\n </>\n );\n}\n","import { CheckBoxOutlined } from '@mui/icons-material';\nimport { TextField, InputAdornment, MenuItem } from '@mui/material';\nimport type { ChangeEvent } from 'react';\nimport { TRINARY_LOGIC } from '@judo/components-api';\n\ninterface TrinaryLogicProps {\n readOnly?: boolean;\n value?: boolean | null;\n id?: string;\n label: string;\n name?: string;\n error?: boolean | undefined;\n helperText?: string | undefined;\n onChange?: (value: boolean | null) => void;\n}\n\nconst TrinaryLogicCombobox = ({\n readOnly = false,\n value = null,\n id,\n label,\n name,\n error,\n helperText,\n onChange,\n}: TrinaryLogicProps) => {\n const onChangeHandler = onChange\n ? (event: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {\n const index = Array.from(TRINARY_LOGIC.values()).indexOf(event.target.value);\n const keysArray = Array.from(TRINARY_LOGIC.keys());\n onChange(keysArray[index]);\n }\n : undefined;\n\n return (\n <TextField\n name={name}\n id={id}\n label={label}\n select\n value={TRINARY_LOGIC.get(value)}\n onChange={onChangeHandler}\n className={readOnly ? 'Mui-readOnly' : undefined}\n error={error}\n helperText={helperText}\n InputProps={{\n readOnly: readOnly,\n startAdornment: (\n <InputAdornment position=\"start\">\n <CheckBoxOutlined />\n </InputAdornment>\n ),\n }}\n >\n {Array.from(TRINARY_LOGIC.keys()).map((key) => (\n <MenuItem key={TRINARY_LOGIC.get(key)} value={TRINARY_LOGIC.get(key)}>\n {TRINARY_LOGIC.get(key)}\n </MenuItem>\n ))}\n </TextField>\n );\n};\n\nexport default TrinaryLogicCombobox;\n","import { mdiCalendarClock, mdiCalendarMonth, mdiFormatTextVariant, mdiNumeric } from '@mdi/js';\nimport Icon from '@mdi/react';\nimport { Close } from '@mui/icons-material';\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogContentText,\n DialogActions,\n Button,\n Slide,\n Box,\n Container,\n Grid,\n TextField,\n MenuItem,\n Checkbox,\n FormControlLabel,\n InputAdornment,\n IconButton,\n Typography,\n} from '@mui/material';\nimport type { TransitionProps } from '@mui/material/transitions';\nimport { DatePicker, DateTimePicker } from '@mui/x-date-pickers';\nimport { forwardRef, useEffect, useRef, useState } from 'react';\nimport type { ChangeEvent, ReactElement, Ref } from 'react';\nimport type {\n Filter,\n FilterDialogProps,\n FilterInputProps,\n FilterOperatorProps,\n FilterProps,\n Operation,\n} from '@judo/components-api';\nimport { FilterType } from '@judo/components-api';\nimport { dateToJudoDateString, exists } from '@judo/utilities';\nimport { mainContainerPadding } from '@judo/theme';\nimport { _BooleanOperation, _EnumerationOperation, _NumericOperation, _StringOperation } from '@judo/data-api-common';\nimport { DropdownButton } from '../DropdownButton';\nimport TrinaryLogicCombobox from '../TrinaryLogicCombobox';\n\nconst getDefaultOperator = (filterType: FilterType) => {\n switch (filterType) {\n case FilterType.boolean:\n return _BooleanOperation['equals'];\n case FilterType.date:\n return _NumericOperation['equal'];\n case FilterType.dateTime:\n return _NumericOperation['equal'];\n // case FilterType.time:\n // return _NumericOperation['equal'];\n case FilterType.enumeration:\n return _EnumerationOperation['equals'];\n case FilterType.numeric:\n return _NumericOperation['equal'];\n case FilterType.string:\n return _StringOperation['equal'];\n case FilterType.trinaryLogic:\n return _BooleanOperation['equals'];\n }\n};\n\nconst getOperationEnumValue = (filter: Filter, operator: string) => {\n switch (filter.filterOption.filterType) {\n case FilterType.boolean:\n return _BooleanOperation[operator as keyof typeof _BooleanOperation];\n case FilterType.date:\n return _NumericOperation[operator as keyof typeof _NumericOperation];\n case FilterType.dateTime:\n return _NumericOperation[operator as keyof typeof _NumericOperation];\n // case FilterType.time:\n // return _NumericOperation[operator as keyof typeof _NumericOperation];\n case FilterType.enumeration:\n return _EnumerationOperation[operator as keyof typeof _BooleanOperation];\n case FilterType.numeric:\n return _NumericOperation[operator as keyof typeof _NumericOperation];\n case FilterType.string:\n return _StringOperation[operator as keyof typeof _StringOperation];\n case FilterType.trinaryLogic:\n return _BooleanOperation[operator as keyof typeof _BooleanOperation];\n }\n};\n\nconst getOperatorsByFilter = (filter: Filter): string[] => {\n switch (filter.filterOption.filterType) {\n case FilterType.boolean:\n return Object.values(_BooleanOperation);\n case FilterType.date:\n return Object.values(_NumericOperation);\n case FilterType.dateTime:\n return Object.values(_NumericOperation);\n // case FilterType.time:\n // return Object.values(_NumericOperation);\n case FilterType.enumeration:\n return Object.values(_EnumerationOperation);\n case FilterType.numeric:\n return Object.values(_NumericOperation);\n case FilterType.string:\n return Object.values(_StringOperation);\n case FilterType.trinaryLogic:\n return Object.values(_BooleanOperation);\n }\n};\n\nconst FilterOperator = ({ filter, setFilterOperator }: FilterOperatorProps) => {\n const onChangeHandler = (event: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {\n setFilterOperator(filter, getOperationEnumValue(filter, event.target.value));\n };\n\n return (\n <TextField\n name={'operation'}\n id={'operation'}\n label={'Operation'}\n select\n value={filter.filterBy.operator}\n onChange={onChangeHandler}\n >\n {getOperatorsByFilter(filter).map((item) => (\n <MenuItem key={item} value={item}>\n {/* TODO: do not forget localization here*/}\n {item}\n </MenuItem>\n ))}\n </TextField>\n );\n};\n\nconst FilterInput = ({ filter, setFilterValue }: FilterInputProps) => {\n if (filter.filterOption.filterType === FilterType.enumeration && !exists(filter.filterOption.enumValues)) {\n throw new Error(`Missing enumValues from FilterOptions of ${filter.filterOption.attributeName}`);\n }\n\n return (\n <>\n {(() => {\n switch (filter.filterOption.filterType) {\n case FilterType.boolean:\n return (\n <FormControlLabel\n control={\n <Checkbox\n checked={!!filter.filterBy.value}\n onChange={(event) => setFilterValue(filter, !!event.target.value)}\n />\n }\n label={filter.filterOption.attributeName}\n />\n );\n case FilterType.date:\n return (\n <DatePicker\n renderInput={(props) => <TextField {...props} />}\n label={filter.filterOption.attributeName}\n value={filter.filterBy.value ?? null}\n onChange={(newValue) => setFilterValue(filter, dateToJudoDateString(newValue))}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <Icon path={mdiCalendarMonth} size={1} />\n </InputAdornment>\n ),\n }}\n />\n );\n case FilterType.dateTime:\n return (\n <DateTimePicker\n renderInput={(props) => <TextField {...props} />}\n label={filter.filterOption.attributeName}\n value={filter.filterBy.value ?? null}\n onChange={(newValue) => setFilterValue(filter, newValue)}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <Icon path={mdiCalendarClock} size={1} />\n </InputAdornment>\n ),\n }}\n />\n );\n // case FilterType.time:\n // return (\n // <TextField\n // label={filter.filterOption.attributeName}\n // value={filter.filterBy.value}\n // onChange={(event) => setFilterValue(filter, event.target.value)}\n // InputProps={{\n // startAdornment: (\n // <InputAdornment position=\"start\">\n // <Icon path={mdiClockOutline} size={1} />\n // </InputAdornment>\n // ),\n // }}\n // />\n // );\n case FilterType.enumeration:\n return (\n <TextField\n label={filter.filterOption.attributeName}\n value={filter.filterBy.value}\n select\n onChange={(event) => setFilterValue(filter, event.target.value)}\n >\n {filter.filterOption.enumValues?.map((item) => (\n <MenuItem key={item} value={item}>\n {item}\n </MenuItem>\n ))}\n </TextField>\n );\n case FilterType.numeric:\n return (\n <TextField\n label={filter.filterOption.attributeName}\n type=\"number\"\n value={filter.filterBy.value}\n onChange={(event) => setFilterValue(filter, Number(event.target.value))}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <Icon path={mdiNumeric} size={1} />\n </InputAdornment>\n ),\n }}\n />\n );\n case FilterType.string:\n return (\n <TextField\n label={filter.filterOption.attributeName}\n value={filter.filterBy.value}\n onChange={(event) => setFilterValue(filter, event.target.value)}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <Icon path={mdiFormatTextVariant} size={1} />\n </InputAdornment>\n ),\n }}\n />\n );\n case FilterType.trinaryLogic:\n return (\n <TrinaryLogicCombobox\n label={filter.filterOption.attributeName}\n value={filter.filterBy.value}\n onChange={(value) => setFilterValue(filter, value)}\n />\n );\n }\n })()}\n </>\n );\n};\n\nconst FilterRow = ({ filter, closeHandler, setFilterOperator, setFilterValue }: FilterProps) => {\n return (\n <Grid item container spacing={2} alignItems={'center'}>\n <Grid item xs={4}>\n {filter && <FilterOperator filter={filter} setFilterOperator={setFilterOperator} />}\n </Grid>\n <Grid item xs={7}>\n {filter && <FilterInput filter={filter} setFilterValue={setFilterValue} />}\n </Grid>\n <Grid item xs={1}>\n <IconButton onClick={() => closeHandler(filter)}>\n <Close />\n </IconButton>\n </Grid>\n </Grid>\n );\n};\n\nconst Transition = forwardRef(function Transition(\n props: TransitionProps & {\n children: ReactElement<any, any>;\n },\n ref: Ref<unknown>,\n) {\n return <Slide direction=\"left\" ref={ref} {...props} />;\n});\n\nexport const FilterDialog = ({ filters, filterOptions, resolve, open, handleClose }: FilterDialogProps) => {\n const descriptionElementRef = useRef<HTMLElement>(null);\n const [tempFilters, setTempFilters] = useState<Filter[]>(filters ?? []);\n\n useEffect(() => {\n if (open) {\n const { current: descriptionElement } = descriptionElementRef;\n if (descriptionElement !== null) {\n descriptionElement.focus();\n }\n }\n }, [open]);\n\n const updateFilterValue = (filter: Filter, value: any) => {\n setTempFilters((prevTempFilters) => {\n return prevTempFilters.map((tempFilter) => {\n if (filter.id === tempFilter.id) {\n return {\n ...tempFilter,\n filterBy: { value: value, operator: tempFilter.filterBy.operator },\n };\n }\n\n return tempFilter;\n });\n });\n };\n\n const updateFilterOperator = (filter: Filter, operator: Operation) => {\n setTempFilters((prevTempFilters) => {\n return prevTempFilters.map((tempFilter) => {\n if (filter.id === tempFilter.id) {\n return {\n ...tempFilter,\n filterBy: { value: tempFilter.filterBy.value, operator: operator },\n };\n }\n\n return tempFilter;\n });\n });\n };\n\n const filterCloseHandler = (filter: Filter) => {\n setTempFilters((prevTempFilters) => [...prevTempFilters.filter((tempFilter) => tempFilter.id !== filter.id)]);\n };\n\n const cancel = () => {\n handleClose();\n resolve(undefined);\n };\n\n const ok = () => {\n handleClose();\n resolve(tempFilters);\n };\n\n return (\n <Dialog\n open={open}\n onClose={cancel}\n scroll=\"paper\"\n TransitionComponent={Transition}\n disableEnforceFocus\n fullWidth\n maxWidth=\"sm\"\n sx={{\n '& .MuiDialog-container': {\n justifyContent: 'flex-end',\n },\n }}\n PaperProps={{\n sx: {\n m: 0,\n height: '100%',\n },\n }}\n >\n <DialogTitle id=\"scroll-dialog-title\">\n <Typography component=\"span\" color=\"text.primary\" variant=\"h5\">\n Filters\n </Typography>\n </DialogTitle>\n <DialogContent dividers={true}>\n <DialogContentText id=\"scroll-dialog-description\" ref={descriptionElementRef} tabIndex={-1}>\n <Container component=\"main\" maxWidth=\"xs\">\n <Box sx={mainContainerPadding}>\n <Grid container spacing={2}>\n {tempFilters.map((filter) => (\n <FilterRow\n key={filter.id}\n filter={filter}\n closeHandler={filterCloseHandler}\n setFilterOperator={updateFilterOperator}\n setFilterValue={updateFilterValue}\n />\n ))}\n <Grid item container>\n <DropdownButton\n fullWidth={true}\n showDropdownIcon={false}\n menuItems={filterOptions.map((filterOption) => {\n return {\n label: filterOption.label ?? filterOption.attributeName,\n onClick: () =>\n setTempFilters((prevTempFilters) => [\n ...prevTempFilters,\n {\n id: prevTempFilters.length,\n filterOption: {\n attributeName: filterOption.attributeName,\n label: filterOption.label,\n filterType: filterOption.filterType,\n },\n filterBy: {\n operator: getDefaultOperator(filterOption.filterType),\n },\n },\n ]),\n };\n })}\n >\n Add new filter\n </DropdownButton>\n </Grid>\n </Grid>\n </Box>\n </Container>\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button fullWidth variant=\"outlined\" onClick={cancel}>\n Cancel\n </Button>\n <Button fullWidth onClick={ok}>\n Apply {'(' + tempFilters.length + ')'}\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import { Dialog, DialogContent, DialogContentText, DialogActions, Button } from '@mui/material';\nimport { useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\n\ninterface PageDialogProps {\n page: ReactNode;\n open: boolean;\n handleClose: () => void;\n resolve: () => void;\n}\n\nexport const PageDialog = ({ page, open, handleClose, resolve }: PageDialogProps) => {\n const descriptionElementRef = useRef<HTMLElement>(null);\n useEffect(() => {\n if (open) {\n const { current: descriptionElement } = descriptionElementRef;\n if (descriptionElement !== null) {\n descriptionElement.focus();\n }\n }\n }, [open]);\n\n const ok = () => {\n resolve();\n handleClose();\n };\n\n return (\n <Dialog open={open} onClose={ok} scroll=\"paper\">\n <DialogContent dividers={true}>\n <DialogContentText ref={descriptionElementRef} tabIndex={-1}>\n {page}\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={ok}>Ok</Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import { TablePagination } from '@mui/material';\nimport type { MouseEvent, Dispatch, SetStateAction } from 'react';\n\nexport interface CustomTablePaginationProps {\n pageChange: (isNext: boolean) => Promise<void>;\n isNextButtonEnabled: boolean;\n page: number;\n rowPerPage: number;\n setPage: Dispatch<SetStateAction<number>>;\n}\n\nexport const CustomTablePagination = (props: CustomTablePaginationProps) => {\n const handleChangePage = async (event: MouseEvent<HTMLButtonElement> | null, newPage: number) => {\n let isNext = true;\n if (newPage < props.page) {\n isNext = false;\n }\n\n props.setPage(newPage);\n\n await props.pageChange(isNext);\n };\n\n return (\n <TablePagination\n component=\"div\"\n count={-1}\n page={props.page}\n onPageChange={handleChangePage}\n rowsPerPage={props.rowPerPage}\n rowsPerPageOptions={[props.rowPerPage]}\n labelDisplayedRows={({ from, to }) => `${from}–${to}`}\n nextIconButtonProps={{\n disabled: !props.isNextButtonEnabled,\n }}\n backIconButtonProps={{\n disabled: props.page === 0,\n }}\n />\n );\n};\n","import { createContext, useContext } from 'react';\nimport {\n ConfirmDialogProviderContext,\n FilterDialogProviderContext,\n PageDialogProviderContext,\n RangeDialogProviderContext,\n} from '@judo/components-api';\n\n// @ts-ignore\nexport const PageDialogContextState = createContext<PageDialogProviderContext>();\n\n// @ts-ignore\nexport const ConfirmDialogContextState = createContext<ConfirmDialogProviderContext>();\n\n// @ts-ignore\nexport const RangeDialogContextState = createContext<RangeDialogProviderContext>();\n\n// @ts-ignore\nexport const FilterDialogContextState = createContext<FilterDialogProviderContext>();\n\nexport const useFilterDialog = () => {\n const context = useContext(FilterDialogContextState);\n\n if (context === undefined) {\n throw new Error('useFilterDialog was used outside of its Provider');\n }\n\n return context;\n};\n\nconst usePageDialog = () => {\n const context = useContext(PageDialogContextState);\n\n if (context === undefined) {\n throw new Error('useConfirmDialog was used outside of its Provider');\n }\n\n return context;\n};\n\nconst useConfirmDialog = () => {\n const context = useContext(ConfirmDialogContextState);\n\n if (context === undefined) {\n throw new Error('useConfirmDialog was used outside of its Provider');\n }\n\n return context;\n};\n\nconst useRangeDialog = () => {\n const context = useContext(RangeDialogContextState);\n\n if (context === undefined) {\n throw new Error('useRangeDialog was used outside of its Provider');\n }\n\n return context;\n};\n","import { Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, Button } from '@mui/material';\nimport {\n GridSortModel,\n GridRowModel,\n DataGrid,\n GridRowParams,\n GridColumns,\n GridSortItem,\n GridSelectionModel,\n GridToolbarContainer,\n GridRowId,\n} from '@mui/x-data-grid';\nimport { useSnackbar } from 'notistack';\nimport { useEffect, useRef, useState } from 'react';\nimport type { JudoStored, QueryCustomizer } from '@judo/data-api-common';\nimport type { Filter, FilterOption } from '@judo/components-api';\nimport { errorHandling } from '@judo/utilities';\nimport { CustomTablePagination } from '../CustomTablePagination';\nimport { useFilterDialog } from './hooks';\n\ninterface RangeDialogProps<T extends JudoStored<T>, U extends QueryCustomizer<T>> {\n resolve: (value: any) => void;\n open: boolean;\n handleClose: () => void;\n single?: boolean;\n columns: GridColumns<T>;\n defaultSortField: GridSortItem;\n rangeCall: (queryCustomizer: U) => Promise<Array<T>>;\n alreadySelectedItems: GridSelectionModel | GridRowId;\n initalQueryCustomizer: U;\n filterOptions: FilterOption[];\n}\n\nexport const RangeDialog = <T extends JudoStored<T>, U extends QueryCustomizer<T>>({\n resolve,\n open,\n handleClose,\n single = false,\n columns,\n defaultSortField,\n rangeCall,\n alreadySelectedItems,\n initalQueryCustomizer,\n filterOptions,\n}: RangeDialogProps<T, U>) => {\n const { openFilterDialog } = useFilterDialog();\n const { enqueueSnackbar } = useSnackbar();\n\n const descriptionElementRef = useRef<HTMLElement>(null);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [rowCount, setRowCount] = useState<number>(0);\n const [sortModel, setSortModel] = useState<GridSortModel>([defaultSortField]);\n const [lastItem, setLastItem] = useState<T>();\n const [firstItem, setFirstItem] = useState<T>();\n const [isNextButtonEnabled, setIsNextButtonEnabled] = useState<boolean>(true);\n const [page, setPage] = useState<number>(0);\n const [data, setData] = useState<GridRowModel<T>[]>([]);\n const [selectionModel, setSelectionModel] = useState<GridSelectionModel | GridRowId | undefined>(\n alreadySelectedItems ?? (single ? undefined : []),\n );\n const [selectedItems, setSelectedItems] = useState<T[] | T | undefined>([]);\n const [filters, setFilters] = useState<Filter[]>([]);\n const [queryCustomizer, setQueryCustomizer] = useState<U>({\n ...initalQueryCustomizer,\n _seek: {\n limit: 6,\n },\n });\n\n const handlePageChange = async (isNext: boolean) => {\n setQueryCustomizer((prevQueryCustomizer) => {\n return {\n ...prevQueryCustomizer,\n _seek: {\n limit: isNext ? 6 : 5,\n reverse: !isNext,\n lastItem: isNext ? lastItem : firstItem,\n },\n };\n });\n\n setIsNextButtonEnabled(!isNext);\n };\n\n const fetchData = async () => {\n setIsLoading(true);\n try {\n const res = await rangeCall(queryCustomizer);\n\n if (res.length > 5) {\n setIsNextButtonEnabled(true);\n res.pop();\n } else if (queryCustomizer._seek?.limit === 6) {\n setIsNextButtonEnabled(false);\n }\n\n setData(res);\n setFirstItem(res[0]);\n setLastItem(res[res.length - 1]);\n setRowCount(res.length || 0);\n } catch (error) {\n errorHandling(error, enqueueSnackbar);\n }\n setIsLoading(false);\n };\n\n const handleFiltersChange = (newFilters: Filter[]) => {\n setPage(0);\n setFilters(newFilters);\n\n // @ts-ignore\n setQueryCustomizer((prevQueryCustomizer) => {\n const tempQueryCustomizer = { ...prevQueryCustomizer };\n\n filterOptions.forEach(\n (filter) =>\n // @ts-ignore\n (tempQueryCustomizer[filter.attributeName] = mapFiltersToQueryCustomizerProperty(\n newFilters,\n filter.attributeName,\n )),\n );\n\n return {\n ...prevQueryCustomizer,\n _seek: {\n lastItem: undefined,\n limit: 6,\n reverse: undefined,\n },\n ...tempQueryCustomizer,\n };\n });\n };\n\n const handleSortModelChange = (newModel: GridSortModel) => {\n setPage(0);\n setSortModel(newModel);\n\n const { field, sort } = newModel[0];\n\n setQueryCustomizer((prevQueryCustomizer) => {\n return {\n ...prevQueryCustomizer,\n _orderBy: [{ attribute: field, descending: sort === 'desc' }],\n };\n });\n };\n\n // useEffect(() => {\n // setPage(0);\n // const { field, sort } = sortModel[0];\n\n // setQueryCustomizer((prevQueryCustomizer) => {\n // const tempQueryCustomizer = { ...prevQueryCustomizer };\n\n // filterOptions.forEach(\n // (filter) =>\n // // @ts-ignore\n // (tempQueryCustomizer[filter.attributeName] = mapFiltersToQueryCustomizerProperty(\n // filters,\n // filter.attributeName,\n // )),\n // );\n\n // return {\n // ...prevQueryCustomizer,\n // _seek: {\n // lastItem: undefined,\n // limit: 6,\n // reverse: undefined,\n // },\n // _orderBy: [{ attribute: field, descending: sort === 'desc' }],\n // ...tempQueryCustomizer,\n // };\n // });\n // }, [sortModel, filters]);\n\n useEffect(() => {\n fetchData();\n }, [queryCustomizer]);\n\n useEffect(() => {\n if (open) {\n const { current: descriptionElement } = descriptionElementRef;\n if (descriptionElement !== null) {\n descriptionElement.focus();\n }\n }\n }, [open]);\n\n const cancel = () => {\n handleClose();\n resolve(undefined);\n };\n\n const ok = () => {\n handleClose();\n resolve(selectedItems);\n };\n\n const handleOnSelection = (newSelectionModel: GridSelectionModel) => {\n if (!Array.isArray(selectionModel)) return;\n\n // added new items\n if (newSelectionModel.length > selectionModel.length) {\n const diff = newSelectionModel.length - selectionModel.length;\n const newItemsId = [...newSelectionModel].slice(diff * -1);\n const newItems = data.filter((value) => newItemsId.indexOf(value.__identifier as GridRowId) !== -1);\n setSelectedItems((prevSelectedItems) => {\n if (!Array.isArray(prevSelectedItems)) return;\n\n return [...prevSelectedItems, ...newItems];\n });\n }\n\n // removed items\n if (newSelectionModel.length < selectionModel.length) {\n const removedItemsId = selectionModel.filter((value) => newSelectionModel.indexOf(value) === -1);\n setSelectedItems((prevSelectedItems) => {\n if (!Array.isArray(prevSelectedItems)) return;\n\n return [...prevSelectedItems.filter((value) => removedItemsId.indexOf(value.__identifier as GridRowId) === -1)];\n });\n }\n\n setSelectionModel(newSelectionModel);\n };\n\n const handleSingleOnSelection = (newSelectionModel: GridSelectionModel) => {\n if (Array.isArray(selectionModel)) return;\n\n if (newSelectionModel.length === 0) {\n setSelectionModel('');\n setSelectedItems(undefined);\n return;\n }\n\n const lastId = newSelectionModel[newSelectionModel.length - 1];\n\n setSelectionModel(lastId);\n setSelectedItems(data.find((value) => value.__identifier === lastId));\n };\n\n const handleIsRowSelectable = (params: GridRowParams<T>) => {\n if (alreadySelectedItems) {\n if (!Array.isArray(alreadySelectedItems)) throw Error('Range dialog gets wrong alreadySelectedItems.');\n\n return !alreadySelectedItems.includes(params.id);\n }\n\n return true;\n };\n\n return (\n <Dialog open={open} onClose={cancel} scroll=\"paper\" fullWidth={true} maxWidth={'sm'}>\n <DialogTitle id=\"scroll-dialog-title\">Select</DialogTitle>\n <DialogContent dividers={true}>\n <DialogContentText id=\"scroll-dialog-description\" ref={descriptionElementRef} tabIndex={-1}>\n <DataGrid\n sx={\n single\n ? {\n '.MuiDataGrid-columnHeaderCheckbox .MuiDataGrid-columnHeaderTitleContainer': {\n display: 'none',\n },\n }\n : undefined\n }\n autoHeight\n getRowId={(row: T) => row.__identifier as GridRowId}\n loading={isLoading}\n paginationMode=\"server\"\n rows={data}\n rowCount={rowCount}\n sortingOrder={['desc', 'asc']}\n sortingMode=\"server\"\n sortModel={sortModel}\n onSortModelChange={handleSortModelChange}\n checkboxSelection\n onSelectionModelChange={!single ? handleOnSelection : handleSingleOnSelection}\n isRowSelectable={!single ? handleIsRowSelectable : undefined}\n selectionModel={selectionModel}\n hideFooterSelectedRowCount={single}\n columns={columns}\n keepNonExistentRowsSelected\n components={{\n Toolbar: () => (\n <GridToolbarContainer>\n <Button\n variant=\"outlined\"\n onClick={async () => {\n const newFilters = await openFilterDialog(filterOptions, filters);\n\n if (newFilters) {\n handleFiltersChange(newFilters);\n }\n }}\n disabled={isLoading}\n >\n Set filters {filters.length !== 0 ? '(' + filters.length + ')' : ''}\n </Button>\n </GridToolbarContainer>\n ),\n Pagination: () => (\n <CustomTablePagination\n pageChange={handlePageChange}\n isNextButtonEnabled={isNextButtonEnabled}\n page={page}\n setPage={setPage}\n rowPerPage={5}\n />\n ),\n }}\n />\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={cancel}>Cancel</Button>\n <Button onClick={ok}>Ok</Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import { useState } from 'react';\nimport type { ReactNode } from 'react';\nimport { ConfirmationDialog } from './ConfirmationDialog';\nimport { FilterDialog } from './FilterDialog';\nimport { PageDialog } from './PageDialog';\nimport { RangeDialog } from './RangeDialog';\nimport type {\n ConfirmDialogProviderContext,\n DialogProviderProps,\n FilterDialogProviderContext,\n OpenRangeDialogProps,\n PageDialogProviderContext,\n RangeDialogProviderContext,\n} from '@judo/components-api';\nimport type { Filter, FilterOption } from '@judo/components-api';\nimport type { JudoStored, QueryCustomizer } from '@judo/data-api-common';\nimport {\n ConfirmDialogContextState,\n FilterDialogContextState,\n PageDialogContextState,\n RangeDialogContextState,\n} from './hooks';\n\nexport const DialogProvider = ({ children }: DialogProviderProps) => {\n // Page Dialog\n const [isOpenPageDialog, setIsOpenPageDialog] = useState(false);\n const [pageDialog, setPageDialog] = useState<ReactNode>();\n\n const handleClosePageDialog = () => {\n setIsOpenPageDialog(false);\n };\n\n const handleOpenPageDialog = async (page: ReactNode) => {\n setIsOpenPageDialog(true);\n return new Promise<void>((resolve) => {\n setPageDialog(<PageDialog page={page} handleClose={handleClosePageDialog} open={true} resolve={resolve} />);\n });\n };\n\n const pageDialogContext: PageDialogProviderContext = {\n openPageDialog: handleOpenPageDialog,\n };\n\n // Range Dialog\n const [isOpenRangeDialog, setIsOpenRangeDialog] = useState(false);\n const [rangeDialog, setRangeDialog] = useState<ReactNode>();\n\n const handleCloseRangeDialog = () => {\n setIsOpenRangeDialog(false);\n };\n\n const handleOpenRangeDialog = async <T extends JudoStored<T>, U extends QueryCustomizer<T>>({\n columns,\n defaultSortField,\n rangeCall,\n single = false,\n alreadySelectedItems,\n filterOptions,\n initialQueryCustomizer,\n }: OpenRangeDialogProps<T, U>) => {\n setIsOpenRangeDialog(true);\n\n return new Promise<T[] | T>((resolve) => {\n setRangeDialog(\n <RangeDialog<T, U>\n handleClose={handleCloseRangeDialog}\n open={true}\n resolve={resolve}\n columns={columns}\n defaultSortField={defaultSortField}\n rangeCall={rangeCall}\n single={single}\n alreadySelectedItems={alreadySelectedItems}\n filterOptions={filterOptions}\n initalQueryCustomizer={initialQueryCustomizer}\n />,\n );\n });\n };\n\n const customDialogContext: RangeDialogProviderContext = {\n openRangeDialog: handleOpenRangeDialog,\n };\n\n // Confirmation Dialog\n const [isOpenConfirmDialog, setIsOpenConfirmDialog] = useState(false);\n const [confirmDialog, setConfirmDialog] = useState<ReactNode>();\n\n const handleCloseConfirmDialog = () => {\n setIsOpenConfirmDialog(false);\n return false;\n };\n\n const handleOpenConfirmDialog = async (confirmationMessage: string | ReactNode, title?: string | ReactNode) => {\n setIsOpenConfirmDialog(true);\n\n return new Promise<boolean>((resolve) => {\n setConfirmDialog(\n <ConfirmationDialog\n confirmationMessage={confirmationMessage}\n title={title}\n handleClose={handleCloseConfirmDialog}\n open={true}\n resolve={resolve}\n />,\n );\n });\n };\n\n const confirmDialogContext: ConfirmDialogProviderContext = {\n openConfirmDialog: handleOpenConfirmDialog,\n };\n\n // Filter dialog\n const [isOpenFilterDialog, setIsOpenFilterDialog] = useState(false);\n const [filterDialog, setFilterDialog] = useState<ReactNode>();\n\n const handleCloseFilterDialog = () => {\n setIsOpenFilterDialog(false);\n return false;\n };\n\n const handleOpenFilterDialog = async (filterOptions: FilterOption[], filters?: Filter[]) => {\n setIsOpenFilterDialog(true);\n\n return new Promise<Filter[]>((resolve) => {\n setFilterDialog(\n <FilterDialog\n filters={filters}\n filterOptions={filterOptions}\n handleClose={handleCloseFilterDialog}\n open={true}\n resolve={resolve}\n />,\n );\n });\n };\n\n const filterDialogContext: FilterDialogProviderContext = {\n openFilterDialog: handleOpenFilterDialog,\n };\n\n return (\n <PageDialogContextState.Provider value={pageDialogContext}>\n <ConfirmDialogContextState.Provider value={confirmDialogContext}>\n <RangeDialogContextState.Provider value={customDialogContext}>\n <FilterDialogContextState.Provider value={filterDialogContext}>\n {children}\n {isOpenPageDialog && pageDialog}\n {isOpenConfirmDialog && confirmDialog}\n {isOpenRangeDialog && rangeDialog}\n {isOpenFilterDialog && filterDialog}\n </FilterDialogContextState.Provider>\n </RangeDialogContextState.Provider>\n </ConfirmDialogContextState.Provider>\n </PageDialogContextState.Provider>\n );\n};\n","import type { ColumnActionsProvider, ColumnsActionsOptions, TableRowAction } from '@judo/utilities';\nimport { exists } from '@judo/utilities';\nimport { MoreHoriz } from '@mui/icons-material';\nimport { Button } from '@mui/material';\nimport { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';\nimport { DropdownButton } from '../DropdownButton';\n\nexport const columnsActionCalculator: ColumnActionsProvider<any> = (\n actions: TableRowAction<any>[],\n options?: ColumnsActionsOptions,\n): GridColDef[] => {\n if (!exists(actions) || actions.length === 0) {\n return [];\n }\n\n let shownActionsNumber = 1;\n if (options?.shownActions) {\n shownActionsNumber = actions.length < options.shownActions ? actions.length : options.shownActions;\n }\n\n if (shownActionsNumber < 0) {\n return standaloneActions(actions, options);\n } else if (shownActionsNumber === 0) {\n return [];\n } else if (shownActionsNumber === 1) {\n return dropdownActions(actions, options);\n } else {\n const sliceNumber = actions.length === shownActionsNumber ? shownActionsNumber : shownActionsNumber - 1;\n const standaloneRowActions = actions.slice(0, sliceNumber);\n const dropdownRowActions = actions.slice(sliceNumber);\n\n return [...standaloneActions(standaloneRowActions, options), ...dropdownActions(dropdownRowActions, options)];\n }\n};\n\nconst standaloneActions: ColumnActionsProvider<unknown> = (\n actions: TableRowAction<unknown>[],\n options?: ColumnsActionsOptions,\n): GridColDef[] => {\n return actions.map((action, index) => {\n return {\n field: action.label + index,\n headerName: '',\n align: 'center',\n type: 'actions',\n renderCell: (params: GridRenderCellParams) => {\n return (\n <Button variant=\"text\" onClick={() => action.action(params.row)}>\n {action.icon}\n {(options?.showLabel ?? true) && action.label}\n </Button>\n );\n },\n };\n });\n};\n\nconst dropdownActions: ColumnActionsProvider<unknown> = (actions: TableRowAction<unknown>[]): GridColDef[] => {\n if (actions.leng