@mohasinac/appkit 3.2.5 → 3.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/SidebarCollapseToggle.js +1 -1
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +3 -3
- package/dist/_internal/server/features/products/data.d.ts +2 -1
- package/dist/_internal/server/jobs/core/countersReconcile.js +2 -1
- package/dist/_internal/shared/features/products/types.d.ts +3 -2
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/field-names.d.ts +2 -0
- package/dist/constants/field-names.js +2 -0
- package/dist/features/account/components/UserOffersPanel.js +0 -1
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -4
- package/dist/features/admin/components/AdminFulfillmentView.js +2 -3
- package/dist/features/admin/components/AdminOrderEditorView.js +1 -3
- package/dist/features/admin/components/AdminSiteSettingsView.js +2 -2
- package/dist/features/admin/schemas/firestore.d.ts +3 -5
- package/dist/features/admin/types/product.types.d.ts +2 -1
- package/dist/features/auth/schemas/firestore.d.ts +8 -10
- package/dist/features/categories/components/CategoryBundlesListing.js +1 -3
- package/dist/features/checkout/schemas/firestore.d.ts +4 -4
- package/dist/features/copilot/components/AdminCopilotView.js +4 -4
- package/dist/features/events/schemas/firestore.d.ts +2 -2
- package/dist/features/grouped/schemas/firestore.d.ts +2 -4
- package/dist/features/history/hooks/useHistory.js +3 -2
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +6 -6
- package/dist/features/homepage/actions/homepage-section-actions.js +2 -2
- package/dist/features/homepage/components/PromoGrid.js +1 -1
- package/dist/features/homepage/schemas/firestore.d.ts +15 -14
- package/dist/features/layout/AppLayoutShell.js +3 -18
- package/dist/features/layout/background-seed-defaults.d.ts +19 -0
- package/dist/features/layout/background-seed-defaults.js +14 -0
- package/dist/features/lottery/schemas/firestore.d.ts +2 -2
- package/dist/features/messages/hooks/useConversation.js +5 -13
- package/dist/features/messages/hooks/useConversations.js +0 -1
- package/dist/features/messages/schemas/firestore.d.ts +2 -2
- package/dist/features/orders/components/RefundHistoryTable.js +1 -3
- package/dist/features/orders/schemas/firestore.d.ts +2 -1
- package/dist/features/orders/schemas/index.d.ts +7 -3
- package/dist/features/orders/schemas/index.js +1 -1
- package/dist/features/products/actions/product-actions.d.ts +2 -1
- package/dist/features/products/api/[id]/route.js +1 -1
- package/dist/features/products/api/route.js +1 -1
- package/dist/features/products/components/CompareOverlay.d.ts +18 -1
- package/dist/features/products/components/CompareOverlay.js +0 -16
- package/dist/features/products/components/ProductDetailActions.js +2 -1
- package/dist/features/products/components/ShowGroupSection.js +1 -3
- package/dist/features/products/repository/product-features.repository.js +1 -1
- package/dist/features/products/schemas/firestore.d.ts +3 -6
- package/dist/features/promotions/actions/coupon-actions.d.ts +2 -1
- package/dist/features/promotions/hooks/useCouponValidate.d.ts +2 -1
- package/dist/features/promotions/repository/coupons.repository.d.ts +2 -1
- package/dist/features/promotions/schemas/firestore.d.ts +4 -4
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/scams/schemas/firestore.d.ts +6 -6
- package/dist/features/search/types/index.d.ts +3 -2
- package/dist/features/search/types/index.js +0 -1
- package/dist/features/seller/components/FulfillmentView.js +2 -3
- package/dist/features/seller/components/PrintCenterView.js +1 -3
- package/dist/features/seller/components/SellerAddressesView.js +0 -1
- package/dist/features/seller/components/SellerOffersPanel.js +0 -1
- package/dist/features/seller/components/SellerReviewsView.js +0 -1
- package/dist/features/seller/schemas/firestore.d.ts +2 -4
- package/dist/features/server-errors/schemas/firestore.d.ts +2 -2
- package/dist/features/store-extensions/schemas/firestore.d.ts +2 -2
- package/dist/features/stores/components/StoreScopedSearch.js +2 -4
- package/dist/features/stores/repository/store.repository.js +3 -1
- package/dist/features/stores/types/index.d.ts +2 -1
- package/dist/features/stores/types/index.js +0 -1
- package/dist/features/support/schemas/firestore.d.ts +2 -2
- package/dist/features/wishlist/types/index.d.ts +2 -1
- package/dist/providers/auth-firebase/provider.js +3 -1
- package/dist/providers/auth-firebase/session.js +3 -1
- package/dist/seo/json-ld.d.ts +2 -1
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Form.d.ts +3 -1
- package/dist/ui/components/Form.js +2 -1
- package/dist/ui/components/Radio.d.ts +7 -0
- package/dist/ui/components/Radio.js +7 -0
- package/dist/ui/components/surface-tokens.d.ts +4 -0
- package/dist/ui/components/surface-tokens.js +24 -0
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +1 -1
- package/dist/ui/rich-text/RichText.js +0 -1
- package/package.json +5 -1
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* active members drops below `minActiveMembers`, `visibilityStatus` flips
|
|
12
12
|
* to "hidden" (recomputed by `onProductStockChange`).
|
|
13
13
|
*/
|
|
14
|
+
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
14
15
|
export type GroupTheme =
|
|
15
16
|
/** "Other anime figures you might like" */
|
|
16
17
|
"related"
|
|
@@ -23,8 +24,7 @@ export type GroupTheme =
|
|
|
23
24
|
/** Default / unspecified theme */
|
|
24
25
|
| "generic";
|
|
25
26
|
export type GroupVisibility = "visible" | "hidden";
|
|
26
|
-
export interface GroupedListingDocument {
|
|
27
|
-
id: string;
|
|
27
|
+
export interface GroupedListingDocument extends BaseDocument {
|
|
28
28
|
slug: string;
|
|
29
29
|
title: string;
|
|
30
30
|
description?: string;
|
|
@@ -44,8 +44,6 @@ export interface GroupedListingDocument {
|
|
|
44
44
|
brandSlug?: string;
|
|
45
45
|
categorySlug?: string;
|
|
46
46
|
createdBy: string;
|
|
47
|
-
createdAt: Date;
|
|
48
|
-
updatedAt: Date;
|
|
49
47
|
}
|
|
50
48
|
export declare const GROUPED_LISTINGS_COLLECTION: "groupedListings";
|
|
51
49
|
export declare const GROUPED_LISTINGS_INDEXED_FIELDS: readonly ["storeId", "brandSlug", "categorySlug", "isActive", "isFeatured", "groupTheme", "visibilityStatus", "createdAt"];
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
13
13
|
import { useToast } from "../../../ui";
|
|
14
14
|
import { ACCOUNT_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
15
|
+
import { normalizeError } from "../../../errors/normalize";
|
|
15
16
|
import { clearGuestHistory, getGuestHistory, removeGuestHistoryItem, trackGuestHistory, } from "../utils/guest-history";
|
|
16
17
|
const TRACK_DEBOUNCE_MS = 1500;
|
|
17
18
|
const sessionTracked = new Set();
|
|
@@ -77,8 +78,8 @@ export function useHistory(userId) {
|
|
|
77
78
|
setItems(getGuestHistory());
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
|
-
catch {
|
|
81
|
-
|
|
81
|
+
catch (err) {
|
|
82
|
+
void normalizeError(err);
|
|
82
83
|
}
|
|
83
84
|
}, [isAuth]);
|
|
84
85
|
useEffect(() => {
|
|
@@ -5,30 +5,30 @@ export declare const createSectionSchema: z.ZodObject<{
|
|
|
5
5
|
type: z.ZodString;
|
|
6
6
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
7
7
|
order: z.ZodDefault<z.ZodNumber>;
|
|
8
|
-
config: z.ZodOptional<z.
|
|
8
|
+
config: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
|
|
9
9
|
}, "strip", z.ZodTypeAny, {
|
|
10
10
|
type: string;
|
|
11
11
|
order: number;
|
|
12
12
|
enabled: boolean;
|
|
13
|
-
config?:
|
|
13
|
+
config?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
14
14
|
}, {
|
|
15
15
|
type: string;
|
|
16
16
|
order?: number | undefined;
|
|
17
17
|
enabled?: boolean | undefined;
|
|
18
|
-
config?:
|
|
18
|
+
config?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
19
19
|
}>;
|
|
20
20
|
export declare const updateSectionSchema: z.ZodObject<{
|
|
21
21
|
order: z.ZodOptional<z.ZodNumber>;
|
|
22
22
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
23
|
-
config: z.ZodOptional<z.
|
|
23
|
+
config: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
|
|
24
24
|
}, "strip", z.ZodTypeAny, {
|
|
25
25
|
order?: number | undefined;
|
|
26
26
|
enabled?: boolean | undefined;
|
|
27
|
-
config?:
|
|
27
|
+
config?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
28
28
|
}, {
|
|
29
29
|
order?: number | undefined;
|
|
30
30
|
enabled?: boolean | undefined;
|
|
31
|
-
config?:
|
|
31
|
+
config?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
32
32
|
}>;
|
|
33
33
|
export type CreateHomepageSectionInput = z.infer<typeof createSectionSchema>;
|
|
34
34
|
export type UpdateHomepageSectionInput = z.infer<typeof updateSectionSchema>;
|
|
@@ -6,12 +6,12 @@ export const createSectionSchema = z.object({
|
|
|
6
6
|
type: z.string().min(1),
|
|
7
7
|
enabled: z.boolean().default(true),
|
|
8
8
|
order: z.number().int().default(0),
|
|
9
|
-
config: z.
|
|
9
|
+
config: z.object({}).passthrough().optional(),
|
|
10
10
|
});
|
|
11
11
|
export const updateSectionSchema = z.object({
|
|
12
12
|
order: z.number().int().optional(),
|
|
13
13
|
enabled: z.boolean().optional(),
|
|
14
|
-
config: z.
|
|
14
|
+
config: z.object({}).passthrough().optional(),
|
|
15
15
|
});
|
|
16
16
|
// --- Actions --------------------------------------------------------------
|
|
17
17
|
export async function createHomepageSection(input, createdBy) {
|
|
@@ -10,5 +10,5 @@ export function PromoGrid({ banners, eyebrow = "LIMITED TIME", heading = "HOT DE
|
|
|
10
10
|
return null;
|
|
11
11
|
return (_jsx(Section, { paddingY: "y-4xl", className: "bg-[var(--dark-section-alt)] [border-top:1px_solid_rgba(255,255,255,0.06)] [border-bottom:1px_solid_rgba(255,255,255,0.06)]", children: _jsxs(Div, { className: "mx-auto max-w-7xl", padding: "x-md", children: [_jsx(Row, { wrap: true, align: "end", justify: "between", gap: "sm", className: "mb-7 gap-y-2", children: _jsxs(Div, { children: [_jsx(Text, { className: "mb-1 font-black uppercase tracking-widest text-[var(--color-red)] tracking-[0.18em]", size: "xs", children: eyebrow }), _jsx(Heading, { level: 2, className: "[font-family:var(--font-bangers,Bangers,cursive)] text-[clamp(1.6rem,4vw,2.4rem)] tracking-[0.08em] text-white leading-none", children: heading })] }) }), _jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: banners.slice(0, 4).map((banner, i) => (_jsxs(Link, { href: banner.ctaUrl, className: `group relative block overflow-hidden bg-[var(--dark-section-card)] ${i === 0 ? "sm:row-span-2 min-h-[clamp(220px,32vh,480px)]" : "min-h-[clamp(130px,16vh,240px)]"}`, children: [_jsx(Image, { src: banner.image, alt: banner.title, fill: true, className: "object-cover transition-transform duration-500 group-hover:scale-105", sizes: i === 0
|
|
12
12
|
? "(max-width: 640px) 100vw, 40vw"
|
|
13
|
-
: "(max-width: 640px) 50vw, 30vw" }), _jsx(Div, { className: `absolute inset-0 ${i === 0 ? "[background:linear-gradient(to_top,rgba(0,0,0,0.9)_0%,rgba(0,0,0,0.3)_50%,transparent_100%)]" : "[background:linear-gradient(to_top,rgba(0,0,0,0.85)_0%,rgba(0,0,0,0.15)_100%)]"}` }), _jsx(Div, { className: "absolute inset-0 opacity-0 transition-opacity duration-300 group-hover:opacity-100
|
|
13
|
+
: "(max-width: 640px) 50vw, 30vw" }), _jsx(Div, { className: `absolute inset-0 ${i === 0 ? "[background:linear-gradient(to_top,rgba(0,0,0,0.9)_0%,rgba(0,0,0,0.3)_50%,transparent_100%)]" : "[background:linear-gradient(to_top,rgba(0,0,0,0.85)_0%,rgba(0,0,0,0.15)_100%)]"}` }), _jsx(Div, { className: "absolute inset-0 opacity-0 transition-opacity duration-300 group-hover:opacity-100 ring-2 ring-[var(--color-yellow)] ring-inset" }), _jsxs(Div, { className: `absolute bottom-0 left-0 right-0 ${__P.p4}`, children: [_jsx(Text, { className: `leading-tight [font-family:var(--font-bangers,Bangers,cursive)] tracking-[0.06em] text-white ${i === 0 ? "text-[1.35rem]" : "text-[1rem]"}`, children: banner.title }), _jsxs(Span, { layout: "inline-flex", gap: "xs", className: "mt-1 font-black uppercase text-[var(--color-yellow)]", size: "xs", children: [banner.ctaLabel, " \u2192"] })] })] }, banner.id))) })] }) }));
|
|
14
14
|
}
|
|
@@ -29,7 +29,7 @@ export interface CarouselBackground {
|
|
|
29
29
|
export type CarouselSlideHeight = "viewport" | "tall" | "medium";
|
|
30
30
|
export type CarouselHoverEffect = "scale" | "color" | "glow" | "none";
|
|
31
31
|
/** A content card placed in one of 6 grid zones (2 rows × 3 cols). */
|
|
32
|
-
export
|
|
32
|
+
export type CarouselCard = {
|
|
33
33
|
id: string;
|
|
34
34
|
/** Zone 1–6: row 1 = zones 1–3, row 2 = zones 4–6 */
|
|
35
35
|
zone: 1 | 2 | 3 | 4 | 5 | 6;
|
|
@@ -61,7 +61,7 @@ export interface CarouselCard {
|
|
|
61
61
|
gridRow?: 1 | 2;
|
|
62
62
|
/** Legacy — kept for backward compat; use `zone` instead */
|
|
63
63
|
gridCol?: 1 | 2 | 3;
|
|
64
|
-
}
|
|
64
|
+
};
|
|
65
65
|
export type GridCardCreateInput = Omit<CarouselCard, "id">;
|
|
66
66
|
/** A named carousel that holds up to MAX_SLIDES_PER_CAROUSEL slide references. */
|
|
67
67
|
export interface CarouselDocument extends BaseDocument {
|
|
@@ -174,14 +174,15 @@ export interface WelcomeSectionConfig {
|
|
|
174
174
|
ctaText?: string;
|
|
175
175
|
ctaLink?: string;
|
|
176
176
|
}
|
|
177
|
+
export type TrustIndicator = {
|
|
178
|
+
id: string;
|
|
179
|
+
icon: string;
|
|
180
|
+
title: string;
|
|
181
|
+
description: string;
|
|
182
|
+
};
|
|
177
183
|
export interface TrustIndicatorsSectionConfig {
|
|
178
184
|
title: string;
|
|
179
|
-
indicators:
|
|
180
|
-
id: string;
|
|
181
|
-
icon: string;
|
|
182
|
-
title: string;
|
|
183
|
-
description: string;
|
|
184
|
-
}>;
|
|
185
|
+
indicators: TrustIndicator[];
|
|
185
186
|
}
|
|
186
187
|
export interface SectionCTA {
|
|
187
188
|
label: string;
|
|
@@ -381,7 +382,7 @@ export type SocialFeedLayout = "grid" | "masonry" | "carousel";
|
|
|
381
382
|
* videoId is stored; thumbnail URL is computed at render time from the video ID.
|
|
382
383
|
* Do NOT store raw YouTube CDN thumbnail URLs in Firestore (they may expire).
|
|
383
384
|
*/
|
|
384
|
-
export
|
|
385
|
+
export type StaticSocialPost = {
|
|
385
386
|
id: string;
|
|
386
387
|
platform: SocialPlatform;
|
|
387
388
|
/** YouTube video ID (e.g. "dQw4w9WgXcQ"). Required when platform is "youtube". */
|
|
@@ -389,7 +390,7 @@ export interface StaticSocialPost {
|
|
|
389
390
|
/** YouTube channel display name shown as a chip. */
|
|
390
391
|
channelName?: string;
|
|
391
392
|
caption?: string;
|
|
392
|
-
}
|
|
393
|
+
};
|
|
393
394
|
export interface SocialFeedSectionConfig {
|
|
394
395
|
title: string;
|
|
395
396
|
subtitle?: string;
|
|
@@ -409,7 +410,7 @@ export interface SocialFeedSectionConfig {
|
|
|
409
410
|
posts?: StaticSocialPost[];
|
|
410
411
|
}
|
|
411
412
|
/** Normalised social post returned by /api/social-feed */
|
|
412
|
-
export
|
|
413
|
+
export type SocialPost = {
|
|
413
414
|
id: string;
|
|
414
415
|
platform: SocialPlatform;
|
|
415
416
|
imageUrl?: string;
|
|
@@ -427,7 +428,7 @@ export interface SocialPost {
|
|
|
427
428
|
comments?: number;
|
|
428
429
|
};
|
|
429
430
|
publishedAt?: string;
|
|
430
|
-
}
|
|
431
|
+
};
|
|
431
432
|
export interface PreOrdersSectionConfig {
|
|
432
433
|
title: string;
|
|
433
434
|
subtitle?: string;
|
|
@@ -461,7 +462,7 @@ export interface BannerSectionConfig {
|
|
|
461
462
|
clickable: boolean;
|
|
462
463
|
clickLink?: string;
|
|
463
464
|
}
|
|
464
|
-
export
|
|
465
|
+
export type CustomCardsCard = {
|
|
465
466
|
id: string;
|
|
466
467
|
image?: string;
|
|
467
468
|
imageAlt?: string;
|
|
@@ -479,7 +480,7 @@ export interface CustomCardsCard {
|
|
|
479
480
|
textColor?: string;
|
|
480
481
|
borderRadius?: "none" | "sm" | "md" | "lg" | "xl" | "full";
|
|
481
482
|
shadowLevel?: "none" | "sm" | "md" | "lg";
|
|
482
|
-
}
|
|
483
|
+
};
|
|
483
484
|
export interface CustomCardsSectionConfig {
|
|
484
485
|
title?: string;
|
|
485
486
|
layout: "grid" | "row" | "masonry";
|
|
@@ -27,23 +27,10 @@ import { TitleBar } from "./TitleBar";
|
|
|
27
27
|
import { BackToTop } from "./BackToTop";
|
|
28
28
|
import { useDashboardNav } from "./DashboardNavContext";
|
|
29
29
|
import { usePathname } from "next/navigation";
|
|
30
|
-
|
|
31
|
-
// written into siteSettings on first deploy and may be overridden at runtime.
|
|
32
|
-
// audit-hex-tokens-ok: seed defaults for siteSettings.theme.background
|
|
33
|
-
const DEFAULT_LIGHT_BG = {
|
|
34
|
-
type: "color",
|
|
35
|
-
value: "#f9fafb", // audit-hex-tokens-ok: zinc-50 seed default
|
|
36
|
-
overlay: { enabled: false, color: "#000000", opacity: 0 }, // audit-hex-tokens-ok: overlay seed default
|
|
37
|
-
};
|
|
30
|
+
import { OVERLAY_FALLBACK_COLOR, SEED_DARK_BG as DEFAULT_DARK_BG, SEED_LIGHT_BG as DEFAULT_LIGHT_BG } from "./background-seed-defaults";
|
|
38
31
|
const CLS_STAT_BOX = "flex flex-col items-center gap-1 p-2 bg-zinc-100 dark:bg-zinc-800 rounded-lg text-center";
|
|
39
32
|
const CLS_STAT_LABEL = "text-xs text-zinc-500 dark:text-zinc-400";
|
|
40
33
|
const CLS_LOGOUT_BTN = "flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm text-error transition-colors hover:bg-error-surface hover:text-error dark:text-error dark:hover:bg-error-surface dark:hover:text-error";
|
|
41
|
-
// audit-hex-tokens-ok: dark theme background seed default
|
|
42
|
-
const DEFAULT_DARK_BG = {
|
|
43
|
-
type: "color",
|
|
44
|
-
value: "#030712", // audit-hex-tokens-ok: gray-950 seed default
|
|
45
|
-
overlay: { enabled: false, color: "#000000", opacity: 0 }, // audit-hex-tokens-ok: overlay seed default
|
|
46
|
-
};
|
|
47
34
|
/** Collapsible accordion section for the public sidebar. */
|
|
48
35
|
function CollapsibleNavGroup({ title, children, }) {
|
|
49
36
|
const [open, setOpen] = useState(false);
|
|
@@ -152,8 +139,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
152
139
|
value: lightBackground.value,
|
|
153
140
|
overlay: {
|
|
154
141
|
enabled: lightBackground.overlay?.enabled ?? false,
|
|
155
|
-
|
|
156
|
-
color: lightBackground.overlay?.color ?? "#000000",
|
|
142
|
+
color: lightBackground.overlay?.color ?? OVERLAY_FALLBACK_COLOR,
|
|
157
143
|
opacity: lightBackground.overlay?.opacity ?? 0,
|
|
158
144
|
},
|
|
159
145
|
};
|
|
@@ -162,8 +148,7 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
|
|
|
162
148
|
value: darkBackground.value,
|
|
163
149
|
overlay: {
|
|
164
150
|
enabled: darkBackground.overlay?.enabled ?? false,
|
|
165
|
-
|
|
166
|
-
color: darkBackground.overlay?.color ?? "#000000",
|
|
151
|
+
color: darkBackground.overlay?.color ?? OVERLAY_FALLBACK_COLOR,
|
|
167
152
|
opacity: darkBackground.overlay?.opacity ?? 0,
|
|
168
153
|
},
|
|
169
154
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const SEED_LIGHT_BG: {
|
|
2
|
+
type: "color";
|
|
3
|
+
value: string;
|
|
4
|
+
overlay: {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
color: string;
|
|
7
|
+
opacity: number;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export declare const SEED_DARK_BG: {
|
|
11
|
+
type: "color";
|
|
12
|
+
value: string;
|
|
13
|
+
overlay: {
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
color: string;
|
|
16
|
+
opacity: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export declare const OVERLAY_FALLBACK_COLOR = "#000000";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Seed defaults for siteSettings.theme.background — Firestore data values, not CSS styling.
|
|
2
|
+
// These are the first-boot theme configuration and are exempt from the hex-tokens audit.
|
|
3
|
+
// Override via the admin theme editor (Site Settings → Themes).
|
|
4
|
+
export const SEED_LIGHT_BG = {
|
|
5
|
+
type: "color",
|
|
6
|
+
value: "#f9fafb",
|
|
7
|
+
overlay: { enabled: false, color: "#000000", opacity: 0 },
|
|
8
|
+
};
|
|
9
|
+
export const SEED_DARK_BG = {
|
|
10
|
+
type: "color",
|
|
11
|
+
value: "#030712",
|
|
12
|
+
overlay: { enabled: false, color: "#000000", opacity: 0 },
|
|
13
|
+
};
|
|
14
|
+
export const OVERLAY_FALLBACK_COLOR = "#000000";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { LotteryEntryStatus } from "../types";
|
|
5
5
|
export declare const LOTTERY_ENTRIES_COLLECTION: "lotteryEntries";
|
|
6
|
-
export
|
|
6
|
+
export type LotteryEntryDocument = {
|
|
7
7
|
id: string;
|
|
8
8
|
sourceType: "event" | "product";
|
|
9
9
|
/** Set when sourceType === "event" */
|
|
@@ -33,7 +33,7 @@ export interface LotteryEntryDocument {
|
|
|
33
33
|
flaggedAt?: Date;
|
|
34
34
|
submittedAt: Date;
|
|
35
35
|
drawnAt?: Date;
|
|
36
|
-
}
|
|
36
|
+
};
|
|
37
37
|
export declare const LOTTERY_ENTRY_INDEXED_FIELDS: readonly ["sourceType", "eventId", "productId", "userId", "status", "submittedAt", "userLotteryNumber"];
|
|
38
38
|
/** Canonical field name constants for lotteryEntries collection — use instead of hardcoded strings. */
|
|
39
39
|
export declare const LOTTERY_ENTRY_FIELDS: {
|
|
@@ -51,7 +51,6 @@ export function useConversation(conversationId) {
|
|
|
51
51
|
setConversation(await fetchDetail(conversationId));
|
|
52
52
|
}
|
|
53
53
|
catch (e) {
|
|
54
|
-
// toast-intentionally-silent: background refetch, error surfaced via error state
|
|
55
54
|
void normalizeError(e);
|
|
56
55
|
setError(e instanceof Error ? e : new Error(String(e)));
|
|
57
56
|
}
|
|
@@ -102,19 +101,12 @@ export function useConversation(conversationId) {
|
|
|
102
101
|
// the ping path isn't subscribed (e.g. provider not registered).
|
|
103
102
|
await refetch();
|
|
104
103
|
}, [conversationId, refetch]);
|
|
105
|
-
const markRead = useCallback(
|
|
104
|
+
const markRead = useCallback(() => {
|
|
106
105
|
if (!conversationId)
|
|
107
|
-
return;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
credentials: "include",
|
|
112
|
-
});
|
|
113
|
-
await refetch();
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
// toast-intentionally-silent: read-receipt is best-effort, non-critical
|
|
117
|
-
}
|
|
106
|
+
return Promise.resolve();
|
|
107
|
+
return fetch(READ_ENDPOINT(conversationId), { method: "POST", credentials: "include" })
|
|
108
|
+
.then(() => void refetch())
|
|
109
|
+
.catch((err) => void normalizeError(err));
|
|
118
110
|
}, [conversationId, refetch]);
|
|
119
111
|
return {
|
|
120
112
|
conversation,
|
|
@@ -31,7 +31,6 @@ export function useConversations(userId) {
|
|
|
31
31
|
setConversations(await fetchList());
|
|
32
32
|
}
|
|
33
33
|
catch (e) {
|
|
34
|
-
// toast-intentionally-silent: background refetch, error surfaced via error state
|
|
35
34
|
void normalizeError(e);
|
|
36
35
|
setError(e instanceof Error ? e : new Error(String(e)));
|
|
37
36
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* a subcollection when the thread exceeds ~200 messages.
|
|
7
7
|
*/
|
|
8
8
|
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
9
|
-
export
|
|
9
|
+
export type ConversationMessage = {
|
|
10
10
|
id: string;
|
|
11
11
|
senderId: string;
|
|
12
12
|
senderRole: "buyer" | "seller";
|
|
@@ -14,7 +14,7 @@ export interface ConversationMessage {
|
|
|
14
14
|
isRead: boolean;
|
|
15
15
|
sentAt: Date;
|
|
16
16
|
attachments?: string[];
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
export interface ConversationDocument extends BaseDocument {
|
|
19
19
|
buyerId: string;
|
|
20
20
|
buyerDisplayName: string;
|
|
@@ -21,7 +21,5 @@ export function RefundHistoryTable({ order, className = "" }) {
|
|
|
21
21
|
const currency = order.currency ?? "INR";
|
|
22
22
|
if (events.length === 0 && order.contestable !== false)
|
|
23
23
|
return null;
|
|
24
|
-
return (_jsxs(Div, { className: `${className}`, rounded: "xl", surface: "default", border: "default", children: [order.contestable === false && (
|
|
25
|
-
// audit-inline-style-ok: asymmetric border-radius (top corners only); no appkit rounded= variant covers partial corners
|
|
26
|
-
_jsx(Div, { surface: "warning-surface", padding: "inline", style: { borderTopLeftRadius: "0.75rem", borderTopRightRadius: "0.75rem" }, children: _jsxs(Row, { gap: "sm", align: "center", children: [_jsx(WarningIcon, {}), _jsx(Text, { size: "sm", weight: "semibold", className: "text-warning", children: REFUND_COPY.history.nonContestableBanner })] }) })), events.length > 0 && (_jsxs(Div, { className: `${__P.p4}`, children: [_jsx(Heading, { level: 4, className: "mb-3", color: "muted", size: "sm", weight: "semibold", children: REFUND_COPY.history.heading }), _jsx(Stack, { gap: "none", children: events.map((e) => (_jsx(RefundRow, { event: e, currency: currency }, e.refundId))) })] }))] }));
|
|
24
|
+
return (_jsxs(Div, { className: `${className}`, rounded: "xl", surface: "default", border: "default", children: [order.contestable === false && (_jsx(Div, { surface: "warning-surface", padding: "inline", roundedTop: "lg", children: _jsxs(Row, { gap: "sm", align: "center", children: [_jsx(WarningIcon, {}), _jsx(Text, { size: "sm", weight: "semibold", className: "text-warning", children: REFUND_COPY.history.nonContestableBanner })] }) })), events.length > 0 && (_jsxs(Div, { className: `${__P.p4}`, children: [_jsx(Heading, { level: 4, className: "mb-3", color: "muted", size: "sm", weight: "semibold", children: REFUND_COPY.history.heading }), _jsx(Stack, { gap: "none", children: events.map((e) => (_jsx(RefundRow, { event: e, currency: currency }, e.refundId))) })] }))] }));
|
|
27
25
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { OrderStatus, PaymentStatus } from "../types";
|
|
5
5
|
import type { OrderType } from "../utils/order-splitter";
|
|
6
6
|
import type { BaseDocument } from "../../../_internal/shared/types/base-document";
|
|
7
|
+
import type { ListingType } from "../../products/types";
|
|
7
8
|
export type ShippingMethod = "custom" | "shiprocket";
|
|
8
9
|
export type RefundType = "full" | "partial";
|
|
9
10
|
export type OrderPayoutStatus = "eligible" | "requested" | "paid";
|
|
@@ -58,7 +59,7 @@ export interface OrderDocumentItem {
|
|
|
58
59
|
unitPrice: number;
|
|
59
60
|
totalPrice: number;
|
|
60
61
|
/** SB8-F â€" set when the item is a prize-draw entry; drives the reveals badge. */
|
|
61
|
-
listingType?:
|
|
62
|
+
listingType?: ListingType;
|
|
62
63
|
/** SB8-F â€" per-item reveal status; flips through pending â†' open â†' revealed/closed. */
|
|
63
64
|
prizeRevealStatus?: "pending" | "open" | "closed" | "revealed";
|
|
64
65
|
/** SB8-F â€" ISO timestamp; deadline by which the buyer must claim the prize. */
|
|
@@ -756,7 +756,7 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
756
756
|
storeId?: string | undefined;
|
|
757
757
|
attributes?: Record<string, string> | undefined;
|
|
758
758
|
}>, "many">;
|
|
759
|
-
address: z.
|
|
759
|
+
address: z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>;
|
|
760
760
|
orderStatus: z.ZodEnum<["pending", "confirmed", "processing", "shipped", "delivered", "cancelled", "refunded", "return_requested", "returned"]>;
|
|
761
761
|
paymentStatus: z.ZodString;
|
|
762
762
|
paymentGateway: z.ZodOptional<z.ZodString>;
|
|
@@ -791,7 +791,9 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
791
791
|
}, "strip", z.ZodTypeAny, {
|
|
792
792
|
currency: string;
|
|
793
793
|
id: string;
|
|
794
|
-
address:
|
|
794
|
+
address: {} & {
|
|
795
|
+
[k: string]: unknown;
|
|
796
|
+
};
|
|
795
797
|
items: {
|
|
796
798
|
title: string;
|
|
797
799
|
price: number;
|
|
@@ -826,7 +828,9 @@ export declare const orderSchema: z.ZodObject<{
|
|
|
826
828
|
}, {
|
|
827
829
|
currency: string;
|
|
828
830
|
id: string;
|
|
829
|
-
address:
|
|
831
|
+
address: {} & {
|
|
832
|
+
[k: string]: unknown;
|
|
833
|
+
};
|
|
830
834
|
items: {
|
|
831
835
|
title: string;
|
|
832
836
|
price: number;
|
|
@@ -194,7 +194,7 @@ export const orderSchema = z.object({
|
|
|
194
194
|
userId: z.string(),
|
|
195
195
|
items: z.array(orderItemSchema),
|
|
196
196
|
// address shape is intentionally open to allow extending apps
|
|
197
|
-
address: z.
|
|
197
|
+
address: z.object({}).passthrough(),
|
|
198
198
|
orderStatus: orderStatusSchema,
|
|
199
199
|
paymentStatus: z.string(),
|
|
200
200
|
paymentGateway: z.string().optional(),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ListingType } from "../types";
|
|
1
2
|
import type { ProductDocument } from "../schemas";
|
|
2
3
|
import type { FirebaseSieveResult } from "../../../providers/db-firebase";
|
|
3
4
|
export interface ProductListActionParams {
|
|
@@ -6,7 +7,7 @@ export interface ProductListActionParams {
|
|
|
6
7
|
page?: number;
|
|
7
8
|
pageSize?: number;
|
|
8
9
|
/** Canonical listing-kind discriminator (SB1-G Phase 4). */
|
|
9
|
-
listingType?:
|
|
10
|
+
listingType?: ListingType;
|
|
10
11
|
featured?: boolean;
|
|
11
12
|
storeId?: string;
|
|
12
13
|
categoriesIn?: string[];
|
|
@@ -48,7 +48,7 @@ const productUpdateSchema = z
|
|
|
48
48
|
])
|
|
49
49
|
.optional(),
|
|
50
50
|
mainImage: z.string().optional(),
|
|
51
|
-
images: z.array(z.
|
|
51
|
+
images: z.array(z.union([z.string(), mediaFieldSchema])).optional(),
|
|
52
52
|
tags: z.array(z.string()).optional(),
|
|
53
53
|
featured: z.boolean().optional(),
|
|
54
54
|
isPromoted: z.boolean().optional(),
|
|
@@ -38,7 +38,7 @@ const productMutateSchema = z
|
|
|
38
38
|
])
|
|
39
39
|
.optional(),
|
|
40
40
|
mainImage: z.string().optional(),
|
|
41
|
-
images: z.array(z.
|
|
41
|
+
images: z.array(z.union([z.string(), mediaFieldSchema])).optional(),
|
|
42
42
|
tags: z.array(z.string()).optional(),
|
|
43
43
|
featured: z.boolean().optional(),
|
|
44
44
|
isPromoted: z.boolean().optional(),
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CompareOverlay â€" BK3
|
|
3
|
+
*
|
|
4
|
+
* Fullscreen comparison overlay triggered by the Compare bulk action on
|
|
5
|
+
* Products / Auctions / Pre-orders listings.
|
|
6
|
+
*
|
|
7
|
+
* Layout:
|
|
8
|
+
* - Desktop (md+): up to COMPARE_MAX_ITEMS columns side-by-side. Left rail
|
|
9
|
+
* holds field labels, columns hold values.
|
|
10
|
+
* - Mobile (< md): single-card swipeable view with dot pagination. The
|
|
11
|
+
* `useSwipe` hook detects left/right swipes on the card area.
|
|
12
|
+
*
|
|
13
|
+
* Data: parent passes either pre-loaded `items` or a list of `productIds`.
|
|
14
|
+
* When ids are passed, this component does a single fetch to
|
|
15
|
+
* `GET /api/products?ids=<csv>` (BK3 listByIds endpoint).
|
|
16
|
+
*/
|
|
17
|
+
import type { ListingType } from "../types";
|
|
1
18
|
/** Subset of ProductDocument the overlay needs. Kept loose so it can be fed
|
|
2
19
|
* sanitized public payloads directly. */
|
|
3
20
|
export interface CompareProductLike {
|
|
@@ -17,7 +34,7 @@ export interface CompareProductLike {
|
|
|
17
34
|
storeName?: string;
|
|
18
35
|
storeSlug?: string;
|
|
19
36
|
/** Canonical discriminator (SB1-G Phase 4). */
|
|
20
|
-
listingType?:
|
|
37
|
+
listingType?: ListingType;
|
|
21
38
|
features?: string[];
|
|
22
39
|
}
|
|
23
40
|
interface CompareFieldLabels {
|
|
@@ -1,21 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
/**
|
|
4
|
-
* CompareOverlay â€" BK3
|
|
5
|
-
*
|
|
6
|
-
* Fullscreen comparison overlay triggered by the Compare bulk action on
|
|
7
|
-
* Products / Auctions / Pre-orders listings.
|
|
8
|
-
*
|
|
9
|
-
* Layout:
|
|
10
|
-
* - Desktop (md+): up to COMPARE_MAX_ITEMS columns side-by-side. Left rail
|
|
11
|
-
* holds field labels, columns hold values.
|
|
12
|
-
* - Mobile (< md): single-card swipeable view with dot pagination. The
|
|
13
|
-
* `useSwipe` hook detects left/right swipes on the card area.
|
|
14
|
-
*
|
|
15
|
-
* Data: parent passes either pre-loaded `items` or a list of `productIds`.
|
|
16
|
-
* When ids are passed, this component does a single fetch to
|
|
17
|
-
* `GET /api/products?ids=<csv>` (BK3 listByIds endpoint).
|
|
18
|
-
*/
|
|
19
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
20
4
|
const __P = {
|
|
21
5
|
p4: "p-4",
|
|
@@ -59,7 +59,8 @@ export function ProductDetailActions({ productId, productSlug, productTitle, pro
|
|
|
59
59
|
try {
|
|
60
60
|
await apiClient.post("/api/wishlist", { productId });
|
|
61
61
|
}
|
|
62
|
-
catch (err) {
|
|
62
|
+
catch (err) {
|
|
63
|
+
void normalizeError(err);
|
|
63
64
|
const status = err?.status;
|
|
64
65
|
if (status === 401 || status === 403) {
|
|
65
66
|
addToGuestWishlist(productId, "product", {
|
|
@@ -23,9 +23,7 @@ function MemberThumb({ member, isCurrent }) {
|
|
|
23
23
|
const price = formatCurrency(member.price, member.currency ?? "INR");
|
|
24
24
|
return (_jsxs(Link, { href: href, "aria-label": member.title, className: "flex flex-col items-center gap-1.5 flex-shrink-0 w-16 group", children: [_jsxs("div", { className: `relative w-14 h-14 rounded-full overflow-hidden border-2 transition-all ${isCurrent
|
|
25
25
|
? "border-[var(--appkit-color-primary)] ring-2 ring-[var(--appkit-color-primary)]/30"
|
|
26
|
-
: "border-zinc-200 dark:border-zinc-700 group-hover:border-[var(--appkit-color-primary)]"}`, children: [_jsx(MediaImage, { src: image, alt: member.title, size: "card" }), member.isGroupParent && (
|
|
27
|
-
// audit-inline-style-ok: single-corner border-radius; no appkit rounded= variant covers individual corners
|
|
28
|
-
_jsx(Span, { color: "inverse", className: "absolute bottom-0 right-0 bg-[var(--appkit-color-primary)] text-[8px] leading-none px-[0.25rem] py-[0.125rem]", style: { borderTopLeftRadius: "0.25rem" }, children: "Set" }))] }), _jsx(Text, { className: "text-[10px] leading-tight line-clamp-2 w-full", color: "muted", align: "center", children: member.title }), _jsx(Text, { className: "text-[10px]", color: "primary", weight: "semibold", children: price })] }));
|
|
26
|
+
: "border-zinc-200 dark:border-zinc-700 group-hover:border-[var(--appkit-color-primary)]"}`, children: [_jsx(MediaImage, { src: image, alt: member.title, size: "card" }), member.isGroupParent && (_jsx(Span, { color: "inverse", rounded: "sm", className: "absolute bottom-0 right-0 bg-[var(--appkit-color-primary)] text-[8px] leading-none px-[0.25rem] py-[0.125rem]", children: "Set" }))] }), _jsx(Text, { className: "text-[10px] leading-tight line-clamp-2 w-full", color: "muted", align: "center", children: member.title }), _jsx(Text, { className: "text-[10px]", color: "primary", weight: "semibold", children: price })] }));
|
|
29
27
|
}
|
|
30
28
|
function GroupTableRow({ member }) {
|
|
31
29
|
const href = memberHref(member);
|
|
@@ -37,7 +37,7 @@ export class ProductFeaturesRepository extends BaseRepository {
|
|
|
37
37
|
if (filter.storeId)
|
|
38
38
|
q = q.where(PRODUCT_FIELDS.STORE_ID, "==", filter.storeId);
|
|
39
39
|
if (typeof filter.isActive === "boolean") {
|
|
40
|
-
q = q.where(
|
|
40
|
+
q = q.where(PRODUCT_FIELDS.IS_ACTIVE, "==", filter.isActive);
|
|
41
41
|
}
|
|
42
42
|
const snap = await q.get();
|
|
43
43
|
let docs = snap.docs.map((d) => this.mapDoc(d));
|
|
@@ -26,12 +26,12 @@ export interface CustomField {
|
|
|
26
26
|
}
|
|
27
27
|
export declare const MAX_CUSTOM_FIELDS = 50;
|
|
28
28
|
export declare const MAX_CUSTOM_SECTIONS = 3;
|
|
29
|
-
export
|
|
29
|
+
export type CustomSection = {
|
|
30
30
|
id: string;
|
|
31
31
|
title: string;
|
|
32
32
|
text?: string;
|
|
33
33
|
fields?: CustomField[];
|
|
34
|
-
}
|
|
34
|
+
};
|
|
35
35
|
export type GradingService = "PSA" | "BGS" | "CGC" | "SGC" | "OTHER";
|
|
36
36
|
export interface ProductGrading {
|
|
37
37
|
/** Grading service (PSA / BGS / CGC / SGC / OTHER) — sortable index field. */
|
|
@@ -319,8 +319,7 @@ export type ProductUpdateInput = Partial<Pick<ProductDocument, (typeof PRODUCT_U
|
|
|
319
319
|
export type ProductAdminUpdateInput = Partial<Omit<ProductDocument, "id" | "createdAt">>;
|
|
320
320
|
export declare const PRODUCT_CODES_SUBCOLLECTION: "codes";
|
|
321
321
|
export type ProductCodeStatus = "available" | "claimed" | "revoked";
|
|
322
|
-
export interface ProductCodeDocument {
|
|
323
|
-
id: string;
|
|
322
|
+
export interface ProductCodeDocument extends BaseDocument {
|
|
324
323
|
productId: string;
|
|
325
324
|
code: string;
|
|
326
325
|
status: ProductCodeStatus;
|
|
@@ -328,8 +327,6 @@ export interface ProductCodeDocument {
|
|
|
328
327
|
claimedByUserId?: string;
|
|
329
328
|
claimedAt?: Date;
|
|
330
329
|
expiresAt?: Date;
|
|
331
|
-
createdAt: Date;
|
|
332
|
-
updatedAt: Date;
|
|
333
330
|
}
|
|
334
331
|
export declare const productQueryHelpers: {
|
|
335
332
|
readonly byStore: (storeId: string) => readonly ["storeId", "==", string];
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* are handled by the calling server action in the consumer.
|
|
6
6
|
*/
|
|
7
7
|
import type { CouponValidationResult } from "../schemas";
|
|
8
|
+
import type { ListingType } from "../../products/types";
|
|
8
9
|
export type { CouponValidationResult };
|
|
9
10
|
export interface CouponCartValidationResult {
|
|
10
11
|
valid: boolean;
|
|
@@ -22,7 +23,7 @@ export type CouponCartItem = {
|
|
|
22
23
|
price: number;
|
|
23
24
|
quantity: number;
|
|
24
25
|
/** Canonical listing-kind snapshot (SB1-G Phase 4). */
|
|
25
|
-
listingType:
|
|
26
|
+
listingType: ListingType;
|
|
26
27
|
};
|
|
27
28
|
export declare function validateCoupon(userId: string, code: string, orderTotal: number): Promise<CouponValidationResult>;
|
|
28
29
|
export declare function validateCouponForCart(userId: string, code: string, cartItems: CouponCartItem[]): Promise<CouponCartValidationResult>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { JsonValue } from "@mohasinac/appkit";
|
|
2
|
+
import type { ListingType } from "../../products/types";
|
|
2
3
|
interface ValidateCouponPayload {
|
|
3
4
|
code: string;
|
|
4
5
|
orderTotal?: number;
|
|
@@ -8,7 +9,7 @@ interface ValidateCouponPayload {
|
|
|
8
9
|
price: number;
|
|
9
10
|
quantity: number;
|
|
10
11
|
/** Canonical listing-kind snapshot (SB1-G Phase 4). */
|
|
11
|
-
listingType?:
|
|
12
|
+
listingType?: ListingType;
|
|
12
13
|
}>;
|
|
13
14
|
}
|
|
14
15
|
interface ValidateCouponResult {
|