@mohasinac/appkit 3.4.4 → 3.4.5
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/_internal/client/features/maintenance/views/AnalysisRunnerView.js +2 -9
- package/dist/features/account/components/NotificationPreferencesPanel.js +2 -11
- package/dist/features/account/components/UserSidebar.js +4 -4
- package/dist/features/admin/components/AdminBrandsView.js +7 -9
- package/dist/features/admin/components/AdminBundlesView.js +10 -14
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -5
- package/dist/features/admin/components/AdminCategoriesView.js +10 -14
- package/dist/features/admin/components/AdminFaqsView.js +7 -9
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -1
- package/dist/features/admin/components/AdminFeaturesView.js +9 -18
- package/dist/features/admin/components/AdminNotificationsView.js +4 -6
- package/dist/features/admin/components/AdminSessionsView.js +7 -9
- package/dist/features/admin/components/AdminSidebar.js +4 -4
- package/dist/features/admin/components/AdminSiteSettingsView.js +1 -1
- package/dist/features/admin/components/QuickActionsPanel.js +2 -2
- package/dist/features/seller/components/SellerCouponsView.js +7 -9
- package/dist/features/seller/components/SellerSidebar.js +4 -4
- package/dist/styles.css +40 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +28 -0
- package/dist/tokens/themes/default-light.js +28 -0
- package/dist/tokens/themes/required.d.ts +1 -1
- package/dist/tokens/themes/required.js +30 -0
- package/dist/tokens/tokens.css +39 -0
- package/dist/ui/components/Toggle.d.ts +3 -1
- package/dist/ui/components/Toggle.js +2 -2
- package/dist/ui/components/surface-tokens.d.ts +113 -113
- package/dist/ui/components/surface-tokens.js +104 -102
- 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(
|
|
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(
|
|
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: [
|
|
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)]
|
|
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(
|
|
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 {
|
|
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 }) => (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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 {
|
|
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: [
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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(
|
|
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 {
|
|
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: [
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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 {
|
|
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 }) => (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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: [
|
|
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 {
|
|
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
|
|
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(
|
|
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,
|
|
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 {
|
|
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,
|
|
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 }) => (
|
|
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,
|
|
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 {
|
|
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,
|
|
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 }) => (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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: [
|
|
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)]
|
|
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(
|
|
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(
|
|
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 ? (
|
|
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
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
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,
|
|
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 }) => (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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))) })),
|
|
@@ -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, Text, Ul } from "../../../ui";
|
|
6
|
+
import { Button, Div, IconButton, Li, Nav, Row, Span, Stack, Text, Ul } from "../../../ui";
|
|
7
7
|
import { MediaImage } from "../../media/MediaImage";
|
|
8
8
|
import { BottomSheet } from "../../layout/BottomSheet";
|
|
9
9
|
import { SidebarCollapseToggle } from "../../../_internal/client/features/layout/SidebarCollapseToggle";
|
|
@@ -40,13 +40,13 @@ function GroupsContent({ groups, activeHref, storeName, storeLogoURL, onItemClic
|
|
|
40
40
|
return (_jsxs(_Fragment, { children: [storeName && (_jsxs(Row, { border: "bottom-subtle", gap: "3", padding: "inline", children: [storeLogoURL ? (_jsx(Div, { className: CLS_STORE_AVATAR, children: _jsx(MediaImage, { src: storeLogoURL, alt: storeName, size: "thumbnail" }) })) : (_jsx(Div, { className: CLS_STORE_FALLBACK, children: storeName[0]?.toUpperCase() })), _jsx(Text, { className: CLS_STORE_NAME, children: storeName })] })), _jsx(Nav, { "aria-label": "Store navigation", padding: "y-xs", children: groups.map((group) => {
|
|
41
41
|
const isOpen = openGroups[group.title] ?? false;
|
|
42
42
|
const hasActive = group.items.some((i) => activeHref === i.href);
|
|
43
|
-
return (_jsxs(Div, { className: "mb-0.5", children: [
|
|
43
|
+
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
|
|
44
44
|
? CLS_NAV_ICON_ACTIVE
|
|
45
|
-
: "text-[var(--appkit-color-text-faint)] hover:text-[var(--appkit-color-text-muted)]
|
|
45
|
+
: "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));
|
|
46
46
|
}) })] }));
|
|
47
47
|
}
|
|
48
48
|
function DrawerPanel({ title, onClose, children }) {
|
|
49
|
-
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(
|
|
49
|
+
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 })] })] }));
|
|
50
50
|
}
|
|
51
51
|
export function StoreSidebar({ items, groups, activeHref, storeName, storeLogoURL, mobileOpen = false, desktopOpen = false, variant = "overlay", onCloseMobile, onToggle, }) {
|
|
52
52
|
const close = onCloseMobile ?? (() => { });
|