@mohasinac/appkit 4.69.1 → 4.71.0
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/PROVIDERS.md +190 -0
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/lottery/LotteryAdminSlotView.js +2 -1
- package/dist/_internal/server/features/checkout/actions.js +43 -8
- package/dist/_internal/server/features/orders/actions.js +2 -1
- package/dist/_internal/server/features/orders/adapters.js +18 -0
- package/dist/_internal/server/features/orders/balance.js +2 -1
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +0 -89
- package/dist/_internal/server/features/orders/cancellation-refund.js +3 -2
- package/dist/_internal/server/features/payouts/actions.js +2 -1
- package/dist/_internal/server/features/points/award.js +3 -2
- package/dist/_internal/server/features/protection-claims/actions.js +2 -1
- package/dist/_internal/server/features/refunds/actions.js +3 -2
- package/dist/_internal/server/features/shipping/courier-block.js +5 -2
- package/dist/_internal/server/features/shipping/reconcile.js +3 -2
- package/dist/_internal/server/features/shipping/reimbursement.js +3 -2
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +12 -2
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +7 -3
- package/dist/_internal/server/jobs/core/emiInstallmentReminder.js +2 -1
- package/dist/_internal/server/jobs/core/obligationSweep.js +5 -4
- package/dist/_internal/server/jobs/core/onScamReportCreate.js +2 -1
- package/dist/_internal/server/jobs/core/paymentReconcile.js +5 -4
- package/dist/_internal/shared/checkout/rules/auction.rule.js +25 -7
- package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +26 -8
- package/dist/_internal/shared/config/schema.d.ts +15 -0
- package/dist/_internal/shared/content/published-values.d.ts +2 -2
- package/dist/_internal/shared/content/published-values.js +31 -12
- package/dist/_internal/shared/features/auctions/errors.js +2 -1
- package/dist/_internal/shared/features/orders/refund-copy.js +3 -2
- package/dist/_internal/shared/features/promotions/errors.js +2 -1
- package/dist/constants/field-names.d.ts +23 -0
- package/dist/constants/field-names.js +23 -0
- package/dist/constants/storage-keys.d.ts +90 -0
- package/dist/constants/storage-keys.js +119 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/payment.d.ts +42 -0
- package/dist/contracts/registry.d.ts +8 -0
- package/dist/contracts/tax.d.ts +118 -0
- package/dist/contracts/tax.js +3 -0
- package/dist/core/brand-resolver.d.ts +92 -0
- package/dist/core/brand-resolver.js +120 -0
- package/dist/core/brand-text.d.ts +15 -0
- package/dist/core/brand-text.js +73 -0
- package/dist/core/hooks/useNewsletterSubscribe.d.ts +30 -0
- package/dist/core/hooks/useNewsletterSubscribe.js +121 -0
- package/dist/features/about/components/DeveloperView.js +2 -1
- package/dist/features/about/index.d.ts +0 -2
- package/dist/features/about/index.js +0 -1
- package/dist/features/account/components/BuyerAccountGuideView.js +2 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -3
- package/dist/features/account/components/BuyerOrdersGuideView.js +6 -5
- package/dist/features/account/components/BuyerShoppingGuideView.js +3 -2
- package/dist/features/account/components/UserOffersPanel.js +2 -1
- package/dist/features/account/hooks/useCollapsedSections.js +5 -3
- package/dist/features/account/schemas/index.js +1 -1
- package/dist/features/admin/actions/notification-actions.js +9 -2
- package/dist/features/admin/components/AdminAnalyticsGuideView.js +2 -1
- package/dist/features/admin/components/AdminBundlesView.js +2 -1
- package/dist/features/admin/components/AdminCatalogGuideView.js +2 -1
- package/dist/features/admin/components/AdminContentGuideView.js +2 -1
- package/dist/features/admin/components/AdminFaqEditorView.js +2 -1
- package/dist/features/admin/components/AdminGatewayEventsView.js +2 -1
- package/dist/features/admin/components/AdminGuideHubView.js +2 -1
- package/dist/features/admin/components/AdminMoneyCasesView.js +2 -1
- package/dist/features/admin/components/AdminOrdersGuideView.js +2 -1
- package/dist/features/admin/components/AdminProductsView.js +3 -2
- package/dist/features/admin/components/AdminSectionsView.js +3 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +8 -7
- package/dist/features/admin/components/AdminStoresGuideView.js +2 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +2 -1
- package/dist/features/admin/components/AdminTeamGuideView.js +2 -1
- package/dist/features/admin/components/AdminTrustGuideView.js +3 -2
- package/dist/features/admin/components/AdminUsersGuideView.js +2 -1
- package/dist/features/admin/components/sections/adminSectionsTypes.js +1 -1
- package/dist/features/admin/schemas/firestore.js +0 -1
- package/dist/features/auth/actions/user-slug.js +21 -2
- package/dist/features/auth/hooks/useAuth.js +11 -4
- package/dist/features/cart/utils/pending-ops.js +16 -3
- package/dist/features/catalogue/utils/product-from-item.d.ts +18 -2
- package/dist/features/catalogue/utils/product-from-item.js +23 -4
- package/dist/features/categories/components/BundleItemsPicker.js +2 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +2 -1
- package/dist/features/categories/components/ContextualTrail.d.ts +9 -2
- package/dist/features/categories/components/ContextualTrail.js +11 -3
- package/dist/features/categories/components/LastCategoryBeacon.js +2 -2
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +9 -1
- package/dist/features/contact/email.js +11 -12
- package/dist/features/email/notification-templates.js +4 -5
- package/dist/features/email/render-static-markup.d.ts +34 -0
- package/dist/features/email/render-static-markup.js +4 -0
- package/dist/features/events/utils/event-charts.js +2 -1
- package/dist/features/homepage/components/AnnouncementBar.js +5 -3
- package/dist/features/homepage/components/FAQSection.js +1 -1
- package/dist/features/homepage/components/MarketplaceHomepageView.js +34 -0
- package/dist/features/homepage/components/WelcomeSection.d.ts +3 -1
- package/dist/features/homepage/components/WelcomeSection.js +2 -2
- package/dist/features/homepage/lib/section-renderer.d.ts +8 -0
- package/dist/features/homepage/lib/section-renderer.js +1 -1
- package/dist/features/layout/AppLayoutShell.d.ts +3 -1
- package/dist/features/layout/AppLayoutShell.js +2 -2
- package/dist/features/layout/TitleBarLayout.d.ts +8 -1
- package/dist/features/layout/TitleBarLayout.js +2 -2
- package/dist/features/media/MediaVideo.js +2 -1
- package/dist/features/orders/actions/refund-actions.js +3 -2
- package/dist/features/orders/components/RefundRequestView.js +15 -2
- package/dist/features/orders/repository/orders.repository.js +2 -1
- package/dist/features/orders/types/index.d.ts +14 -0
- package/dist/features/orders/utils/payment-proof-message.js +5 -2
- package/dist/features/price-index/components/PriceIndexToolView.js +6 -5
- package/dist/features/pricing/components/AdminPricingView.js +2 -1
- package/dist/features/print/components/labels.js +2 -1
- package/dist/features/products/components/CityCollectiblesView.js +2 -1
- package/dist/features/products/components/MakeOfferButton.js +2 -1
- package/dist/features/products/components/PrizeDrawCollage.js +3 -2
- package/dist/features/products/components/PrizeRevealModal.js +2 -1
- package/dist/features/products/repository/products.repository.js +2 -1
- package/dist/features/promotions/components/CouponCard.js +3 -2
- package/dist/features/promotions/components/CouponHelpDetails.js +2 -1
- package/dist/features/promotions/constants/coupon-help.d.ts +1 -1
- package/dist/features/promotions/constants/coupon-help.js +8 -1
- package/dist/features/scams/components/ScamAwarenessModal.js +2 -1
- package/dist/features/scams/components/ScamProfileView.js +5 -3
- package/dist/features/scams/components/ScamRegistryView.js +2 -1
- package/dist/features/scams/constants/scam-types.d.ts +13 -1
- package/dist/features/scams/constants/scam-types.js +67 -23
- package/dist/features/scams/schemas/firestore.js +1 -1
- package/dist/features/search/components/Search.js +5 -1
- package/dist/features/seller/actions/offer-actions.js +47 -9
- package/dist/features/seller/components/DispatchForm.js +3 -2
- package/dist/features/seller/components/PrintCenterView.js +4 -2
- package/dist/features/seller/components/SellerBundlesView.js +2 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -2
- package/dist/features/seller/components/SellerProductShell.js +3 -2
- package/dist/features/seller/components/SellerProductsView.js +4 -3
- package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +30 -4
- package/dist/features/store-extensions/repository/store-extensions.repositories.js +46 -7
- package/dist/features/store-extensions/schemas/firestore.d.ts +22 -3
- package/dist/features/store-extensions/schemas/firestore.js +20 -0
- package/dist/features/stores/components/StoreCapabilitiesGuideView.js +7 -6
- package/dist/features/stores/components/StoreFinanceGuideView.js +6 -5
- package/dist/features/stores/components/StoreGuideHubView.js +2 -1
- package/dist/features/stores/components/StoreListingsGuideView.js +2 -1
- package/dist/features/stores/components/StoreOrdersGuideView.js +5 -4
- package/dist/features/stores/components/StoreSettingsGuideView.js +2 -1
- package/dist/features/support/schemas/index.d.ts +2 -2
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.d.ts +1 -1
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +3 -2
- package/dist/features/whatsapp-bot/helpers/whatsapp.js +5 -2
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -6
- package/dist/providers/payment-manual/index.d.ts +11 -0
- package/dist/providers/payment-manual/index.js +13 -0
- package/dist/providers/payment-phonepe/index.d.ts +23 -7
- package/dist/providers/payment-phonepe/index.js +15 -0
- package/dist/providers/payment-razorpay/index.d.ts +13 -0
- package/dist/providers/payment-razorpay/index.js +21 -1
- package/dist/providers/tax-india-gst/index.d.ts +45 -0
- package/dist/providers/tax-india-gst/index.js +78 -0
- package/dist/react/hooks/useScrollToTop.d.ts +5 -0
- package/dist/react/hooks/useScrollToTop.js +70 -0
- package/dist/schemas/registry.d.ts +2 -2
- package/dist/seo/json-ld.js +25 -4
- package/dist/styles.css +20 -0
- package/dist/ui/components/SiteLogo.d.ts +20 -2
- package/dist/ui/components/SiteLogo.js +28 -6
- package/dist/ui/components/SiteMark.d.ts +1 -1
- package/dist/ui/components/SiteMark.js +21 -6
- package/dist/ui/components/Tabs.d.ts +9 -1
- package/dist/ui/components/Tabs.js +20 -3
- package/dist/ui/components/Tabs.style.css +20 -0
- package/dist/ui/rich-text/RichText.d.ts +0 -12
- package/dist/ui/rich-text/RichText.js +52 -1
- package/dist/utils/date.formatter.d.ts +20 -0
- package/dist/utils/date.formatter.js +25 -0
- package/dist/utils/number.formatter.d.ts +47 -0
- package/dist/utils/number.formatter.js +79 -1
- package/dist/validation/phone.validator.d.ts +0 -1
- package/dist/validation/phone.validator.js +17 -3
- package/package.json +17 -3
- package/dist/features/about/components/AboutView.d.ts +0 -30
- package/dist/features/about/components/AboutView.js +0 -14
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* useNewsletterSubscribe — the one newsletter submit path.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists (A5b)
|
|
6
|
+
*
|
|
7
|
+
* Two consumer newsletter boxes — homepage and footer — had independently
|
|
8
|
+
* hand-written the same submit flow: `useFormShellState(newsletterSubscribeSchema)`,
|
|
9
|
+
* `clearErrors()`, `validate()`, `useApiMutation` posting `{ email, source }`,
|
|
10
|
+
* and an `isPending` label swap. Roughly 25 lines each, identical in substance.
|
|
11
|
+
*
|
|
12
|
+
* 🛑 The argument for merging is not the line count — it is that **both files
|
|
13
|
+
* carried comments describing the SAME TWO BUGS, each fixed separately**:
|
|
14
|
+
*
|
|
15
|
+
* 1. *"the Button carried `type="submit"` AND an `onClick`, while
|
|
16
|
+
* `<Form onSubmit={e => e.preventDefault()}>` swallowed the native
|
|
17
|
+
* submit — so pressing Enter in the email field fired `submit`, not
|
|
18
|
+
* `click`, and nothing at all happened. Only the mouse worked."*
|
|
19
|
+
* 2. *"`<Form schema={…}>` was passed here before but nothing ever ran it —
|
|
20
|
+
* the only guard was `!email.trim()`, so `not-an-email` round-tripped to
|
|
21
|
+
* the server and came back as a toast instead of an inline field error."*
|
|
22
|
+
*
|
|
23
|
+
* That is the Duplication Framework's bug-fix multiplier having already fired
|
|
24
|
+
* twice, which the framework treats as decisive regardless of the Rule of
|
|
25
|
+
* Three. Keyboard submission silently not working is also exactly the class of
|
|
26
|
+
* defect nobody reports — it looks like the user simply did not click.
|
|
27
|
+
*
|
|
28
|
+
* ## 🛑 A HOOK, not a shared component
|
|
29
|
+
*
|
|
30
|
+
* The two boxes differ in seven ways and every one is presentational: layout
|
|
31
|
+
* (inline row vs stacked), the label, the button variant, the pending label
|
|
32
|
+
* ("Subscribing..." vs "…"), the toast wording, and — the real one — what
|
|
33
|
+
* success looks like. The footer REPLACES itself with a thank-you; the
|
|
34
|
+
* homepage keeps the field mounted and adds a `role="status"` line beneath.
|
|
35
|
+
*
|
|
36
|
+
* A single component would need ~7 conditional props, which the framework
|
|
37
|
+
* lists as a reason to KEEP duplication rather than remove it. So the
|
|
38
|
+
* mechanism is shared and the JSX stays at each call site, where the
|
|
39
|
+
* differences actually live.
|
|
40
|
+
*
|
|
41
|
+
* 🛑 `<FormErrorSummary>` is rendered by the CALLER, not here — this is a hook
|
|
42
|
+
* and renders nothing. Both consumers (`HomepageNewsletterForm`,
|
|
43
|
+
* `FooterNewsletterSlot`) mount it beside their own `<Form shellCtx={…}>`,
|
|
44
|
+
* which is the only place it can go: the summary reads the shell context this
|
|
45
|
+
* hook creates and the caller provides.
|
|
46
|
+
*
|
|
47
|
+
* @tag domain:newsletter
|
|
48
|
+
* @tag layer:client
|
|
49
|
+
* @tag access:client
|
|
50
|
+
* @tag sideEffects:network
|
|
51
|
+
*/
|
|
52
|
+
// audit-form-error-summary-ok: a hook renders nothing; both call sites mount
|
|
53
|
+
// <FormErrorSummary> beside their own <Form shellCtx={…}> — see the header.
|
|
54
|
+
import { useCallback, useState } from "react";
|
|
55
|
+
import { useApiMutation } from "../../client/api/useApiMutation.js";
|
|
56
|
+
import { apiClient } from "../../http/ApiClient.js";
|
|
57
|
+
import { normalizeError } from "../../errors/normalize.js";
|
|
58
|
+
import { newsletterSubscribeSchema } from "../../features/admin/schemas/small-forms.js";
|
|
59
|
+
import { useFormShellState } from "../../ui/forms/FormShell.js";
|
|
60
|
+
import { useToast } from "../../ui/components/Toast.js";
|
|
61
|
+
export function useNewsletterSubscribe({ endpoint, source, successToast, errorMessage = "Could not subscribe. Please try again.", onSuccess, }) {
|
|
62
|
+
const [email, setEmail] = useState("");
|
|
63
|
+
const { showToast } = useToast();
|
|
64
|
+
/*
|
|
65
|
+
* State is hoisted here and handed back as `shellCtx` for `<Form shellCtx=…>`
|
|
66
|
+
* rather than left to `<Form>`'s own internal instance.
|
|
67
|
+
*
|
|
68
|
+
* That is load-bearing, not tidiness: it is what lets `submit` live outside
|
|
69
|
+
* the render prop, which is what makes bug 1 above fixable. The caller must
|
|
70
|
+
* pass `shellCtx` to its `<Form>` or the inline field errors land in a
|
|
71
|
+
* different context than the one this hook writes to.
|
|
72
|
+
*/
|
|
73
|
+
const form = useFormShellState(newsletterSubscribeSchema);
|
|
74
|
+
const mutation = useApiMutation({
|
|
75
|
+
errorMessage,
|
|
76
|
+
/*
|
|
77
|
+
* The server sends per-field `issues` and `surfaceError` knows how to
|
|
78
|
+
* apply them — but only when the caller supplies a sink. Without this the
|
|
79
|
+
* paths are parsed off the wire and discarded, and the user sees one
|
|
80
|
+
* generic sentence with no indication of which field.
|
|
81
|
+
*/
|
|
82
|
+
setFieldError: form.setFieldError,
|
|
83
|
+
mutationFn: (payload) => apiClient.post(endpoint, { ...payload, source }),
|
|
84
|
+
onSuccess: () => {
|
|
85
|
+
showToast(successToast, "success");
|
|
86
|
+
setEmail("");
|
|
87
|
+
onSuccess?.();
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
const submit = useCallback(async () => {
|
|
91
|
+
if (mutation.isPending)
|
|
92
|
+
return;
|
|
93
|
+
form.clearErrors();
|
|
94
|
+
/*
|
|
95
|
+
* `validate` runs the schema AND pipes any issues to the inline field
|
|
96
|
+
* errors itself, so there is no bare `safeParse` result to forget to use
|
|
97
|
+
* (`audit-unvalidated-safeparse`). This is bug 2's fix.
|
|
98
|
+
*/
|
|
99
|
+
const parsed = form.validate({ email: email.trim() });
|
|
100
|
+
if (!parsed)
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
await mutation.mutateAsync(parsed);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
void normalizeError(err);
|
|
107
|
+
// Inline on the field as well — the mutation already toasted, and a
|
|
108
|
+
// toast that has faded leaves the user staring at a form with no
|
|
109
|
+
// indication of what went wrong.
|
|
110
|
+
form.setFieldError("email", errorMessage);
|
|
111
|
+
}
|
|
112
|
+
}, [email, errorMessage, form, mutation]);
|
|
113
|
+
return {
|
|
114
|
+
email,
|
|
115
|
+
setEmail,
|
|
116
|
+
submit,
|
|
117
|
+
isPending: mutation.isPending,
|
|
118
|
+
/** Pass to `<Form shellCtx={…}>` — see the note on `form` above. */
|
|
119
|
+
shellCtx: form.shellCtx,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -14,6 +14,7 @@ import { TextLink } from "../../../ui/components/TextLink.js";
|
|
|
14
14
|
import { ROUTES } from "../../../constants/index.js";
|
|
15
15
|
import { PAGE_CONTAINER } from "../../../_internal/shared/styles/page.js";
|
|
16
16
|
import { siteSettingsRepository } from "../../admin/repository/site-settings.repository.js";
|
|
17
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
17
18
|
/**
|
|
18
19
|
* Reuses the existing `siteSettings.aboutContent.teamMembers` /
|
|
19
20
|
* `.milestones` data (already admin-editable via Site Settings → About) —
|
|
@@ -39,5 +40,5 @@ export async function DeveloperView({ labels = {} }) {
|
|
|
39
40
|
});
|
|
40
41
|
}
|
|
41
42
|
const developers = teamMembers.filter((m) => m.isDeveloper);
|
|
42
|
-
return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(ProseHero, { width: "sm", centered: false, subtitleSize: "sm", title: labels.title ?? "Meet the Developer", subtitle: labels.subtitle ??
|
|
43
|
+
return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(ProseHero, { width: "sm", centered: false, subtitleSize: "sm", title: labels.title ?? "Meet the Developer", subtitle: labels.subtitle ?? `The person building ${getBrandName()}, one feature at a time.` }), _jsxs(Div, { className: `${page.container.sm}`, padding: "content-banner", children: [developers.length === 0 ? (_jsx(Text, { variant: "secondary", children: labels.empty ?? "Developer profile coming soon." })) : (_jsx(Stack, { gap: "xl", children: developers.map((dev) => (_jsxs(Stack, { gap: "md", surface: "default", padding: "lg", rounded: "xl", border: "default", children: [_jsxs(Row, { gap: "md", align: "center", children: [_jsx(Avatar, { src: dev.photoUrl, name: dev.name, alt: dev.name, size: "lg" }), _jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, children: dev.name }), _jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: dev.role }), dev.isFounder && _jsx(Badge, { variant: "primary", size: "xs", children: "Founder" }), _jsx(Badge, { variant: "secondary", size: "xs", children: "Developer" })] })] })] }), _jsx(Text, { variant: "secondary", className: "leading-relaxed", children: dev.bio }), dev.githubUrl && (_jsx(Anchor, { href: dev.githubUrl, tone: "brand", size: "sm", underline: "hover", children: "GitHub \u2197" }))] }, dev.name))) })), milestones.length > 0 && (_jsxs(Div, { className: "mt-14 border-t", border: "default", padding: "t-xl", children: [_jsx(Heading, { level: 3, className: "mb-6", color: "muted", size: "sm", weight: "semibold", transform: "uppercase", children: labels.journeyTitle ?? "The Journey" }), _jsx(Stack, { className: "relative border-l-2 border-primary/30 max-w-2xl pl-[2rem]", gap: "lg", children: milestones.map(({ year, text }) => (_jsxs(Div, { className: "relative", children: [_jsx(Div, { className: "absolute -left-10 top-1 w-4 h-4 bg-primary border-2 border-white", rounded: "full" }), _jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: year }), _jsx(Text, { className: "mt-1", children: text })] }, year))) })] })), _jsx(Div, { className: "mt-10", children: _jsx(TextLink, { href: String(ROUTES.PUBLIC.ABOUT), variant: "muted", size: "sm", children: labels.backToTeam ?? "← Meet the whole team" }) })] })] }));
|
|
43
44
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { AboutView } from "./components/AboutView";
|
|
2
|
-
export type { AboutViewProps, AboutHowItem, AboutValueItem, AboutMilestone, AboutTeamMember, } from "./components/AboutView";
|
|
3
1
|
export type { AboutContentDocument } from "./schemas/firestore";
|
|
4
2
|
export { TrackOrderView } from "./components/TrackOrderView";
|
|
5
3
|
export type { TrackOrderViewProps } from "./components/TrackOrderView";
|
|
@@ -8,6 +8,7 @@ import { Row, Stack } from "../../../ui/components/Layout.js";
|
|
|
8
8
|
import { TextLink } from "../../../ui/components/TextLink.js";
|
|
9
9
|
import { ROUTES } from "../../../constants/index.js";
|
|
10
10
|
import { GC } from "../../_guide-cls.js";
|
|
11
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
11
12
|
const SECTIONS = [
|
|
12
13
|
{
|
|
13
14
|
Icon: UserCheck, title: "Your Profile",
|
|
@@ -40,5 +41,5 @@ const SECTIONS = [
|
|
|
40
41
|
];
|
|
41
42
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
42
43
|
export function BuyerAccountGuideView() {
|
|
43
|
-
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: `flex-shrink-0 w-10 h-10 ${GC.iconBg}`, align: "center", justify: "center", rounded: "xl", children: _jsx(UserCheck, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Buyer Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Account & Safety" }),
|
|
44
|
+
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: `flex-shrink-0 w-10 h-10 ${GC.iconBg}`, align: "center", justify: "center", rounded: "xl", children: _jsx(UserCheck, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Buyer Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Account & Safety" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", children: ["Manage your ", getBrandName(), " profile, stay safe from scams, and know how to report problems."] })] }), SECTIONS.map(({ Icon, iconCls, title, content }) => (_jsxs(Section, { className: GC.sectionWrap, children: [_jsxs(Div, { className: GC.sectionHeader, children: [_jsx(Icon, { className: iconCls ?? GC.iconPrimary }), _jsx(Heading, { level: 2, className: GC.sectionTitle, children: title })] }), _jsx(Div, { className: GC.sectionBody, children: content })] }, title)))] }));
|
|
44
45
|
}
|
|
@@ -7,6 +7,7 @@ import { Li, Section, Ul } from "../../../ui/components/Semantic.js";
|
|
|
7
7
|
import { Row, Stack } from "../../../ui/components/Layout.js";
|
|
8
8
|
import { GC } from "../../_guide-cls.js";
|
|
9
9
|
import { resolvePublishedValues } from "../../../_internal/shared/content/published-values.js";
|
|
10
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
10
11
|
/* Phase 9 — one definition of the refund timeline. See the 🛑 at the call site. */
|
|
11
12
|
const { refundBusinessDaysMin: REFUND_DAYS_MIN, refundBusinessDaysMax: REFUND_DAYS_MAX } = resolvePublishedValues();
|
|
12
13
|
const SECTIONS = [
|
|
@@ -16,7 +17,7 @@ const SECTIONS = [
|
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
19
|
Icon: TrendingUp, title: "Placing a Bid",
|
|
19
|
-
content: (_jsxs(_Fragment, { children: [_jsxs(Text, { className: `${GC.textMuted} mb-3`, children: ["Enter your bid amount \u2014 it must be at least the current high bid plus the increment \u2014 and click ", _jsx(Span, { weight: "bold", children: "Place Bid" }), ". You'll be asked to confirm."] }), _jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Outbid notification" }), " \u2014 you'll receive an in-app notification and email if someone outbids you."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Max bid (proxy bidding)" }), " \u2014 enter the maximum you're willing to pay.
|
|
20
|
+
content: (_jsxs(_Fragment, { children: [_jsxs(Text, { className: `${GC.textMuted} mb-3`, children: ["Enter your bid amount \u2014 it must be at least the current high bid plus the increment \u2014 and click ", _jsx(Span, { weight: "bold", children: "Place Bid" }), ". You'll be asked to confirm."] }), _jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Outbid notification" }), " \u2014 you'll receive an in-app notification and email if someone outbids you."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Max bid (proxy bidding)" }), " \u2014 enter the maximum you're willing to pay. ", getBrandName(), " will automatically bid on your behalf up to that amount, in the minimum increments needed to keep you in the lead. Your max bid is never revealed to other bidders."] })] }), _jsxs(Text, { className: `${GC.textMuted} mt-3`, children: [_jsx(Span, { weight: "bold", children: "Example:" }), " Current bid is \u20B9500, increment is \u20B950. You set a max bid of \u20B9800. Your bid is placed at \u20B9550. If someone bids \u20B9600, the system auto-bids \u20B9650 for you \u2014 up to \u20B9800."] })] })),
|
|
20
21
|
},
|
|
21
22
|
{
|
|
22
23
|
Icon: Trophy, title: "Winning an Auction",
|
|
@@ -24,7 +25,7 @@ const SECTIONS = [
|
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
27
|
Icon: AlertTriangle, iconCls: "w-5 h-5 text-amber-500", title: "Bid Retraction Policy",
|
|
27
|
-
content: (_jsxs(Alert, { variant: "warning", children: ["Bids on
|
|
28
|
+
content: (_jsxs(Alert, { variant: "warning", children: ["Bids on ", getBrandName(), " are ", _jsx(Span, { weight: "bold", children: "binding" }), ". Only place a bid if you intend to pay. Retraction requires contacting support with a valid reason. Repeated retractions may result in a temporary restriction on placing new bids."] })),
|
|
28
29
|
},
|
|
29
30
|
{
|
|
30
31
|
Icon: Package, title: "Pre-orders",
|
|
@@ -41,5 +42,5 @@ const SECTIONS = [
|
|
|
41
42
|
];
|
|
42
43
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
43
44
|
export function BuyerAuctionsGuideView() {
|
|
44
|
-
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: `flex-shrink-0 w-10 h-10 ${GC.iconBg}`, align: "center", justify: "center", rounded: "xl", children: _jsx(Gavel, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Buyer Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Auctions & Pre-orders" }),
|
|
45
|
+
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: `flex-shrink-0 w-10 h-10 ${GC.iconBg}`, align: "center", justify: "center", rounded: "xl", children: _jsx(Gavel, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Buyer Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Auctions & Pre-orders" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", children: ["How bidding works, how to win, and everything you need to know about pre-orders on ", getBrandName(), "."] })] }), SECTIONS.map(({ Icon, iconCls, title, content }) => (_jsxs(Section, { className: GC.sectionWrap, children: [_jsxs(Div, { className: GC.sectionHeader, children: [_jsx(Icon, { className: iconCls ?? GC.iconPrimary }), _jsx(Heading, { level: 2, className: GC.sectionTitle, children: title })] }), _jsx(Div, { className: GC.sectionBody, children: content })] }, title)))] }));
|
|
45
46
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { PackageCheck, Truck, Camera, RotateCcw, Clock, Headphones, Scale } from "lucide-react";
|
|
3
3
|
import { Alert } from "../../../ui/components/Alert.js";
|
|
4
4
|
import { Code, Li, Section, Ul } from "../../../ui/components/Semantic.js";
|
|
@@ -6,6 +6,7 @@ import { Div } from "../../../ui/components/Div.js";
|
|
|
6
6
|
import { Heading, Span, Text } from "../../../ui/components/Typography.js";
|
|
7
7
|
import { Row, Stack } from "../../../ui/components/Layout.js";
|
|
8
8
|
import { GC } from "../../_guide-cls.js";
|
|
9
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
9
10
|
// ─── Section data ─────────────────────────────────────────────────────────────
|
|
10
11
|
const ORDER_STATUSES = [
|
|
11
12
|
{ status: "PENDING", desc: "Payment received. Seller has been notified. You can cancel at this stage." },
|
|
@@ -19,7 +20,7 @@ const ORDER_STATUSES = [
|
|
|
19
20
|
const SECTIONS = [
|
|
20
21
|
{
|
|
21
22
|
Icon: PackageCheck, title: "Order Lifecycle",
|
|
22
|
-
content: (_jsxs(Stack, { textSize: "sm", gap: "md", children: [
|
|
23
|
+
content: (_jsxs(Stack, { textSize: "sm", gap: "md", children: [_jsxs(Text, { className: GC.textMuted, children: ["Every order on ", getBrandName(), " moves through these statuses:"] }), _jsx(Stack, { gap: "sm", children: ORDER_STATUSES.map(({ status, desc }) => (_jsxs(Row, { gap: "3", children: [_jsx(Text, { className: "flex-shrink-0 font-mono text-[var(--appkit-color-primary)] w-36", size: "xs", weight: "semibold", children: status }), _jsx(Text, { className: GC.textMuted, children: desc })] }, status))) })] })),
|
|
23
24
|
},
|
|
24
25
|
{
|
|
25
26
|
Icon: Truck, title: "Tracking Your Order",
|
|
@@ -35,7 +36,7 @@ const SECTIONS = [
|
|
|
35
36
|
},
|
|
36
37
|
{
|
|
37
38
|
Icon: Clock, title: "Refund Timeline",
|
|
38
|
-
content: (_jsxs(Stack, { gap: "3", children: [_jsx(Text, { className: GC.textMuted, children: "After the seller accepts your return and confirms receipt of the item:" }), _jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Credit/debit card" }), " \u2014 3\u20137 business days back to your card via Razorpay."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "UPI" }), " \u2014 typically 1\u20132 business days."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Net banking" }), " \u2014 3\u20135 business days."] })] }),
|
|
39
|
+
content: (_jsxs(Stack, { gap: "3", children: [_jsx(Text, { className: GC.textMuted, children: "After the seller accepts your return and confirms receipt of the item:" }), _jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Credit/debit card" }), " \u2014 3\u20137 business days back to your card via Razorpay."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "UPI" }), " \u2014 typically 1\u20132 business days."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Net banking" }), " \u2014 3\u20135 business days."] })] }), _jsxs(Text, { className: GC.textMuted, children: ["Refunds go back to the original payment method. ", getBrandName(), " does not issue platform credits in lieu of a refund unless both parties agree."] })] })),
|
|
39
40
|
},
|
|
40
41
|
{
|
|
41
42
|
Icon: Headphones, title: "Opening a Support Ticket",
|
|
@@ -43,10 +44,10 @@ const SECTIONS = [
|
|
|
43
44
|
},
|
|
44
45
|
{
|
|
45
46
|
Icon: Scale, title: "Dispute Escalation",
|
|
46
|
-
content: (_jsxs(Stack, { gap: "3", children: [_jsxs(Text, { className: GC.textMuted, children: ["If the seller does not respond to your return request within ", _jsx(Span, { weight: "bold", children: "3 days" }), ",
|
|
47
|
+
content: (_jsxs(Stack, { gap: "3", children: [_jsxs(Text, { className: GC.textMuted, children: ["If the seller does not respond to your return request within ", _jsx(Span, { weight: "bold", children: "3 days" }), ", ", getBrandName(), "'s support team steps in to mediate."] }), _jsxs(Text, { className: GC.textMuted, children: ["If mediation cannot resolve the dispute, ", getBrandName(), " issues a ", _jsx(Span, { weight: "bold", children: "full platform-funded refund" }), " to the buyer. The seller's payout for that order is withheld."] }), _jsx(Alert, { variant: "info", children: "All dispute decisions are final. False dispute claims may result in account restrictions. Always provide accurate, photographic evidence when raising a dispute." })] })),
|
|
47
48
|
},
|
|
48
49
|
];
|
|
49
50
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
50
51
|
export function BuyerOrdersGuideView() {
|
|
51
|
-
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: `flex-shrink-0 w-10 h-10 ${GC.iconBg}`, align: "center", justify: "center", rounded: "xl", children: _jsx(PackageCheck, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Buyer Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Orders, Returns & Support" }),
|
|
52
|
+
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: `flex-shrink-0 w-10 h-10 ${GC.iconBg}`, align: "center", justify: "center", rounded: "xl", children: _jsx(PackageCheck, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Buyer Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Orders, Returns & Support" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", children: ["From placing an order to getting a refund \u2014 everything you need to know about managing your orders on ", getBrandName(), "."] })] }), SECTIONS.map(({ Icon, iconCls, title, content }) => (_jsxs(Section, { className: GC.sectionWrap, children: [_jsxs(Div, { className: GC.sectionHeader, children: [_jsx(Icon, { className: iconCls ?? GC.iconPrimary }), _jsx(Heading, { level: 2, className: GC.sectionTitle, children: title })] }), _jsx(Div, { className: GC.sectionBody, children: content })] }, title)))] }));
|
|
52
53
|
}
|
|
@@ -6,6 +6,7 @@ import { Heading, Span, Text } from "../../../ui/components/Typography.js";
|
|
|
6
6
|
import { Li, Section, Ul } from "../../../ui/components/Semantic.js";
|
|
7
7
|
import { Row, Stack } from "../../../ui/components/Layout.js";
|
|
8
8
|
import { GC } from "../../_guide-cls.js";
|
|
9
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
9
10
|
const SECTIONS = [
|
|
10
11
|
{
|
|
11
12
|
Icon: ShoppingBag, title: "Browsing & Search",
|
|
@@ -21,7 +22,7 @@ const SECTIONS = [
|
|
|
21
22
|
},
|
|
22
23
|
{
|
|
23
24
|
Icon: CreditCard, title: "Checkout",
|
|
24
|
-
content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Shipping address" }), " \u2014 enter or select a saved address. All fields required: full name, phone, address line, city, state, pin code."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Payment methods" }), " \u2014 UPI, credit/debit card (Visa, Mastercard, Rupay), net banking \u2014 all processed via Razorpay."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Secure checkout" }), " \u2014
|
|
25
|
+
content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Shipping address" }), " \u2014 enter or select a saved address. All fields required: full name, phone, address line, city, state, pin code."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Payment methods" }), " \u2014 UPI, credit/debit card (Visa, Mastercard, Rupay), net banking \u2014 all processed via Razorpay."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", className: GC.textStrong, children: "Secure checkout" }), " \u2014 ", getBrandName(), " never stores your card details. All payment data is handled by Razorpay's PCI-DSS compliant gateway."] })] })),
|
|
25
26
|
},
|
|
26
27
|
{
|
|
27
28
|
Icon: Tag, title: "Coupons",
|
|
@@ -38,5 +39,5 @@ const SECTIONS = [
|
|
|
38
39
|
];
|
|
39
40
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
40
41
|
export function BuyerShoppingGuideView() {
|
|
41
|
-
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: `flex-shrink-0 w-10 h-10 ${GC.iconBg}`, align: "center", justify: "center", rounded: "xl", children: _jsx(ShoppingBag, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Buyer Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Shopping & Checkout" }),
|
|
42
|
+
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: `flex-shrink-0 w-10 h-10 ${GC.iconBg}`, align: "center", justify: "center", rounded: "xl", children: _jsx(ShoppingBag, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Buyer Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Shopping & Checkout" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", children: ["Everything you need to know about finding, evaluating, and buying collectibles on ", getBrandName(), "."] })] }), SECTIONS.map(({ Icon, title, content }) => (_jsxs(Section, { className: GC.sectionWrap, children: [_jsxs(Div, { className: GC.sectionHeader, children: [_jsx(Icon, { className: GC.iconPrimary }), _jsx(Heading, { level: 2, className: GC.sectionTitle, children: title })] }), _jsx(Div, { className: GC.sectionBody, children: content })] }, title)))] }));
|
|
42
43
|
}
|
|
@@ -16,11 +16,12 @@ import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
|
|
|
16
16
|
import { ACCOUNT_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
17
17
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
18
18
|
import { toUserMessage } from "../../../errors/error-display-map.js";
|
|
19
|
+
import { formatMoney } from "../../../utils/number.formatter.js";
|
|
19
20
|
const CLS_COUNTER_AMOUNT = "text-[length:var(--appkit-text-sm)] font-semibold text-info dark:text-info";
|
|
20
21
|
function formatRupees(amount) {
|
|
21
22
|
if (amount === undefined || amount === null)
|
|
22
23
|
return "—";
|
|
23
|
-
return
|
|
24
|
+
return formatMoney(amount);
|
|
24
25
|
}
|
|
25
26
|
function relativeTime(date) {
|
|
26
27
|
if (!date)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
4
|
+
import { STORAGE_KEYS } from "../../../constants/storage-keys.js";
|
|
4
5
|
import { useCurrentProfile, useUpdateCurrentProfile } from "./useProfile.js";
|
|
5
6
|
const PERSIST_DEBOUNCE_MS = 600;
|
|
6
|
-
const
|
|
7
|
+
// 🛑 Read at USE time, not captured in a const — the namespace is set by the
|
|
8
|
+
// consumer's bootstrap, which runs after this module is evaluated.
|
|
7
9
|
/**
|
|
8
10
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
9
11
|
* Persisted, per-user, per-surface open/closed state for collapsible sections.
|
|
@@ -50,7 +52,7 @@ function readLocalScopes() {
|
|
|
50
52
|
if (typeof window === "undefined")
|
|
51
53
|
return {};
|
|
52
54
|
try {
|
|
53
|
-
const raw = window.localStorage.getItem(
|
|
55
|
+
const raw = window.localStorage.getItem(STORAGE_KEYS.sectionState);
|
|
54
56
|
if (!raw)
|
|
55
57
|
return {};
|
|
56
58
|
const parsed = JSON.parse(raw);
|
|
@@ -82,7 +84,7 @@ function writeLocalScope(scope, openIds) {
|
|
|
82
84
|
try {
|
|
83
85
|
const all = readLocalScopes();
|
|
84
86
|
all[scope] = openIds;
|
|
85
|
-
window.localStorage.setItem(
|
|
87
|
+
window.localStorage.setItem(STORAGE_KEYS.sectionState, JSON.stringify(all));
|
|
86
88
|
// setItem throws on quota exhaustion and in private mode. Same reasoning as
|
|
87
89
|
// readLocalScopes — the debounced profile PATCH is the durable write, so
|
|
88
90
|
// losing the mirror costs one frame of default layout and nothing else.
|
|
@@ -63,7 +63,7 @@ export const updateProfileSchema = z.object({
|
|
|
63
63
|
profileIsPublic: annotate(z.boolean().optional(), {
|
|
64
64
|
section: "visibility", sectionLabel: "Visibility", order: 1, row: "full",
|
|
65
65
|
label: "Public profile",
|
|
66
|
-
help: "When on, your profile is visible to other
|
|
66
|
+
help: "When on, your profile is visible to other members.",
|
|
67
67
|
}),
|
|
68
68
|
});
|
|
69
69
|
/**
|
|
@@ -34,6 +34,7 @@ function absoluteActionUrl(path) {
|
|
|
34
34
|
return base ? `${base}${path}` : path;
|
|
35
35
|
}
|
|
36
36
|
import { DEFAULT_NOTIFICATION_CHANNELS, meetsMinPriority, } from "../schemas/firestore.js";
|
|
37
|
+
import { getBrandName, getBrandNoreplyEmail } from "../../../core/brand-resolver.js";
|
|
37
38
|
/**
|
|
38
39
|
* Notification types whose WhatsApp send is gated behind the SELLER's
|
|
39
40
|
* standing WhatsApp-notifications subscription — see `storeId` on
|
|
@@ -304,8 +305,14 @@ export async function sendNotification(input) {
|
|
|
304
305
|
meetsMinPriority(priority, channels.email.minPriority) &&
|
|
305
306
|
(!channels.email.types?.length || channels.email.types.includes(type))) {
|
|
306
307
|
const apiKey = creds.resendApiKey?.trim() ?? "";
|
|
307
|
-
|
|
308
|
-
|
|
308
|
+
/*
|
|
309
|
+
* 🛑 These were `?? "noreply@letitrip.in"` and `?? "LetItRip"` — not
|
|
310
|
+
* placeholders but live ENVELOPE VALUES, so an install that had not filled
|
|
311
|
+
* in its email settings sent real mail as this marketplace. The admin
|
|
312
|
+
* setting still wins; only the fallback changed owner.
|
|
313
|
+
*/
|
|
314
|
+
const fromEmail = settings.emailSettings?.fromEmail ?? getBrandNoreplyEmail();
|
|
315
|
+
const fromName = settings.emailSettings?.fromName ?? getBrandName();
|
|
309
316
|
if (apiKey && !apiKey.includes("PLACEHOLDER") && (await isChannelHealthy("email"))) {
|
|
310
317
|
try {
|
|
311
318
|
const emailProvider = createResendProvider({ apiKey, fromEmail, fromName });
|
|
@@ -6,8 +6,9 @@ import { Heading, Span, Text } from "../../../ui/components/Typography.js";
|
|
|
6
6
|
import { Li, Section, Ul } from "../../../ui/components/Semantic.js";
|
|
7
7
|
import { Row, Stack } from "../../../ui/components/Layout.js";
|
|
8
8
|
import { GC } from "../../_guide-cls.js";
|
|
9
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
9
10
|
export function AdminAnalyticsGuideView() {
|
|
10
|
-
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(BarChart2, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Analytics" }),
|
|
11
|
+
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(BarChart2, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Analytics" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", children: ["Revenue, order funnel, product performance, and store metrics on ", getBrandName(), "."] })] }), [
|
|
11
12
|
{
|
|
12
13
|
title: "Revenue Dashboard",
|
|
13
14
|
content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "GMV (Gross Merchandise Value)" }), ": Total order subtotals across all stores. Does not deduct platform commission or refunds."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Net Revenue" }), ": GMV \u00D7 platformFee \u2014 refunded orders excluded."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Period filter" }), ": Last 7 / 30 / 90 days, or custom date range. All monetary values shown in INR."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Payout total" }), ": Total disbursed to sellers in the same period. Revenue - Payout = Platform earnings."] })] })),
|
|
@@ -17,10 +17,11 @@ import { DataListingView } from "./DataListingView.js";
|
|
|
17
17
|
import { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "../../../_internal/shared/features/categories/bundle-copy.js";
|
|
18
18
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
|
|
19
19
|
import { ADMIN_BULK_ACTIONS, ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs.js";
|
|
20
|
+
import { formatMoney } from "../../../utils/number.formatter.js";
|
|
20
21
|
function formatPrice(value) {
|
|
21
22
|
if (typeof value !== "number" || value <= 0)
|
|
22
23
|
return "—";
|
|
23
|
-
return
|
|
24
|
+
return formatMoney(value);
|
|
24
25
|
}
|
|
25
26
|
const COLUMNS = [
|
|
26
27
|
{
|
|
@@ -6,9 +6,10 @@ import { Div } from "../../../ui/components/Div.js";
|
|
|
6
6
|
import { Heading, Span, Text } from "../../../ui/components/Typography.js";
|
|
7
7
|
import { Row, Stack } from "../../../ui/components/Layout.js";
|
|
8
8
|
import { GC } from "../../_guide-cls.js";
|
|
9
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
9
10
|
const CLS_CODE = "text-[length:var(--appkit-text-xs)] bg-amber-100 dark:bg-amber-900/30 px-[var(--appkit-space-1)] rounded";
|
|
10
11
|
export function AdminCatalogGuideView() {
|
|
11
|
-
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(Package, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Catalog" }),
|
|
12
|
+
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(Package, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Catalog" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", children: ["Products, categories, brands, and reviews \u2014 how the ", getBrandName(), " catalog is structured and managed."] })] }), [
|
|
12
13
|
{
|
|
13
14
|
Icon: Package, title: "Product Management",
|
|
14
15
|
content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Listing types" }), ": ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "standard" }), " (prefix ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "product-" }), "), ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "auction" }), " (prefix ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "auction-" }), "), ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "pre-order" }), " (prefix ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "preorder-" }), ")."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Admin vs store creation" }), ": Admins can create products on behalf of any store via the store picker in AdminProductEditorView."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Status lifecycle" }), ": DRAFT \u2192 PUBLISHED \u2192 ARCHIVED. Published products appear in search. Archived products are hidden but not deleted."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "J13 rule" }), ": Always use ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "listingType" }), " field \u2014 the legacy ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "isAuction" }), "/", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "isPreOrder" }), " booleans have been removed. All queries use ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "where(\"listingType\", \"==\", x)" }), "."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Media URLs" }), ": Never write raw Firebase Storage URLs to Firestore. All product image URLs must use the ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "/api/media/[slug]" }), " proxy format."] })] })),
|
|
@@ -6,8 +6,9 @@ import { Div } from "../../../ui/components/Div.js";
|
|
|
6
6
|
import { Heading, Span, Text } from "../../../ui/components/Typography.js";
|
|
7
7
|
import { Row, Stack } from "../../../ui/components/Layout.js";
|
|
8
8
|
import { GC } from "../../_guide-cls.js";
|
|
9
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
9
10
|
export function AdminContentGuideView() {
|
|
10
|
-
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(FileText, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Content & Marketing" }),
|
|
11
|
+
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(FileText, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Content & Marketing" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", children: ["Blog posts, events, FAQs, carousel, homepage sections, ads, and newsletter on ", getBrandName(), "."] })] }), [
|
|
11
12
|
{
|
|
12
13
|
Icon: FileText, title: "Blog Posts",
|
|
13
14
|
content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Statuses" }), ": ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "draft" }), " (not public), ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "published" }), " (live, indexed)."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "readTimeMinutes" }), ": Auto-calculated from content word count (200 wpm). Override manually if the post has heavy media."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "youtubeId" }), ": Optional embedded YouTube video. Use only the video ID (e.g. ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "dQw4w9WgXcQ" }), "), not the full URL."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "Media in body" }), ": All images in rich text must use the ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "/api/media/" }), " proxy URL, not raw Firebase Storage links."] }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "isFeatured" }), ": Sets the post as featured in the blog listing. Different from the homepage featured section \u2014 that is driven by a separate ", _jsx(Code, { size: "xs", padding: "xs", rounded: "default", surface: "subtle", children: "homepageSections" }), " config."] })] })),
|
|
@@ -20,6 +20,7 @@ import { SectionForm, useSectionFormNav } from "../../shell/SectionForm.js";
|
|
|
20
20
|
import { apiClient } from "../../../http/index.js";
|
|
21
21
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
22
22
|
import { FAQ_CATEGORY_LABELS } from "../../faq/schemas/firestore.js";
|
|
23
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
23
24
|
/**
|
|
24
25
|
* Matches the draft this form actually holds, and the payload it actually
|
|
25
26
|
* sends.
|
|
@@ -194,7 +195,7 @@ export function AdminFaqEditorView({ faqId, onSaved, onDeleted, embedded, ...res
|
|
|
194
195
|
fields: ["question", "slug", "answer"],
|
|
195
196
|
// The rich-text editor holds an uncommitted buffer.
|
|
196
197
|
keepMounted: true,
|
|
197
|
-
render: ({ values: v, onChange, errors }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FieldInput, { name: "question", label: "Question", value: String(v.question ?? ""), onChange: (val) => onChange({ question: val }), required: true, placeholder:
|
|
198
|
+
render: ({ values: v, onChange, errors }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FieldInput, { name: "question", label: "Question", value: String(v.question ?? ""), onChange: (val) => onChange({ question: val }), required: true, placeholder: `e.g. How does bidding work on ${getBrandName()}?`, error: errors.question }), _jsx(Input, { label: "Slug", value: String(v.slug ?? ""), onChange: (e) => {
|
|
198
199
|
setSlugManual(true);
|
|
199
200
|
onChange({ slug: e.target.value });
|
|
200
201
|
}, placeholder: "faq-how-does-bidding-work", helperText: "Auto-generated from the question until you edit it. Must start with 'faq-'.", error: errors.slug }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Answer" }), _jsx(RichTextEditor, { ariaLabel: "Answer", value: String(v.answer ?? ""), onChange: (val) => onChange({ answer: val }), placeholder: "Write a clear, helpful answer...", minHeightClassName: "min-h-[200px]" }), errors.answer && (_jsx(Text, { size: "xs", color: "error", role: "alert", children: errors.answer }))] })] })),
|
|
@@ -63,6 +63,7 @@ import { Button } from "../../../ui/components/Button.js";
|
|
|
63
63
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
64
64
|
import { ADMIN_GATEWAY_EVENT_KIND_TABS, ADMIN_GATEWAY_EVENT_STATUS_TABS, } from "../constants/filter-tabs.js";
|
|
65
65
|
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData.js";
|
|
66
|
+
import { formatMoney } from "../../../utils/number.formatter.js";
|
|
66
67
|
import { DataListingView } from "./DataListingView.js";
|
|
67
68
|
/** Operator-facing status names — see the 🛑 about `ignored` and `conflict`. */
|
|
68
69
|
const STATUS_LABEL = {
|
|
@@ -194,7 +195,7 @@ export function AdminGatewayEventsView({ children, ...props }) {
|
|
|
194
195
|
{ label: "Body SHA-256", value: toStringValue(detail.bodySha256, "") },
|
|
195
196
|
{ label: "Gateway order", value: toStringValue(detail.gatewayOrderId, "") || "—" },
|
|
196
197
|
{ label: "Gateway payment", value: toStringValue(detail.gatewayPaymentId, "") || "—" },
|
|
197
|
-
{ label: "Amount", value: typeof detail.amount === "number" ?
|
|
198
|
+
{ label: "Amount", value: typeof detail.amount === "number" ? formatMoney(detail.amount) : "—" },
|
|
198
199
|
{ label: "Orders placed", value: Array.isArray(detail.orderIds) ? detail.orderIds.join(", ") : "—" },
|
|
199
200
|
{ label: "Received", value: toStringValue(detail.receivedAt, "") },
|
|
200
201
|
{ label: "Settled", value: toStringValue(detail.processedAt, "") || "— not settled" },
|
|
@@ -7,6 +7,7 @@ import { Heading, Text } from "../../../ui/components/Typography.js";
|
|
|
7
7
|
import { Section } from "../../../ui/components/Semantic.js";
|
|
8
8
|
import { TextLink } from "../../../ui/components/TextLink.js";
|
|
9
9
|
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
10
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
10
11
|
const CLS_WARN_PANEL = "rounded-2xl border border-amber-200 bg-warning-surface dark:border-amber-800 p-[var(--appkit-space-6)]";
|
|
11
12
|
const CLS_WARN_HEAD = "font-semibold text-amber-800 dark:text-amber-300 mb-1";
|
|
12
13
|
const CLS_WARN_BODY = "text-[length:var(--appkit-text-sm)] text-amber-700 dark:text-amber-400";
|
|
@@ -86,5 +87,5 @@ export function AdminGuideHubView({ permissions = [], isFullAdmin = false }) {
|
|
|
86
87
|
const hasPermission = (perm) => isFullAdmin || permissions.includes(perm);
|
|
87
88
|
const visibleCards = GUIDE_CARDS.filter((c) => hasPermission(c.permission));
|
|
88
89
|
const hasAny = visibleCards.length > 0;
|
|
89
|
-
return (_jsxs(Stack, { gap: "xl", padding: "b-2xl", children: [_jsx(Section, { overflow: "hidden", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "2xl", shadow: "sm", children: _jsxs(Div, { paddingX: "x-md-xl", paddingY: "y-xl", className: "[background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_60%,var(--appkit-color-secondary-400)_100%)]", children: [_jsxs(Row, { className: "mb-3", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10", surface: "default", align: "center", justify: "center", rounded: "xl", children: _jsx(BookOpen, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { color: "inverse-muted", className: "tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin & Employee Guide" })] }),
|
|
90
|
+
return (_jsxs(Stack, { gap: "xl", padding: "b-2xl", children: [_jsx(Section, { overflow: "hidden", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "2xl", shadow: "sm", children: _jsxs(Div, { paddingX: "x-md-xl", paddingY: "y-xl", className: "[background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_60%,var(--appkit-color-secondary-400)_100%)]", children: [_jsxs(Row, { className: "mb-3", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10", surface: "default", align: "center", justify: "center", rounded: "xl", children: _jsx(BookOpen, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { color: "inverse-muted", className: "tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin & Employee Guide" })] }), _jsxs(Heading, { color: "inverse", level: 1, className: "mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: [getBrandName(), " Internal Guide"] }), _jsxs(Text, { color: "inverse-muted", size: "base", children: ["Everything you need to operate and manage ", getBrandName(), " \u2014 India's largest collectibles marketplace."] })] }) }), _jsx(Alert, { variant: "info", compact: true, children: "These guides describe expected platform behaviour. Real access to each section is enforced independently by the permission system \u2014 this page is reference only." }), !hasAny && (_jsx(Alert, { variant: "info", title: "Limited access", children: "Your permission set doesn't include read access to any admin sections yet. Contact your admin to request the permissions you need." })), hasAny && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "text-[var(--appkit-color-text)] mb-4", size: "lg", weight: "semibold", children: "Guides" }), _jsx(Grid, { cols: "cards", gap: "md", children: visibleCards.map(({ Icon, title, description, href }) => (_jsxs(TextLink, { variant: "bare", href: href, rounded: "xl", layout: "flex-col", gap: "md", paddingX: "lg", paddingY: "lg", shadow: "hover-md", className: "group border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] hover:border-[var(--appkit-color-primary)] transition-all", children: [_jsxs(Row, { align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-9 h-9 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "lg", children: _jsx(Icon, { className: "w-4 h-4 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text)] group-hover:text-[var(--appkit-color-primary)] transition-colors", weight: "semibold", children: title })] }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] leading-relaxed", size: "sm", children: description }), _jsx(Text, { className: "text-[var(--appkit-color-primary)] mt-auto", size: "xs", weight: "medium", children: "Read guide \u2192" })] }, title))) })] })), _jsx(Section, { className: CLS_WARN_PANEL, children: _jsxs(Row, { align: "start", gap: "3", children: [_jsx(AlertTriangle, { className: "w-5 h-5 text-warning flex-shrink-0 mt-0.5" }), _jsxs(Div, { children: [_jsx(Text, { className: CLS_WARN_HEAD, children: "Remember: with great access comes responsibility" }), _jsx(Text, { className: CLS_WARN_BODY, children: "Admin actions are logged. Never access user PII (email, phone) without a legitimate support reason. Never grant capabilities or permissions without documented approval. When in doubt, escalate to a senior admin." })] })] }) })] }));
|
|
90
91
|
}
|
|
@@ -68,6 +68,7 @@ import { Text } from "../../../ui/components/Typography.js";
|
|
|
68
68
|
import { RecordStatusTimeline } from "../../status-history/components/RecordStatusTimeline.js";
|
|
69
69
|
import { ReviewDecisionModal } from "../../store-extensions/components/ReviewDecisionModal.js";
|
|
70
70
|
import { paymentCaseResolveFormSchema, } from "../../payment-cases/schemas/resolve-form.js";
|
|
71
|
+
import { formatMoney } from "../../../utils/number.formatter.js";
|
|
71
72
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
72
73
|
import { ADMIN_PAYMENT_CASE_KIND_TABS, ADMIN_PAYMENT_CASE_SEVERITY_TABS, ADMIN_PAYMENT_CASE_STATUS_TABS, } from "../constants/filter-tabs.js";
|
|
73
74
|
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData.js";
|
|
@@ -95,7 +96,7 @@ const KIND_LABEL = {
|
|
|
95
96
|
};
|
|
96
97
|
/** Rupees, for a row. Plain `toFixed` — these are already decimal rupees. */
|
|
97
98
|
function money(value) {
|
|
98
|
-
return typeof value === "number" ?
|
|
99
|
+
return typeof value === "number" ? formatMoney(value) : null;
|
|
99
100
|
}
|
|
100
101
|
/**
|
|
101
102
|
* Narrow a JSON value to status-history entries.
|
|
@@ -6,6 +6,7 @@ import { Heading, Span, Text } from "../../../ui/components/Typography.js";
|
|
|
6
6
|
import { Row, Stack } from "../../../ui/components/Layout.js";
|
|
7
7
|
import { GC } from "../../_guide-cls.js";
|
|
8
8
|
import { resolvePublishedValues } from "../../../_internal/shared/content/published-values.js";
|
|
9
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
9
10
|
/*
|
|
10
11
|
* Phase 9 — read from the published-value registry rather than restated.
|
|
11
12
|
*
|
|
@@ -16,7 +17,7 @@ import { resolvePublishedValues } from "../../../_internal/shared/content/publis
|
|
|
16
17
|
*/
|
|
17
18
|
const { returnRespondHours: RETURN_RESPOND_HOURS, refundBusinessDaysMin: REFUND_DAYS_MIN, refundBusinessDaysMax: REFUND_DAYS_MAX, } = resolvePublishedValues();
|
|
18
19
|
export function AdminOrdersGuideView() {
|
|
19
|
-
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(ShoppingBag, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Orders & Finance" }),
|
|
20
|
+
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(ShoppingBag, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Orders & Finance" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", children: ["Order statuses, payouts, disputes, returns, and commission math on ", getBrandName(), "."] })] }), [
|
|
20
21
|
{
|
|
21
22
|
Icon: ShoppingBag, title: "Order Statuses",
|
|
22
23
|
content: (_jsxs(Stack, { textSize: "sm", gap: "sm", children: [[
|