@actuate-media/cms-admin 0.46.0 → 0.47.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @actuate-media/cms-admin
2
2
 
3
+ ## 0.47.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 135d0c7: Taxonomy Phase B — admin facet filters and taxonomy columns on collection lists.
8
+ - cms-core: `GET /collections/:slug` now accepts `filter.<field>=<value>` query params for fields declared in the collection config. Text/select fields filter by exact match; tag-shaped array fields filter by containment, matching both the canonical `[{ tag }]` row shape and legacy plain-string arrays. Undeclared fields are silently ignored. `listDocuments` gains a `dataFilters` option and `DataFieldFilter` is exported.
9
+ - cms-admin: the collection list grows a facet filter bar (select fields use config options; free-text `category` and tag-shaped arrays load the collection's in-use values via the field-values API) plus taxonomy columns showing category/tag values per row, with a one-click "Clear filters" action.
10
+
3
11
  ## 0.46.0
4
12
 
5
13
  ### Minor Changes
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=collection-list-facets.render.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-list-facets.render.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/views/collection-list-facets.render.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,131 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // @vitest-environment happy-dom
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
5
+ /**
6
+ * CollectionList facet filters + taxonomy columns (taxonomy Phase B).
7
+ * Pins: facet dropdowns appear for select fields (config options), free-text
8
+ * `category`, and tag-shaped arrays (in-use values via field-values API);
9
+ * picking one adds `filter.<field>=` to the list endpoint; and taxonomy
10
+ * columns surface category/tags values in the table.
11
+ */
12
+ let fieldValues = {};
13
+ const cmsApi = vi.fn(async (endpoint, _options) => {
14
+ const match = endpoint.match(/field-values\?field=([^&]+)/);
15
+ if (match) {
16
+ const field = decodeURIComponent(match[1] ?? '');
17
+ return { data: { field, values: fieldValues[field] ?? [] }, status: 200 };
18
+ }
19
+ return { data: {}, status: 200 };
20
+ });
21
+ vi.mock('../../lib/api.js', () => ({
22
+ cmsApi: (e, o) => cmsApi(e, o),
23
+ ensureCsrfToken: vi.fn(async () => { }),
24
+ }));
25
+ let lastEndpoint = '';
26
+ let listDocs = [];
27
+ vi.mock('../../lib/useApiData.js', () => ({
28
+ useApiData: (endpoint) => {
29
+ lastEndpoint = endpoint;
30
+ return {
31
+ data: { docs: listDocs, total: listDocs.length },
32
+ loading: false,
33
+ error: null,
34
+ refetch: vi.fn(),
35
+ };
36
+ },
37
+ }));
38
+ const { CollectionList } = await import('../../views/CollectionList.js');
39
+ const CONFIG = {
40
+ collections: {
41
+ posts: {
42
+ slug: 'posts',
43
+ labels: { singular: 'Post', plural: 'Posts' },
44
+ fields: {
45
+ title: { type: 'text', label: 'Title' },
46
+ topic: {
47
+ type: 'select',
48
+ label: 'Topic',
49
+ options: [
50
+ { label: 'News', value: 'news' },
51
+ { label: 'Tutorials', value: 'tutorials' },
52
+ ],
53
+ },
54
+ category: { type: 'text', label: 'Category', maxLength: 80 },
55
+ body: { type: 'richText', label: 'Body' },
56
+ tags: {
57
+ type: 'array',
58
+ label: 'Tags',
59
+ fields: { tag: { type: 'text', label: 'Tag', required: true } },
60
+ },
61
+ },
62
+ },
63
+ },
64
+ };
65
+ function renderList() {
66
+ return render(_jsx(CollectionList, { collectionSlug: "posts", config: CONFIG, onNavigate: vi.fn() }));
67
+ }
68
+ beforeEach(() => {
69
+ cmsApi.mockClear();
70
+ fieldValues = {};
71
+ listDocs = [];
72
+ lastEndpoint = '';
73
+ });
74
+ describe('CollectionList facets', () => {
75
+ it('renders facet dropdowns for select, category, and tag-shaped fields', async () => {
76
+ fieldValues = {
77
+ category: [{ value: 'Releases', count: 4 }],
78
+ tags: [{ value: 'AI', count: 3 }],
79
+ };
80
+ renderList();
81
+ expect(screen.getByLabelText('Filter by Topic')).toBeTruthy();
82
+ await waitFor(() => {
83
+ expect(screen.getByLabelText('Filter by Category')).toBeTruthy();
84
+ expect(screen.getByLabelText('Filter by Tags')).toBeTruthy();
85
+ });
86
+ // Select facets list config option labels, not in-use values.
87
+ expect(screen.getByRole('option', { name: 'Tutorials' })).toBeTruthy();
88
+ expect(screen.getByRole('option', { name: 'Releases' })).toBeTruthy();
89
+ });
90
+ it('adds filter.<field> to the list endpoint when a facet is picked', async () => {
91
+ fieldValues = { tags: [{ value: 'AI', count: 3 }] };
92
+ renderList();
93
+ fireEvent.change(screen.getByLabelText('Filter by Topic'), { target: { value: 'news' } });
94
+ await waitFor(() => expect(lastEndpoint).toContain('filter.topic=news'));
95
+ await waitFor(() => expect(screen.getByLabelText('Filter by Tags')).toBeTruthy());
96
+ fireEvent.change(screen.getByLabelText('Filter by Tags'), { target: { value: 'AI' } });
97
+ await waitFor(() => {
98
+ expect(lastEndpoint).toContain('filter.topic=news');
99
+ expect(lastEndpoint).toContain('filter.tags=AI');
100
+ });
101
+ });
102
+ it('clears all facets via the clear button', async () => {
103
+ renderList();
104
+ fireEvent.change(screen.getByLabelText('Filter by Topic'), { target: { value: 'news' } });
105
+ await waitFor(() => expect(lastEndpoint).toContain('filter.topic=news'));
106
+ fireEvent.click(screen.getByRole('button', { name: 'Clear filters' }));
107
+ await waitFor(() => expect(lastEndpoint).not.toContain('filter.'));
108
+ });
109
+ it('shows taxonomy columns with select labels and tag lists', async () => {
110
+ listDocs = [
111
+ {
112
+ id: '1',
113
+ title: 'Hello',
114
+ status: 'PUBLISHED',
115
+ updatedAt: '2026-06-01T00:00:00Z',
116
+ data: { topic: 'news', category: 'Releases', tags: [{ tag: 'AI' }, 'CMS'] },
117
+ },
118
+ ];
119
+ renderList();
120
+ // Column headers for each facet field.
121
+ expect(screen.getByRole('columnheader', { name: 'Topic' })).toBeTruthy();
122
+ expect(screen.getByRole('columnheader', { name: 'Category' })).toBeTruthy();
123
+ expect(screen.getByRole('columnheader', { name: 'Tags' })).toBeTruthy();
124
+ // Select value maps to its config label; tags render row + string shapes.
125
+ const table = within(screen.getByRole('table'));
126
+ expect(table.getByText('News')).toBeTruthy();
127
+ expect(table.getByText('Releases')).toBeTruthy();
128
+ expect(table.getByText('AI, CMS')).toBeTruthy();
129
+ });
130
+ });
131
+ //# sourceMappingURL=collection-list-facets.render.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-list-facets.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/views/collection-list-facets.render.test.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CollectionList.d.ts","sourceRoot":"","sources":["../../src/views/CollectionList.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,GAAG,CAAA;IACX,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC;AAsDD,wBAAgB,cAAc,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,mBAAmB,2CA4VzF"}
1
+ {"version":3,"file":"CollectionList.d.ts","sourceRoot":"","sources":["../../src/views/CollectionList.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,GAAG,CAAA;IACX,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC;AAuHD,wBAAgB,cAAc,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,mBAAmB,2CA+bzF"}
@@ -5,15 +5,73 @@ import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
5
5
  import { toast } from 'sonner';
6
6
  import { cmsApi } from '../lib/api.js';
7
7
  import { useApiData } from '../lib/useApiData.js';
8
- function resolveLabel(config, slug) {
9
- const fallback = { singular: slug, plural: slug };
8
+ function resolveCollection(config, slug) {
10
9
  if (!config?.collections)
11
- return fallback;
10
+ return undefined;
12
11
  const list = Array.isArray(config.collections)
13
12
  ? config.collections
14
13
  : Object.values(config.collections);
15
- const match = list.find((c) => c.slug === slug);
16
- return match?.labels ?? fallback;
14
+ return list.find((c) => c.slug === slug);
15
+ }
16
+ function resolveLabel(config, slug) {
17
+ return resolveCollection(config, slug)?.labels ?? { singular: slug, plural: slug };
18
+ }
19
+ /** True for array fields shaped like tags: a single required text sub-field. */
20
+ function isTagShapedArray(field) {
21
+ if (field?.type !== 'array' || !field.fields)
22
+ return false;
23
+ const subs = Object.values(field.fields);
24
+ return subs.length === 1 && subs[0]?.type === 'text';
25
+ }
26
+ /**
27
+ * Facetable fields for the filter bar (taxonomy Phase B): structured selects
28
+ * use their config options; free-text `category` and tag-shaped arrays load
29
+ * the collection's in-use values. Capped so the bar stays scannable.
30
+ */
31
+ function facetFieldsFor(config, slug) {
32
+ const fields = resolveCollection(config, slug)?.fields;
33
+ if (!fields || typeof fields !== 'object')
34
+ return [];
35
+ const out = [];
36
+ for (const [name, field] of Object.entries(fields)) {
37
+ if (field?.hidden)
38
+ continue;
39
+ if (field?.type === 'select' && Array.isArray(field.options)) {
40
+ out.push({ name, label: field.label ?? name, source: 'options', options: field.options });
41
+ }
42
+ else if (field?.type === 'text' && name === 'category') {
43
+ out.push({ name, label: field.label ?? 'Category', source: 'values' });
44
+ }
45
+ else if (isTagShapedArray(field)) {
46
+ out.push({ name, label: field.label ?? name, source: 'values' });
47
+ }
48
+ if (out.length >= 3)
49
+ break;
50
+ }
51
+ return out;
52
+ }
53
+ /** Read a doc's display value for a facet column (select values map to labels). */
54
+ function facetCellValue(doc, facet) {
55
+ const raw = doc?.data?.[facet.name];
56
+ if (typeof raw === 'string') {
57
+ if (!raw)
58
+ return '';
59
+ const opt = facet.options?.find((o) => o.value === raw);
60
+ return opt?.label ?? raw;
61
+ }
62
+ if (Array.isArray(raw)) {
63
+ const tags = raw
64
+ .map((item) => typeof item === 'string'
65
+ ? item
66
+ : item && typeof item === 'object'
67
+ ? Object.values(item).find((v) => typeof v === 'string')
68
+ : undefined)
69
+ .filter((v) => Boolean(v));
70
+ if (tags.length === 0)
71
+ return '';
72
+ return tags.length > 3 ? `${tags.slice(0, 3).join(', ')} +${tags.length - 3}` : tags.join(', ');
73
+ }
74
+ return '';
17
75
  }
18
76
  function statusColor(status) {
19
77
  switch (status?.toUpperCase()) {
@@ -54,12 +112,16 @@ function formatDate(d) {
54
112
  }
55
113
  export function CollectionList({ collectionSlug, config, onNavigate }) {
56
114
  const labels = useMemo(() => resolveLabel(config, collectionSlug), [config, collectionSlug]);
115
+ const facets = useMemo(() => facetFieldsFor(config, collectionSlug), [config, collectionSlug]);
57
116
  const [page, setPage] = useState(1);
58
117
  const [search, setSearch] = useState('');
59
118
  const [debouncedSearch, setDebouncedSearch] = useState('');
60
119
  const [sort, setSort] = useState(null);
61
120
  const [order, setOrder] = useState('asc');
62
121
  const [selected, setSelected] = useState(new Set());
122
+ const [filters, setFilters] = useState({});
123
+ // In-use values per `values`-sourced facet, fetched once per collection.
124
+ const [facetValues, setFacetValues] = useState({});
63
125
  const searchTimer = useRef(undefined);
64
126
  useEffect(() => {
65
127
  clearTimeout(searchTimer.current);
@@ -75,8 +137,12 @@ export function CollectionList({ collectionSlug, config, onNavigate }) {
75
137
  url += `&search=${encodeURIComponent(debouncedSearch)}`;
76
138
  if (sort)
77
139
  url += `&sort=${sort}&order=${order}`;
140
+ for (const [field, value] of Object.entries(filters)) {
141
+ if (value)
142
+ url += `&filter.${encodeURIComponent(field)}=${encodeURIComponent(value)}`;
143
+ }
78
144
  return url;
79
- }, [collectionSlug, page, debouncedSearch, sort, order]);
145
+ }, [collectionSlug, page, debouncedSearch, sort, order, filters]);
80
146
  const { data, loading, error, refetch } = useApiData(endpoint);
81
147
  const docs = data?.docs ?? [];
82
148
  const total = data?.total ?? 0;
@@ -85,7 +151,39 @@ export function CollectionList({ collectionSlug, config, onNavigate }) {
85
151
  setPage(1);
86
152
  setSelected(new Set());
87
153
  setSearch('');
154
+ setFilters({});
155
+ setFacetValues({});
88
156
  }, [collectionSlug]);
157
+ // Load in-use values for facets that aren't config-defined selects.
158
+ useEffect(() => {
159
+ let cancelled = false;
160
+ for (const facet of facets) {
161
+ if (facet.source !== 'values')
162
+ continue;
163
+ cmsApi(`/collections/${collectionSlug}/field-values?field=${encodeURIComponent(facet.name)}`).then((res) => {
164
+ if (cancelled || !res.data?.values)
165
+ return;
166
+ setFacetValues((prev) => ({
167
+ ...prev,
168
+ [facet.name]: res.data.values.map((v) => v.value),
169
+ }));
170
+ });
171
+ }
172
+ return () => {
173
+ cancelled = true;
174
+ };
175
+ }, [collectionSlug, facets]);
176
+ const setFilter = useCallback((field, value) => {
177
+ setFilters((prev) => {
178
+ const next = { ...prev };
179
+ if (value)
180
+ next[field] = value;
181
+ else
182
+ delete next[field];
183
+ return next;
184
+ });
185
+ setPage(1);
186
+ }, []);
89
187
  const toggleSort = useCallback((field) => {
90
188
  setSort((prev) => {
91
189
  if (prev === field) {
@@ -124,10 +222,22 @@ export function CollectionList({ collectionSlug, config, onNavigate }) {
124
222
  if (loading && docs.length === 0) {
125
223
  return (_jsx("div", { className: "flex h-64 items-center justify-center p-4", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin", style: { color: 'var(--actuate-primary, #2563eb)' } }) }));
126
224
  }
127
- return (_jsxs("div", { className: "flex h-full flex-col p-3 pr-6 sm:p-4 sm:pr-8", children: [_jsxs("div", { className: "mb-4 flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold sm:text-2xl", style: { color: 'var(--actuate-text, #111827)' }, children: labels.plural }), _jsxs("p", { className: "text-sm", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: [total, " total"] })] }), _jsxs("button", { onClick: () => onNavigate(`/${collectionSlug}/new`), className: "flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white transition-colors", style: { background: 'var(--actuate-primary, #2563eb)' }, children: [_jsx(Plus, { className: "h-4 w-4" }), " New ", labels.singular] })] }), _jsxs("div", { className: "relative mb-4", children: [_jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2", style: { color: 'var(--actuate-text-muted, #9ca3af)' } }), _jsx("input", { type: "text", placeholder: `Search ${labels.plural.toLowerCase()}...`, value: search, onChange: (e) => setSearch(e.target.value), className: "w-full rounded-lg border py-2 pr-3 pl-9 text-sm focus:ring-2 focus:outline-none", style: {
128
- borderColor: 'var(--actuate-border, #d1d5db)',
129
- color: 'var(--actuate-text, #111827)',
130
- } })] }), selected.size > 0 && (_jsxs("div", { className: "mb-4 flex flex-wrap items-center justify-between gap-2 rounded-lg p-3", style: {
225
+ return (_jsxs("div", { className: "flex h-full flex-col p-3 pr-6 sm:p-4 sm:pr-8", children: [_jsxs("div", { className: "mb-4 flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold sm:text-2xl", style: { color: 'var(--actuate-text, #111827)' }, children: labels.plural }), _jsxs("p", { className: "text-sm", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: [total, " total"] })] }), _jsxs("button", { onClick: () => onNavigate(`/${collectionSlug}/new`), className: "flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-white transition-colors", style: { background: 'var(--actuate-primary, #2563eb)' }, children: [_jsx(Plus, { className: "h-4 w-4" }), " New ", labels.singular] })] }), _jsxs("div", { className: "mb-4 flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "relative min-w-[200px] flex-1", children: [_jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2", style: { color: 'var(--actuate-text-muted, #9ca3af)' } }), _jsx("input", { type: "text", placeholder: `Search ${labels.plural.toLowerCase()}...`, value: search, onChange: (e) => setSearch(e.target.value), className: "w-full rounded-lg border py-2 pr-3 pl-9 text-sm focus:ring-2 focus:outline-none", style: {
226
+ borderColor: 'var(--actuate-border, #d1d5db)',
227
+ color: 'var(--actuate-text, #111827)',
228
+ } })] }), facets.map((facet) => {
229
+ const values = facet.source === 'options'
230
+ ? (facet.options ?? [])
231
+ : (facetValues[facet.name] ?? []).map((v) => ({ label: v, value: v }));
232
+ if (values.length === 0)
233
+ return null;
234
+ return (_jsxs("select", { "aria-label": `Filter by ${facet.label}`, value: filters[facet.name] ?? '', onChange: (e) => setFilter(facet.name, e.target.value), className: "rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none", style: {
235
+ borderColor: 'var(--actuate-border, #d1d5db)',
236
+ color: filters[facet.name]
237
+ ? 'var(--actuate-text, #111827)'
238
+ : 'var(--actuate-text-secondary, #6b7280)',
239
+ }, children: [_jsxs("option", { value: "", children: ["All ", facet.label.toLowerCase()] }), values.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }, facet.name));
240
+ }), Object.keys(filters).length > 0 && (_jsx("button", { type: "button", onClick: () => setFilters({}), className: "rounded-lg px-2 py-2 text-sm underline transition-opacity hover:opacity-75", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: "Clear filters" }))] }), selected.size > 0 && (_jsxs("div", { className: "mb-4 flex flex-wrap items-center justify-between gap-2 rounded-lg p-3", style: {
131
241
  background: 'var(--actuate-info-bg, #eff6ff)',
132
242
  borderColor: 'var(--actuate-info-border, #bfdbfe)',
133
243
  borderWidth: 1,
@@ -141,6 +251,6 @@ export function CollectionList({ collectionSlug, config, onNavigate }) {
141
251
  }, children: [_jsx(FileText, { className: "mb-3 h-10 w-10 opacity-40" }), _jsxs("p", { className: "mb-3 text-sm", children: ["No ", labels.plural.toLowerCase(), " found"] }), _jsxs("button", { onClick: () => onNavigate(`/${collectionSlug}/new`), className: "rounded-lg px-4 py-2 text-sm text-white", style: { background: 'var(--actuate-primary, #2563eb)' }, children: ["Create ", labels.singular] })] })) : (_jsx("div", { className: "flex-1 overflow-auto rounded-lg border", style: { borderColor: 'var(--actuate-border, #d1d5db)' }, children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "sticky top-0", style: {
142
252
  background: 'var(--actuate-surface-alt, #f9fafb)',
143
253
  borderBottom: '1px solid var(--actuate-border, #d1d5db)',
144
- }, children: _jsxs("tr", { children: [_jsx("th", { className: "w-10 px-3 py-2 text-left", children: _jsx("input", { type: "checkbox", checked: selected.size === docs.length && docs.length > 0, onChange: (e) => toggleAll(e.target.checked), className: "rounded" }) }), _jsx("th", { className: "px-3 py-2 text-left", children: _jsx(SortCol, { field: "title", children: "Title" }) }), _jsx("th", { className: "px-3 py-2 text-left", children: _jsx(SortCol, { field: "status", children: "Status" }) }), _jsx("th", { className: "px-3 py-2 text-left", children: _jsx(SortCol, { field: "updatedAt", children: "Updated" }) }), _jsx("th", { className: "px-3 py-2 text-left text-xs font-medium", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: "Actions" })] }) }), _jsx("tbody", { children: docs.map((doc) => (_jsxs("tr", { className: "transition-colors hover:opacity-95", style: { borderBottom: '1px solid var(--actuate-border, #e5e7eb)' }, children: [_jsx("td", { className: "px-3 py-2", children: _jsx("input", { type: "checkbox", checked: selected.has(String(doc.id)), onChange: () => toggleSelect(String(doc.id)), className: "rounded" }) }), _jsx("td", { className: "px-3 py-2", children: _jsx("button", { type: "button", onClick: () => onNavigate(`/${collectionSlug}/${doc.id}`), className: "text-left font-medium hover:underline", style: { color: 'var(--actuate-text, #111827)' }, children: doc.title || doc.name || `#${doc.id}` }) }), _jsx("td", { className: "px-3 py-2", children: _jsx("span", { className: "inline-block rounded-full px-2 py-0.5 text-xs font-medium", style: { background: statusColor(doc.status), color: statusText(doc.status) }, children: doc.status ?? 'DRAFT' }) }), _jsx("td", { className: "px-3 py-2", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: formatDate(doc.updatedAt) }), _jsx("td", { className: "px-3 py-2", children: _jsx("button", { type: "button", onClick: () => onNavigate(`/${collectionSlug}/${doc.id}`), className: "rounded p-1.5 transition-opacity hover:opacity-75", title: "Edit", children: _jsx(MoreHorizontal, { className: "h-4 w-4", style: { color: 'var(--actuate-text-secondary, #6b7280)' } }) }) })] }, doc.id))) })] }) })), totalPages > 1 && (_jsxs("div", { className: "mt-4 flex items-center justify-between text-sm", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: [_jsxs("span", { children: ["Page ", page, " of ", totalPages] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs("button", { disabled: page <= 1, onClick: () => setPage((p) => p - 1), className: "flex items-center gap-1 rounded-lg border px-3 py-1.5 transition-opacity disabled:opacity-40", style: { borderColor: 'var(--actuate-border, #d1d5db)' }, children: [_jsx(ChevronLeft, { className: "h-4 w-4" }), " Previous"] }), _jsxs("button", { disabled: page >= totalPages, onClick: () => setPage((p) => p + 1), className: "flex items-center gap-1 rounded-lg border px-3 py-1.5 transition-opacity disabled:opacity-40", style: { borderColor: 'var(--actuate-border, #d1d5db)' }, children: ["Next ", _jsx(ChevronRight, { className: "h-4 w-4" })] })] })] }))] }));
254
+ }, children: _jsxs("tr", { children: [_jsx("th", { className: "w-10 px-3 py-2 text-left", children: _jsx("input", { type: "checkbox", checked: selected.size === docs.length && docs.length > 0, onChange: (e) => toggleAll(e.target.checked), className: "rounded" }) }), _jsx("th", { className: "px-3 py-2 text-left", children: _jsx(SortCol, { field: "title", children: "Title" }) }), facets.map((facet) => (_jsx("th", { className: "px-3 py-2 text-left text-xs font-medium", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: facet.label }, facet.name))), _jsx("th", { className: "px-3 py-2 text-left", children: _jsx(SortCol, { field: "status", children: "Status" }) }), _jsx("th", { className: "px-3 py-2 text-left", children: _jsx(SortCol, { field: "updatedAt", children: "Updated" }) }), _jsx("th", { className: "px-3 py-2 text-left text-xs font-medium", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: "Actions" })] }) }), _jsx("tbody", { children: docs.map((doc) => (_jsxs("tr", { className: "transition-colors hover:opacity-95", style: { borderBottom: '1px solid var(--actuate-border, #e5e7eb)' }, children: [_jsx("td", { className: "px-3 py-2", children: _jsx("input", { type: "checkbox", checked: selected.has(String(doc.id)), onChange: () => toggleSelect(String(doc.id)), className: "rounded" }) }), _jsx("td", { className: "px-3 py-2", children: _jsx("button", { type: "button", onClick: () => onNavigate(`/${collectionSlug}/${doc.id}`), className: "text-left font-medium hover:underline", style: { color: 'var(--actuate-text, #111827)' }, children: doc.title || doc.name || `#${doc.id}` }) }), facets.map((facet) => (_jsx("td", { className: "max-w-[200px] truncate px-3 py-2", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, title: facetCellValue(doc, facet) || undefined, children: facetCellValue(doc, facet) || '—' }, facet.name))), _jsx("td", { className: "px-3 py-2", children: _jsx("span", { className: "inline-block rounded-full px-2 py-0.5 text-xs font-medium", style: { background: statusColor(doc.status), color: statusText(doc.status) }, children: doc.status ?? 'DRAFT' }) }), _jsx("td", { className: "px-3 py-2", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: formatDate(doc.updatedAt) }), _jsx("td", { className: "px-3 py-2", children: _jsx("button", { type: "button", onClick: () => onNavigate(`/${collectionSlug}/${doc.id}`), className: "rounded p-1.5 transition-opacity hover:opacity-75", title: "Edit", children: _jsx(MoreHorizontal, { className: "h-4 w-4", style: { color: 'var(--actuate-text-secondary, #6b7280)' } }) }) })] }, doc.id))) })] }) })), totalPages > 1 && (_jsxs("div", { className: "mt-4 flex items-center justify-between text-sm", style: { color: 'var(--actuate-text-secondary, #6b7280)' }, children: [_jsxs("span", { children: ["Page ", page, " of ", totalPages] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs("button", { disabled: page <= 1, onClick: () => setPage((p) => p - 1), className: "flex items-center gap-1 rounded-lg border px-3 py-1.5 transition-opacity disabled:opacity-40", style: { borderColor: 'var(--actuate-border, #d1d5db)' }, children: [_jsx(ChevronLeft, { className: "h-4 w-4" }), " Previous"] }), _jsxs("button", { disabled: page >= totalPages, onClick: () => setPage((p) => p + 1), className: "flex items-center gap-1 rounded-lg border px-3 py-1.5 transition-opacity disabled:opacity-40", style: { borderColor: 'var(--actuate-border, #d1d5db)' }, children: ["Next ", _jsx(ChevronRight, { className: "h-4 w-4" })] })] })] }))] }));
145
255
  }
