@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
|
@@ -2,28 +2,29 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
4
|
import { useServerFn } from "@tanstack/react-start";
|
|
5
|
-
import { CalendarClock,
|
|
6
|
-
import { Button,
|
|
5
|
+
import { CalendarClock, MailX, Search } from "lucide-react";
|
|
6
|
+
import { Button, toast } from "@flowselections/core";
|
|
7
|
+
import { fmtDayTime } from "../lib/format-date.js";
|
|
8
|
+
import { label, ANALYSIS_STATE, FILTER_OUTCOME } from "../lib/labels.js";
|
|
7
9
|
import { listSkippedMessages, analyzeMessageNow } from "../lib/mailbox.functions.js";
|
|
8
10
|
import { TablePagination } from "./TablePagination.js";
|
|
9
11
|
import { usePersistedState, normalizeText, matchesPeriod } from "./MailboxFilterBar.js";
|
|
10
12
|
import { MailboxPageFrame } from "./MailboxPageFrame.js";
|
|
11
|
-
import { DataTable } from "./fs-ui/DataTable.js";
|
|
13
|
+
import { DataTable, CellText } from "./fs-ui/DataTable.js";
|
|
14
|
+
import { FilterPanel, FilterOptions, PERIOD_OPTIONS } from "./fs-ui/FilterPanel.js";
|
|
12
15
|
import { StatusIcon } from "./fs-ui/StatusIcon.js";
|
|
13
16
|
import { EmptyState } from "./fs-ui/EmptyState.js";
|
|
14
|
-
import { InlineAlert } from "./fs-ui/InlineAlert.js";
|
|
15
17
|
import { TableSkeleton } from "./fs-ui/TableSkeleton.js";
|
|
16
18
|
import { Toolbar } from "./fs-ui/Toolbar.js";
|
|
17
19
|
import { SearchInput } from "./fs-ui/SearchInput.js";
|
|
18
20
|
import { FilterChips } from "./fs-ui/FilterChips.js";
|
|
19
21
|
import { useListHotkeys } from "./fs-ui/useListHotkeys.js";
|
|
22
|
+
import { RowMenu } from "./fs-ui/RowMenu.js";
|
|
20
23
|
const statusOf = (m) => m.analysis_state === "wacht"
|
|
21
|
-
? { label:
|
|
22
|
-
: m.filter_outcome === "te_oud"
|
|
23
|
-
? { label:
|
|
24
|
-
:
|
|
25
|
-
? { label: "Geen bestelling", tone: "grey" }
|
|
26
|
-
: { label: "Overgeslagen", tone: "grey" };
|
|
24
|
+
? { label: label(ANALYSIS_STATE, "wacht"), tone: "amber" }
|
|
25
|
+
: m.filter_outcome === "te_oud" || m.filter_outcome === "geen_bestelling"
|
|
26
|
+
? { label: label(FILTER_OUTCOME, m.filter_outcome), tone: "grey" }
|
|
27
|
+
: { label: "Overgeslagen", tone: "grey" };
|
|
27
28
|
/** Reden in gewone woorden (codes uit de verwerking worden vertaald). */
|
|
28
29
|
const reasonText = (m) => {
|
|
29
30
|
const raw = String(m.analysis_note || m.filter_reason || "");
|
|
@@ -33,6 +34,11 @@ const reasonText = (m) => {
|
|
|
33
34
|
return "Uit een map die niet gevolgd wordt";
|
|
34
35
|
if (/map onbekend/i.test(raw))
|
|
35
36
|
return "Map niet bekend";
|
|
37
|
+
// Nooit ruwe codes (snake_case, HOOFDLETTERCODES, [haakjes]) in beeld.
|
|
38
|
+
if (/[a-z]+_[a-z_]+|\b[A-Z]{2,}_[A-Z_]+\b|\[[^\]]*\]/.test(raw)) {
|
|
39
|
+
console.warn("[mailbox] reden", raw);
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
36
42
|
return raw;
|
|
37
43
|
};
|
|
38
44
|
const PERIOD_LABEL = { today: "Vandaag", "7d": "Laatste 7 dagen", "30d": "Laatste 30 dagen" };
|
|
@@ -55,6 +61,7 @@ export function MailboxSkippedPage() {
|
|
|
55
61
|
const [sortOrder, setSortOrder] = usePersistedState("mailbox-skipped:sort", "date-desc");
|
|
56
62
|
const [cursor, setCursor] = useState(null);
|
|
57
63
|
const [helpOpen, setHelpOpen] = useState(false);
|
|
64
|
+
const [filtersOpen, setFiltersOpen] = useState(false);
|
|
58
65
|
const searchRef = useRef(null);
|
|
59
66
|
const filtered = useMemo(() => {
|
|
60
67
|
const q = normalizeText(search).trim();
|
|
@@ -102,38 +109,43 @@ export function MailboxSkippedPage() {
|
|
|
102
109
|
}
|
|
103
110
|
};
|
|
104
111
|
useListHotkeys({
|
|
105
|
-
disabled: helpOpen,
|
|
112
|
+
disabled: helpOpen || filtersOpen,
|
|
106
113
|
onSearch: () => searchRef.current?.focus(),
|
|
107
114
|
onNext: () => setCursor((c) => (paged.length ? Math.min((c ?? -1) + 1, paged.length - 1) : null)),
|
|
108
115
|
onPrev: () => setCursor((c) => (paged.length ? Math.max((c ?? 1) - 1, 0) : null)),
|
|
109
116
|
onEscape: hasActiveFilters ? clearFilters : undefined,
|
|
110
117
|
onHelp: () => setHelpOpen(true),
|
|
111
118
|
});
|
|
112
|
-
const
|
|
119
|
+
const rowAction = (m) => (_jsx(RowMenu, { label: `Acties voor ${m.from_name || m.from_email || "mail"}`, items: [{ label: busyId === m.id ? "Bezig…" : m.analysis_state === "wacht" ? "Nu uitlezen" : "Toch uitlezen", disabled: busyId === m.id, onSelect: () => void run(m.id) }] }));
|
|
113
120
|
const columns = [
|
|
114
121
|
{
|
|
115
|
-
key: "afzender", header: "Afzender", width:
|
|
116
|
-
cell: (m) =>
|
|
122
|
+
key: "afzender", header: "Afzender", width: 200,
|
|
123
|
+
cell: (m) => _jsx(CellText, { primary: m.from_name || m.from_email || "Onbekend", secondary: m.from_name ? m.from_email : undefined, strong: true }),
|
|
117
124
|
},
|
|
118
125
|
{
|
|
119
|
-
key: "bericht", header: "Bericht", width:
|
|
120
|
-
cell: (m) =>
|
|
126
|
+
key: "bericht", header: "Bericht", width: 300,
|
|
127
|
+
cell: (m) => _jsx(CellText, { primary: m.subject || "(geen onderwerp)", secondary: m.preview || "" }),
|
|
121
128
|
},
|
|
122
129
|
{
|
|
123
|
-
key: "datum", header: "Ontvangen", width:
|
|
124
|
-
cell: (m) => _jsx("span", { className: "tabular-nums
|
|
130
|
+
key: "datum", header: "Ontvangen", width: 132, sortable: true,
|
|
131
|
+
cell: (m) => _jsx("span", { className: "block truncate tabular-nums", children: fmtDayTime(m.received_at) }),
|
|
125
132
|
},
|
|
126
133
|
{
|
|
127
|
-
key: "reden", header: "Reden", width:
|
|
134
|
+
key: "reden", header: "Reden", width: 200,
|
|
128
135
|
cell: (m) => {
|
|
129
136
|
const st = statusOf(m);
|
|
130
|
-
return (_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(StatusIcon, { tone: st.tone === "amber" ? "busy" : "open", label: st.label, detail: reasonText(m) || undefined }),
|
|
137
|
+
return (_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(StatusIcon, { tone: st.tone === "amber" ? "busy" : "open", label: st.label, detail: reasonText(m) || undefined }), _jsx(CellText, { primary: st.label, secondary: reasonText(m) })] }));
|
|
131
138
|
},
|
|
132
139
|
},
|
|
133
|
-
{ key: "actie", header: "", width: "130px", cell: (m) => actionBtn(m) },
|
|
134
140
|
];
|
|
135
141
|
const chips = period !== "all" ? [{ key: "period", label: PERIOD_LABEL[period] ?? period, icon: _jsx(CalendarClock, {}), onRemove: () => setPeriod("all") }] : [];
|
|
136
|
-
|
|
142
|
+
const notices = waiting > 0 ? [{
|
|
143
|
+
key: "waiting",
|
|
144
|
+
tone: "warning",
|
|
145
|
+
title: `${waiting} ${waiting === 1 ? "mail wacht" : "mails wachten"} op uitlezen.`,
|
|
146
|
+
details: "De limiet voor vandaag is bereikt of automatisch uitlezen staat uit. Je kunt een mail ook zelf laten uitlezen.",
|
|
147
|
+
}] : [];
|
|
148
|
+
return (_jsxs(MailboxPageFrame, { tab: "overgeslagen", helpOpen: helpOpen, onHelpOpenChange: setHelpOpen, notices: notices, 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 afzender, onderwerp of reden" }) }), _jsx(FilterPanel, { open: filtersOpen, onOpenChange: setFiltersOpen, onClear: clearFilters, children: _jsx(FilterOptions, { label: "Periode", value: period, options: PERIOD_OPTIONS, onChange: (v) => setPeriod(v) }) }), isLoading && !data ? (_jsx(TableSkeleton, { columns: [200, 300, 132, 200] })) : error && !data ? (_jsx("div", { className: "rounded-xl border border-border bg-card shadow-sm", children: _jsx(EmptyState, { icon: _jsx(MailX, { 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" }) }) })) : filtered.length === 0 ? (_jsx("div", { className: "rounded-xl border border-border bg-card shadow-sm", children: hasActiveFilters
|
|
137
149
|
? _jsx(EmptyState, { icon: _jsx(Search, { className: "h-6 w-6" }), title: "Niets gevonden", text: "Er zijn geen overgeslagen mails die bij je zoekopdracht passen.", action: _jsx(Button, { size: "sm", variant: "outline", onClick: clearFilters, children: "Filters wissen" }) })
|
|
138
|
-
: _jsx(EmptyState, { icon: _jsx(MailX, { className: "h-6 w-6" }), title: "Geen overgeslagen mails", text: "Mails die geen bestelling lijken, verschijnen hier met de reden." }) })) : (_jsx(DataTable, { rows: paged, columns: columns, rowKey: (m) => m.id, activeIndex: cursor, sort: { key: "datum", dir: sortOrder === "date-asc" ? "asc" : "desc" }, onSortChange: () => setSortOrder(sortOrder === "date-asc" ? "date-desc" : "date-asc"), mobileCard: (m) => (_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsx("div", { className: "truncate font-semibold", children: m.from_name || m.from_email || "Onbekend" }), _jsx("div", { className: "truncate text-muted-foreground", children: m.subject || "(geen onderwerp)" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [
|
|
150
|
+
: _jsx(EmptyState, { icon: _jsx(MailX, { className: "h-6 w-6" }), title: "Geen overgeslagen mails", text: "Mails die geen bestelling lijken, verschijnen hier met de reden." }) })) : (_jsx(DataTable, { rows: paged, columns: columns, rowKey: (m) => m.id, activeIndex: cursor, rowAction: rowAction, sort: { key: "datum", dir: sortOrder === "date-asc" ? "asc" : "desc" }, onSortChange: () => setSortOrder(sortOrder === "date-asc" ? "date-desc" : "date-asc"), mobileCard: (m) => (_jsxs("div", { className: "min-w-0 space-y-1", children: [_jsx("div", { className: "truncate font-semibold", children: m.from_name || m.from_email || "Onbekend" }), _jsx("div", { className: "truncate text-muted-foreground", children: m.subject || "(geen onderwerp)" }), _jsxs("div", { className: "text-xs text-muted-foreground", children: [fmtDayTime(m.received_at), " \u00B7 ", statusOf(m).label] })] })), footer: _jsx(TablePagination, { total: filtered.length, page: current, pageSize: pageSize, onPageChange: setPage, onPageSizeChange: setPageSize }) }))] }));
|
|
139
151
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type MailboxStatus } from "../lib/mailbox.functions.js";
|
|
3
|
+
import type { Notice } from "./fs-ui/NoticeBar.js";
|
|
1
4
|
/** Pad naar de AI-koppelingen in de shell (Instellingen › AI-Authenticatie). */
|
|
2
5
|
export declare const AI_AUTH_PATH = "/ai-authenticatie";
|
|
3
|
-
|
|
4
|
-
|
|
6
|
+
export declare function useMailboxStatus(): MailboxStatus | null;
|
|
7
|
+
/** Inhoud van de infoballon bij "Bijgewerkt": wanneer opgehaald, hoeveel wachten, adressen. */
|
|
8
|
+
export declare function statusInfo(s: MailboxStatus | null): ReactNode;
|
|
5
9
|
/** Meldingen over het ophalen (alleen als de gebruiker er iets mee moet). */
|
|
6
|
-
export declare function
|
|
10
|
+
export declare function useMailboxStatusNotices(s: MailboxStatus | null): Notice[];
|
|
7
11
|
//# sourceMappingURL=MailboxStatusLine.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MailboxStatusLine.d.ts","sourceRoot":"","sources":["../../src/components/MailboxStatusLine.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MailboxStatusLine.d.ts","sourceRoot":"","sources":["../../src/components/MailboxStatusLine.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEhF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,gFAAgF;AAChF,eAAO,MAAM,YAAY,sBAAsB,CAAC;AAEhD,wBAAgB,gBAAgB,IAOL,aAAa,GAAG,IAAI,CAC9C;AAED,+FAA+F;AAC/F,wBAAgB,UAAU,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,CAU7D;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,EAAE,CA0CzE"}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useQuery } from "@tanstack/react-query";
|
|
3
3
|
import { useServerFn } from "@tanstack/react-start";
|
|
4
4
|
import { useIsSuperadmin } from "@flowselections/core";
|
|
5
5
|
import { getMailboxStatus } from "../lib/mailbox.functions.js";
|
|
6
|
-
import {
|
|
6
|
+
import { fmtDayTime } from "../lib/format-date.js";
|
|
7
7
|
/** Pad naar de AI-koppelingen in de shell (Instellingen › AI-Authenticatie). */
|
|
8
8
|
export const AI_AUTH_PATH = "/ai-authenticatie";
|
|
9
|
-
|
|
10
|
-
function useMailboxStatus() {
|
|
9
|
+
export function useMailboxStatus() {
|
|
11
10
|
const fetchStatus = useServerFn(getMailboxStatus);
|
|
12
11
|
const { data } = useQuery({
|
|
13
12
|
queryKey: ["mailbox-status"],
|
|
@@ -16,31 +15,55 @@ function useMailboxStatus() {
|
|
|
16
15
|
});
|
|
17
16
|
return (data ?? null);
|
|
18
17
|
}
|
|
19
|
-
/**
|
|
20
|
-
export function
|
|
21
|
-
const s = useMailboxStatus();
|
|
18
|
+
/** Inhoud van de infoballon bij "Bijgewerkt": wanneer opgehaald, hoeveel wachten, adressen. */
|
|
19
|
+
export function statusInfo(s) {
|
|
22
20
|
if (!s)
|
|
23
21
|
return null;
|
|
24
22
|
const addresses = Array.isArray(s.addresses) ? s.addresses : [];
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
parts.push(last ? `Mails opgehaald ${last}` : "Nog geen mails opgehaald");
|
|
23
|
+
const lines = [];
|
|
24
|
+
lines.push(s.lastOkAt ? `Mails opgehaald ${fmtDayTime(s.lastOkAt)}` : "Nog geen mails opgehaald");
|
|
28
25
|
if (s.waiting > 0)
|
|
29
|
-
|
|
26
|
+
lines.push(`${s.waiting} wachten op verwerking`);
|
|
30
27
|
if (addresses.length > 0)
|
|
31
|
-
|
|
28
|
+
lines.push(addresses.join(", "));
|
|
32
29
|
if (s.ignoredNotFollowed > 0)
|
|
33
|
-
|
|
30
|
+
lines.push(`${s.ignoredNotFollowed} uit mappen die niet gevolgd worden`);
|
|
34
31
|
if (s.ignoredUnknown > 0)
|
|
35
|
-
|
|
36
|
-
return _jsx("
|
|
32
|
+
lines.push(`${s.ignoredUnknown} uit een onbekende map overgeslagen`);
|
|
33
|
+
return _jsx(_Fragment, { children: lines.map((l) => _jsx("div", { className: "truncate", children: l }, l)) });
|
|
37
34
|
}
|
|
38
35
|
/** Meldingen over het ophalen (alleen als de gebruiker er iets mee moet). */
|
|
39
|
-
export function
|
|
40
|
-
const s = useMailboxStatus();
|
|
36
|
+
export function useMailboxStatusNotices(s) {
|
|
41
37
|
const { isAdmin } = useIsSuperadmin();
|
|
42
38
|
if (!s)
|
|
43
|
-
return
|
|
39
|
+
return [];
|
|
40
|
+
const out = [];
|
|
44
41
|
const disconnected = Array.isArray(s.disconnected) ? s.disconnected : [];
|
|
45
|
-
|
|
42
|
+
if (disconnected.length > 0) {
|
|
43
|
+
const n = disconnected.reduce((a, d) => a + (Number(d.count) || 0), 0);
|
|
44
|
+
out.push({
|
|
45
|
+
key: "disconnected",
|
|
46
|
+
tone: "info",
|
|
47
|
+
title: `${n} mails niet verwerkt: adres ontkoppeld`,
|
|
48
|
+
details: `Mails van ${disconnected.length === 1 ? "een ontkoppeld adres" : `${disconnected.length} ontkoppelde adressen`} worden niet verwerkt. Koppel het adres opnieuw bij Instellingen.`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (s.problem) {
|
|
52
|
+
out.push({
|
|
53
|
+
key: "problem",
|
|
54
|
+
tone: "error",
|
|
55
|
+
title: "Er komen mails binnen, maar ze worden nu niet verwerkt.",
|
|
56
|
+
details: (_jsxs(_Fragment, { children: ["We proberen het elke minuut opnieuw. Blijft dit staan, neem dan contact op met je beheerder.", isAdmin && (_jsxs("details", { className: "mt-1 text-xs text-muted-foreground", children: [_jsx("summary", { className: "cursor-pointer", children: "Details" }), s.problem] }))] })),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (!s.aiAgent) {
|
|
60
|
+
out.push({
|
|
61
|
+
key: "ai",
|
|
62
|
+
tone: "warning",
|
|
63
|
+
title: "Automatisch uitlezen staat uit.",
|
|
64
|
+
details: "Bestellingen komen wel binnen, maar je vult ze zelf in. Een beheerder kan dit aanzetten bij Instellingen.",
|
|
65
|
+
action: _jsx("a", { href: AI_AUTH_PATH, className: "text-sm font-medium underline underline-offset-2", children: "Naar Instellingen" }),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
46
69
|
}
|
|
@@ -6,7 +6,7 @@ type Props = {
|
|
|
6
6
|
onPageChange: (page: number) => void;
|
|
7
7
|
onPageSizeChange: (size: number) => void;
|
|
8
8
|
};
|
|
9
|
-
/** Paginering
|
|
10
|
-
export declare function TablePagination({ total, page, pageSize, onPageChange, onPageSizeChange
|
|
9
|
+
/** Paginering exact als Verkooporders: "1–25 van 120" · Per pagina · pijlen met "1 / 5". */
|
|
10
|
+
export declare function TablePagination({ total, page, pageSize, onPageChange, onPageSizeChange }: Props): import("react").JSX.Element;
|
|
11
11
|
export {};
|
|
12
12
|
//# sourceMappingURL=TablePagination.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TablePagination.d.ts","sourceRoot":"","sources":["../../src/components/TablePagination.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TablePagination.d.ts","sourceRoot":"","sources":["../../src/components/TablePagination.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,iBAAiB,UAAoB,CAAC;AAEnD,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,CAAC;AAEF,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE,EAAE,KAAK,+BAkC/F"}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
2
3
|
import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
|
|
3
|
-
export const PAGE_SIZE_OPTIONS = [10,
|
|
4
|
-
/** Paginering
|
|
5
|
-
export function TablePagination({ total, page, pageSize, onPageChange, onPageSizeChange
|
|
6
|
-
const
|
|
7
|
-
const current = Math.min(Math.max(1, page),
|
|
8
|
-
|
|
9
|
-
const to = Math.min(current * pageSize, total);
|
|
10
|
-
return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-t px-4 py-2 text-sm", children: [_jsxs("span", { className: "text-muted-foreground", children: [from, "\u2013", to, " van ", total] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: "text-muted-foreground", children: "Per pagina" }), _jsxs(Select, { value: String(pageSize), onValueChange: (v) => {
|
|
11
|
-
onPageSizeChange(Number(v));
|
|
12
|
-
onPageChange(1);
|
|
13
|
-
}, children: [_jsx(SelectTrigger, { className: "h-8 w-[80px]", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: PAGE_SIZE_OPTIONS.map((n) => (_jsx(SelectItem, { value: String(n), children: n }, n))) })] }), _jsx(Button, { size: "sm", variant: "outline", disabled: current <= 1, onClick: () => onPageChange(current - 1), children: "Vorige" }), _jsxs("span", { className: "text-muted-foreground", children: [current, " / ", pageCount] }), _jsx(Button, { size: "sm", variant: "outline", disabled: current >= pageCount, onClick: () => onPageChange(current + 1), children: "Volgende" })] })] }));
|
|
4
|
+
export const PAGE_SIZE_OPTIONS = [10, 20, 50, 100];
|
|
5
|
+
/** Paginering exact als Verkooporders: "1–25 van 120" · Per pagina · pijlen met "1 / 5". */
|
|
6
|
+
export function TablePagination({ total, page, pageSize, onPageChange, onPageSizeChange }) {
|
|
7
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
8
|
+
const current = Math.min(Math.max(1, page), totalPages);
|
|
9
|
+
return (_jsxs("nav", { "aria-label": "Paginering", className: "shrink-0 flex flex-wrap items-center justify-between gap-3 text-sm text-muted-foreground", children: [_jsx("div", { className: "tabular-nums", children: total === 0 ? "Geen resultaten" : `${(current - 1) * pageSize + 1}–${Math.min(current * pageSize, total)} van ${total}` }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "hidden items-center gap-2 sm:flex", children: [_jsx("span", { children: "Per pagina" }), _jsxs(Select, { value: String(pageSize), onValueChange: (v) => { onPageSizeChange(Number(v)); onPageChange(1); }, children: [_jsx(SelectTrigger, { className: "h-8 w-20", "aria-label": "Resultaten per pagina", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: PAGE_SIZE_OPTIONS.map((n) => (_jsx(SelectItem, { value: String(n), children: n }, n))) })] })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Button, { variant: "outline", size: "icon", className: "h-10 w-10 sm:h-8 sm:w-8", onClick: () => onPageChange(Math.max(1, current - 1)), disabled: current <= 1, "aria-label": "Vorige pagina", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) }), _jsxs("div", { className: "px-2 text-foreground tabular-nums", "aria-current": "page", children: [current, " / ", totalPages] }), _jsx(Button, { variant: "outline", size: "icon", className: "h-10 w-10 sm:h-8 sm:w-8", onClick: () => onPageChange(Math.min(totalPages, current + 1)), disabled: current >= totalPages, "aria-label": "Volgende pagina", children: _jsx(ChevronRight, { className: "h-4 w-4" }) })] })] })] }));
|
|
14
10
|
}
|
|
@@ -2,16 +2,16 @@ import { type ReactNode } from "react";
|
|
|
2
2
|
export interface DataColumn<T> {
|
|
3
3
|
key: string;
|
|
4
4
|
header: string;
|
|
5
|
-
/**
|
|
6
|
-
width:
|
|
5
|
+
/** Vaste breedte in px (colgroup, table-fixed). */
|
|
6
|
+
width: number;
|
|
7
7
|
cell: (row: T) => ReactNode;
|
|
8
8
|
/** Klikbare kop voor sorteren. */
|
|
9
9
|
sortable?: boolean;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* Tabel
|
|
13
|
-
*
|
|
14
|
-
* Onder 768 px als kaarten (mobileCard).
|
|
12
|
+
* Tabel exact als Verkooporders: eigen <table> met table-fixed en colgroup in px,
|
|
13
|
+
* kop 40 px die plakt, rij 60 px, geselecteerde rij met 3 px groene streep.
|
|
14
|
+
* Onder 768 px als kaarten (mobileCard). Paginering (footer) staat onder het vlak.
|
|
15
15
|
*/
|
|
16
16
|
export declare function DataTable<T>({ rows, columns, rowKey, onRowClick, activeIndex, mobileCard, sort, onSortChange, footer, rowAction, }: {
|
|
17
17
|
rows: T[];
|
|
@@ -26,9 +26,15 @@ export declare function DataTable<T>({ rows, columns, rowKey, onRowClick, active
|
|
|
26
26
|
} | null;
|
|
27
27
|
onSortChange?: (key: string) => void;
|
|
28
28
|
footer?: ReactNode;
|
|
29
|
-
/**
|
|
29
|
+
/** Rijmenu of knop rechts in de rij; klik opent de rij niet. */
|
|
30
30
|
rowAction?: (row: T) => ReactNode;
|
|
31
31
|
}): import("react").JSX.Element;
|
|
32
|
+
/** Cel met hooguit twee regels: 14 px en 12 px grijs; afkappen met volledige tekst als infoballon. */
|
|
33
|
+
export declare function CellText({ primary, secondary, strong }: {
|
|
34
|
+
primary: ReactNode;
|
|
35
|
+
secondary?: ReactNode;
|
|
36
|
+
strong?: boolean;
|
|
37
|
+
}): import("react").JSX.Element;
|
|
32
38
|
/** Initialen-avatar voor klantnamen. */
|
|
33
39
|
export declare function InitialsAvatar({ name }: {
|
|
34
40
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/DataTable.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG1D,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,
|
|
1
|
+
{"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/DataTable.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG1D,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,SAAS,CAAC;IAC5B,kCAAkC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAQD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,EAC3B,IAAI,EACJ,OAAO,EACP,MAAM,EACN,UAAU,EACV,WAAW,EACX,UAAU,EACV,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,SAAS,GACV,EAAE;IACD,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IACzB,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,SAAS,CAAC;IAClC,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACnD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,gEAAgE;IAChE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,SAAS,CAAC;CACnC,+BA0FA;AAED,sGAAsG;AACtG,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;IAAE,OAAO,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,+BASvH;AAED,wCAAwC;AACxC,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,+BAQxD"}
|
|
@@ -1,29 +1,36 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from "react";
|
|
3
3
|
import { ArrowDown, ArrowUp, ChevronRight } from "lucide-react";
|
|
4
|
+
const TABLE_CLS = "w-full [&_thead_th]:h-10 [&_thead_th]:text-[11px] [&_thead_th]:font-semibold [&_thead_th]:uppercase [&_thead_th]:tracking-wide [&_thead_th]:text-muted-foreground min-w-[900px] table-fixed caption-bottom text-sm [&_thead_th]:sticky [&_thead_th]:top-0 [&_thead_th]:z-10 [&_thead_th]:bg-card [&_tbody_tr]:transition-colors";
|
|
5
|
+
/** Kop- en celruimte zijn gelijk, zodat koptekst en celtekst op dezelfde x beginnen. */
|
|
6
|
+
const PAD = "px-2";
|
|
4
7
|
/**
|
|
5
|
-
* Tabel
|
|
6
|
-
*
|
|
7
|
-
* Onder 768 px als kaarten (mobileCard).
|
|
8
|
+
* Tabel exact als Verkooporders: eigen <table> met table-fixed en colgroup in px,
|
|
9
|
+
* kop 40 px die plakt, rij 60 px, geselecteerde rij met 3 px groene streep.
|
|
10
|
+
* Onder 768 px als kaarten (mobileCard). Paginering (footer) staat onder het vlak.
|
|
8
11
|
*/
|
|
9
12
|
export function DataTable({ rows, columns, rowKey, onRowClick, activeIndex, mobileCard, sort, onSortChange, footer, rowAction, }) {
|
|
10
|
-
const
|
|
13
|
+
const hasEnd = !!(rowAction || onRowClick);
|
|
11
14
|
const activeRef = useRef(null);
|
|
12
15
|
useEffect(() => { activeRef.current?.scrollIntoView({ block: "nearest" }); }, [activeIndex]);
|
|
13
|
-
return (_jsxs("div", { className: "flex min-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
(onRowClick ? "
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex w-full min-w-0 flex-col overflow-clip rounded-xl border border-border bg-card shadow-sm", children: [_jsx("div", { className: "hidden md:block", children: _jsxs("table", { className: TABLE_CLS, children: [_jsxs("colgroup", { children: [columns.map((c) => _jsx("col", { style: { width: `${c.width}px` } }, c.key)), hasEnd && _jsx("col", { style: { width: "56px" } })] }), _jsx("thead", { className: "[&_tr]:border-b", children: _jsxs("tr", { className: "border-b border-border", children: [columns.map((c) => {
|
|
17
|
+
const active = sort?.key === c.key;
|
|
18
|
+
return (_jsx("th", { scope: "col", className: PAD + " text-left align-middle", "aria-sort": active ? (sort.dir === "asc" ? "ascending" : "descending") : undefined, children: c.sortable && onSortChange ? (_jsxs("button", { type: "button", onClick: () => onSortChange(c.key), className: "inline-flex max-w-full items-center gap-1 rounded-sm uppercase focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", "aria-label": `Sorteer op ${c.header}`, children: [_jsx("span", { className: "truncate", children: c.header }), active && (sort.dir === "asc" ? _jsx(ArrowUp, { className: "h-3 w-3 shrink-0", "aria-hidden": true }) : _jsx(ArrowDown, { className: "h-3 w-3 shrink-0", "aria-hidden": true }))] })) : _jsx("span", { className: "block truncate", children: c.header }) }, c.key));
|
|
19
|
+
}), hasEnd && _jsx("th", { scope: "col", className: PAD, children: _jsx("span", { className: "sr-only", children: "Acties" }) })] }) }), _jsx("tbody", { children: rows.map((r, i) => {
|
|
20
|
+
const active = activeIndex === i;
|
|
21
|
+
return (_jsxs("tr", { ref: active ? activeRef : undefined, tabIndex: onRowClick ? 0 : undefined, "data-state": active ? "selected" : undefined, onClick: onRowClick ? () => onRowClick(r, i) : undefined, onKeyDown: onRowClick ? (e) => { if (e.key === "Enter" && e.target === e.currentTarget) {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
onRowClick(r, i);
|
|
24
|
+
} } : undefined, style: { height: 60 }, className: "group border-b border-border text-foreground last:border-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring " +
|
|
25
|
+
(onRowClick ? "cursor-pointer hover:bg-muted/50 " : "") +
|
|
26
|
+
(active ? "bg-muted/60" : ""), children: [columns.map((c, ci) => (_jsxs("td", { className: PAD + " relative overflow-hidden py-0 align-middle", children: [ci === 0 && active && _jsx("span", { "aria-hidden": true, className: "absolute inset-y-0 left-0 w-[3px] bg-primary" }), c.cell(r)] }, c.key))), hasEnd && (_jsx("td", { className: PAD + " py-0 text-right align-middle", onClick: rowAction ? (e) => e.stopPropagation() : undefined, children: rowAction ? rowAction(r) : (_jsx(ChevronRight, { className: "ml-auto h-4 w-4 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100", "aria-hidden": true })) }))] }, rowKey(r)));
|
|
27
|
+
}) })] }) }), _jsx("div", { className: "divide-y divide-border md:hidden", children: rows.map((r, i) => (_jsxs("div", { role: onRowClick ? "button" : undefined, tabIndex: onRowClick ? 0 : undefined, onClick: onRowClick ? () => onRowClick(r, i) : undefined, className: "relative min-h-11 p-4 text-sm transition-colors active:bg-muted " + (activeIndex === i ? "bg-muted/60" : ""), children: [activeIndex === i && _jsx("span", { "aria-hidden": true, className: "absolute inset-y-0 left-0 w-[3px] bg-primary" }), _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "min-w-0 flex-1", children: mobileCard(r) }), rowAction && _jsx("div", { onClick: (e) => e.stopPropagation(), children: rowAction(r) })] })] }, rowKey(r)))) })] }), footer] }));
|
|
28
|
+
}
|
|
29
|
+
/** Cel met hooguit twee regels: 14 px en 12 px grijs; afkappen met volledige tekst als infoballon. */
|
|
30
|
+
export function CellText({ primary, secondary, strong }) {
|
|
31
|
+
const t1 = typeof primary === "string" ? primary : undefined;
|
|
32
|
+
const t2 = typeof secondary === "string" ? secondary : undefined;
|
|
33
|
+
return (_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm " + (strong ? "font-medium" : ""), title: t1, children: primary }), secondary != null && secondary !== "" && _jsx("div", { className: "truncate text-xs text-muted-foreground", title: t2, children: secondary })] }));
|
|
27
34
|
}
|
|
28
35
|
/** Initialen-avatar voor klantnamen. */
|
|
29
36
|
export function InitialsAvatar({ name }) {
|
|
@@ -5,7 +5,7 @@ export interface FilterChip {
|
|
|
5
5
|
icon?: ReactNode;
|
|
6
6
|
onRemove: () => void;
|
|
7
7
|
}
|
|
8
|
-
/** Actieve filters als
|
|
8
|
+
/** Actieve filters als FilterChip uit Verkooporders (hele chip wist), plus "Alles wissen". */
|
|
9
9
|
export declare function FilterChips({ chips, onClearAll }: {
|
|
10
10
|
chips: FilterChip[];
|
|
11
11
|
onClearAll?: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterChips.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/FilterChips.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,
|
|
1
|
+
{"version":3,"file":"FilterChips.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/FilterChips.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;IAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,sCAwBlG"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { X } from "lucide-react";
|
|
3
|
-
/** Actieve filters als
|
|
3
|
+
/** Actieve filters als FilterChip uit Verkooporders (hele chip wist), plus "Alles wissen". */
|
|
4
4
|
export function FilterChips({ chips, onClearAll }) {
|
|
5
5
|
if (chips.length === 0)
|
|
6
6
|
return null;
|
|
7
|
-
return (_jsxs(
|
|
7
|
+
return (_jsxs(_Fragment, { children: [chips.map((c) => (_jsxs("button", { type: "button", onClick: c.onRemove, "aria-label": `Filter ${c.label} wissen`, className: "inline-flex h-8 items-center gap-1.5 rounded-full border border-border bg-muted px-3 text-xs font-medium text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:h-3.5 [&_svg]:w-3.5", children: [c.icon, c.label, _jsx(X, { className: "h-3.5 w-3.5", "aria-hidden": true })] }, c.key))), onClearAll && (_jsx("button", { type: "button", onClick: onClearAll, className: "h-8 rounded-lg px-2 text-xs font-medium text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: "Alles wissen" }))] }));
|
|
8
8
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/** Zijpaneel met álle extra filters (achter de knop "Filters"). */
|
|
3
|
+
export declare function FilterPanel({ open, onOpenChange, onClear, children, }: {
|
|
4
|
+
open: boolean;
|
|
5
|
+
onOpenChange: (v: boolean) => void;
|
|
6
|
+
onClear?: () => void;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
/** Eén filtergroep in het paneel: titel + keuzeknoppen. */
|
|
10
|
+
export declare function FilterOptions<T extends string>({ label, value, options, onChange, }: {
|
|
11
|
+
label: string;
|
|
12
|
+
value: T;
|
|
13
|
+
options: Array<{
|
|
14
|
+
value: T;
|
|
15
|
+
label: string;
|
|
16
|
+
}>;
|
|
17
|
+
onChange: (v: T) => void;
|
|
18
|
+
}): import("react").JSX.Element;
|
|
19
|
+
export declare const PERIOD_OPTIONS: ({
|
|
20
|
+
value: "all";
|
|
21
|
+
label: string;
|
|
22
|
+
} | {
|
|
23
|
+
value: "today";
|
|
24
|
+
label: string;
|
|
25
|
+
} | {
|
|
26
|
+
value: "7d";
|
|
27
|
+
label: string;
|
|
28
|
+
} | {
|
|
29
|
+
value: "30d";
|
|
30
|
+
label: string;
|
|
31
|
+
})[];
|
|
32
|
+
//# sourceMappingURL=FilterPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterPanel.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/FilterPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,YAAY,EACZ,OAAO,EACP,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAeA;AAED,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,EAC9C,KAAK,EACL,KAAK,EACL,OAAO,EACP,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;CAC1B,+BAyBA;AAED,eAAO,MAAM,cAAc;;;;;;;;;;;;IAK1B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle } from "@flowselections/core";
|
|
3
|
+
/** Zijpaneel met álle extra filters (achter de knop "Filters"). */
|
|
4
|
+
export function FilterPanel({ open, onOpenChange, onClear, children, }) {
|
|
5
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "left-auto right-0 top-0 flex h-dvh max-h-dvh w-full max-w-sm translate-x-0 translate-y-0 flex-col gap-4 rounded-none border-l border-border p-6 sm:rounded-none", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Filters" }) }), _jsx("div", { className: "min-h-0 flex-1 space-y-6 overflow-y-auto", children: children }), _jsxs("div", { className: "flex items-center justify-between gap-2 border-t border-border pt-4", children: [onClear ? _jsx(Button, { variant: "ghost", size: "sm", onClick: onClear, children: "Alles wissen" }) : _jsx("span", {}), _jsx(Button, { size: "sm", onClick: () => onOpenChange(false), children: "Klaar" })] })] }) }));
|
|
6
|
+
}
|
|
7
|
+
/** Eén filtergroep in het paneel: titel + keuzeknoppen. */
|
|
8
|
+
export function FilterOptions({ label, value, options, onChange, }) {
|
|
9
|
+
return (_jsxs("fieldset", { className: "space-y-2", children: [_jsx("legend", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: label }), _jsx("div", { className: "flex flex-wrap gap-2", children: options.map((o) => {
|
|
10
|
+
const on = o.value === value;
|
|
11
|
+
return (_jsx("button", { type: "button", "aria-pressed": on, onClick: () => onChange(o.value), className: "inline-flex h-9 items-center rounded-lg border px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring " +
|
|
12
|
+
(on ? "border-primary/30 bg-primary/10 font-semibold text-foreground" : "border-border bg-card text-foreground/80 hover:bg-muted"), children: o.label }, o.value));
|
|
13
|
+
}) })] }));
|
|
14
|
+
}
|
|
15
|
+
export const PERIOD_OPTIONS = [
|
|
16
|
+
{ value: "all", label: "Alle periodes" },
|
|
17
|
+
{ value: "today", label: "Vandaag" },
|
|
18
|
+
{ value: "7d", label: "Laatste 7 dagen" },
|
|
19
|
+
{ value: "30d", label: "Laatste 30 dagen" },
|
|
20
|
+
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/**
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/** Losse melding in dezelfde vorm als de NoticeBar (één regel, details uitklapbaar). */
|
|
3
3
|
export declare function InlineAlert({ tone, title, children, action, }: {
|
|
4
4
|
tone?: "warning" | "error" | "info";
|
|
5
5
|
title: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InlineAlert.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/InlineAlert.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"InlineAlert.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/InlineAlert.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,EAC1B,IAAgB,EAChB,KAAK,EACL,QAAQ,EACR,MAAM,GACP,EAAE;IACD,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,+BAEA"}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
/** Melding: zacht vlak, icoon links, actie rechts. Op mobiel ingeklapt tot één regel. */
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { NoticeRow } from "./NoticeBar.js";
|
|
3
|
+
/** Losse melding in dezelfde vorm als de NoticeBar (één regel, details uitklapbaar). */
|
|
5
4
|
export function InlineAlert({ tone = "warning", title, children, action, }) {
|
|
6
|
-
|
|
7
|
-
const Icon = tone === "error" ? XCircle : tone === "info" ? Info : AlertTriangle;
|
|
8
|
-
const cls = tone === "error"
|
|
9
|
-
? "border-destructive/30 bg-destructive/5 text-foreground [&_.ia-icon]:text-destructive"
|
|
10
|
-
: tone === "info"
|
|
11
|
-
? "border-border bg-muted/50 text-foreground [&_.ia-icon]:text-muted-foreground"
|
|
12
|
-
: "border-warning/30 bg-warning-soft text-warning-soft-foreground [&_.ia-icon]:text-warning-soft-foreground";
|
|
13
|
-
return (_jsxs("div", { role: tone === "error" ? "alert" : "status", className: "flex shrink-0 items-start gap-3 rounded-lg border px-3 py-2.5 text-sm " + cls, children: [_jsx(Icon, { className: "ia-icon mt-0.5 h-4 w-4 shrink-0", "aria-hidden": true }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("button", { type: "button", onClick: () => setOpen((v) => !v), className: "block w-full text-left font-medium sm:pointer-events-none", "aria-expanded": open, children: _jsx("span", { className: open ? "" : "line-clamp-1 sm:line-clamp-none", children: title }) }), children && _jsx("div", { className: "mt-0.5 text-sm opacity-90 " + (open ? "" : "hidden sm:block"), children: children })] }), action && _jsx("div", { className: "shrink-0", children: action })] }));
|
|
5
|
+
return _jsx(NoticeRow, { notice: { key: title, tone, title, details: children, action } });
|
|
14
6
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
/** Rij KPI-tegels: 4 naast elkaar vanaf desktop, 2 op tablet, veegbaar op mobiel. */
|
|
3
3
|
export function KpiRow({ children }) {
|
|
4
|
-
return (_jsx("div", { className: "-mx-4 flex shrink-0 snap-x snap-mandatory gap-3 overflow-x-auto px-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden " +
|
|
4
|
+
return (_jsx("div", { className: "-mx-4 flex shrink-0 scroll-px-4 snap-x snap-mandatory gap-3 overflow-x-auto px-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden " +
|
|
5
5
|
"md:mx-0 md:grid md:grid-cols-2 md:overflow-visible md:px-0 lg:grid-cols-4 " +
|
|
6
6
|
"[&>*]:w-[78%] [&>*]:shrink-0 [&>*]:snap-start md:[&>*]:w-auto", children: children }));
|
|
7
7
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export interface Notice {
|
|
3
|
+
key: string;
|
|
4
|
+
tone?: "warning" | "error" | "info";
|
|
5
|
+
title: string;
|
|
6
|
+
/** Uitklapbare toelichting (nooit een lijst van items). */
|
|
7
|
+
details?: ReactNode;
|
|
8
|
+
action?: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
/** Eén melding op één regel; details klappen uit (stijl NoticeBar uit Verkooporders). */
|
|
11
|
+
export declare function NoticeRow({ notice }: {
|
|
12
|
+
notice: Notice;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
/** Meldingen direct onder de kop: hooguit 2 zichtbaar, de rest achter "+ n meldingen". */
|
|
15
|
+
export declare function NoticeBar({ notices }: {
|
|
16
|
+
notices: Notice[];
|
|
17
|
+
}): import("react").JSX.Element | null;
|
|
18
|
+
//# sourceMappingURL=NoticeBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoticeBar.d.ts","sourceRoot":"","sources":["../../../src/components/fs-ui/NoticeBar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGjD,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAQD,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,+BAwBvD;AAED,0FAA0F;AAC1F,wBAAgB,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,sCAmB3D"}
|