@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
|
@@ -13,7 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
*
|
|
14
14
|
* 1. Almost no small seller will create a Meta app and mint a system user
|
|
15
15
|
* token. A setting nobody can complete is a feature nobody has.
|
|
16
|
-
* 2. It made
|
|
16
|
+
* 2. It made {getBrandName()} the custodian of a live Meta credential per seller.
|
|
17
17
|
* `StoreRepository.mapDoc` decrypted that token on EVERY read, so any
|
|
18
18
|
* code path that forgot to project was handling plaintext — a gap
|
|
19
19
|
* CLAUDE.md flags as deserving its own session.
|
|
@@ -48,6 +48,7 @@ import { Toggle } from "../../../ui/components/Toggle.js";
|
|
|
48
48
|
import { useToast } from "../../../ui/components/toast-context.js";
|
|
49
49
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
50
50
|
import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
51
|
+
import { getBrandName } from "../../../core/brand-resolver.js";
|
|
51
52
|
export function SellerWhatsAppSettingsView({ subscribed, monthlyFee, currency = "INR", channelEnabled, onToggleSubscription, hasCapability = true, }) {
|
|
52
53
|
const [isOn, setIsOn] = useState(subscribed);
|
|
53
54
|
const [saving, setSaving] = useState(false);
|
|
@@ -79,5 +80,5 @@ export function SellerWhatsAppSettingsView({ subscribed, monthlyFee, currency =
|
|
|
79
80
|
setSaving(false);
|
|
80
81
|
}
|
|
81
82
|
};
|
|
82
|
-
return (_jsx(Section, { children: _jsxs(Div, { className: "mx-auto max-w-2xl", children: [_jsx(Heading, { level: 1, className: "mb-1", color: "primary", size: "2xl", weight: "bold", children: "WhatsApp order updates" }),
|
|
83
|
+
return (_jsx(Section, { children: _jsxs(Div, { className: "mx-auto max-w-2xl", children: [_jsx(Heading, { level: 1, className: "mb-1", color: "primary", size: "2xl", weight: "bold", children: "WhatsApp order updates" }), _jsxs(Text, { variant: "secondary", className: "mb-6", size: "sm", children: ["Send your buyers order updates on WhatsApp \u2014 confirmed, shipped, delivered \u2014 from", getBrandName(), "'s verified business number. You don't need a WhatsApp Business account of your own, and nothing is installed on your phone."] }), !channelEnabled && (_jsx(Alert, { variant: "warning", className: "mb-4", children: "WhatsApp updates are currently switched off platform-wide, so nothing will be sent even if you subscribe. We'll start sending as soon as it's back on." })), _jsxs(Stack, { gap: "md", surface: "card", padding: "md", children: [_jsxs(Row, { justify: "between", align: "center", gap: "md", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Subscription" }), _jsx(Badge, { variant: isOn ? "success" : "inactive", children: isOn ? "Active" : "Not subscribed" })] }), _jsxs(Text, { color: "muted", size: "sm", children: [formatCurrency(monthlyFee, currency), " per month. Covers every order update to every buyer \u2014 not charged per message."] })] }), _jsx(Toggle, { checked: isOn, onChange: handleToggle, disabled: saving || !hasCapability, size: "md" })] }), !hasCapability && (_jsx(Div, { textSize: "sm", className: "border border-warning/20", color: "warning", surface: "warning-surface", padding: "inlineSm", rounded: "lg", children: "Your store doesn't have this add-on enabled. Contact support to turn it on." }))] }), _jsx(Heading, { level: 2, className: "mt-8 mb-3", size: "lg", weight: "semibold", children: "What your buyers get" }), _jsxs(Stack, { gap: "sm", surface: "card", padding: "md", children: [_jsxs(Text, { color: "muted", size: "sm", children: ["A WhatsApp message when an order is ", _jsx(Span, { weight: "semibold", children: "confirmed" }), ",", " ", _jsx(Span, { weight: "semibold", children: "shipped" }), ", ", _jsx(Span, { weight: "semibold", children: "delivered" }), ",", " ", _jsx(Span, { weight: "semibold", children: "cancelled" }), " or ", _jsx(Span, { weight: "semibold", children: "refunded" }), ". Nothing promotional \u2014 buyers who opted out of WhatsApp never receive these."] }), _jsxs(Text, { color: "muted", size: "sm", children: ["Messages come from ", getBrandName(), "'s number, not yours, so your personal number stays private and replies land with our support team rather than on your phone."] }), _jsx(Text, { color: "muted", size: "sm", children: "Turning this off changes nothing else: buyers keep getting the same updates by email and in their account." })] })] }) }));
|
|
83
84
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
2
2
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
3
3
|
import { getDefaultCurrencySymbol, getDefaultLocale, } from "../../../core/baseline-resolver.js";
|
|
4
|
+
import { formatMoney } from "../../../utils/number.formatter.js";
|
|
4
5
|
const META_GRAPH_BASE = "https://graph.facebook.com/v20.0";
|
|
5
6
|
export function buildCheckoutMessageURL(input) {
|
|
6
7
|
const { waNumber, cart, total, address, isPreorder = false } = input;
|
|
@@ -241,8 +242,10 @@ export async function syncProductsToCatalog(input) {
|
|
|
241
242
|
export function buildPurchaseAnnouncementMessage(input) {
|
|
242
243
|
const { buyerName, firstItemName, additionalItemCount, totalAmount, orderId } = input;
|
|
243
244
|
const extra = additionalItemCount > 0 ? ` + ${additionalItemCount} more item${additionalItemCount > 1 ? "s" : ""}` : "";
|
|
244
|
-
|
|
245
|
-
|
|
245
|
+
// `formatMoney` groups and places the symbol from the market profile; the
|
|
246
|
+
// intermediate `toLocaleString("en-IN")` this replaced hardcoded the locale.
|
|
247
|
+
const amount = formatMoney(totalAmount);
|
|
248
|
+
return `🛍️ New order! ${buyerName} purchased ${firstItemName}${extra} for ${amount}. Order #${orderId}`;
|
|
246
249
|
}
|
|
247
250
|
/**
|
|
248
251
|
* Build a wa.me share link with a pre-filled message.
|
package/dist/index.d.ts
CHANGED
|
@@ -286,7 +286,6 @@ export { isNumeric } from "./validation/input.validator";
|
|
|
286
286
|
export { isRequired } from "./validation/input.validator";
|
|
287
287
|
export { isValidEmail } from "./validation/email.validator";
|
|
288
288
|
export { isValidEmailDomain } from "./validation/email.validator";
|
|
289
|
-
export { isValidIndianPincode } from "./validation/phone.validator";
|
|
290
289
|
export { isValidMobile } from "./validation/phone.validator";
|
|
291
290
|
export { isValidPhone } from "./validation/phone.validator";
|
|
292
291
|
export { isValidUrl } from "./validation/url.validator";
|
|
@@ -753,7 +752,6 @@ export { validateFileSize } from "./providers/storage-firebase/client";
|
|
|
753
752
|
export { validateFileType } from "./providers/storage-firebase/client";
|
|
754
753
|
export { validateImage } from "./providers/storage-firebase/client";
|
|
755
754
|
export type { StorageHelpers } from "./providers/storage-firebase/client";
|
|
756
|
-
export { AboutView } from "./features/about/components/AboutView";
|
|
757
755
|
export { FAQPageView } from "./features/about/components/FAQPageView";
|
|
758
756
|
export { FeesView } from "./features/about/components/FeesView";
|
|
759
757
|
export { HelpPageView } from "./features/about/components/HelpPageView";
|
|
@@ -774,7 +772,6 @@ export { TrackOrderView } from "./features/about/components/TrackOrderView";
|
|
|
774
772
|
export type { AboutHowItem } from "./features/about/schemas/firestore";
|
|
775
773
|
export type { AboutMilestone } from "./features/about/schemas/firestore";
|
|
776
774
|
export type { AboutValueItem } from "./features/about/schemas/firestore";
|
|
777
|
-
export type { AboutViewProps } from "./features/about/components/AboutView";
|
|
778
775
|
export type { AboutTeamMember } from "./features/about/schemas/firestore";
|
|
779
776
|
export type { AboutContentDocument } from "./features/about/schemas/firestore";
|
|
780
777
|
export type { FAQPageViewProps } from "./features/about/components/FAQPageView";
|
package/dist/index.js
CHANGED
|
@@ -447,8 +447,6 @@ export { isValidEmail } from "./validation/email.validator.js";
|
|
|
447
447
|
// isValidEmailDomain - Shared export for is valid email domain.
|
|
448
448
|
export { isValidEmailDomain } from "./validation/email.validator.js";
|
|
449
449
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
450
|
-
// isValidIndianPincode - Shared export for is valid indian pincode.
|
|
451
|
-
export { isValidIndianPincode } from "./validation/phone.validator.js";
|
|
452
450
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
453
451
|
// isValidMobile - Shared export for is valid mobile.
|
|
454
452
|
export { isValidMobile } from "./validation/phone.validator.js";
|
|
@@ -1580,10 +1578,6 @@ export { validateFileType } from "./providers/storage-firebase/client.js";
|
|
|
1580
1578
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
1581
1579
|
// validateImage - Helper for validate image.
|
|
1582
1580
|
export { validateImage } from "./providers/storage-firebase/client.js";
|
|
1583
|
-
// ./features/about/index
|
|
1584
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
1585
|
-
// AboutView - Component for about view.
|
|
1586
|
-
export { AboutView } from "./features/about/components/AboutView.js";
|
|
1587
1581
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
1588
1582
|
// FAQPageView - Component for faqpage view.
|
|
1589
1583
|
export { FAQPageView } from "./features/about/components/FAQPageView.js";
|
|
@@ -25,6 +25,17 @@ export declare class ManualPaymentProvider extends IPaymentProvider {
|
|
|
25
25
|
createOrder(amount: number, currency: string, metadata?: Record<string, JsonValue>): Promise<PaymentOrder>;
|
|
26
26
|
/** Manual payments have no webhook source — always false. */
|
|
27
27
|
verifyWebhook(_payload: string, _signature: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Manual payment has no gateway callback to verify, so there is nothing that
|
|
30
|
+
* could make this true.
|
|
31
|
+
*
|
|
32
|
+
* 🛑 `false`, never `true`. This is the check that decides whether a payment
|
|
33
|
+
* is genuine; a provider that cannot verify must REJECT, not wave through.
|
|
34
|
+
* Returning `true` here would mean a forged gateway callback is accepted on
|
|
35
|
+
* any install where the gateway flag is off — which is exactly the install
|
|
36
|
+
* where nobody is watching for one.
|
|
37
|
+
*/
|
|
38
|
+
verifyCheckoutSignature(_payload: Record<string, string>, _secret: string): boolean;
|
|
28
39
|
/**
|
|
29
40
|
* Manual payments are considered captured the moment the buyer's
|
|
30
41
|
* reference/proof is recorded — there is no separate auth+capture step.
|
|
@@ -40,6 +40,19 @@ export class ManualPaymentProvider extends IPaymentProvider {
|
|
|
40
40
|
verifyWebhook(_payload, _signature) {
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Manual payment has no gateway callback to verify, so there is nothing that
|
|
45
|
+
* could make this true.
|
|
46
|
+
*
|
|
47
|
+
* 🛑 `false`, never `true`. This is the check that decides whether a payment
|
|
48
|
+
* is genuine; a provider that cannot verify must REJECT, not wave through.
|
|
49
|
+
* Returning `true` here would mean a forged gateway callback is accepted on
|
|
50
|
+
* any install where the gateway flag is off — which is exactly the install
|
|
51
|
+
* where nobody is watching for one.
|
|
52
|
+
*/
|
|
53
|
+
verifyCheckoutSignature(_payload, _secret) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
43
56
|
/**
|
|
44
57
|
* Manual payments are considered captured the moment the buyer's
|
|
45
58
|
* reference/proof is recorded — there is no separate auth+capture step.
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
*/
|
|
39
39
|
import type { JsonValue } from "@mohasinac/appkit";
|
|
40
40
|
import { IPaymentProvider } from "../../contracts";
|
|
41
|
-
import type { PaymentOrder, PaymentCapture, Refund, Dispute, GatewayPaymentRecord } from "../../contracts";
|
|
41
|
+
import type { CheckoutIntentMetadata, PaymentOrder, PaymentCapture, Refund, Dispute, GatewayPaymentRecord } from "../../contracts";
|
|
42
42
|
/**
|
|
43
43
|
* PhonePe's `metaInfo` (udf1-udf5, ≤256 chars each) is the only channel that
|
|
44
44
|
* survives from `createOrder()` through to BOTH the Order Status API AND the
|
|
@@ -48,12 +48,15 @@ import type { PaymentOrder, PaymentCapture, Refund, Dispute, GatewayPaymentRecor
|
|
|
48
48
|
* the tab mid-payment. See `confirmAndPlacePhonePeOrderAction` in
|
|
49
49
|
* `_internal/server/features/checkout/actions.ts`.
|
|
50
50
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
/**
|
|
52
|
+
* @deprecated Use `CheckoutIntentMetadata` from `contracts/payment`.
|
|
53
|
+
*
|
|
54
|
+
* Kept as an alias so existing imports keep compiling. Every field is ours —
|
|
55
|
+
* the only PhonePe-specific thing was the name, and it forced generic checkout
|
|
56
|
+
* code to import this provider for a TYPE (A4). PhonePe carries it in
|
|
57
|
+
* `udf1..udf4`; that is a packing detail, not a definition.
|
|
58
|
+
*/
|
|
59
|
+
export type PhonePeCheckoutIntent = CheckoutIntentMetadata;
|
|
57
60
|
export interface PhonePeConfig {
|
|
58
61
|
clientId: string;
|
|
59
62
|
clientSecret: string;
|
|
@@ -97,6 +100,19 @@ export declare class PhonePeProvider extends IPaymentProvider {
|
|
|
97
100
|
* the existing webhook route already does.
|
|
98
101
|
*/
|
|
99
102
|
verifyWebhook(payload: string, signature: string): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* PhonePe has no buyer-present SIGNED callback of this shape.
|
|
105
|
+
*
|
|
106
|
+
* Its redirect carries no signature — confirmation comes from the Order
|
|
107
|
+
* Status API or the webhook (`verifyWebhook` above, which does the real
|
|
108
|
+
* `validateCallback`). So there is nothing here to verify, and the honest
|
|
109
|
+
* answer is rejection.
|
|
110
|
+
*
|
|
111
|
+
* 🛑 `false`, never `true`. "No signature to check" and "the signature is
|
|
112
|
+
* good" must never collapse into the same return value on the call that
|
|
113
|
+
* decides whether a payment is genuine.
|
|
114
|
+
*/
|
|
115
|
+
verifyCheckoutSignature(_payload: Record<string, string>, _secret: string): boolean;
|
|
100
116
|
/** Wraps the Order Status API. `orderId` here is our `merchantOrderId`. */
|
|
101
117
|
capturePayment(orderId: string): Promise<PaymentCapture>;
|
|
102
118
|
/**
|
|
@@ -158,6 +158,21 @@ export class PhonePeProvider extends IPaymentProvider {
|
|
|
158
158
|
return false;
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* PhonePe has no buyer-present SIGNED callback of this shape.
|
|
163
|
+
*
|
|
164
|
+
* Its redirect carries no signature — confirmation comes from the Order
|
|
165
|
+
* Status API or the webhook (`verifyWebhook` above, which does the real
|
|
166
|
+
* `validateCallback`). So there is nothing here to verify, and the honest
|
|
167
|
+
* answer is rejection.
|
|
168
|
+
*
|
|
169
|
+
* 🛑 `false`, never `true`. "No signature to check" and "the signature is
|
|
170
|
+
* good" must never collapse into the same return value on the call that
|
|
171
|
+
* decides whether a payment is genuine.
|
|
172
|
+
*/
|
|
173
|
+
verifyCheckoutSignature(_payload, _secret) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
161
176
|
/** Wraps the Order Status API. `orderId` here is our `merchantOrderId`. */
|
|
162
177
|
async capturePayment(orderId) {
|
|
163
178
|
const status = await this.client.getOrderStatus(orderId, true);
|
|
@@ -89,6 +89,19 @@ export declare class RazorpayProvider extends IPaymentProvider {
|
|
|
89
89
|
* is not guaranteed to reproduce the bytes that were signed.
|
|
90
90
|
*/
|
|
91
91
|
verifyWebhook(payload: string, signature: string): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* The buyer-present callback from `checkout.js`.
|
|
94
|
+
*
|
|
95
|
+
* 🛑 Delegates to the SAME `verifyPaymentSignature` the checkout action
|
|
96
|
+
* called directly before A4b — identical HMAC, identical `hexEqual`
|
|
97
|
+
* comparison, identical `if (!keySecret) return false`. The dispatch moved
|
|
98
|
+
* behind the contract; the arithmetic did not move at all. That is what let
|
|
99
|
+
* this be proven equivalent rather than argued about.
|
|
100
|
+
*
|
|
101
|
+
* The three fields are checked before the cast: a callback missing any of
|
|
102
|
+
* them cannot be verified, and "cannot verify" is rejection.
|
|
103
|
+
*/
|
|
104
|
+
verifyCheckoutSignature(payload: Record<string, string>, secret: string): boolean;
|
|
92
105
|
/**
|
|
93
106
|
* Resolves a `pay_…` id for either id shape.
|
|
94
107
|
*
|
|
@@ -54,7 +54,9 @@ import Razorpay from "razorpay";
|
|
|
54
54
|
import { IPaymentProvider } from "../../contracts/index.js";
|
|
55
55
|
import { getDefaultCurrency } from "../../core/baseline-resolver.js";
|
|
56
56
|
import { paiseToRupees } from "../../core/money.js";
|
|
57
|
-
|
|
57
|
+
// `verifyPaymentSignature` is re-exported below, but a re-export does not bring
|
|
58
|
+
// it into THIS module's scope — `verifyCheckoutSignature` calls it directly.
|
|
59
|
+
import { verifyPaymentSignature, verifyWebhookSignature } from "./signature.js";
|
|
58
60
|
export { verifyPaymentSignature, verifyWebhookSignature, } from "./signature.js";
|
|
59
61
|
/** Razorpay's own order status vocabulary → the contract's. */
|
|
60
62
|
function mapOrderStatus(status) {
|
|
@@ -144,6 +146,24 @@ export class RazorpayProvider extends IPaymentProvider {
|
|
|
144
146
|
return false;
|
|
145
147
|
return verifyWebhookSignature(payload, signature, this.webhookSecret);
|
|
146
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* The buyer-present callback from `checkout.js`.
|
|
151
|
+
*
|
|
152
|
+
* 🛑 Delegates to the SAME `verifyPaymentSignature` the checkout action
|
|
153
|
+
* called directly before A4b — identical HMAC, identical `hexEqual`
|
|
154
|
+
* comparison, identical `if (!keySecret) return false`. The dispatch moved
|
|
155
|
+
* behind the contract; the arithmetic did not move at all. That is what let
|
|
156
|
+
* this be proven equivalent rather than argued about.
|
|
157
|
+
*
|
|
158
|
+
* The three fields are checked before the cast: a callback missing any of
|
|
159
|
+
* them cannot be verified, and "cannot verify" is rejection.
|
|
160
|
+
*/
|
|
161
|
+
verifyCheckoutSignature(payload, secret) {
|
|
162
|
+
const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = payload;
|
|
163
|
+
if (!razorpay_order_id || !razorpay_payment_id || !razorpay_signature)
|
|
164
|
+
return false;
|
|
165
|
+
return verifyPaymentSignature({ razorpay_order_id, razorpay_payment_id, razorpay_signature }, secret);
|
|
166
|
+
}
|
|
147
167
|
/**
|
|
148
168
|
* Resolves a `pay_…` id for either id shape.
|
|
149
169
|
*
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* India GST — the default `ITaxProvider`.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 This DELEGATES. Every figure still comes from `calculateGst` /
|
|
5
|
+
* `calculateGstInclusive`.
|
|
6
|
+
*
|
|
7
|
+
* Not a rewrite, on purpose. Those two functions split the intra-state half
|
|
8
|
+
* differently and the difference is load-bearing:
|
|
9
|
+
*
|
|
10
|
+
* calculateGst cgst = sgst = round(gst / 2); gst RE-DERIVED as half*2
|
|
11
|
+
* calculateGstInclusive cgst = round(gst / 2); sgst = round(gst - cgst)
|
|
12
|
+
*
|
|
13
|
+
* Measured on ₹66.67 at 18% inclusive: cgst 5.09, sgst 5.08 — the remainder
|
|
14
|
+
* lands in sgst so the parts still sum to 10.17. Reimplementing the maths here
|
|
15
|
+
* to make the two agree would move a paisa on figures GST is filed on. The
|
|
16
|
+
* seam's whole value is that it adds an abstraction WITHOUT touching arithmetic.
|
|
17
|
+
*
|
|
18
|
+
* Verified by `.build-memory/tax-roundtrip.mjs` — 397 breakdowns across every
|
|
19
|
+
* slab, both supply directions and the rounding edges, byte-identical.
|
|
20
|
+
*
|
|
21
|
+
* @tag domain:tax
|
|
22
|
+
* @tag layer:provider
|
|
23
|
+
* @tag access:isomorphic
|
|
24
|
+
*/
|
|
25
|
+
import { ITaxProvider, type TaxBreakdown, type TaxJurisdiction, type TaxableItem } from "../../contracts/tax";
|
|
26
|
+
export declare class IndiaGstTaxProvider extends ITaxProvider {
|
|
27
|
+
readonly id = "india-gst";
|
|
28
|
+
/**
|
|
29
|
+
* Intra-state when origin and destination states match.
|
|
30
|
+
*
|
|
31
|
+
* 🛑 Absent information is treated as INTRA-state, which preserves the
|
|
32
|
+
* existing behaviour exactly rather than improving on it. Every current
|
|
33
|
+
* caller passes an `intraState` boolean it computed itself, and the defaults
|
|
34
|
+
* across those call sites resolve to intra-state when a place of supply is
|
|
35
|
+
* unknown — changing that here would alter tax on orders with an incomplete
|
|
36
|
+
* delivery snapshot, which is precisely the class of row most likely to
|
|
37
|
+
* exist. A stricter rule is a product decision, not a side effect of adding
|
|
38
|
+
* an interface.
|
|
39
|
+
*/
|
|
40
|
+
isSameRegion(jurisdiction: TaxJurisdiction): boolean;
|
|
41
|
+
onExclusive(item: TaxableItem, jurisdiction: TaxJurisdiction, taxableAmount: number): TaxBreakdown;
|
|
42
|
+
onInclusive(item: TaxableItem, jurisdiction: TaxJurisdiction, gross: number): TaxBreakdown;
|
|
43
|
+
}
|
|
44
|
+
/** The default instance. A consumer in another jurisdiction registers its own. */
|
|
45
|
+
export declare const indiaGstTaxProvider: IndiaGstTaxProvider;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* India GST — the default `ITaxProvider`.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 This DELEGATES. Every figure still comes from `calculateGst` /
|
|
5
|
+
* `calculateGstInclusive`.
|
|
6
|
+
*
|
|
7
|
+
* Not a rewrite, on purpose. Those two functions split the intra-state half
|
|
8
|
+
* differently and the difference is load-bearing:
|
|
9
|
+
*
|
|
10
|
+
* calculateGst cgst = sgst = round(gst / 2); gst RE-DERIVED as half*2
|
|
11
|
+
* calculateGstInclusive cgst = round(gst / 2); sgst = round(gst - cgst)
|
|
12
|
+
*
|
|
13
|
+
* Measured on ₹66.67 at 18% inclusive: cgst 5.09, sgst 5.08 — the remainder
|
|
14
|
+
* lands in sgst so the parts still sum to 10.17. Reimplementing the maths here
|
|
15
|
+
* to make the two agree would move a paisa on figures GST is filed on. The
|
|
16
|
+
* seam's whole value is that it adds an abstraction WITHOUT touching arithmetic.
|
|
17
|
+
*
|
|
18
|
+
* Verified by `.build-memory/tax-roundtrip.mjs` — 397 breakdowns across every
|
|
19
|
+
* slab, both supply directions and the rounding edges, byte-identical.
|
|
20
|
+
*
|
|
21
|
+
* @tag domain:tax
|
|
22
|
+
* @tag layer:provider
|
|
23
|
+
* @tag access:isomorphic
|
|
24
|
+
*/
|
|
25
|
+
import { ITaxProvider, } from "../../contracts/tax.js";
|
|
26
|
+
import { calculateGst, calculateGstInclusive, } from "../../_internal/shared/fees/calculator.js";
|
|
27
|
+
/** CGST and SGST are each HALF the headline rate; IGST carries all of it. */
|
|
28
|
+
function componentsFrom(breakdown, rate) {
|
|
29
|
+
const half = rate / 2;
|
|
30
|
+
if (breakdown.igst > 0 || (breakdown.cgst === 0 && breakdown.sgst === 0 && breakdown.gstAmount > 0)) {
|
|
31
|
+
return [{ code: "igst", label: "IGST", rate, amount: breakdown.igst }];
|
|
32
|
+
}
|
|
33
|
+
if (breakdown.gstAmount === 0)
|
|
34
|
+
return [];
|
|
35
|
+
return [
|
|
36
|
+
{ code: "cgst", label: "CGST", rate: half, amount: breakdown.cgst },
|
|
37
|
+
{ code: "sgst", label: "SGST", rate: half, amount: breakdown.sgst },
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
function toBreakdown(breakdown, rate) {
|
|
41
|
+
return {
|
|
42
|
+
taxableAmount: breakdown.taxableAmount,
|
|
43
|
+
taxAmount: breakdown.gstAmount,
|
|
44
|
+
components: componentsFrom(breakdown, rate),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export class IndiaGstTaxProvider extends ITaxProvider {
|
|
48
|
+
constructor() {
|
|
49
|
+
super(...arguments);
|
|
50
|
+
this.id = "india-gst";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Intra-state when origin and destination states match.
|
|
54
|
+
*
|
|
55
|
+
* 🛑 Absent information is treated as INTRA-state, which preserves the
|
|
56
|
+
* existing behaviour exactly rather than improving on it. Every current
|
|
57
|
+
* caller passes an `intraState` boolean it computed itself, and the defaults
|
|
58
|
+
* across those call sites resolve to intra-state when a place of supply is
|
|
59
|
+
* unknown — changing that here would alter tax on orders with an incomplete
|
|
60
|
+
* delivery snapshot, which is precisely the class of row most likely to
|
|
61
|
+
* exist. A stricter rule is a product decision, not a side effect of adding
|
|
62
|
+
* an interface.
|
|
63
|
+
*/
|
|
64
|
+
isSameRegion(jurisdiction) {
|
|
65
|
+
const { origin, destination } = jurisdiction;
|
|
66
|
+
if (!origin || !destination)
|
|
67
|
+
return true;
|
|
68
|
+
return origin.trim().toUpperCase() === destination.trim().toUpperCase();
|
|
69
|
+
}
|
|
70
|
+
onExclusive(item, jurisdiction, taxableAmount) {
|
|
71
|
+
return toBreakdown(calculateGst(item.rate, this.isSameRegion(jurisdiction), taxableAmount), item.rate);
|
|
72
|
+
}
|
|
73
|
+
onInclusive(item, jurisdiction, gross) {
|
|
74
|
+
return toBreakdown(calculateGstInclusive(item.rate, this.isSameRegion(jurisdiction), gross), item.rate);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** The default instance. A consumer in another jurisdiction registers its own. */
|
|
78
|
+
export const indiaGstTaxProvider = new IndiaGstTaxProvider();
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* useScrollToTop — jump to the top whenever a key changes.
|
|
4
|
+
*
|
|
5
|
+
* ## Why a HOOK and not a `<ScrollToTop />` component
|
|
6
|
+
*
|
|
7
|
+
* The consumer had a 17-line component doing exactly this. Lifting it as a
|
|
8
|
+
* component would not work: the only interesting line is
|
|
9
|
+
* `usePathname()` from `@/i18n/navigation`, and that is the consumer's own
|
|
10
|
+
* routing choice (next-intl, locale-prefixed). appkit cannot import it, and
|
|
11
|
+
* taking a `pathname` prop would mean a component whose entire job is to pass
|
|
12
|
+
* one value to one effect.
|
|
13
|
+
*
|
|
14
|
+
* So the mechanism moves and the binding stays: the consumer keeps a four-line
|
|
15
|
+
* mount that supplies its own `usePathname()`.
|
|
16
|
+
*
|
|
17
|
+
* ## 🛑 `behavior: "auto"`, deliberately
|
|
18
|
+
*
|
|
19
|
+
* Not `"smooth"`. A route change should look instantaneous; smooth-scrolling a
|
|
20
|
+
* fresh page animates content the reader has not seen yet, and on a long page
|
|
21
|
+
* the animation is still running when they start reading. It also ignores
|
|
22
|
+
* `prefers-reduced-motion`, which `"auto"` sidesteps entirely by not animating.
|
|
23
|
+
*
|
|
24
|
+
* ## 🛑 Does NOT fire on mount
|
|
25
|
+
*
|
|
26
|
+
* `useEffect` with a dependency runs on mount too, which would fight the
|
|
27
|
+
* browser's own scroll restoration on a back-navigation — the reader returns to
|
|
28
|
+
* a list, the browser restores their place, and this yanks them to the top.
|
|
29
|
+
* The first run is skipped so only genuine changes scroll.
|
|
30
|
+
*
|
|
31
|
+
* That is a behaviour FIX, not a port: the consumer's component scrolled on
|
|
32
|
+
* mount. It was unnoticeable there because Next remounts the tree on a hard
|
|
33
|
+
* load anyway, but it is wrong for a hook anyone may mount mid-tree.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* "use client";
|
|
38
|
+
* import { usePathname } from "@/i18n/navigation";
|
|
39
|
+
* import { useScrollToTop } from "@mohasinac/appkit/react/hooks/useScrollToTop";
|
|
40
|
+
*
|
|
41
|
+
* export function ScrollToTop() {
|
|
42
|
+
* useScrollToTop(usePathname());
|
|
43
|
+
* return null;
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @tag domain:routing
|
|
48
|
+
* @tag layer:client
|
|
49
|
+
* @tag access:client
|
|
50
|
+
* @tag sideEffects:dom
|
|
51
|
+
*/
|
|
52
|
+
import { useEffect, useRef } from "react";
|
|
53
|
+
export function useScrollToTop(key, { enabled = true } = {}) {
|
|
54
|
+
const previous = useRef(undefined);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!enabled)
|
|
57
|
+
return;
|
|
58
|
+
// First run: record and do nothing — see the note on scroll restoration.
|
|
59
|
+
if (previous.current === undefined) {
|
|
60
|
+
previous.current = key;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (previous.current === key)
|
|
64
|
+
return;
|
|
65
|
+
previous.current = key;
|
|
66
|
+
if (typeof window === "undefined")
|
|
67
|
+
return;
|
|
68
|
+
window.scrollTo({ top: 0, left: 0, behavior: "auto" });
|
|
69
|
+
}, [key, enabled]);
|
|
70
|
+
}
|
|
@@ -4608,7 +4608,7 @@ export declare const SCHEMAS: {
|
|
|
4608
4608
|
status: "open" | "closed" | "in_progress" | "waiting_on_user" | "resolved";
|
|
4609
4609
|
id: string;
|
|
4610
4610
|
description: string;
|
|
4611
|
-
category: "general" | "
|
|
4611
|
+
category: "general" | "order_issue" | "billing_payment" | "account" | "listing_dispute" | "scam_report" | "refund_request" | "auction_dispute" | "store_change_request" | "order_modification_request" | "unban_request";
|
|
4612
4612
|
createdAt: string | Date | import("zod").objectOutputType<{
|
|
4613
4613
|
toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
|
|
4614
4614
|
}, import("zod").ZodTypeAny, "passthrough">;
|
|
@@ -4651,7 +4651,7 @@ export declare const SCHEMAS: {
|
|
|
4651
4651
|
status: "open" | "closed" | "in_progress" | "waiting_on_user" | "resolved";
|
|
4652
4652
|
id: string;
|
|
4653
4653
|
description: string;
|
|
4654
|
-
category: "general" | "
|
|
4654
|
+
category: "general" | "order_issue" | "billing_payment" | "account" | "listing_dispute" | "scam_report" | "refund_request" | "auction_dispute" | "store_change_request" | "order_modification_request" | "unban_request";
|
|
4655
4655
|
createdAt: string | Date | import("zod").objectInputType<{
|
|
4656
4656
|
toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
|
|
4657
4657
|
}, import("zod").ZodTypeAny, "passthrough">;
|
package/dist/seo/json-ld.js
CHANGED
|
@@ -13,7 +13,27 @@
|
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
15
|
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "";
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* The brand name, from the injected identity rather than an env var.
|
|
18
|
+
*
|
|
19
|
+
* 🛑 A FUNCTION, not a module `const`, and that matters twice over. This was
|
|
20
|
+
* `const SITE_NAME = process.env.NEXT_PUBLIC_SITE_NAME ?? "App"`, which:
|
|
21
|
+
*
|
|
22
|
+
* - captured its value at IMPORT time, so even after `configureBrand()` runs
|
|
23
|
+
* it would have kept whatever it read first; and
|
|
24
|
+
* - read the env var this file's own comment at `SiteIdentityOptions` records
|
|
25
|
+
* as having shipped `"Letitrip"` to production — the wrong casing, in the
|
|
26
|
+
* `Organization.name` Google may put in a Knowledge Panel.
|
|
27
|
+
*
|
|
28
|
+
* That comment fixed the two site-wide nodes by taking `opts.siteName`. It did
|
|
29
|
+
* not fix `publisher` below, which has no opts path, so every article still
|
|
30
|
+
* emitted the env value. Resolving through `getBrandName()` closes both: there
|
|
31
|
+
* is now one owner (`appkit.config.js → brand`) and no mis-typeable env var in
|
|
32
|
+
* the path.
|
|
33
|
+
*/
|
|
34
|
+
function siteName() {
|
|
35
|
+
return getBrandName();
|
|
36
|
+
}
|
|
17
37
|
/**
|
|
18
38
|
* schema.org requires ABSOLUTE URLs. A relative `image` or `item` is not a
|
|
19
39
|
* lenient-parse case — Google rejects the property, and `image` is *required*
|
|
@@ -57,6 +77,7 @@ function absoluteUrl(value, base = SITE_URL) {
|
|
|
57
77
|
return `${base.replace(/\/+$/, "")}/${v.replace(/^\/+/, "")}`;
|
|
58
78
|
}
|
|
59
79
|
import { ProductStatusValues } from "../features/products/schemas/index.js";
|
|
80
|
+
import { getBrandName } from "../core/brand-resolver.js";
|
|
60
81
|
import { getDefaultCurrency } from "../core/baseline-resolver.js";
|
|
61
82
|
// The defining module, never the `/client` entry: this file is reached from
|
|
62
83
|
// server-only metadata paths and must not pull in a `"use client"` module
|
|
@@ -252,12 +273,12 @@ export function blogPostJsonLd(post) {
|
|
|
252
273
|
author: post.authorName
|
|
253
274
|
? { "@type": "Person", name: post.authorName, image: post.authorAvatar }
|
|
254
275
|
: undefined,
|
|
255
|
-
publisher: { "@type": "Organization", name:
|
|
276
|
+
publisher: { "@type": "Organization", name: siteName(), url: SITE_URL },
|
|
256
277
|
mainEntityOfPage: { "@type": "WebPage", "@id": url },
|
|
257
278
|
};
|
|
258
279
|
}
|
|
259
280
|
export function organizationJsonLd(opts) {
|
|
260
|
-
const name = opts?.siteName?.trim() ||
|
|
281
|
+
const name = opts?.siteName?.trim() || siteName();
|
|
261
282
|
const base = (opts?.siteUrl?.trim() || SITE_URL).replace(/\/+$/, "");
|
|
262
283
|
// `sameAs: []` was hardcoded empty while appkit.config.js carried real
|
|
263
284
|
// socialUrls that nothing read. An empty array is worse than omitting the key.
|
|
@@ -305,7 +326,7 @@ export function organizationJsonLd(opts) {
|
|
|
305
326
|
};
|
|
306
327
|
}
|
|
307
328
|
export function searchBoxJsonLd(opts) {
|
|
308
|
-
const name = opts?.siteName?.trim() ||
|
|
329
|
+
const name = opts?.siteName?.trim() || siteName();
|
|
309
330
|
const base = (opts?.siteUrl?.trim() || SITE_URL).replace(/\/+$/, "");
|
|
310
331
|
return {
|
|
311
332
|
"@context": "https://schema.org",
|
package/dist/styles.css
CHANGED
|
@@ -7444,6 +7444,26 @@
|
|
|
7444
7444
|
color: var(--appkit-tabs-ink-active);
|
|
7445
7445
|
}
|
|
7446
7446
|
|
|
7447
|
+
/*
|
|
7448
|
+
* Wrapping tab bar — `<TabsList wrap>`.
|
|
7449
|
+
*
|
|
7450
|
+
* 🛑 For CONTENT tabs inside a page section, never for navigation. The
|
|
7451
|
+
* default list scrolls horizontally and collapses into a dropdown once it
|
|
7452
|
+
* overflows, which is right for a nav bar on a phone and wrong in the middle
|
|
7453
|
+
* of a homepage section: there it renders as a stray form control, and the
|
|
7454
|
+
* visitor cannot see what the section contains without opening it.
|
|
7455
|
+
*
|
|
7456
|
+
* Wrapping keeps every tab visible on a second line. `overflow: visible` is
|
|
7457
|
+
* required alongside `flex-wrap` — the base rule's `overflow-x: auto` would
|
|
7458
|
+
* otherwise still clip the wrapped rows and reintroduce a scrollbar.
|
|
7459
|
+
*/
|
|
7460
|
+
.appkit-tabs-list--wrap {
|
|
7461
|
+
flex-wrap: wrap;
|
|
7462
|
+
overflow-x: visible;
|
|
7463
|
+
overflow-y: visible;
|
|
7464
|
+
scrollbar-width: auto;
|
|
7465
|
+
}
|
|
7466
|
+
|
|
7447
7467
|
/* Radio — toggle and classic radio group variants */
|
|
7448
7468
|
|
|
7449
7469
|
.appkit-radio-group {
|
|
@@ -15,8 +15,26 @@ export interface SiteLogoProps {
|
|
|
15
15
|
* dark-primary surfaces in light mode etc.
|
|
16
16
|
*/
|
|
17
17
|
tone?: SiteLogoTone;
|
|
18
|
-
/** Accessible title
|
|
18
|
+
/** Accessible title. Defaults to `wordmark` + `tld`, so one prop usually suffices. */
|
|
19
19
|
title?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The wordmark GLYPHS. Defaults to `title`.
|
|
22
|
+
*
|
|
23
|
+
* 🛑 Separate from `title` because they are different things and conflating
|
|
24
|
+
* them is what hid the brand here. `title` is the accessible name; this is
|
|
25
|
+
* the text actually drawn in the SVG — and until A5 that text was the literal
|
|
26
|
+
* `LetItRip`, unreachable by any prop. A consumer could rename the accessible
|
|
27
|
+
* title and still render someone else's brand.
|
|
28
|
+
*/
|
|
29
|
+
wordmark?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Optional TLD badge drawn in a pill after the wordmark, e.g. `"in"`.
|
|
32
|
+
*
|
|
33
|
+
* Omitted by default: a library must not assume its consumer has a TLD worth
|
|
34
|
+
* showing, let alone which one. `TitleBarLayout` threads `brandTld` through
|
|
35
|
+
* for the installs that want it.
|
|
36
|
+
*/
|
|
37
|
+
tld?: string;
|
|
20
38
|
/**
|
|
21
39
|
* Optional admin-uploaded image URL. When present, renders an `<MediaImage>`
|
|
22
40
|
* instead of the SVG wordmark (so the watermark proxy applies). Falls back
|
|
@@ -24,4 +42,4 @@ export interface SiteLogoProps {
|
|
|
24
42
|
*/
|
|
25
43
|
src?: string;
|
|
26
44
|
}
|
|
27
|
-
export declare function SiteLogo({ size, tone, title, src, }: SiteLogoProps): import("react").JSX.Element;
|
|
45
|
+
export declare function SiteLogo({ size, tone, title, wordmark, tld, src, }: SiteLogoProps): import("react").JSX.Element;
|