@mohasinac/appkit 2.6.6 → 2.6.8
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/server/jobs/core/onSupportTicketCreate.d.ts +10 -0
- package/dist/_internal/server/jobs/core/onSupportTicketCreate.js +26 -0
- package/dist/_internal/server/jobs/core/onSupportTicketUpdate.d.ts +15 -0
- package/dist/_internal/server/jobs/core/onSupportTicketUpdate.js +50 -0
- package/dist/_internal/server/jobs/core/onUserBanChange.d.ts +17 -0
- package/dist/_internal/server/jobs/core/onUserBanChange.js +59 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +3 -0
- package/dist/_internal/server/jobs/handlers/index.js +4 -0
- package/dist/_internal/server/jobs/handlers/onSupportTicketCreate.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/onSupportTicketCreate.js +7 -0
- package/dist/_internal/server/jobs/handlers/onSupportTicketUpdate.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/onSupportTicketUpdate.js +8 -0
- package/dist/_internal/server/jobs/handlers/onUserBanChange.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/onUserBanChange.js +10 -0
- package/dist/client.d.ts +2 -2
- package/dist/client.js +1 -1
- package/dist/configs/next.js +12 -0
- package/dist/features/account/components/UserSupportView.d.ts +3 -0
- package/dist/features/account/components/UserSupportView.js +90 -0
- package/dist/features/account/components/index.d.ts +2 -0
- package/dist/features/account/components/index.js +1 -0
- package/dist/features/admin/components/AdminScammerEditorView.d.ts +15 -0
- package/dist/features/admin/components/AdminScammerEditorView.js +61 -0
- package/dist/features/admin/components/AdminScammersView.d.ts +4 -0
- package/dist/features/admin/components/AdminScammersView.js +147 -0
- package/dist/features/admin/components/index.d.ts +4 -0
- package/dist/features/admin/components/index.js +2 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +17 -0
- package/dist/features/admin/constants/filter-tabs.js +8 -0
- package/dist/features/faq/schemas/firestore.js +1 -0
- package/dist/features/faq/types/index.d.ts +1 -1
- package/dist/features/layout/AppLayoutShell.d.ts +2 -1
- package/dist/features/layout/AppLayoutShell.js +10 -4
- package/dist/features/layout/TitleBarLayout.d.ts +3 -1
- package/dist/features/layout/TitleBarLayout.js +8 -4
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/jobs.d.ts +1 -1
- package/dist/jobs.js +2 -0
- package/dist/next/routing/route-map.d.ts +14 -0
- package/dist/next/routing/route-map.js +7 -0
- package/dist/seed/faq-seed-data.js +303 -0
- package/dist/seed/users-seed-data.js +81 -3
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Badge.d.ts +1 -0
- package/dist/ui/components/Badge.js +1 -0
- package/dist/ui/components/Badge.style.css +11 -0
- package/dist/ui/components/RoleBadge.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState, useCallback } from "react";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
|
+
import { FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, } from "../../../ui";
|
|
7
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
+
import { ADMIN_SCAMMER_STATUS_TABS } from "../constants/filter-tabs";
|
|
9
|
+
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
10
|
+
import { DataTable } from "./DataTable";
|
|
11
|
+
import { AdminScammerEditorView } from "./AdminScammerEditorView";
|
|
12
|
+
const PAGE_SIZE = 25;
|
|
13
|
+
const FILTER_KEYS = ["status"];
|
|
14
|
+
const DEFAULT_SORT = "-createdAt";
|
|
15
|
+
const SORT_OPTIONS = [
|
|
16
|
+
{ value: "-createdAt", label: "Newest" },
|
|
17
|
+
{ value: "createdAt", label: "Oldest" },
|
|
18
|
+
{ value: "-views", label: "Most viewed" },
|
|
19
|
+
{ value: "-incidentCount", label: "Most incidents" },
|
|
20
|
+
];
|
|
21
|
+
const STATUS_BADGE = {
|
|
22
|
+
pending_review: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/40 dark:text-yellow-300",
|
|
23
|
+
verified: "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300",
|
|
24
|
+
rejected: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300",
|
|
25
|
+
removed: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
|
|
26
|
+
};
|
|
27
|
+
export function AdminScammersView({ children, ...props }) {
|
|
28
|
+
const hasChildren = React.Children.count(children) > 0;
|
|
29
|
+
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
30
|
+
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
31
|
+
const [filterOpen, setFilterOpen] = useState(false);
|
|
32
|
+
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
33
|
+
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
34
|
+
const [selectedRow, setSelectedRow] = useState(null);
|
|
35
|
+
const openFilters = useCallback(() => {
|
|
36
|
+
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
37
|
+
setFilterOpen(true);
|
|
38
|
+
}, [table]);
|
|
39
|
+
const applyFilters = useCallback(() => {
|
|
40
|
+
const updates = { page: "1" };
|
|
41
|
+
for (const k of FILTER_KEYS)
|
|
42
|
+
updates[k] = pendingFilters[k] ?? "";
|
|
43
|
+
table.setMany(updates);
|
|
44
|
+
setFilterOpen(false);
|
|
45
|
+
}, [pendingFilters, table]);
|
|
46
|
+
const clearFilters = useCallback(() => {
|
|
47
|
+
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
48
|
+
}, []);
|
|
49
|
+
const resetAll = useCallback(() => {
|
|
50
|
+
const updates = { q: "", sort: "" };
|
|
51
|
+
for (const k of FILTER_KEYS)
|
|
52
|
+
updates[k] = "";
|
|
53
|
+
table.setMany(updates);
|
|
54
|
+
setSearchInput("");
|
|
55
|
+
}, [table]);
|
|
56
|
+
const commitSearch = useCallback(() => {
|
|
57
|
+
table.set("q", searchInput.trim());
|
|
58
|
+
}, [searchInput, table]);
|
|
59
|
+
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
60
|
+
const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
|
|
61
|
+
const filterParts = [];
|
|
62
|
+
const statusRaw = table.get("status");
|
|
63
|
+
if (statusRaw && statusRaw !== "All")
|
|
64
|
+
filterParts.push(`status==${statusRaw}`);
|
|
65
|
+
const filters = filterParts.join(",") || undefined;
|
|
66
|
+
const { rows, total, isLoading, errorMessage } = useAdminListingData({
|
|
67
|
+
queryKey: ["admin", "scammers", "listing"],
|
|
68
|
+
endpoint: ADMIN_ENDPOINTS.SCAMMERS,
|
|
69
|
+
page: table.getNumber("page", 1),
|
|
70
|
+
pageSize: PAGE_SIZE,
|
|
71
|
+
sorts: table.get("sort") || DEFAULT_SORT,
|
|
72
|
+
filters,
|
|
73
|
+
q: table.get("q") || undefined,
|
|
74
|
+
mapRows: (response) => toRecordArray(response.scammers).map((item, index) => ({
|
|
75
|
+
id: toStringValue(item.id, `scammer-${index}`),
|
|
76
|
+
primary: Array.isArray(item.displayNames)
|
|
77
|
+
? item.displayNames.join(", ")
|
|
78
|
+
: toStringValue(item.displayNames, "Unknown"),
|
|
79
|
+
secondary: [
|
|
80
|
+
toStringValue(item.scamType, ""),
|
|
81
|
+
Array.isArray(item.phones) ? `${item.phones.length} phone(s)` : null,
|
|
82
|
+
`${Number(item.incidentCount ?? 0)} incident(s)`,
|
|
83
|
+
]
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.join(" · "),
|
|
86
|
+
status: toStringValue(item.status, "pending_review"),
|
|
87
|
+
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
88
|
+
_raw: item,
|
|
89
|
+
})),
|
|
90
|
+
getTotal: (response, mappedRows) => {
|
|
91
|
+
if (typeof response.meta?.filteredTotal === "number")
|
|
92
|
+
return response.meta.filteredTotal;
|
|
93
|
+
if (typeof response.meta?.total === "number")
|
|
94
|
+
return response.meta.total;
|
|
95
|
+
if (typeof response.total === "number")
|
|
96
|
+
return response.total;
|
|
97
|
+
return mappedRows.length;
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
const currentPage = table.getNumber("page", 1);
|
|
101
|
+
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
102
|
+
if (hasChildren) {
|
|
103
|
+
return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
|
|
104
|
+
}
|
|
105
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by name, phone, UPI ID", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
|
|
106
|
+
table.set("sort", v);
|
|
107
|
+
table.setPage(1);
|
|
108
|
+
}, hideViewToggle: true, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No scammer profiles found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
109
|
+
{
|
|
110
|
+
label: "Review",
|
|
111
|
+
onClick: () => {
|
|
112
|
+
setSelectedRow(row);
|
|
113
|
+
setDrawerOpen(true);
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
] })), columns: [
|
|
117
|
+
{
|
|
118
|
+
key: "primary",
|
|
119
|
+
header: "Name / Aliases",
|
|
120
|
+
render: (row) => {
|
|
121
|
+
const r = row;
|
|
122
|
+
return (_jsxs("div", { className: "space-y-0.5", children: [_jsx("p", { className: "font-medium text-zinc-900 dark:text-zinc-100", children: r.primary }), r.secondary ? (_jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: r.secondary })) : null] }));
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
key: "status",
|
|
127
|
+
header: "Status",
|
|
128
|
+
className: "w-36",
|
|
129
|
+
render: (row) => {
|
|
130
|
+
const r = row;
|
|
131
|
+
return (_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_BADGE[r.status] ?? STATUS_BADGE.pending_review}`, children: r.status.replace(/_/g, " ") }));
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
key: "updatedAt",
|
|
136
|
+
header: "Updated",
|
|
137
|
+
className: "w-32",
|
|
138
|
+
render: (row) => (_jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: row.updatedAt })),
|
|
139
|
+
},
|
|
140
|
+
] })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_SCAMMER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }), _jsx(AdminScammerEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), scammerId: selectedRow?.id, displayNames: Array.isArray(selectedRow?._raw?.displayNames)
|
|
141
|
+
? selectedRow._raw.displayNames
|
|
142
|
+
: [selectedRow?.primary ?? ""], scamType: toStringValue(selectedRow?._raw?.scamType, ""), description: toStringValue(selectedRow?._raw?.description, ""), phones: Array.isArray(selectedRow?._raw?.phones)
|
|
143
|
+
? selectedRow._raw.phones
|
|
144
|
+
: [], upiIds: Array.isArray(selectedRow?._raw?.upiIds)
|
|
145
|
+
? selectedRow._raw.upiIds
|
|
146
|
+
: [], currentStatus: selectedRow?.status, verificationNote: toStringValue(selectedRow?._raw?.verificationNote, "") || undefined, reportedBy: toStringValue(selectedRow?._raw?.reportedBy, ""), reportedByAnon: Boolean(selectedRow?._raw?.reportedByAnon) })] }));
|
|
147
|
+
}
|
|
@@ -141,3 +141,7 @@ export { AdminSupportTicketsView } from "./AdminSupportTicketsView";
|
|
|
141
141
|
export type { AdminSupportTicketsViewProps } from "./AdminSupportTicketsView";
|
|
142
142
|
export { AdminSupportTicketDetailView } from "./AdminSupportTicketDetailView";
|
|
143
143
|
export type { AdminSupportTicketDetailViewProps } from "./AdminSupportTicketDetailView";
|
|
144
|
+
export { AdminScammersView } from "./AdminScammersView";
|
|
145
|
+
export type { AdminScammersViewProps } from "./AdminScammersView";
|
|
146
|
+
export { AdminScammerEditorView } from "./AdminScammerEditorView";
|
|
147
|
+
export type { AdminScammerEditorViewProps } from "./AdminScammerEditorView";
|
|
@@ -73,3 +73,5 @@ export { AdminTeamView } from "./AdminTeamView";
|
|
|
73
73
|
export { AdminEmployeeEditorView } from "./AdminEmployeeEditorView";
|
|
74
74
|
export { AdminSupportTicketsView } from "./AdminSupportTicketsView";
|
|
75
75
|
export { AdminSupportTicketDetailView } from "./AdminSupportTicketDetailView";
|
|
76
|
+
export { AdminScammersView } from "./AdminScammersView";
|
|
77
|
+
export { AdminScammerEditorView } from "./AdminScammerEditorView";
|
|
@@ -266,6 +266,23 @@ export declare const ADMIN_EVENT_STATUS_TABS: readonly [{
|
|
|
266
266
|
readonly id: "ended";
|
|
267
267
|
readonly label: "Ended";
|
|
268
268
|
}];
|
|
269
|
+
/** Admin > Scammers — scammer profile status filter chip set. */
|
|
270
|
+
export declare const ADMIN_SCAMMER_STATUS_TABS: readonly [{
|
|
271
|
+
readonly id: "All";
|
|
272
|
+
readonly label: "All";
|
|
273
|
+
}, {
|
|
274
|
+
readonly id: "pending_review";
|
|
275
|
+
readonly label: "Pending";
|
|
276
|
+
}, {
|
|
277
|
+
readonly id: "verified";
|
|
278
|
+
readonly label: "Verified";
|
|
279
|
+
}, {
|
|
280
|
+
readonly id: "rejected";
|
|
281
|
+
readonly label: "Rejected";
|
|
282
|
+
}, {
|
|
283
|
+
readonly id: "removed";
|
|
284
|
+
readonly label: "Removed";
|
|
285
|
+
}];
|
|
269
286
|
/** Admin > Support Tickets — ticket-status filter chip set. */
|
|
270
287
|
export declare const ADMIN_SUPPORT_TICKET_STATUS_TABS: readonly [{
|
|
271
288
|
readonly id: "All";
|
|
@@ -130,6 +130,14 @@ export const ADMIN_EVENT_STATUS_TABS = [
|
|
|
130
130
|
{ id: "active", label: "Active" },
|
|
131
131
|
{ id: "ended", label: "Ended" },
|
|
132
132
|
];
|
|
133
|
+
/** Admin > Scammers — scammer profile status filter chip set. */
|
|
134
|
+
export const ADMIN_SCAMMER_STATUS_TABS = [
|
|
135
|
+
ALL_TAB,
|
|
136
|
+
{ id: "pending_review", label: "Pending" },
|
|
137
|
+
{ id: "verified", label: "Verified" },
|
|
138
|
+
{ id: "rejected", label: "Rejected" },
|
|
139
|
+
{ id: "removed", label: "Removed" },
|
|
140
|
+
];
|
|
133
141
|
/** Admin > Support Tickets — ticket-status filter chip set. */
|
|
134
142
|
export const ADMIN_SUPPORT_TICKET_STATUS_TABS = [
|
|
135
143
|
ALL_TAB,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type FAQAnswerFormat = "plain" | "markdown" | "html";
|
|
2
|
-
export type FAQCategory = "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support" | "general";
|
|
2
|
+
export type FAQCategory = "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support" | "general" | "scam_awareness";
|
|
3
3
|
export interface FAQAnswer {
|
|
4
4
|
text: string;
|
|
5
5
|
format: FAQAnswerFormat;
|
|
@@ -41,6 +41,7 @@ export interface AppLayoutShellProps {
|
|
|
41
41
|
userId?: string | null;
|
|
42
42
|
profileHref: string;
|
|
43
43
|
loginHref: string;
|
|
44
|
+
registerHref?: string;
|
|
44
45
|
homeHref: string;
|
|
45
46
|
shopHref: string;
|
|
46
47
|
footer: FooterLayoutProps;
|
|
@@ -106,4 +107,4 @@ export interface AppLayoutShellProps {
|
|
|
106
107
|
};
|
|
107
108
|
};
|
|
108
109
|
}
|
|
109
|
-
export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, lightBackground, darkBackground, }: AppLayoutShellProps): import("react/jsx-runtime").JSX.Element;
|
|
110
|
+
export declare function AppLayoutShell({ children, navItems, sidebarItems, sidebarSections, sidebarPrimaryActions, sidebarTitle, hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle, suppressDashboardNav, hideSidebarToggle, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar, sidebarProfileLabels, eventBannerSlot, lightBackground, darkBackground, }: AppLayoutShellProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
5
|
-
import { Main, Div, Text, TextLink, Ul, Li, AvatarDisplay,
|
|
5
|
+
import { Main, Div, Text, TextLink, Ul, Li, AvatarDisplay, BackgroundRenderer, UnsavedChangesModal, } from "../../ui";
|
|
6
|
+
const ROLE_DOT_COLORS = {
|
|
7
|
+
admin: "#9333ea",
|
|
8
|
+
moderator: "#0ea5e9",
|
|
9
|
+
seller: "#0d9488",
|
|
10
|
+
employee: "#f59e0b",
|
|
11
|
+
};
|
|
6
12
|
import { useTheme } from "../../react";
|
|
7
13
|
import { useBottomActionsContext } from "./BottomActionsContext";
|
|
8
14
|
import BottomActions from "./BottomActions";
|
|
@@ -37,7 +43,7 @@ function CollapsibleSidebarSection({ section, navItemClass, }) {
|
|
|
37
43
|
}
|
|
38
44
|
return (_jsxs(Div, { className: "space-y-0.5", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center justify-between px-1 py-1 text-xs font-semibold uppercase tracking-wider text-zinc-500 dark:text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-300 transition-colors", children: [_jsx("span", { children: section.title }), _jsx("svg", { className: `w-3.5 h-3.5 transition-transform duration-200 ${open ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), open && (_jsx(Ul, { className: "space-y-0.5", children: section.items.map((item) => (_jsx(Li, { children: _jsxs(TextLink, { href: item.href, variant: "none", className: navItemClass, children: [item.icon && (_jsx("span", { className: "flex-shrink-0 w-5 text-center", "aria-hidden": "true", children: item.icon })), item.label] }) }, `${item.href}-${item.label}`))) }))] }));
|
|
39
45
|
}
|
|
40
|
-
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
46
|
+
export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarSections, sidebarPrimaryActions, sidebarTitle = "Navigation", hiddenNavItems, user, brandName, brandShortName, siteLogoUrl, logoHref, promotionsHref, cartHref, wishlistHref, userId, profileHref, loginHref, registerHref, homeHref, shopHref, footer, searchSlot, searchSlotRenderer, titleBarNavSlot, titleBarNotificationSlot, titleBarDevSlot, titleBarPromoStripText, showThemeToggle = false, suppressDashboardNav = false, hideSidebarToggle = false, onLogout, adminHref, storeHref, sellerHref, userOrdersHref, userWishlistHref, userSettingsHref, sidebarLocaleSlot, showThemeToggleInSidebar = false, sidebarProfileLabels, eventBannerSlot, lightBackground = DEFAULT_LIGHT_BG, darkBackground = DEFAULT_DARK_BG, }) {
|
|
41
47
|
const [queryClient] = useState(() => new QueryClient());
|
|
42
48
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
43
49
|
const [searchOpen, setSearchOpen] = useState(false);
|
|
@@ -148,7 +154,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
148
154
|
opacity: darkBackground.overlay?.opacity ?? 0,
|
|
149
155
|
},
|
|
150
156
|
};
|
|
151
|
-
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsxs(Div, { className: "flex min-h-screen w-full flex-col overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(MainNavbar, { navItems: navItems, hiddenNavItems: hiddenNavItems }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot)] }), eventBannerSlot, _jsx(AutoBreadcrumbs, {}), _jsxs(Div, { className: "relative flex w-full flex-1 overflow-x-clip", children: [_jsx(SidebarLayout, { isOpen: sidebarOpen, ariaLabel: "Secondary navigation", header: user ? (_jsxs(Div, { className: "flex items-center justify-between gap-3", children: [_jsxs(Div, { className: "flex items-center gap-3 flex-1 min-w-0", children: [_jsxs(Div, { className: "flex-shrink-0 relative", children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata
|
|
157
|
+
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsxs(Div, { className: "flex min-h-screen w-full flex-col overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, loginHref: loginHref, registerHref: registerHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(MainNavbar, { navItems: navItems, hiddenNavItems: hiddenNavItems }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot)] }), eventBannerSlot, _jsx(AutoBreadcrumbs, {}), _jsxs(Div, { className: "relative flex w-full flex-1 overflow-x-clip", children: [_jsx(SidebarLayout, { isOpen: sidebarOpen, ariaLabel: "Secondary navigation", header: user ? (_jsxs(Div, { className: "flex items-center justify-between gap-3", children: [_jsxs(Div, { className: "flex items-center gap-3 flex-1 min-w-0", children: [_jsxs(Div, { className: "flex-shrink-0 relative", children: [_jsx(AvatarDisplay, { cropData: user.avatarMetadata
|
|
152
158
|
? {
|
|
153
159
|
url: user.avatarMetadata.url,
|
|
154
160
|
position: user.avatarMetadata.position ?? {
|
|
@@ -163,5 +169,5 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
163
169
|
position: { x: 50, y: 50 },
|
|
164
170
|
zoom: 1,
|
|
165
171
|
}
|
|
166
|
-
: null, size: "md", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && (_jsx(Div, { className: "absolute -bottom-
|
|
172
|
+
: null, size: "md", alt: user.displayName || "User", displayName: user.displayName, email: user.email }), user.role && user.role !== "user" && (_jsx(Div, { className: "absolute -bottom-0.5 -right-0.5 flex items-center justify-center w-4 h-4 rounded-full border-2 border-white dark:border-slate-900 text-white text-[9px] font-bold leading-none select-none", style: { background: ROLE_DOT_COLORS[user.role] ?? "#6b7280" }, title: user.role.charAt(0).toUpperCase() + user.role.slice(1), "aria-label": user.role, children: user.role.charAt(0).toUpperCase() }))] }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100 truncate", children: user.displayName || "User" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400 truncate", children: user.email || "" })] })] }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: () => setSidebarOpen(false), className: "flex-shrink-0 rounded-full p-2 text-zinc-600 hover:bg-zinc-200 hover:text-zinc-900 dark:text-zinc-300 dark:hover:bg-slate-800 dark:hover:text-zinc-100 transition-all hover:rotate-90", 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" }) }) })] })) : (_jsxs(Div, { className: "flex items-center justify-between", children: [_jsx(Div, { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: sidebarTitle }), _jsx("button", { type: "button", "aria-label": "Close menu", onClick: () => setSidebarOpen(false), className: "rounded-full p-2 text-zinc-600 hover:bg-zinc-200 hover:text-zinc-900 dark:text-zinc-300 dark:hover:bg-slate-800 dark:hover:text-zinc-100 transition-all hover:rotate-90", 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" }) }) })] })), onClose: () => setSidebarOpen(false), children: sidebarContent }), _jsx(Main, { id: "main-content", className: `w-full flex-1 ${hasBottomActions ? "mb-28" : "mb-16"} md:mb-0`, children: _jsx(Div, { className: "container mx-auto w-full max-w-screen-2xl px-4 py-6 md:px-6 lg:px-8", children: children }) })] }), _jsx(BackToTop, {}), _jsx(FooterLayout, { ...footer }), _jsx(BottomActions, {}), _jsx(BottomNavbar, { user: user, homeHref: homeHref, shopHref: shopHref, cartHref: cartHref, profileHref: profileHref, loginHref: loginHref, onSearchToggle: () => setSearchOpen((prev) => !prev), navItems: navItems, onMoreToggle: hasDashboardNav ? toggleDashboardNav : handleTogglePublicSidebar }), _jsx(UnsavedChangesModal, {})] }) }));
|
|
167
173
|
}
|
|
@@ -25,6 +25,8 @@ export interface TitleBarLayoutProps {
|
|
|
25
25
|
cartHref?: string;
|
|
26
26
|
cartCount?: number;
|
|
27
27
|
profileHref?: string;
|
|
28
|
+
loginHref?: string;
|
|
29
|
+
registerHref?: string;
|
|
28
30
|
user?: TitleBarUser | null;
|
|
29
31
|
/** Slot rendered beside the profile link (e.g. NotificationBell). */
|
|
30
32
|
notificationSlot?: React.ReactNode;
|
|
@@ -54,4 +56,4 @@ export interface TitleBarLayoutProps {
|
|
|
54
56
|
*
|
|
55
57
|
* Receives all domain data as props — zero domain imports.
|
|
56
58
|
*/
|
|
57
|
-
export declare function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, searchOpen: _searchOpen, brandName, brandShortName: _brandShortName, siteLogoUrl, logoHref, promotionsHref, compareHref, wishlistHref, wishlistCount, cartHref, cartCount, profileHref, user, notificationSlot, devSlot, navSlot, promoStripText, isDark, onToggleTheme, hasDashboardNav: _hasDashboardNav, onToggleDashboardNav: _onToggleDashboardNav, hideSidebarToggle, id, className, }: TitleBarLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
59
|
+
export declare function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, searchOpen: _searchOpen, brandName, brandShortName: _brandShortName, siteLogoUrl, logoHref, promotionsHref, compareHref, wishlistHref, wishlistCount, cartHref, cartCount, profileHref, loginHref, registerHref, user, notificationSlot, devSlot, navSlot, promoStripText, isDark, onToggleTheme, hasDashboardNav: _hasDashboardNav, onToggleDashboardNav: _onToggleDashboardNav, hideSidebarToggle, id, className, }: TitleBarLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import Image from "next/image";
|
|
3
2
|
import Link from "next/link";
|
|
4
3
|
import { BlockHeader, Button, Div, Row, SiteLogo, Span } from "../../ui";
|
|
5
4
|
/** Shared icon-button class for all action buttons in the title bar. */
|
|
@@ -15,7 +14,7 @@ const countBadge = "absolute -top-0.5 -right-0.5 flex items-center justify-cente
|
|
|
15
14
|
*
|
|
16
15
|
* Receives all domain data as props — zero domain imports.
|
|
17
16
|
*/
|
|
18
|
-
export function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, searchOpen: _searchOpen, brandName, brandShortName: _brandShortName, siteLogoUrl, logoHref, promotionsHref, compareHref, wishlistHref, wishlistCount = 0, cartHref, cartCount = 0, profileHref, user, notificationSlot, devSlot, navSlot, promoStripText, isDark = false, onToggleTheme, hasDashboardNav: _hasDashboardNav, onToggleDashboardNav: _onToggleDashboardNav, hideSidebarToggle = false, id = "titlebar", className = "", }) {
|
|
17
|
+
export function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, searchOpen: _searchOpen, brandName, brandShortName: _brandShortName, siteLogoUrl, logoHref, promotionsHref, compareHref, wishlistHref, wishlistCount = 0, cartHref, cartCount = 0, profileHref, loginHref, registerHref, user, notificationSlot, devSlot, navSlot, promoStripText, isDark = false, onToggleTheme, hasDashboardNav: _hasDashboardNav, onToggleDashboardNav: _onToggleDashboardNav, hideSidebarToggle = false, id = "titlebar", className = "", }) {
|
|
19
18
|
// ── Element builders ────────────────────────────────────────────────────────
|
|
20
19
|
const promotionsEl = promotionsHref ? (_jsxs(Link, { href: promotionsHref, "aria-label": "Today's deals", className: "flex items-center gap-1 px-3 py-1 rounded-full text-xs font-bold bg-primary-100 text-primary-700 dark:bg-secondary-900/40 dark:text-secondary-400 hover:bg-primary-200 dark:hover:bg-secondary-900/60 transition-colors border border-primary-200/60 dark:border-secondary-700/40", children: [_jsx("svg", { className: "w-3 h-3", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { d: "M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.37.86.58 1.41.58.55 0 1.05-.21 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z" }) }), _jsx(Span, { className: "hidden sm:inline", children: "Today's Deals" })] })) : null;
|
|
21
20
|
const themeBtn = onToggleTheme ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": isDark ? "Switch to light mode" : "Switch to dark mode", onClick: onToggleTheme, className: iconBtn, children: isDark ? (_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m8.66-9h-1M4.34 12h-1m15.07-6.07-.71.71M6.34 17.66l-.71.71m12.73 0-.71-.71M6.34 6.34l-.71-.71M12 5a7 7 0 1 0 0 14A7 7 0 0 0 12 5z" }) })) : (_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 12.79A9 9 0 1 1 11.21 3a7 7 0 1 0 9.79 9.79z" }) })) })) : null;
|
|
@@ -25,8 +24,13 @@ export function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, s
|
|
|
25
24
|
const searchBtn = (_jsx(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": "Search", onClick: onSearchToggle, className: iconBtn, children: _jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-4.35-4.35M17 11A6 6 0 1 1 5 11a6 6 0 0 1 12 0z" }) }) }));
|
|
26
25
|
const wishlistEl = wishlistHref ? (_jsxs(Link, { href: wishlistHref, "aria-label": `Wishlist${wishlistCount > 0 ? `, ${wishlistCount} items` : ""}`, className: `relative ${iconBtn}`, children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4.318 6.318a4.5 4.5 0 0 0 0 6.364L12 20.364l7.682-7.682a4.5 4.5 0 0 0-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 0 0-6.364 0z" }) }), wishlistCount > 0 && (_jsx(Span, { className: countBadge, children: wishlistCount > 9 ? "9+" : wishlistCount }))] })) : null;
|
|
27
26
|
const cartEl = cartHref ? (_jsxs(Link, { href: cartHref, "aria-label": `Cart${cartCount > 0 ? `, ${cartCount} items` : ""}`, className: `relative ${iconBtn}`, children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0z" }) }), cartCount > 0 && (_jsx(Span, { className: countBadge, children: cartCount > 9 ? "9+" : cartCount }))] })) : null;
|
|
28
|
-
const profileEl = profileHref ? (_jsx(Link, { href: profileHref, "aria-label": user ? `Profile — ${user.displayName ?? user.email}` : "Sign in", className: iconBtn, children: user?.photoURL ? (
|
|
27
|
+
const profileEl = profileHref ? (_jsx(Link, { href: profileHref, "aria-label": user ? `Profile — ${user.displayName ?? user.email}` : "Sign in", className: iconBtn, children: user?.photoURL ? (
|
|
28
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
29
|
+
_jsx("img", { src: user.photoURL, alt: user.displayName ?? "Profile", width: 28, height: 28, className: "w-7 h-7 rounded-full object-cover" })) : (_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0zM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632z" }) })) })) : null;
|
|
30
|
+
const authButtonsEl = !user && (loginHref || registerHref) ? (_jsxs(Row, { gap: "xs", className: "hidden lg:flex items-center", children: [loginHref && (_jsx(Link, { href: loginHref, className: "px-3 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-300 hover:text-primary-700 dark:hover:text-secondary-400 transition-colors rounded-lg hover:bg-primary-50 dark:hover:bg-slate-800", children: "Sign in" })), registerHref && (_jsx(Link, { href: registerHref, className: "px-3 py-1.5 text-sm font-semibold rounded-lg bg-primary text-white hover:bg-primary/90 transition-colors btn-glow shadow-sm", children: "Register" }))] })) : null;
|
|
29
31
|
const hasTb2 = !!(wishlistEl || cartEl || profileEl);
|
|
30
32
|
// ── Render ───────────────────────────────────────────────────────────────────
|
|
31
|
-
return (_jsxs(BlockHeader, { id: id, className: `sticky top-0 z-50 bg-white/95 dark:bg-slate-950/95 backdrop-blur-md border-b border-zinc-100 dark:border-slate-800 shadow-sm ${className}`, children: [promoStripText && (_jsx(Div, { className: "bg-gradient-to-r from-primary-700 to-secondary-600 dark:from-primary-700 dark:to-cobalt-700 text-white text-xs py-1 text-center font-medium", children: promoStripText })), _jsxs(Div, { className: "container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1920px]", children: [_jsxs(Row, { justify: "between", gap: "none", className: "h-14", children: [_jsx(Row, { gap: "3", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteLogo, { title: brandName, src: siteLogoUrl, className: "h-7 md:h-9 lg:h-10" }) }) }), navSlot && _jsx(Div, { className: "hidden md:flex", children: navSlot }), _jsxs(Row, { gap: "xs", children: [devSlot, compareEl, notificationSlot, wishlistEl && _jsx(Div, { className: "hidden lg:flex", children: wishlistEl }), cartEl && _jsx(Div, { className: "hidden lg:flex", children: cartEl }),
|
|
33
|
+
return (_jsxs(BlockHeader, { id: id, className: `sticky top-0 z-50 bg-white/95 dark:bg-slate-950/95 backdrop-blur-md border-b border-zinc-100 dark:border-slate-800 shadow-sm ${className}`, children: [promoStripText && (_jsx(Div, { className: "bg-gradient-to-r from-primary-700 to-secondary-600 dark:from-primary-700 dark:to-cobalt-700 text-white text-xs py-1 text-center font-medium", children: promoStripText })), _jsxs(Div, { className: "container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1920px]", children: [_jsxs(Row, { justify: "between", gap: "none", className: "h-14", children: [_jsx(Row, { gap: "3", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteLogo, { title: brandName, src: siteLogoUrl, className: "h-7 md:h-9 lg:h-10" }) }) }), navSlot && _jsx(Div, { className: "hidden md:flex", children: navSlot }), _jsxs(Row, { gap: "xs", children: [devSlot, compareEl, notificationSlot, wishlistEl && _jsx(Div, { className: "hidden lg:flex", children: wishlistEl }), cartEl && _jsx(Div, { className: "hidden lg:flex", children: cartEl }), user
|
|
34
|
+
? profileEl && _jsx(Div, { className: "hidden lg:flex", children: profileEl })
|
|
35
|
+
: authButtonsEl ?? (profileEl && _jsx(Div, { className: "hidden lg:flex", children: profileEl })), searchBtn, promotionsEl, themeBtn, hamburgerBtn] })] }), hasTb2 && (_jsxs(Row, { as: "nav", "aria-label": "Account actions", justify: "end", gap: "xs", className: "flex lg:hidden h-10 border-t border-zinc-100 dark:border-slate-800 px-1", children: [wishlistEl, cartEl, profileEl] }))] })] }));
|
|
32
36
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1219,6 +1219,10 @@ export type { AdminTeamViewProps } from "./features/admin/index";
|
|
|
1219
1219
|
export type { AdminEmployeeEditorViewProps } from "./features/admin/index";
|
|
1220
1220
|
export type { AdminSupportTicketsViewProps } from "./features/admin/index";
|
|
1221
1221
|
export type { AdminSupportTicketDetailViewProps } from "./features/admin/index";
|
|
1222
|
+
export { AdminScammersView } from "./features/admin/index";
|
|
1223
|
+
export { AdminScammerEditorView } from "./features/admin/index";
|
|
1224
|
+
export type { AdminScammersViewProps } from "./features/admin/index";
|
|
1225
|
+
export type { AdminScammerEditorViewProps } from "./features/admin/index";
|
|
1222
1226
|
export type { DrawerFormFooterProps } from "./features/admin/index";
|
|
1223
1227
|
export type { FeatureFlagKey } from "./features/admin/index";
|
|
1224
1228
|
export type { FeatureFlagMeta } from "./features/admin/index";
|
package/dist/index.js
CHANGED
|
@@ -2311,6 +2311,10 @@ export { toStringValue } from "./features/admin/index";
|
|
|
2311
2311
|
// [CLIENT-SSR]-Utility function for converting date values to relative date string.
|
|
2312
2312
|
// toRelativeDate - Helper for converting date to relative date string.
|
|
2313
2313
|
export { toRelativeDate } from "./features/admin/index";
|
|
2314
|
+
// AdminScammersView - Admin scammer profile list view.
|
|
2315
|
+
export { AdminScammersView } from "./features/admin/index";
|
|
2316
|
+
// AdminScammerEditorView - Scammer profile review/status SideDrawer.
|
|
2317
|
+
export { AdminScammerEditorView } from "./features/admin/index";
|
|
2314
2318
|
// ./features/admin/server
|
|
2315
2319
|
// [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
|
|
2316
2320
|
// adminBidsGET - Shared export for admin bids get.
|
package/dist/jobs.d.ts
CHANGED
|
@@ -20,5 +20,5 @@
|
|
|
20
20
|
* listingProcessorHandler,
|
|
21
21
|
* } from "@mohasinac/appkit/jobs";
|
|
22
22
|
*/
|
|
23
|
-
export { couponExpiryHandler, offerExpiryHandler, cartPruneHandler, notificationPruneHandler, dailyDataCleanupHandler, cleanupRtdbEventsHandler, auctionSettlementHandler, autoPayoutEligibilityHandler, countersReconcileHandler, onOrderCreateHandler, onOrderStatusChangeHandler, onBidPlacedHandler, onReviewWriteHandler, promotionsHandler, mediaTmpCleanupHandler, pendingOrderTimeoutHandler, productStatsSyncHandler, positionsReconcileHandler, payoutBatchHandler, weeklyPayoutEligibilityHandler, onCategoryWriteHandler, onProductWriteHandler, onStoreWriteHandler, adminAnalyticsHandler, storeAnalyticsHandler, listingProcessorHandler, supportedListingCollections, prizeRevealOpenHandler, prizeRevealCloseHandler, prizeRevealExpiryHandler, prizeRevealReminderHandler, bundleStockSyncHandler, onProductStockChangeHandler, triggerEventRaffleHandler, assignSpinPrizeHandler, bindSchedule, bindDocumentWritten, bindDocumentCreated, bindDocumentUpdated, bindCallable, bindHttps, bindToFirebase, } from "./_internal/server/jobs/index.js";
|
|
23
|
+
export { couponExpiryHandler, offerExpiryHandler, cartPruneHandler, notificationPruneHandler, dailyDataCleanupHandler, cleanupRtdbEventsHandler, auctionSettlementHandler, autoPayoutEligibilityHandler, countersReconcileHandler, onOrderCreateHandler, onOrderStatusChangeHandler, onBidPlacedHandler, onReviewWriteHandler, promotionsHandler, mediaTmpCleanupHandler, pendingOrderTimeoutHandler, productStatsSyncHandler, positionsReconcileHandler, payoutBatchHandler, weeklyPayoutEligibilityHandler, onCategoryWriteHandler, onProductWriteHandler, onStoreWriteHandler, adminAnalyticsHandler, storeAnalyticsHandler, listingProcessorHandler, supportedListingCollections, prizeRevealOpenHandler, prizeRevealCloseHandler, prizeRevealExpiryHandler, prizeRevealReminderHandler, bundleStockSyncHandler, onProductStockChangeHandler, triggerEventRaffleHandler, assignSpinPrizeHandler, onSupportTicketCreateHandler, onSupportTicketUpdateHandler, onUserBanChangeHandler, bindSchedule, bindDocumentWritten, bindDocumentCreated, bindDocumentUpdated, bindCallable, bindHttps, bindToFirebase, } from "./_internal/server/jobs/index.js";
|
|
24
24
|
export type { PromotionsCallableResult, AdminAnalyticsResult, StoreAnalyticsInput, StoreAnalyticsResult, ListingRequestBody, ListingResponseBody, JobContext, JobLogger, JobHandlers, ScheduleHandler, FirestoreTriggerHandler, FirestoreTriggerEvent, CallableHandler, BindHttpsOptions, } from "./_internal/server/jobs/index.js";
|
package/dist/jobs.js
CHANGED
|
@@ -27,5 +27,7 @@ couponExpiryHandler, offerExpiryHandler, cartPruneHandler, notificationPruneHand
|
|
|
27
27
|
prizeRevealOpenHandler, prizeRevealCloseHandler, prizeRevealExpiryHandler, prizeRevealReminderHandler, bundleStockSyncHandler,
|
|
28
28
|
// SB-UNI-V — Firestore onWrite trigger for product stock changes.
|
|
29
29
|
onProductStockChangeHandler, triggerEventRaffleHandler, assignSpinPrizeHandler,
|
|
30
|
+
// BAN9 — support ticket lifecycle + ban audit trail
|
|
31
|
+
onSupportTicketCreateHandler, onSupportTicketUpdateHandler, onUserBanChangeHandler,
|
|
30
32
|
// Firebase binder adapter
|
|
31
33
|
bindSchedule, bindDocumentWritten, bindDocumentCreated, bindDocumentUpdated, bindCallable, bindHttps, bindToFirebase, } from "./_internal/server/jobs/index.js";
|
|
@@ -81,6 +81,7 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
81
81
|
readonly SCAM_DETAIL: (id: string) => string;
|
|
82
82
|
readonly SCAM_REPORT: "/scams/report";
|
|
83
83
|
readonly SCAM_TYPES: "/scams/types";
|
|
84
|
+
readonly SCAM_FAQS: "/scams/faqs";
|
|
84
85
|
};
|
|
85
86
|
readonly ERRORS: {
|
|
86
87
|
readonly UNAUTHORIZED: "/unauthorized";
|
|
@@ -117,6 +118,8 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
117
118
|
readonly REVIEWS: "/user/reviews";
|
|
118
119
|
readonly BIDS: "/user/bids";
|
|
119
120
|
readonly RETURNS: "/user/returns";
|
|
121
|
+
readonly SUPPORT: "/user/support";
|
|
122
|
+
readonly SUPPORT_TICKET: (id: string) => string;
|
|
120
123
|
};
|
|
121
124
|
readonly STORE: {
|
|
122
125
|
readonly DASHBOARD: "/store";
|
|
@@ -231,6 +234,10 @@ export declare const DEFAULT_ROUTE_MAP: {
|
|
|
231
234
|
readonly PRIZE_DRAWS: "/admin/prize-draws";
|
|
232
235
|
readonly PRIZE_DRAWS_EDIT: (id: string) => string;
|
|
233
236
|
readonly TEAM: "/admin/team";
|
|
237
|
+
readonly SUPPORT_TICKETS: "/admin/support-tickets";
|
|
238
|
+
readonly SUPPORT_TICKET_BY_ID: (id: string) => string;
|
|
239
|
+
readonly SCAMMERS: "/admin/scammers";
|
|
240
|
+
readonly SCAMMER_BY_ID: (id: string) => string;
|
|
234
241
|
};
|
|
235
242
|
readonly DEMO: {
|
|
236
243
|
readonly SEED: "/demo/seed";
|
|
@@ -311,6 +318,7 @@ export declare const ROUTES: {
|
|
|
311
318
|
readonly SCAM_DETAIL: (id: string) => string;
|
|
312
319
|
readonly SCAM_REPORT: "/scams/report";
|
|
313
320
|
readonly SCAM_TYPES: "/scams/types";
|
|
321
|
+
readonly SCAM_FAQS: "/scams/faqs";
|
|
314
322
|
};
|
|
315
323
|
readonly ERRORS: {
|
|
316
324
|
readonly UNAUTHORIZED: "/unauthorized";
|
|
@@ -347,6 +355,8 @@ export declare const ROUTES: {
|
|
|
347
355
|
readonly REVIEWS: "/user/reviews";
|
|
348
356
|
readonly BIDS: "/user/bids";
|
|
349
357
|
readonly RETURNS: "/user/returns";
|
|
358
|
+
readonly SUPPORT: "/user/support";
|
|
359
|
+
readonly SUPPORT_TICKET: (id: string) => string;
|
|
350
360
|
};
|
|
351
361
|
readonly STORE: {
|
|
352
362
|
readonly DASHBOARD: "/store";
|
|
@@ -461,6 +471,10 @@ export declare const ROUTES: {
|
|
|
461
471
|
readonly PRIZE_DRAWS: "/admin/prize-draws";
|
|
462
472
|
readonly PRIZE_DRAWS_EDIT: (id: string) => string;
|
|
463
473
|
readonly TEAM: "/admin/team";
|
|
474
|
+
readonly SUPPORT_TICKETS: "/admin/support-tickets";
|
|
475
|
+
readonly SUPPORT_TICKET_BY_ID: (id: string) => string;
|
|
476
|
+
readonly SCAMMERS: "/admin/scammers";
|
|
477
|
+
readonly SCAMMER_BY_ID: (id: string) => string;
|
|
464
478
|
};
|
|
465
479
|
readonly DEMO: {
|
|
466
480
|
readonly SEED: "/demo/seed";
|
|
@@ -69,6 +69,7 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
69
69
|
SCAM_DETAIL: (id) => `/scams/${id}`,
|
|
70
70
|
SCAM_REPORT: "/scams/report",
|
|
71
71
|
SCAM_TYPES: "/scams/types",
|
|
72
|
+
SCAM_FAQS: "/scams/faqs",
|
|
72
73
|
},
|
|
73
74
|
ERRORS: {
|
|
74
75
|
UNAUTHORIZED: "/unauthorized",
|
|
@@ -105,6 +106,8 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
105
106
|
REVIEWS: "/user/reviews",
|
|
106
107
|
BIDS: "/user/bids",
|
|
107
108
|
RETURNS: "/user/returns",
|
|
109
|
+
SUPPORT: "/user/support",
|
|
110
|
+
SUPPORT_TICKET: (id) => `/user/support/${id}`,
|
|
108
111
|
},
|
|
109
112
|
STORE: {
|
|
110
113
|
DASHBOARD: "/store",
|
|
@@ -222,6 +225,10 @@ export const DEFAULT_ROUTE_MAP = {
|
|
|
222
225
|
PRIZE_DRAWS: "/admin/prize-draws",
|
|
223
226
|
PRIZE_DRAWS_EDIT: (id) => `/admin/prize-draws/${id}/edit`,
|
|
224
227
|
TEAM: "/admin/team",
|
|
228
|
+
SUPPORT_TICKETS: "/admin/support-tickets",
|
|
229
|
+
SUPPORT_TICKET_BY_ID: (id) => `/admin/support-tickets/${id}`,
|
|
230
|
+
SCAMMERS: "/admin/scammers",
|
|
231
|
+
SCAMMER_BY_ID: (id) => `/admin/scammers/${id}`,
|
|
225
232
|
},
|
|
226
233
|
DEMO: {
|
|
227
234
|
SEED: "/demo/seed",
|