@mohasinac/appkit 3.4.4 → 3.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +2 -9
  2. package/dist/features/account/components/NotificationPreferencesPanel.js +2 -11
  3. package/dist/features/account/components/UserSidebar.js +4 -4
  4. package/dist/features/admin/components/AdminBrandsView.js +7 -9
  5. package/dist/features/admin/components/AdminBundlesView.js +10 -14
  6. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -5
  7. package/dist/features/admin/components/AdminCategoriesView.js +10 -14
  8. package/dist/features/admin/components/AdminFaqsView.js +7 -9
  9. package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -1
  10. package/dist/features/admin/components/AdminFeaturesView.js +9 -18
  11. package/dist/features/admin/components/AdminNotificationsView.js +4 -6
  12. package/dist/features/admin/components/AdminSessionsView.js +7 -9
  13. package/dist/features/admin/components/AdminSidebar.js +4 -4
  14. package/dist/features/admin/components/AdminSiteSettingsView.js +1 -1
  15. package/dist/features/admin/components/QuickActionsPanel.js +2 -2
  16. package/dist/features/homepage/components/HeroCarousel.js +2 -2
  17. package/dist/features/media/upload/MediaUploadField.js +1 -7
  18. package/dist/features/seller/components/SellerCouponsView.js +7 -9
  19. package/dist/features/seller/components/SellerSidebar.js +4 -4
  20. package/dist/features/site-settings/components/ThemeManagerView.js +25 -1
  21. package/dist/styles.css +49 -10
  22. package/dist/tailwind-utilities.css +1 -1
  23. package/dist/tokens/themes/default-dark.js +28 -0
  24. package/dist/tokens/themes/default-light.js +28 -0
  25. package/dist/tokens/themes/required.d.ts +1 -1
  26. package/dist/tokens/themes/required.js +30 -0
  27. package/dist/tokens/tokens.css +39 -0
  28. package/dist/ui/components/Toggle.d.ts +3 -1
  29. package/dist/ui/components/Toggle.js +2 -2
  30. package/dist/ui/components/Typography.js +8 -3
  31. package/dist/ui/components/Typography.style.css +9 -9
  32. package/dist/ui/components/surface-tokens.d.ts +113 -113
  33. package/dist/ui/components/surface-tokens.js +104 -102
  34. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../../../errors/normalize";
4
4
  import * as React from "react";
5
- import { Div, Heading, Li, Section, Span, Table, Tbody, Td, Text, Th, Thead, Tr, Ul } from "@mohasinac/appkit";
5
+ import { Button, Div, Heading, Li, Section, Span, Table, Tbody, Td, Text, Th, Thead, Tr, Ul } from "@mohasinac/appkit";
6
6
  export function AnalysisRunnerView() {
7
7
  const [days, setDays] = React.useState(7);
8
8
  const [source, setSource] = React.useState("all");
@@ -34,14 +34,7 @@ export function AnalysisRunnerView() {
34
34
  setRunning(false);
35
35
  }
36
36
  }
37
- return (_jsxs(Div, { style: { padding: "1.5rem", maxWidth: "1200px", margin: "0 auto" }, children: [_jsx(Heading, { level: 1, style: { fontSize: "1.5rem", fontWeight: 600 }, children: "Maintenance analysis" }), _jsxs(Div, { style: { display: "flex", gap: "1rem", alignItems: "center", margin: "1rem 0" }, children: [_jsxs("label", { children: ["Days back:\u00A0", _jsx("input", { type: "number", min: 1, max: 30, value: days, onChange: (e) => setDays(Math.max(1, Math.min(30, Number(e.target.value) || 7))), style: { width: 80, padding: "0.35rem 0.5rem", border: "1px solid var(--appkit-color-border)", borderRadius: 4 } })] }), _jsxs("label", { children: ["Source:\u00A0", _jsxs("select", { value: source, onChange: (e) => setSource(e.target.value), style: { padding: "0.35rem 0.5rem", border: "1px solid var(--appkit-color-border)", borderRadius: 4 }, children: [_jsx("option", { value: "all", children: "all" }), _jsx("option", { value: "vercel", children: "server (vercel)" }), _jsx("option", { value: "client", children: "client" }), _jsx("option", { value: "function", children: "function" })] })] }), _jsx("button", { type: "button", onClick: run, disabled: running, style: {
38
- padding: "0.5rem 1rem",
39
- background: "var(--appkit-color-primary)",
40
- color: "var(--appkit-color-surface)",
41
- border: 0,
42
- borderRadius: 4,
43
- cursor: running ? "wait" : "pointer",
44
- }, children: running ? "Running…" : "Run analysis" })] }), error ? _jsx(Text, { as: "p", style: { color: "var(--appkit-color-error)" }, children: error }) : null, report ? _jsx(ReportView, { report: report }) : null] }));
37
+ return (_jsxs(Div, { style: { padding: "1.5rem", maxWidth: "1200px", margin: "0 auto" }, children: [_jsx(Heading, { level: 1, style: { fontSize: "1.5rem", fontWeight: 600 }, children: "Maintenance analysis" }), _jsxs(Div, { style: { display: "flex", gap: "1rem", alignItems: "center", margin: "1rem 0" }, children: [_jsxs("label", { children: ["Days back:\u00A0", _jsx("input", { type: "number", min: 1, max: 30, value: days, onChange: (e) => setDays(Math.max(1, Math.min(30, Number(e.target.value) || 7))), style: { width: 80, padding: "0.35rem 0.5rem", border: "1px solid var(--appkit-color-border)", borderRadius: 4 } })] }), _jsxs("label", { children: ["Source:\u00A0", _jsxs("select", { value: source, onChange: (e) => setSource(e.target.value), style: { padding: "0.35rem 0.5rem", border: "1px solid var(--appkit-color-border)", borderRadius: 4 }, children: [_jsx("option", { value: "all", children: "all" }), _jsx("option", { value: "vercel", children: "server (vercel)" }), _jsx("option", { value: "client", children: "client" }), _jsx("option", { value: "function", children: "function" })] })] }), _jsx(Button, { type: "button", onClick: run, isLoading: running, children: "Run analysis" })] }), error ? _jsx(Text, { as: "p", style: { color: "var(--appkit-color-error)" }, children: error }) : null, report ? _jsx(ReportView, { report: report }) : null] }));
45
38
  }
