@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
@@ -1,3 +1,4 @@
1
+ import type { Notice } from "./fs-ui/NoticeBar.js";
1
2
  type ActiveMailbox = {
2
3
  connected: null;
3
4
  error?: string;
@@ -12,8 +13,7 @@ type ActiveMailbox = {
12
13
  display_name: string | null;
13
14
  needs_reauth: boolean;
14
15
  } | undefined | null;
15
- export declare function MailboxAuthBanner({ active }: {
16
- active: ActiveMailbox;
17
- }): import("react").JSX.Element | null;
16
+ /** Meldingen over de koppeling van de mailbox (voor de NoticeBar onder de kop). */
17
+ export declare function mailboxAuthNotices(active: ActiveMailbox): Notice[];
18
18
  export {};
19
19
  //# sourceMappingURL=MailboxAuthBanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MailboxAuthBanner.d.ts","sourceRoot":"","sources":["../../src/components/MailboxAuthBanner.tsx"],"names":[],"mappings":"AAGA,KAAK,aAAa,GACd;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACpC;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;CACvB,GACD,SAAS,GACT,IAAI,CAAC;AAET,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,sCAuDtE"}
1
+ {"version":3,"file":"MailboxAuthBanner.d.ts","sourceRoot":"","sources":["../../src/components/MailboxAuthBanner.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,KAAK,aAAa,GACd;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACpC;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;CACvB,GACD,SAAS,GACT,IAAI,CAAC;AAMT,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CAyBlE"}
@@ -1,17 +1,30 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { AlertTriangle } from "lucide-react";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
3
2
  import { bannerKind } from "../lib/mailbox-active.js";
4
- export function MailboxAuthBanner({ active }) {
3
+ const settingsLink = (label) => (_jsx("a", { href: "/e-mail-authenticatie", className: "text-sm font-medium underline underline-offset-2", children: label }));
4
+ /** Meldingen over de koppeling van de mailbox (voor de NoticeBar onder de kop). */
5
+ export function mailboxAuthNotices(active) {
5
6
  const kind = bannerKind(active);
6
7
  if (kind === "unknown") {
7
- return (_jsx("div", { className: "shrink-0 text-xs text-muted-foreground", children: "De status van de mailboxkoppeling kon niet worden opgehaald." }));
8
+ return [{ key: "auth-unknown", tone: "info", title: "De status van de mailbox kon niet worden opgehaald." }];
8
9
  }
9
10
  if (kind === "no_mailbox") {
10
- return (_jsxs("div", { className: "flex items-start gap-3 rounded-md border border-yellow-300 bg-yellow-50 p-3 text-sm text-yellow-900", children: [_jsx(AlertTriangle, { className: "h-4 w-4 mt-0.5 shrink-0" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium", children: "Geen actieve mailbox voor bestellingen" }), _jsxs("div", { className: "text-yellow-800", children: ["Er is nog geen e-mailkoppeling met doeleinde ", _jsx("span", { className: "font-mono", children: "orders / ontvangen" }), " ingesteld."] })] }), _jsx("a", { href: "/e-mail-authenticatie", className: "shrink-0 rounded-md bg-yellow-900 px-3 py-1.5 text-xs font-medium text-yellow-50 hover:bg-yellow-800", children: "Koppeling instellen" })] }));
11
+ return [{
12
+ key: "auth-none",
13
+ tone: "warning",
14
+ title: "Er is nog geen mailbox gekoppeld voor bestellingen.",
15
+ details: "Koppel een mailadres voor het ontvangen van bestellingen bij Instellingen.",
16
+ action: settingsLink("Naar Instellingen"),
17
+ }];
11
18
  }
12
19
  if (kind === "reauth") {
13
20
  const a = active;
14
- return (_jsxs("div", { className: "flex items-start gap-3 rounded-md border border-red-300 bg-red-50 p-3 text-sm text-red-900", children: [_jsx(AlertTriangle, { className: "h-4 w-4 mt-0.5 shrink-0" }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "font-medium", children: ["Koppeling met ", a.provider, " (", a.account_email, ") is verlopen"] }), _jsx("div", { className: "text-red-800", children: "Er komen geen nieuwe bestellingen binnen tot je de koppeling opnieuw autoriseert." })] }), _jsx("a", { href: "/e-mail-authenticatie", className: "shrink-0 rounded-md bg-red-900 px-3 py-1.5 text-xs font-medium text-red-50 hover:bg-red-800", children: "Opnieuw verbinden" })] }));
21
+ return [{
22
+ key: "auth-reauth",
23
+ tone: "error",
24
+ title: `De koppeling met ${a.account_email} is verlopen.`,
25
+ details: "Er komen geen nieuwe bestellingen binnen tot je de koppeling vernieuwt.",
26
+ action: settingsLink("Opnieuw koppelen"),
27
+ }];
15
28
  }
16
- return null;
29
+ return [];
17
30
  }
@@ -0,0 +1,25 @@
1
+ import { type ReactNode } from "react";
2
+ import { type Notice } from "./fs-ui/NoticeBar.js";
3
+ export type MailboxTab = "voorstellen" | "verwerkt" | "overgeslagen";
4
+ export type MailboxKpiKey = "pending" | "today" | "approved_today" | "customer_unknown";
5
+ export declare const browserTz: () => string;
6
+ /** Zet een bewaard filter van de voorstellenlijst vóór het navigeren. */
7
+ export declare function setProposalFilter(key: "period" | "filter", value: unknown): void;
8
+ export declare const SHORTCUTS: Array<[string[], string]>;
9
+ /**
10
+ * Gezamenlijke opbouw van de drie mailboxpagina's, in de vaste volgorde:
11
+ * kop → meldingen → 4 KPI's → tabbladen → (children: werkbalk, tabel, paginering).
12
+ */
13
+ export declare function MailboxPageFrame({ tab, children, onKpi, activeKpi, notices, helpOpen, onHelpOpenChange, }: {
14
+ tab: MailboxTab;
15
+ children: ReactNode;
16
+ /** Pagina handelt een KPI-klik zelf af (bijv. filter zetten); anders navigeren. */
17
+ onKpi?: (key: MailboxKpiKey) => boolean | void;
18
+ /** Tegel waarop de gebruiker zelf gefilterd heeft (alleen die krijgt de rand). */
19
+ activeKpi?: MailboxKpiKey | null;
20
+ /** Meldingen van de pagina zelf; komen bij de andere meldingen onder de kop. */
21
+ notices?: Notice[];
22
+ helpOpen?: boolean;
23
+ onHelpOpenChange?: (v: boolean) => void;
24
+ }): import("react").JSX.Element;
25
+ //# sourceMappingURL=MailboxPageFrame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MailboxPageFrame.d.ts","sourceRoot":"","sources":["../../src/components/MailboxPageFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAe5D,OAAO,EAAa,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3D,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,UAAU,GAAG,cAAc,CAAC;AACrE,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;AAQxF,eAAO,MAAM,SAAS,cAErB,CAAC;AAEF,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,GAAG,QAAQ,EAAE,KAAK,EAAE,OAAO,QAEzE;AAED,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,MAAM,CAAC,CAM/C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,QAAQ,EACR,KAAK,EACL,SAAS,EACT,OAAY,EACZ,QAAQ,EACR,gBAAgB,GACjB,EAAE;IACD,GAAG,EAAE,UAAU,CAAC;IAChB,QAAQ,EAAE,SAAS,CAAC;IACpB,mFAAmF;IACnF,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,GAAG,IAAI,CAAC;IAC/C,kFAAkF;IAClF,SAAS,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACjC,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC,+BA+EA"}
@@ -0,0 +1,103 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { useQuery } from "@tanstack/react-query";
4
+ import { useServerFn } from "@tanstack/react-start";
5
+ import { useNavigate } from "@tanstack/react-router";
6
+ import { CalendarClock, CheckCircle2, ClipboardCheck, MailX, UserX, Inbox } from "lucide-react";
7
+ import { ensureMailboxDefaultsFn, getActiveOrdersMailbox } from "../lib/mailbox.functions.js";
8
+ import { getMailboxKpis } from "../lib/mailbox-list.functions.js";
9
+ import { mailboxAuthNotices } from "./MailboxAuthBanner.js";
10
+ import { statusInfo, useMailboxStatus, useMailboxStatusNotices } from "./MailboxStatusLine.js";
11
+ import { PageHeader } from "./fs-ui/PageHeader.js";
12
+ import { UpdatedAt } from "./fs-ui/UpdatedAt.js";
13
+ import { KpiRow } from "./fs-ui/KpiRow.js";
14
+ import { KpiTile } from "./fs-ui/KpiTile.js";
15
+ import { PillTabs } from "./fs-ui/PillTabs.js";
16
+ import { ShortcutHelp } from "./fs-ui/ShortcutHelp.js";
17
+ import { NoticeBar } from "./fs-ui/NoticeBar.js";
18
+ const TAB_PATH = {
19
+ voorstellen: "/mailbox-orders/voorstellen",
20
+ verwerkt: "/mailbox-orders/verwerkt",
21
+ overgeslagen: "/mailbox-orders/overgeslagen",
22
+ };
23
+ export const browserTz = () => {
24
+ try {
25
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
26
+ }
27
+ catch {
28
+ return "UTC";
29
+ }
30
+ };
31
+ /** Zet een bewaard filter van de voorstellenlijst vóór het navigeren. */
32
+ export function setProposalFilter(key, value) {
33
+ try {
34
+ window.localStorage.setItem(`mailbox-proposals:${key}`, JSON.stringify(value));
35
+ }
36
+ catch { /* negeer */ }
37
+ }
38
+ export const SHORTCUTS = [
39
+ [["/"], "Zoeken"],
40
+ [["J", "K"], "Volgende / vorige rij"],
41
+ [["Enter"], "Geselecteerde rij openen"],
42
+ [["Esc"], "Zoeken wissen / sluiten"],
43
+ [["?"], "Dit overzicht"],
44
+ ];
45
+ /**
46
+ * Gezamenlijke opbouw van de drie mailboxpagina's, in de vaste volgorde:
47
+ * kop → meldingen → 4 KPI's → tabbladen → (children: werkbalk, tabel, paginering).
48
+ */
49
+ export function MailboxPageFrame({ tab, children, onKpi, activeKpi, notices = [], helpOpen, onHelpOpenChange, }) {
50
+ const navigate = useNavigate();
51
+ const fetchActive = useServerFn(getActiveOrdersMailbox);
52
+ const fetchKpis = useServerFn(getMailboxKpis);
53
+ const ensureDefaults = useServerFn(ensureMailboxDefaultsFn);
54
+ const [localHelp, setLocalHelp] = useState(false);
55
+ const help = helpOpen ?? localHelp;
56
+ const setHelp = onHelpOpenChange ?? setLocalHelp;
57
+ // Standaardinstellingen één keer per paginabezoek aanvullen (niet bij elke lijstaanvraag).
58
+ useEffect(() => {
59
+ void ensureDefaults().catch((e) => console.warn("[mailbox] standaardinstellingen", e));
60
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
61
+ const { data: activeMailbox } = useQuery({
62
+ queryKey: ["mailbox-active-account"],
63
+ queryFn: () => fetchActive(),
64
+ refetchInterval: 60000,
65
+ retry: 1,
66
+ });
67
+ const kpiQ = useQuery({
68
+ queryKey: ["mailbox-kpis"],
69
+ queryFn: () => fetchKpis({ data: { tz: browserTz() } }),
70
+ refetchInterval: 60000,
71
+ placeholderData: (prev) => prev,
72
+ });
73
+ const status = useMailboxStatus();
74
+ const statusNotices = useMailboxStatusNotices(status);
75
+ const k = (kpiQ.data ?? null);
76
+ const loading = kpiQ.isLoading;
77
+ const allNotices = [...mailboxAuthNotices(activeMailbox), ...statusNotices, ...notices];
78
+ const go = (t) => { if (t !== tab)
79
+ void navigate({ to: TAB_PATH[t] }); };
80
+ const kpi = (key) => {
81
+ if (onKpi && onKpi(key) === true)
82
+ return;
83
+ if (key === "approved_today") {
84
+ go("verwerkt");
85
+ return;
86
+ }
87
+ if (key === "today")
88
+ setProposalFilter("period", "today");
89
+ if (key === "customer_unknown")
90
+ setProposalFilter("filter", "klant_onbekend");
91
+ if (key === "pending") {
92
+ setProposalFilter("period", "all");
93
+ setProposalFilter("filter", null);
94
+ }
95
+ if (tab !== "voorstellen")
96
+ go("voorstellen");
97
+ };
98
+ return (_jsxs("main", { className: "flex h-[calc(100dvh-var(--app-header-height)-var(--core-topbar-h,0px))] max-h-[calc(100dvh-var(--app-header-height)-var(--core-topbar-h,0px))] min-h-0 w-full min-w-0 flex-col gap-4 overflow-hidden overflow-x-clip p-4 pb-24 sm:p-6 md:pb-6", children: [_jsx(PageHeader, { title: "Mailbox bestellingen", meta: _jsx(UpdatedAt, { at: kpiQ.dataUpdatedAt || null, stale: kpiQ.isError, info: statusInfo(status) }) }), _jsx(NoticeBar, { notices: allNotices }), _jsxs(KpiRow, { children: [_jsx(KpiTile, { title: "Te controleren", value: k?.pending ?? 0, sub: "Bestellingen uit e-mail", icon: _jsx(ClipboardCheck, {}), loading: loading, active: activeKpi === "pending", onClick: () => kpi("pending") }), _jsx(KpiTile, { title: "Vandaag binnen", value: k?.pending_today ?? 0, sub: "Nog te controleren", icon: _jsx(CalendarClock, {}), loading: loading, active: activeKpi === "today", onClick: () => kpi("today") }), _jsx(KpiTile, { title: "Vandaag verwerkt", value: k?.approved_today ?? 0, sub: "Orders aangemaakt", icon: _jsx(CheckCircle2, {}), loading: loading, active: activeKpi === "approved_today", onClick: () => kpi("approved_today") }), _jsx(KpiTile, { title: "Klant onbekend", value: k?.customer_unknown ?? 0, sub: (k?.customer_unknown ?? 0) > 0 ? _jsx("span", { className: "text-destructive", children: "Kies of maak de klant" }) : "Alles herkend", icon: _jsx(UserX, {}), tone: "warning", loading: loading, active: activeKpi === "customer_unknown", onClick: () => kpi("customer_unknown") })] }), _jsx(PillTabs, { ariaLabel: "Mailbox", active: tab, onSelect: (t) => go(t), tabs: [
99
+ { key: "voorstellen", label: "Te controleren", icon: _jsx(Inbox, {}), count: k ? k.pending : null },
100
+ { key: "verwerkt", label: "Verwerkt", icon: _jsx(CheckCircle2, {}), count: k ? k.processed : null },
101
+ { key: "overgeslagen", label: "Overgeslagen", icon: _jsx(MailX, {}), count: k ? k.skipped : null },
102
+ ] }), children, _jsx(ShortcutHelp, { open: help, onOpenChange: setHelp, items: SHORTCUTS })] }));
103
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"MailboxProcessedPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxProcessedPage.tsx"],"names":[],"mappings":"AAaA,wBAAgB,oBAAoB,gCA4KnC"}
1
+ {"version":3,"file":"MailboxProcessedPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxProcessedPage.tsx"],"names":[],"mappings":"AA4BA,wBAAgB,oBAAoB,gCAmLnC"}
@@ -1,90 +1,131 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useMemo, useState } from "react";
2
+ import { useEffect, useRef, useState } from "react";
3
3
  import { useQuery, useQueryClient } from "@tanstack/react-query";
4
4
  import { useServerFn } from "@tanstack/react-start";
5
+ import { useNavigate } from "@tanstack/react-router";
5
6
  import { toast } from "sonner";
6
- import { Badge, Button, Card, CardContent, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@flowselections/core";
7
- import { listProposals, reopenProposal, getActiveOrdersMailbox } from "../lib/mailbox.functions.js";
8
- import { MailboxAuthBanner } from "./MailboxAuthBanner.js";
7
+ import { CalendarClock, CheckCircle2, Search } from "lucide-react";
8
+ import { Button } from "@flowselections/core";
9
+ import { reopenProposal } from "../lib/mailbox.functions.js";
10
+ import { listProposalsPage } from "../lib/mailbox-list.functions.js";
11
+ import { fmtDayTime } from "../lib/format-date.js";
12
+ import { label, PROPOSAL_STATUS } from "../lib/labels.js";
9
13
  import { TablePagination } from "./TablePagination.js";
10
- import { MailboxFilterBar, usePersistedState, normalizeText, matchesPeriod } from "./MailboxFilterBar.js";
14
+ import { usePersistedState } from "./MailboxFilterBar.js";
15
+ import { MailboxPageFrame, browserTz } from "./MailboxPageFrame.js";
16
+ import { DataTable, InitialsAvatar, CellText } from "./fs-ui/DataTable.js";
17
+ import { StatusIcon } from "./fs-ui/StatusIcon.js";
18
+ import { EmptyState } from "./fs-ui/EmptyState.js";
19
+ import { TableSkeleton } from "./fs-ui/TableSkeleton.js";
20
+ import { Toolbar } from "./fs-ui/Toolbar.js";
21
+ import { SearchInput } from "./fs-ui/SearchInput.js";
22
+ import { FilterChips } from "./fs-ui/FilterChips.js";
23
+ import { FilterPanel, FilterOptions, PERIOD_OPTIONS } from "./fs-ui/FilterPanel.js";
24
+ import { RowMenu } from "./fs-ui/RowMenu.js";
25
+ import { useListHotkeys } from "./fs-ui/useListHotkeys.js";
26
+ const PERIOD_LABEL = { today: "Vandaag", "7d": "Laatste 7 dagen", "30d": "Laatste 30 dagen" };
27
+ const COLS = [220, 300, 130, 140, 120];
11
28
  export function MailboxProcessedPage() {
12
- const fetchList = useServerFn(listProposals);
29
+ const fetchList = useServerFn(listProposalsPage);
13
30
  const reopen = useServerFn(reopenProposal);
14
- const fetchActive = useServerFn(getActiveOrdersMailbox);
15
31
  const qc = useQueryClient();
16
- const { data: activeMailbox } = useQuery({
17
- queryKey: ["mailbox-active-account"],
18
- queryFn: () => fetchActive(),
19
- refetchInterval: 60000,
20
- retry: 1,
21
- });
22
- const approved = useQuery({
23
- queryKey: ["mailbox-proposals", "approved"],
24
- queryFn: () => fetchList({ data: { status: "approved" } }),
25
- });
26
- const rejected = useQuery({
27
- queryKey: ["mailbox-proposals", "rejected"],
28
- queryFn: () => fetchList({ data: { status: "rejected" } }),
29
- });
30
- const items = [
31
- ...(Array.isArray(approved.data?.items) ? approved.data.items : []),
32
- ...(Array.isArray(rejected.data?.items) ? rejected.data.items : []),
33
- ];
32
+ const navigate = useNavigate();
34
33
  const [busyId, setBusyId] = useState(null);
35
34
  const [page, setPage] = useState(1);
36
- const [pageSize, setPageSize] = useState(25);
35
+ const [pageSize, setPageSize] = useState(50);
37
36
  const [search, setSearch] = usePersistedState("mailbox-processed:search", "");
38
37
  const [period, setPeriod] = usePersistedState("mailbox-processed:period", "all");
39
38
  const [sortOrder, setSortOrder] = usePersistedState("mailbox-processed:sort", "date-desc");
40
39
  const [statusFilter, setStatusFilter] = usePersistedState("mailbox-processed:status", "all");
41
- const customerNameFor = (p) => p.customer_name || p.mailbox_messages?.from_name || p.mailbox_messages?.from_email || "Onbekend";
42
- const filteredItems = useMemo(() => {
43
- const q = normalizeText(search).trim();
44
- const filtered = items.filter((p) => {
45
- if (statusFilter !== "all" && p.status !== statusFilter)
46
- return false;
47
- if (!matchesPeriod(p.reviewed_at, period))
48
- return false;
49
- if (!q)
50
- return true;
51
- const haystack = normalizeText([
52
- customerNameFor(p),
53
- p.mailbox_messages?.from_email,
54
- p.mailbox_messages?.subject,
55
- p.mailbox_messages?.preview,
56
- ].join(" "));
57
- return q.split(/\s+/).every((token) => haystack.includes(token));
58
- });
59
- return filtered.sort((a, b) => {
60
- const aDate = a.reviewed_at ?? "";
61
- const bDate = b.reviewed_at ?? "";
62
- return sortOrder === "date-desc" ? bDate.localeCompare(aDate) : aDate.localeCompare(bDate);
63
- });
64
- }, [items, search, period, sortOrder, statusFilter]);
65
- const hasActiveFilters = !!search || period !== "all" || sortOrder !== "date-desc" || statusFilter !== "all";
40
+ const [cursor, setCursor] = useState(null);
41
+ const [helpOpen, setHelpOpen] = useState(false);
42
+ const [filtersOpen, setFiltersOpen] = useState(false);
43
+ const searchRef = useRef(null);
44
+ const params = {
45
+ statuses: statusFilter === "approved" ? ["approved"] : statusFilter === "rejected" ? ["rejected"] : ["approved", "rejected"],
46
+ search,
47
+ period,
48
+ sort: sortOrder === "date-asc" ? "asc" : "desc",
49
+ dateField: "reviewed",
50
+ tz: browserTz(),
51
+ page,
52
+ pageSize,
53
+ };
54
+ const { data, isLoading, error, refetch } = useQuery({
55
+ queryKey: ["mailbox-proposals", "processed", params],
56
+ queryFn: () => fetchList({ data: params }),
57
+ placeholderData: (prev) => prev,
58
+ refetchInterval: 60000,
59
+ });
60
+ const items = Array.isArray(data?.items) ? data.items : [];
61
+ const total = Number(data?.total ?? 0);
62
+ const hasActiveFilters = !!search || period !== "all" || statusFilter !== "all";
66
63
  const clearFilters = () => { setSearch(""); setPeriod("all"); setSortOrder("date-desc"); setStatusFilter("all"); };
67
- useEffect(() => { setPage(1); }, [search, period, sortOrder, statusFilter]);
68
- const pageCount = Math.max(1, Math.ceil(filteredItems.length / pageSize));
64
+ useEffect(() => { setPage(1); setCursor(null); }, [search, period, sortOrder, statusFilter]);
65
+ const pageCount = Math.max(1, Math.ceil(total / pageSize));
69
66
  const currentPage = Math.min(page, pageCount);
70
- const pagedItems = filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize);
67
+ const openOrder = (p) => {
68
+ if (p?.created_order_id)
69
+ void navigate({ to: `/floriday-verkoop/verkooporders/${p.created_order_id}` });
70
+ };
71
71
  const handleReopen = async (proposalId) => {
72
72
  setBusyId(proposalId);
73
73
  try {
74
74
  await reopen({ data: { proposalId } });
75
- toast.success("Voorstel teruggezet naar 'Te controleren'.");
75
+ toast.success("Bestelling teruggezet naar 'Te controleren'.");
76
76
  await qc.invalidateQueries({ queryKey: ["mailbox-proposals"] });
77
+ await qc.invalidateQueries({ queryKey: ["mailbox-kpis"] });
77
78
  }
78
79
  catch (e) {
79
- toast.error(e?.message ?? "Terugzetten mislukt.");
80
+ console.warn("[mailbox] terugzetten", e);
81
+ toast.error("Terugzetten lukt niet. Probeer het opnieuw.");
80
82
  }
81
83
  finally {
82
84
  setBusyId(null);
83
85
  }
84
86
  };
85
- return (_jsxs("div", { className: "flex h-[calc(100dvh-var(--app-header-height)-var(--core-topbar-h,0px))] max-h-[calc(100dvh-var(--app-header-height)-var(--core-topbar-h,0px))] min-h-0 w-full flex-col gap-4 overflow-hidden p-6", children: [_jsx(MailboxAuthBanner, { active: activeMailbox }), _jsx("h1", { className: "text-2xl font-semibold shrink-0", children: "Verwerkt" }), items.length > 0 && (_jsx(MailboxFilterBar, { search: search, onSearchChange: setSearch, period: period, onPeriodChange: setPeriod, sortOrder: sortOrder, onSortOrderChange: setSortOrder, status: statusFilter, onStatusChange: setStatusFilter, statusOptions: [
86
- { value: "all", label: "Alle statussen" },
87
- { value: "approved", label: "Goedgekeurd" },
88
- { value: "rejected", label: "Afgewezen" },
89
- ], filteredCount: filteredItems.length, totalCount: items.length, countLabel: "mails", onClear: clearFilters, hasActiveFilters: hasActiveFilters })), _jsx("div", { className: "flex-1 min-h-0 overflow-y-auto overscroll-contain [scrollbar-gutter:stable]", children: _jsx(Card, { children: _jsxs(CardContent, { className: "p-0", children: [items.length === 0 ? (_jsx("div", { className: "p-8 text-center text-muted-foreground", children: "Nog geen verwerkte voorstellen." })) : filteredItems.length === 0 ? (_jsx("div", { className: "p-8 text-center text-muted-foreground", children: "Geen mails gevonden voor deze zoekopdracht." })) : (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Status" }), _jsx(TableHead, { children: "Afzender" }), _jsx(TableHead, { children: "Onderwerp" }), _jsx(TableHead, { children: "Verwerkt op" }), _jsx(TableHead, { className: "text-right", children: "Acties" })] }) }), _jsx(TableBody, { children: pagedItems.map((p) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: _jsx(Badge, { variant: p.status === "approved" ? "default" : "destructive", children: p.status === "approved" ? "Goedgekeurd" : "Afgewezen" }) }), _jsx(TableCell, { children: p.mailbox_messages?.from_email ?? "-" }), _jsxs(TableCell, { className: "max-w-md", children: [_jsx("div", { className: "truncate", children: p.mailbox_messages?.subject ?? "-" }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: p.mailbox_messages?.preview || "" })] }), _jsx(TableCell, { className: "text-sm text-muted-foreground", children: p.reviewed_at ? new Date(p.reviewed_at).toLocaleString("nl-NL") : "-" }), _jsx(TableCell, { className: "text-right space-x-2", children: p.status === "rejected" && (_jsx(Button, { size: "sm", variant: "outline", disabled: busyId === p.id, onClick: () => handleReopen(p.id), children: busyId === p.id ? "Bezig..." : "Terugzetten" })) })] }, p.id))) })] })), items.length > 0 && (_jsx(TablePagination, { total: filteredItems.length, page: currentPage, pageSize: pageSize, onPageChange: setPage, onPageSizeChange: setPageSize }))] }) }) })] }));
87
+ useListHotkeys({
88
+ disabled: helpOpen || filtersOpen,
89
+ onSearch: () => searchRef.current?.focus(),
90
+ onNext: () => setCursor((c) => (items.length ? Math.min((c ?? -1) + 1, items.length - 1) : null)),
91
+ onPrev: () => setCursor((c) => (items.length ? Math.max((c ?? 1) - 1, 0) : null)),
92
+ onOpen: cursor != null && items[cursor]?.created_order_id ? () => openOrder(items[cursor]) : undefined,
93
+ onEscape: hasActiveFilters ? clearFilters : undefined,
94
+ onHelp: () => setHelpOpen(true),
95
+ });
96
+ const nameOf = (p) => p.customer_name || p.from_name || p.from_email || "Onbekend";
97
+ const statusText = (p) => label(PROPOSAL_STATUS, p.status);
98
+ const statusOf = (p) => p.status === "approved"
99
+ ? _jsx(StatusIcon, { tone: "done", label: statusText(p), detail: p.order_number ? `Order ${p.order_number}` : undefined })
100
+ : _jsx(StatusIcon, { tone: "rejected", label: statusText(p) });
101
+ const rowAction = (p) => (_jsx(RowMenu, { label: `Acties voor ${nameOf(p)}`, items: [
102
+ ...(p.created_order_id ? [{ label: "Open order", onSelect: () => openOrder(p) }] : []),
103
+ ...(p.status === "rejected" ? [{ label: busyId === p.id ? "Bezig…" : "Terugzetten naar te controleren", disabled: busyId === p.id, onSelect: () => void handleReopen(p.id) }] : []),
104
+ ] }));
105
+ const columns = [
106
+ {
107
+ key: "klant", header: "Klant", width: COLS[0],
108
+ cell: (p) => (_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx(InitialsAvatar, { name: nameOf(p) }), _jsx(CellText, { primary: nameOf(p), secondary: p.from_email ?? "", strong: true })] })),
109
+ },
110
+ {
111
+ key: "bericht", header: "Onderwerp", width: COLS[1],
112
+ cell: (p) => _jsx(CellText, { primary: p.subject || "(geen onderwerp)", secondary: String(p.preview ?? "").slice(0, 180) }),
113
+ },
114
+ { key: "order", header: "Order", width: COLS[2], cell: (p) => _jsx("span", { className: "block truncate tabular-nums", children: p.order_number || "–" }) },
115
+ {
116
+ key: "datum", header: "Verwerkt op", width: COLS[3], sortable: true,
117
+ cell: (p) => _jsx("span", { className: "block truncate tabular-nums", children: fmtDayTime(p.reviewed_at) }),
118
+ },
119
+ {
120
+ key: "status", header: "Status", width: COLS[4],
121
+ cell: (p) => (_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [statusOf(p), _jsx("span", { className: "truncate text-sm", children: statusText(p) })] })),
122
+ },
123
+ ];
124
+ const chips = [
125
+ ...(period !== "all" ? [{ key: "period", label: PERIOD_LABEL[period] ?? period, icon: _jsx(CalendarClock, {}), onRemove: () => setPeriod("all") }] : []),
126
+ ...(statusFilter !== "all" ? [{ key: "status", label: label(PROPOSAL_STATUS, statusFilter), icon: _jsx(CheckCircle2, {}), onRemove: () => setStatusFilter("all") }] : []),
127
+ ];
128
+ return (_jsxs(MailboxPageFrame, { tab: "verwerkt", helpOpen: helpOpen, onHelpOpenChange: setHelpOpen, activeKpi: period === "today" ? "approved_today" : null, children: [_jsx(Toolbar, { onOpenFilters: () => setFiltersOpen(true), filterCount: chips.length, chips: _jsx(FilterChips, { chips: chips, onClearAll: clearFilters }), children: _jsx(SearchInput, { ref: searchRef, value: search, onChange: setSearch, placeholder: "Zoek op klant, afzender of onderwerp" }) }), _jsxs(FilterPanel, { open: filtersOpen, onOpenChange: setFiltersOpen, onClear: clearFilters, children: [_jsx(FilterOptions, { label: "Periode", value: period, options: PERIOD_OPTIONS, onChange: (v) => setPeriod(v) }), _jsx(FilterOptions, { label: "Status", value: statusFilter, options: [{ value: "all", label: "Alle statussen" }, { value: "approved", label: "Goedgekeurd" }, { value: "rejected", label: "Afgewezen" }], onChange: setStatusFilter })] }), isLoading && !data ? (_jsx(TableSkeleton, { columns: COLS })) : error && !data ? (_jsx("div", { className: "rounded-xl border border-border bg-card shadow-sm", children: _jsx(EmptyState, { icon: _jsx(CheckCircle2, { className: "h-6 w-6" }), title: "De lijst kon niet worden geladen", text: "Probeer het opnieuw.", action: _jsx(Button, { size: "sm", variant: "outline", onClick: () => void refetch(), children: "Opnieuw proberen" }) }) })) : total === 0 ? (_jsx("div", { className: "rounded-xl border border-border bg-card shadow-sm", children: hasActiveFilters
129
+ ? _jsx(EmptyState, { icon: _jsx(Search, { className: "h-6 w-6" }), title: "Niets gevonden", text: "Er zijn geen verwerkte bestellingen die bij je zoekopdracht of filters passen.", action: _jsx(Button, { size: "sm", variant: "outline", onClick: clearFilters, children: "Filters wissen" }) })
130
+ : _jsx(EmptyState, { icon: _jsx(CheckCircle2, { className: "h-6 w-6" }), title: "Nog niets verwerkt", text: "Bevestigde en afgewezen bestellingen verschijnen hier." }) })) : (_jsx(DataTable, { rows: items, columns: columns, rowKey: (p) => p.id, activeIndex: cursor, rowAction: rowAction, sort: { key: "datum", dir: sortOrder === "date-asc" ? "asc" : "desc" }, onSortChange: () => setSortOrder(sortOrder === "date-asc" ? "date-desc" : "date-asc"), mobileCard: (p) => (_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { className: "truncate font-semibold", children: nameOf(p) }), statusOf(p)] }), _jsx("div", { className: "truncate text-muted-foreground", children: p.subject || "(geen onderwerp)" }), _jsxs("div", { className: "text-xs tabular-nums text-muted-foreground", children: [p.order_number ? `${p.order_number} · ` : "", fmtDayTime(p.reviewed_at)] })] })), footer: _jsx(TablePagination, { total: total, page: currentPage, pageSize: pageSize, onPageChange: setPage, onPageSizeChange: setPageSize }) }))] }));
90
131
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MailboxProposalsPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxProposalsPage.tsx"],"names":[],"mappings":"AAiHA,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AA2CD,wBAAgB,oBAAoB,gCAycnC"}
1
+ {"version":3,"file":"MailboxProposalsPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxProposalsPage.tsx"],"names":[],"mappings":"AA8HA,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AA2CD,wBAAgB,oBAAoB,gCAsgBnC"}