Skip to content

Commit

Permalink
save state
Browse files Browse the repository at this point in the history
  • Loading branch information
noherczeg committed Nov 8, 2024
1 parent 1a69ece commit 95ac72e
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
{{> fragment.header.hbs }}

import { useState, useMemo, useCallback, useEffect, type ChangeEvent } from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Grid from '@mui/material/Grid';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardActions from '@mui/material/CardActions';
import Pagination from '@mui/material/Pagination';
import Typography from '@mui/material/Typography';
import TablePagination from '@mui/material/TablePagination';
import type {
GridColDef,
GridEventListener,
GridSortModel,
GridFilterModel,
GridValidRowModel,
GridRowModel,
GridSortModel,
GridValidRowModel,
} from '@mui/x-data-grid{{ getMUIDataGridPlanSuffix }}';
import { useL10N } from '~/l10n/l10n-context';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { FiltersSerializer } from '~/utilities';
import { mapAllFiltersToQueryCustomizerProperties, processQueryCustomizer, useErrorHandler } from '~/utilities';
import { basePageSizeOptions, baseTableConfig, filterDebounceMs } from '~/config';
import type { Filter, FilterOption } from '~/components-api';
import { X_JUDO_COUNT } from '~/services/data-api/rest/headers';
import { basePageSizeOptions } from '~/config';
import { useL10N } from '~/l10n/l10n-context';
import type { QueryCustomizer } from '~/services/data-api/common/QueryCustomizer';
import { X_JUDO_COUNT } from '~/services/data-api/rest/headers';
import type { FiltersSerializer } from '~/utilities';
import { mapAllFiltersToQueryCustomizerProperties, processQueryCustomizer, useErrorHandler } from '~/utilities';
import { CardsFilter, type CardsFilterDefinition } from './CardsFilter';
import { DefaultCard } from './DefaultCard';

