@flowselections/mailbox-orders 1.0.53 → 1.0.57

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 (98) hide show
  1. package/dist-lib/components/MailboxAuthBanner.d.ts +3 -3
  2. package/dist-lib/components/MailboxAuthBanner.d.ts.map +1 -1
  3. package/dist-lib/components/MailboxAuthBanner.js +20 -7
  4. package/dist-lib/components/MailboxPageFrame.d.ts +25 -0
  5. package/dist-lib/components/MailboxPageFrame.d.ts.map +1 -0
  6. package/dist-lib/components/MailboxPageFrame.js +103 -0
  7. package/dist-lib/components/MailboxProcessedPage.d.ts.map +1 -1
  8. package/dist-lib/components/MailboxProcessedPage.js +102 -61
  9. package/dist-lib/components/MailboxProposalsPage.d.ts.map +1 -1
  10. package/dist-lib/components/MailboxProposalsPage.js +191 -76
  11. package/dist-lib/components/MailboxSkippedPage.d.ts.map +1 -1
  12. package/dist-lib/components/MailboxSkippedPage.js +97 -19
  13. package/dist-lib/components/MailboxStatusLine.d.ts +8 -2
  14. package/dist-lib/components/MailboxStatusLine.d.ts.map +1 -1
  15. package/dist-lib/components/MailboxStatusLine.js +54 -8
  16. package/dist-lib/components/TablePagination.d.ts +2 -2
  17. package/dist-lib/components/TablePagination.d.ts.map +1 -1
  18. package/dist-lib/components/TablePagination.js +8 -12
  19. package/dist-lib/components/fs-ui/DataTable.d.ts +42 -0
  20. package/dist-lib/components/fs-ui/DataTable.d.ts.map +1 -0
  21. package/dist-lib/components/fs-ui/DataTable.js +40 -0
  22. package/dist-lib/components/fs-ui/EmptyState.d.ts +9 -0
  23. package/dist-lib/components/fs-ui/EmptyState.d.ts.map +1 -0
  24. package/dist-lib/components/fs-ui/EmptyState.js +5 -0
  25. package/dist-lib/components/fs-ui/FilterChips.d.ts +13 -0
  26. package/dist-lib/components/fs-ui/FilterChips.d.ts.map +1 -0
  27. package/dist-lib/components/fs-ui/FilterChips.js +8 -0
  28. package/dist-lib/components/fs-ui/FilterPanel.d.ts +32 -0
  29. package/dist-lib/components/fs-ui/FilterPanel.d.ts.map +1 -0
  30. package/dist-lib/components/fs-ui/FilterPanel.js +20 -0
  31. package/dist-lib/components/fs-ui/HoverTip.d.ts +15 -0
  32. package/dist-lib/components/fs-ui/HoverTip.d.ts.map +1 -0
  33. package/dist-lib/components/fs-ui/HoverTip.js +24 -0
  34. package/dist-lib/components/fs-ui/InlineAlert.d.ts +9 -0
  35. package/dist-lib/components/fs-ui/InlineAlert.d.ts.map +1 -0
  36. package/dist-lib/components/fs-ui/InlineAlert.js +6 -0
  37. package/dist-lib/components/fs-ui/KpiRow.d.ts +6 -0
  38. package/dist-lib/components/fs-ui/KpiRow.d.ts.map +1 -0
  39. package/dist-lib/components/fs-ui/KpiRow.js +7 -0
  40. package/dist-lib/components/fs-ui/KpiTile.d.ts +21 -0
  41. package/dist-lib/components/fs-ui/KpiTile.d.ts.map +1 -0
  42. package/dist-lib/components/fs-ui/KpiTile.js +40 -0
  43. package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts +10 -0
  44. package/dist-lib/components/fs-ui/MobilePrimaryAction.d.ts.map +1 -0
  45. package/dist-lib/components/fs-ui/MobilePrimaryAction.js +7 -0
  46. package/dist-lib/components/fs-ui/NoticeBar.d.ts +18 -0
  47. package/dist-lib/components/fs-ui/NoticeBar.d.ts.map +1 -0
  48. package/dist-lib/components/fs-ui/NoticeBar.js +24 -0
  49. package/dist-lib/components/fs-ui/PageHeader.d.ts +8 -0
  50. package/dist-lib/components/fs-ui/PageHeader.d.ts.map +1 -0
  51. package/dist-lib/components/fs-ui/PageHeader.js +5 -0
  52. package/dist-lib/components/fs-ui/PillTabs.d.ts +16 -0
  53. package/dist-lib/components/fs-ui/PillTabs.d.ts.map +1 -0
  54. package/dist-lib/components/fs-ui/PillTabs.js +29 -0
  55. package/dist-lib/components/fs-ui/RowMenu.d.ts +13 -0
  56. package/dist-lib/components/fs-ui/RowMenu.d.ts.map +1 -0
  57. package/dist-lib/components/fs-ui/RowMenu.js +10 -0
  58. package/dist-lib/components/fs-ui/SearchInput.d.ts +12 -0
  59. package/dist-lib/components/fs-ui/SearchInput.d.ts.map +1 -0
  60. package/dist-lib/components/fs-ui/SearchInput.js +38 -0
  61. package/dist-lib/components/fs-ui/ShortcutHelp.d.ts +7 -0
  62. package/dist-lib/components/fs-ui/ShortcutHelp.d.ts.map +1 -0
  63. package/dist-lib/components/fs-ui/ShortcutHelp.js +6 -0
  64. package/dist-lib/components/fs-ui/StatusIcon.d.ts +8 -0
  65. package/dist-lib/components/fs-ui/StatusIcon.d.ts.map +1 -0
  66. package/dist-lib/components/fs-ui/StatusIcon.js +16 -0
  67. package/dist-lib/components/fs-ui/TableSkeleton.d.ts +6 -0
  68. package/dist-lib/components/fs-ui/TableSkeleton.d.ts.map +1 -0
  69. package/dist-lib/components/fs-ui/TableSkeleton.js +6 -0
  70. package/dist-lib/components/fs-ui/Toolbar.d.ts +13 -0
  71. package/dist-lib/components/fs-ui/Toolbar.d.ts.map +1 -0
  72. package/dist-lib/components/fs-ui/Toolbar.js +10 -0
  73. package/dist-lib/components/fs-ui/UpdatedAt.d.ts +8 -0
  74. package/dist-lib/components/fs-ui/UpdatedAt.d.ts.map +1 -0
  75. package/dist-lib/components/fs-ui/UpdatedAt.js +17 -0
  76. package/dist-lib/components/fs-ui/useListHotkeys.d.ts +18 -0
  77. package/dist-lib/components/fs-ui/useListHotkeys.d.ts.map +1 -0
  78. package/dist-lib/components/fs-ui/useListHotkeys.js +48 -0
  79. package/dist-lib/components/settings/MailboxAiPromptCard.d.ts +2 -1
  80. package/dist-lib/components/settings/MailboxAiPromptCard.d.ts.map +1 -1
  81. package/dist-lib/components/settings/MailboxAiPromptCard.js +9 -1
  82. package/dist-lib/components/settings/MailboxFilterSettingsCard.js +1 -1
  83. package/dist-lib/components/settings/MailboxTestSetCard.d.ts +2 -1
  84. package/dist-lib/components/settings/MailboxTestSetCard.d.ts.map +1 -1
  85. package/dist-lib/components/settings/MailboxTestSetCard.js +10 -2
  86. package/dist-lib/integrations/supabase/types.d.ts +372 -0
  87. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  88. package/dist-lib/lib/format-date.d.ts +5 -0
  89. package/dist-lib/lib/format-date.d.ts.map +1 -0
  90. package/dist-lib/lib/format-date.js +30 -0
  91. package/dist-lib/lib/labels.d.ts +15 -0
  92. package/dist-lib/lib/labels.d.ts.map +1 -0
  93. package/dist-lib/lib/labels.js +57 -0
  94. package/dist-lib/lib/mailbox-list.functions.d.ts +67 -0
  95. package/dist-lib/lib/mailbox-list.functions.d.ts.map +1 -0
  96. package/dist-lib/lib/mailbox-list.functions.js +61 -0
  97. package/dist-lib/lib/mailbox.functions.js +3 -3
  98. package/package.json +1 -1
