@flowselections/mailbox-orders 1.0.55 → 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 (68) 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 +18 -6
  4. package/dist-lib/components/MailboxPageFrame.d.ts +11 -5
  5. package/dist-lib/components/MailboxPageFrame.d.ts.map +1 -1
  6. package/dist-lib/components/MailboxPageFrame.js +24 -16
  7. package/dist-lib/components/MailboxProcessedPage.d.ts.map +1 -1
  8. package/dist-lib/components/MailboxProcessedPage.js +30 -21
  9. package/dist-lib/components/MailboxProposalsPage.d.ts.map +1 -1
  10. package/dist-lib/components/MailboxProposalsPage.js +36 -28
  11. package/dist-lib/components/MailboxSkippedPage.d.ts.map +1 -1
  12. package/dist-lib/components/MailboxSkippedPage.js +34 -22
  13. package/dist-lib/components/MailboxStatusLine.d.ts +7 -3
  14. package/dist-lib/components/MailboxStatusLine.d.ts.map +1 -1
  15. package/dist-lib/components/MailboxStatusLine.js +42 -19
  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 +12 -6
  20. package/dist-lib/components/fs-ui/DataTable.d.ts.map +1 -1
  21. package/dist-lib/components/fs-ui/DataTable.js +26 -19
  22. package/dist-lib/components/fs-ui/FilterChips.d.ts +1 -1
  23. package/dist-lib/components/fs-ui/FilterChips.d.ts.map +1 -1
  24. package/dist-lib/components/fs-ui/FilterChips.js +3 -3
  25. package/dist-lib/components/fs-ui/FilterPanel.d.ts +32 -0
  26. package/dist-lib/components/fs-ui/FilterPanel.d.ts.map +1 -0
  27. package/dist-lib/components/fs-ui/FilterPanel.js +20 -0
  28. package/dist-lib/components/fs-ui/InlineAlert.d.ts +2 -2
  29. package/dist-lib/components/fs-ui/InlineAlert.d.ts.map +1 -1
  30. package/dist-lib/components/fs-ui/InlineAlert.js +4 -12
  31. package/dist-lib/components/fs-ui/KpiRow.js +1 -1
  32. package/dist-lib/components/fs-ui/NoticeBar.d.ts +18 -0
  33. package/dist-lib/components/fs-ui/NoticeBar.d.ts.map +1 -0
  34. package/dist-lib/components/fs-ui/NoticeBar.js +24 -0
  35. package/dist-lib/components/fs-ui/PageHeader.d.ts +1 -1
  36. package/dist-lib/components/fs-ui/PageHeader.d.ts.map +1 -1
  37. package/dist-lib/components/fs-ui/PageHeader.js +2 -2
  38. package/dist-lib/components/fs-ui/PillTabs.d.ts +3 -2
  39. package/dist-lib/components/fs-ui/PillTabs.d.ts.map +1 -1
  40. package/dist-lib/components/fs-ui/PillTabs.js +26 -11
  41. package/dist-lib/components/fs-ui/RowMenu.d.ts +13 -0
  42. package/dist-lib/components/fs-ui/RowMenu.d.ts.map +1 -0
  43. package/dist-lib/components/fs-ui/RowMenu.js +10 -0
  44. package/dist-lib/components/fs-ui/SearchInput.d.ts +2 -2
  45. package/dist-lib/components/fs-ui/SearchInput.d.ts.map +1 -1
  46. package/dist-lib/components/fs-ui/SearchInput.js +11 -6
  47. package/dist-lib/components/fs-ui/ShortcutHelp.d.ts +3 -3
  48. package/dist-lib/components/fs-ui/ShortcutHelp.d.ts.map +1 -1
  49. package/dist-lib/components/fs-ui/ShortcutHelp.js +2 -2
  50. package/dist-lib/components/fs-ui/TableSkeleton.d.ts +2 -2
  51. package/dist-lib/components/fs-ui/TableSkeleton.d.ts.map +1 -1
  52. package/dist-lib/components/fs-ui/TableSkeleton.js +2 -2
  53. package/dist-lib/components/fs-ui/Toolbar.d.ts +8 -3
  54. package/dist-lib/components/fs-ui/Toolbar.d.ts.map +1 -1
  55. package/dist-lib/components/fs-ui/Toolbar.js +8 -3
  56. package/dist-lib/components/fs-ui/UpdatedAt.d.ts +5 -4
  57. package/dist-lib/components/fs-ui/UpdatedAt.d.ts.map +1 -1
  58. package/dist-lib/components/fs-ui/UpdatedAt.js +12 -4
  59. package/dist-lib/lib/format-date.d.ts +5 -0
  60. package/dist-lib/lib/format-date.d.ts.map +1 -0
  61. package/dist-lib/lib/format-date.js +30 -0
  62. package/dist-lib/lib/labels.d.ts +15 -0
  63. package/dist-lib/lib/labels.d.ts.map +1 -0
  64. package/dist-lib/lib/labels.js +57 -0
  65. package/dist-lib/lib/mailbox-list.functions.d.ts.map +1 -1
  66. package/dist-lib/lib/mailbox-list.functions.js +0 -2
  67. package/dist-lib/lib/mailbox.functions.js +3 -3
  68. package/package.json +1 -1