46
39
  function ReportView({ report }) {
47
40
  return (_jsxs(Div, { children: [_jsxs(Text, { as: "p", style: { color: "var(--appkit-color-text-muted)" }, children: ["Generated ", report.generatedAt, ". Window: ", report.window.days, "d. Total: ", report.totalErrors, ". Unique users: ", report.uniqueUsers, "."] }), _jsx(ReportSection, { title: "Top codes", children: _jsxs(Table, { style: { width: "100%", fontSize: "0.85rem" }, children: [_jsx(Thead, { children: _jsxs(Tr, { children: [_jsx(Th, { align: "left", children: "Code" }), _jsx(Th, { align: "right", children: "Count" }), _jsx(Th, { align: "right", children: "User impact" })] }) }), _jsx(Tbody, { children: report.topCodes.map((c) => (_jsxs(Tr, { children: [_jsx(Td, { style: { fontFamily: "monospace" }, children: c.code }), _jsx(Td, { align: "right", children: c.count }), _jsx(Td, { align: "right", children: c.userImpact })] }, c.code))) })] }) }), _jsx(ReportSection, { title: "Top routes", children: _jsxs(Table, { style: { width: "100%", fontSize: "0.85rem" }, children: [_jsx(Thead, { children: _jsxs(Tr, { children: [_jsx(Th, { align: "left", children: "Route" }), _jsx(Th, { align: "left", children: "Source" }), _jsx(Th, { align: "right", children: "Count" })] }) }), _jsx(Tbody, { children: report.topRoutes.map((r) => (_jsxs(Tr, { children: [_jsx(Td, { style: { fontFamily: "monospace" }, children: r.route }), _jsx(Td, { children: r.source }), _jsx(Td, { align: "right", children: r.count })] }, `${r.source}:${r.route}`))) })] }) }), report.stackClusters.length > 0 ? (_jsx(ReportSection, { title: `Stack clusters (≥5)`, children: _jsx(Ul, { children: report.stackClusters.map((c) => (_jsxs(Li, { style: { marginBottom: "0.5rem", fontSize: "0.82rem" }, children: [_jsxs(Span, { weight: "bold", children: [c.count, "\u00D7"] }), " ", _jsx(Span, { style: { fontFamily: "monospace" }, children: c.signature }), _jsxs(Div, { style: { color: "var(--appkit-color-text-muted)" }, children: ["sample: ", c.sampleMessage] })] }, c.signature))) }) })) : null, report.burstWindows.length > 0 ? (_jsx(ReportSection, { title: "5xx burst windows", children: _jsx(Ul, { children: report.burstWindows.map((b) => (_jsxs(Li, { children: [b.start, " \u2014 ", b.count, " errors (", b.multiplier, "\u00D7 avg)"] }, b.start))) }) })) : null, _jsx(ReportSection, { title: "Recommendations", children: _jsx(Ul, { children: report.recommendations.map((r, i) => _jsx(Li, { children: r }, i)) }) })] }));
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useCallback } from "react";
4
- import { Button, Div, Row, Span, Stack, Text, useToast } from "../../../ui";
4
+ import { Button, Div, Row, Span, Stack, Text, Toggle, useToast } from "../../../ui";
5
5
  import { useSiteSettings } from "../../../core/hooks/useSiteSettings";
6
6
  import { normalizeError } from "../../../errors/normalize";
7
7
  const __O = {
@@ -19,16 +19,7 @@ const TYPE_ROWS = [
19
19
  ];
20
20
  // ─── Toggle row ─────────────────────────────────────────────────────────────
21
21
  function ToggleRow({ label, description, checked, onChange, disabled, }) {
22
- return (_jsxs(Row, { align: "center", justify: "between", gap: "md", className: "border-b last:border-0 border-[var(--appkit-color-border)]", padding: "y-sm", children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: "text-[var(--appkit-color-text)]", size: "sm", weight: "medium", children: label }), _jsx(Text, { variant: "secondary", className: "mt-0.5", size: "xs", children: description })] }), _jsx("button", { type: "button", role: "switch", "aria-checked": checked, "aria-label": label, disabled: disabled, onClick: () => onChange(!checked), className: [
23
- "relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent",
24
- "transition-colors duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2",
25
- "focus-visible:ring-[var(--appkit-color-primary)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
26
- checked ? "bg-[var(--appkit-color-primary)]" : "bg-[var(--appkit-color-text-faint)]",
27
- ].join(" "), children: _jsx(Span, { className: [
28
- "pointer-events-none inline-block h-4 w-4 transform rounded-full bg-[var(--appkit-color-surface)] shadow-lg",
29
- "ring-0 transition duration-200 ease-in-out",
30
- checked ? "translate-x-4" : "translate-x-0",
31
- ].join(" ") }) })] }));
22
+ return (_jsxs(Row, { align: "center", justify: "between", gap: "md", className: "border-b last:border-0 border-[var(--appkit-color-border)]", padding: "y-sm", children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: "text-[var(--appkit-color-text)]", size: "sm", weight: "medium", children: label }), _jsx(Text, { variant: "secondary", className: "mt-0.5", size: "xs", children: description })] }), _jsx(Toggle, { checked: checked, ariaLabel: label, disabled: disabled, onChange: onChange, size: "sm" })] }));
32
23
  }
33
24
  // ─── Section card ───────────────────────────────────────────────────────────