@@ -0,0 +1,6 @@
1
+ import type { ReactNode } from "react";
2
+ /** Rij KPI-tegels: 4 naast elkaar vanaf desktop, 2 op tablet, veegbaar op mobiel. */
3
+ export declare function KpiRow({ children }: {
4
+ children: ReactNode;
5
+ }): import("react").JSX.Element;
6
+ //# sourceMappingURL=KpiRow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KpiRow.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/KpiRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,qFAAqF;AACrF,wBAAgB,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAY3D"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /** Rij KPI-tegels: 4 naast elkaar vanaf desktop, 2 op tablet, veegbaar op mobiel. */
3
+ export function KpiRow({ children }) {
4
+ return (_jsx("div", { className: "-mx-4 flex shrink-0 scroll-px-4 snap-x snap-mandatory gap-3 overflow-x-auto px-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden " +
5
+ "md:mx-0 md:grid md:grid-cols-2 md:overflow-visible md:px-0 lg:grid-cols-4 " +
6
+ "[&>*]:w-[78%] [&>*]:shrink-0 [&>*]:snap-start md:[&>*]:w-auto", children: children }));
7
+ }
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from "react";
2
+ export interface KpiTrend {
3
+ /** Procentuele wijziging t.o.v. de vorige periode (null = geen vergelijking). */
4
+ pct: number | null;
5
+ label: string;
6
+ /** Hoger is beter (omzet) of slechter (actie nodig). */
7
+ goodWhenUp?: boolean;
8
+ }
9
+ /** KPI-tegel: icoon, label, groot getal, subregel en optionele trend. Alleen weergave. */
10
+ export declare function KpiTile({ title, value, sub, icon, tone, loading, onClick, active, trend, }: {
11
+ title: string;
12
+ value: ReactNode;
13
+ sub?: ReactNode;
14
+ icon?: ReactNode;
15
+ tone?: "default" | "warning";
16
+ loading?: boolean;
17
+ onClick?: () => void;
18
+ active?: boolean;
19
+ trend?: KpiTrend | null;
20
+ }): import("react").JSX.Element;
21
+ //# sourceMappingURL=KpiTile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KpiTile.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/KpiTile.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,WAAW,QAAQ;IACvB,iFAAiF;IACjF,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAoCD,0FAA0F;AAC1F,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,KAAK,EACL,GAAG,EACH,IAAI,EACJ,IAAgB,EAChB,OAAO,EACP,OAAO,EACP,MAAM,EACN,KAAK,GACN,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CACzB,+BA4DA"}
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Card, Skeleton } from "@flowselections/core";
3
+ import { ChevronRight, Minus, TrendingDown, TrendingUp } from "lucide-react";
4
+ function Trend({ trend }) {
5
+ if (trend.pct == null)
6
+ return null;
7
+ const flat = Math.abs(trend.pct) < 0.5;
8
+ const up = trend.pct > 0;
9
+ const good = flat ? null : up === (trend.goodWhenUp ?? true);
10
+ const Icon = flat ? Minus : up ? TrendingUp : TrendingDown;
11
+ const tone = good == null ? "text-muted-foreground" : good ? "text-primary" : "text-destructive";
12
+ return (_jsxs("span", { className: "inline-flex min-w-0 items-center gap-1 whitespace-nowrap text-xs font-medium tabular-nums " + tone, title: trend.label, children: [_jsx(Icon, { className: "h-3.5 w-3.5", "aria-hidden": true }), flat ? "0%" : `${up ? "+" : ""}${Math.round(trend.pct)}%`, _jsx("span", { className: "truncate font-normal text-muted-foreground", children: trend.label })] }));
13
+ }
14
+ /** Klein trendlijntje van vorige naar huidige periode (alleen weergave). */
15
+ function Spark({ trend }) {
16
+ if (trend.pct == null)
17
+ return null;
18
+ const up = trend.pct > 0.5;
19
+ const down = trend.pct < -0.5;
20
+ const good = up || down ? up === (trend.goodWhenUp ?? true) : null;
21
+ const tone = good == null ? "text-muted-foreground" : good ? "text-primary" : "text-destructive";
22
+ const y0 = up ? 26 : down ? 6 : 16;
23
+ const y1 = up ? 6 : down ? 26 : 16;
24
+ const mid = (y0 + y1) / 2;
25
+ const d = `M2 ${y0} C 14 ${y0}, 18 ${mid + (up ? 4 : down ? -4 : 0)}, 28 ${mid} S 44 ${mid - (up ? 6 : down ? -6 : 0)}, 50 ${(mid + y1) / 2} S 58 ${y1}, 62 ${y1}`;
26
+ return (_jsx("svg", { viewBox: "0 0 64 32", className: "hidden h-8 w-16 shrink-0 self-end sm:block " + tone, "aria-hidden": true, children: _jsx("path", { d: d, fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
27
+ }
28
+ /** KPI-tegel: icoon, label, groot getal, subregel en optionele trend. Alleen weergave. */
29
+ export function KpiTile({ title, value, sub, icon, tone = "default", loading, onClick, active, trend, }) {
30
+ const body = (_jsxs("div", { className: "flex items-center gap-3 sm:gap-4", children: [icon && (_jsx("span", { className: "inline-flex h-10 w-10 sm:h-12 sm:w-12 shrink-0 items-center justify-center rounded-xl [&_svg]:h-5 [&_svg]:w-5 sm:[&_svg]:h-6 sm:[&_svg]:w-6 " +
31
+ (tone === "warning" ? "bg-warning-soft text-warning-soft-foreground" : "bg-primary/10 text-primary"), "aria-hidden": true, children: icon })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-[11px] font-semibold uppercase tracking-wider text-muted-foreground", children: title }), _jsx("div", { className: "mt-0.5 text-2xl font-semibold leading-tight tabular-nums text-foreground", children: loading ? _jsx(Skeleton, { className: "h-7 w-24" }) : value }), _jsx("div", { className: "mt-1 flex min-h-4 flex-col gap-0.5 text-xs text-muted-foreground", children: loading ? _jsx(Skeleton, { className: "h-3 w-32" }) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "truncate", children: sub }), trend && _jsx(Trend, { trend: trend })] })) })] }), !loading && trend && _jsx(Spark, { trend: trend }), onClick && tone === "warning" && _jsx(ChevronRight, { className: "h-5 w-5 shrink-0 text-muted-foreground", "aria-hidden": true })] }));
32
+ return (_jsx(Card, { className: "h-full p-4 sm:p-5 transition-shadow " +
33
+ (onClick ? "hover:border-ring/50 hover:shadow-md " : "") +
34
+ (active ? "ring-2 ring-ring" : ""), children: onClick ? (_jsx("div", { role: "button", tabIndex: 0, onClick: onClick, onKeyDown: (e) => {
35
+ if (e.key === "Enter" || e.key === " ") {
36
+ e.preventDefault();
37
+ onClick();
38
+ }
39
+ }, className: "block w-full cursor-pointer rounded-sm text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: body })) : (body) }));
40
+ }
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ /** Vaste, schermbrede hoofdknop onderin op mobiel (onder 768 px). */
3
+ export declare function MobilePrimaryAction({ label, icon, onClick, disabled, hidden, }: {
4
+ label: string;
5
+ icon?: ReactNode;
6
+ onClick: () => void;
7
+ disabled?: boolean;
8
+ hidden?: boolean;
9
+ }): import("react").JSX.Element | null;
10
+ //# sourceMappingURL=MobilePrimaryAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MobilePrimaryAction.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/MobilePrimaryAction.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,sCAeA"}
@@ -0,0 +1,7 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /** Vaste, schermbrede hoofdknop onderin op mobiel (onder 768 px). */
3
+ export function MobilePrimaryAction({ label, icon, onClick, disabled, hidden, }) {
4
+ if (hidden)
5
+ return null;
6
+ return (_jsx("div", { className: "fixed inset-x-0 bottom-0 z-30 border-t border-border bg-background/95 px-4 pt-3 shadow-lg backdrop-blur md:hidden", style: { paddingBottom: "calc(env(safe-area-inset-bottom) + 12px)" }, children: _jsxs("button", { type: "button", onClick: onClick, disabled: disabled, className: "inline-flex h-12 w-full items-center justify-center gap-2 rounded-lg bg-primary text-base font-semibold text-primary-foreground transition-opacity active:opacity-80 disabled:opacity-50 [&_svg]:h-5 [&_svg]:w-5", children: [icon, label] }) }));
7
+ }
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from "react";
2
+ export interface Notice {
3
+ key: string;
4
+ tone?: "warning" | "error" | "info";
5
+ title: string;
6
+ /** Uitklapbare toelichting (nooit een lijst van items). */
7
+ details?: ReactNode;
8
+ action?: ReactNode;
9
+ }
10
+ /** Eén melding op één regel; details klappen uit (stijl NoticeBar uit Verkooporders). */
11
+ export declare function NoticeRow({ notice }: {
12
+ notice: Notice;
13
+ }): import("react").JSX.Element;
14
+ /** Meldingen direct onder de kop: hooguit 2 zichtbaar, de rest achter "+ n meldingen". */
15
+ export declare function NoticeBar({ notices }: {
16
+ notices: Notice[];
17
+ }): import("react").JSX.Element | null;
18
+ //# sourceMappingURL=NoticeBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoticeBar.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/NoticeBar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGjD,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAQD,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,+BAwBvD;AAED,0FAA0F;AAC1F,wBAAgB,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,sCAmB3D"}
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { AlertTriangle, ChevronDown, Info, XCircle } from "lucide-react";
4
+ const TONE = {
5
+ warning: "border-border bg-warning-soft text-warning-soft-foreground",
6
+ error: "border-destructive/30 bg-destructive/5 text-foreground [&_.nb-icon]:text-destructive",
7
+ info: "border-border bg-muted/50 text-foreground [&_.nb-icon]:text-muted-foreground",
8
+ };
9
+ /** Eén melding op één regel; details klappen uit (stijl NoticeBar uit Verkooporders). */
10
+ export function NoticeRow({ notice }) {
11
+ const [open, setOpen] = useState(false);
12
+ const tone = notice.tone ?? "warning";
13
+ const Icon = tone === "error" ? XCircle : tone === "info" ? Info : AlertTriangle;
14
+ return (_jsxs("div", { role: tone === "error" ? "alert" : "status", className: "shrink-0 rounded-lg border text-sm " + TONE[tone], children: [_jsxs("div", { className: "flex items-center gap-2 pr-2", children: [_jsxs("button", { type: "button", "aria-expanded": notice.details ? open : undefined, onClick: () => notice.details && setOpen((o) => !o), className: "flex min-w-0 flex-1 items-center gap-2 rounded-lg px-3 py-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring " + (notice.details ? "" : "cursor-default"), children: [_jsx(Icon, { className: "nb-icon h-4 w-4 shrink-0", "aria-hidden": true }), _jsx("span", { className: "min-w-0 flex-1 truncate font-medium", title: notice.title, children: notice.title }), notice.details && _jsx(ChevronDown, { className: "h-4 w-4 shrink-0 transition-transform " + (open ? "rotate-180" : ""), "aria-hidden": true })] }), notice.action && _jsx("div", { className: "shrink-0", children: notice.action })] }), open && notice.details && (_jsx("div", { className: "border-t border-border/60 px-3 py-2 pl-9 opacity-90", children: notice.details }))] }));
15
+ }
16
+ /** Meldingen direct onder de kop: hooguit 2 zichtbaar, de rest achter "+ n meldingen". */
17
+ export function NoticeBar({ notices }) {
18
+ const [all, setAll] = useState(false);
19
+ if (notices.length === 0)
20
+ return null;
21
+ const visible = all ? notices : notices.slice(0, 2);
22
+ const rest = notices.length - visible.length;
23
+ return (_jsxs("div", { className: "flex shrink-0 flex-col gap-2", "aria-live": "polite", children: [visible.map((n) => _jsx(NoticeRow, { notice: n }, n.key)), rest > 0 && (_jsxs("button", { type: "button", onClick: () => setAll(true), className: "self-start rounded-md px-1 text-xs font-medium text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: ["+ ", rest, " ", rest === 1 ? "melding" : "meldingen"] }))] }));
24
+ }
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from "react";
2
+ /** Paginakop exact als Verkooporders: h1, daaronder alleen "Bijgewerkt", acties rechts. */
3
+ export declare function PageHeader({ title, meta, actions, }: {
4
+ title: string;
5
+ meta?: ReactNode;
6
+ actions?: ReactNode;
7
+ }): import("react").JSX.Element;
8
+ //# sourceMappingURL=PageHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageHeader.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/PageHeader.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,IAAI,EACJ,OAAO,GACR,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,+BAcA"}
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** Paginakop exact als Verkooporders: h1, daaronder alleen "Bijgewerkt", acties rechts. */
3
+ export function PageHeader({ title, meta, actions, }) {
4
+ return (_jsxs("header", { className: "shrink-0 grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 sm:flex sm:flex-wrap sm:justify-between", children: [_jsx("div", { className: "flex min-w-0 items-center gap-3", children: _jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "truncate text-xl font-bold leading-tight sm:text-2xl tracking-tight text-foreground", children: title }), _jsx("p", { className: "flex flex-wrap items-center gap-x-2 text-xs text-muted-foreground", "aria-live": "polite", children: meta })] }) }), actions && _jsx("div", { className: "contents sm:flex sm:flex-wrap sm:items-center sm:gap-2", children: actions })] }));
5
+ }
@@ -0,0 +1,16 @@
1
+ import { type ReactNode } from "react";
2
+ export interface PillTab {
3
+ key: string;
4
+ label: string;
5
+ icon?: ReactNode;
6
+ count?: number | null;
7
+ hint?: string;
8
+ }
9
+ /** Tabbladen exact als StatusTabs in Verkooporders: één regel, veegbaar met vervaging. */
10
+ export declare function PillTabs({ tabs, active, onSelect, ariaLabel, }: {
11
+ tabs: PillTab[];
12
+ active: string;
13
+ onSelect: (key: string) => void;
14
+ ariaLabel?: string;
15
+ }): import("react").JSX.Element;
16
+ //# sourceMappingURL=PillTabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PillTabs.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/PillTabs.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEhE,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,0FAA0F;AAC1F,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,SAAS,GACV,EAAE;IACD,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BAyDA"}
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useRef } from "react";
3
+ /** Tabbladen exact als StatusTabs in Verkooporders: één regel, veegbaar met vervaging. */
4
+ export function PillTabs({ tabs, active, onSelect, ariaLabel, }) {
5
+ const listRef = useRef(null);
6
+ useLayoutEffect(() => {
7
+ const measure = () => {
8
+ const el = listRef.current?.querySelector(`[data-tab="${active}"]`);
9
+ const list = listRef.current;
10
+ if (el && list && (el.offsetLeft < list.scrollLeft || el.offsetLeft + el.offsetWidth > list.scrollLeft + list.clientWidth)) {
11
+ list.scrollLeft = el.offsetLeft - 8;
12
+ }
13
+ };
14
+ measure();
15
+ window.addEventListener("resize", measure);
16
+ return () => window.removeEventListener("resize", measure);
17
+ }, [active]);
18
+ return (_jsx("div", { className: "shrink-0", children: _jsx("div", { ref: listRef, role: "tablist", "aria-label": ariaLabel, className: "relative -mx-4 flex items-center gap-2 overflow-x-auto px-4 pb-1 [scrollbar-width:none] [mask-image:linear-gradient(to_right,black_85%,transparent)] [&::-webkit-scrollbar]:hidden sm:mx-0 sm:px-0 sm:[mask-image:none]", children: tabs.map((t) => {
19
+ const isActive = active === t.key;
20
+ const count = t.count ?? null;
21
+ return (_jsxs("button", { "data-tab": t.key, type: "button", role: "tab", "aria-selected": isActive, onClick: () => onSelect(t.key), title: t.hint, style: { transitionProperty: "color, background-color, border-color", transitionDuration: "150ms" }, className: "relative flex h-9 items-center gap-2 whitespace-nowrap rounded-lg border px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring " +
22
+ (isActive
23
+ ? "border-primary/30 bg-primary/10 font-semibold text-foreground"
24
+ : count === 0
25
+ ? "border-border bg-card text-muted-foreground/70 hover:text-foreground"
26
+ : "border-border bg-card text-foreground/80 hover:bg-muted hover:text-foreground"), children: [t.icon && _jsx("span", { className: "inline-flex shrink-0 [&_svg]:h-4 [&_svg]:w-4", "aria-hidden": true, children: t.icon }), _jsx("span", { children: t.label }), count != null && (_jsx("span", { className: "inline-flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] font-semibold tabular-nums " +
27
+ (isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"), children: count }))] }, t.key));
28
+ }) }) }));
29
+ }
@@ -0,0 +1,13 @@
1
+ export interface RowMenuItem {
2
+ label: string;
3
+ onSelect: () => void;
4
+ disabled?: boolean;
5
+ destructive?: boolean;
6
+ title?: string;
7
+ }
8
+ /** Rijmenu exact als Verkooporders (drie puntjes, acties in een menu). */
9
+ export declare function RowMenu({ label, items }: {
10
+ label: string;
11
+ items: RowMenuItem[];
12
+ }): import("react").JSX.Element | null;
13
+ //# sourceMappingURL=RowMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RowMenu.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/RowMenu.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,EAAE,CAAA;CAAE,sCA0BhF"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { MoreVertical } from "lucide-react";
3
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@flowselections/core";
4
+ /** Rijmenu exact als Verkooporders (drie puntjes, acties in een menu). */
5
+ export function RowMenu({ label, items }) {
6
+ if (items.length === 0)
7
+ return null;
8
+ return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { "aria-label": label, onClick: (e) => e.stopPropagation(), className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(MoreVertical, { className: "h-4 w-4" }) }), _jsx(DropdownMenuContent, { align: "end", onClick: (e) => e.stopPropagation(), children: items.map((it) => (_jsx(DropdownMenuItem, { disabled: it.disabled, title: it.title, className: it.destructive ? "text-destructive focus:text-destructive" : undefined, onClick: () => { if (!it.disabled)
9
+ it.onSelect(); }, children: it.label }, it.label))) })] }));
10
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Zoekveld exact als Verkooporders: h-9, sm:w-80, breder bij focus.
3
+ * Filtert tijdens typen (250 ms), wis-kruisje, Esc wist. "/" regelt de pagina.
4
+ */
5
+ export declare const SearchInput: import("react").ForwardRefExoticComponent<{
6
+ value: string;
7
+ onChange: (v: string) => void;
8
+ placeholder?: string;
9
+ delay?: number;
10
+ ariaLabel?: string;
11
+ } & import("react").RefAttributes<HTMLInputElement>>;
12
+ //# sourceMappingURL=SearchInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchInput.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/SearchInput.tsx"],"names":[],"mappings":"AAGA;;;GAGG;AACH,eAAO,MAAM,WAAW;WACf,MAAM;cACH,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI;kBACf,MAAM;YACZ,MAAM;gBACF,MAAM;oDAqElB,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useEffect, useRef, useState } from "react";
3
+ import { Search, X } from "lucide-react";
4
+ /**
5
+ * Zoekveld exact als Verkooporders: h-9, sm:w-80, breder bij focus.
6
+ * Filtert tijdens typen (250 ms), wis-kruisje, Esc wist. "/" regelt de pagina.
7
+ */
8
+ export const SearchInput = forwardRef(function SearchInput({ value, onChange, placeholder = "Zoeken", delay = 250, ariaLabel }, ref) {
9
+ const [local, setLocal] = useState(value);
10
+ const timer = useRef(null);
11
+ useEffect(() => { setLocal(value); }, [value]);
12
+ const push = (v, now = false) => {
13
+ setLocal(v);
14
+ if (timer.current)
15
+ clearTimeout(timer.current);
16
+ if (now)
17
+ onChange(v);
18
+ else
19
+ timer.current = setTimeout(() => onChange(v), delay);
20
+ };
21
+ useEffect(() => () => { if (timer.current)
22
+ clearTimeout(timer.current); }, []);
23
+ return (_jsxs("div", { className: "group relative flex h-10 sm:h-9 w-full items-center overflow-hidden rounded-md border border-border bg-card " +
24
+ "transition-[box-shadow,border-color,width] duration-[180ms] ease-out motion-reduce:transition-none " +
25
+ "focus-within:border-primary focus-within:ring-[3px] focus-within:ring-ring/20 " +
26
+ "sm:w-80 sm:focus-within:w-[400px]", children: [_jsx(Search, { className: "ml-3 h-4 w-4 shrink-0 text-muted-foreground transition-colors duration-[120ms] group-focus-within:text-primary", "aria-hidden": true }), _jsx("input", { ref: ref, type: "search", placeholder: placeholder, "aria-label": ariaLabel ?? "Zoeken", value: local, maxLength: 100, autoComplete: "off", spellCheck: false, onChange: (e) => push(e.target.value), onKeyDown: (e) => {
27
+ if (e.key === "Escape") {
28
+ e.preventDefault();
29
+ if (local)
30
+ push("", true);
31
+ else
32
+ e.target.blur();
33
+ }
34
+ }, className: "h-full min-w-0 flex-1 appearance-none border-0 bg-transparent px-2 text-base sm:text-sm text-foreground outline-none ring-0 shadow-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 " +
35
+ "placeholder:text-muted-foreground [&::-webkit-search-cancel-button]:hidden" }), _jsxs("span", { className: "relative mr-1.5 flex h-7 w-7 shrink-0 items-center justify-center", children: [_jsx("kbd", { className: "pointer-events-none absolute hidden rounded border border-border px-1.5 font-mono text-[11px] leading-5 text-muted-foreground transition-opacity duration-[120ms] md:inline-block " +
36
+ (local ? "opacity-0" : "opacity-100 group-focus-within:opacity-0"), "aria-hidden": true, children: "/" }), _jsx("button", { type: "button", onClick: () => push("", true), tabIndex: local ? 0 : -1, "aria-label": "Zoekopdracht wissen", className: "absolute inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground transition-[opacity,transform,background-color] duration-[120ms] ease-out hover:bg-muted hover:text-foreground " +
37
+ (local ? "scale-100 opacity-100" : "pointer-events-none scale-90 opacity-0"), children: _jsx(X, { className: "h-4 w-4" }) })] })] }));
38
+ });
@@ -0,0 +1,7 @@
1
+ /** Overzicht van de sneltoetsen, exact als Verkooporders (opent met "?"). */
2
+ export declare function ShortcutHelp({ open, onOpenChange, items }: {
3
+ open: boolean;
4
+ onOpenChange: (o: boolean) => void;
5
+ items: Array<[string[], string]>;
6
+ }): import("react").JSX.Element;
7
+ //# sourceMappingURL=ShortcutHelp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ShortcutHelp.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/ShortcutHelp.tsx"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,+BAwBlJ"}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@flowselections/core";
3
+ /** Overzicht van de sneltoetsen, exact als Verkooporders (opent met "?"). */
4
+ export function ShortcutHelp({ open, onOpenChange, items }) {
5
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-w-sm", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Sneltoetsen" }) }), _jsx("ul", { className: "divide-y divide-border text-sm", children: items.map(([keys, label]) => (_jsxs("li", { className: "flex items-center justify-between py-2", children: [_jsx("span", { className: "text-foreground", children: label }), _jsx("span", { className: "flex gap-1", children: keys.map((k) => (_jsx("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-[11px] text-muted-foreground", children: k }, k))) })] }, label))) })] }) }));
6
+ }
@@ -0,0 +1,8 @@
1
+ export type StatusTone = "done" | "open" | "busy" | "error" | "rejected";
2
+ /** Klein rond statusicoon met tooltip. Geen gekleurd vlak eromheen. */
3
+ export declare function StatusIcon({ tone, label, detail }: {
4
+ tone: StatusTone;
5
+ label: string;
6
+ detail?: string;
7
+ }): import("react").JSX.Element;
8
+ //# sourceMappingURL=StatusIcon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusIcon.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/StatusIcon.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAWzE,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,+BAevG"}
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AlertCircle, CheckCircle2, Circle, Clock, XCircle } from "lucide-react";
3
+ import { HoverTip } from "./HoverTip.js";
4
+ const ICON = { done: CheckCircle2, open: Circle, busy: Clock, error: AlertCircle, rejected: XCircle };
5
+ const TONE = {
6
+ done: "text-primary",
7
+ open: "text-muted-foreground",
8
+ busy: "text-warning",
9
+ error: "text-destructive",
10
+ rejected: "text-muted-foreground",
11
+ };
12
+ /** Klein rond statusicoon met tooltip. Geen gekleurd vlak eromheen. */
13
+ export function StatusIcon({ tone, label, detail }) {
14
+ const Icon = ICON[tone];
15
+ return (_jsx(HoverTip, { label: label, content: _jsxs("div", { children: [_jsx("div", { className: "text-[13px] font-semibold text-foreground", children: label }), detail && _jsx("div", { className: "mt-1 text-muted-foreground", children: detail })] }), children: _jsx(Icon, { className: "h-4 w-4 " + TONE[tone], "aria-hidden": true }) }));
16
+ }
@@ -0,0 +1,6 @@
1
+ /** Skelet in de vorm van de echte tabel (desktop) of kaarten (mobiel). Breedtes in px. */
2
+ export declare function TableSkeleton({ columns, rows }: {
3
+ columns: number[];
4
+ rows?: number;
5
+ }): import("react").JSX.Element;
6
+ //# sourceMappingURL=TableSkeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableSkeleton.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/TableSkeleton.tsx"],"names":[],"mappings":"AAEA,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,IAAQ,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,+BA2BxF"}
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Skeleton } from "@flowselections/core";
3
+ /** Skelet in de vorm van de echte tabel (desktop) of kaarten (mobiel). Breedtes in px. */
4
+ export function TableSkeleton({ columns, rows = 8 }) {
5
+ return (_jsxs("div", { className: "min-h-0 flex-1 overflow-hidden rounded-xl border border-border bg-card shadow-sm", "aria-busy": "true", "aria-label": "Laden", children: [_jsx("div", { className: "hidden md:block", children: _jsxs("table", { className: "w-full min-w-[900px] table-fixed", children: [_jsx("colgroup", { children: columns.map((w, i) => _jsx("col", { style: { width: `${w}px` } }, i)) }), _jsx("thead", { children: _jsx("tr", { className: "h-10 border-b border-border", children: columns.map((_, i) => _jsx("th", { className: "px-4", children: _jsx(Skeleton, { className: "h-3 w-16" }) }, i)) }) }), _jsx("tbody", { children: Array.from({ length: rows }).map((_, r) => (_jsx("tr", { style: { height: 60 }, className: "border-b border-border last:border-0", children: columns.map((_, i) => _jsx("td", { className: "px-4 py-0", children: _jsx(Skeleton, { className: i === 1 ? "h-4 w-4/5" : "h-4 w-3/5" }) }, i)) }, r))) })] }) }), _jsx("div", { className: "divide-y divide-border md:hidden", children: Array.from({ length: Math.min(rows, 5) }).map((_, r) => (_jsxs("div", { className: "space-y-2 p-4", children: [_jsx(Skeleton, { className: "h-4 w-1/2" }), _jsx(Skeleton, { className: "h-3 w-4/5" }), _jsx(Skeleton, { className: "h-3 w-1/3" })] }, r))) })] }));
6
+ }
@@ -0,0 +1,13 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * Werkbalk exact als Verkooporders: zoekveld, direct rechts "Filters" (opent het
4
+ * zijpaneel), chips op dezelfde regel; uiterst rechts alleen "Kolommen".
5
+ */
6
+ export declare function Toolbar({ children, onOpenFilters, filterCount, chips, right, }: {
7
+ children: ReactNode;
8
+ onOpenFilters?: () => void;
9
+ filterCount?: number;
10
+ chips?: ReactNode;
11
+ right?: ReactNode;
12
+ }): import("react").JSX.Element;
13
+ //# sourceMappingURL=Toolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/Toolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC;;;GAGG;AACH,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,aAAa,EACb,WAAe,EACf,KAAK,EACL,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,+BAmBA"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SlidersHorizontal } from "lucide-react";
3
+ import { Button } from "@flowselections/core";
4
+ /**
5
+ * Werkbalk exact als Verkooporders: zoekveld, direct rechts "Filters" (opent het
6
+ * zijpaneel), chips op dezelfde regel; uiterst rechts alleen "Kolommen".
7
+ */
8
+ export function Toolbar({ children, onOpenFilters, filterCount = 0, chips, right, }) {
9
+ return (_jsxs("div", { className: "shrink-0 flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "min-w-0 flex-1 basis-[calc(100%-3.5rem)] sm:basis-auto sm:flex-none", children: children }), onOpenFilters && (_jsxs(Button, { variant: "outline", size: "sm", onClick: onOpenFilters, "aria-label": "Filters", className: "relative h-10 px-3 sm:h-9", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4 sm:mr-2" }), _jsx("span", { className: "hidden sm:inline", children: "Filters" }), filterCount > 0 && (_jsx("span", { className: "ml-2 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground", children: filterCount }))] })), chips, right && _jsx("div", { className: "ml-auto flex items-center gap-2", children: right })] }));
10
+ }
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from "react";
2
+ /** "Bijgewerkt hh:mm" met klokicoon (12 px); extra gegevens in de infoballon. */
3
+ export declare function UpdatedAt({ at, stale, info }: {
4
+ at: number | string | Date | null | undefined;
5
+ stale?: boolean;
6
+ info?: ReactNode;
7
+ }): import("react").JSX.Element | null;
8
+ //# sourceMappingURL=UpdatedAt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpdatedAt.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/UpdatedAt.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,sCAmBlI"}
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Clock } from "lucide-react";
3
+ import { HoverTip } from "./HoverTip.js";
4
+ /** "Bijgewerkt hh:mm" met klokicoon (12 px); extra gegevens in de infoballon. */
5
+ export function UpdatedAt({ at, stale, info }) {
6
+ const d = at ? new Date(at) : null;
7
+ const time = d && !Number.isNaN(d.getTime())
8
+ ? d.toLocaleTimeString("nl-NL", { hour: "2-digit", minute: "2-digit" })
9
+ : null;
10
+ if (!time)
11
+ return null;
12
+ const label = stale ? `Niet bijgewerkt sinds ${time}` : `Bijgewerkt ${time}`;
13
+ const body = (_jsxs("span", { className: "inline-flex items-center gap-1 tabular-nums " + (stale ? "text-warning" : ""), children: [_jsx(Clock, { className: "h-3 w-3", "aria-hidden": true }), label] }));
14
+ if (!info)
15
+ return body;
16
+ return (_jsx(HoverTip, { label: label, side: "bottom", align: "start", content: _jsx("div", { className: "space-y-1 text-xs text-muted-foreground", children: info }), children: body }));
17
+ }
@@ -0,0 +1,18 @@
1
+ /** True als de gebruiker in een invoerveld typt; dan werken sneltoetsen niet. */
2
+ export declare function isTypingTarget(t: EventTarget | null): boolean;
3
+ /**
4
+ * Sneltoetsen zoals Verkooporders: / zoeken, J/K rij, Enter openen, Esc wissen,
5
+ * ? overzicht, N nieuw (alleen als onNew is meegegeven). Nooit tijdens typen of
6
+ * als een dialoog open staat (`disabled`).
7
+ */
8
+ export declare function useListHotkeys(opts: {
9
+ disabled?: boolean;
10
+ onSearch?: () => void;
11
+ onNext?: () => void;
12
+ onPrev?: () => void;
13
+ onOpen?: () => void;
14
+ onEscape?: () => void;
15
+ onHelp?: () => void;
16
+ onNew?: () => void;
17
+ }): void;
18
+ //# sourceMappingURL=useListHotkeys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useListHotkeys.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/useListHotkeys.ts"],"names":[],"mappings":"AAEA,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,CAAC,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAK7D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CACpB,QAqBA"}
@@ -0,0 +1,48 @@
1
+ import { useEffect, useRef } from "react";
2
+ /** True als de gebruiker in een invoerveld typt; dan werken sneltoetsen niet. */
3
+ export function isTypingTarget(t) {
4
+ const el = t;
5
+ if (!el || !el.tagName)
6
+ return false;
7
+ const tag = el.tagName.toLowerCase();
8
+ return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable === true;
9
+ }
10
+ /**
11
+ * Sneltoetsen zoals Verkooporders: / zoeken, J/K rij, Enter openen, Esc wissen,
12
+ * ? overzicht, N nieuw (alleen als onNew is meegegeven). Nooit tijdens typen of
13
+ * als een dialoog open staat (`disabled`).
14
+ */
15
+ export function useListHotkeys(opts) {
16
+ const ref = useRef(opts);
17
+ ref.current = opts;
18
+ useEffect(() => {
19
+ const h = (e) => {
20
+ const o = ref.current;
21
+ if (o.disabled || e.metaKey || e.ctrlKey || e.altKey)
22
+ return;
23
+ if (isTypingTarget(e.target))
24
+ return;
25
+ const k = e.key;
26
+ const run = (fn) => { if (fn) {
27
+ e.preventDefault();
28
+ fn();
29
+ } };
30
+ if (k === "/")
31
+ run(o.onSearch);
32
+ else if (k === "j" || k === "J")
33
+ run(o.onNext);
34
+ else if (k === "k" || k === "K")
35
+ run(o.onPrev);
36
+ else if (k === "Enter")
37
+ run(o.onOpen);
38
+ else if (k === "Escape")
39
+ run(o.onEscape);
40
+ else if (k === "?")
41
+ run(o.onHelp);
42
+ else if (k === "n" || k === "N")
43
+ run(o.onNew);
44
+ };
45
+ window.addEventListener("keydown", h);
46
+ return () => window.removeEventListener("keydown", h);
47
+ }, []);
48
+ }
@@ -1,2 +1,3 @@
1
- export declare function MailboxAiPromptCard(): import("react").JSX.Element;
1
+ /** Beheeronderdeel: alleen zichtbaar voor admin en superadmin. */
2
+ export declare function MailboxAiPromptCard(): import("react").JSX.Element | null;
2
3
  //# sourceMappingURL=MailboxAiPromptCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MailboxAiPromptCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/MailboxAiPromptCard.tsx"],"names":[],"mappings":"AAUA,wBAAgB,mBAAmB,gCA2FlC"}
