import * as React from 'react'; import { ReactElement, FC } from 'react'; import { Box, Chip, useMediaQuery, Theme } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; import { CreateButton, ExportButton, FilterProps, InputProps, ListActionsProps, ListBase, ListProps, NumberInput, Pagination, ReferenceInput, SearchInput, SelectInput, SortButton, Title, TopToolbar, useTranslate, useListContext, } from 'react-admin'; import { useDefineAppLocation } from '@react-admin/ra-navigation'; import { FilterWithSave } from '@react-admin/ra-preferences'; import GridList from './GridList'; import Aside from './Aside'; import CustomBreadcrumb from '../layout/Breadcrumb'; const useQuickFilterStyles = makeStyles(theme => ({ root: { marginBottom: theme.spacing(3), }, })); const QuickFilter = ({ label }: InputProps): ReactElement => { const translate = useTranslate(); const classes = useQuickFilterStyles(); return <Chip className={classes.root} label={translate(label)} />; }; export const ProductFilter = ( props: Omit<FilterProps, 'children'> ): ReactElement => ( <FilterWithSave {...props}> <SearchInput source="q" alwaysOn /> <ReferenceInput source="category_id" reference="categories" sort={{ field: 'id', order: 'ASC' }} > <SelectInput source="name" /> </ReferenceInput> <NumberInput source="width_gte" /> <NumberInput source="width_lte" /> <NumberInput source="height_gte" /> <NumberInput source="height_lte" /> <QuickFilter label="resources.products.fields.stock_lte" source="stock_lte" defaultValue={10} /> </FilterWithSave> ); const ListActions = ({ isSmall, }: ListActionsProps & { isSmall: boolean }): ReactElement => { const classes = useListActionsStyles(); return ( <TopToolbar className={classes.root}> <CustomBreadcrumb variant="actions" /> {isSmall && <ProductFilter context="button" />} <SortButton fields={['reference', 'sales', 'stock']} /> <CreateButton basePath="/products" /> <ExportButton /> </TopToolbar> ); }; const useListActionsStyles = makeStyles(theme => ({ root: { paddingBottom: 0, paddingTop: theme.spacing(7), }, })); const ProductList: FC<ListProps> = ({ actions, ...props }) => { useDefineAppLocation('catalog.products'); const isSmall = useMediaQuery<Theme>(theme => theme.breakpoints.down('sm')); return ( <ListBase filters={isSmall ? <ProductFilter /> : undefined} perPage={20} sort={{ field: 'reference', order: 'ASC' }} {...props} > <ProductListView actions={actions} isSmall={isSmall} aside={props.aside} title={props.title} /> </ListBase> ); }; // The aside prop is only used to disable the aside here if explicitly set to false // If undefined, we consider the aside is not explicitly disabled an display the filter // vertical bar export const ProductListView = ({ isSmall, actions = <ListActions isSmall={isSmall} />, aside, title, }: { isSmall: boolean; actions?: ReactElement | false; aside?: ReactElement | false; title?: string | ReactElement; }): ReactElement => { const { defaultTitle } = useListContext(); const classes = useStyles(); return ( <div className={classes.root}> {title && defaultTitle && <Title title={title || defaultTitle} />} {actions} {isSmall && ( <Box m={1}> <ProductFilter context="form" /> </Box> )} <Box display="flex"> {aside !== false ? <Aside /> : null} <Box width={ isSmall || aside === false ? 'auto' : 'calc(100% - 16em)' } > <GridList /> <Pagination rowsPerPageOptions={[10, 20, 40]} /> </Box> </Box> </div> ); }; export default ProductList; const useStyles = makeStyles(theme => ({ root: { marginTop: -theme.spacing(7), }, }));