@mohasinac/appkit 3.9.1 → 4.0.2
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/shared/actions/action-registry.js +8 -0
- package/dist/client.d.ts +2 -2
- package/dist/client.js +1 -4
- package/dist/constants/api-endpoints.d.ts +3 -0
- package/dist/constants/api-endpoints.js +1 -0
- package/dist/features/account/hooks/useDataViewMode.d.ts +17 -0
- package/dist/features/account/hooks/useDataViewMode.js +55 -0
- package/dist/features/account/hooks/useProfile.d.ts +2 -0
- package/dist/features/admin/components/AdminBundlesView.d.ts +3 -4
- package/dist/features/admin/components/AdminBundlesView.js +2 -2
- package/dist/features/admin/components/AdminCarouselView.js +1 -1
- package/dist/features/admin/components/AdminCategoriesView.d.ts +2 -6
- package/dist/features/admin/components/AdminCategoriesView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.d.ts +0 -2
- package/dist/features/admin/components/AdminPrizeDrawsView.js +1 -1
- package/dist/features/admin/components/DataListingView.d.ts +8 -2
- package/dist/features/admin/components/DataListingView.js +7 -3
- package/dist/features/admin/components/DataTable.d.ts +10 -2
- package/dist/features/admin/components/DataTable.js +4 -2
- package/dist/features/admin/hooks/useAdminListing.d.ts +2 -2
- package/dist/features/admin/hooks/useAdminListing.js +6 -1
- package/dist/features/categories/components/BrandDetailTabs.js +2 -12
- package/dist/features/categories/components/CategoryDetailTabs.js +2 -12
- package/dist/features/events/components/AdminEventsView.d.ts +1 -5
- package/dist/features/faq/components/FAQAccordion.js +3 -2
- package/dist/features/faq/components/RelatedFAQs.js +1 -1
- package/dist/features/homepage/components/FAQSection.js +3 -3
- package/dist/features/layout/FooterLayout.js +2 -2
- package/dist/features/lottery/schemas/zod.d.ts +4 -4
- package/dist/features/payments/schemas/firestore.d.ts +6 -0
- package/dist/features/payments/schemas/firestore.js +2 -0
- package/dist/features/seller/components/SellerBundlesView.d.ts +3 -4
- package/dist/features/seller/components/SellerBundlesView.js +2 -2
- package/dist/features/seller/components/SellerPayoutsView.d.ts +2 -5
- package/dist/features/seller/components/SellerPayoutsView.js +100 -21
- package/dist/features/seller/components/SellerPreOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPrizeDrawsView.js +1 -1
- package/dist/features/seller/components/SellerProductsView.js +6 -2
- package/dist/features/shipments/components/AdminShipmentsView.d.ts +1 -5
- package/dist/index.d.ts +3 -5
- package/dist/index.js +3 -3
- package/dist/react/index.d.ts +0 -2
- package/dist/react/index.js +0 -2
- package/dist/seed/faq-seed-data.js +11 -11
- package/dist/seed/homepage-sections-seed-data.js +1 -1
- package/dist/styles.css +30 -99
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/DataTable.js +1 -1
- package/dist/ui/DataTable.style.css +10 -0
- package/dist/ui/components/DetailPageTabs.d.ts +1 -1
- package/dist/ui/components/DetailPageTabs.js +6 -30
- package/dist/ui/components/HorizontalScroller.d.ts +1 -1
- package/dist/ui/components/HorizontalScroller.js +78 -197
- package/dist/ui/components/Tabs.d.ts +9 -1
- package/dist/ui/components/Tabs.js +13 -8
- package/dist/ui/components/Tabs.style.css +19 -0
- package/dist/ui/components/index.style.css +0 -1
- package/dist/ui/index.d.ts +0 -2
- package/dist/ui/index.js +0 -1
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ export function FooterLayout({ brandName, brandDescription, socialLinks, linkGro
|
|
|
8
8
|
const [openGroups, setOpenGroups] = useState({});
|
|
9
9
|
const toggleGroup = (idx) => setOpenGroups((prev) => ({ ...prev, [idx]: !prev[idx] }));
|
|
10
10
|
const visibleTrustItems = trustBarItems.filter((item) => item.visible !== false);
|
|
11
|
-
return (_jsxs(BlockFooter, { id: id, className: "bg-
|
|
11
|
+
return (_jsxs(BlockFooter, { id: id, className: "bg-[var(--appkit-color-bg)] border-t border-[var(--appkit-color-border-subtle)]", children: [showTrustBar && visibleTrustItems.length > 0 && (_jsx(Div, { border: "subtle", className: "border-b", padding: "y-xl", children: _jsx(Div, { paddingX: "x-page", className: "container mx-auto max-w-[1920px]", children: _jsx(Row, { as: "ul", wrap: true, justify: "center", gap: "lg", className: "lg:gap-[var(--appkit-space-10)]", children: visibleTrustItems.map((item, i) => (_jsx(Li, { children: _jsxs(Row, { textSize: "sm", gap: "sm", className: "min-w-[160px]", children: [_jsx(Span, { className: "flex-shrink-0 text-primary-600 dark:text-secondary-400 w-5 h-5", children: item.icon }), _jsxs(Span, { children: [_jsx(Span, { size: "sm", weight: "bold", className: "block", color: "primary", children: item.label }), item.subtitle && (_jsx(Span, { size: "xs", className: "block", color: "muted", children: item.subtitle }))] })] }) }, item.id ?? String(i)))) }) }) })), _jsxs(Div, { paddingX: "x-page", className: "container mx-auto max-w-[1920px] lg:py-[var(--appkit-space-14)]", padding: "y-2xl", children: [_jsxs(Div, { className: "grid grid-cols-1 lg:grid-cols-5 gap-[var(--appkit-space-10)] lg:gap-[var(--appkit-space-16)]", children: [_jsxs(Stack, { className: "lg:col-span-2", gap: "md", children: [_jsx(Heading, { level: 5, size: "xl", weight: "bold", color: "primary", children: brandName }), _jsx(Text, { className: "leading-relaxed max-w-sm", color: "muted", size: "sm", children: brandDescription }), socialLinks.length > 0 && (_jsx(Row, { as: Ul, gap: "sm", children: socialLinks.map((link) => (_jsx(Li, { children: _jsx(Link, { href: link.href, "aria-label": link.ariaLabel, target: "_blank", rel: "noopener noreferrer", className: "flex items-center justify-center w-9 h-9 rounded-full border border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] hover:text-primary dark:hover:text-secondary hover:border-primary/40 dark:hover:border-secondary/40 transition-colors", children: link.icon }) }, link.platform))) })), newsletterEnabled && newsletterSlot && (_jsx(Div, { padding: "t-xs", children: newsletterSlot }))] }), _jsx(Div, { className: `lg:col-span-3 grid grid-cols-1 gap-[var(--appkit-space-0)] lg:gap-[var(--appkit-space-8)] ${linkGroups.length <= 3 ? "lg:grid-cols-3" :
|
|
12
12
|
linkGroups.length === 4 ? "lg:grid-cols-4" :
|
|
13
|
-
"lg:grid-cols-5"}`, children: linkGroups.map((group, idx) => (_jsxs(Div, { children: [_jsxs(Button, { type: "button", variant: "ghost", onClick: () => toggleGroup(idx), className: "w-full justify-between border-b
|
|
13
|
+
"lg:grid-cols-5"}`, children: linkGroups.map((group, idx) => (_jsxs(Div, { children: [_jsxs(Button, { type: "button", variant: "ghost", onClick: () => toggleGroup(idx), className: "w-full justify-between border-b py-[var(--appkit-space-3)] text-left text-[length:var(--appkit-text-sm)] font-semibold transition-colors border-[var(--appkit-color-border-subtle)] text-[var(--appkit-color-text)] hover:text-primary dark:hover:text-secondary lg:hidden", "aria-expanded": !!openGroups[idx], children: [group.heading, _jsx(ChevronDown, { className: `w-4 h-4 transition-transform duration-200 ${openGroups[idx] ? "rotate-180" : ""}` })] }), _jsx(Ul, { className: `lg:hidden overflow-hidden transition-all duration-200 ${openGroups[idx] ? "max-h-96 py-[var(--appkit-space-3)]" : "max-h-0"} space-y-2.5`, children: group.links.map((link) => (_jsx(Li, { children: _jsx(Link, { href: link.href, className: "text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] hover:text-primary dark:hover:text-secondary transition-colors", children: link.label }) }, link.href))) }), _jsxs(Div, { className: "hidden lg:block", children: [_jsx(Text, { className: "tracking-wider mb-3", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: group.heading }), _jsx(Ul, { className: "space-y-2.5", children: group.links.map((link) => (_jsx(Li, { children: _jsx(Link, { href: link.href, className: "text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] hover:text-primary dark:hover:text-secondary transition-colors", children: link.label }) }, link.href))) })] })] }, idx))) })] }), _jsxs(Stack, { direction: "sm-row", color: "muted", textSize: "xs", justify: "between", border: "default", className: "mt-10 border-t border-[var(--appkit-color-border-subtle)] text-[var(--appkit-color-text-muted)]", padding: "t-lg", align: "center", gap: "3", children: [_jsx(Text, { size: "xs", color: "muted", children: copyrightText }), bottomLinks.length > 0 && (_jsx(Nav, { "aria-label": "Site utilities", children: _jsx(Ul, { className: "flex flex-wrap items-center gap-x-4 gap-y-1 justify-center", children: bottomLinks.map((link) => (_jsx(Li, { children: _jsx(Link, { href: link.href, className: "text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)] hover:text-primary dark:hover:text-secondary transition-colors underline-offset-2 hover:underline", children: link.label }) }, link.href))) }) })), madeInText && (_jsx(Text, { size: "xs", color: "muted", children: madeInText }))] })] })] }));
|
|
14
14
|
}
|
|
@@ -66,8 +66,8 @@ export declare const submitLotteryPullSchema: z.ZodEffects<z.ZodObject<{
|
|
|
66
66
|
purchasedItemNumber: z.ZodNumber;
|
|
67
67
|
userPhone: z.ZodString;
|
|
68
68
|
}, "strip", z.ZodTypeAny, {
|
|
69
|
-
userId: string;
|
|
70
69
|
transactionId: string;
|
|
70
|
+
userId: string;
|
|
71
71
|
userPhone: string;
|
|
72
72
|
sourceType: "product" | "event";
|
|
73
73
|
paymentTime: string;
|
|
@@ -77,8 +77,8 @@ export declare const submitLotteryPullSchema: z.ZodEffects<z.ZodObject<{
|
|
|
77
77
|
userDisplayName?: string | undefined;
|
|
78
78
|
eventId?: string | undefined;
|
|
79
79
|
}, {
|
|
80
|
-
userId: string;
|
|
81
80
|
transactionId: string;
|
|
81
|
+
userId: string;
|
|
82
82
|
userPhone: string;
|
|
83
83
|
sourceType: "product" | "event";
|
|
84
84
|
paymentTime: string;
|
|
@@ -88,8 +88,8 @@ export declare const submitLotteryPullSchema: z.ZodEffects<z.ZodObject<{
|
|
|
88
88
|
userDisplayName?: string | undefined;
|
|
89
89
|
eventId?: string | undefined;
|
|
90
90
|
}>, {
|
|
91
|
-
userId: string;
|
|
92
91
|
transactionId: string;
|
|
92
|
+
userId: string;
|
|
93
93
|
userPhone: string;
|
|
94
94
|
sourceType: "product" | "event";
|
|
95
95
|
paymentTime: string;
|
|
@@ -99,8 +99,8 @@ export declare const submitLotteryPullSchema: z.ZodEffects<z.ZodObject<{
|
|
|
99
99
|
userDisplayName?: string | undefined;
|
|
100
100
|
eventId?: string | undefined;
|
|
101
101
|
}, {
|
|
102
|
-
userId: string;
|
|
103
102
|
transactionId: string;
|
|
103
|
+
userId: string;
|
|
104
104
|
userPhone: string;
|
|
105
105
|
sourceType: "product" | "event";
|
|
106
106
|
paymentTime: string;
|
|
@@ -66,6 +66,10 @@ export interface PayoutDocument extends BaseDocument {
|
|
|
66
66
|
netAmount?: number;
|
|
67
67
|
requestedAt: Date;
|
|
68
68
|
processedAt?: Date;
|
|
69
|
+
/** UTR / UPI ref / bank transfer ID entered by admin when marking paid. */
|
|
70
|
+
transactionId?: string;
|
|
71
|
+
/** Seller-set personal follow-up flag — surfaced only to the owning seller, never mutated by admin. */
|
|
72
|
+
sellerReminderFlag?: boolean;
|
|
69
73
|
}
|
|
70
74
|
export declare const PAYOUT_COLLECTION: "payouts";
|
|
71
75
|
export declare const PAYOUT_INDEXED_FIELDS: readonly ["storeId", "status", "requestedAt", "createdAt"];
|
|
@@ -88,6 +92,8 @@ export declare const PAYOUT_FIELDS: {
|
|
|
88
92
|
};
|
|
89
93
|
export declare const PAYOUT_PUBLIC_FIELDS: readonly ["id", "storeId", "amount", "currency", "status", "paymentMethod", "requestedAt", "processedAt", "createdAt"];
|
|
90
94
|
export declare const PAYOUT_ADMIN_UPDATEABLE_FIELDS: readonly ["status", "adminNote", "processedAt"];
|
|
95
|
+
/** The only field a seller may write on their own payout doc. */
|
|
96
|
+
export declare const PAYOUT_SELLER_UPDATEABLE_FIELDS: readonly ["sellerReminderFlag"];
|
|
91
97
|
export type PayoutCreateInput = Omit<PayoutDocument, "id" | "createdAt" | "updatedAt" | "status" | "requestedAt">;
|
|
92
98
|
export type PayoutUpdateInput = Partial<Pick<PayoutDocument, (typeof PAYOUT_ADMIN_UPDATEABLE_FIELDS)[number]>>;
|
|
93
99
|
export declare const payoutQueryHelpers: {
|
|
@@ -52,6 +52,8 @@ export const PAYOUT_ADMIN_UPDATEABLE_FIELDS = [
|
|
|
52
52
|
"adminNote",
|
|
53
53
|
"processedAt",
|
|
54
54
|
];
|
|
55
|
+
/** The only field a seller may write on their own payout doc. */
|
|
56
|
+
export const PAYOUT_SELLER_UPDATEABLE_FIELDS = ["sellerReminderFlag"];
|
|
55
57
|
export const payoutQueryHelpers = {
|
|
56
58
|
byStore: (storeId) => ["storeId", "==", storeId],
|
|
57
59
|
byStatus: (status) => ["status", "==", status],
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
export interface SellerBundlesViewProps {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}) => string;
|
|
3
|
+
/** Plain string with an `{id}` placeholder — safe to build in a Server Component page.tsx. */
|
|
4
|
+
editHrefTemplate: string;
|
|
6
5
|
newHref: string;
|
|
7
6
|
}
|
|
8
|
-
export declare function SellerBundlesView({
|
|
7
|
+
export declare function SellerBundlesView({ editHrefTemplate, newHref }: SellerBundlesViewProps): React.JSX.Element;
|
|
@@ -37,7 +37,7 @@ const COLUMNS = [
|
|
|
37
37
|
},
|
|
38
38
|
{ key: "updatedAt", header: "Updated", className: "w-28" },
|
|
39
39
|
];
|
|
40
|
-
export function SellerBundlesView({
|
|
40
|
+
export function SellerBundlesView({ editHrefTemplate, newHref }) {
|
|
41
41
|
const config = {
|
|
42
42
|
portal: "seller",
|
|
43
43
|
title: "Bundles",
|
|
@@ -78,7 +78,7 @@ export function SellerBundlesView({ getEditHref, newHref }) {
|
|
|
78
78
|
parts.push(sieveFilter("bundleStockStatus", SIEVE_OP.EQ, filterState.bundleStockStatus));
|
|
79
79
|
return parts.join(",") || undefined;
|
|
80
80
|
},
|
|
81
|
-
|
|
81
|
+
rowHrefTemplate: editHrefTemplate,
|
|
82
82
|
toolbarExtra: (_jsx(Button, { asChild: true, size: "sm", variant: "primary", children: _jsx(TextLink, { href: newHref, layout: "flex", align: "center", gap: "xs", children: "+ New Bundle" }) })),
|
|
83
83
|
// Rule #7: bulk-action array sourced from the SELLER_BULK_ACTIONS preset.
|
|
84
84
|
buildBulkActions: (selection) => {
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ListingLayoutProps } from "../../../ui";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
onExportClick?: (id: string) => void;
|
|
6
|
-
}
|
|
7
|
-
export declare function SellerPayoutsView({ children, onViewClick, onExportClick, ...props }: SellerPayoutsViewProps): React.JSX.Element;
|
|
3
|
+
export type SellerPayoutsViewProps = ListingLayoutProps;
|
|
4
|
+
export declare function SellerPayoutsView({ children, ...props }: SellerPayoutsViewProps): React.JSX.Element;
|
|
@@ -1,18 +1,77 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { normalizeError } from "../../../errors/normalize";
|
|
3
4
|
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
4
5
|
import { sortBy } from "@mohasinac/appkit";
|
|
5
|
-
import React from "react";
|
|
6
|
-
import { FilterChipGroup, ListingLayout, RowActionMenu } from "../../../ui";
|
|
6
|
+
import React, { useState } from "react";
|
|
7
|
+
import { Badge, Div, FilterChipGroup, ListingLayout, Row, RowActionMenu, SideDrawer, Span, Stack, Text, Toggle, useToast } from "../../../ui";
|
|
7
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { ADMIN_PAYOUT_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
9
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
+
import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
|
|
12
|
+
import { apiClient } from "../../../http";
|
|
10
13
|
import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../../admin/hooks/useAdminListingData";
|
|
11
14
|
import { DataListingView } from "../../admin/components/DataListingView";
|
|
12
|
-
|
|
15
|
+
const PAYOUT_STAGES = ["pending", "processing", "paid"];
|
|
16
|
+
const PAYOUT_CYCLE_DAYS = 7;
|
|
17
|
+
function formatDate(value) {
|
|
18
|
+
if (!value)
|
|
19
|
+
return "—";
|
|
20
|
+
const d = new Date(value);
|
|
21
|
+
return Number.isNaN(d.getTime()) ? "—" : d.toLocaleDateString("en-IN", { day: "numeric", month: "short", year: "numeric" });
|
|
22
|
+
}
|
|
23
|
+
function StatusProgress({ status }) {
|
|
24
|
+
const normalized = status.toLowerCase();
|
|
25
|
+
if (normalized === "failed") {
|
|
26
|
+
return _jsx(Badge, { variant: "danger", children: "Failed" });
|
|
27
|
+
}
|
|
28
|
+
const currentIndex = PAYOUT_STAGES.indexOf(normalized);
|
|
29
|
+
return (_jsx(Row, { gap: "xs", align: "center", children: PAYOUT_STAGES.map((stage, i) => (_jsxs(React.Fragment, { children: [i > 0 && _jsx(Div, { className: `h-0.5 w-6 ${i <= currentIndex ? "bg-primary" : "bg-[var(--appkit-color-border)]"}` }), _jsx(Span, { size: "xs", weight: i === currentIndex ? "bold" : "medium", className: i <= currentIndex ? "text-primary" : "text-[var(--appkit-color-text-muted)]", children: stage[0].toUpperCase() + stage.slice(1) })] }, stage))) }));
|
|
30
|
+
}
|
|
31
|
+
export function SellerPayoutsView({ children, ...props }) {
|
|
32
|
+
const { showToast } = useToast();
|
|
33
|
+
const [selectedPayout, setSelectedPayout] = useState(null);
|
|
34
|
+
const [reminderPending, setReminderPending] = useState(false);
|
|
35
|
+
// Mirrors the raw API record per row id so the detail panel can open
|
|
36
|
+
// instantly from data already in memory (list already fetched it) —
|
|
37
|
+
// matches AdminProductsView's rawByIdRef pattern.
|
|
38
|
+
const rawByIdRef = React.useRef({});
|
|
13
39
|
if (React.Children.count(children) > 0) {
|
|
14
40
|
return (_jsx(ListingLayout, { portal: "seller", ...props, children: children }));
|
|
15
41
|
}
|
|
42
|
+
const handleExport = (rows) => {
|
|
43
|
+
const header = ["id", "amount", "currency", "status", "requestedAt", "processedAt", "transactionId"];
|
|
44
|
+
const lines = [header.join(",")];
|
|
45
|
+
for (const p of rows) {
|
|
46
|
+
lines.push(header.map((k) => JSON.stringify(p[k] ?? "")).join(","));
|
|
47
|
+
}
|
|
48
|
+
const blob = new Blob([lines.join("\n")], { type: "text/csv" });
|
|
49
|
+
const url = URL.createObjectURL(blob);
|
|
50
|
+
const a = document.createElement("a");
|
|
51
|
+
a.href = url;
|
|
52
|
+
a.download = `payouts-${new Date().toISOString().slice(0, 10)}.csv`;
|
|
53
|
+
a.click();
|
|
54
|
+
URL.revokeObjectURL(url);
|
|
55
|
+
};
|
|
56
|
+
const handleToggleReminder = async (next) => {
|
|
57
|
+
if (!selectedPayout)
|
|
58
|
+
return;
|
|
59
|
+
setReminderPending(true);
|
|
60
|
+
try {
|
|
61
|
+
await apiClient.patch(SELLER_ENDPOINTS.PAYOUT_BY_ID(String(selectedPayout.id)), {
|
|
62
|
+
sellerReminderFlag: next,
|
|
63
|
+
});
|
|
64
|
+
setSelectedPayout((prev) => (prev ? { ...prev, sellerReminderFlag: next } : prev));
|
|
65
|
+
showToast(next ? "Reminder set." : "Reminder cleared.", "success");
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
void normalizeError(err);
|
|
69
|
+
showToast("Failed to update reminder.", "error");
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
setReminderPending(false);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
16
75
|
const config = {
|
|
17
76
|
portal: "seller",
|
|
18
77
|
title: "Payouts",
|
|
@@ -26,29 +85,49 @@ export function SellerPayoutsView({ children, onViewClick, onExportClick, ...pro
|
|
|
26
85
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
27
86
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
28
87
|
],
|
|
29
|
-
mapRows: (response) => toRecordArray(response.payouts).map((item, index) =>
|
|
30
|
-
id
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
88
|
+
mapRows: (response) => toRecordArray(response.payouts).map((item, index) => {
|
|
89
|
+
const id = toStringValue(item.id, `payout-${index}`);
|
|
90
|
+
rawByIdRef.current[id] = item;
|
|
91
|
+
return {
|
|
92
|
+
id,
|
|
93
|
+
primary: `Payout #${toStringValue(item.payoutNumber ?? item.id, "Unknown")}`,
|
|
94
|
+
secondary: [
|
|
95
|
+
toCurrency(item.amount ?? item.totalAmount),
|
|
96
|
+
`Requested: ${toRelativeDate(item.requestedAt ?? item.createdAt)}`,
|
|
97
|
+
].join(" · "),
|
|
98
|
+
status: toStringValue(item.status, "Pending"),
|
|
99
|
+
updatedAt: toRelativeDate(item.processedAt ?? item.updatedAt ?? item.createdAt),
|
|
100
|
+
};
|
|
101
|
+
}),
|
|
39
102
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number"
|
|
40
103
|
? response.meta.total
|
|
41
104
|
: mappedRows.length,
|
|
42
105
|
buildFilters: (state) => state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : undefined,
|
|
106
|
+
// Rule #7: bulk-action array sourced from the CTA registry, not inline objects.
|
|
107
|
+
buildBulkActions: (selection) => [
|
|
108
|
+
buildBulkAction(ACTIONS.STORE["export-payout"], () => {
|
|
109
|
+
const rows = selection.selectedIds
|
|
110
|
+
.map((id) => rawByIdRef.current[id])
|
|
111
|
+
.filter((r) => Boolean(r));
|
|
112
|
+
handleExport(rows);
|
|
113
|
+
selection.clearSelection();
|
|
114
|
+
}),
|
|
115
|
+
],
|
|
43
116
|
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
: []),
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
: []
|
|
117
|
+
{
|
|
118
|
+
label: ACTIONS.STORE["view-payout"].label,
|
|
119
|
+
onClick: () => setSelectedPayout(rawByIdRef.current[row.id] ?? { id: row.id }),
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
label: ACTIONS.STORE["export-payout"].label,
|
|
123
|
+
onClick: () => handleExport([rawByIdRef.current[row.id] ?? { id: row.id }]),
|
|
124
|
+
},
|
|
50
125
|
] })),
|
|
51
126
|
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PAYOUT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
|
|
52
127
|
};
|
|
53
|
-
|
|
128
|
+
const expectedByDate = selectedPayout?.requestedAt
|
|
129
|
+
? new Date(new Date(selectedPayout.requestedAt).getTime() + PAYOUT_CYCLE_DAYS * 24 * 60 * 60 * 1000)
|
|
130
|
+
: null;
|
|
131
|
+
const orderIds = Array.isArray(selectedPayout?.orderIds) ? selectedPayout.orderIds : [];
|
|
132
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(SideDrawer, { isOpen: Boolean(selectedPayout), onClose: () => setSelectedPayout(null), title: selectedPayout ? `Payout #${toStringValue(selectedPayout.payoutNumber ?? selectedPayout.id, "")}` : "Payout", mode: "view", children: selectedPayout && (_jsxs(Stack, { gap: "lg", children: [_jsx(StatusProgress, { status: toStringValue(selectedPayout.status, "pending") }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Amount" }), _jsx(Text, { size: "xl", weight: "bold", children: toCurrency(selectedPayout.amount ?? selectedPayout.totalAmount) })] }), selectedPayout.transactionId ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Transaction / reference ID" }), _jsx(Text, { size: "sm", className: "font-mono", children: String(selectedPayout.transactionId) })] })) : null, _jsxs(Row, { gap: "lg", wrap: true, children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Requested" }), _jsx(Text, { size: "sm", children: formatDate(selectedPayout.requestedAt ?? selectedPayout.createdAt) })] }), selectedPayout.status === "paid" ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Paid on" }), _jsx(Text, { size: "sm", children: formatDate(selectedPayout.processedAt) })] })) : (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Expected by" }), _jsx(Text, { size: "sm", children: expectedByDate ? formatDate(expectedByDate.toISOString()) : "—" })] })), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Payment method" }), _jsx(Text, { size: "sm", className: "capitalize", children: toStringValue(selectedPayout.paymentMethod, "—").replace("_", " ") })] })] }), orderIds.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Orders included (", orderIds.length, ")"] }), _jsx(Div, { className: "max-h-40", overflow: "y-auto", children: _jsx(Stack, { gap: "xs", children: orderIds.map((oid) => (_jsx(Text, { size: "xs", className: "font-mono", color: "muted", children: oid }, oid))) }) })] })), _jsxs(Row, { align: "center", justify: "between", border: "default", rounded: "lg", padding: "inline", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Remind me" }), _jsx(Text, { size: "xs", color: "muted", children: "Flag this payout for a personal follow-up" })] }), _jsx(Toggle, { checked: Boolean(selectedPayout.sellerReminderFlag), onChange: handleToggleReminder, disabled: reminderPending, size: "md", "aria-label": ACTIONS.STORE["set-payout-reminder"].ariaLabel })] })] })) })] }));
|
|
54
133
|
}
|
|
@@ -111,7 +111,7 @@ export function SellerPreOrdersView({ children, onDelete, ...props }) {
|
|
|
111
111
|
const status = state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : null;
|
|
112
112
|
return ["listingType==pre-order", status].filter(Boolean).join(",");
|
|
113
113
|
},
|
|
114
|
-
|
|
114
|
+
rowHrefTemplate: String(ROUTES.STORE.PRE_ORDERS_EDIT("{id}")),
|
|
115
115
|
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
116
116
|
{
|
|
117
117
|
label: ACTIONS.STORE["edit-listing"].label,
|
|
@@ -108,7 +108,7 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
|
|
|
108
108
|
const status = state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : null;
|
|
109
109
|
return ["listingType==prize-draw", status].filter(Boolean).join(",");
|
|
110
110
|
},
|
|
111
|
-
|
|
111
|
+
rowHrefTemplate: String(ROUTES.STORE.PRIZE_DRAWS_EDIT("{id}")),
|
|
112
112
|
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
113
113
|
{
|
|
114
114
|
label: ACTIONS.STORE["edit-listing"].label,
|
|
@@ -18,6 +18,7 @@ import { ROUTES } from "../../../constants";
|
|
|
18
18
|
import { normalizeListingType } from "../../products/utils/listing-type";
|
|
19
19
|
import { toRecordArray, toRelativeDate, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
20
20
|
import { DataTable } from "../../admin/components/DataTable";
|
|
21
|
+
import { useDataViewMode } from "../../account/hooks/useDataViewMode";
|
|
21
22
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
22
23
|
import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
|
|
23
24
|
import { useListingTypeFlags } from "../../../react/hooks/useListingTypeFlags";
|
|
@@ -114,7 +115,7 @@ const PRODUCT_COLUMNS = [
|
|
|
114
115
|
];
|
|
115
116
|
export function SellerProductsView({ onDeleteProduct, onCreateClick, children, ...props }) {
|
|
116
117
|
const hasChildren = React.Children.count(children) > 0;
|
|
117
|
-
const
|
|
118
|
+
const { view, setView } = useDataViewMode("table");
|
|
118
119
|
const dispatch = useActionDispatch();
|
|
119
120
|
const { showToast } = useToast();
|
|
120
121
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
@@ -302,8 +303,11 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
|
|
|
302
303
|
{ label: "Show sold", active: showSold, onChange: (next) => table.set("showSold", next ? "true" : "") },
|
|
303
304
|
], extra: onCreateClick ? (_jsx(Button, { variant: "primary", size: "sm", onClick: onCreateClick, children: "+ New Listing" })) : null }), _jsx(TypeDropdown, { active: listingKind, onChange: handleKindChange }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "default", border: true, padding: "sm", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), selection.selectedIds.length > 0 && (_jsx(StickyToolbar, { offset: "header+bulk-actions", tone: "default", border: true, padding: "sm", z: "above-toolbar", children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _jsxs(Div, { paddingX: "x-sm-lg-md", padding: "y-md", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), view !== "table" && (_jsx(SellerProductsCards, { view: view, rows: visibleRows, isLoading: isLoading, listingKind: listingKind, selectedIds: selection.selectedIdSet, toggle: selection.toggle, onEdit: handleEdit, onDuplicate: (row) => void handleDuplicate(row), onDelete: onDeleteProduct ? (row) => void handleDelete(row) : undefined })), view === "table" && (_jsx(DataTable, { columns: PRODUCT_COLUMNS, rows: visibleRows, isLoading: isLoading, emptyLabel: listingKind !== "all"
|
|
304
305
|
? `No ${listingKind} listings found`
|
|
305
|
-
: "No products listed yet", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: () => selection.toggleAll(),
|
|
306
|
+
: "No products listed yet", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: () => selection.toggleAll(), rowHrefTemplate: (row) =>
|
|
306
307
|
// S-STORE-2-D — row click → public detail/preview, not edit.
|
|
308
|
+
// A function is safe here: both this view and DataTable are
|
|
309
|
+
// already client components, and the target varies per row
|
|
310
|
+
// (not expressible as a single {id} template string).
|
|
307
311
|
row.listingKind === "auction"
|
|
308
312
|
? `/auctions/${row.id}`
|
|
309
313
|
: row.listingKind === "pre-order"
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ListingLayoutProps } from "../../../ui";
|
|
3
|
-
export
|
|
4
|
-
getRowHref?: (row: {
|
|
5
|
-
id: string;
|
|
6
|
-
}) => string;
|
|
7
|
-
}
|
|
3
|
+
export type AdminShipmentsViewProps = ListingLayoutProps;
|
|
8
4
|
export declare function AdminShipmentsView({ children, ...props }: AdminShipmentsViewProps): React.JSX.Element;
|
package/dist/index.d.ts
CHANGED
|
@@ -132,8 +132,6 @@ export type { UseGestureOptions } from "./react/index";
|
|
|
132
132
|
export type { UseKeyPressOptions } from "./react/index";
|
|
133
133
|
export type { UsePullToRefreshOptions } from "./react/index";
|
|
134
134
|
export type { UsePullToRefreshReturn } from "./react/index";
|
|
135
|
-
export type { UseVisibleItemsOptions } from "./react/index";
|
|
136
|
-
export type { UseVisibleItemsResult } from "./react/index";
|
|
137
135
|
export { CONFIRMATION_MESSAGES } from "./values/index";
|
|
138
136
|
export { INFO_MESSAGES } from "./values/index";
|
|
139
137
|
export { deepClone } from "./utils/index";
|
|
@@ -3051,9 +3049,9 @@ export { Button } from "./ui/index";
|
|
|
3051
3049
|
export { Heading, Spinner, IconButton, Progress, IndeterminateProgress, Pagination, PageLoader } from "./ui/index";
|
|
3052
3050
|
export { ModalFooter, UnsavedChangesModal, ResponsiveView, Textarea, Checkbox } from "./ui/index";
|
|
3053
3051
|
export { ToastProvider, StarRating, StepperNav, ViewToggle, PriceDisplay, SummaryCard } from "./ui/index";
|
|
3054
|
-
export { CountdownDisplay, ItemRow, HorizontalScroller
|
|
3052
|
+
export { CountdownDisplay, ItemRow, HorizontalScroller } from "./ui/index";
|
|
3055
3053
|
export { CardBody, MenuTrigger, MenuContent, MenuSeparator, RoleBadge, SkipToMain } from "./ui/index";
|
|
3056
|
-
export { TabsList, TabsContent, PasswordStrengthIndicator, TablePagination } from "./ui/index";
|
|
3054
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, PasswordStrengthIndicator, TablePagination } from "./ui/index";
|
|
3057
3055
|
export { Stack, classNames, mergeTailwindClasses, SideModal, SideDrawer, RichText, RichTextRenderer } from "./ui/index";
|
|
3058
3056
|
export type { RichTextRendererProps } from "./ui/index";
|
|
3059
3057
|
export { SlottedListingView, DetailViewShell, StackedViewShell } from "./ui/index";
|
|
@@ -3067,7 +3065,7 @@ export { API_ENDPOINTS, API_ROUTES, LOGS_ENDPOINTS, AUTH_ENDPOINTS, ACCOUNT_ENDP
|
|
|
3067
3065
|
export { WishlistFullError } from "./features/wishlist/server";
|
|
3068
3066
|
export { useHistory, useHistoryMergeOnLogin, getGuestHistory, trackGuestHistory, removeGuestHistoryItem, clearGuestHistory, getGuestHistoryCount, type GuestHistoryItem, type GuestHistoryType, type UserHistoryItem, type HistoryProductType, type HistoryItemSnapshot, type TrackArgs as TrackHistoryArgs, } from "./features/history";
|
|
3069
3067
|
export { historyRepository, UserHistoryRepository, trackHistoryView, getHistoryForUser, removeHistoryItem, clearHistory, mergeGuestHistory, } from "./features/history/server";
|
|
3070
|
-
export { useMediaQuery, useBreakpoint, useClickOutside, useKeyPress, useLongPress, useGesture, useSwipe, useSession, useAuth, useCountdown, useCamera, useBulkSelection, useUrlTable, usePendingFilters, usePendingTable, useUnsavedChanges, UNSAVED_CHANGES_EVENT, useBulkAction, useEntityDelete, useContainerGrid, useMessage,
|
|
3068
|
+
export { useMediaQuery, useBreakpoint, useClickOutside, useKeyPress, useLongPress, useGesture, useSwipe, useSession, useAuth, useCountdown, useCamera, useBulkSelection, useUrlTable, usePendingFilters, usePendingTable, useUnsavedChanges, UNSAVED_CHANGES_EVENT, useBulkAction, useEntityDelete, useContainerGrid, useMessage, useModalStack, RTDBPayloadStatus, } from "./react/index";
|
|
3071
3069
|
export { RoleGate, ProtectedRoute, SocialAuthButtons, AuthStatusPanel, LoginForm, RegisterForm, ForgotPasswordView, ResetPasswordView, VerifyEmailView, OAuthLoadingView, } from "./features/auth/index";
|
|
3072
3070
|
export type { RoleGateProps, ProtectedRouteProps, AuthGuardUser, AuthRouteConfig, LoginFormProps, LoginFormValues, RegisterFormProps, RegisterFormValues, ForgotPasswordViewProps, ResetPasswordViewProps, VerifyEmailViewProps, } from "./features/auth/index";
|
|
3073
3071
|
export { CartItemRow, CartDrawer, CartView, CartSummary, ShippingPicker } from "./features/cart/index";
|
package/dist/index.js
CHANGED
|
@@ -5468,9 +5468,9 @@ export { Button } from "./ui/index";
|
|
|
5468
5468
|
export { Heading, Spinner, IconButton, Progress, IndeterminateProgress, Pagination, PageLoader } from "./ui/index";
|
|
5469
5469
|
export { ModalFooter, UnsavedChangesModal, ResponsiveView, Textarea, Checkbox } from "./ui/index";
|
|
5470
5470
|
export { ToastProvider, StarRating, StepperNav, ViewToggle, PriceDisplay, SummaryCard } from "./ui/index";
|
|
5471
|
-
export { CountdownDisplay, ItemRow, HorizontalScroller
|
|
5471
|
+
export { CountdownDisplay, ItemRow, HorizontalScroller } from "./ui/index";
|
|
5472
5472
|
export { CardBody, MenuTrigger, MenuContent, MenuSeparator, RoleBadge, SkipToMain } from "./ui/index";
|
|
5473
|
-
export { TabsList, TabsContent, PasswordStrengthIndicator, TablePagination } from "./ui/index";
|
|
5473
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, PasswordStrengthIndicator, TablePagination } from "./ui/index";
|
|
5474
5474
|
export { Stack, classNames, mergeTailwindClasses, SideModal, SideDrawer, RichText, RichTextRenderer } from "./ui/index";
|
|
5475
5475
|
export { SlottedListingView, DetailViewShell, StackedViewShell } from "./ui/index";
|
|
5476
5476
|
export { buildColumns, createColumnBuilder } from "./ui/index";
|
|
@@ -5486,7 +5486,7 @@ export { WishlistFullError } from "./features/wishlist/server";
|
|
|
5486
5486
|
export { useHistory, useHistoryMergeOnLogin, getGuestHistory, trackGuestHistory, removeGuestHistoryItem, clearGuestHistory, getGuestHistoryCount, } from "./features/history";
|
|
5487
5487
|
export { historyRepository, UserHistoryRepository, trackHistoryView, getHistoryForUser, removeHistoryItem, clearHistory, mergeGuestHistory, } from "./features/history/server";
|
|
5488
5488
|
// Missing React hooks and providers
|
|
5489
|
-
export { useMediaQuery, useBreakpoint, useClickOutside, useKeyPress, useLongPress, useGesture, useSwipe, useSession, useAuth, useCountdown, useCamera, useBulkSelection, useUrlTable, usePendingFilters, usePendingTable, useUnsavedChanges, UNSAVED_CHANGES_EVENT, useBulkAction, useEntityDelete, useContainerGrid, useMessage,
|
|
5489
|
+
export { useMediaQuery, useBreakpoint, useClickOutside, useKeyPress, useLongPress, useGesture, useSwipe, useSession, useAuth, useCountdown, useCamera, useBulkSelection, useUrlTable, usePendingFilters, usePendingTable, useUnsavedChanges, UNSAVED_CHANGES_EVENT, useBulkAction, useEntityDelete, useContainerGrid, useMessage, useModalStack, RTDBPayloadStatus, } from "./react/index";
|
|
5490
5490
|
// Missing core types (already exported above; skipped to avoid duplicates)
|
|
5491
5491
|
// Missing auth feature components
|
|
5492
5492
|
export { RoleGate, ProtectedRoute, SocialAuthButtons, AuthStatusPanel, LoginForm, RegisterForm, ForgotPasswordView, ResetPasswordView, VerifyEmailView, OAuthLoadingView, } from "./features/auth/index";
|
package/dist/react/index.d.ts
CHANGED
|
@@ -46,8 +46,6 @@ export type { BulkActionPayload, BulkActionResult, BulkActionSummary, BulkAction
|
|
|
46
46
|
export { useContainerGrid } from "./hooks/useContainerGrid";
|
|
47
47
|
export { useMessage } from "./hooks/useMessage";
|
|
48
48
|
export type { UseContainerGridOptions, UseContainerGridResult, } from "./hooks/useContainerGrid";
|
|
49
|
-
export { useVisibleItems } from "./hooks/useVisibleItems";
|
|
50
|
-
export type { UseVisibleItemsOptions, UseVisibleItemsResult, } from "./hooks/useVisibleItems";
|
|
51
49
|
export { useModalStack } from "./useModalStack";
|
|
52
50
|
export type { ModalEntry } from "./useModalStack";
|
|
53
51
|
export { ErrorBoundary } from "./ErrorBoundary";
|
package/dist/react/index.js
CHANGED
|
@@ -46,8 +46,6 @@ export { useBulkAction } from "./hooks/useBulkAction";
|
|
|
46
46
|
export { useContainerGrid } from "./hooks/useContainerGrid";
|
|
47
47
|
// Success/error message management
|
|
48
48
|
export { useMessage } from "./hooks/useMessage";
|
|
49
|
-
// Visible-items counter for tab strips and overflow scrollers
|
|
50
|
-
export { useVisibleItems } from "./hooks/useVisibleItems";
|
|
51
49
|
// Modal stack management
|
|
52
50
|
export { useModalStack } from "./useModalStack";
|
|
53
51
|
// Error boundary
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* WHY: Seeds
|
|
2
|
+
* WHY: Seeds 63 FAQs for LetItRip collectibles marketplace — covers general platform, shipping,
|
|
3
3
|
* returns, payments, auctions, pre-orders, product info, account/security, scam awareness.
|
|
4
4
|
* WHAT: Mix of broad collectibles marketplace FAQs + category-specific FAQs (trading cards, action figures,
|
|
5
5
|
* diecast, Beyblade, model kits, grading, authenticity, bundles, prize draws), plus (Phase 17,
|
|
@@ -86,7 +86,7 @@ export const faqSeedData = [
|
|
|
86
86
|
question: "How do I become a seller on LetItRip?",
|
|
87
87
|
answer: html(`<p>Go to your Account → Become a Seller. Fill in your store details, upload verification documents, and submit for review. Our team typically approves sellers within 48 hours. Once approved, you can start listing immediately.</p>`),
|
|
88
88
|
category: "general",
|
|
89
|
-
showOnHomepage:
|
|
89
|
+
showOnHomepage: true,
|
|
90
90
|
showInFooter: true,
|
|
91
91
|
isPinned: false,
|
|
92
92
|
order: 4,
|
|
@@ -258,7 +258,7 @@ export const faqSeedData = [
|
|
|
258
258
|
question: "Is free shipping available?",
|
|
259
259
|
answer: html(`<p>Many sellers offer free shipping on orders above a certain amount. Look for the "Free Shipping" badge on product listings. You can also use the FREESHIP999 coupon for free shipping on orders ₹999+.</p>`),
|
|
260
260
|
category: "shipping_delivery",
|
|
261
|
-
showOnHomepage:
|
|
261
|
+
showOnHomepage: true,
|
|
262
262
|
showInFooter: false,
|
|
263
263
|
isPinned: false,
|
|
264
264
|
order: 13,
|
|
@@ -296,7 +296,7 @@ export const faqSeedData = [
|
|
|
296
296
|
question: "How do I track my order?",
|
|
297
297
|
answer: html(`<p>Once your seller ships the order, you'll receive a carrier name and tracking number via email and in-app notification. Go to Account → My Orders → click the order to see it. Paste the tracking number into the carrier's own website (India Post, DTDC, BlueDart, Delhivery, etc.) for real-time updates — sellers ship manually, so tracking is via the carrier's site, not an in-app live map.</p>`),
|
|
298
298
|
category: "shipping_delivery",
|
|
299
|
-
showOnHomepage:
|
|
299
|
+
showOnHomepage: true,
|
|
300
300
|
showInFooter: false,
|
|
301
301
|
isPinned: false,
|
|
302
302
|
order: 15,
|
|
@@ -373,7 +373,7 @@ export const faqSeedData = [
|
|
|
373
373
|
question: "How do I request a refund?",
|
|
374
374
|
answer: html(`<p>Go to Account → My Orders → select the order → Request Refund. Describe the issue and upload photos if the item is damaged or not as described. The seller has 48 hours to respond. If they don't, LetItRip support will step in.</p>`),
|
|
375
375
|
category: "returns_refunds",
|
|
376
|
-
showOnHomepage:
|
|
376
|
+
showOnHomepage: true,
|
|
377
377
|
showInFooter: false,
|
|
378
378
|
isPinned: false,
|
|
379
379
|
order: 19,
|
|
@@ -449,7 +449,7 @@ export const faqSeedData = [
|
|
|
449
449
|
question: "What if I receive a counterfeit item?",
|
|
450
450
|
answer: html(`<p>LetItRip has a <strong>zero-tolerance counterfeit policy</strong>. If you receive a counterfeit item, open a dispute with photo evidence. You'll receive a full refund and the seller's account will be reviewed for suspension. For high-value items, we recommend buying graded/certified products which come with third-party authentication.</p>`),
|
|
451
451
|
category: "returns_refunds",
|
|
452
|
-
showOnHomepage:
|
|
452
|
+
showOnHomepage: true,
|
|
453
453
|
showInFooter: true,
|
|
454
454
|
isPinned: false,
|
|
455
455
|
order: 23,
|
|
@@ -488,7 +488,7 @@ export const faqSeedData = [
|
|
|
488
488
|
question: "What payment methods are accepted?",
|
|
489
489
|
answer: html(`<p>LetItRip's default payment method is <strong>Manual UPI / Bank Transfer</strong> — you pay directly and submit your payment reference, which the seller or LetItRip confirms before shipping. Where enabled, you can also pay instantly via <strong>Razorpay</strong> (cards, UPI, net banking), choose <strong>Cash on Delivery</strong> (a small deposit online plus cash on arrival), or spread the cost with <strong>EMI</strong> on eligible orders. Sellers only receive payout funds after you confirm delivery (or after the dispute window closes).</p>`),
|
|
490
490
|
category: "orders_payment",
|
|
491
|
-
showOnHomepage:
|
|
491
|
+
showOnHomepage: true,
|
|
492
492
|
showInFooter: true,
|
|
493
493
|
isPinned: false,
|
|
494
494
|
order: 25,
|
|
@@ -660,7 +660,7 @@ export const faqSeedData = [
|
|
|
660
660
|
question: "How can I verify if a collectible is authentic?",
|
|
661
661
|
answer: html(`<p>Look for these trust signals on LetItRip: <strong>Verified Seller</strong> badge (store has passed manual review), <strong>Graded</strong> badge (PSA/BGS/CGC certified with certificate number), <strong>Sealed</strong> badge (factory-sealed product). For ungraded items, check the seller's photo quality and description detail. If in doubt, message the seller before purchasing.</p>`),
|
|
662
662
|
category: "product_information",
|
|
663
|
-
showOnHomepage:
|
|
663
|
+
showOnHomepage: true,
|
|
664
664
|
showInFooter: false,
|
|
665
665
|
isPinned: false,
|
|
666
666
|
order: 32,
|
|
@@ -717,7 +717,7 @@ export const faqSeedData = [
|
|
|
717
717
|
question: "What do condition grades like NM, LP, MP mean?",
|
|
718
718
|
answer: html(`<p>Sellers use standard condition grades for ungraded items: <strong>NM</strong> (Near Mint) — virtually perfect, minimal handling wear. <strong>LP</strong> (Lightly Played) — minor edge wear, small scratches. <strong>MP</strong> (Moderately Played) — visible wear, light creasing. <strong>HP</strong> (Heavily Played) — significant wear, creases, whitening. <strong>Sealed</strong> — factory sealed, never opened. Always check the seller's photos for the actual condition.</p>`),
|
|
719
719
|
category: "product_information",
|
|
720
|
-
showOnHomepage:
|
|
720
|
+
showOnHomepage: true,
|
|
721
721
|
showInFooter: false,
|
|
722
722
|
isPinned: false,
|
|
723
723
|
order: 35,
|
|
@@ -813,7 +813,7 @@ export const faqSeedData = [
|
|
|
813
813
|
question: "How does bidding work on LetItRip?",
|
|
814
814
|
answer: html(`<p>Place a bid on any active auction listing. You'll be notified if you're outbid. When the auction ends, the highest bidder wins. Some auctions have a Buy It Now (BIN) price — you can skip bidding and purchase immediately at the BIN price. Auctions auto-extend by 2 minutes if a bid is placed in the final 2 minutes to prevent sniping.</p>`),
|
|
815
815
|
category: "general",
|
|
816
|
-
showOnHomepage:
|
|
816
|
+
showOnHomepage: true,
|
|
817
817
|
showInFooter: true,
|
|
818
818
|
isPinned: false,
|
|
819
819
|
order: 40,
|
|
@@ -909,7 +909,7 @@ export const faqSeedData = [
|
|
|
909
909
|
question: "How do pre-orders work?",
|
|
910
910
|
answer: html(`<p>Pre-orders let you reserve upcoming products with a deposit (typically 20–30% of the retail price). When the product releases and ships, you pay the remaining balance. If the seller cancels the pre-order, you receive a full refund of your deposit. Pre-orders are available for upcoming trading card sets, action figure releases, and model kit drops.</p>`),
|
|
911
911
|
category: "general",
|
|
912
|
-
showOnHomepage:
|
|
912
|
+
showOnHomepage: true,
|
|
913
913
|
showInFooter: false,
|
|
914
914
|
isPinned: false,
|
|
915
915
|
order: 45,
|
|
@@ -301,7 +301,7 @@ export const homepageSectionsSeedData = [
|
|
|
301
301
|
title: "Frequently Asked Questions",
|
|
302
302
|
subtitle: "Quick answers about buying, selling, and collecting on LetItRip",
|
|
303
303
|
showOnHomepage: true,
|
|
304
|
-
displayCount:
|
|
304
|
+
displayCount: 16,
|
|
305
305
|
linkToFullPage: true,
|
|
306
306
|
showCategoryTabs: true,
|
|
307
307
|
visibleTabs: ["general", "shipping_delivery", "returns_refunds", "orders_payment", "product_information", "account_security"],
|