34
25
  function SectionCard({ title, children }) {
@@ -4,7 +4,7 @@ import { useState, useCallback, useEffect } from "react";
4
4
  import { createPortal } from "react-dom";
5
5
  import Link from "next/link";
6
6
  import { usePathname } from "next/navigation";
7
- import { ConfirmDeleteModal, Div, Li, Nav, Row, Span, Stack, Ul } from "../../../ui";
7
+ import { Button, ConfirmDeleteModal, Div, IconButton, Li, Nav, Row, Span, Stack, Ul } from "../../../ui";
8
8
  import { BottomSheet } from "../../layout/BottomSheet";
9
9
  import { SidebarCollapseToggle } from "../../../_internal/client/features/layout/SidebarCollapseToggle";
10
10
  const __O = {
@@ -55,13 +55,13 @@ function DrawerContent({ groups, items, activeHref, onItemClick, }) {
55
55
  return (_jsx(Nav, { "aria-label": "User navigation", padding: "y-xs", children: groups.map((group) => {
56
56
  const isOpen = openGroups[group.title] ?? false;
57
57
  const hasActive = group.items.some((i) => activeHref === i.href || activeHref.startsWith(i.href + "/"));
58
- return (_jsxs(Div, { className: "mb-0.5", children: [_jsxs("button", { type: "button", onClick: () => toggle(group.title), className: `flex w-full items-center justify-between px-4 py-2 text-[0.6875rem] font-semibold uppercase tracking-widest transition-colors ${hasActive && !isOpen
58
+ return (_jsxs(Div, { className: "mb-0.5", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => toggle(group.title), paddingX: "md", paddingY: "sm", weight: "semibold", rounded: "none", className: `w-full text-[0.6875rem] uppercase tracking-widest transition-colors ${hasActive && !isOpen
59
59
  ? "text-primary-600 dark:text-primary-400"
60
- : "text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)]"}`, children: [_jsx(Span, { children: group.title }), _jsx("svg", { className: `w-3 h-3 transition-transform duration-150 ${isOpen ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M19 9l-7 7-7-7" }) })] }), isOpen && (_jsx(Ul, { paddingX: "x-sm", paddingY: "y-bottom-xs", spacing: "2xs", children: group.items.map((item) => (_jsx(Li, { children: _jsx(NavLink, { item: item, isActive: isNavItemActive(item, activeHref), onClick: onItemClick }) }, item.href))) }))] }, group.title));
60
+ : "text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text-muted)]"}`, children: _jsxs(Row, { align: "center", justify: "between", className: "w-full", children: [_jsx(Span, { children: group.title }), _jsx("svg", { className: `w-3 h-3 transition-transform duration-150 ${isOpen ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M19 9l-7 7-7-7" }) })] }) }), isOpen && (_jsx(Ul, { paddingX: "x-sm", paddingY: "y-bottom-xs", spacing: "2xs", children: group.items.map((item) => (_jsx(Li, { children: _jsx(NavLink, { item: item, isActive: isNavItemActive(item, activeHref), onClick: onItemClick }) }, item.href))) }))] }, group.title));
61
61
  }) }));
62
62
  }
63
63
  function DrawerPanel({ title, onClose, children, }) {
64
- return (_jsxs(Div, { className: "hidden lg:block", children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 z-40 backdrop-blur-sm", onClick: onClose, "aria-hidden": "true" }), _jsxs(Stack, { border: "default", shadow: "2xl", role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 right-0 z-50 h-full w-64 border-l", surface: "default", children: [_jsxs(Row, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", align: "center", justify: "between", children: [_jsx(Span, { size: "xs", weight: "semibold", transform: "uppercase", color: "muted", children: title }), _jsx("button", { type: "button", onClick: onClose, "aria-label": "Close", className: "flex items-center justify-center w-7 h-7 rounded-md text-zinc-400 hover:bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-elevated)] hover:text-zinc-700 hover:text-[var(--appkit-color-text-muted)] transition-colors", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: children })] })] }));
64
+ return (_jsxs(Div, { className: "hidden lg:block", children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 z-40 backdrop-blur-sm", onClick: onClose, "aria-hidden": "true" }), _jsxs(Stack, { border: "default", shadow: "2xl", role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 right-0 z-50 h-full w-64 border-l", surface: "default", children: [_jsxs(Row, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", align: "center", justify: "between", children: [_jsx(Span, { size: "xs", weight: "semibold", transform: "uppercase", color: "muted", children: title }), _jsx(IconButton, { "aria-label": "Close", variant: "ghost", size: "sm", onClick: onClose, icon: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: children })] })] }));
65
65
  }
66
66
  export function UserSidebar({ items, groups, mobileOpen = false, onCloseMobile, desktopOpen = false, onToggle, variant = "overlay" }) {
67
67
  const pathname = usePathname();
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Row, SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
3
+ import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
4
4
  import { sortBy } from "@mohasinac/appkit";
5
5
  import React from "react";
6
- import { ListingLayout, Text } from "../../../ui";
6
+ import { FilterChipGroup, ListingLayout, Text } from "../../../ui";
7
7
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
8
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
9
9
  import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
@@ -76,13 +76,11 @@ const ADMIN_BRANDS_CONFIG = {
76
76
  },
77
77
  },
78
78
  ],
79
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [
80
- { label: "All", value: "" },
81
- { label: "Active", value: "true" },
82
- { label: "Inactive", value: "false" },
83
- ].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
84
- ? "bg-primary text-white border-primary"
85
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] })),
79
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: [
80
+ { id: "All", label: "All" },
81
+ { id: "true", label: "Active" },
82
+ { id: "false", label: "Inactive" },
83
+ ], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) })),
86
84
  renderEditor: ({ editId, closePanel }) => (_jsx(AdminBrandEditorView, { brandId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })),
87
85
  resolveEditorTitle: ({ isCreate }) => (isCreate ? "Add Brand" : "Edit Brand"),
88
86
  };
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
- import { Row, SIEVE_OP, sieveFilter } from "@mohasinac/appkit";
4
+ import { SIEVE_OP, sieveFilter } from "@mohasinac/appkit";
5
5
  import { sortBy } from "@mohasinac/appkit";
6
6
  import { useState, useCallback } from "react";
7
- import { Badge, Button, Stack, Text, TextLink, useToast } from "../../../ui";
7
+ import { Badge, Button, FilterChipGroup, Stack, Text, TextLink, useToast } from "../../../ui";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
9
  import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
10
10
  import { DataListingView } from "./DataListingView";
@@ -124,18 +124,14 @@ export function AdminBundlesView({ getEditHref, newHref }) {
124
124
  }),
125
125
  ],
126
126
  renderRowActions: (row) => (_jsx(Button, { variant: "ghost", size: "sm", isLoading: rebuildingId === row.id, disabled: rebuildingId === row.id, onClick: () => void handleRebuild(row.id), children: ACTIONS.ADMIN["rebuild-bundle"].label })),
127
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [
128
- { label: "All", value: "" },
129
- { label: "Active", value: "true" },
130
- { label: "Inactive", value: "false" },
131
- ].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
132
- ? "bg-primary text-white border-primary"
133
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Stock" }), _jsx(Row, { wrap: true, gap: "sm", children: [
134
- { label: "All", value: "" },
135
- { label: "Sold out", value: "out_of_stock" },
136
- ].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, bundleStockStatus: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.bundleStockStatus || "") === opt.value
137
- ? "bg-primary text-white border-primary"
138
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] })] })),
127
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: [
128
+ { id: "All", label: "All" },
129
+ { id: "true", label: "Active" },
130
+ { id: "false", label: "Inactive" },
131
+ ], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) }), _jsx(FilterChipGroup, { label: "Stock", tabs: [
132
+ { id: "All", label: "All" },
133
+ { id: "out_of_stock", label: "Sold out" },
134
+ ], value: pendingFilters.bundleStockStatus || "", onChange: (v) => setPendingFilters((p) => ({ ...p, bundleStockStatus: v })) })] })),
139
135
  };
140
136
  return _jsx(DataListingView, { config: config });
141
137
  }
@@ -69,11 +69,7 @@ function ZonePicker({ selected, onChange, disabled, }) {
69
69
  const zones = [1, 2, 3, 4, 5, 6];
70
70
  return (_jsxs(Div, { children: [_jsx(Text, { className: "mb-1", color: "muted", size: "xs", children: "Zone (row \u00D7 col grid)" }), _jsx(Div, { layout: "grid", gap: "1", className: "grid-rows-2 grid-cols-3 w-[150px]", children: zones.map((z) => {
71
71
  const isDisabled = disabled?.(z) ?? false;
72
- return (_jsx("button", { type: "button", disabled: isDisabled, onClick: () => !isDisabled && onChange(z), className: `rounded text-xs font-mono py-2 border transition-colors ${selected === z
73
- ? "bg-primary text-white border-primary"
74
- : isDisabled
75
- ? "bg-[var(--appkit-color-surface)] text-zinc-300 border-zinc-200 bg-[var(--appkit-color-surface-elevated)] border-[var(--appkit-color-border)] cursor-not-allowed"
76
- : "bg-[var(--appkit-color-surface)] border-zinc-300 text-zinc-700 hover:border-primary bg-[var(--appkit-color-surface)] border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)]"}`, children: z }, z));
72
+ return (_jsx(Button, { type: "button", variant: selected === z ? "primary" : "outline", size: "sm", disabled: isDisabled, paddingY: "sm", textSize: "xs", className: "font-mono", onClick: () => !isDisabled && onChange(z), children: z }, z));
77
73
  }) }), _jsx(Text, { className: "text-[10px] mt-1", color: "faint", children: "Row 1 = zones 1\u20133 \u00B7 Row 2 = zones 4\u20136" })] }));
78
74
  }
79
75
  // ── Background Editor ────────────────────────────────────────────────────────
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { Row, SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
3
+ import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
4
4
  import { sortBy } from "@mohasinac/appkit";
5
5
  import React from "react";
6
- import { Heading, ListingLayout, Span, Text } from "../../../ui";
6
+ import { FilterChipGroup, Heading, ListingLayout, Span, Text } from "../../../ui";
7
7
  import { CATEGORY_ENDPOINTS } from "../../../constants/api-endpoints";
8
8
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
9
9
  import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
@@ -85,18 +85,14 @@ const ADMIN_CATEGORIES_CONFIG = {
85
85
  },
86
86
  },