1
+ {"version":3,"file":"MailboxAiPromptCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/MailboxAiPromptCard.tsx"],"names":[],"mappings":"AAwGA,kEAAkE;AAClE,wBAAgB,mBAAmB,uCAIlC"}
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useIsSuperadmin } from "@flowselections/core";
2
3
  import { useEffect, useState } from "react";
3
4
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4
5
  import { useServerFn } from "@tanstack/react-start";
5
6
  import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Label, Textarea, Badge, toast, } from "@flowselections/core";
6
7
  import { getMailboxAiSettings, saveMailboxAiSettings } from "../../lib/mailbox-ai-settings.functions.js";
7
8
  import { REQUIRED_FIELD_CHOICES, DEFAULT_REQUIRED_FIELDS } from "../../lib/mailbox-ai-prompt.js";
8
- export function MailboxAiPromptCard() {
9
+ function MailboxAiPromptCardInner() {
9
10
  const qc = useQueryClient();
10
11
  const fetchSettings = useServerFn(getMailboxAiSettings);
11
12
  const save = useServerFn(saveMailboxAiSettings);
@@ -37,3 +38,10 @@ export function MailboxAiPromptCard() {
37
38
  setRequired(DEFAULT_REQUIRED_FIELDS);
38
39
  }, children: "Standaard herstellen" })] })] })] }));
39
40
  }
41
+ /** Beheeronderdeel: alleen zichtbaar voor admin en superadmin. */
42
+ export function MailboxAiPromptCard() {
43
+ const { isAdmin, isLoading } = useIsSuperadmin();
44
+ if (isLoading || !isAdmin)
45
+ return null;
46
+ return _jsx(MailboxAiPromptCardInner, {});
47
+ }