@arqel-dev/ui 0.16.0 → 0.18.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.
Files changed (65) hide show
  1. package/dist/ArqelEditPage-7OFWPULE.js +12 -0
  2. package/dist/{ArqelEditPage-RRYFYOTC.js.map → ArqelEditPage-7OFWPULE.js.map} +1 -1
  3. package/dist/ArqelIndexPage-ZAVCCM6V.js +14 -0
  4. package/dist/{ArqelIndexPage-J2EUV2PI.js.map → ArqelIndexPage-ZAVCCM6V.js.map} +1 -1
  5. package/dist/ArqelNotificationsPage-MBJBYFHW.js +6 -0
  6. package/dist/ArqelNotificationsPage-MBJBYFHW.js.map +1 -0
  7. package/dist/{UserMenu-B5XbC_Gy.d.ts → UserMenu-QlwvvZRU.d.ts} +2 -1
  8. package/dist/action.js +2 -1
  9. package/dist/{chunk-SK6H7ARE.js → chunk-3E66JNQO.js} +3 -18
  10. package/dist/chunk-3E66JNQO.js.map +1 -0
  11. package/dist/chunk-53J74IYR.js +22 -0
  12. package/dist/chunk-53J74IYR.js.map +1 -0
  13. package/dist/chunk-6YGRTXEL.js +87 -0
  14. package/dist/chunk-6YGRTXEL.js.map +1 -0
  15. package/dist/{chunk-SLLTFOOS.js → chunk-7NGUPQON.js} +5 -192
  16. package/dist/chunk-7NGUPQON.js.map +1 -0
  17. package/dist/chunk-CH7YLELO.js +137 -0
  18. package/dist/chunk-CH7YLELO.js.map +1 -0
  19. package/dist/{chunk-K7QY56XN.js → chunk-D2MUCHFN.js} +5 -119
  20. package/dist/chunk-D2MUCHFN.js.map +1 -0
  21. package/dist/chunk-DO3PRGVV.js +198 -0
  22. package/dist/chunk-DO3PRGVV.js.map +1 -0
  23. package/dist/{chunk-X4ZFKOPX.js → chunk-DOQUHQND.js} +4 -4
  24. package/dist/{chunk-X4ZFKOPX.js.map → chunk-DOQUHQND.js.map} +1 -1
  25. package/dist/{chunk-X2X6YDOX.js → chunk-EXA6DWLB.js} +11 -3
  26. package/dist/chunk-EXA6DWLB.js.map +1 -0
  27. package/dist/{chunk-S5UNPCMQ.js → chunk-JKD6SE75.js} +3 -269
  28. package/dist/chunk-JKD6SE75.js.map +1 -0
  29. package/dist/{chunk-EG2DASKR.js → chunk-KGAY2AZH.js} +4 -3
  30. package/dist/chunk-KGAY2AZH.js.map +1 -0
  31. package/dist/chunk-U4MPY2XT.js +91 -0
  32. package/dist/chunk-U4MPY2XT.js.map +1 -0
  33. package/dist/chunk-WKIYS2FQ.js +509 -0
  34. package/dist/chunk-WKIYS2FQ.js.map +1 -0
  35. package/dist/chunk-XK2P6CSK.js +8 -0
  36. package/dist/chunk-XK2P6CSK.js.map +1 -0
  37. package/dist/chunk-YEPQWAD6.js +275 -0
  38. package/dist/chunk-YEPQWAD6.js.map +1 -0
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +11 -7
  41. package/dist/pages.d.ts +26 -1
  42. package/dist/pages.js +14 -8
  43. package/dist/pages.js.map +1 -1
  44. package/dist/primitives.js +3 -2
  45. package/dist/resource.js +3 -2
  46. package/dist/shell.d.ts +37 -3
  47. package/dist/shell.js +166 -7
  48. package/dist/shell.js.map +1 -1
  49. package/dist/table.js +2 -1
  50. package/dist/utility.js +2 -1
  51. package/dist/utils.d.ts +16 -1
  52. package/dist/utils.js +1 -0
  53. package/package.json +6 -6
  54. package/dist/ArqelEditPage-RRYFYOTC.js +0 -8
  55. package/dist/ArqelIndexPage-J2EUV2PI.js +0 -12
  56. package/dist/chunk-EG2DASKR.js.map +0 -1
  57. package/dist/chunk-K7QY56XN.js.map +0 -1
  58. package/dist/chunk-R4BOUU6Y.js +0 -55
  59. package/dist/chunk-R4BOUU6Y.js.map +0 -1
  60. package/dist/chunk-S5UNPCMQ.js.map +0 -1
  61. package/dist/chunk-SK6H7ARE.js.map +0 -1
  62. package/dist/chunk-SLLTFOOS.js.map +0 -1
  63. package/dist/chunk-X2X6YDOX.js.map +0 -1
  64. package/dist/chunk-YQ6KXR3U.js +0 -55
  65. package/dist/chunk-YQ6KXR3U.js.map +0 -1
