feat(filters): add favorites and status dimensions (#188)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import TreeSelect from 'antd/es/tree-select';
|
||||
import Empty from 'antd/es/empty';
|
||||
import Select from 'antd/es/select';
|
||||
import Switch from 'antd/es/switch';
|
||||
import Tag from 'antd/es/tag';
|
||||
import Slider from 'antd/es/slider';
|
||||
import InputNumber from 'antd/es/input-number';
|
||||
@@ -8,6 +9,9 @@ import {
|
||||
buildCategoryTree,
|
||||
categoryPath,
|
||||
formatPriceRange,
|
||||
ItemStatus,
|
||||
STATUS_OPTIONS,
|
||||
statusLabel,
|
||||
toCategoryTreeData
|
||||
} from '../../filters';
|
||||
import type { FilterDimension } from './dimension';
|
||||
@@ -190,3 +194,78 @@ export const priceDimension: FilterDimension = {
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const favoritesDimension: FilterDimension = {
|
||||
key: 'favorites',
|
||||
placement: 'drawer',
|
||||
heading: 'Favorites',
|
||||
|
||||
// Shown to signed-out visitors too: switching it on prompts them to sign in,
|
||||
// which is how they learn favorites exist. The prompt itself is not this
|
||||
// dimension's business — useCatalogue reports needsFavoritesAuth and the page
|
||||
// owns the modal.
|
||||
render: ({ filters, onChange }) => (
|
||||
// Deliberately not wrapped in a <label>: antd renders the switch as a
|
||||
// button, which is labelable, so a wrapping label can forward a click the
|
||||
// switch already handled and toggle it twice.
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<Switch
|
||||
checked={filters.favoritesOnly}
|
||||
onChange={(checked) => onChange({ ...filters, favoritesOnly: checked })}
|
||||
aria-label="Only my favorites"
|
||||
/>
|
||||
<span>Only my favorites</span>
|
||||
</div>
|
||||
),
|
||||
|
||||
chips: ({ filters, onChange }) =>
|
||||
filters.favoritesOnly
|
||||
? [
|
||||
{
|
||||
key: 'favorites',
|
||||
label: 'My favorites',
|
||||
onRemove: () => onChange({ ...filters, favoritesOnly: false })
|
||||
}
|
||||
]
|
||||
: []
|
||||
};
|
||||
|
||||
export const statusDimension: FilterDimension = {
|
||||
key: 'status',
|
||||
placement: 'drawer',
|
||||
heading: 'Status — any of these',
|
||||
|
||||
// The status dimension itself rather than presets over it, which #105's Sold
|
||||
// / Not sold / All control was. Presets could not express Published or
|
||||
// Unpublished and could not isolate Reserved. Selecting statuses answers all
|
||||
// of them: Unpublished is Pending, Published is the other three, and Not sold
|
||||
// is everything except Sold. See #132.
|
||||
render: ({ filters, onChange }) => (
|
||||
<Select
|
||||
allowClear
|
||||
mode="multiple"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder="Any status"
|
||||
aria-label="Filter by status"
|
||||
style={{ width: '100%' }}
|
||||
value={filters.status ?? []}
|
||||
onChange={(value: ItemStatus[]) =>
|
||||
onChange({ ...filters, status: value.length ? value : null })
|
||||
}
|
||||
options={STATUS_OPTIONS}
|
||||
/>
|
||||
),
|
||||
|
||||
chips: ({ filters, onChange }) =>
|
||||
(filters.status ?? []).map((status) => ({
|
||||
key: `status-${status}`,
|
||||
label: statusLabel(status),
|
||||
onRemove: () => {
|
||||
const rest = (filters.status ?? []).filter((value) => value !== status);
|
||||
// Null rather than an empty list: emptying it means "no status filter",
|
||||
// where an empty list would mean "no statuses" and show nothing.
|
||||
onChange({ ...filters, status: rest.length ? rest : null });
|
||||
}
|
||||
}))
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user