@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.
Files changed (182) hide show
  1. package/PROVIDERS.md +190 -0
  2. package/dist/.build-stamp.json +3 -3
  3. package/dist/_internal/client/features/lottery/LotteryAdminSlotView.js +2 -1
  4. package/dist/_internal/server/features/checkout/actions.js +43 -8
  5. package/dist/_internal/server/features/orders/actions.js +2 -1
  6. package/dist/_internal/server/features/orders/adapters.js +18 -0
  7. package/dist/_internal/server/features/orders/balance.js +2 -1
  8. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +0 -89
  9. package/dist/_internal/server/features/orders/cancellation-refund.js +3 -2
  10. package/dist/_internal/server/features/payouts/actions.js +2 -1
  11. package/dist/_internal/server/features/points/award.js +3 -2
  12. package/dist/_internal/server/features/protection-claims/actions.js +2 -1
  13. package/dist/_internal/server/features/refunds/actions.js +3 -2
  14. package/dist/_internal/server/features/shipping/courier-block.js +5 -2
  15. package/dist/_internal/server/features/shipping/reconcile.js +3 -2
  16. package/dist/_internal/server/features/shipping/reimbursement.js +3 -2
  17. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  18. package/dist/_internal/server/features/stores/adapters.js +12 -2
  19. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +7 -3
  20. package/dist/_internal/server/jobs/core/emiInstallmentReminder.js +2 -1
  21. package/dist/_internal/server/jobs/core/obligationSweep.js +5 -4
  22. package/dist/_internal/server/jobs/core/onScamReportCreate.js +2 -1
  23. package/dist/_internal/server/jobs/core/paymentReconcile.js +5 -4
  24. package/dist/_internal/shared/checkout/rules/auction.rule.js +25 -7
  25. package/dist/_internal/shared/checkout/rules/prize-draw.rule.js +26 -8
  26. package/dist/_internal/shared/config/schema.d.ts +15 -0
  27. package/dist/_internal/shared/content/published-values.d.ts +2 -2
  28. package/dist/_internal/shared/content/published-values.js +31 -12
  29. package/dist/_internal/shared/features/auctions/errors.js +2 -1
  30. package/dist/_internal/shared/features/orders/refund-copy.js +3 -2
  31. package/dist/_internal/shared/features/promotions/errors.js +2 -1
  32. package/dist/constants/field-names.d.ts +23 -0
  33. package/dist/constants/field-names.js +23 -0
  34. package/dist/constants/storage-keys.d.ts +90 -0
  35. package/dist/constants/storage-keys.js +119 -0
  36. package/dist/contracts/index.d.ts +1 -1
  37. package/dist/contracts/payment.d.ts +42 -0
  38. package/dist/contracts/registry.d.ts +8 -0
  39. package/dist/contracts/tax.d.ts +118 -0
  40. package/dist/contracts/tax.js +3 -0
  41. package/dist/core/brand-resolver.d.ts +92 -0
  42. package/dist/core/brand-resolver.js +120 -0
  43. package/dist/core/brand-text.d.ts +15 -0
  44. package/dist/core/brand-text.js +73 -0
  45. package/dist/core/hooks/useNewsletterSubscribe.d.ts +30 -0
  46. package/dist/core/hooks/useNewsletterSubscribe.js +121 -0
  47. package/dist/features/about/components/DeveloperView.js +2 -1
  48. package/dist/features/about/index.d.ts +0 -2
  49. package/dist/features/about/index.js +0 -1
  50. package/dist/features/account/components/BuyerAccountGuideView.js +2 -1
  51. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -3
  52. package/dist/features/account/components/BuyerOrdersGuideView.js +6 -5
  53. package/dist/features/account/components/BuyerShoppingGuideView.js +3 -2
  54. package/dist/features/account/components/UserOffersPanel.js +2 -1
  55. package/dist/features/account/hooks/useCollapsedSections.js +5 -3
  56. package/dist/features/account/schemas/index.js +1 -1
  57. package/dist/features/admin/actions/notification-actions.js +9 -2
  58. package/dist/features/admin/components/AdminAnalyticsGuideView.js +2 -1
  59. package/dist/features/admin/components/AdminBundlesView.js +2 -1
  60. package/dist/features/admin/components/AdminCatalogGuideView.js +2 -1
  61. package/dist/features/admin/components/AdminContentGuideView.js +2 -1
  62. package/dist/features/admin/components/AdminFaqEditorView.js +2 -1
  63. package/dist/features/admin/components/AdminGatewayEventsView.js +2 -1
  64. package/dist/features/admin/components/AdminGuideHubView.js +2 -1
  65. package/dist/features/admin/components/AdminMoneyCasesView.js +2 -1
  66. package/dist/features/admin/components/AdminOrdersGuideView.js +2 -1
  67. package/dist/features/admin/components/AdminProductsView.js +3 -2
  68. package/dist/features/admin/components/AdminSectionsView.js +3 -2
  69. package/dist/features/admin/components/AdminSiteSettingsView.js +8 -7
  70. package/dist/features/admin/components/AdminStoresGuideView.js +2 -1
  71. package/dist/features/admin/components/AdminSupportTicketDetailView.js +2 -1
  72. package/dist/features/admin/components/AdminTeamGuideView.js +2 -1
  73. package/dist/features/admin/components/AdminTrustGuideView.js +3 -2
  74. package/dist/features/admin/components/AdminUsersGuideView.js +2 -1
  75. package/dist/features/admin/components/sections/adminSectionsTypes.js +1 -1
  76. package/dist/features/admin/schemas/firestore.js +0 -1
  77. package/dist/features/auth/actions/user-slug.js +21 -2
  78. package/dist/features/auth/hooks/useAuth.js +11 -4
  79. package/dist/features/cart/utils/pending-ops.js +16 -3
  80. package/dist/features/catalogue/utils/product-from-item.d.ts +18 -2
  81. package/dist/features/catalogue/utils/product-from-item.js +23 -4
  82. package/dist/features/categories/components/BundleItemsPicker.js +2 -1
  83. package/dist/features/categories/components/CategoryDetailPageView.js +2 -1
  84. package/dist/features/categories/components/ContextualTrail.d.ts +9 -2
  85. package/dist/features/categories/components/ContextualTrail.js +11 -3
  86. package/dist/features/categories/components/LastCategoryBeacon.js +2 -2
  87. package/dist/features/checkout/actions/checkout-value-otp-actions.js +9 -1
  88. package/dist/features/contact/email.js +11 -12
  89. package/dist/features/email/notification-templates.js +4 -5
  90. package/dist/features/email/render-static-markup.d.ts +34 -0
  91. package/dist/features/email/render-static-markup.js +4 -0
  92. package/dist/features/events/utils/event-charts.js +2 -1
  93. package/dist/features/homepage/components/AnnouncementBar.js +5 -3
  94. package/dist/features/homepage/components/FAQSection.js +1 -1
  95. package/dist/features/homepage/components/MarketplaceHomepageView.js +34 -0
  96. package/dist/features/homepage/components/WelcomeSection.d.ts +3 -1
  97. package/dist/features/homepage/components/WelcomeSection.js +2 -2
  98. package/dist/features/homepage/lib/section-renderer.d.ts +8 -0
  99. package/dist/features/homepage/lib/section-renderer.js +1 -1
  100. package/dist/features/layout/AppLayoutShell.d.ts +3 -1
  101. package/dist/features/layout/AppLayoutShell.js +2 -2
  102. package/dist/features/layout/TitleBarLayout.d.ts +8 -1
  103. package/dist/features/layout/TitleBarLayout.js +2 -2
  104. package/dist/features/media/MediaVideo.js +2 -1
  105. package/dist/features/orders/actions/refund-actions.js +3 -2
  106. package/dist/features/orders/components/RefundRequestView.js +15 -2
  107. package/dist/features/orders/repository/orders.repository.js +2 -1
  108. package/dist/features/orders/types/index.d.ts +14 -0
  109. package/dist/features/orders/utils/payment-proof-message.js +5 -2
  110. package/dist/features/price-index/components/PriceIndexToolView.js +6 -5
  111. package/dist/features/pricing/components/AdminPricingView.js +2 -1
  112. package/dist/features/print/components/labels.js +2 -1
  113. package/dist/features/products/components/CityCollectiblesView.js +2 -1
  114. package/dist/features/products/components/MakeOfferButton.js +2 -1
  115. package/dist/features/products/components/PrizeDrawCollage.js +3 -2
  116. package/dist/features/products/components/PrizeRevealModal.js +2 -1
  117. package/dist/features/products/repository/products.repository.js +2 -1
  118. package/dist/features/promotions/components/CouponCard.js +3 -2
  119. package/dist/features/promotions/components/CouponHelpDetails.js +2 -1
  120. package/dist/features/promotions/constants/coupon-help.d.ts +1 -1
  121. package/dist/features/promotions/constants/coupon-help.js +8 -1
  122. package/dist/features/scams/components/ScamAwarenessModal.js +2 -1
  123. package/dist/features/scams/components/ScamProfileView.js +5 -3
  124. package/dist/features/scams/components/ScamRegistryView.js +2 -1
  125. package/dist/features/scams/constants/scam-types.d.ts +13 -1
  126. package/dist/features/scams/constants/scam-types.js +67 -23
  127. package/dist/features/scams/schemas/firestore.js +1 -1
  128. package/dist/features/search/components/Search.js +5 -1
  129. package/dist/features/seller/actions/offer-actions.js +47 -9
  130. package/dist/features/seller/components/DispatchForm.js +3 -2
  131. package/dist/features/seller/components/PrintCenterView.js +4 -2
  132. package/dist/features/seller/components/SellerBundlesView.js +2 -1
  133. package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -2
  134. package/dist/features/seller/components/SellerProductShell.js +3 -2
  135. package/dist/features/seller/components/SellerProductsView.js +4 -3
  136. package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +30 -4
  137. package/dist/features/store-extensions/repository/store-extensions.repositories.js +46 -7
  138. package/dist/features/store-extensions/schemas/firestore.d.ts +22 -3
  139. package/dist/features/store-extensions/schemas/firestore.js +20 -0
  140. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +7 -6
  141. package/dist/features/stores/components/StoreFinanceGuideView.js +6 -5
  142. package/dist/features/stores/components/StoreGuideHubView.js +2 -1
  143. package/dist/features/stores/components/StoreListingsGuideView.js +2 -1
  144. package/dist/features/stores/components/StoreOrdersGuideView.js +5 -4
  145. package/dist/features/stores/components/StoreSettingsGuideView.js +2 -1
  146. package/dist/features/support/schemas/index.d.ts +2 -2
  147. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.d.ts +1 -1
  148. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +3 -2
  149. package/dist/features/whatsapp-bot/helpers/whatsapp.js +5 -2
  150. package/dist/index.d.ts +0 -3
  151. package/dist/index.js +0 -6
  152. package/dist/providers/payment-manual/index.d.ts +11 -0
  153. package/dist/providers/payment-manual/index.js +13 -0
  154. package/dist/providers/payment-phonepe/index.d.ts +23 -7
  155. package/dist/providers/payment-phonepe/index.js +15 -0
  156. package/dist/providers/payment-razorpay/index.d.ts +13 -0
  157. package/dist/providers/payment-razorpay/index.js +21 -1
  158. package/dist/providers/tax-india-gst/index.d.ts +45 -0
  159. package/dist/providers/tax-india-gst/index.js +78 -0
  160. package/dist/react/hooks/useScrollToTop.d.ts +5 -0
  161. package/dist/react/hooks/useScrollToTop.js +70 -0
  162. package/dist/schemas/registry.d.ts +2 -2
  163. package/dist/seo/json-ld.js +25 -4
  164. package/dist/styles.css +20 -0
  165. package/dist/ui/components/SiteLogo.d.ts +20 -2
  166. package/dist/ui/components/SiteLogo.js +28 -6
  167. package/dist/ui/components/SiteMark.d.ts +1 -1
  168. package/dist/ui/components/SiteMark.js +21 -6
  169. package/dist/ui/components/Tabs.d.ts +9 -1
  170. package/dist/ui/components/Tabs.js +20 -3
  171. package/dist/ui/components/Tabs.style.css +20 -0
  172. package/dist/ui/rich-text/RichText.d.ts +0 -12
  173. package/dist/ui/rich-text/RichText.js +52 -1
  174. package/dist/utils/date.formatter.d.ts +20 -0
  175. package/dist/utils/date.formatter.js +25 -0
  176. package/dist/utils/number.formatter.d.ts +47 -0
  177. package/dist/utils/number.formatter.js +79 -1
  178. package/dist/validation/phone.validator.d.ts +0 -1
  179. package/dist/validation/phone.validator.js +17 -3
  180. package/package.json +17 -3
  181. package/dist/features/about/components/AboutView.d.ts +0 -30
  182. 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 ?? "The person building LetItRip, 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