export interface CardsContainerProps<T extends GridValidRowModel> {
uniqueId: string;
Expand All @@ -41,9 +38,10 @@ export interface CardsContainerProps<T extends GridValidRowModel> {
relationName: string;
filtersSerializer: FiltersSerializer;
showTotalCount?: boolean;
layout?: 'horizontal' | 'vertical';
}

export const CardsContainer = <T extends GridValidRowModel,>(props: CardsContainerProps<T>) => {
export const CardsContainer = <T extends GridValidRowModel>(props: CardsContainerProps<T>) => {
const {
uniqueId,
defaultSortParams,
Expand All @@ -59,6 +57,7 @@ export const CardsContainer = <T extends GridValidRowModel,>(props: CardsContain
relationName,
filtersSerializer,
showTotalCount,
layout = 'horizontal'
} = props;

const { locale: l10nLocale } = useL10N();
Expand Down Expand Up @@ -122,50 +121,6 @@ export const CardsContainer = <T extends GridValidRowModel,>(props: CardsContain
});
};

function handleSortModelChange(newModel: GridSortModel) {
setPage(0);
setSortModel(newModel);

const _orderBy = newModel
.filter((m: any) => m.sort)
.map((m: any) => ({
attribute: m.field,
descending: m.sort === 'desc',
}));

setQueryCustomizer((prevQueryCustomizer) => {
const strippedQueryCustomizer: QueryCustomizer<T> = {
...prevQueryCustomizer,
};
if (!!strippedQueryCustomizer._seek) {
delete strippedQueryCustomizer._seek.lastItem;
}
// we need to reset _seek so that previous configuration is erased
return {
...strippedQueryCustomizer,
_orderBy,
_seek: {
limit: rowsPerPage + 1,
},
};
});
}

async function handlePageChange(isNext: boolean) {
setQueryCustomizer((prevQueryCustomizer) => {
return {
...prevQueryCustomizer,
_seek: {
limit: isNext ? rowsPerPage + 1 : rowsPerPage,
reverse: !isNext,
lastItem: isNext ? lastItem : firstItem,
},
};
});

setIsNextButtonEnabled(!isNext);
}

async function fetching() {
setIsInternalLoading(true);

Expand All @@ -176,9 +131,7 @@ export const CardsContainer = <T extends GridValidRowModel,>(props: CardsContain
};
const { data: res, headers } = await fetch!(processedQueryCustomizer);

// if (typeof showTotalCount === 'boolean' ? showTotalCount : true) {
setTotalCount(headers[X_JUDO_COUNT] ? Number(headers[X_JUDO_COUNT]) : -1);
// }
setTotalCount(headers[X_JUDO_COUNT] ? Number(headers[X_JUDO_COUNT]) : -1);

if (res.length > rowsPerPage) {
setIsNextButtonEnabled(true);
Expand Down Expand Up @@ -206,53 +159,66 @@ export const CardsContainer = <T extends GridValidRowModel,>(props: CardsContain
fetchData();
}, [queryCustomizer, refreshCounter]);

const onPageChange = useCallback((event: ChangeEvent<unknown>, page: number) => {
alert(page);
}, []);
async function handlePageChange(isNext: boolean) {
setQueryCustomizer((prevQueryCustomizer) => {
return {
...prevQueryCustomizer,
_seek: {
limit: isNext ? rowsPerPage + 1 : rowsPerPage,
reverse: !isNext,
lastItem: isNext ? lastItem : firstItem,
},
};
});

setIsNextButtonEnabled(!isNext);
}

const formatValue = useCallback((value: any) => {
if (value instanceof Date) {
return new Intl.DateTimeFormat(l10nLocale, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).format(value);
}
if (value === undefined || value === null) {
return '';
}
return value.toString();
}, []);
const filterDefs: CardsFilterDefinition[] = [
{ type: 'boolean', field: 'nakedEye', label: 'Naked Eye', values: [{ value: true, label: 'Yes' }, { value: false, label: 'No' }] },
{ type: 'string', field: 'constellation', label: 'Constellation', values: [{ value: 'Andromeda', label: 'Andromeda' }, { value: 'Corvus', label: 'Corvus' }, { value: 'Ursa Major', label: 'Ursa Major' }] },
];

return <Box>
<Typography>ID: {uniqueId}</Typography>
<Grid container spacing={2}>
<Grid item xs={12}>Filters</Grid>
<Grid item xs={12}>
<Grid container spacing={2} direction="row">
{data.map(d => (<Grid item sm={12} md={6} lg={6} xl={4} key={d.__identifier}>
<Card variant="outlined">
<CardContent>
{columns.map((k, idx) => (
<Typography key={`${d.__identifier}-${k.field}`} variant={idx === 0 ? 'h5' : 'body2'}>
{formatValue(d[k.field])}
</Typography>
))}
</CardContent>
<CardActions>
<Button size="small" onClick={() => actions.openPageAction(d)}>Learn More</Button>
</CardActions>
</Card>
</Grid>))}
return (
<Box>
<Grid container spacing={2}>
<Grid item xs={layout === 'horizontal' ? 3 : 12}>
<CardsFilter filterDefinitions={filterDefs} />
</Grid>
<Grid item xs={layout === 'horizontal' ? 9 : 12}>
<Grid container spacing={2} direction="row">
{data.map((d) => (
<DefaultCard key={d.__identifier} onRowClick={actions.openPageAction} row={d} columns={columns} />
))}
</Grid>
</Grid>
<Grid item xs={12}>
<TablePagination
component="div"
count={totalCount ?? -1}
page={page}
onPageChange={ (_, newPage) => {
let isNext = true;
if (newPage < page) {
isNext = false;
}
setPage(newPage);
handlePageChange(isNext);
} }
rowsPerPage={rowsPerPage}
rowsPerPageOptions={pageSizeOptions}
labelDisplayedRows={({ from, to }) =>
`${from}–${to}${totalCount !== undefined && totalCount > -1 ? ` / ${totalCount}` : ''}`
}
nextIconButtonProps={ {
disabled: !isNextButtonEnabled,
} }
backIconButtonProps={ {
disabled: page === 0,
} }
/>
</Grid>
</Grid>
<Grid item xs={12}>
<Pagination count={Math.ceil(totalCount / pageLimit)} page={page} onChange={onPageChange} variant="outlined" shape="rounded" />
</Grid>
</Grid>
</Box>;
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{{> fragment.header.hbs }}

import { type FC, useState, useCallback } from 'react';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import FormGroup from '@mui/material/FormGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import { useL10N } from '~/l10n/l10n-context';
import { useTranslation } from 'react-i18next';

export interface CardsFilterDefinition {
type: 'boolean' | 'string';
field: string;
label: string;
values: { value: any, label: string }[];
}

export const CardsFilter: FC<{ filterDefinitions: CardsFilterDefinition[], onFiltersChanged?: (values: Record<string, any>) => void }> = ({ filterDefinitions, onFiltersChanged }) => {
const { locale: l10nLocale } = useL10N();
const { t } = useTranslation();

const [values, setValues] = useState<Record<string, any>>({});

const updateValue = useCallback((field: string, value: any) => {
const newValues = {
...values,
[field]: values[field] === value ? null : value,
};
setValues(newValues);
onFiltersChanged?.(newValues);
}, [values]);

const clearFilters = useCallback(() => {
setValues({});
onFiltersChanged?.({});
}, [values]);

return (
<Grid container direction="row" spacing={2}>
<Grid item xs={12}>
<Typography variant="h4">
Filters
</Typography>
<Button variant={'text'} onClick={clearFilters} >Clear all</Button>
</Grid>
{filterDefinitions.map(d => (
<Grid item xs={12} key={d.field}>
<Typography variant="subtitle1">
{d.label}:
</Typography>
<FormGroup>
{d.values.map(v => (
<FormControlLabel
key={`${d.label}-${v.value}`}
control={<Checkbox
size="small"
checked={values[d.field as any] === v.value}
onClick={() => updateValue(d.field, v.value)}
/>}
label={v.label}
/>
))}
</FormGroup>
</Grid>
))}
</Grid>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{{> fragment.header.hbs }}

import { type FC, useCallback } from 'react';
import Grid from '@mui/material/Grid';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import CardActions from '@mui/material/CardActions';
import Button from '@mui/material/Button';
import type {
GridColDef,
GridEventListener,
GridValidRowModel,
} from '@mui/x-data-grid{{ getMUIDataGridPlanSuffix }}';
import { useL10N } from '~/l10n/l10n-context';
import { useTranslation } from 'react-i18next';

export interface CardProps<T extends GridValidRowModel> {
row: T;
columns: GridColDef<T>[];
onRowClick?: (row: T) => void;
}

export const DefaultCard: FC<CardProps<any>> = ({ onRowClick, row, columns }) => {
const { locale: l10nLocale } = useL10N();
const { t } = useTranslation();

const formatValue = useCallback((value: any) => {
if (value instanceof Date) {
return new Intl.DateTimeFormat(l10nLocale, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).format(value);
}
if (value === undefined || value === null) {
return '';
}
return value.toString();
}, []);

return (
<Grid item sm={12} md={6} lg={6} xl={4}>
<Card variant="outlined">
<CardContent>
{columns.map((k, idx) => (
<Typography key={`${row.__identifier}-${k.field}`} variant={idx === 0 ? 'h5' : 'body2'}>
{formatValue(row[k.field])}
</Typography>
))}
</CardContent>
<CardActions>
<Button size="small" onClick={() => onRowClick?.(row)}>
Learn More
</Button>
</CardActions>
</Card>
</Grid>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ export * from './SingleRelationInput';
export * from './Tags';
export * from './AssociationButton';
export * from './BinaryInput';
export * from './DefaultCard';
export * from './CardsContainer';
export * from './CardsFilter';
export * from './NumericInput';
export * from './TextWithTypeAhead';
export * from './TrinaryLogicCombobox';
8 changes: 8 additions & 0 deletions judo-ui-react/src/main/resources/ui-react.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,14 @@ templates:
pathExpression: "'src/components/widgets/CardsContainer.tsx'"
templateName: actor/src/components/widgets/CardsContainer.tsx.hbs

- name: actor/src/components/widgets/CardsFilter.tsx
pathExpression: "'src/components/widgets/CardsFilter.tsx'"
templateName: actor/src/components/widgets/CardsFilter.tsx.hbs

- name: actor/src/components/widgets/DefaultCard.tsx
pathExpression: "'src/components/widgets/DefaultCard.tsx'"
templateName: actor/src/components/widgets/DefaultCard.tsx.hbs

# Actor - src - components-api

- name: actor/src/components-api/components/Action.ts
Expand Down

0 comments on commit 95ac72e

Please sign in to comment.