aboutsummaryrefslogtreecommitdiff
path: root/src/components/input/FieldFilters.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/input/FieldFilters.tsx')
-rw-r--r--src/components/input/FieldFilters.tsx117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/components/input/FieldFilters.tsx b/src/components/input/FieldFilters.tsx
new file mode 100644
index 0000000..2174068
--- /dev/null
+++ b/src/components/input/FieldFilters.tsx
@@ -0,0 +1,117 @@
+import {
+ Button,
+ Column,
+ Grid,
+ Icon,
+ List,
+ ListItem,
+ Menu,
+ MenuItem,
+ MenuTrigger,
+ Popover,
+ Row,
+} from '@umami/react-zen';
+import { endOfDay, subMonths } from 'date-fns';
+import type { Key } from 'react';
+import { Empty } from '@/components/common/Empty';
+import { FilterRecord } from '@/components/common/FilterRecord';
+import { useFields, useMessages, useMobile } from '@/components/hooks';
+import { Plus } from '@/components/icons';
+
+export interface FieldFiltersProps {
+ websiteId: string;
+ value?: { name: string; operator: string; value: string }[];
+ exclude?: string[];
+ onChange?: (data: any) => void;
+}
+
+export function FieldFilters({ websiteId, value, exclude = [], onChange }: FieldFiltersProps) {
+ const { formatMessage, messages } = useMessages();
+ const { fields } = useFields();
+ const startDate = subMonths(endOfDay(new Date()), 6);
+ const endDate = endOfDay(new Date());
+ const { isMobile } = useMobile();
+
+ const updateFilter = (name: string, props: Record<string, any>) => {
+ onChange(value.map(filter => (filter.name === name ? { ...filter, ...props } : filter)));
+ };
+
+ const handleAdd = (name: Key) => {
+ onChange(value.concat({ name: name.toString(), operator: 'eq', value: '' }));
+ };
+
+ const handleChange = (name: string, value: Key) => {
+ updateFilter(name, { value });
+ };
+
+ const handleSelect = (name: string, operator: Key) => {
+ updateFilter(name, { operator });
+ };
+
+ const handleRemove = (name: string) => {
+ onChange(value.filter(filter => filter.name !== name));
+ };
+
+ return (
+ <Grid columns={{ xs: '1fr', md: '180px 1fr' }} overflow="hidden" gapY="6">
+ <Row display={{ xs: 'flex', md: 'none' }}>
+ <MenuTrigger>
+ <Button>
+ <Icon>
+ <Plus />
+ </Icon>
+ </Button>
+ <Popover placement={isMobile ? 'left' : 'bottom start'} shouldFlip>
+ <Menu
+ onAction={handleAdd}
+ style={{ maxHeight: 'calc(100vh - 2rem)', overflowY: 'auto' }}
+ >
+ {fields
+ .filter(({ name }) => !exclude.includes(name))
+ .map(field => {
+ const isDisabled = !!value.find(({ name }) => name === field.name);
+ return (
+ <MenuItem key={field.name} id={field.name} isDisabled={isDisabled}>
+ {field.label}
+ </MenuItem>
+ );
+ })}
+ </Menu>
+ </Popover>
+ </MenuTrigger>
+ </Row>
+ <Column display={{ xs: 'none', md: 'flex' }} border="right" paddingRight="3" marginRight="6">
+ <List onAction={handleAdd}>
+ {fields
+ .filter(({ name }) => !exclude.includes(name))
+ .map(field => {
+ const isDisabled = !!value.find(({ name }) => name === field.name);
+ return (
+ <ListItem key={field.name} id={field.name} isDisabled={isDisabled}>
+ {field.label}
+ </ListItem>
+ );
+ })}
+ </List>
+ </Column>
+ <Column overflow="auto" gapY="4" style={{ contain: 'layout' }}>
+ {value.map(filter => {
+ return (
+ <FilterRecord
+ key={filter.name}
+ websiteId={websiteId}
+ type={filter.name}
+ startDate={startDate}
+ endDate={endDate}
+ {...filter}
+ onSelect={handleSelect}
+ onRemove={handleRemove}
+ onChange={handleChange}
+ />
+ );
+ })}
+ {!value.length && <Empty message={formatMessage(messages.nothingSelected)} />}
+ </Column>
+ </Grid>
+ );
+}