@flowselections/mailbox-orders 1.0.55 → 1.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/EmailPreview.d.ts.map +1 -1
- package/dist-lib/components/EmailPreview.js +2 -1
- package/dist-lib/components/MailboxAuthBanner.d.ts +3 -3
- package/dist-lib/components/MailboxAuthBanner.d.ts.map +1 -1
- package/dist-lib/components/MailboxAuthBanner.js +18 -6
- package/dist-lib/components/MailboxPageFrame.d.ts +11 -5
- package/dist-lib/components/MailboxPageFrame.d.ts.map +1 -1
- package/dist-lib/components/MailboxPageFrame.js +24 -16
- package/dist-lib/components/MailboxProcessedPage.d.ts.map +1 -1
- package/dist-lib/components/MailboxProcessedPage.js +30 -21
- package/dist-lib/components/MailboxProposalsPage.d.ts.map +1 -1
- package/dist-lib/components/MailboxProposalsPage.js +38 -30
- package/dist-lib/components/MailboxSkippedPage.d.ts.map +1 -1
- package/dist-lib/components/MailboxSkippedPage.js +35 -23
- package/dist-lib/components/MailboxStatusLine.d.ts +7 -3
- package/dist-lib/components/MailboxStatusLine.d.ts.map +1 -1
- package/dist-lib/components/MailboxStatusLine.js +42 -19
- package/dist-lib/components/TablePagination.d.ts +2 -2
- package/dist-lib/components/TablePagination.d.ts.map +1 -1
- package/dist-lib/components/TablePagination.js +8 -12
- package/dist-lib/components/fs-ui/DataTable.d.ts +12 -6
- package/dist-lib/components/fs-ui/DataTable.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/DataTable.js +26 -19
- package/dist-lib/components/fs-ui/FilterChips.d.ts +1 -1
- package/dist-lib/components/fs-ui/FilterChips.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/FilterChips.js +3 -3
- package/dist-lib/components/fs-ui/FilterPanel.d.ts +32 -0
- package/dist-lib/components/fs-ui/FilterPanel.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/FilterPanel.js +20 -0
- package/dist-lib/components/fs-ui/InlineAlert.d.ts +2 -2
- package/dist-lib/components/fs-ui/InlineAlert.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/InlineAlert.js +4 -12
- package/dist-lib/components/fs-ui/KpiRow.js +1 -1
- package/dist-lib/components/fs-ui/NoticeBar.d.ts +18 -0
- package/dist-lib/components/fs-ui/NoticeBar.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/NoticeBar.js +24 -0
- package/dist-lib/components/fs-ui/PageHeader.d.ts +1 -1
- package/dist-lib/components/fs-ui/PageHeader.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/PageHeader.js +2 -2
- package/dist-lib/components/fs-ui/PillTabs.d.ts +3 -2
- package/dist-lib/components/fs-ui/PillTabs.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/PillTabs.js +26 -11
- package/dist-lib/components/fs-ui/RowMenu.d.ts +13 -0
- package/dist-lib/components/fs-ui/RowMenu.d.ts.map +1 -0
- package/dist-lib/components/fs-ui/RowMenu.js +10 -0
- package/dist-lib/components/fs-ui/SearchInput.d.ts +2 -2
- package/dist-lib/components/fs-ui/SearchInput.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/SearchInput.js +11 -6
- package/dist-lib/components/fs-ui/ShortcutHelp.d.ts +3 -3
- package/dist-lib/components/fs-ui/ShortcutHelp.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/ShortcutHelp.js +2 -2
- package/dist-lib/components/fs-ui/TableSkeleton.d.ts +2 -2
- package/dist-lib/components/fs-ui/TableSkeleton.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/TableSkeleton.js +2 -2
- package/dist-lib/components/fs-ui/Toolbar.d.ts +8 -3
- package/dist-lib/components/fs-ui/Toolbar.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/Toolbar.js +8 -3
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts +5 -4
- package/dist-lib/components/fs-ui/UpdatedAt.d.ts.map +1 -1
- package/dist-lib/components/fs-ui/UpdatedAt.js +12 -4
- package/dist-lib/integrations/supabase/types.d.ts +76 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/format-date.d.ts +5 -0
- package/dist-lib/lib/format-date.d.ts.map +1 -0
- package/dist-lib/lib/format-date.js +30 -0
- package/dist-lib/lib/labels.d.ts +15 -0
- package/dist-lib/lib/labels.d.ts.map +1 -0
- package/dist-lib/lib/labels.js +57 -0
- package/dist-lib/lib/mailbox-list.functions.d.ts.map +1 -1
- package/dist-lib/lib/mailbox-list.functions.js +0 -2
- package/dist-lib/lib/mailbox.functions.js +3 -3
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmailPreview.d.ts","sourceRoot":"","sources":["../../src/components/EmailPreview.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EmailPreview.d.ts","sourceRoot":"","sources":["../../src/components/EmailPreview.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAC7E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AAEF,KAAK,KAAK,GAAG;IACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC;IACtB,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAsEF,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,QAAQ,EACR,SAAS,EACT,UAAU,EACV,EAAO,EACP,EAAO,EACP,QAAQ,EACR,QAAQ,EACR,WAAgB,EAChB,cAAc,GACf,EAAE,KAAK,+BAsHP"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { fmtDayTime } from "../lib/format-date.js";
|
|
2
3
|
import { useEffect, useMemo, useState } from "react";
|
|
3
4
|
import { Button, Badge } from "@flowselections/core";
|
|
4
5
|
import { Paperclip, Download, Code, FileText, ShieldOff, Shield } from "lucide-react";
|
|
@@ -52,5 +53,5 @@ export function EmailPreview({ subject, fromName, fromEmail, receivedAt, to = []
|
|
|
52
53
|
const rendered = blockRemoteContent ? blockRemote(html) : html;
|
|
53
54
|
const safeAttachments = Array.isArray(attachments) ? attachments : [];
|
|
54
55
|
const inlineOnly = safeAttachments.every((a) => !!a.contentId);
|
|
55
|
-
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsxs("div", { className: "shrink-0 space-y-1 border-b pb-3", children: [_jsx("div", { className: "text-base font-semibold leading-snug", children: subject || "(geen onderwerp)" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [_jsx("span", { className: "font-medium text-foreground", children: fromName || fromEmail }), fromName && fromEmail ? ` <${fromEmail}>` : "", receivedAt ? ` · ${
|
|
56
|
+
return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsxs("div", { className: "shrink-0 space-y-1 border-b pb-3", children: [_jsx("div", { className: "text-base font-semibold leading-snug", children: subject || "(geen onderwerp)" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [_jsx("span", { className: "font-medium text-foreground", children: fromName || fromEmail }), fromName && fromEmail ? ` <${fromEmail}>` : "", receivedAt ? ` · ${fmtDayTime(receivedAt)}` : ""] }), _jsx(AddressLine, { label: "Aan", list: to }), _jsx(AddressLine, { label: "CC", list: cc }), (safeAttachments.length > 0 || hasAttachments) && (_jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-2", children: [safeAttachments.length === 0 ? (_jsxs(Badge, { variant: "outline", className: "gap-1 font-normal", children: [_jsx(Paperclip, { className: "h-3 w-3" }), " Deze mail bevat bijlagen (nog niet opgehaald)"] })) : (safeAttachments.map((a, i) => (_jsxs("span", { className: "inline-flex items-center gap-1 rounded border bg-muted/40 px-2 py-1 text-xs", children: [_jsx(Paperclip, { className: "h-3 w-3 shrink-0" }), _jsx("span", { className: "max-w-[16rem] truncate", children: a.name }), formatSize(a.size) && (_jsx("span", { className: "text-muted-foreground", children: formatSize(a.size) })), a.url && (_jsx("a", { href: a.url, target: "_blank", rel: "noopener noreferrer", className: "text-primary", "aria-label": `Download ${a.name}`, children: _jsx(Download, { className: "h-3 w-3" }) }))] }, `${a.name}-${i}`)))), safeAttachments.length > 0 && inlineOnly && (_jsx("span", { className: "text-[11px] text-muted-foreground", children: "(in de mail ingesloten afbeeldingen)" }))] })), _jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-2", children: [_jsx(Button, { type: "button", size: "sm", variant: "outline", onClick: () => setMode(mode === "html" ? "text" : "html"), disabled: !html, children: mode === "html" ? (_jsxs(_Fragment, { children: [_jsx(FileText, { className: "mr-1 h-3.5 w-3.5" }), " Platte tekst"] })) : (_jsxs(_Fragment, { children: [_jsx(Code, { className: "mr-1 h-3.5 w-3.5" }), " Opgemaakte mail"] })) }), _jsx(Button, { type: "button", size: "sm", variant: "ghost", disabled: mode !== "html" || !html, onClick: () => setBlockRemoteContent((v) => !v), children: blockRemoteContent ? (_jsxs(_Fragment, { children: [_jsx(Shield, { className: "mr-1 h-3.5 w-3.5" }), " Externe inhoud tonen"] })) : (_jsxs(_Fragment, { children: [_jsx(ShieldOff, { className: "mr-1 h-3.5 w-3.5" }), " Externe inhoud blokkeren"] })) })] })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden pt-3", children: mode === "html" && html ? (_jsx(EmailFrame, { html: rendered })) : (_jsx("div", { className: "h-full w-full overflow-y-auto overscroll-contain rounded border bg-white p-3", children: _jsx("pre", { className: "whitespace-pre-wrap break-words font-sans text-sm leading-relaxed text-[#111]", children: bodyText || "(geen tekst)" }) })) })] }));
|
|
56
57
|
}
|
|
@@ -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
|
-
|
|
16
|
-
|
|
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":"
|
|
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,18 +1,30 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { bannerKind } from "../lib/mailbox-active.js";
|
|
3
|
-
import { InlineAlert } from "./fs-ui/InlineAlert.js";
|
|
4
3
|
const settingsLink = (label) => (_jsx("a", { href: "/e-mail-authenticatie", className: "text-sm font-medium underline underline-offset-2", children: label }));
|
|
5
|
-
|
|
4
|
+
/** Meldingen over de koppeling van de mailbox (voor de NoticeBar onder de kop). */
|
|
5
|
+
export function mailboxAuthNotices(active) {
|
|
6
6
|
const kind = bannerKind(active);
|
|
7
7
|
if (kind === "unknown") {
|
|
8
|
-
return
|
|
8
|
+
return [{ key: "auth-unknown", tone: "info", title: "De status van de mailbox kon niet worden opgehaald." }];
|
|
9
9
|
}
|
|
10
10
|
if (kind === "no_mailbox") {
|
|
11
|
-
return
|
|
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
|
+
}];
|
|
12
18
|
}
|
|
13
19
|
if (kind === "reauth") {
|
|
14
20
|
const a = active;
|
|
15
|
-
return
|
|
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
|
+
}];
|
|
16
28
|
}
|
|
17
|
-
return
|
|
29
|
+
return [];
|
|
18
30
|
}
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
+
import { type Notice } from "./fs-ui/NoticeBar.js";
|
|
2
3
|
export type MailboxTab = "voorstellen" | "verwerkt" | "overgeslagen";
|
|
4
|
+
export type MailboxKpiKey = "pending" | "today" | "approved_today" | "customer_unknown";
|
|
3
5
|
export declare const browserTz: () => string;
|
|
4
6
|
/** Zet een bewaard filter van de voorstellenlijst vóór het navigeren. */
|
|
5
7
|
export declare function setProposalFilter(key: "period" | "filter", value: unknown): void;
|
|
6
|
-
export declare const SHORTCUTS: Array<[string, string]>;
|
|
8
|
+
export declare const SHORTCUTS: Array<[string[], string]>;
|
|
7
9
|
/**
|
|
8
|
-
* Gezamenlijke opbouw van de drie mailboxpagina's
|
|
9
|
-
*
|
|
10
|
+
* Gezamenlijke opbouw van de drie mailboxpagina's, in de vaste volgorde:
|
|
11
|
+
* kop → meldingen → 4 KPI's → tabbladen → (children: werkbalk, tabel, paginering).
|
|
10
12
|
*/
|
|
11
|
-
export declare function MailboxPageFrame({ tab, children, onKpi, helpOpen, onHelpOpenChange, }: {
|
|
13
|
+
export declare function MailboxPageFrame({ tab, children, onKpi, activeKpi, notices, helpOpen, onHelpOpenChange, }: {
|
|
12
14
|
tab: MailboxTab;
|
|
13
15
|
children: ReactNode;
|
|
14
16
|
/** Pagina handelt een KPI-klik zelf af (bijv. filter zetten); anders navigeren. */
|
|
15
|
-
onKpi?: (key:
|
|
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[];
|
|
16
22
|
helpOpen?: boolean;
|
|
17
23
|
onHelpOpenChange?: (v: boolean) => void;
|
|
18
24
|
}): import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MailboxPageFrame.d.ts","sourceRoot":"","sources":["../../src/components/MailboxPageFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
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"}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
3
|
import { useQuery } from "@tanstack/react-query";
|
|
4
4
|
import { useServerFn } from "@tanstack/react-start";
|
|
5
5
|
import { useNavigate } from "@tanstack/react-router";
|
|
6
|
-
import { CalendarClock, CheckCircle2, ClipboardCheck,
|
|
7
|
-
import {
|
|
8
|
-
import { getActiveOrdersMailbox } from "../lib/mailbox.functions.js";
|
|
6
|
+
import { CalendarClock, CheckCircle2, ClipboardCheck, MailX, UserX, Inbox } from "lucide-react";
|
|
7
|
+
import { ensureMailboxDefaultsFn, getActiveOrdersMailbox } from "../lib/mailbox.functions.js";
|
|
9
8
|
import { getMailboxKpis } from "../lib/mailbox-list.functions.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { mailboxAuthNotices } from "./MailboxAuthBanner.js";
|
|
10
|
+
import { statusInfo, useMailboxStatus, useMailboxStatusNotices } from "./MailboxStatusLine.js";
|
|
12
11
|
import { PageHeader } from "./fs-ui/PageHeader.js";
|
|
12
|
+
import { UpdatedAt } from "./fs-ui/UpdatedAt.js";
|
|
13
13
|
import { KpiRow } from "./fs-ui/KpiRow.js";
|
|
14
14
|
import { KpiTile } from "./fs-ui/KpiTile.js";
|
|
15
15
|
import { PillTabs } from "./fs-ui/PillTabs.js";
|
|
16
16
|
import { ShortcutHelp } from "./fs-ui/ShortcutHelp.js";
|
|
17
|
+
import { NoticeBar } from "./fs-ui/NoticeBar.js";
|
|
17
18
|
const TAB_PATH = {
|
|
18
19
|
voorstellen: "/mailbox-orders/voorstellen",
|
|
19
20
|
verwerkt: "/mailbox-orders/verwerkt",
|
|
@@ -35,24 +36,28 @@ export function setProposalFilter(key, value) {
|
|
|
35
36
|
catch { /* negeer */ }
|
|
36
37
|
}
|
|
37
38
|
export const SHORTCUTS = [
|
|
38
|
-
["/", "Zoeken"],
|
|
39
|
-
["J", "Volgende rij"],
|
|
40
|
-
["
|
|
41
|
-
["
|
|
42
|
-
["
|
|
43
|
-
["?", "Dit overzicht"],
|
|
39
|
+
[["/"], "Zoeken"],
|
|
40
|
+
[["J", "K"], "Volgende / vorige rij"],
|
|
41
|
+
[["Enter"], "Geselecteerde rij openen"],
|
|
42
|
+
[["Esc"], "Zoeken wissen / sluiten"],
|
|
43
|
+
[["?"], "Dit overzicht"],
|
|
44
44
|
];
|
|
45
45
|
/**
|
|
46
|
-
* Gezamenlijke opbouw van de drie mailboxpagina's
|
|
47
|
-
*
|
|
46
|
+
* Gezamenlijke opbouw van de drie mailboxpagina's, in de vaste volgorde:
|
|
47
|
+
* kop → meldingen → 4 KPI's → tabbladen → (children: werkbalk, tabel, paginering).
|
|
48
48
|
*/
|
|
49
|
-
export function MailboxPageFrame({ tab, children, onKpi, helpOpen, onHelpOpenChange, }) {
|
|
49
|
+
export function MailboxPageFrame({ tab, children, onKpi, activeKpi, notices = [], helpOpen, onHelpOpenChange, }) {
|
|
50
50
|
const navigate = useNavigate();
|
|
51
51
|
const fetchActive = useServerFn(getActiveOrdersMailbox);
|
|
52
52
|
const fetchKpis = useServerFn(getMailboxKpis);
|
|
53
|
+
const ensureDefaults = useServerFn(ensureMailboxDefaultsFn);
|
|
53
54
|
const [localHelp, setLocalHelp] = useState(false);
|
|
54
55
|
const help = helpOpen ?? localHelp;
|
|
55
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
|
|
56
61
|
const { data: activeMailbox } = useQuery({
|
|
57
62
|
queryKey: ["mailbox-active-account"],
|
|
58
63
|
queryFn: () => fetchActive(),
|
|
@@ -65,8 +70,11 @@ export function MailboxPageFrame({ tab, children, onKpi, helpOpen, onHelpOpenCha
|
|
|
65
70
|
refetchInterval: 60000,
|
|
66
71
|
placeholderData: (prev) => prev,
|
|
67
72
|
});
|
|
73
|
+
const status = useMailboxStatus();
|
|
74
|
+
const statusNotices = useMailboxStatusNotices(status);
|
|
68
75
|
const k = (kpiQ.data ?? null);
|
|
69
76
|
const loading = kpiQ.isLoading;
|
|
77
|
+
const allNotices = [...mailboxAuthNotices(activeMailbox), ...statusNotices, ...notices];
|
|
70
78
|
const go = (t) => { if (t !== tab)
|
|
71
79
|
void navigate({ to: TAB_PATH[t] }); };
|
|
72
80
|
const kpi = (key) => {
|
|
@@ -87,7 +95,7 @@ export function MailboxPageFrame({ tab, children, onKpi, helpOpen, onHelpOpenCha
|
|
|
87
95
|
if (tab !== "voorstellen")
|
|
88
96
|
go("voorstellen");
|
|
89
97
|
};
|
|
90
|
-
return (_jsxs("
|
|
98
|
+
return (_jsxs("main", { className: "flex w-full min-w-0 flex-col gap-4 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: [
|
|
91
99
|
{ key: "voorstellen", label: "Te controleren", icon: _jsx(Inbox, {}), count: k ? k.pending : null },
|
|
92
100
|
{ key: "verwerkt", label: "Verwerkt", icon: _jsx(CheckCircle2, {}), count: k ? k.processed : null },
|
|
93
101
|
{ key: "overgeslagen", label: "Overgeslagen", icon: _jsx(MailX, {}), count: k ? k.skipped : null },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MailboxProcessedPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxProcessedPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MailboxProcessedPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxProcessedPage.tsx"],"names":[],"mappings":"AA4BA,wBAAgB,oBAAoB,gCAmLnC"}
|
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
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
5
|
import { useNavigate } from "@tanstack/react-router";
|
|
6
6
|
import { toast } from "sonner";
|
|
7
|
-
import { CalendarClock, CheckCircle2,
|
|
8
|
-
import { Button
|
|
7
|
+
import { CalendarClock, CheckCircle2, Search } from "lucide-react";
|
|
8
|
+
import { Button } from "@flowselections/core";
|
|
9
9
|
import { reopenProposal } from "../lib/mailbox.functions.js";
|
|
10
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";
|
|
11
13
|
import { TablePagination } from "./TablePagination.js";
|
|
12
14
|
import { usePersistedState } from "./MailboxFilterBar.js";
|
|
13
15
|
import { MailboxPageFrame, browserTz } from "./MailboxPageFrame.js";
|
|
14
|
-
import { DataTable, InitialsAvatar } from "./fs-ui/DataTable.js";
|
|
16
|
+
import { DataTable, InitialsAvatar, CellText } from "./fs-ui/DataTable.js";
|
|
15
17
|
import { StatusIcon } from "./fs-ui/StatusIcon.js";
|
|
16
18
|
import { EmptyState } from "./fs-ui/EmptyState.js";
|
|
17
|
-
import { InlineAlert } from "./fs-ui/InlineAlert.js";
|
|
18
19
|
import { TableSkeleton } from "./fs-ui/TableSkeleton.js";
|
|
19
20
|
import { Toolbar } from "./fs-ui/Toolbar.js";
|
|
20
21
|
import { SearchInput } from "./fs-ui/SearchInput.js";
|
|
21
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";
|
|
22
25
|
import { useListHotkeys } from "./fs-ui/useListHotkeys.js";
|
|
23
26
|
const PERIOD_LABEL = { today: "Vandaag", "7d": "Laatste 7 dagen", "30d": "Laatste 30 dagen" };
|
|
27
|
+
const COLS = [220, 300, 130, 140, 120];
|
|
24
28
|
export function MailboxProcessedPage() {
|
|
25
29
|
const fetchList = useServerFn(listProposalsPage);
|
|
26
30
|
const reopen = useServerFn(reopenProposal);
|
|
@@ -35,6 +39,7 @@ export function MailboxProcessedPage() {
|
|
|
35
39
|
const [statusFilter, setStatusFilter] = usePersistedState("mailbox-processed:status", "all");
|
|
36
40
|
const [cursor, setCursor] = useState(null);
|
|
37
41
|
const [helpOpen, setHelpOpen] = useState(false);
|
|
42
|
+
const [filtersOpen, setFiltersOpen] = useState(false);
|
|
38
43
|
const searchRef = useRef(null);
|
|
39
44
|
const params = {
|
|
40
45
|
statuses: statusFilter === "approved" ? ["approved"] : statusFilter === "rejected" ? ["rejected"] : ["approved", "rejected"],
|
|
@@ -80,7 +85,7 @@ export function MailboxProcessedPage() {
|
|
|
80
85
|
}
|
|
81
86
|
};
|
|
82
87
|
useListHotkeys({
|
|
83
|
-
disabled: helpOpen,
|
|
88
|
+
disabled: helpOpen || filtersOpen,
|
|
84
89
|
onSearch: () => searchRef.current?.focus(),
|
|
85
90
|
onNext: () => setCursor((c) => (items.length ? Math.min((c ?? -1) + 1, items.length - 1) : null)),
|
|
86
91
|
onPrev: () => setCursor((c) => (items.length ? Math.max((c ?? 1) - 1, 0) : null)),
|
|
@@ -89,34 +94,38 @@ export function MailboxProcessedPage() {
|
|
|
89
94
|
onHelp: () => setHelpOpen(true),
|
|
90
95
|
});
|
|
91
96
|
const nameOf = (p) => p.customer_name || p.from_name || p.from_email || "Onbekend";
|
|
97
|
+
const statusText = (p) => label(PROPOSAL_STATUS, p.status);
|
|
92
98
|
const statusOf = (p) => p.status === "approved"
|
|
93
|
-
? _jsx(StatusIcon, { tone: "done", label:
|
|
94
|
-
: _jsx(StatusIcon, { tone: "rejected", label:
|
|
95
|
-
const rowAction = (p) =>
|
|
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
|
+
] }));
|
|
96
105
|
const columns = [
|
|
97
106
|
{
|
|
98
|
-
key: "klant", header: "Klant", width:
|
|
99
|
-
cell: (p) => (_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx(InitialsAvatar, { name: nameOf(p) }),
|
|
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 })] })),
|
|
100
109
|
},
|
|
101
110
|
{
|
|
102
|
-
key: "bericht", header: "Onderwerp", width:
|
|
103
|
-
cell: (p) =>
|
|
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) }),
|
|
104
113
|
},
|
|
105
|
-
{ key: "order", header: "Order", width:
|
|
114
|
+
{ key: "order", header: "Order", width: COLS[2], cell: (p) => _jsx("span", { className: "block truncate tabular-nums", children: p.order_number || "–" }) },
|
|
106
115
|
{
|
|
107
|
-
key: "datum", header: "Verwerkt op", width:
|
|
108
|
-
cell: (p) => _jsx("span", { className: "tabular-nums
|
|
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) }),
|
|
109
118
|
},
|
|
110
119
|
{
|
|
111
|
-
key: "status", header: "Status", width:
|
|
112
|
-
cell: (p) => (_jsxs("div", { className: "flex items-center gap-2", children: [statusOf(p), _jsx("span", { className: "
|
|
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) })] })),
|
|
113
122
|
},
|
|
114
123
|
];
|
|
115
124
|
const chips = [
|
|
116
125
|
...(period !== "all" ? [{ key: "period", label: PERIOD_LABEL[period] ?? period, icon: _jsx(CalendarClock, {}), onRemove: () => setPeriod("all") }] : []),
|
|
117
|
-
...(statusFilter !== "all" ? [{ key: "status", label: statusFilter
|
|
126
|
+
...(statusFilter !== "all" ? [{ key: "status", label: label(PROPOSAL_STATUS, statusFilter), icon: _jsx(CheckCircle2, {}), onRemove: () => setStatusFilter("all") }] : []),
|
|
118
127
|
];
|
|
119
|
-
return (_jsxs(MailboxPageFrame, { tab: "verwerkt", helpOpen: helpOpen, onHelpOpenChange: setHelpOpen,
|
|
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
|
|
120
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" }) })
|
|
121
|
-
: _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} · ` : "",
|
|
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 }) }))] }));
|
|
122
131
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MailboxProposalsPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxProposalsPage.tsx"],"names":[],"mappings":"
|
|
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"}
|
|
@@ -4,7 +4,7 @@ import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query";
|
|
|
4
4
|
import { useServerFn } from "@tanstack/react-start";
|
|
5
5
|
import { useNavigate } from "@tanstack/react-router";
|
|
6
6
|
import { Button, Badge, Card, CardContent, CardHeader, CardTitle, Input, Label, Textarea, Switch, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Dialog, DialogContent, DialogHeader, DialogTitle, toast, } from "@flowselections/core";
|
|
7
|
-
import { Check, X, Trash2, Plus, AlertTriangle, RefreshCw, CalendarClock, UserX,
|
|
7
|
+
import { Check, X, Trash2, Plus, AlertTriangle, RefreshCw, CalendarClock, UserX, Search, Inbox, ClipboardCheck } from "lucide-react";
|
|
8
8
|
import { getProposalDetail, approveProposal, rejectProposal, getStickerDefaults, listCustomersForPicker, createCustomerFromGroup, listProductsForPicker, listDeliveryLocationsForCustomer, listPickupLocations, listCarriersForPicker, getActiveOrdersMailbox, syncMailboxNow, refreshProposalsForAgentChange, reparseProposalNow, listBeladingOptionsForProducts, listAllowedLoadCarriers, getDeparturePreview, getAgreementPrices, countOutdatedProposals, } from "../lib/mailbox.functions.js";
|
|
9
9
|
import { normalizeUnitKind, toPlants, describeConversion, lineTotal, UNIT_KIND_LABEL, } from "../lib/mailbox-units.js";
|
|
10
10
|
import { enabledLoadCarriers, normalizeAllowedLoadCarriers, EMPTY_LOAD_CARRIERS } from "../lib/load-carriers.js";
|
|
@@ -13,10 +13,11 @@ import { SearchableSelect } from "./SearchableSelect.js";
|
|
|
13
13
|
import { AI_AUTH_PATH } from "./MailboxStatusLine.js";
|
|
14
14
|
import { listProposalsPage } from "../lib/mailbox-list.functions.js";
|
|
15
15
|
import { MailboxPageFrame, browserTz } from "./MailboxPageFrame.js";
|
|
16
|
-
import { DataTable, InitialsAvatar } from "./fs-ui/DataTable.js";
|
|
16
|
+
import { DataTable, InitialsAvatar, CellText } from "./fs-ui/DataTable.js";
|
|
17
|
+
import { FilterPanel, FilterOptions, PERIOD_OPTIONS } from "./fs-ui/FilterPanel.js";
|
|
18
|
+
import { fmtDay, fmtDayTime } from "../lib/format-date.js";
|
|
17
19
|
import { StatusIcon } from "./fs-ui/StatusIcon.js";
|
|
18
20
|
import { EmptyState } from "./fs-ui/EmptyState.js";
|
|
19
|
-
import { InlineAlert } from "./fs-ui/InlineAlert.js";
|
|
20
21
|
import { TableSkeleton } from "./fs-ui/TableSkeleton.js";
|
|
21
22
|
import { Toolbar } from "./fs-ui/Toolbar.js";
|
|
22
23
|
import { SearchInput } from "./fs-ui/SearchInput.js";
|
|
@@ -130,8 +131,14 @@ export function MailboxProposalsPage() {
|
|
|
130
131
|
const [listFilter, setListFilter] = usePersistedState("mailbox-proposals:filter", null);
|
|
131
132
|
const [cursor, setCursor] = useState(null);
|
|
132
133
|
const [helpOpen, setHelpOpen] = useState(false);
|
|
134
|
+
const [filtersOpen, setFiltersOpen] = useState(false);
|
|
133
135
|
const searchRef = useRef(null);
|
|
134
|
-
|
|
136
|
+
// Zware keuzelijsten (klanten, artikelen, …) pas laden zodra een voorstel
|
|
137
|
+
// voor het eerst geopend wordt; daarna blijven ze in de cache.
|
|
138
|
+
const [refsWanted, setRefsWanted] = useState(false);
|
|
139
|
+
useEffect(() => { if (activeId)
|
|
140
|
+
setRefsWanted(true); }, [activeId]);
|
|
141
|
+
const refsEnabled = refsWanted;
|
|
135
142
|
const refOpts = {
|
|
136
143
|
enabled: refsEnabled,
|
|
137
144
|
staleTime: 5 * 60000,
|
|
@@ -349,7 +356,7 @@ export function MailboxProposalsPage() {
|
|
|
349
356
|
}
|
|
350
357
|
};
|
|
351
358
|
useListHotkeys({
|
|
352
|
-
disabled: !!activeId || helpOpen,
|
|
359
|
+
disabled: !!activeId || helpOpen || filtersOpen,
|
|
353
360
|
onSearch: () => searchRef.current?.focus(),
|
|
354
361
|
onNext: () => setCursor((c) => (items.length ? Math.min((c ?? -1) + 1, items.length - 1) : null)),
|
|
355
362
|
onPrev: () => setCursor((c) => (items.length ? Math.max((c ?? 1) - 1, 0) : null)),
|
|
@@ -362,37 +369,41 @@ export function MailboxProposalsPage() {
|
|
|
362
369
|
...(period !== "all" ? [{ key: "period", label: periodLabel[period] ?? period, icon: _jsx(CalendarClock, {}), onRemove: () => setPeriod("all") }] : []),
|
|
363
370
|
...(listFilter === "klant_onbekend" ? [{ key: "klant", label: "Klant onbekend", icon: _jsx(UserX, {}), onRemove: () => setListFilter(null) }] : []),
|
|
364
371
|
];
|
|
372
|
+
const activeKpi = listFilter === "klant_onbekend" ? "customer_unknown" : period === "today" ? "today" : null;
|
|
365
373
|
const columns = [
|
|
366
374
|
{
|
|
367
|
-
key: "klant", header: "Klant", width:
|
|
375
|
+
key: "klant", header: "Klant", width: 220,
|
|
368
376
|
cell: (p) => {
|
|
369
377
|
const name = customerNameFor(p);
|
|
370
|
-
return (_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx(InitialsAvatar, { name: name }),
|
|
378
|
+
return (_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx(InitialsAvatar, { name: name }), _jsx(CellText, { primary: name, secondary: p.customer_name ? undefined : "Klant nog niet herkend", strong: true })] }));
|
|
371
379
|
},
|
|
372
380
|
},
|
|
373
381
|
{
|
|
374
|
-
key: "bericht", header: "Bericht", width:
|
|
375
|
-
cell: (p) =>
|
|
382
|
+
key: "bericht", header: "Bericht", width: 320,
|
|
383
|
+
cell: (p) => _jsx(CellText, { primary: p.mailbox_messages?.subject || "(geen onderwerp)", secondary: p.mailbox_messages?.preview || "Geen voorbeeldtekst" }),
|
|
376
384
|
},
|
|
377
385
|
{
|
|
378
|
-
key: "ontvangen_op", header: "
|
|
379
|
-
cell: (p) => _jsx(
|
|
386
|
+
key: "ontvangen_op", header: "Mailadres", width: 180,
|
|
387
|
+
cell: (p) => _jsx(CellText, { primary: p.mailbox_messages?.account_email || "–" }),
|
|
380
388
|
},
|
|
381
389
|
{
|
|
382
|
-
key: "datum", header: "Ontvangen", width:
|
|
383
|
-
cell: (p) => {
|
|
384
|
-
const d = p.mailbox_messages?.received_at ?? p.created_at;
|
|
385
|
-
return _jsx("span", { className: "tabular-nums text-muted-foreground", children: d ? new Date(d).toLocaleDateString("nl-NL") : "–" });
|
|
386
|
-
},
|
|
390
|
+
key: "datum", header: "Ontvangen", width: 132, sortable: true,
|
|
391
|
+
cell: (p) => _jsx("span", { className: "block truncate tabular-nums", children: fmtDayTime(p.mailbox_messages?.received_at ?? p.created_at) }),
|
|
387
392
|
},
|
|
388
393
|
{
|
|
389
|
-
key: "status", header: "Status", width:
|
|
394
|
+
key: "status", header: "Status", width: 120,
|
|
390
395
|
cell: (p) => (_jsxs("div", { className: "flex items-center gap-2", children: [p.readiness === "klaar"
|
|
391
396
|
? _jsx(StatusIcon, { tone: "done", label: "Klaar om te bevestigen" })
|
|
392
|
-
: _jsx(StatusIcon, { tone: "open", label: "Te controleren", detail: "Controleer de gegevens en bevestig de bestelling." }),
|
|
397
|
+
: _jsx(StatusIcon, { tone: "open", label: "Te controleren", detail: "Controleer de gegevens en bevestig de bestelling." }), p.duplicate && _jsx(StatusIcon, { tone: "busy", label: "Mogelijk dubbel", detail: "Deze mail lijkt op een eerdere bestelling." })] })),
|
|
393
398
|
},
|
|
394
399
|
];
|
|
395
|
-
|
|
400
|
+
const pageNotices = outdatedCount > 0 ? [{
|
|
401
|
+
key: "outdated",
|
|
402
|
+
tone: "warning",
|
|
403
|
+
title: `${outdatedCount} ${outdatedCount === 1 ? "bestelling is" : "bestellingen zijn"} met de vorige instelling uitgelezen.`,
|
|
404
|
+
action: (_jsx(Button, { size: "sm", variant: "outline", disabled: agentRefreshMut.isPending, onClick: () => agentRefreshMut.mutate(), children: agentRefreshMut.isPending ? "Bezig…" : "Opnieuw uitlezen" })),
|
|
405
|
+
}] : [];
|
|
406
|
+
return (_jsxs(MailboxPageFrame, { tab: "voorstellen", helpOpen: helpOpen, onHelpOpenChange: setHelpOpen, activeKpi: activeKpi, notices: pageNotices, onKpi: (k) => {
|
|
396
407
|
if (k === "today") {
|
|
397
408
|
setListFilter(null);
|
|
398
409
|
setPeriod("today");
|
|
@@ -408,11 +419,11 @@ export function MailboxProposalsPage() {
|
|
|
408
419
|
return true;
|
|
409
420
|
}
|
|
410
421
|
return false;
|
|
411
|
-
}, children: [
|
|
422
|
+
}, 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: "Klant", value: listFilter ?? "all", options: [{ value: "all", label: "Alle klanten" }, { value: "klant_onbekend", label: "Klant onbekend" }], onChange: (v) => setListFilter(v === "klant_onbekend" ? "klant_onbekend" : null) })] }), isLoading && !data ? (_jsx(TableSkeleton, { columns: [220, 320, 180, 132, 120] })) : listError && !data ? (_jsx("div", { className: "rounded-xl border border-border bg-card shadow-sm", children: _jsx(EmptyState, { icon: _jsx(Inbox, { className: "h-6 w-6" }), title: "De lijst kon niet worden geladen", text: "Je bestellingen zijn er nog; ze worden alleen nu niet getoond.", action: _jsx(Button, { size: "sm", variant: "outline", onClick: () => void refetchList(), children: "Opnieuw proberen" }) }) })) : total === 0 ? (_jsx("div", { className: "rounded-xl border border-border bg-card shadow-sm", children: hasActiveFilters ? (_jsx(EmptyState, { icon: _jsx(Search, { className: "h-6 w-6" }), title: "Niets gevonden", text: "Er zijn geen bestellingen die bij je zoekopdracht of filters passen.", action: _jsx(Button, { size: "sm", variant: "outline", onClick: clearFilters, children: "Filters wissen" }) })) : (_jsx(EmptyState, { icon: _jsx(Inbox, { className: "h-6 w-6" }), title: "Geen bestellingen om te controleren", text: "Nieuwe bestellingen uit je mailbox verschijnen hier vanzelf." })) })) : (_jsx(DataTable, { rows: items, columns: columns, rowKey: (p) => p.id, activeIndex: cursor, onRowClick: (p, i) => { setCursor(i); setActiveId(p.id); }, sort: { key: "datum", dir: sortOrder === "date-asc" ? "asc" : "desc" }, onSortChange: () => setSortOrder(sortOrder === "date-asc" ? "date-desc" : "date-asc"), mobileCard: (p) => {
|
|
412
423
|
const d = p.mailbox_messages?.received_at ?? p.created_at;
|
|
413
424
|
return (_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: customerNameFor(p) }), p.readiness === "klaar"
|
|
414
425
|
? _jsx(StatusIcon, { tone: "done", label: "Klaar om te bevestigen" })
|
|
415
|
-
: _jsx(StatusIcon, { tone: "open", label: "Te controleren" })] }), _jsx("div", { className: "truncate text-muted-foreground", children: p.mailbox_messages?.subject || "(geen onderwerp)" }), _jsxs("div", { className: "text-xs tabular-nums text-muted-foreground", children: [
|
|
426
|
+
: _jsx(StatusIcon, { tone: "open", label: "Te controleren" })] }), _jsx("div", { className: "truncate text-muted-foreground", children: p.mailbox_messages?.subject || "(geen onderwerp)" }), _jsxs("div", { className: "text-xs tabular-nums text-muted-foreground", children: [fmtDayTime(d), p.duplicate ? " · mogelijk dubbel" : ""] })] }));
|
|
416
427
|
}, footer: _jsx(TablePagination, { total: total, page: currentPage, pageSize: pageSize, onPageChange: setPage, onPageSizeChange: setPageSize }) })), _jsx("span", { className: "sr-only", "aria-live": "polite", children: dataUpdatedAt ? `${total} bestellingen` : "" }), _jsx(MobilePrimaryAction, { label: "Volgende controleren", icon: _jsx(ClipboardCheck, {}), onClick: () => void openOldest(), hidden: !!activeId || total === 0 }), _jsx(Dialog, { open: !!activeId, onOpenChange: (o) => { if (!o) {
|
|
417
428
|
setActiveId(null);
|
|
418
429
|
setReparseError(null);
|
|
@@ -694,8 +705,8 @@ function ProposalEditor({ proposal, template, customers, products, allowBackorde
|
|
|
694
705
|
const preview = (previewData?.ok ? previewData.preview : null);
|
|
695
706
|
const previewError = previewData?.ok === false ? String(previewData.error ?? "") : null;
|
|
696
707
|
const dateFeasible = preview?.ok === true && preview?.deliveryDateFeasible === true;
|
|
697
|
-
const fmtDate = (iso) => iso ?
|
|
698
|
-
const fmtDateTime = (iso) => iso ?
|
|
708
|
+
const fmtDate = (iso) => iso ? fmtDay(iso) : "";
|
|
709
|
+
const fmtDateTime = (iso) => iso ? fmtDayTime(iso) : "";
|
|
699
710
|
// ─── Prijsafspraak ter vergelijking ───────────────────────────────────────
|
|
700
711
|
const fetchAgreement = useServerFn(getAgreementPrices);
|
|
701
712
|
const agreementKey = lines.map((l) => `${l.product_id}:${l.quantity}`).join(",");
|
|
@@ -1127,7 +1138,7 @@ function NewCustomerDialog({ candidate, onClose, onCreated, }) {
|
|
|
1127
1138
|
}
|
|
1128
1139
|
function DynamicField({ field, value, meta, onChange, customers, products, plantTypes, suppliers, onSelectCustomer, }) {
|
|
1129
1140
|
const reviewClass = meta?.needs_review ? "border-warning/30 ring-1 ring-warning/40 rounded-md p-2" : "";
|
|
1130
|
-
const label = (_jsxs(Label, { className: "flex items-center gap-2", children: [field.label, field.required && _jsx("span", { className: "text-destructive", children: "*" }), meta?.needs_review && (_jsx(
|
|
1141
|
+
const label = (_jsxs(Label, { className: "flex items-center gap-2", children: [field.label, field.required && _jsx("span", { className: "text-destructive", children: "*" }), meta?.needs_review && (_jsx(StatusIcon, { tone: "busy", label: "Controleren", detail: "Dit veld is niet zeker uit de mail gehaald." })), typeof meta?.ai_confidence === "number" && (_jsxs("span", { className: "text-xs text-muted-foreground", children: [Math.round(meta.ai_confidence * 100), "%"] }))] }));
|
|
1131
1142
|
const renderControl = () => {
|
|
1132
1143
|
switch (field.field_type) {
|
|
1133
1144
|
case "text":
|
|
@@ -1172,12 +1183,9 @@ function ConfBadge({ meta, thresholds, }) {
|
|
|
1172
1183
|
if (!meta)
|
|
1173
1184
|
return null;
|
|
1174
1185
|
const c = Math.max(0, Math.min(1, Number(meta.confidence ?? 0)));
|
|
1175
|
-
const
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
? "border-warning/30 bg-warning-soft text-warning-soft-foreground"
|
|
1179
|
-
: "border-destructive/40 bg-destructive/5 text-destructive";
|
|
1180
|
-
return (_jsxs("span", { className: `inline-flex items-center rounded border px-1.5 py-0.5 text-[11px] ${cls}`, children: [Math.round(c * 100), "% \u00B7 ", meta.source] }));
|
|
1186
|
+
const tone = c >= thresholds.green ? "done" : c >= thresholds.orange ? "busy" : "error";
|
|
1187
|
+
const text = c >= thresholds.green ? "Zeker" : c >= thresholds.orange ? "Controleren" : "Onzeker";
|
|
1188
|
+
return (_jsxs("span", { className: "inline-flex items-center gap-1 text-[11px] font-normal text-muted-foreground", children: [_jsx(StatusIcon, { tone: tone, label: text, detail: `${Math.round(c * 100)}% zeker · ${meta.source}` }), text] }));
|
|
1181
1189
|
}
|
|
1182
1190
|
function LateSignupChoice({ open, setOpen, reason, setReason }) {
|
|
1183
1191
|
if (!open) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MailboxSkippedPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxSkippedPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MailboxSkippedPage.d.ts","sourceRoot":"","sources":["../../src/components/MailboxSkippedPage.tsx"],"names":[],"mappings":"AA0CA,wBAAgB,kBAAkB,gCAoKjC"}
|