87
87
  ],
88
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Active" }), _jsx(Row, { wrap: true, gap: "sm", children: [
89
- { label: "All", value: "" },
90
- { label: "Active", value: "true" },
91
- { label: "Inactive", value: "false" },
92
- ].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
93
- ? "bg-primary text-white border-primary"
94
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] }), _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Featured" }), _jsx(Row, { wrap: true, gap: "sm", children: [
95
- { label: "All", value: "" },
96
- { label: "Featured only", value: "true" },
97
- ].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isFeatured: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isFeatured || "") === opt.value
98
- ? "bg-primary text-white border-primary"
99
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] })] })),
88
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Active", tabs: [
89
+ { id: "All", label: "All" },
90
+ { id: "true", label: "Active" },
91
+ { id: "false", label: "Inactive" },
92
+ ], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) }), _jsx(FilterChipGroup, { label: "Featured", tabs: [
93
+ { id: "All", label: "All" },
94
+ { id: "true", label: "Featured only" },
95
+ ], value: pendingFilters.isFeatured || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isFeatured: v })) })] })),
100
96
  renderEditor: ({ editId, closePanel }) => (_jsx(AdminCategoryEditorView, { categoryId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })),
101
97
  resolveEditorTitle: ({ isCreate }) => (isCreate ? "Add Category" : "Edit Category"),
102
98
  };
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { Row, SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
3
+ import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
4
4
  import { sortBy } from "@mohasinac/appkit";
5
5
  import React, { useMemo } from "react";
6
- import { Heading, ListingLayout, Span, Text } from "../../../ui";
6
+ import { FilterChipGroup, Heading, ListingLayout, Span, Text } from "../../../ui";
7
7
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
8
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
9
9
  import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
@@ -81,13 +81,11 @@ export function AdminFaqsView({ children, onBulkArchive, onBulkDelete, ...props
81
81
  ]
82
82
  : []),
83
83
  ],
84
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [
85
- { label: "All", value: "" },
86
- { label: "Published", value: "true" },
87
- { label: "Draft", value: "false" },
88
- ].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
89
- ? "bg-primary text-white border-primary"
90
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] })),
84
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: [
85
+ { id: "All", label: "All" },
86
+ { id: "true", label: "Published" },
87
+ { id: "false", label: "Draft" },
88
+ ], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) })),
91
89
  renderEditor: ({ editId, closePanel }) => (_jsx(AdminFaqEditorView, { faqId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })),
92
90
  resolveEditorTitle: ({ isCreate }) => (isCreate ? "Add FAQ" : "Edit FAQ"),
93
91
  }), [onBulkArchive, onBulkDelete]);
@@ -42,7 +42,7 @@ const CATEGORY_TYPE_FLAGS = [
42
42
  ];
43
43
  function AccordionSection({ title, defaultOpen = true, children }) {
44
44
  const [open, setOpen] = React.useState(defaultOpen);
45
- return (_jsxs(Div, { className: `${__O.hidden}`, rounded: "lg", border: "default", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "w-full flex items-center justify-between px-4 py-3 bg-[var(--appkit-color-surface-elevated)]/60 text-left", "aria-expanded": open, children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: title }), _jsx("svg", { className: `h-4 w-4 text-zinc-400 transition-transform ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(Div, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", children: children }))] }));
45
+ return (_jsxs(Div, { className: `${__O.hidden}`, rounded: "lg", border: "default", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => setOpen((v) => !v), paddingX: "md", paddingY: "md", className: "w-full text-left bg-[var(--appkit-color-surface-elevated)]/60", "aria-expanded": open, children: _jsxs(Row, { align: "center", justify: "between", className: "w-full", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: title }), _jsx("svg", { className: `h-4 w-4 text-zinc-400 transition-transform ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })] }) }), open && (_jsx(Div, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", children: children }))] }));
46
46
  }
47
47
  function FlagRow({ label, desc, checked, onChange, rollout, onRolloutChange, showRollout }) {
48
48
  return (_jsxs(Row, { surface: "default", padding: "inline", align: "center", justify: "between", gap: "md", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Toggle, { checked: checked, onChange: onChange, label: label }), _jsx(Text, { className: "mt-0.5 ml-10 truncate", color: "faint", size: "xs", children: desc })] }), showRollout && (_jsxs(Stack, { className: "w-28 shrink-0", gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Rollout %" }), _jsx(Input, { type: "number", min: 0, max: 100, value: rollout ?? 100, onChange: (e) => onRolloutChange?.(Math.min(100, Math.max(0, Number(e.target.value)))), disabled: !checked, className: "w-full" })] }))] }));
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { sortBy } from "@mohasinac/appkit";
4
4
  import React from "react";
5
5
  import { useRouter, useSearchParams, usePathname } from "next/navigation";
6
- import { ListingLayout, Row } from "../../../ui";
6
+ import { FilterChipGroup, ListingLayout } from "../../../ui";
7
7
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
8
8
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
9
9
  import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
@@ -12,10 +12,7 @@ import { AdminFeatureEditorView } from "./AdminFeatureEditorView";
12
12
  import { PRODUCT_FEATURE_SCOPE_TABS } from "../../products/constants/product-features.constants";
13
13
  const PAGE_SIZE = 50;
14
14
  const DEFAULT_SCOPE = "platform";
15
- const STICKY_TABS_CLASS = "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex gap-2 bg-white/95 bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-2";
16
- const TAB_BASE_CLASS = "rounded-full px-3 py-1 text-xs font-medium border transition-colors";
17
- const TAB_ACTIVE_CLASS = "bg-primary text-white border-primary";
18
- const TAB_INACTIVE_CLASS = "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]";
15
+ const STICKY_TABS_CLASS = "sticky top-[calc(var(--header-height,0px)+44px)] z-10 bg-white/95 bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-2";
19
16
  function mapFeatureRow(item, index) {
20
17
  const productTypes = Array.isArray(item.productTypes) ? item.productTypes : [];
21
18
  const isActive = typeof item.isActive === "boolean" ? item.isActive : true;
@@ -34,6 +31,8 @@ function mapFeatureRow(item, index) {
34
31
  };
35
32
  }
36
33
  export function AdminFeaturesView({ children, ...props }) {
34
+ const router = useRouter();
35
+ const pathname = usePathname();
37
36
  const searchParams = useSearchParams();
38
37
  const scopeFilter = searchParams.get("scope") || DEFAULT_SCOPE;
39
38
  if (React.Children.count(children) > 0) {
@@ -70,21 +69,13 @@ export function AdminFeaturesView({ children, ...props }) {
70
69
  onClick: () => selection.clearSelection(),
71
70
  },
72
71
  ],
73
- renderAboveContent: () => (_jsx(Row, { className: STICKY_TABS_CLASS, gap: "xs", children: PRODUCT_FEATURE_SCOPE_TABS.map((tab) => (_jsx(ScopeTabButton, { value: tab.value, label: tab.label, current: scopeFilter }, tab.value))) })),
72
+ renderAboveContent: () => (_jsx(FilterChipGroup, { label: "Scope", className: STICKY_TABS_CLASS, tabs: PRODUCT_FEATURE_SCOPE_TABS.map((tab) => ({ id: tab.value, label: tab.label })), value: scopeFilter, onChange: (value) => {
73
+ const params = new URLSearchParams(searchParams.toString());
74
+ params.set("scope", value);
75
+ router.replace(`${pathname}?${params.toString()}`);
76
+ } })),
74
77
  renderEditor: ({ editId, closePanel }) => (_jsx(AdminFeatureEditorView, { featureId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })),
75
78
  resolveEditorTitle: ({ isCreate }) => (isCreate ? "Add Feature" : "Edit Feature"),
76
79
  };
77
80
  return _jsx(DataListingView, { config: config });
78
81
  }
79
- function ScopeTabButton({ value, label, current, }) {
80
- const router = useRouter();
81
- const pathname = usePathname();
82
- const searchParams = useSearchParams();
83
- const handleClick = () => {
84
- const params = new URLSearchParams(searchParams.toString());
85
- params.set("scope", value);
86
- router.replace(`${pathname}?${params.toString()}`);
87
- };
88
- const isActive = current === value;
89
- return (_jsx("button", { type: "button", onClick: handleClick, className: `${TAB_BASE_CLASS} ${isActive ? TAB_ACTIVE_CLASS : TAB_INACTIVE_CLASS}`, children: label }));
90
- }
@@ -1,11 +1,11 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useApiMutation } from "@mohasinac/appkit/client";
4
- import { Row, SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
4
+ import { SIEVE_OP, sieveFilter } from "@mohasinac/appkit";
5
5
  import { sortBy } from "@mohasinac/appkit";
6
6
  import React, { useState } from "react";
7
7
  import { useQueryClient } from "@tanstack/react-query";
8
- import { ConfirmDeleteModal, ListingLayout, RowActionMenu, Text, useToast } from "../../../ui";
8
+ import { ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
9
9
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
10
10
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
11
11
  import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
@@ -97,9 +97,7 @@ export function AdminNotificationsView({ children, ...props }) {
97
97
  onClick: () => setDeleteTarget(row),
98
98
  },
99
99
  ] })),