@@ -0,0 +1,275 @@
1
+ import { Button } from './chunk-H23VX3RR.js';
2
+ import { cn } from './chunk-B7ULUJVO.js';
3
+ import { useArqelTranslations, useArqelLocale } from '@arqel-dev/react/utils';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+ import { useMemo } from 'react';
6
+
7
+ function TableFilters({
8
+ filters,
9
+ values,
10
+ onChange,
11
+ onClearAll,
12
+ className
13
+ }) {
14
+ const t = useArqelTranslations();
15
+ if (filters.length === 0) return null;
16
+ const activeCount = filters.reduce((acc, f) => {
17
+ const v = values[f.name];
18
+ return v !== void 0 && v !== null && v !== "" ? acc + 1 : acc;
19
+ }, 0);
20
+ return /* @__PURE__ */ jsxs("fieldset", { className: cn("flex flex-wrap items-end gap-2 border-0 p-0", className), children: [
21
+ /* @__PURE__ */ jsx("legend", { className: "sr-only", children: t("table.filters.legend", "Filters") }),
22
+ filters.map((filter) => /* @__PURE__ */ jsx(
23
+ FilterControl,
24
+ {
25
+ filter,
26
+ value: values[filter.name],
27
+ onChange: (v) => onChange(filter.name, v)
28
+ },
29
+ filter.name
30
+ )),
31
+ activeCount > 0 && onClearAll && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onClearAll, children: t("table.filters.clear", `Clear filters (${activeCount})`, { count: activeCount }) })
32
+ ] });
33
+ }
34
+ function FilterControl({
35
+ filter,
36
+ value,
37
+ onChange
38
+ }) {
39
+ switch (filter.type) {
40
+ case "select":
41
+ return /* @__PURE__ */ jsx(SelectFilter, { filter, value, onChange });
42
+ case "multiSelect":
43
+ return /* @__PURE__ */ jsx(MultiSelectFilter, { filter, value, onChange });
44
+ case "text":
45
+ return /* @__PURE__ */ jsx(TextFilter, { filter, value, onChange });
46
+ case "ternary":
47
+ return /* @__PURE__ */ jsx(TernaryFilter, { filter, value, onChange });
48
+ case "dateRange":
49
+ case "scope":
50
+ return null;
51
+ }
52
+ }
53
+ function controlClasses() {
54
+ return cn(
55
+ // 44px touch target on mobile (WCAG 2.5.5); dense 36px on >=md.
56
+ "h-11 rounded-sm border border-[var(--input)] md:h-9",
57
+ "bg-background px-3 text-sm",
58
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
59
+ );
60
+ }
61
+ function SelectFilter({
62
+ filter,
63
+ value,
64
+ onChange
65
+ }) {
66
+ const t = useArqelTranslations();
67
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
68
+ filter.label ?? filter.name,
69
+ /* @__PURE__ */ jsxs(
70
+ "select",
71
+ {
72
+ className: controlClasses(),
73
+ value: value === void 0 || value === null ? "" : String(value),
74
+ onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
75
+ children: [
76
+ /* @__PURE__ */ jsx("option", { value: "", children: t("table.filters.all", "All") }),
77
+ filter.props.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
78
+ ]
79
+ }
80
+ )
81
+ ] });
82
+ }
83
+ function MultiSelectFilter({
84
+ filter,
85
+ value,
86
+ onChange
87
+ }) {
88
+ const arr = Array.isArray(value) ? value : [];
89
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
90
+ filter.label ?? filter.name,
91
+ /* @__PURE__ */ jsx(
92
+ "select",
93
+ {
94
+ multiple: true,
95
+ className: cn(controlClasses(), "h-auto min-h-11 py-1 md:min-h-[2.25rem]"),
96
+ value: arr.map(String),
97
+ onChange: (e) => {
98
+ const next = Array.from(e.target.selectedOptions, (o) => o.value);
99
+ onChange(next.length === 0 ? null : next);
100
+ },
101
+ children: filter.props.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
102
+ }
103
+ )
104
+ ] });
105
+ }
106
+ function TextFilter({
107
+ filter,
108
+ value,
109
+ onChange
110
+ }) {
111
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
112
+ filter.label ?? filter.name,
113
+ /* @__PURE__ */ jsx(
114
+ "input",
115
+ {
116
+ type: "text",
117
+ className: controlClasses(),
118
+ value: typeof value === "string" ? value : "",
119
+ onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
120
+ }
121
+ )
122
+ ] });
123
+ }
124
+ function TernaryFilter({
125
+ filter,
126
+ value,
127
+ onChange
128
+ }) {
129
+ const t = useArqelTranslations();
130
+ const current = value === void 0 || value === null ? "all" : String(value);
131
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
132
+ filter.label ?? filter.name,
133
+ /* @__PURE__ */ jsxs(
134
+ "select",
135
+ {
136
+ className: controlClasses(),
137
+ value: current,
138
+ onChange: (e) => {
139
+ const v = e.target.value;
140
+ onChange(v === "all" ? null : v);
141
+ },
142
+ children: [
143
+ /* @__PURE__ */ jsx("option", { value: "all", children: filter.props.allLabel ?? t("table.filters.all", "All") }),
144
+ /* @__PURE__ */ jsx("option", { value: "true", children: filter.props.trueLabel ?? t("table.filters.yes", "Yes") }),
145
+ /* @__PURE__ */ jsx("option", { value: "false", children: filter.props.falseLabel ?? t("table.filters.no", "No") })
146
+ ]
147
+ }
148
+ )
149
+ ] });
150
+ }
151
+ function TablePagination({
152
+ meta,
153
+ onPageChange,
154
+ onPerPageChange,
155
+ perPageOptions = [10, 25, 50, 100],
156
+ className
157
+ }) {
158
+ const t = useArqelTranslations();
159
+ const locale = useArqelLocale();
160
+ const numberFormatter = useMemo(() => new Intl.NumberFormat(locale), [locale]);
161
+ const fromRaw = (meta.currentPage - 1) * meta.perPage + 1;
162
+ const toRaw = Math.min(meta.currentPage * meta.perPage, meta.total);
163
+ const from = numberFormatter.format(fromRaw);
164
+ const to = numberFormatter.format(toRaw);
165
+ const total = numberFormatter.format(meta.total);
166
+ const isFirst = meta.currentPage <= 1;
167
+ const isLast = meta.currentPage >= meta.lastPage;
168
+ return /* @__PURE__ */ jsxs(
169
+ "nav",
170
+ {
171
+ className: cn(
172
+ "flex flex-wrap items-center justify-between gap-2 border-t border-border px-3 py-2 text-sm",
173
+ className
174
+ ),
175
+ "aria-label": t("table.pagination.label", "Pagination"),
176
+ children: [
177
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: meta.total === 0 ? t("table.empty", "No records found.") : t(
178
+ "table.pagination.showing",
179
+ // Fallback is rendered verbatim when the key is missing, so it must
180
+ // already carry the interpolated values (the replacements map only
181
+ // applies to a resolved dictionary string).
182
+ `Showing ${from} to ${to} of ${total} results`,
183
+ { from, to, total }
184
+ ) }),
185
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
186
+ onPerPageChange && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
187
+ t("table.per_page", "Per page"),
188
+ /* @__PURE__ */ jsx(
189
+ "select",
190
+ {
191
+ className: "h-11 rounded-sm border border-[var(--input)] bg-background px-2 md:h-8",
192
+ value: meta.perPage,
193
+ onChange: (e) => onPerPageChange(Number(e.target.value)),
194
+ children: perPageOptions.map((n) => /* @__PURE__ */ jsx("option", { value: n, children: n }, n))
195
+ }
196
+ )
197
+ ] }),
198
+ /* @__PURE__ */ jsx(
199
+ Button,
200
+ {
201
+ variant: "outline",
202
+ size: "sm",
203
+ className: "h-11 md:h-8",
204
+ disabled: isFirst,
205
+ onClick: () => onPageChange(meta.currentPage - 1),
206
+ "aria-label": t("table.pagination.previous_page", "Previous page"),
207
+ children: t("table.pagination.previous", "Prev")
208
+ }
209
+ ),
210
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
211
+ numberFormatter.format(meta.currentPage),
212
+ " / ",
213
+ numberFormatter.format(meta.lastPage)
214
+ ] }),
215
+ /* @__PURE__ */ jsx(
216
+ Button,
217
+ {
218
+ variant: "outline",
219
+ size: "sm",
220
+ className: "h-11 md:h-8",
221
+ disabled: isLast,
222
+ onClick: () => onPageChange(meta.currentPage + 1),
223
+ "aria-label": t("table.pagination.next_page", "Next page"),
224
+ children: t("table.pagination.next", "Next")
225
+ }
226
+ )
227
+ ] })
228
+ ]
229
+ }
230
+ );
231
+ }
232
+ function TableToolbar({
233
+ search,
234
+ filters,
235
+ selectedCount = 0,
236
+ bulkActions,
237
+ onClearSelection,
238
+ className
239
+ }) {
240
+ const t = useArqelTranslations();
241
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-3 px-3 py-3", className), children: [
242
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [
243
+ search,
244
+ filters
245
+ ] }),
246
+ selectedCount > 0 && /* @__PURE__ */ jsxs(
247
+ "section",
248
+ {
249
+ className: "flex flex-wrap items-center gap-2 rounded-sm border border-border bg-muted px-3 py-2",
250
+ "aria-label": t("table.bulk.label", "Bulk actions"),
251
+ children: [
252
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: t("table.bulk.selected", "{one} :count selected|{other} :count selected", {
253
+ count: selectedCount
254
+ }) }),
255
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [
256
+ bulkActions,
257
+ onClearSelection && /* @__PURE__ */ jsx(
258
+ "button",
259
+ {
260
+ type: "button",
261
+ className: "text-sm text-muted-foreground hover:underline",
262
+ onClick: onClearSelection,
263
+ children: t("table.bulk.clear", "Clear")
264
+ }
265
+ )
266
+ ] })
267
+ ]
268
+ }
269
+ )
270
+ ] });
271
+ }
272
+
273
+ export { TableFilters, TablePagination, TableToolbar };
274
+ //# sourceMappingURL=chunk-YEPQWAD6.js.map
275
+ //# sourceMappingURL=chunk-YEPQWAD6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/table/TableFilters.tsx","../src/table/TablePagination.tsx","../src/table/TableToolbar.tsx"],"names":["useArqelTranslations","jsxs","jsx"],"mappings":"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","file":"chunk-YEPQWAD6.js","sourcesContent":["/**\n * `<TableFilters>` — renders one input per `FilterSchema` and emits\n * a single `onChange(name, value)` callback. Active values render as\n * dismissible chips so users can read state without re-opening menus.\n *\n * Phase 1 covers the four most-used filter types (select, multiSelect,\n * text, ternary). dateRange / scope land alongside FieldRenderer in\n * UI-004, where the date primitives ship.\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type {\n FilterSchema,\n MultiSelectFilterSchema,\n SelectFilterSchema,\n TernaryFilterSchema,\n TextFilterSchema,\n} from '@arqel-dev/types/tables';\nimport { Button } from '../action/Button.js';\nimport { cn } from '../utils/cn.js';\n\nexport interface TableFiltersProps {\n filters: FilterSchema[];\n values: Record<string, unknown>;\n onChange: (name: string, value: unknown) => void;\n onClearAll?: (() => void) | undefined;\n className?: string | undefined;\n}\n\nexport function TableFilters({\n filters,\n values,\n onChange,\n onClearAll,\n className,\n}: TableFiltersProps) {\n const t = useArqelTranslations();\n if (filters.length === 0) return null;\n\n const activeCount = filters.reduce((acc, f) => {\n const v = values[f.name];\n return v !== undefined && v !== null && v !== '' ? acc + 1 : acc;\n }, 0);\n\n return (\n <fieldset className={cn('flex flex-wrap items-end gap-2 border-0 p-0', className)}>\n <legend className=\"sr-only\">{t('table.filters.legend', 'Filters')}</legend>\n {filters.map((filter) => (\n <FilterControl\n key={filter.name}\n filter={filter}\n value={values[filter.name]}\n onChange={(v) => onChange(filter.name, v)}\n />\n ))}\n {activeCount > 0 && onClearAll && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onClearAll}>\n {t('table.filters.clear', `Clear filters (${activeCount})`, { count: activeCount })}\n </Button>\n )}\n </fieldset>\n );\n}\n\nfunction FilterControl({\n filter,\n value,\n onChange,\n}: {\n filter: FilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n switch (filter.type) {\n case 'select':\n return <SelectFilter filter={filter} value={value} onChange={onChange} />;\n case 'multiSelect':\n return <MultiSelectFilter filter={filter} value={value} onChange={onChange} />;\n case 'text':\n return <TextFilter filter={filter} value={value} onChange={onChange} />;\n case 'ternary':\n return <TernaryFilter filter={filter} value={value} onChange={onChange} />;\n case 'dateRange':\n case 'scope':\n return null;\n }\n}\n\nfunction controlClasses() {\n return cn(\n // 44px touch target on mobile (WCAG 2.5.5); dense 36px on >=md.\n 'h-11 rounded-sm border border-[var(--input)] md:h-9',\n 'bg-background px-3 text-sm',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n );\n}\n\nfunction SelectFilter({\n filter,\n value,\n onChange,\n}: {\n filter: SelectFilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const t = useArqelTranslations();\n return (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n {filter.label ?? filter.name}\n <select\n className={controlClasses()}\n value={value === undefined || value === null ? '' : String(value)}\n onChange={(e) => onChange(e.target.value === '' ? null : e.target.value)}\n >\n <option value=\"\">{t('table.filters.all', 'All')}</option>\n {filter.props.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n </label>\n );\n}\n\nfunction MultiSelectFilter({\n filter,\n value,\n onChange,\n}: {\n filter: MultiSelectFilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const arr = Array.isArray(value) ? (value as Array<string | number>) : [];\n return (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n {filter.label ?? filter.name}\n <select\n multiple\n className={cn(controlClasses(), 'h-auto min-h-11 py-1 md:min-h-[2.25rem]')}\n value={arr.map(String)}\n onChange={(e) => {\n const next = Array.from(e.target.selectedOptions, (o) => o.value);\n onChange(next.length === 0 ? null : next);\n }}\n >\n {filter.props.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n </label>\n );\n}\n\nfunction TextFilter({\n filter,\n value,\n onChange,\n}: {\n filter: TextFilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n return (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n {filter.label ?? filter.name}\n <input\n type=\"text\"\n className={controlClasses()}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value === '' ? null : e.target.value)}\n />\n </label>\n );\n}\n\nfunction TernaryFilter({\n filter,\n value,\n onChange,\n}: {\n filter: TernaryFilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const t = useArqelTranslations();\n const current = value === undefined || value === null ? 'all' : String(value);\n return (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n {filter.label ?? filter.name}\n <select\n className={controlClasses()}\n value={current}\n onChange={(e) => {\n const v = e.target.value;\n onChange(v === 'all' ? null : v);\n }}\n >\n <option value=\"all\">{filter.props.allLabel ?? t('table.filters.all', 'All')}</option>\n <option value=\"true\">{filter.props.trueLabel ?? t('table.filters.yes', 'Yes')}</option>\n <option value=\"false\">{filter.props.falseLabel ?? t('table.filters.no', 'No')}</option>\n </select>\n </label>\n );\n}\n","/**\n * `<TablePagination>` — prev/next + range + per-page picker.\n *\n * Stateless: parent owns `currentPage` / `perPage` and handles transitions\n * (typically via Inertia `router.get` with `preserveState: true`).\n */\n\nimport { useArqelLocale, useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { PaginationMeta } from '@arqel-dev/types/resources';\nimport { useMemo } from 'react';\nimport { Button } from '../action/Button.js';\nimport { cn } from '../utils/cn.js';\n\nexport interface TablePaginationProps {\n meta: PaginationMeta;\n onPageChange: (page: number) => void;\n onPerPageChange?: ((perPage: number) => void) | undefined;\n perPageOptions?: number[];\n className?: string;\n}\n\nexport function TablePagination({\n meta,\n onPageChange,\n onPerPageChange,\n perPageOptions = [10, 25, 50, 100],\n className,\n}: TablePaginationProps) {\n const t = useArqelTranslations();\n const locale = useArqelLocale();\n const numberFormatter = useMemo(() => new Intl.NumberFormat(locale), [locale]);\n const fromRaw = (meta.currentPage - 1) * meta.perPage + 1;\n const toRaw = Math.min(meta.currentPage * meta.perPage, meta.total);\n // Interpolated counts are locale number-grouped (12,345 / 12.345) before they\n // reach the translator, whose applyReplacements does plain String() substitution.\n const from = numberFormatter.format(fromRaw);\n const to = numberFormatter.format(toRaw);\n const total = numberFormatter.format(meta.total);\n const isFirst = meta.currentPage <= 1;\n const isLast = meta.currentPage >= meta.lastPage;\n\n return (\n <nav\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2 border-t border-border px-3 py-2 text-sm',\n className,\n )}\n aria-label={t('table.pagination.label', 'Pagination')}\n >\n <span className=\"text-muted-foreground\">\n {meta.total === 0\n ? t('table.empty', 'No records found.')\n : t(\n 'table.pagination.showing',\n // Fallback is rendered verbatim when the key is missing, so it must\n // already carry the interpolated values (the replacements map only\n // applies to a resolved dictionary string).\n `Showing ${from} to ${to} of ${total} results`,\n { from, to, total },\n )}\n </span>\n <div className=\"flex items-center gap-2\">\n {onPerPageChange && (\n <label className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n {t('table.per_page', 'Per page')}\n {/* 44px touch target on mobile (WCAG 2.5.5); dense 32px on >=md. */}\n <select\n className=\"h-11 rounded-sm border border-[var(--input)] bg-background px-2 md:h-8\"\n value={meta.perPage}\n onChange={(e) => onPerPageChange(Number(e.target.value))}\n >\n {perPageOptions.map((n) => (\n <option key={n} value={n}>\n {n}\n </option>\n ))}\n </select>\n </label>\n )}\n {/* Buttons use size=\"sm\" (h-8) for the dense desktop bar; the responsive\n h-11 override lifts them to a 44px touch target below md. */}\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-11 md:h-8\"\n disabled={isFirst}\n onClick={() => onPageChange(meta.currentPage - 1)}\n aria-label={t('table.pagination.previous_page', 'Previous page')}\n >\n {t('table.pagination.previous', 'Prev')}\n </Button>\n <span className=\"text-xs text-muted-foreground\">\n {numberFormatter.format(meta.currentPage)} / {numberFormatter.format(meta.lastPage)}\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-11 md:h-8\"\n disabled={isLast}\n onClick={() => onPageChange(meta.currentPage + 1)}\n aria-label={t('table.pagination.next_page', 'Next page')}\n >\n {t('table.pagination.next', 'Next')}\n </Button>\n </div>\n </nav>\n );\n}\n","/**\n * `<TableToolbar>` — search input + filters + bulk-action bar slot.\n *\n * The bulk-action bar appears only when `selectedCount > 0`. ResourceIndex\n * passes `bulkActions` rendered as a row of buttons; the toolbar just\n * positions them.\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { ReactNode } from 'react';\nimport { cn } from '../utils/cn.js';\n\nexport interface TableToolbarProps {\n search?: ReactNode;\n filters?: ReactNode;\n selectedCount?: number;\n bulkActions?: ReactNode;\n onClearSelection?: (() => void) | undefined;\n className?: string;\n}\n\nexport function TableToolbar({\n search,\n filters,\n selectedCount = 0,\n bulkActions,\n onClearSelection,\n className,\n}: TableToolbarProps) {\n const t = useArqelTranslations();\n return (\n <div className={cn('flex flex-col gap-3 px-3 py-3', className)}>\n <div className=\"flex flex-wrap items-end gap-2\">\n {search}\n {filters}\n </div>\n {selectedCount > 0 && (\n <section\n className=\"flex flex-wrap items-center gap-2 rounded-sm border border-border bg-muted px-3 py-2\"\n aria-label={t('table.bulk.label', 'Bulk actions')}\n >\n <span className=\"text-sm font-medium\">\n {t('table.bulk.selected', '{one} :count selected|{other} :count selected', {\n count: selectedCount,\n })}\n </span>\n <div className=\"ml-auto flex flex-wrap items-center gap-2\">\n {bulkActions}\n {onClearSelection && (\n <button\n type=\"button\"\n className=\"text-sm text-muted-foreground hover:underline\"\n onClick={onClearSelection}\n >\n {t('table.bulk.clear', 'Clear')}\n </button>\n )}\n </div>\n </section>\n )}\n </div>\n );\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { F as FieldComponent, a as FieldRenderer, b as FieldRendererProps, c as
5
5
  export { CommandPalette, CommandPaletteProps, PaletteCommand } from './palette.js';