+ 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";
@@ -1,4 +1,3 @@
1
- export { AboutView } from "./components/AboutView.js";
2
1
  export { TrackOrderView } from "./components/TrackOrderView.js";
3
2
  /*
4
3
  * The shared prose-page banner. Exported because it is the one thing all
@@ -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" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Manage your LetItRip 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
+ 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. LetItRip 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
+ 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 LetItRip 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
+ 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" }), _jsx(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 LetItRip." })] }), 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
+ 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 { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
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: [_jsx(Text, { className: GC.textMuted, children: "Every order on LetItRip 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
+ 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."] })] }), _jsx(Text, { className: GC.textMuted, children: "Refunds go back to the original payment method. LetItRip does not issue platform credits in lieu of a refund unless both parties agree." })] })),
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" }), ", LetItRip's support team steps in to mediate."] }), _jsxs(Text, { className: GC.textMuted, children: ["If mediation cannot resolve the dispute, LetItRip 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
+ 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" }), _jsx(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 LetItRip." })] }), 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
+ 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 LetItRip never stores your card details. All payment data is handled by Razorpay's PCI-DSS compliant gateway."] })] })),
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" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Everything you need to know about finding, evaluating, and buying collectibles on LetItRip." })] }), 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
+ 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 `₹${amount.toLocaleString("en-IN")}`;
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 LOCAL_KEY = "letitrip:section-state";
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(LOCAL_KEY);
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(LOCAL_KEY, JSON.stringify(all));
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 LetItRip users.",
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
- const fromEmail = settings.emailSettings?.fromEmail ?? "noreply@letitrip.in";
308
- const fromName = settings.emailSettings?.fromName ?? "LetItRip";
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" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Revenue, order funnel, product performance, and store metrics on LetItRip." })] }), [
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 `₹${value.toLocaleString("en-IN")}`;
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" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Products, categories, brands, and reviews \u2014 how the LetItRip catalog is structured and managed." })] }), [
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" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Blog posts, events, FAQs, carousel, homepage sections, ads, and newsletter on LetItRip." })] }), [
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: "e.g. How does bidding work on LetItRip?", error: errors.question }), _jsx(Input, { label: "Slug", value: String(v.slug ?? ""), onChange: (e) => {
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" ? `₹${detail.amount.toFixed(2)}` : "—" },
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" })] }), _jsx(Heading, { color: "inverse", level: 1, className: "mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "LetItRip Internal Guide" }), _jsx(Text, { color: "inverse-muted", size: "base", children: "Everything you need to operate and manage LetItRip \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
+ 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" ? `₹${value.toFixed(2)}` : null;
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" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Order statuses, payouts, disputes, returns, and commission math on LetItRip." })] }), [
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: [[