146
256
  //# sourceMappingURL=CollectionList.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CollectionList.js","sourceRoot":"","sources":["../../src/views/CollectionList.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CollectionList.js","sourceRoot":"","sources":["../../src/views/CollectionList.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actuate-media/cms-admin",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/actuate-media/actuatecms.git",
@@ -89,7 +89,7 @@
89
89
  "tailwindcss": "^4.0.0",
90
90
  "typescript": "^5.7.0",
91
91
  "vitest": "^4.1.0",
92
- "@actuate-media/cms-core": "0.63.0",
92
+ "@actuate-media/cms-core": "0.64.0",
93
93
  "@actuate-media/component-blocks": "0.2.1"
94
94
  },
95
95
  "scripts": {
@@ -0,0 +1,148 @@
1
+ // @vitest-environment happy-dom
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
3
+ import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
4
+
5
+ /**
6
+ * CollectionList facet filters + taxonomy columns (taxonomy Phase B).
7
+ * Pins: facet dropdowns appear for select fields (config options), free-text
8
+ * `category`, and tag-shaped arrays (in-use values via field-values API);
9
+ * picking one adds `filter.<field>=` to the list endpoint; and taxonomy
10
+ * columns surface category/tags values in the table.
11
+ */
12
+
13
+ let fieldValues: Record<string, Array<{ value: string; count: number }>> = {}
14
+
15
+ const cmsApi = vi.fn(async (endpoint: string, _options?: unknown) => {
16
+ const match = endpoint.match(/field-values\?field=([^&]+)/)
17
+ if (match) {
18
+ const field = decodeURIComponent(match[1] ?? '')
19
+ return { data: { field, values: fieldValues[field] ?? [] }, status: 200 }
20
+ }
21
+ return { data: {}, status: 200 }
22
+ })
23
+
24
+ vi.mock('../../lib/api.js', () => ({
25
+ cmsApi: (e: string, o?: unknown) => cmsApi(e, o as never),
26
+ ensureCsrfToken: vi.fn(async () => {}),
27
+ }))
28
+
29
+ let lastEndpoint = ''
30
+ let listDocs: any[] = []
31
+
32
+ vi.mock('../../lib/useApiData.js', () => ({
33
+ useApiData: (endpoint: string) => {
34
+ lastEndpoint = endpoint
35
+ return {
36
+ data: { docs: listDocs, total: listDocs.length },
37
+ loading: false,
38
+ error: null,
39
+ refetch: vi.fn(),
40
+ }
41
+ },
42
+ }))
43
+
44
+ const { CollectionList } = await import('../../views/CollectionList.js')
45
+
46
+ const CONFIG = {
47
+ collections: {
48
+ posts: {
49
+ slug: 'posts',
50
+ labels: { singular: 'Post', plural: 'Posts' },
51
+ fields: {
52
+ title: { type: 'text', label: 'Title' },
53
+ topic: {
54
+ type: 'select',
55
+ label: 'Topic',
56
+ options: [
57
+ { label: 'News', value: 'news' },
58
+ { label: 'Tutorials', value: 'tutorials' },
59
+ ],
60
+ },
61
+ category: { type: 'text', label: 'Category', maxLength: 80 },
62
+ body: { type: 'richText', label: 'Body' },
63
+ tags: {
64
+ type: 'array',
65
+ label: 'Tags',
66
+ fields: { tag: { type: 'text', label: 'Tag', required: true } },
67
+ },
68
+ },
69
+ },
70
+ },
71
+ }
72
+
73
+ function renderList() {
74
+ return render(<CollectionList collectionSlug="posts" config={CONFIG} onNavigate={vi.fn()} />)
75
+ }
76
+
77
+ beforeEach(() => {
78
+ cmsApi.mockClear()
79
+ fieldValues = {}
80
+ listDocs = []
81
+ lastEndpoint = ''
82
+ })
83
+
84
+ describe('CollectionList facets', () => {
85
+ it('renders facet dropdowns for select, category, and tag-shaped fields', async () => {
86
+ fieldValues = {
87
+ category: [{ value: 'Releases', count: 4 }],
88
+ tags: [{ value: 'AI', count: 3 }],
89
+ }
90
+ renderList()
91
+
92
+ expect(screen.getByLabelText('Filter by Topic')).toBeTruthy()
93
+ await waitFor(() => {
94
+ expect(screen.getByLabelText('Filter by Category')).toBeTruthy()
95
+ expect(screen.getByLabelText('Filter by Tags')).toBeTruthy()
96
+ })
97
+ // Select facets list config option labels, not in-use values.
98
+ expect(screen.getByRole('option', { name: 'Tutorials' })).toBeTruthy()
99
+ expect(screen.getByRole('option', { name: 'Releases' })).toBeTruthy()
100
+ })
101
+
102
+ it('adds filter.<field> to the list endpoint when a facet is picked', async () => {
103
+ fieldValues = { tags: [{ value: 'AI', count: 3 }] }
104
+ renderList()
105
+
106
+ fireEvent.change(screen.getByLabelText('Filter by Topic'), { target: { value: 'news' } })
107
+ await waitFor(() => expect(lastEndpoint).toContain('filter.topic=news'))
108
+
109
+ await waitFor(() => expect(screen.getByLabelText('Filter by Tags')).toBeTruthy())
110
+ fireEvent.change(screen.getByLabelText('Filter by Tags'), { target: { value: 'AI' } })
111
+ await waitFor(() => {
112
+ expect(lastEndpoint).toContain('filter.topic=news')
113
+ expect(lastEndpoint).toContain('filter.tags=AI')
114
+ })
115
+ })
116
+
117
+ it('clears all facets via the clear button', async () => {
118
+ renderList()
119
+ fireEvent.change(screen.getByLabelText('Filter by Topic'), { target: { value: 'news' } })
120
+ await waitFor(() => expect(lastEndpoint).toContain('filter.topic=news'))
121
+
122
+ fireEvent.click(screen.getByRole('button', { name: 'Clear filters' }))
123
+ await waitFor(() => expect(lastEndpoint).not.toContain('filter.'))
124
+ })
125
+
126
+ it('shows taxonomy columns with select labels and tag lists', async () => {
127
+ listDocs = [
128
+ {
129
+ id: '1',
130
+ title: 'Hello',
131
+ status: 'PUBLISHED',
132
+ updatedAt: '2026-06-01T00:00:00Z',
133
+ data: { topic: 'news', category: 'Releases', tags: [{ tag: 'AI' }, 'CMS'] },
134
+ },
135
+ ]
136
+ renderList()
137
+
138
+ // Column headers for each facet field.
139
+ expect(screen.getByRole('columnheader', { name: 'Topic' })).toBeTruthy()
140
+ expect(screen.getByRole('columnheader', { name: 'Category' })).toBeTruthy()
141
+ expect(screen.getByRole('columnheader', { name: 'Tags' })).toBeTruthy()
142
+ // Select value maps to its config label; tags render row + string shapes.
143
+ const table = within(screen.getByRole('table'))
144
+ expect(table.getByText('News')).toBeTruthy()
145
+ expect(table.getByText('Releases')).toBeTruthy()
146
+ expect(table.getByText('AI, CMS')).toBeTruthy()
147
+ })
148
+ })
@@ -24,14 +24,79 @@ export interface CollectionListProps {
24
24
  type SortField = 'title' | 'status' | 'updatedAt'
25
25
  type SortOrder = 'asc' | 'desc'
26
26
 
27
- function resolveLabel(config: any, slug: string): { singular: string; plural: string } {
28
- const fallback = { singular: slug, plural: slug }
29
- if (!config?.collections) return fallback
27
+ function resolveCollection(config: any, slug: string): any | undefined {
28
+ if (!config?.collections) return undefined
30
29
  const list = Array.isArray(config.collections)
31
30
  ? config.collections
32
31
  : Object.values(config.collections)
33
- const match = (list as any[]).find((c: any) => c.slug === slug)
34
- return match?.labels ?? fallback
32
+ return (list as any[]).find((c: any) => c.slug === slug)
33
+ }
34
+
35
+ function resolveLabel(config: any, slug: string): { singular: string; plural: string } {
36
+ return resolveCollection(config, slug)?.labels ?? { singular: slug, plural: slug }
37
+ }
38
+
39
+ /** One facet dropdown in the filter bar. */
40
+ interface FacetDef {
41
+ name: string
42
+ label: string
43
+ /** `options` = fixed list from the config; `values` = in-use values from the field-values API. */
44
+ source: 'options' | 'values'
45
+ options?: Array<{ label: string; value: string }>
46
+ }
47
+
48
+ /** True for array fields shaped like tags: a single required text sub-field. */
49
+ function isTagShapedArray(field: any): boolean {
50
+ if (field?.type !== 'array' || !field.fields) return false
51
+ const subs = Object.values(field.fields) as any[]
52
+ return subs.length === 1 && subs[0]?.type === 'text'
53
+ }
54
+
55
+ /**
56
+ * Facetable fields for the filter bar (taxonomy Phase B): structured selects
57
+ * use their config options; free-text `category` and tag-shaped arrays load
58
+ * the collection's in-use values. Capped so the bar stays scannable.
59
+ */
60
+ function facetFieldsFor(config: any, slug: string): FacetDef[] {
61
+ const fields = resolveCollection(config, slug)?.fields
62
+ if (!fields || typeof fields !== 'object') return []
63
+ const out: FacetDef[] = []
64
+ for (const [name, field] of Object.entries(fields) as Array<[string, any]>) {
65
+ if (field?.hidden) continue
66
+ if (field?.type === 'select' && Array.isArray(field.options)) {
67
+ out.push({ name, label: field.label ?? name, source: 'options', options: field.options })
68
+ } else if (field?.type === 'text' && name === 'category') {
69
+ out.push({ name, label: field.label ?? 'Category', source: 'values' })
70
+ } else if (isTagShapedArray(field)) {
71
+ out.push({ name, label: field.label ?? name, source: 'values' })
72
+ }
73
+ if (out.length >= 3) break
74
+ }
75
+ return out
76
+ }
77
+
78
+ /** Read a doc's display value for a facet column (select values map to labels). */
79
+ function facetCellValue(doc: any, facet: FacetDef): string {
80
+ const raw = doc?.data?.[facet.name]
81
+ if (typeof raw === 'string') {
82
+ if (!raw) return ''
83
+ const opt = facet.options?.find((o) => o.value === raw)
84
+ return opt?.label ?? raw
85
+ }
86
+ if (Array.isArray(raw)) {
87
+ const tags = raw
88
+ .map((item) =>
89
+ typeof item === 'string'
90
+ ? item
91
+ : item && typeof item === 'object'
92
+ ? Object.values(item).find((v) => typeof v === 'string')
93
+ : undefined,
94
+ )
95
+ .filter((v): v is string => Boolean(v))
96
+ if (tags.length === 0) return ''
97
+ return tags.length > 3 ? `${tags.slice(0, 3).join(', ')} +${tags.length - 3}` : tags.join(', ')
98
+ }
99
+ return ''
35
100
  }
36
101
 
37
102
  function statusColor(status: string): string {
@@ -75,6 +140,7 @@ function formatDate(d: string | undefined): string {
75
140
 
76
141
  export function CollectionList({ collectionSlug, config, onNavigate }: CollectionListProps) {
77
142
  const labels = useMemo(() => resolveLabel(config, collectionSlug), [config, collectionSlug])
143
+ const facets = useMemo(() => facetFieldsFor(config, collectionSlug), [config, collectionSlug])
78
144
 
79
145
  const [page, setPage] = useState(1)
80
146
  const [search, setSearch] = useState('')
@@ -82,6 +148,9 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
82
148
  const [sort, setSort] = useState<SortField | null>(null)
83
149
  const [order, setOrder] = useState<SortOrder>('asc')
84
150
  const [selected, setSelected] = useState<Set<string>>(new Set())
151
+ const [filters, setFilters] = useState<Record<string, string>>({})
152
+ // In-use values per `values`-sourced facet, fetched once per collection.
153
+ const [facetValues, setFacetValues] = useState<Record<string, string[]>>({})
85
154
  const searchTimer = useRef<ReturnType<typeof setTimeout>>(undefined)
86
155
 
87
156
  useEffect(() => {
@@ -97,8 +166,11 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
97
166
  let url = `/collections/${collectionSlug}?page=${page}&pageSize=25`
98
167
  if (debouncedSearch) url += `&search=${encodeURIComponent(debouncedSearch)}`
99
168
  if (sort) url += `&sort=${sort}&order=${order}`
169
+ for (const [field, value] of Object.entries(filters)) {
170
+ if (value) url += `&filter.${encodeURIComponent(field)}=${encodeURIComponent(value)}`
171
+ }
100
172
  return url
101
- }, [collectionSlug, page, debouncedSearch, sort, order])
173
+ }, [collectionSlug, page, debouncedSearch, sort, order, filters])
102
174
 
103
175
  const { data, loading, error, refetch } = useApiData<{ docs: any[]; total: number }>(endpoint)
104
176
  const docs = data?.docs ?? []
@@ -109,8 +181,40 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
109
181
  setPage(1)
110
182
  setSelected(new Set())
111
183
  setSearch('')
184
+ setFilters({})
185
+ setFacetValues({})
112
186
  }, [collectionSlug])
113
187
 
188
+ // Load in-use values for facets that aren't config-defined selects.
189
+ useEffect(() => {
190
+ let cancelled = false
191
+ for (const facet of facets) {
192
+ if (facet.source !== 'values') continue
193
+ cmsApi<{ field: string; values: Array<{ value: string; count: number }> }>(
194
+ `/collections/${collectionSlug}/field-values?field=${encodeURIComponent(facet.name)}`,
195
+ ).then((res) => {
196
+ if (cancelled || !res.data?.values) return
197
+ setFacetValues((prev) => ({
198
+ ...prev,
199
+ [facet.name]: res.data!.values.map((v) => v.value),
200
+ }))
201
+ })
202
+ }
203
+ return () => {
204
+ cancelled = true
205
+ }
206
+ }, [collectionSlug, facets])
207
+
208
+ const setFilter = useCallback((field: string, value: string) => {
209
+ setFilters((prev) => {
210
+ const next = { ...prev }
211
+ if (value) next[field] = value
212
+ else delete next[field]
213
+ return next
214
+ })
215
+ setPage(1)
216
+ }, [])
217
+
114
218
  const toggleSort = useCallback((field: SortField) => {
115
219
  setSort((prev) => {
116
220
  if (prev === field) {
@@ -198,23 +302,64 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
198
302
  </button>
199
303
  </div>
200
304
 
201
- {/* Search */}
202
- <div className="relative mb-4">
203
- <Search
204
- className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2"
205
- style={{ color: 'var(--actuate-text-muted, #9ca3af)' }}
206
- />
207
- <input
208
- type="text"
209
- placeholder={`Search ${labels.plural.toLowerCase()}...`}
210
- value={search}
211
- onChange={(e) => setSearch(e.target.value)}
212
- className="w-full rounded-lg border py-2 pr-3 pl-9 text-sm focus:ring-2 focus:outline-none"
213
- style={{
214
- borderColor: 'var(--actuate-border, #d1d5db)',
215
- color: 'var(--actuate-text, #111827)',
216
- }}
217
- />
305
+ {/* Search + facet filters */}
306
+ <div className="mb-4 flex flex-wrap items-center gap-2">
307
+ <div className="relative min-w-[200px] flex-1">
308
+ <Search
309
+ className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2"
310
+ style={{ color: 'var(--actuate-text-muted, #9ca3af)' }}
311
+ />
312
+ <input
313
+ type="text"
314
+ placeholder={`Search ${labels.plural.toLowerCase()}...`}
315
+ value={search}
316
+ onChange={(e) => setSearch(e.target.value)}
317
+ className="w-full rounded-lg border py-2 pr-3 pl-9 text-sm focus:ring-2 focus:outline-none"
318
+ style={{
319
+ borderColor: 'var(--actuate-border, #d1d5db)',
320
+ color: 'var(--actuate-text, #111827)',
321
+ }}
322
+ />
323
+ </div>
324
+ {facets.map((facet) => {
325
+ const values =
326
+ facet.source === 'options'
327
+ ? (facet.options ?? [])
328
+ : (facetValues[facet.name] ?? []).map((v) => ({ label: v, value: v }))
329
+ if (values.length === 0) return null
330
+ return (
331
+ <select
332
+ key={facet.name}
333
+ aria-label={`Filter by ${facet.label}`}
334
+ value={filters[facet.name] ?? ''}
335
+ onChange={(e) => setFilter(facet.name, e.target.value)}
336
+ className="rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
337
+ style={{
338
+ borderColor: 'var(--actuate-border, #d1d5db)',
339
+ color: filters[facet.name]
340
+ ? 'var(--actuate-text, #111827)'
341
+ : 'var(--actuate-text-secondary, #6b7280)',
342
+ }}
343
+ >
344
+ <option value="">All {facet.label.toLowerCase()}</option>
345
+ {values.map((opt) => (
346
+ <option key={opt.value} value={opt.value}>
347
+ {opt.label}
348
+ </option>
349
+ ))}
350
+ </select>
351
+ )
352
+ })}
353
+ {Object.keys(filters).length > 0 && (
354
+ <button
355
+ type="button"
356
+ onClick={() => setFilters({})}
357
+ className="rounded-lg px-2 py-2 text-sm underline transition-opacity hover:opacity-75"
358
+ style={{ color: 'var(--actuate-text-secondary, #6b7280)' }}
359
+ >
360
+ Clear filters
361
+ </button>
362
+ )}
218
363
  </div>
219
364
 
220
365
  {/* Bulk actions */}
@@ -317,6 +462,15 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
317
462
  <th className="px-3 py-2 text-left">
318
463
  <SortCol field="title">Title</SortCol>
319
464
  </th>
465
+ {facets.map((facet) => (
466
+ <th
467
+ key={facet.name}
468
+ className="px-3 py-2 text-left text-xs font-medium"
469
+ style={{ color: 'var(--actuate-text-secondary, #6b7280)' }}
470
+ >
471
+ {facet.label}
472
+ </th>
473
+ ))}
320
474
  <th className="px-3 py-2 text-left">
321
475
  <SortCol field="status">Status</SortCol>
322
476
  </th>
@@ -356,6 +510,16 @@ export function CollectionList({ collectionSlug, config, onNavigate }: Collectio
356
510
  {doc.title || doc.name || `#${doc.id}`}
357
511
  </button>
358
512
  </td>
513
+ {facets.map((facet) => (
514
+ <td
515
+ key={facet.name}
516
+ className="max-w-[200px] truncate px-3 py-2"
517
+ style={{ color: 'var(--actuate-text-secondary, #6b7280)' }}
518
+ title={facetCellValue(doc, facet) || undefined}
519
+ >
520
+ {facetCellValue(doc, facet) || '—'}
521
+ </td>
522
+ ))}
359
523
  <td className="px-3 py-2">
360
524
  <span
361
525
  className="inline-block rounded-full px-2 py-0.5 text-xs font-medium"