6
6
  export { Alert, AlertDescription, AlertTitle, Badge, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Input, Label, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, Skeleton, Textarea, badgeVariants } from './primitives.js';
7
7
  export { ResourceIndex, ResourceIndexUIProps } from './resource.js';
8
- export { A as AppShell, a as AppShellProps, b as AppShellVariant, F as Footer, c as FooterProps, M as MainContent, d as MainContentMaxWidth, e as MainContentProps, S as Sidebar, f as SidebarProps, T as Topbar, g as TopbarProps, U as UserMenu, h as UserMenuProps } from './UserMenu-B5XbC_Gy.js';
8
+ export { A as AppShell, a as AppShellProps, b as AppShellVariant, F as Footer, c as FooterProps, M as MainContent, d as MainContentMaxWidth, e as MainContentProps, S as Sidebar, f as SidebarProps, T as Topbar, g as TopbarProps, U as UserMenu, h as UserMenuProps } from './UserMenu-QlwvvZRU.js';
9
9
  export { D as DataTable, a as DataTableProps, b as DataTableRecord, T as TableFilters, c as TableFiltersProps, d as TablePagination, e as TablePaginationProps, f as TableToolbar, g as TableToolbarProps } from './TableToolbar-POLdlSgC.js';
10
10
  export { BreadcrumbItem, Breadcrumbs, BreadcrumbsProps, EmptyState, EmptyStateProps, ErrorState, ErrorStateProps, LoadingSkeleton, LoadingSkeletonProps, PageHeader, PageHeaderProps, SkeletonVariant } from './utility.js';
