@open-mercato/core 0.6.7-develop.6785.1.1dd7cfac55 → 0.6.7-develop.6795.1.8a3f27921c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/modules/auth/backend/settings/page.js +14 -9
- package/dist/modules/auth/backend/settings/page.js.map +2 -2
- package/dist/modules/customers/cli.js +17 -7
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +229 -216
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/search.js +50 -29
- package/dist/modules/customers/search.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/[id]/route.js +22 -9
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/route.js +13 -4
- package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
- package/dist/modules/data_sync/api/options.js +7 -2
- package/dist/modules/data_sync/api/options.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +7 -2
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +8 -3
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +7 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/route.js +8 -3
- package/dist/modules/data_sync/api/runs/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/runs.js +7 -2
- package/dist/modules/data_sync/api/runs.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +19 -6
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +13 -4
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/data_sync/api/validate.js +9 -4
- package/dist/modules/data_sync/api/validate.js.map +2 -2
- package/dist/modules/design_system/acl.js +9 -0
- package/dist/modules/design_system/acl.js.map +7 -0
- package/dist/modules/design_system/backend/design-system/page.js +11 -0
- package/dist/modules/design_system/backend/design-system/page.js.map +7 -0
- package/dist/modules/design_system/backend/design-system/page.meta.js +15 -0
- package/dist/modules/design_system/backend/design-system/page.meta.js.map +7 -0
- package/dist/modules/design_system/gallery/components/CodeSnippet.js +52 -0
- package/dist/modules/design_system/gallery/components/CodeSnippet.js.map +7 -0
- package/dist/modules/design_system/gallery/components/EntryCard.js +63 -0
- package/dist/modules/design_system/gallery/components/EntryCard.js.map +7 -0
- package/dist/modules/design_system/gallery/components/GalleryShell.js +163 -0
- package/dist/modules/design_system/gallery/components/GalleryShell.js.map +7 -0
- package/dist/modules/design_system/gallery/components/VariantPreview.js +9 -0
- package/dist/modules/design_system/gallery/components/VariantPreview.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/banners.js +186 -0
- package/dist/modules/design_system/gallery/entries/banners.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/buttons.js +361 -0
- package/dist/modules/design_system/gallery/entries/buttons.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/charts.js +457 -0
- package/dist/modules/design_system/gallery/entries/charts.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/dates.js +306 -0
- package/dist/modules/design_system/gallery/entries/dates.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/detail.js +358 -0
- package/dist/modules/design_system/gallery/entries/detail.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/display.js +785 -0
- package/dist/modules/design_system/gallery/entries/display.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/feedback.js +658 -0
- package/dist/modules/design_system/gallery/entries/feedback.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/filters.js +505 -0
- package/dist/modules/design_system/gallery/entries/filters.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/foundations.js +453 -0
- package/dist/modules/design_system/gallery/entries/foundations.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/icons.js +140 -0
- package/dist/modules/design_system/gallery/entries/icons.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/inputs.js +1217 -0
- package/dist/modules/design_system/gallery/entries/inputs.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/messages.js +334 -0
- package/dist/modules/design_system/gallery/entries/messages.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/navigation.js +475 -0
- package/dist/modules/design_system/gallery/entries/navigation.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/notifications.js +257 -0
- package/dist/modules/design_system/gallery/entries/notifications.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/overlays.js +576 -0
- package/dist/modules/design_system/gallery/entries/overlays.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/scaffolding.js +378 -0
- package/dist/modules/design_system/gallery/entries/scaffolding.js.map +7 -0
- package/dist/modules/design_system/gallery/entries/schedule.js +297 -0
- package/dist/modules/design_system/gallery/entries/schedule.js.map +7 -0
- package/dist/modules/design_system/gallery/registry.js +139 -0
- package/dist/modules/design_system/gallery/registry.js.map +7 -0
- package/dist/modules/design_system/gallery/types.js +1 -0
- package/dist/modules/design_system/gallery/types.js.map +7 -0
- package/dist/modules/design_system/index.js +9 -0
- package/dist/modules/design_system/index.js.map +7 -0
- package/dist/modules/design_system/setup.js +14 -0
- package/dist/modules/design_system/setup.js.map +7 -0
- package/dist/modules/inbox_ops/search.js +15 -4
- package/dist/modules/inbox_ops/search.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +11 -5
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/health/route.js +6 -3
- package/dist/modules/integrations/api/[id]/health/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/route.js +6 -3
- package/dist/modules/integrations/api/[id]/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/state/route.js +6 -3
- package/dist/modules/integrations/api/[id]/state/route.js.map +2 -2
- package/dist/modules/integrations/api/[id]/version/route.js +6 -3
- package/dist/modules/integrations/api/[id]/version/route.js.map +2 -2
- package/dist/modules/integrations/api/logs/route.js +6 -3
- package/dist/modules/integrations/api/logs/route.js.map +2 -2
- package/dist/modules/integrations/api/route.js +6 -3
- package/dist/modules/integrations/api/route.js.map +2 -2
- package/dist/modules/integrations/api/umes-read.js +2 -2
- package/dist/modules/integrations/api/umes-read.js.map +2 -2
- package/dist/modules/integrations/lib/organization-scope.js +2 -9
- package/dist/modules/integrations/lib/organization-scope.js.map +2 -2
- package/dist/modules/messages/search.js +10 -5
- package/dist/modules/messages/search.js.map +2 -2
- package/dist/modules/query_index/data/entities.js +2 -1
- package/dist/modules/query_index/data/entities.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +32 -50
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js +17 -0
- package/dist/modules/query_index/migrations/Migration20260731105052_query_index.js.map +7 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/package.json +16 -16
- package/src/modules/auth/backend/settings/page.tsx +17 -8
- package/src/modules/customers/cli.ts +17 -7
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +24 -13
- package/src/modules/customers/i18n/de.json +17 -0
- package/src/modules/customers/i18n/en.json +17 -0
- package/src/modules/customers/i18n/es.json +17 -0
- package/src/modules/customers/i18n/pl.json +17 -0
- package/src/modules/customers/search.ts +59 -27
- package/src/modules/data_sync/AGENTS.md +1 -0
- package/src/modules/data_sync/api/mappings/[id]/route.ts +22 -9
- package/src/modules/data_sync/api/mappings/route.ts +13 -4
- package/src/modules/data_sync/api/options.ts +7 -2
- package/src/modules/data_sync/api/run.ts +7 -2
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +8 -3
- package/src/modules/data_sync/api/runs/[id]/retry.ts +7 -2
- package/src/modules/data_sync/api/runs/[id]/route.ts +8 -3
- package/src/modules/data_sync/api/runs.ts +7 -2
- package/src/modules/data_sync/api/schedules/[id]/route.ts +19 -6
- package/src/modules/data_sync/api/schedules/route.ts +13 -4
- package/src/modules/data_sync/api/validate.ts +9 -4
- package/src/modules/design_system/AGENTS.md +34 -0
- package/src/modules/design_system/acl.ts +5 -0
- package/src/modules/design_system/backend/design-system/page.meta.ts +11 -0
- package/src/modules/design_system/backend/design-system/page.tsx +14 -0
- package/src/modules/design_system/gallery/components/CodeSnippet.tsx +55 -0
- package/src/modules/design_system/gallery/components/EntryCard.tsx +95 -0
- package/src/modules/design_system/gallery/components/GalleryShell.tsx +206 -0
- package/src/modules/design_system/gallery/components/VariantPreview.tsx +15 -0
- package/src/modules/design_system/gallery/entries/banners.tsx +226 -0
- package/src/modules/design_system/gallery/entries/buttons.tsx +422 -0
- package/src/modules/design_system/gallery/entries/charts.tsx +518 -0
- package/src/modules/design_system/gallery/entries/dates.tsx +325 -0
- package/src/modules/design_system/gallery/entries/detail.tsx +403 -0
- package/src/modules/design_system/gallery/entries/display.tsx +893 -0
- package/src/modules/design_system/gallery/entries/feedback.tsx +746 -0
- package/src/modules/design_system/gallery/entries/filters.tsx +570 -0
- package/src/modules/design_system/gallery/entries/foundations.tsx +529 -0
- package/src/modules/design_system/gallery/entries/icons.tsx +168 -0
- package/src/modules/design_system/gallery/entries/inputs.tsx +1516 -0
- package/src/modules/design_system/gallery/entries/messages.tsx +339 -0
- package/src/modules/design_system/gallery/entries/navigation.tsx +573 -0
- package/src/modules/design_system/gallery/entries/notifications.tsx +285 -0
- package/src/modules/design_system/gallery/entries/overlays.tsx +677 -0
- package/src/modules/design_system/gallery/entries/scaffolding.tsx +430 -0
- package/src/modules/design_system/gallery/entries/schedule.tsx +307 -0
- package/src/modules/design_system/gallery/registry.ts +152 -0
- package/src/modules/design_system/gallery/types.ts +32 -0
- package/src/modules/design_system/i18n/.hardcoded-allowlist.json +71 -0
- package/src/modules/design_system/i18n/de.json +46 -0
- package/src/modules/design_system/i18n/en.json +46 -0
- package/src/modules/design_system/i18n/es.json +46 -0
- package/src/modules/design_system/i18n/pl.json +46 -0
- package/src/modules/design_system/index.ts +7 -0
- package/src/modules/design_system/setup.ts +12 -0
- package/src/modules/inbox_ops/i18n/de.json +3 -0
- package/src/modules/inbox_ops/i18n/en.json +3 -0
- package/src/modules/inbox_ops/i18n/es.json +3 -0
- package/src/modules/inbox_ops/i18n/pl.json +3 -0
- package/src/modules/inbox_ops/search.ts +20 -4
- package/src/modules/integrations/api/[id]/credentials/route.ts +11 -5
- package/src/modules/integrations/api/[id]/health/route.ts +6 -3
- package/src/modules/integrations/api/[id]/route.ts +6 -3
- package/src/modules/integrations/api/[id]/state/route.ts +6 -3
- package/src/modules/integrations/api/[id]/version/route.ts +6 -3
- package/src/modules/integrations/api/logs/route.ts +6 -3
- package/src/modules/integrations/api/route.ts +6 -3
- package/src/modules/integrations/api/umes-read.ts +2 -2
- package/src/modules/integrations/lib/organization-scope.ts +5 -25
- package/src/modules/messages/i18n/de.json +2 -0
- package/src/modules/messages/i18n/en.json +2 -0
- package/src/modules/messages/i18n/es.json +2 -0
- package/src/modules/messages/i18n/pl.json +2 -0
- package/src/modules/messages/search.ts +11 -5
- package/src/modules/query_index/data/entities.ts +7 -0
- package/src/modules/query_index/lib/engine.ts +40 -63
- package/src/modules/query_index/migrations/.snapshot-open-mercato.json +12 -0
- package/src/modules/query_index/migrations/Migration20260731105052_query_index.ts +35 -0
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { Download } from 'lucide-react'
|
|
3
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
4
|
+
import {
|
|
5
|
+
FilterBar,
|
|
6
|
+
type FilterDef,
|
|
7
|
+
type FilterValues,
|
|
8
|
+
} from '@open-mercato/ui/backend/FilterBar'
|
|
9
|
+
import { QuickFilters, type FilterPreset } from '@open-mercato/ui/backend/filters/QuickFilters'
|
|
10
|
+
import { ActiveFilterChips } from '@open-mercato/ui/backend/filters/ActiveFilterChips'
|
|
11
|
+
import { AdvancedFilterBuilder } from '@open-mercato/ui/backend/filters/AdvancedFilterBuilder'
|
|
12
|
+
import { FilterEmptyState } from '@open-mercato/ui/backend/filters/FilterEmptyState'
|
|
13
|
+
import { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'
|
|
14
|
+
import { FilteredEmptyResults } from '@open-mercato/ui/backend/filters/FilteredEmptyResults'
|
|
15
|
+
import { SearchEmptyResults } from '@open-mercato/ui/backend/filters/SearchEmptyResults'
|
|
16
|
+
import type { FilterFieldDef } from '@open-mercato/shared/lib/query/advanced-filter'
|
|
17
|
+
import type {
|
|
18
|
+
AdvancedFilterTree,
|
|
19
|
+
FilterGroup,
|
|
20
|
+
FilterRule,
|
|
21
|
+
} from '@open-mercato/shared/lib/query/advanced-filter-tree'
|
|
22
|
+
import type { GalleryEntry } from '../types'
|
|
23
|
+
|
|
24
|
+
// Component titles and variant names are proper nouns from the codebase and
|
|
25
|
+
// are deliberately not translated. `code` MUST contain the entry's importPath
|
|
26
|
+
// (enforced by the registry-integrity test) and is always reviewed alongside
|
|
27
|
+
// its sibling `render`.
|
|
28
|
+
//
|
|
29
|
+
// AdvancedFilterPanel itself is not showcased: it is a popover host that
|
|
30
|
+
// orchestrates open/anchor state and saved-filter persistence. Its
|
|
31
|
+
// presentational subcomponents (AdvancedFilterBuilder, QuickFilters,
|
|
32
|
+
// FilterEmptyState) are shown standalone below with variant titles noting it.
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Inline demo data — no API calls. Field defs and trees mirror what CRM list
|
|
36
|
+
// pages pass to the advanced filter stack.
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
const demoFilterDefs: FilterDef[] = [
|
|
40
|
+
{ id: 'city', label: 'City', type: 'text', placeholder: 'e.g. Berlin' },
|
|
41
|
+
{
|
|
42
|
+
id: 'status',
|
|
43
|
+
label: 'Status',
|
|
44
|
+
type: 'select',
|
|
45
|
+
options: [
|
|
46
|
+
{ value: 'active', label: 'Active' },
|
|
47
|
+
{ value: 'prospect', label: 'Prospect' },
|
|
48
|
+
{ value: 'churned', label: 'Churned' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{ id: 'hasOpenDeals', label: 'Has open deals', type: 'checkbox' },
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
const demoFields: FilterFieldDef[] = [
|
|
55
|
+
{ key: 'name', label: 'Name', type: 'text' },
|
|
56
|
+
{ key: 'email', label: 'Email', type: 'text' },
|
|
57
|
+
{
|
|
58
|
+
key: 'status',
|
|
59
|
+
label: 'Status',
|
|
60
|
+
type: 'select',
|
|
61
|
+
options: [
|
|
62
|
+
{ value: 'active', label: 'Active', tone: 'success' },
|
|
63
|
+
{ value: 'prospect', label: 'Prospect', tone: 'info' },
|
|
64
|
+
{ value: 'churned', label: 'Churned', tone: 'error' },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{ key: 'employees', label: 'Employees', type: 'number' },
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
function rule(id: string, field: string, operator: FilterRule['operator'], value: unknown): FilterRule {
|
|
71
|
+
return { id, type: 'rule', field, operator, value }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function tree(children: FilterGroup['children']): AdvancedFilterTree {
|
|
75
|
+
return { root: { id: 'root', type: 'group', combinator: 'and', children } }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const ruleChipsTree = tree([
|
|
79
|
+
rule('r1', 'name', 'contains', 'north'),
|
|
80
|
+
rule('r2', 'status', 'is', 'active'),
|
|
81
|
+
rule('r3', 'employees', 'between', [10, 50]),
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
const groupChipTree = tree([
|
|
85
|
+
{
|
|
86
|
+
id: 'g1',
|
|
87
|
+
type: 'group',
|
|
88
|
+
combinator: 'or',
|
|
89
|
+
children: [
|
|
90
|
+
rule('g1r1', 'status', 'is', 'prospect'),
|
|
91
|
+
rule('g1r2', 'status', 'is', 'churned'),
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
])
|
|
95
|
+
|
|
96
|
+
const valuelessChipTree = tree([rule('r4', 'email', 'is_empty', null)])
|
|
97
|
+
|
|
98
|
+
const builderSingleRuleTree = tree([rule('b1', 'status', 'is', 'active')])
|
|
99
|
+
|
|
100
|
+
const builderNestedTree = tree([
|
|
101
|
+
rule('b2', 'name', 'contains', 'north'),
|
|
102
|
+
{
|
|
103
|
+
id: 'bg1',
|
|
104
|
+
type: 'group',
|
|
105
|
+
combinator: 'or',
|
|
106
|
+
children: [
|
|
107
|
+
rule('bg1r1', 'status', 'is', 'prospect'),
|
|
108
|
+
rule('bg1r2', 'employees', 'greater_than', 100),
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
])
|
|
112
|
+
|
|
113
|
+
const demoPresets: FilterPreset[] = [
|
|
114
|
+
{
|
|
115
|
+
id: 'active-accounts',
|
|
116
|
+
labelKey: 'Active accounts',
|
|
117
|
+
iconName: 'filter',
|
|
118
|
+
build: () => tree([rule('p1', 'status', 'is', 'active')]),
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: 'added-this-week',
|
|
122
|
+
labelKey: 'Added this week',
|
|
123
|
+
iconName: 'clock',
|
|
124
|
+
build: () => tree([rule('p2', 'name', 'is_not_empty', null)]),
|
|
125
|
+
},
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
const userScopedPresets: FilterPreset[] = [
|
|
129
|
+
...demoPresets,
|
|
130
|
+
{
|
|
131
|
+
id: 'my-accounts',
|
|
132
|
+
labelKey: 'My accounts',
|
|
133
|
+
requiresUser: true,
|
|
134
|
+
build: ({ userId }) => tree([rule('p3', 'name', 'contains', userId)]),
|
|
135
|
+
},
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// Demo wrappers — controlled components need local state to be interactive in
|
|
140
|
+
// the gallery. The `code` snippets show the essential consumer usage, not
|
|
141
|
+
// these wrappers.
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
function DemoFilterBar({
|
|
145
|
+
layout,
|
|
146
|
+
trailingItems,
|
|
147
|
+
}: {
|
|
148
|
+
layout?: 'stacked' | 'inline'
|
|
149
|
+
trailingItems?: React.ReactNode
|
|
150
|
+
}) {
|
|
151
|
+
const [search, setSearch] = React.useState('')
|
|
152
|
+
const [values, setValues] = React.useState<FilterValues>({ status: 'active' })
|
|
153
|
+
return (
|
|
154
|
+
<div className="w-full max-w-2xl">
|
|
155
|
+
<FilterBar
|
|
156
|
+
searchValue={search}
|
|
157
|
+
onSearchChange={setSearch}
|
|
158
|
+
filters={demoFilterDefs}
|
|
159
|
+
values={values}
|
|
160
|
+
onApply={setValues}
|
|
161
|
+
onClear={() => setValues({})}
|
|
162
|
+
layout={layout}
|
|
163
|
+
trailingItems={trailingItems}
|
|
164
|
+
/>
|
|
165
|
+
</div>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function DemoActiveFilterChips({ initialTree }: { initialTree: AdvancedFilterTree }) {
|
|
170
|
+
const [value, setValue] = React.useState(initialTree)
|
|
171
|
+
const removeNode = (id: string) =>
|
|
172
|
+
setValue((prev) => ({
|
|
173
|
+
root: { ...prev.root, children: prev.root.children.filter((child) => child.id !== id) },
|
|
174
|
+
}))
|
|
175
|
+
return (
|
|
176
|
+
<div className="w-full max-w-2xl">
|
|
177
|
+
<ActiveFilterChips
|
|
178
|
+
tree={value}
|
|
179
|
+
fields={demoFields}
|
|
180
|
+
popoverOpen={false}
|
|
181
|
+
onRemoveNode={removeNode}
|
|
182
|
+
onOpen={() => {}}
|
|
183
|
+
/>
|
|
184
|
+
</div>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function DemoAdvancedFilterBuilder({ initialTree }: { initialTree: AdvancedFilterTree }) {
|
|
189
|
+
const [value, setValue] = React.useState(initialTree)
|
|
190
|
+
return (
|
|
191
|
+
<div className="w-full max-w-2xl rounded-lg border bg-popover p-3">
|
|
192
|
+
<AdvancedFilterBuilder
|
|
193
|
+
fields={demoFields}
|
|
194
|
+
value={value}
|
|
195
|
+
onChange={setValue}
|
|
196
|
+
onApply={() => {}}
|
|
197
|
+
onClear={() => setValue(tree([]))}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function DemoFilterEmptyState({ withQuickFilters }: { withQuickFilters?: boolean }) {
|
|
204
|
+
const addConditionRef = React.useRef<HTMLButtonElement | null>(null)
|
|
205
|
+
return (
|
|
206
|
+
<div className="w-full max-w-xl rounded-lg border bg-popover">
|
|
207
|
+
<FilterEmptyState
|
|
208
|
+
onAddCondition={() => {}}
|
|
209
|
+
addConditionRef={addConditionRef}
|
|
210
|
+
quickFilters={
|
|
211
|
+
withQuickFilters ? (
|
|
212
|
+
<QuickFilters presets={demoPresets} userId="user-1" onApply={() => {}} />
|
|
213
|
+
) : undefined
|
|
214
|
+
}
|
|
215
|
+
/>
|
|
216
|
+
</div>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const filterBarEntry: GalleryEntry = {
|
|
221
|
+
id: 'filter-bar',
|
|
222
|
+
title: 'FilterBar',
|
|
223
|
+
importPath: '@open-mercato/ui/backend/FilterBar',
|
|
224
|
+
variants: [
|
|
225
|
+
{
|
|
226
|
+
id: 'stacked',
|
|
227
|
+
title: 'Stacked (default)',
|
|
228
|
+
render: () => <DemoFilterBar />,
|
|
229
|
+
code: `import { FilterBar, type FilterDef, type FilterValues } from '@open-mercato/ui/backend/FilterBar'
|
|
230
|
+
|
|
231
|
+
const filters: FilterDef[] = [
|
|
232
|
+
{ id: 'city', label: 'City', type: 'text' },
|
|
233
|
+
{ id: 'status', label: 'Status', type: 'select', options: statusOptions },
|
|
234
|
+
{ id: 'hasOpenDeals', label: 'Has open deals', type: 'checkbox' },
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
<FilterBar
|
|
238
|
+
searchValue={search}
|
|
239
|
+
onSearchChange={setSearch}
|
|
240
|
+
filters={filters}
|
|
241
|
+
values={values}
|
|
242
|
+
onApply={setValues}
|
|
243
|
+
onClear={() => setValues({})}
|
|
244
|
+
/>`,
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: 'inline',
|
|
248
|
+
title: 'Inline layout',
|
|
249
|
+
render: () => <DemoFilterBar layout="inline" />,
|
|
250
|
+
code: `import { FilterBar } from '@open-mercato/ui/backend/FilterBar'
|
|
251
|
+
|
|
252
|
+
<FilterBar
|
|
253
|
+
searchValue={search}
|
|
254
|
+
onSearchChange={setSearch}
|
|
255
|
+
filters={filters}
|
|
256
|
+
values={values}
|
|
257
|
+
onApply={setValues}
|
|
258
|
+
layout="inline"
|
|
259
|
+
/>`,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: 'trailing-items',
|
|
263
|
+
title: 'With trailing items',
|
|
264
|
+
render: () => (
|
|
265
|
+
<DemoFilterBar
|
|
266
|
+
trailingItems={
|
|
267
|
+
<Button variant="outline" size="sm">
|
|
268
|
+
<Download />
|
|
269
|
+
Export
|
|
270
|
+
</Button>
|
|
271
|
+
}
|
|
272
|
+
/>
|
|
273
|
+
),
|
|
274
|
+
code: `import { Download } from 'lucide-react'
|
|
275
|
+
import { FilterBar } from '@open-mercato/ui/backend/FilterBar'
|
|
276
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
277
|
+
|
|
278
|
+
<FilterBar
|
|
279
|
+
searchValue={search}
|
|
280
|
+
onSearchChange={setSearch}
|
|
281
|
+
filters={filters}
|
|
282
|
+
values={values}
|
|
283
|
+
onApply={setValues}
|
|
284
|
+
trailingItems={<Button variant="outline" size="sm"><Download />Export</Button>}
|
|
285
|
+
/>`,
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const quickFiltersEntry: GalleryEntry = {
|
|
291
|
+
id: 'quick-filters',
|
|
292
|
+
title: 'QuickFilters',
|
|
293
|
+
importPath: '@open-mercato/ui/backend/filters/QuickFilters',
|
|
294
|
+
variants: [
|
|
295
|
+
{
|
|
296
|
+
id: 'presets',
|
|
297
|
+
title: 'Presets (used inside AdvancedFilterPanel)',
|
|
298
|
+
render: () => (
|
|
299
|
+
<div className="w-full max-w-xl">
|
|
300
|
+
<QuickFilters presets={demoPresets} userId="user-1" onApply={() => {}} />
|
|
301
|
+
</div>
|
|
302
|
+
),
|
|
303
|
+
code: `import { QuickFilters, type FilterPreset } from '@open-mercato/ui/backend/filters/QuickFilters'
|
|
304
|
+
|
|
305
|
+
const presets: FilterPreset[] = [
|
|
306
|
+
{ id: 'active-accounts', labelKey: 'customers.filters.activeAccounts', iconName: 'filter', build: () => activeTree },
|
|
307
|
+
{ id: 'added-this-week', labelKey: 'customers.filters.addedThisWeek', iconName: 'clock', build: () => recentTree },
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
<QuickFilters presets={presets} userId={userId} onApply={(tree, preset) => applyTree(tree)} />`,
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: 'user-scoped',
|
|
314
|
+
title: 'User-scoped preset (hidden without userId)',
|
|
315
|
+
render: () => (
|
|
316
|
+
<div className="flex w-full max-w-xl flex-col gap-6">
|
|
317
|
+
<QuickFilters presets={userScopedPresets} userId="user-1" onApply={() => {}} />
|
|
318
|
+
<QuickFilters presets={userScopedPresets} userId="" onApply={() => {}} />
|
|
319
|
+
</div>
|
|
320
|
+
),
|
|
321
|
+
code: `import { QuickFilters } from '@open-mercato/ui/backend/filters/QuickFilters'
|
|
322
|
+
|
|
323
|
+
// Presets with requiresUser: true are hidden when userId is empty.
|
|
324
|
+
<QuickFilters presets={presets} userId={userId} onApply={applyTree} />`,
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const activeFilterChipsEntry: GalleryEntry = {
|
|
330
|
+
id: 'active-filter-chips',
|
|
331
|
+
title: 'ActiveFilterChips',
|
|
332
|
+
importPath: '@open-mercato/ui/backend/filters/ActiveFilterChips',
|
|
333
|
+
variants: [
|
|
334
|
+
{
|
|
335
|
+
id: 'rule-chips',
|
|
336
|
+
title: 'Rule chips (text, toned select, range)',
|
|
337
|
+
render: () => <DemoActiveFilterChips initialTree={ruleChipsTree} />,
|
|
338
|
+
code: `import { ActiveFilterChips } from '@open-mercato/ui/backend/filters/ActiveFilterChips'
|
|
339
|
+
|
|
340
|
+
<ActiveFilterChips
|
|
341
|
+
tree={tree}
|
|
342
|
+
fields={fields}
|
|
343
|
+
popoverOpen={panelOpen}
|
|
344
|
+
onRemoveNode={(id) => dispatch({ type: 'removeNode', id })}
|
|
345
|
+
onOpen={(focusNodeId) => setPanelOpen(true)}
|
|
346
|
+
/>`,
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: 'group-chip',
|
|
350
|
+
title: 'Group chip (first rule +N)',
|
|
351
|
+
render: () => <DemoActiveFilterChips initialTree={groupChipTree} />,
|
|
352
|
+
code: `import { ActiveFilterChips } from '@open-mercato/ui/backend/filters/ActiveFilterChips'
|
|
353
|
+
|
|
354
|
+
// An OR group renders as a single chip: "Status: Prospect +1".
|
|
355
|
+
<ActiveFilterChips tree={tree} fields={fields} popoverOpen={false} onRemoveNode={removeNode} onOpen={openPanel} />`,
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
id: 'valueless-operator',
|
|
359
|
+
title: 'Valueless operator chip',
|
|
360
|
+
render: () => <DemoActiveFilterChips initialTree={valuelessChipTree} />,
|
|
361
|
+
code: `import { ActiveFilterChips } from '@open-mercato/ui/backend/filters/ActiveFilterChips'
|
|
362
|
+
|
|
363
|
+
// Valueless operators read as a phrase: "Email is empty".
|
|
364
|
+
<ActiveFilterChips tree={tree} fields={fields} popoverOpen={false} onRemoveNode={removeNode} onOpen={openPanel} />`,
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const advancedFilterBuilderEntry: GalleryEntry = {
|
|
370
|
+
id: 'advanced-filter-builder',
|
|
371
|
+
title: 'AdvancedFilterBuilder',
|
|
372
|
+
importPath: '@open-mercato/ui/backend/filters/AdvancedFilterBuilder',
|
|
373
|
+
variants: [
|
|
374
|
+
{
|
|
375
|
+
id: 'single-rule',
|
|
376
|
+
title: 'Single rule (presentational core of AdvancedFilterPanel)',
|
|
377
|
+
render: () => <DemoAdvancedFilterBuilder initialTree={builderSingleRuleTree} />,
|
|
378
|
+
code: `import { AdvancedFilterBuilder } from '@open-mercato/ui/backend/filters/AdvancedFilterBuilder'
|
|
379
|
+
|
|
380
|
+
<AdvancedFilterBuilder
|
|
381
|
+
fields={fields}
|
|
382
|
+
value={tree}
|
|
383
|
+
onChange={setTree}
|
|
384
|
+
onApply={applyFilters}
|
|
385
|
+
onClear={clearFilters}
|
|
386
|
+
/>`,
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
id: 'nested-group',
|
|
390
|
+
title: 'Rule with nested OR group',
|
|
391
|
+
render: () => <DemoAdvancedFilterBuilder initialTree={builderNestedTree} />,
|
|
392
|
+
code: `import { AdvancedFilterBuilder } from '@open-mercato/ui/backend/filters/AdvancedFilterBuilder'
|
|
393
|
+
|
|
394
|
+
// Groups nest up to 3 levels; the value is a plain AdvancedFilterTree.
|
|
395
|
+
<AdvancedFilterBuilder fields={fields} value={tree} onChange={setTree} onApply={applyFilters} onClear={clearFilters} />`,
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const filterEmptyStateEntry: GalleryEntry = {
|
|
401
|
+
id: 'filter-empty-state',
|
|
402
|
+
title: 'FilterEmptyState',
|
|
403
|
+
importPath: '@open-mercato/ui/backend/filters/FilterEmptyState',
|
|
404
|
+
variants: [
|
|
405
|
+
{
|
|
406
|
+
id: 'default',
|
|
407
|
+
title: 'Default (panel empty state of AdvancedFilterPanel)',
|
|
408
|
+
render: () => <DemoFilterEmptyState />,
|
|
409
|
+
code: `import { FilterEmptyState } from '@open-mercato/ui/backend/filters/FilterEmptyState'
|
|
410
|
+
|
|
411
|
+
<FilterEmptyState onAddCondition={openFieldPicker} addConditionRef={addConditionRef} />`,
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
id: 'with-quick-filters',
|
|
415
|
+
title: 'With quick filters slot',
|
|
416
|
+
render: () => <DemoFilterEmptyState withQuickFilters />,
|
|
417
|
+
code: `import { FilterEmptyState } from '@open-mercato/ui/backend/filters/FilterEmptyState'
|
|
418
|
+
import { QuickFilters } from '@open-mercato/ui/backend/filters/QuickFilters'
|
|
419
|
+
|
|
420
|
+
<FilterEmptyState
|
|
421
|
+
onAddCondition={openFieldPicker}
|
|
422
|
+
addConditionRef={addConditionRef}
|
|
423
|
+
quickFilters={<QuickFilters presets={presets} userId={userId} onApply={applyTree} />}
|
|
424
|
+
/>`,
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const listEmptyStateEntry: GalleryEntry = {
|
|
430
|
+
id: 'list-empty-state',
|
|
431
|
+
title: 'ListEmptyState',
|
|
432
|
+
importPath: '@open-mercato/ui/backend/filters/ListEmptyState',
|
|
433
|
+
variants: [
|
|
434
|
+
{
|
|
435
|
+
id: 'with-create-action',
|
|
436
|
+
title: 'With create action',
|
|
437
|
+
render: () => (
|
|
438
|
+
<div className="w-full max-w-xl">
|
|
439
|
+
<ListEmptyState entityName="companies" onCreate={() => {}} createLabel="Add company" />
|
|
440
|
+
</div>
|
|
441
|
+
),
|
|
442
|
+
code: `import { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'
|
|
443
|
+
|
|
444
|
+
// Pass to DataTable's emptyState for the zero-records case.
|
|
445
|
+
<ListEmptyState entityName="companies" createHref="/backend/customers/companies/create" createLabel="Add company" />`,
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
id: 'custom-copy',
|
|
449
|
+
title: 'Custom title and description',
|
|
450
|
+
render: () => (
|
|
451
|
+
<div className="w-full max-w-xl">
|
|
452
|
+
<ListEmptyState
|
|
453
|
+
title="No imports yet"
|
|
454
|
+
description="Connect a source or upload a CSV file to bring records in."
|
|
455
|
+
/>
|
|
456
|
+
</div>
|
|
457
|
+
),
|
|
458
|
+
code: `import { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'
|
|
459
|
+
|
|
460
|
+
<ListEmptyState
|
|
461
|
+
title="No imports yet"
|
|
462
|
+
description="Connect a source or upload a CSV file to bring records in."
|
|
463
|
+
/>`,
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const filteredEmptyResultsEntry: GalleryEntry = {
|
|
469
|
+
id: 'filtered-empty-results',
|
|
470
|
+
title: 'FilteredEmptyResults',
|
|
471
|
+
importPath: '@open-mercato/ui/backend/filters/FilteredEmptyResults',
|
|
472
|
+
variants: [
|
|
473
|
+
{
|
|
474
|
+
id: 'filters-only',
|
|
475
|
+
title: 'Filters active',
|
|
476
|
+
render: () => (
|
|
477
|
+
<div className="w-full max-w-xl">
|
|
478
|
+
<FilteredEmptyResults
|
|
479
|
+
entityNamePlural="companies"
|
|
480
|
+
canRemoveLast
|
|
481
|
+
onClearAll={() => {}}
|
|
482
|
+
onRemoveLast={() => {}}
|
|
483
|
+
/>
|
|
484
|
+
</div>
|
|
485
|
+
),
|
|
486
|
+
code: `import { FilteredEmptyResults } from '@open-mercato/ui/backend/filters/FilteredEmptyResults'
|
|
487
|
+
|
|
488
|
+
// DataTable renders this automatically when filters match nothing.
|
|
489
|
+
<FilteredEmptyResults
|
|
490
|
+
entityNamePlural="companies"
|
|
491
|
+
canRemoveLast={tree.root.children.length > 0}
|
|
492
|
+
onClearAll={clearFilters}
|
|
493
|
+
onRemoveLast={removeLastFilter}
|
|
494
|
+
/>`,
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
id: 'with-search',
|
|
498
|
+
title: 'Search and filters active',
|
|
499
|
+
render: () => (
|
|
500
|
+
<div className="w-full max-w-xl">
|
|
501
|
+
<FilteredEmptyResults
|
|
502
|
+
entityNamePlural="companies"
|
|
503
|
+
canRemoveLast
|
|
504
|
+
onClearAll={() => {}}
|
|
505
|
+
onRemoveLast={() => {}}
|
|
506
|
+
onClearSearch={() => {}}
|
|
507
|
+
/>
|
|
508
|
+
</div>
|
|
509
|
+
),
|
|
510
|
+
code: `import { FilteredEmptyResults } from '@open-mercato/ui/backend/filters/FilteredEmptyResults'
|
|
511
|
+
|
|
512
|
+
// Passing onClearSearch switches to combined copy and clears both at once.
|
|
513
|
+
<FilteredEmptyResults
|
|
514
|
+
entityNamePlural="companies"
|
|
515
|
+
canRemoveLast
|
|
516
|
+
onClearAll={clearFilters}
|
|
517
|
+
onRemoveLast={removeLastFilter}
|
|
518
|
+
onClearSearch={clearSearch}
|
|
519
|
+
/>`,
|
|
520
|
+
},
|
|
521
|
+
],
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const searchEmptyResultsEntry: GalleryEntry = {
|
|
525
|
+
id: 'search-empty-results',
|
|
526
|
+
title: 'SearchEmptyResults',
|
|
527
|
+
importPath: '@open-mercato/ui/backend/filters/SearchEmptyResults',
|
|
528
|
+
variants: [
|
|
529
|
+
{
|
|
530
|
+
id: 'default',
|
|
531
|
+
title: 'Default',
|
|
532
|
+
render: () => (
|
|
533
|
+
<div className="flex w-full max-w-xl justify-center">
|
|
534
|
+
<SearchEmptyResults query="northwind" onClearSearch={() => {}} />
|
|
535
|
+
</div>
|
|
536
|
+
),
|
|
537
|
+
code: `import { SearchEmptyResults } from '@open-mercato/ui/backend/filters/SearchEmptyResults'
|
|
538
|
+
|
|
539
|
+
// DataTable renders this automatically when a search matches nothing.
|
|
540
|
+
<SearchEmptyResults query={search} onClearSearch={() => setSearch('')} />`,
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
id: 'with-entity',
|
|
544
|
+
title: 'With entity label',
|
|
545
|
+
render: () => (
|
|
546
|
+
<div className="flex w-full max-w-xl justify-center">
|
|
547
|
+
<SearchEmptyResults
|
|
548
|
+
query="northwind"
|
|
549
|
+
entityNamePlural="companies"
|
|
550
|
+
onClearSearch={() => {}}
|
|
551
|
+
/>
|
|
552
|
+
</div>
|
|
553
|
+
),
|
|
554
|
+
code: `import { SearchEmptyResults } from '@open-mercato/ui/backend/filters/SearchEmptyResults'
|
|
555
|
+
|
|
556
|
+
<SearchEmptyResults query={search} entityNamePlural="companies" onClearSearch={() => setSearch('')} />`,
|
|
557
|
+
},
|
|
558
|
+
],
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
export const entries: GalleryEntry[] = [
|
|
562
|
+
filterBarEntry,
|
|
563
|
+
quickFiltersEntry,
|
|
564
|
+
activeFilterChipsEntry,
|
|
565
|
+
advancedFilterBuilderEntry,
|
|
566
|
+
filterEmptyStateEntry,
|
|
567
|
+
listEmptyStateEntry,
|
|
568
|
+
filteredEmptyResultsEntry,
|
|
569
|
+
searchEmptyResultsEntry,
|
|
570
|
+
]
|