100
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Type" }), _jsx(Row, { wrap: true, gap: "sm", children: NOTIF_TYPES.map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, type: opt === "All" ? "" : opt })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.type || "All") === opt
101
- ? "bg-primary text-white border-primary"
102
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt }, opt))) })] })),
100
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Type", tabs: NOTIF_TYPES.map((opt) => ({ id: opt, label: opt })), value: pendingFilters.type || "All", onChange: (v) => setPendingFilters((p) => ({ ...p, type: v })) })),
103
101
  };
104
102
  return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(ConfirmDeleteModal, { isOpen: Boolean(deleteTarget), onClose: () => setDeleteTarget(null), onConfirm: () => {
105
103
  if (deleteTarget)
@@ -1,11 +1,11 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useApiMutation } from "@mohasinac/appkit/client";
4
- import { Row, SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
4
+ import { SIEVE_OP, sieveFilter } from "@mohasinac/appkit";
5
5
  import { sortBy } from "@mohasinac/appkit";
6
6
  import React, { useState } from "react";
7
7
  import { useQueryClient } from "@tanstack/react-query";
8
- import { ConfirmDeleteModal, ListingLayout, RowActionMenu, Text, useToast } from "../../../ui";
8
+ import { ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
9
9
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
10
10
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
11
11
  import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
@@ -91,12 +91,10 @@ export function AdminSessionsView({ children, ...props }) {
91
91
  onClick: () => setRevokeTarget(row),
92
92
  },
93
93
  ] })),
94
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [
95
- { label: "All", value: "" },
96
- { label: "Active only", value: "true" },
97
- ].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
98
- ? "bg-primary text-white border-primary"
99
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] })),
94
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: [
95
+ { id: "All", label: "All" },
96
+ { id: "true", label: "Active only" },
97
+ ], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) })),
100
98
  };
101
99
  return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(ConfirmDeleteModal, { isOpen: Boolean(revokeTarget), onClose: () => setRevokeTarget(null), onConfirm: () => {
102
100
  if (revokeTarget)
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useEffect, useState, useCallback } from "react";
4
4
  import { createPortal } from "react-dom";
5
5
  import Link from "next/link";
6
- import { Div, Li, Nav, Row, Span, Stack, Ul } from "../../../ui";
6
+ import { Button, Div, IconButton, Li, Nav, Row, Span, Stack, Ul } from "../../../ui";
7
7
  import { BottomSheet } from "../../layout/BottomSheet";
8
8
  import { SidebarCollapseToggle } from "../../../_internal/client/features/layout/SidebarCollapseToggle";
9
9
  const __O = {
@@ -27,13 +27,13 @@ function GroupsContent({ groups, activePath, onItemClick, }) {
27
27
  return (_jsx(Nav, { "aria-label": "Admin navigation", padding: "y-xs", children: groups.map((group) => {
28
28
  const isOpen = openGroups[group.title] ?? false;
29
29
  const hasActive = group.items.some((i) => activePath === i.href || activePath.startsWith(i.href + "/"));
30
- return (_jsxs(Div, { className: "mb-0.5", children: [_jsxs("button", { type: "button", onClick: () => toggle(group.title), className: `flex w-full items-center justify-between px-4 py-2 text-[0.6875rem] font-semibold uppercase tracking-widest transition-colors ${hasActive && !isOpen
30
+ return (_jsxs(Div, { className: "mb-0.5", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => toggle(group.title), paddingX: "md", paddingY: "sm", weight: "semibold", rounded: "none", className: `w-full text-[0.6875rem] uppercase tracking-widest transition-colors ${hasActive && !isOpen
31
31
  ? "text-[var(--appkit-color-text-muted)]"
32
- : "text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text-muted)]"}`, children: [_jsx(Span, { children: group.title }), _jsx("svg", { className: `w-3 h-3 transition-transform duration-150 ${isOpen ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M19 9l-7 7-7-7" }) })] }), isOpen && (_jsx(Ul, { paddingX: "x-sm", paddingY: "y-bottom-xs", spacing: "2xs", children: group.items.map((item) => (_jsx(Li, { children: _jsx(NavLink, { item: item, isActive: isNavItemActive(item, activePath), onClick: onItemClick }) }, item.href))) }))] }, group.title));
32
+ : "text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text-muted)]"}`, children: _jsxs(Row, { align: "center", justify: "between", className: "w-full", children: [_jsx(Span, { children: group.title }), _jsx("svg", { className: `w-3 h-3 transition-transform duration-150 ${isOpen ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2.5, d: "M19 9l-7 7-7-7" }) })] }) }), isOpen && (_jsx(Ul, { paddingX: "x-sm", paddingY: "y-bottom-xs", spacing: "2xs", children: group.items.map((item) => (_jsx(Li, { children: _jsx(NavLink, { item: item, isActive: isNavItemActive(item, activePath), onClick: onItemClick }) }, item.href))) }))] }, group.title));
33
33
  }) }));
34
34
  }