11
11
  export { c as cn } from './cn-By8XWiSA.js';
package/dist/index.js CHANGED
@@ -1,14 +1,18 @@
1
- export { Breadcrumbs, EmptyState, ErrorState, LoadingSkeleton } from './chunk-SK6H7ARE.js';
1
+ export { Breadcrumbs, EmptyState, LoadingSkeleton } from './chunk-3E66JNQO.js';
2
+ export { ErrorState } from './chunk-53J74IYR.js';
2
3
  export { StatCard, WidgetWrapper } from './chunk-WMOBLT3Z.js';
3
- export { Alert, AlertDescription, AlertTitle, Badge, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Label, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Textarea, badgeVariants } from './chunk-K7QY56XN.js';
4
+ export { Alert, AlertDescription, AlertTitle, Checkbox, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Label, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Textarea } from './chunk-D2MUCHFN.js';
5
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './chunk-6YGRTXEL.js';
4
6
  export { PageHeader } from './chunk-MXUXMHOK.js';
5
- export { AppShell, Footer, MainContent, Sidebar, Topbar, UserMenu } from './chunk-X2X6YDOX.js';
7
+ export { AppShell, Footer, MainContent, Sidebar, Topbar, UserMenu } from './chunk-EXA6DWLB.js';
6
8
  export { CommandPalette } from './chunk-HNUUZEOL.js';