@@ -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
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
- /** Paginakop: titel + bijgewerkt-tijd links, acties rechts. Alleen weergave. */
2
+ /** Paginakop exact als Verkooporders: h1, daaronder alleen "Bijgewerkt", acties rechts. */
3
3
  export declare function PageHeader({ title, meta, actions, }: {
4
4
  title: string;
5
5
  meta?: ReactNode;
@@ -1 +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,gFAAgF;AAChF,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,+BAUA"}
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"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /** Paginakop: titel + bijgewerkt-tijd links, acties rechts. Alleen weergave. */
2
+ /** Paginakop exact als Verkooporders: h1, daaronder alleen "Bijgewerkt", acties rechts. */
3
3
  export function PageHeader({ title, meta, actions, }) {
4
- return (_jsxs("div", { className: "flex shrink-0 flex-wrap items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "truncate text-xl font-bold leading-tight text-foreground sm:text-2xl", children: title }), meta && _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: meta })] }), actions && _jsx("div", { className: "flex flex-wrap items-center gap-2", children: 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
5
  }
@@ -1,11 +1,12 @@
1
- import type { ReactNode } from "react";
1
+ import { type ReactNode } from "react";
2
2
  export interface PillTab {
3
3
  key: string;
4
4
  label: string;
5
5
  icon?: ReactNode;
6
6
  count?: number | null;
7
+ hint?: string;
7
8
  }
8
- /** Tabbladen als pillen met icoon, label en teller. Veegbaar op mobiel. */
9
+ /** Tabbladen exact als StatusTabs in Verkooporders: één regel, veegbaar met vervaging. */
9
10
  export declare function PillTabs({ tabs, active, onSelect, ariaLabel, }: {
10
11
  tabs: PillTab[];
11
12
  active: string;
@@ -1 +1 @@
1
- {"version":3,"file":"PillTabs.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/PillTabs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,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;CACvB;AAED,2EAA2E;AAC3E,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,+BA6CA"}
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"}
@@ -1,14 +1,29 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /** Tabbladen als pillen met icoon, label en teller. Veegbaar op mobiel. */
2
+ import { useLayoutEffect, useRef } from "react";
3
+ /** Tabbladen exact als StatusTabs in Verkooporders: één regel, veegbaar met vervaging. */
3
4
  export function PillTabs({ tabs, active, onSelect, ariaLabel, }) {
4
- return (_jsxs("div", { className: "relative shrink-0", children: [_jsx("div", { role: "tablist", "aria-label": ariaLabel, className: "-mx-4 flex gap-2 overflow-x-auto px-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden sm:mx-0 sm:px-0", children: tabs.map((t) => {
5
- const isActive = t.key === active;
6
- const empty = (t.count ?? null) === 0;
7
- return (_jsxs("button", { type: "button", role: "tab", "aria-selected": isActive, onClick: () => onSelect(t.key), className: "inline-flex h-9 shrink-0 items-center gap-2 rounded-lg border px-3 text-sm font-medium transition-colors duration-150 ease-out motion-reduce:transition-none " +
8
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:h-4 [&_svg]:w-4 " +
9
- (isActive
10
- ? "border-primary/40 bg-primary/10 text-primary"
11
- : "border-transparent text-foreground hover:bg-muted " + (empty ? "text-muted-foreground" : "")), children: [t.icon, _jsx("span", { children: t.label }), t.count != null && (_jsx("span", { className: "inline-flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-xs font-semibold tabular-nums " +
12
- (isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"), children: t.count }))] }, t.key));
13
- }) }), _jsx("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-8 bg-gradient-to-l from-background sm:hidden", "aria-hidden": true })] }));
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
+ }) }) }));
14
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
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Zoekveld: filtert tijdens typen (250 ms), wis-kruisje, Esc wist.
3
- * De sneltoets "/" wordt door de pagina geregeld (useListHotkeys).
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
4
  */
5
5
  export declare const SearchInput: import("react").ForwardRefExoticComponent<{
6
6
  value: string;
@@ -1 +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;oDAgDlB,CAAC"}
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"}
@@ -2,10 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, useEffect, useRef, useState } from "react";
3
3
  import { Search, X } from "lucide-react";
4
4
  /**
5
- * Zoekveld: filtert tijdens typen (250 ms), wis-kruisje, Esc wist.
6
- * De sneltoets "/" wordt door de pagina geregeld (useListHotkeys).
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
7
  */
8
- export const SearchInput = forwardRef(function SearchInput({ value, onChange, placeholder = "Zoeken…", delay = 250, ariaLabel }, ref) {
8
+ export const SearchInput = forwardRef(function SearchInput({ value, onChange, placeholder = "Zoeken", delay = 250, ariaLabel }, ref) {
9
9
  const [local, setLocal] = useState(value);
10
10
  const timer = useRef(null);
11
11
  useEffect(() => { setLocal(value); }, [value]);
@@ -20,7 +20,10 @@ export const SearchInput = forwardRef(function SearchInput({ value, onChange, pl
20
20
  };
21
21
  useEffect(() => () => { if (timer.current)
22
22
  clearTimeout(timer.current); }, []);
23
- return (_jsxs("div", { className: "relative w-full max-w-md", children: [_jsx(Search, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground", "aria-hidden": true }), _jsx("input", { ref: ref, type: "search", value: local, "aria-label": ariaLabel ?? placeholder, placeholder: placeholder, onChange: (e) => push(e.target.value), onKeyDown: (e) => {
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) => {
24
27
  if (e.key === "Escape") {
25
28
  e.preventDefault();
26
29
  if (local)
@@ -28,6 +31,8 @@ export const SearchInput = forwardRef(function SearchInput({ value, onChange, pl
28
31
  else
29
32
  e.target.blur();
30
33
  }
31
- }, className: "h-10 w-full rounded-lg border border-input bg-background pl-9 pr-16 text-base text-foreground placeholder:text-muted-foreground sm:h-9 sm:text-sm " +
32
- "transition-shadow duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-search-cancel-button]:hidden" }), local ? (_jsx("button", { type: "button", "aria-label": "Zoekopdracht wissen", onClick: () => push("", true), className: "absolute right-2 top-1/2 inline-flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded text-muted-foreground hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(X, { className: "h-4 w-4" }) })) : (_jsx("kbd", { className: "pointer-events-none absolute right-2 top-1/2 hidden -translate-y-1/2 rounded-sm border border-border px-1.5 text-[11px] text-muted-foreground sm:inline-block", children: "/" }))] }));
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" }) })] })] }));
33
38
  });
@@ -1,7 +1,7 @@
1
- /** Overzicht van de sneltoetsen (opent met "?"). */
1
+ /** Overzicht van de sneltoetsen, exact als Verkooporders (opent met "?"). */
2
2
  export declare function ShortcutHelp({ open, onOpenChange, items }: {
3
3
  open: boolean;
4
- onOpenChange: (v: boolean) => void;
5
- items: Array<[string, string]>;
4
+ onOpenChange: (o: boolean) => void;
5
+ items: Array<[string[], string]>;
6
6
  }): import("react").JSX.Element;
7
7
  //# sourceMappingURL=ShortcutHelp.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShortcutHelp.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/ShortcutHelp.tsx"],"names":[],"mappings":"AAEA,oDAAoD;AACpD,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,MAAM,CAAC,CAAC,CAAA;CAAE,+BAgBhJ"}
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"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@flowselections/core";
3
- /** Overzicht van de sneltoetsen (opent met "?"). */
3
+ /** Overzicht van de sneltoetsen, exact als Verkooporders (opent met "?"). */
4
4
  export function ShortcutHelp({ open, onOpenChange, items }) {
5
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-w-sm rounded-lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Sneltoetsen" }) }), _jsx("ul", { className: "space-y-2 text-sm", children: items.map(([k, label]) => (_jsxs("li", { className: "flex items-center justify-between gap-4", children: [_jsx("span", { className: "text-muted-foreground", children: label }), _jsx("kbd", { className: "rounded-sm border border-border px-1.5 py-0.5 text-xs", children: k })] }, k))) })] }) }));
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
6
  }
@@ -1,6 +1,6 @@
1
- /** Skelet in de vorm van de echte tabel (desktop) of kaarten (mobiel). */
1
+ /** Skelet in de vorm van de echte tabel (desktop) of kaarten (mobiel). Breedtes in px. */
2
2
  export declare function TableSkeleton({ columns, rows }: {
3
- columns: string[];
3
+ columns: number[];
4
4
  rows?: number;
5
5
  }): import("react").JSX.Element;
6
6
  //# sourceMappingURL=TableSkeleton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TableSkeleton.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/TableSkeleton.tsx"],"names":[],"mappings":"AAEA,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,IAAQ,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,+BAwBxF"}
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"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Skeleton } from "@flowselections/core";
3
- /** Skelet in de vorm van de echte tabel (desktop) of kaarten (mobiel). */
3
+ /** Skelet in de vorm van de echte tabel (desktop) of kaarten (mobiel). Breedtes in px. */
4
4
  export function TableSkeleton({ columns, rows = 8 }) {
5
- return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-border bg-card shadow-sm", "aria-busy": "true", "aria-label": "Laden", children: [_jsxs("div", { className: "hidden md:block", children: [_jsx("div", { className: "grid h-10 items-center gap-4 border-b border-border px-4", style: { gridTemplateColumns: columns.join(" ") }, children: columns.map((_, i) => _jsx(Skeleton, { className: "h-3 w-16" }, i)) }), Array.from({ length: rows }).map((_, r) => (_jsx("div", { className: "grid h-[60px] items-center gap-4 border-b border-border px-4 last:border-0", style: { gridTemplateColumns: columns.join(" ") }, children: columns.map((_, i) => _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))) })] }));
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
6
  }
@@ -1,8 +1,13 @@
1
1
  import type { ReactNode } from "react";
2
- /** Werkbalk: zoeken + filters links, extra acties rechts; chips eronder. */
3
- export declare function Toolbar({ children, right, chips }: {
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, }: {
4
7
  children: ReactNode;
5
- right?: ReactNode;
8
+ onOpenFilters?: () => void;
9
+ filterCount?: number;
6
10
  chips?: ReactNode;
11
+ right?: ReactNode;
7
12
  }): import("react").JSX.Element;
8
13
  //# sourceMappingURL=Toolbar.d.ts.map
@@ -1 +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;AAEvC,4EAA4E;AAC5E,wBAAgB,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,+BAUhH"}
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"}
@@ -1,5 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /** Werkbalk: zoeken + filters links, extra acties rechts; chips eronder. */
3
- export function Toolbar({ children, right, chips }) {
4
- return (_jsxs("div", { className: "flex shrink-0 flex-col gap-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: children }), right && _jsx("div", { className: "flex items-center gap-2", children: right })] }), chips] }));
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 })] }));
5
10
  }
@@ -1,7 +1,8 @@
1
1
  import type { ReactNode } from "react";
2
- /** "Bijgewerkt hh:mm" met optionele extra tekst. */
3
- export declare function UpdatedAt({ at, extra }: {
2
+ /** "Bijgewerkt hh:mm" met klokicoon (12 px); extra gegevens in de infoballon. */
3
+ export declare function UpdatedAt({ at, stale, info }: {
4
4
  at: number | string | Date | null | undefined;
5
- extra?: ReactNode;
6
- }): import("react").JSX.Element;
5
+ stale?: boolean;
6
+ info?: ReactNode;
7
+ }): import("react").JSX.Element | null;
7
8
  //# sourceMappingURL=UpdatedAt.d.ts.map
@@ -1 +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;AAEvC,oDAAoD;AACpD,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,+BAW5G"}
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"}
@@ -1,9 +1,17 @@
1
- import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- /** "Bijgewerkt hh:mm" met optionele extra tekst. */
3
- export function UpdatedAt({ at, extra }) {
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 }) {
4
6
  const d = at ? new Date(at) : null;
5
7
  const time = d && !Number.isNaN(d.getTime())
6
8
  ? d.toLocaleTimeString("nl-NL", { hour: "2-digit", minute: "2-digit" })
7
9
  : null;
8
- return (_jsxs("span", { className: "tabular-nums", children: [time ? `Bijgewerkt ${time}` : "Nog niet bijgewerkt", extra ? _jsxs(_Fragment, { children: [" \u00B7 ", extra] }) : 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 }));
9
17
  }
@@ -0,0 +1,5 @@
1
+ export declare function fmtDay(v: unknown, now?: Date): string;
2
+ export declare function fmtDayTime(v: unknown, now?: Date): string;
3
+ /** Bedrag als "€ 1.234,56". */
4
+ export declare function fmtEuro(n: number): string;
5
+ //# sourceMappingURL=format-date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-date.d.ts","sourceRoot":"","sources":["../../src/lib/format-date.ts"],"names":[],"mappings":"AAaA,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAKjE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAMrE;AAED,+BAA+B;AAC/B,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC"}
@@ -0,0 +1,30 @@
1
+ // Datums zoals in Verkoop: "di 3 nov", met tijd "di 3 nov 08:00".
2
+ // Alleen een ander jaar krijgt het jaartal erbij.
3
+ const DAYS = ["zo", "ma", "di", "wo", "do", "vr", "za"];
4
+ const MONTHS = ["jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"];
5
+ function toDate(v) {
6
+ if (v == null || v === "")
7
+ return null;
8
+ const s = String(v);
9
+ const d = /^\d{4}-\d{2}-\d{2}$/.test(s) ? new Date(`${s}T12:00:00`) : new Date(s);
10
+ return Number.isNaN(d.getTime()) ? null : d;
11
+ }
12
+ export function fmtDay(v, now = new Date()) {
13
+ const d = toDate(v);
14
+ if (!d)
15
+ return "–";
16
+ const base = `${DAYS[d.getDay()]} ${d.getDate()} ${MONTHS[d.getMonth()]}`;
17
+ return d.getFullYear() === now.getFullYear() ? base : `${base} ${d.getFullYear()}`;
18
+ }
19
+ export function fmtDayTime(v, now = new Date()) {
20
+ const d = toDate(v);
21
+ if (!d)
22
+ return "–";
23
+ const hh = String(d.getHours()).padStart(2, "0");
24
+ const mm = String(d.getMinutes()).padStart(2, "0");
25
+ return `${fmtDay(d, now)} ${hh}:${mm}`;
26
+ }
27
+ /** Bedrag als "€ 1.234,56". */
28
+ export function fmtEuro(n) {
29
+ return "€ " + (Number(n) || 0).toLocaleString("nl-NL", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
30
+ }
@@ -0,0 +1,15 @@
1
+ export declare const PROPOSAL_STATUS: Record<string, string>;
2
+ export declare const MESSAGE_STATUS: Record<string, string>;
3
+ export declare const FILTER_OUTCOME: Record<string, string>;
4
+ export declare const ANALYSIS_STATE: Record<string, string>;
5
+ export declare const READINESS: Record<string, string>;
6
+ export declare const ALL_LABEL_TABLES: {
7
+ PROPOSAL_STATUS: Record<string, string>;
8
+ MESSAGE_STATUS: Record<string, string>;
9
+ FILTER_OUTCOME: Record<string, string>;
10
+ ANALYSIS_STATE: Record<string, string>;
11
+ READINESS: Record<string, string>;
12
+ };
13
+ /** Vertaal een databasewaarde. Onbekend → "Overig"; de ruwe waarde gaat alleen naar console.warn. */
14
+ export declare function label(table: Record<string, string>, raw: unknown, fallback?: string): string;
15
+ //# sourceMappingURL=labels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/lib/labels.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOlD,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAajD,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMjD,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKjD,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAG5C,CAAC;AAEF,eAAO,MAAM,gBAAgB;;;;;;CAAiF,CAAC;AAI/G,qGAAqG;AACrG,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,SAAW,GAAG,MAAM,CAU9F"}
@@ -0,0 +1,57 @@
1
+ // Eén vertaalbestand: elke status, reden, soort of bron uit de database gaat
2
+ // hierlangs naar Nederlandse tekst. Onbekend → "Overig" (ruwe waarde alleen in console).
3
+ export const PROPOSAL_STATUS = {
4
+ pending: "Te controleren",
5
+ approved: "Goedgekeurd",
6
+ rejected: "Afgewezen",
7
+ converted: "Order aangemaakt",
8
+ blocked_insufficient_stock: "Te weinig voorraad",
9
+ manual_review_load_carrier_disabled: "Belading controleren",
10
+ };
11
+ export const MESSAGE_STATUS = {
12
+ new: "Nieuw",
13
+ parsed: "Uitgelezen",
14
+ pending: "Te controleren",
15
+ skipped: "Overgeslagen",
16
+ waiting: "Wacht op uitlezen",
17
+ converted: "Order aangemaakt",
18
+ rejected: "Afgewezen",
19
+ approved: "Goedgekeurd",
20
+ manual_review_load_carrier_disabled: "Belading controleren",
21
+ blocked_insufficient_stock: "Te weinig voorraad",
22
+ error: "Niet gelukt",
23
+ fout: "Niet gelukt",
24
+ };
25
+ export const FILTER_OUTCOME = {
26
+ analyseren: "Wordt uitgelezen",
27
+ geen_bestelling: "Geen bestelling",
28
+ te_oud: "Te oud",
29
+ overslaan: "Overgeslagen",
30
+ bestelling: "Bestelling",
31
+ };
32
+ export const ANALYSIS_STATE = {
33
+ wacht: "Wacht op uitlezen",
34
+ klaar: "Uitgelezen",
35
+ bezig: "Wordt uitgelezen",
36
+ fout: "Niet gelukt",
37
+ };
38
+ export const READINESS = {
39
+ klaar: "Klaar",
40
+ controleren: "Controleren",
41
+ };
42
+ export const ALL_LABEL_TABLES = { PROPOSAL_STATUS, MESSAGE_STATUS, FILTER_OUTCOME, ANALYSIS_STATE, READINESS };
43
+ const warned = new Set();
44
+ /** Vertaal een databasewaarde. Onbekend → "Overig"; de ruwe waarde gaat alleen naar console.warn. */
45
+ export function label(table, raw, fallback = "Overig") {
46
+ const key = String(raw ?? "").trim();
47
+ if (!key)
48
+ return fallback;
49
+ const hit = table[key];
50
+ if (hit)
51
+ return hit;
52
+ if (!warned.has(key)) {
53
+ warned.add(key);
54
+ console.warn("[labels] onbekende waarde", key);
55
+ }
56
+ return fallback;
57
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"mailbox-list.functions.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox-list.functions.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,QAAA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUb,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAE3D,gEAAgE;AAChE,eAAO,MAAM,iBAAiB;;;;8CAEJ,OAAO;;;;;;;;;;;;;GAqB7B,CAAC;AAEL,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sEAAsE;AACtE,eAAO,MAAM,cAAc;;;;8CAED,OAAO;;wBAiB7B,CAAC"}
1
+ {"version":3,"file":"mailbox-list.functions.d.ts","sourceRoot":"","sources":["../../src/lib/mailbox-list.functions.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUb,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAE3D,gEAAgE;AAChE,eAAO,MAAM,iBAAiB;;;;8CAEJ,OAAO;;;;;;;;;;;;;GAoB7B,CAAC;AAEL,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sEAAsE;AACtE,eAAO,MAAM,cAAc;;;;8CAED,OAAO;;wBAiB7B,CAAC"}
@@ -3,7 +3,6 @@
3
3
  import { createServerFn } from "@tanstack/react-start";
4
4
  import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
5
5
  import { z } from "zod";
6
- import { ensureMailboxDefaults } from "./mailbox.functions.js";
7
6
  const pageInput = z.object({
8
7
  statuses: z.array(z.enum(["pending", "approved", "rejected"])).min(1),
9
8
  search: z.string().max(200).optional().default(""),
@@ -21,7 +20,6 @@ export const listProposalsPage = createServerFn({ method: "GET" })
21
20
  .inputValidator((input) => pageInput.parse(input ?? {}))
22
21
  .handler(async ({ data }) => {
23
22
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
24
- await ensureMailboxDefaults(supabaseAdmin);
25
23
  const { data: res, error } = await supabaseAdmin.rpc("mailbox_proposals_page", {
26
24
  p_statuses: data.statuses,
27
25
  p_search: data.search?.trim() ? data.search.trim() : null,
@@ -141,7 +141,7 @@ export const listProposals = createServerFn({ method: "GET" })
141
141
  .inputValidator((input) => z.object({ status: z.enum(["pending", "approved", "rejected"]).optional() }).parse(input ?? {}))
142
142
  .handler(async ({ data }) => {
143
143
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
144
- await ensureMailboxDefaults(supabaseAdmin);
144
+ // Standaardinstellingen: één keer per paginabezoek via ensureMailboxDefaultsFn.
145
145
  // Alleen de kolommen die de lijstweergave toont. De mailtekst (body_text /
146
146
  // body_html) en de bestelregels zijn bewust NIET opgenomen: die worden pas
147
147
  // opgehaald zodra een voorstel geopend wordt (`getProposalDetail`).
@@ -1169,7 +1169,7 @@ export const listSkippedMessages = createServerFn({ method: "GET" })
1169
1169
  .middleware([requireSupabaseAuth])
1170
1170
  .handler(async () => {
1171
1171
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
1172
- await ensureMailboxDefaults(supabaseAdmin);
1172
+ // Standaardinstellingen: één keer per paginabezoek via ensureMailboxDefaultsFn.
1173
1173
  const { data, error } = await supabaseAdmin
1174
1174
  .from("mailbox_messages")
1175
1175
  .select("id, from_email, from_name, subject, received_at, preview, filter_outcome, filter_reason, analysis_state, analysis_note, mailbox_order_proposals(id)")
@@ -1256,7 +1256,7 @@ export const getMailboxStatus = createServerFn({ method: "GET" })
1256
1256
  .handler(async () => {
1257
1257
  const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
1258
1258
  const admin = supabaseAdmin;
1259
- await ensureMailboxDefaults(admin);
1259
+ // Standaardinstellingen: één keer per paginabezoek via ensureMailboxDefaultsFn.
1260
1260
  const { data: active } = await admin.rpc("email_auth_for_purpose", { _purpose: "orders", _direction: "receive" });
1261
1261
  const activeRows = (Array.isArray(active) ? active : []);
1262
1262
  const activeIds = activeRows.map((r) => r.id).filter(Boolean);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/mailbox-orders",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",