35
35
  function DrawerPanel({ title, onClose, children, }) {
36
- return (_jsxs(Div, { className: "hidden lg:block", children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 z-40 backdrop-blur-sm", onClick: onClose, "aria-hidden": "true" }), _jsxs(Stack, { border: "default", shadow: "2xl", role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 right-0 z-50 h-full w-64 border-l", surface: "default", children: [_jsxs(Row, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", align: "center", justify: "between", children: [_jsx(Span, { size: "xs", weight: "semibold", transform: "uppercase", color: "muted", children: title }), _jsx("button", { type: "button", onClick: onClose, "aria-label": "Close", className: "flex items-center justify-center w-7 h-7 rounded-md text-zinc-400 hover:bg-[var(--appkit-color-surface)] hover:bg-[var(--appkit-color-surface-elevated)] hover:text-zinc-700 hover:text-[var(--appkit-color-text-muted)] transition-colors", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: children })] })] }));
36
+ return (_jsxs(Div, { className: "hidden lg:block", children: [_jsx(Div, { surface: "overlay-xs", className: "fixed inset-0 z-40 backdrop-blur-sm", onClick: onClose, "aria-hidden": "true" }), _jsxs(Stack, { border: "default", shadow: "2xl", role: "dialog", "aria-modal": "true", "aria-label": title, className: "fixed top-0 right-0 z-50 h-full w-64 border-l", surface: "default", children: [_jsxs(Row, { border: "bottom-subtle", paddingY: "y-sm-tall", className: "shrink-0", padding: "x-md", align: "center", justify: "between", children: [_jsx(Span, { size: "xs", weight: "semibold", transform: "uppercase", color: "muted", children: title }), _jsx(IconButton, { "aria-label": "Close", variant: "ghost", size: "sm", onClick: onClose, icon: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsx(Div, { className: `flex-1 ${__O.yAuto}`, children: children })] })] }));
37
37
  }
38
38
  export function AdminSidebar({ renderNavItems, renderHeader, renderFooter, activePath = "", groups, mobileOpen = false, desktopOpen = false, variant = "overlay", onCloseMobile, onToggle, }) {
39
39
  const close = onCloseMobile ?? (() => { });
@@ -17,7 +17,7 @@ const __O = {
17
17
  // --- Helpers -----------------------------------------------------------------
18
18
  function MaskedInput({ label, value, onChange, placeholder, helperText, }) {
19
19
  const [revealed, setRevealed] = React.useState(false);
20
- return (_jsxs(Div, { className: "relative", children: [_jsx(Input, { label: label, value: value, onChange: (e) => onChange(e.target.value), type: revealed ? "text" : "password", placeholder: placeholder, helperText: helperText }), _jsx("button", { type: "button", onClick: () => setRevealed((r) => !r), className: "absolute right-3 top-8 text-xs text-zinc-400 hover:text-zinc-700 hover:text-[var(--appkit-color-text-muted)]", children: revealed ? "Hide" : "Reveal" })] }));
20
+ return (_jsxs(Div, { className: "relative", children: [_jsx(Input, { label: label, value: value, onChange: (e) => onChange(e.target.value), type: revealed ? "text" : "password", placeholder: placeholder, helperText: helperText }), _jsx(Button, { type: "button", variant: "ghost", textSize: "xs", textColor: "faint", paddingX: "none", paddingY: "none", onClick: () => setRevealed((r) => !r), className: "absolute right-3 top-8", children: revealed ? "Hide" : "Reveal" })] }));
21
21
  }
22
22
  const NOTIF_CHANNEL_INDENT = "space-y-4 pl-4 border-l-2 border-[var(--appkit-color-border)]";
23
23
  const CLS_TEXTAREA = "w-full rounded-md border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] p-3 text-sm text-[var(--appkit-color-text-muted)] resize-y focus:outline-none focus:ring-2 focus:ring-blue-500";
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Div, Heading, TextLink } from "../../../ui";
2
+ import { Button, Div, Heading, Row, TextLink } from "../../../ui";
3
3
  export function QuickActionsPanel({ title, actions, renderAction, className = "", }) {
4
- return (_jsxs(Div, { className: className, children: [title && (_jsx(Heading, { level: 3, className: "mb-4", children: title })), _jsx(Div, { layout: "grid", gap: "4", className: "grid-cols-1 md:grid-cols-3", children: actions.map((action) => (_jsx(Div, { children: renderAction ? (renderAction(action)) : action.onClick ? (_jsxs("button", { type: "button", onClick: action.onClick, className: "w-full inline-flex items-center justify-start gap-2 rounded-lg border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text)] px-3 py-2 text-sm", children: [action.icon, action.label] })) : (_jsxs(TextLink, { variant: "bare", href: action.href ?? "#", rounded: "lg", paddingX: "sm", paddingY: "xs", size: "sm", layout: "inline-flex", align: "center", justify: "start", gap: "sm", className: "w-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text)]", children: [action.icon, action.label] })) }, action.id))) })] }));
4
+ return (_jsxs(Div, { className: className, children: [title && (_jsx(Heading, { level: 3, className: "mb-4", children: title })), _jsx(Div, { layout: "grid", gap: "4", className: "grid-cols-1 md:grid-cols-3", children: actions.map((action) => (_jsx(Div, { children: renderAction ? (renderAction(action)) : action.onClick ? (_jsx(Button, { type: "button", variant: "outline", onClick: action.onClick, rounded: "lg", paddingX: "sm", paddingY: "sm", textSize: "sm", className: "w-full", children: _jsxs(Row, { align: "center", justify: "start", gap: "sm", className: "w-full", children: [action.icon, action.label] }) })) : (_jsxs(TextLink, { variant: "bare", href: action.href ?? "#", rounded: "lg", paddingX: "sm", paddingY: "xs", size: "sm", layout: "inline-flex", align: "center", justify: "start", gap: "sm", className: "w-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text)]", children: [action.icon, action.label] })) }, action.id))) })] }));
5
5
  }
@@ -115,7 +115,7 @@ function CarouselCardRenderer({ card, isMobile, push, }) {
115
115
  gridRow: String(gridPos.gridRow),
116
116
  gridColumn: String(gridPos.gridColumn),
117
117
  minHeight: isMobile ? 100 : 140,
118
- }, children: [_jsx(CardBackground, { bg: card.background }), !card.isButtonOnly && (_jsxs(_Fragment, { children: [_jsx(Scrim, { direction: "bottom-up", intensity: "strong", className: POSITION_FILL }), _jsxs(Stack, { justify: "end", className: `${POSITION_FILL} md:p-[1.5rem] ${textAlignClass}`, padding: "xs", children: [card.content?.eyebrow && (_jsx(Text, { color: "inverse", shadow: "sm", className: "text-[10px] !/70 mb-0.5 tracking-wider", mdSize: "xs", transform: "uppercase", children: card.content.eyebrow })), card.content?.subtitle && (_jsx(Text, { color: "inverse", shadow: "sm", className: "hidden md:block !/90 mb-0.5 md:mb-2", mdSize: "sm", size: "xs", children: card.content.subtitle })), card.content?.title && (_jsx(Heading, { level: 2, className: `text-[11px] mb-0.5 md:mb-3`, truncate: 2, weight: "bold", shadow: "md", mdSize: "2xl", lgSize: "3xl", style: { color: card.content.textColor ?? "white" }, children: card.content.title })), card.content?.description && (_jsx(Text, { color: "inverse", shadow: "sm", className: `text-[10px] md:text-[0.875rem] !/80 mb-1 md:mb-4`, truncate: true, children: card.content.description })), (card.buttons?.length ?? 0) > 0 && (_jsx(Row, { wrap: true, gap: "xs", className: "md:gap-[0.5rem]", children: (card.buttons ?? []).map((btn, i) => {
118
+ }, children: [_jsx(CardBackground, { bg: card.background }), !card.isButtonOnly && (_jsxs(_Fragment, { children: [_jsx(Scrim, { direction: "bottom-up", intensity: "strong", className: POSITION_FILL }), _jsxs(Stack, { justify: "end", className: `${POSITION_FILL} md:p-[1.5rem] ${textAlignClass}`, padding: "xs", children: [card.content?.eyebrow && (_jsx(Text, { color: "inverse", shadow: "sm", className: "text-[10px] !/70 mb-0.5 tracking-wider", mdSize: "xs", transform: "uppercase", children: card.content.eyebrow })), card.content?.subtitle && (_jsx(Text, { color: "inverse", shadow: "sm", className: "hidden md:block !/90 mb-0.5 md:mb-2", mdSize: "sm", size: "xs", children: card.content.subtitle })), card.content?.title && (_jsx(Heading, { level: 2, className: `text-[11px] mb-0.5 md:mb-3`, truncate: 2, weight: "bold", shadow: "md", mdSize: "2xl", lgSize: "3xl", style: { color: card.content.textColor ?? "white" }, children: card.content.title })), card.content?.description && (_jsx(Text, { color: "inverse", shadow: "sm", className: `text-[10px] md:text-[0.875rem] !/80 mb-1 md:mb-4`, truncate: true, children: card.content.description })), (card.buttons?.length ?? 0) > 0 && (_jsx(Row, { wrap: true, gap: "xs", className: "md:gap-[var(--appkit-space-2)]", children: (card.buttons ?? []).map((btn, i) => {
119
119
  const variant = btn.variant === "link" || btn.variant === "ghost"
120
120
  ? "ghost"
121
121
  : btn.variant;
@@ -184,7 +184,7 @@ export function HeroCarousel({ initialSlides, push } = {}) {
184
184
  return (_jsxs(Section, { ref: sectionRef, className: `relative w-full ${heightClass} overflow-hidden`, "aria-roledescription": "carousel", "aria-label": "Hero carousel", onKeyDown: handleKeyDown, onMouseEnter: () => setIsPaused(true), onMouseLeave: () => setIsPaused(false), onFocus: () => setIsPaused(true), onBlur: () => setIsPaused(false), tabIndex: 0, children: [_jsx(Div, { "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: `Slide ${currentSlide + 1} of ${slides.length}` }), _jsx(Div, { className: POSITION_FILL, children: _jsx(Div, { layout: "flex", ref: slidesRef, onScroll: handleSlidesScroll, className: "h-full overflow-x-auto scroll-smooth snap-x snap-mandatory [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", style: { gap: 0 }, children: slides.map((slide, slideIndex) => {
185
185
  const slideHeightClass = getSlideHeightClass(slide.settings?.height);
186
186
  const hasCards = slide.cards && slide.cards.length > 0;
187
- return (_jsxs(Div, { className: `snap-start flex-none w-full relative self-stretch bg-neutral-900 ${slideHeightClass}`, children: [_jsx(Div, { className: POSITION_FILL, children: _jsx(SlideBackground, { bg: slide.background, legacy: slide.media, mobileLegacy: slide.mobileMedia, isMobile: isMobile, priority: slideIndex === 0 }) }), slide.overlay && (_jsxs(Div, { className: `${POSITION_FILL} ${FLEX_CENTER} [flex-direction:column] text-center md:px-[4rem] lg:px-[8rem]`, padding: "x-lg", children: [slide.overlay.subtitle && (_jsx(Text, { color: "inverse", shadow: "sm", className: "stagger-1 !/80 mb-1 md:mb-2 tracking-widest", mdSize: "sm", size: "xs", transform: "uppercase", children: slide.overlay.subtitle })), slide.overlay.title && (_jsx(Heading, { color: "inverse", level: 1, className: "stagger-2 font-display ! mb-2 md:mb-4", size: "4xl", shadow: "2xl", mdSize: "6xl", lgSize: "8xl", children: slide.overlay.title })), slide.overlay.description && (_jsx(Text, { color: "inverse", shadow: "sm", className: "stagger-3 !/90 mb-4 md:mb-8 max-w-2xl mx-auto", mdSize: "lg", lgSize: "xl", size: "sm", children: slide.overlay.description })), slide.overlay.button && (_jsx(Div, { className: "stagger-4", children: _jsx(Button, { variant: slide.overlay.button.variant, size: "sm", onClick: makeButtonClickHandler(slide.overlay.button.link, slide.overlay.button.openInNewTab, push), children: slide.overlay.button.text }) }))] })), hasCards && (_jsx(Div, { className: `${POSITION_FILL} grid gap-[0.5rem] md:gap-[1rem] md:p-[2rem]`, padding: "md", style: {
187
+ return (_jsxs(Div, { className: `snap-start flex-none w-full relative self-stretch bg-neutral-900 ${slideHeightClass}`, children: [_jsx(Div, { className: POSITION_FILL, children: _jsx(SlideBackground, { bg: slide.background, legacy: slide.media, mobileLegacy: slide.mobileMedia, isMobile: isMobile, priority: slideIndex === 0 }) }), slide.overlay && (_jsxs(Div, { className: `${POSITION_FILL} ${FLEX_CENTER} [flex-direction:column] text-center md:px-[4rem] lg:px-[8rem]`, padding: "x-lg", children: [slide.overlay.subtitle && (_jsx(Text, { color: "inverse", shadow: "sm", className: "stagger-1 !/80 mb-1 md:mb-2 tracking-widest", mdSize: "sm", size: "xs", transform: "uppercase", children: slide.overlay.subtitle })), slide.overlay.title && (_jsx(Heading, { color: "inverse", level: 1, className: "stagger-2 font-display ! mb-2 md:mb-4", size: "4xl", shadow: "2xl", mdSize: "6xl", lgSize: "8xl", children: slide.overlay.title })), slide.overlay.description && (_jsx(Text, { color: "inverse", shadow: "sm", className: "stagger-3 !/90 mb-4 md:mb-8 max-w-2xl mx-auto", mdSize: "lg", lgSize: "xl", size: "sm", children: slide.overlay.description })), slide.overlay.button && (_jsx(Div, { className: "stagger-4", children: _jsx(Button, { variant: slide.overlay.button.variant, size: "sm", onClick: makeButtonClickHandler(slide.overlay.button.link, slide.overlay.button.openInNewTab, push), children: slide.overlay.button.text }) }))] })), hasCards && (_jsx(Div, { className: `${POSITION_FILL} grid gap-[var(--appkit-space-2)] md:gap-[var(--appkit-space-4)] md:p-[var(--appkit-space-8)]`, padding: "md", style: {
188
188
  gridTemplateRows: "repeat(2, 1fr)",
189
189
  gridTemplateColumns: isMobile ? "1fr" : "repeat(3, 1fr)",
190
190
  alignContent: "center",
@@ -18,12 +18,6 @@ import CameraCapture from "./CameraCapture";
18
18
  import { extractVideoPosterFrame, posterBlobAsFile } from "./video-poster";
19
19
  import { inferMediaTypeFromMime } from "../types/index";
20
20
  import { normalizeError } from "../../../errors/normalize";
21
- const __P = {
22
- p3: "p-3",
23
- };
24
- const __O = {
25
- hidden: "overflow-hidden",
26
- };
27
21
  const CLS_PDF_BADGE = "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-error-surface text-error dark:bg-error-surface dark:text-error";
28
22
  const CLS_PDF_NAME = "text-sm font-medium text-info underline break-all dark:text-info";
29
23
  const CLS_PDF_LINK = "text-sm underline break-all text-info dark:text-info";
@@ -85,7 +79,7 @@ function ExternalUrlTabPanel({ extInput, setExtInput, extError, setExtError, onA
85
79
  return (_jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { gap: "sm", children: [_jsx("input", { type: "url", value: extInput, onChange: (e) => { setExtInput(e.target.value); setExtError(""); }, placeholder: "https://example.com/image.jpg", className: "appkit-input flex-1" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: onApply, children: "Apply" })] }), extError && _jsx(Alert, { variant: "error", children: extError }), _jsx(Text, { size: "xs", variant: "secondary", children: "External URLs are stored as-is and are not watermarked." })] }));
86
80
  }
87
81
  function MediaPreviewPanel({ value, label, disabled, enableTrim, enableThumbnail, onEditVideo, onRemove, tUpload, tMediaEditor, }) {
88
- return (_jsxs(Div, { className: `${__P.p3}`, rounded: "lg", surface: "muted", border: "default", children: [isVideo(value) ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: `relative aspect-video ${__O.hidden}`, rounded: "lg", children: _jsx(MediaVideo, { src: value, alt: label, controls: true, objectFit: "contain" }) }), _jsx(Text, { size: "xs", variant: "secondary", children: "Video uploaded successfully. Large videos may take longer to process." })] })) : isImage(value) ? (_jsx(Div, { className: `relative aspect-video ${__O.hidden}`, rounded: "lg", children: _jsx(MediaImage, { src: value, alt: label, size: "card", objectFit: "contain" }) })) : isPdf(value) ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: `relative w-full ${__O.hidden}`, rounded: "lg", border: "default", style: { height: 280 }, children: _jsx("iframe", { src: value, title: filenameFromUrl(value), className: "w-full h-full border-0" }) }), _jsxs(Row, { gap: "md", align: "center", children: [_jsx(Div, { "aria-hidden": true, className: CLS_PDF_BADGE, children: _jsx(Span, { size: "xs", weight: "bold", children: "PDF" }) }), _jsx(Anchor, { href: value, className: CLS_PDF_NAME, children: filenameFromUrl(value) })] })] })) : (_jsx(Anchor, { href: value, className: CLS_PDF_LINK, children: filenameFromUrl(value) })), !disabled && (_jsxs(Row, { wrap: true, gap: "sm", className: "mt-2", children: [isVideo(value) && (enableTrim || enableThumbnail) && (_jsx(Button, { type: "button", onClick: () => onEditVideo(value), variant: "secondary", size: "sm", children: tMediaEditor("editVideo") })), _jsx(Button, { type: "button", onClick: onRemove, variant: "danger", size: "sm", children: tUpload("remove") })] }))] }));
82
+ return (_jsxs(Div, { padding: "sm", rounded: "lg", surface: "muted", border: "default", children: [isVideo(value) ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: "relative aspect-video", overflow: "hidden", rounded: "lg", children: _jsx(MediaVideo, { src: value, alt: label, controls: true, objectFit: "contain" }) }), _jsx(Text, { size: "xs", variant: "secondary", children: "Video uploaded successfully. Large videos may take longer to process." })] })) : isImage(value) ? (_jsx(Div, { className: "relative aspect-video", overflow: "hidden", rounded: "lg", children: _jsx(MediaImage, { src: value, alt: label, size: "card", objectFit: "contain" }) })) : isPdf(value) ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: "relative w-full", overflow: "hidden", rounded: "lg", border: "default", style: { height: 280 }, children: _jsx("iframe", { src: value, title: filenameFromUrl(value), className: "w-full h-full border-0" }) }), _jsxs(Row, { gap: "md", align: "center", children: [_jsx(Div, { "aria-hidden": true, className: CLS_PDF_BADGE, children: _jsx(Span, { size: "xs", weight: "bold", children: "PDF" }) }), _jsx(Anchor, { href: value, className: CLS_PDF_NAME, children: filenameFromUrl(value) })] })] })) : (_jsx(Anchor, { href: value, className: CLS_PDF_LINK, children: filenameFromUrl(value) })), !disabled && (_jsxs(Row, { wrap: true, gap: "sm", className: "mt-2", children: [isVideo(value) && (enableTrim || enableThumbnail) && (_jsx(Button, { type: "button", onClick: () => onEditVideo(value), variant: "secondary", size: "sm", children: tMediaEditor("editVideo") })), _jsx(Button, { type: "button", onClick: onRemove, variant: "danger", size: "sm", children: tUpload("remove") })] }))] }));
89
83
  }
90
84
  export function MediaUploadField({ label, value, onChange, onChangeField, onUpload,
91
85
  // SB-UNI-Z5 2026-05-13 — `kind` derives accept/maxSizeMB when not set
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Row, SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
3
+ import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
4
4
  import { sortBy } from "@mohasinac/appkit";
5
5
  import React, { useState, useCallback } from "react";
6
6
  import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
7
- import { Div, ListingLayout, Text, useToast } from "../../../ui";
7
+ import { Div, FilterChipGroup, ListingLayout, useToast } from "../../../ui";
8
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
9
  import { toRecordArray, toStringValue, } from "../../admin/hooks/useAdminListingData";
10
10
  import { normalizeError } from "../../../errors/normalize";
@@ -71,13 +71,11 @@ export function SellerCouponsView({ onCreateClick, onEditClick, onToggle, onDele
71
71
  primaryAction: onCreateClick
72
72
  ? { label: "Add Coupon", onClick: () => onCreateClick() }
73
73
  : undefined,
74
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { className: "tracking-widest", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "Status" }), _jsx(Row, { wrap: true, gap: "sm", children: [
75
- { label: "All", value: "" },
76
- { label: "Active", value: "true" },
77
- { label: "Inactive", value: "false" },
78
- ].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, isActive: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.isActive || "") === opt.value
79
- ? "bg-[var(--appkit-color-primary)] text-white border-[var(--appkit-color-primary)]"
80
- : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:bg-zinc-50 hover:bg-[var(--appkit-color-surface-elevated)]"}`, children: opt.label }, opt.label))) })] })),
74
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: [
75
+ { id: "All", label: "All" },
76
+ { id: "true", label: "Active" },
77
+ { id: "false", label: "Inactive" },
78
+ ], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) })),
81
79
  renderCards: (rows, _view, _selection, isLoading) => isLoading ? (_jsx(Div, { gap: "3", className: "fluid-grid-card", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Stack, { border: "skeleton", className: "animate-pulse", gap: "3", rounded: "xl", padding: "md", children: [_jsx(Div, { className: "h-6 w-2/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" })] }, i))) })) : (_jsx(Div, { gap: "3", className: "fluid-grid-card", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: onEditClick ? handleEdit : undefined, onToggleActive: onToggle ? handleToggle : undefined, onDelete: onDelete ? handleDelete : undefined, className: togglingId === row.id || deletingId === row.id
82
80
  ? "pointer-events-none opacity-60"
83
81
  : undefined }, row.id))) })),