7
- export { Input, Separator, Skeleton } from './chunk-R4BOUU6Y.js';
8
- export { ResourceIndex } from './chunk-EG2DASKR.js';
9
- export { DataTable, TableFilters, TablePagination, TableToolbar } from './chunk-S5UNPCMQ.js';
9
+ export { Badge, Input, Separator, Skeleton, badgeVariants } from './chunk-U4MPY2XT.js';
10
+ export { ResourceIndex } from './chunk-KGAY2AZH.js';
11
+ export { TableFilters, TablePagination, TableToolbar } from './chunk-YEPQWAD6.js';
12
+ export { DataTable } from './chunk-JKD6SE75.js';
10
13
  import './chunk-RMPOR2YZ.js';
11
- export { ActionButton, ActionFormModal, ActionMenu, ConfirmDialog } from './chunk-SLLTFOOS.js';
14
+ export { ActionButton, ActionFormModal, ActionMenu } from './chunk-7NGUPQON.js';
15
+ export { ConfirmDialog } from './chunk-DO3PRGVV.js';
12
16
  import './chunk-VKZY6J6G.js';
13
17
  export { FormActions } from './chunk-MZMYV43C.js';
14
18
  export { FieldRenderer, FormRenderer, clearFieldRegistry, getFieldComponent, registerField } from './chunk-CMNZLA32.js';
package/dist/pages.d.ts CHANGED
@@ -64,6 +64,31 @@ declare function ArqelEditPage<TRecord extends RecordType = RecordType>(): JSX.E
64
64
 
65
65
  declare function ArqelIndexPage<TRecord extends RecordType = RecordType>(): JSX.Element;
66
66
 
67
+ /**
68
+ * Default Inertia page for `arqel::notifications`.
69
+ *
70
+ * Renders the paginated notification history emitted by
71
+ * `Arqel\Core\Http\Controllers\NotificationController::index`
72
+ * (`{ history: paginator, filter: 'all' | 'unread' }`). The page-prop
73
+ * is named `history` — *not* `notifications` — because `notifications`
74
+ * is already a shared Inertia prop (`{unread_count, recent}`, see
75
+ * `HandleArqelInertiaRequests`) consumed by the `<NotificationBell>`
76
+ * mounted on every page's topbar. Reusing that key here would let this
77
+ * page's paginator silently overwrite the shared prop and break the
78
+ * bell (see branch review, milestone 0.19).
79
+ *
80
+ * The paginator serializes each item as `{id, type, data, read_at,
81
+ * created_at}` — same shape as the `notifications.recent` array the
82
+ * `<NotificationBell>` dropdown consumes, so titles/bodies follow the
83
+ * same `data.title` / `data.body` convention.
84
+ *
85
+ * Per-item actions do scoped Inertia visits (`only: ['history']` for
86
+ * mark-as-read, a plain reload for delete) so the list refreshes
87
+ * without a full page navigation.
88
+ */
89
+
90
+ declare function ArqelNotificationsPage(): JSX.Element;
91
+
67
92
  /**
68
93
  * Default Inertia page for `arqel::show`.
69
94
  *
@@ -101,4 +126,4 @@ type LazyPage = () => Promise<{
101
126
  }>;
102
127
  declare const arqelPages: Record<string, LazyPage>;
103
128
 
104
- export { ArqelCreatePage, ArqelDashboardPage, ArqelEditPage, ArqelIndexPage, ArqelShowPage, arqelPages };
129
+ export { ArqelCreatePage, ArqelDashboardPage, ArqelEditPage, ArqelIndexPage, ArqelNotificationsPage, ArqelShowPage, arqelPages };
package/dist/pages.js CHANGED
@@ -1,15 +1,20 @@
1
+ export { ArqelNotificationsPage } from './chunk-CH7YLELO.js';
1
2
  export { ArqelShowPage } from './chunk-Q7WGPKUN.js';
2
3
  export { ArqelDashboardPage } from './chunk-3VSDQCFO.js';
3
- export { ArqelEditPage } from './chunk-YQ6KXR3U.js';
4
- export { ArqelIndexPage } from './chunk-X4ZFKOPX.js';
4
+ export { ArqelEditPage } from './chunk-WKIYS2FQ.js';
5
+ export { ArqelIndexPage } from './chunk-DOQUHQND.js';
5
6
  import './chunk-YYQ45W25.js';
6
7
  export { ArqelCreatePage } from './chunk-IO6RMVUF.js';
8
+ import './chunk-53J74IYR.js';
7
9
  import './chunk-WMOBLT3Z.js';
10
+ import './chunk-6YGRTXEL.js';
8
11
  import './chunk-MXUXMHOK.js';
9
- import './chunk-EG2DASKR.js';
10
- import './chunk-S5UNPCMQ.js';
12
+ import './chunk-KGAY2AZH.js';
13
+ import './chunk-YEPQWAD6.js';
14
+ import './chunk-JKD6SE75.js';
11
15
  import './chunk-RMPOR2YZ.js';
12
- import './chunk-SLLTFOOS.js';
16
+ import './chunk-7NGUPQON.js';
17
+ import './chunk-DO3PRGVV.js';
13
18
  import './chunk-VKZY6J6G.js';
14
19
  import './chunk-MZMYV43C.js';
15
20
  import './chunk-CMNZLA32.js';
@@ -18,11 +23,12 @@ import './chunk-B7ULUJVO.js';
18
23
 
19
24
  // src/pages/index.ts
20
25
  var arqelPages = {
21
- "arqel::index": () => import('./ArqelIndexPage-J2EUV2PI.js'),
26
+ "arqel::index": () => import('./ArqelIndexPage-ZAVCCM6V.js'),
22
27
  "arqel::create": () => import('./ArqelCreatePage-UZAHV4DK.js'),
23
- "arqel::edit": () => import('./ArqelEditPage-RRYFYOTC.js'),
28
+ "arqel::edit": () => import('./ArqelEditPage-7OFWPULE.js'),
24
29
  "arqel::show": () => import('./ArqelShowPage-TVCJAV2F.js'),
25
- "arqel::dashboard": () => import('./ArqelDashboardPage-Z5R4XSH4.js')
30
+ "arqel::dashboard": () => import('./ArqelDashboardPage-Z5R4XSH4.js'),
31
+ "arqel::notifications": () => import('./ArqelNotificationsPage-MBJBYFHW.js')
26
32
  };
27
33
 
28
34
  export { arqelPages };
package/dist/pages.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pages/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AA0BO,IAAM,UAAA,GAAuC;AAAA,EAClD,cAAA,EAAgB,MACd,OAAO,8BAAqB,CAAA;AAAA,EAC9B,eAAA,EAAiB,MACf,OAAO,+BAAsB,CAAA;AAAA,EAC/B,aAAA,EAAe,MAAM,OAAO,6BAAoB,CAAA;AAAA,EAChD,aAAA,EAAe,MAAM,OAAO,6BAAoB,CAAA;AAAA,EAChD,kBAAA,EAAoB,MAClB,OAAO,kCAAyB;AACpC","file":"pages.js","sourcesContent":["/**\n * Built-in Inertia page registry for `arqel::*`.\n *\n * `createArqelApp({ pages })` from `@arqel-dev/react/inertia` accepts\n * one or more `PageRegistry` records — merge `arqelPages` first so\n * user-defined pages can override per-resource:\n *\n * ```ts\n * import { arqelPages } from '@arqel-dev/ui/pages';\n * import { createArqelApp } from '@arqel-dev/react/inertia';\n *\n * const userPages = import.meta.glob('./Pages/**\\/*.tsx');\n *\n * createArqelApp({\n * pages: { ...arqelPages, ...userPages },\n * });\n * ```\n *\n * Names match what `ResourceController` emits via\n * `Inertia::render('arqel::index', ...)`.\n */\n\nimport type { ComponentType } from 'react';\n\ntype LazyPage = () => Promise<{ default: ComponentType<unknown> }>;\n\nexport const arqelPages: Record<string, LazyPage> = {\n 'arqel::index': () =>\n import('./ArqelIndexPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::create': () =>\n import('./ArqelCreatePage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::edit': () => import('./ArqelEditPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::show': () => import('./ArqelShowPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::dashboard': () =>\n import('./ArqelDashboardPage.js') as Promise<{ default: ComponentType<unknown> }>,\n};\n\nexport { default as ArqelCreatePage } from './ArqelCreatePage.js';\nexport { default as ArqelDashboardPage } from './ArqelDashboardPage.js';\nexport { default as ArqelEditPage } from './ArqelEditPage.js';\nexport { default as ArqelIndexPage } from './ArqelIndexPage.js';\nexport { default as ArqelShowPage } from './ArqelShowPage.js';\n"]}
1
+ {"version":3,"sources":["../src/pages/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA0BO,IAAM,UAAA,GAAuC;AAAA,EAClD,cAAA,EAAgB,MACd,OAAO,8BAAqB,CAAA;AAAA,EAC9B,eAAA,EAAiB,MACf,OAAO,+BAAsB,CAAA;AAAA,EAC/B,aAAA,EAAe,MAAM,OAAO,6BAAoB,CAAA;AAAA,EAChD,aAAA,EAAe,MAAM,OAAO,6BAAoB,CAAA;AAAA,EAChD,kBAAA,EAAoB,MAClB,OAAO,kCAAyB,CAAA;AAAA,EAClC,sBAAA,EAAwB,MACtB,OAAO,sCAA6B;AACxC","file":"pages.js","sourcesContent":["/**\n * Built-in Inertia page registry for `arqel::*`.\n *\n * `createArqelApp({ pages })` from `@arqel-dev/react/inertia` accepts\n * one or more `PageRegistry` records — merge `arqelPages` first so\n * user-defined pages can override per-resource:\n *\n * ```ts\n * import { arqelPages } from '@arqel-dev/ui/pages';\n * import { createArqelApp } from '@arqel-dev/react/inertia';\n *\n * const userPages = import.meta.glob('./Pages/**\\/*.tsx');\n *\n * createArqelApp({\n * pages: { ...arqelPages, ...userPages },\n * });\n * ```\n *\n * Names match what `ResourceController` emits via\n * `Inertia::render('arqel::index', ...)`.\n */\n\nimport type { ComponentType } from 'react';\n\ntype LazyPage = () => Promise<{ default: ComponentType<unknown> }>;\n\nexport const arqelPages: Record<string, LazyPage> = {\n 'arqel::index': () =>\n import('./ArqelIndexPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::create': () =>\n import('./ArqelCreatePage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::edit': () => import('./ArqelEditPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::show': () => import('./ArqelShowPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::dashboard': () =>\n import('./ArqelDashboardPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::notifications': () =>\n import('./ArqelNotificationsPage.js') as Promise<{ default: ComponentType<unknown> }>,\n};\n\nexport { default as ArqelCreatePage } from './ArqelCreatePage.js';\nexport { default as ArqelDashboardPage } from './ArqelDashboardPage.js';\nexport { default as ArqelEditPage } from './ArqelEditPage.js';\nexport { default as ArqelIndexPage } from './ArqelIndexPage.js';\nexport { default as ArqelNotificationsPage } from './ArqelNotificationsPage.js';\nexport { default as ArqelShowPage } from './ArqelShowPage.js';\n"]}
@@ -1,5 +1,6 @@
1
- export { Alert, AlertDescription, AlertTitle, Badge, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Label, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Textarea, badgeVariants } from './chunk-K7QY56XN.js';
2
- export { Input, Separator, Skeleton } from './chunk-R4BOUU6Y.js';
1
+ export { Alert, AlertDescription, AlertTitle, Checkbox, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Label, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Textarea } from './chunk-D2MUCHFN.js';
2
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './chunk-6YGRTXEL.js';
3
+ export { Badge, Input, Separator, Skeleton, badgeVariants } from './chunk-U4MPY2XT.js';
3
4
  import './chunk-B7ULUJVO.js';
4
5
  //# sourceMappingURL=primitives.js.map
5
6
  //# sourceMappingURL=primitives.js.map
package/dist/resource.js CHANGED
@@ -1,5 +1,6 @@
1
- export { ResourceIndex } from './chunk-EG2DASKR.js';
2
- import './chunk-S5UNPCMQ.js';
1
+ export { ResourceIndex } from './chunk-KGAY2AZH.js';
2
+ import './chunk-YEPQWAD6.js';
3
+ import './chunk-JKD6SE75.js';
3
4
  import './chunk-RMPOR2YZ.js';
4
5
  import './chunk-H23VX3RR.js';
5
6
  import './chunk-B7ULUJVO.js';
package/dist/shell.d.ts CHANGED
@@ -1,9 +1,43 @@
1
- export { A as AppShell, a as AppShellProps, b as AppShellVariant, F as Footer, c as FooterProps, M as MainContent, d as MainContentMaxWidth, e as MainContentProps, S as Sidebar, f as SidebarProps, T as Topbar, g as TopbarProps, U as UserMenu, h as UserMenuProps } from './UserMenu-B5XbC_Gy.js';
2
- import { TenantSummary } from '@arqel-dev/types/tenant';
1
+ export { A as AppShell, a as AppShellProps, b as AppShellVariant, F as Footer, c as FooterProps, M as MainContent, d as MainContentMaxWidth, e as MainContentProps, S as Sidebar, f as SidebarProps, T as Topbar, g as TopbarProps, U as UserMenu, h as UserMenuProps } from './UserMenu-QlwvvZRU.js';
3
2
  import { JSX } from 'react';
3
+ import { NotificationItem } from '@arqel-dev/types/inertia';
4
+ import { TenantSummary } from '@arqel-dev/types/tenant';
4
5
  import 'react/jsx-runtime';
5
6
  import '@arqel-dev/hooks';
6
7
 
8
+ /**
9
+ * `<NotificationBell>` — database-notifications trigger for the Topbar
10
+ * `notifications` slot.
11
+ *
12
+ * Reads the shared `notifications` prop (`NotificationPayload | null`,
13
+ * see `HandleArqelInertiaRequests`) via `usePage()`. Mounts a bell icon
14
+ * with an unread-count `Badge` (shown only when `unread_count > 0`) and
15
+ * a `DropdownMenu` listing the `recent` notifications. Marking a single
16
+ * notification read, or all of them, does a scoped Inertia partial
17
+ * reload (`only: ['notifications']`) — no full page reload.
18
+ */
19
+
20
+ interface NotificationBellProps {
21
+ className?: string;
22
+ }
23
+ declare function NotificationBell({ className }: NotificationBellProps): JSX.Element;
24
+
25
+ /**
26
+ * `<NotificationList>` — renders the `recent` notifications inside the
27
+ * `<NotificationBell>` dropdown content.
28
+ *
29
+ * Extracted from `NotificationBell` so it can be exercised directly in
30
+ * tests without depending on Radix `DropdownMenu` mounting its portal
31
+ * content in jsdom.
32
+ */
33
+
34
+ interface NotificationListProps {
35
+ items: ReadonlyArray<NotificationItem>;
36
+ emptyLabel: string;
37
+ onItemClick: (item: NotificationItem) => void;
38
+ }
39
+ declare function NotificationList({ items, emptyLabel, onItemClick, }: NotificationListProps): JSX.Element;
40
+
7
41
  /**
8
42
  * `<TenantSwitcher>` — dropdown that lists tenants the user can
9
43
  * access and dispatches `POST /admin/tenants/{id}/switch` via
@@ -27,4 +61,4 @@ interface TenantSwitcherProps {
27
61
  }
28
62
  declare function TenantSwitcher({ current, available, switchUrl, }: TenantSwitcherProps): JSX.Element | null;
29
63
 
30
- export { TenantSwitcher, type TenantSwitcherProps };
64
+ export { NotificationBell, type NotificationBellProps, NotificationList, type NotificationListProps, TenantSwitcher, type TenantSwitcherProps };