@mohasinac/appkit 3.3.6 → 3.4.1

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 (141) hide show
  1. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +2 -1
  2. package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +2 -1
  3. package/dist/_internal/client/features/live/LiveItemDetailView.js +2 -1
  4. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +1 -1
  5. package/dist/_internal/client/features/lottery/LotteryDetailView.js +2 -1
  6. package/dist/_internal/client/features/lottery/LotteryListView.js +2 -1
  7. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +2 -1
  8. package/dist/_internal/server/features/checkout/actions.js +0 -1
  9. package/dist/_internal/server/features/lottery/actions.js +0 -2
  10. package/dist/_internal/server/features/reviews/actions.js +1 -1
  11. package/dist/features/about/components/FAQPageView.js +0 -2
  12. package/dist/features/about/components/FeesView.js +13 -1
  13. package/dist/features/about/components/HowCheckoutWorksView.js +17 -3
  14. package/dist/features/about/components/HowEmiWorksView.d.ts +3 -0
  15. package/dist/features/about/components/HowEmiWorksView.js +117 -0
  16. package/dist/features/about/components/PublicProfileView.js +1 -4
  17. package/dist/features/about/index.d.ts +2 -0
  18. package/dist/features/about/index.js +1 -0
  19. package/dist/features/account/components/BecomeSellerView.d.ts +1 -1
  20. package/dist/features/account/components/BecomeSellerView.js +1 -1
  21. package/dist/features/account/components/NotificationBell.js +1 -2
  22. package/dist/features/account/components/UserSupportView.js +0 -1
  23. package/dist/features/admin/components/AdminAdsView.js +3 -3
  24. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  25. package/dist/features/admin/components/AdminBundleEditorView.js +0 -4
  26. package/dist/features/admin/components/AdminCarouselEditorView.js +3 -3
  27. package/dist/features/admin/components/AdminCarouselView.js +3 -2
  28. package/dist/features/admin/components/AdminGuideHubView.js +2 -2
  29. package/dist/features/admin/components/AdminMediaView.js +2 -4
  30. package/dist/features/admin/components/AdminUserEditorView.d.ts +1 -1
  31. package/dist/features/admin/components/AdminUserEditorView.js +1 -1
  32. package/dist/features/admin/components/QuickActionsPanel.js +2 -2
  33. package/dist/features/auctions/components/AuctionDetailPageView.js +1 -1
  34. package/dist/features/blog/components/BlogFeaturedCard.js +2 -1
  35. package/dist/features/blog/components/BlogIndexListing.js +1 -1
  36. package/dist/features/blog/components/BlogListView.js +2 -1
  37. package/dist/features/blog/components/BlogPostForm.js +1 -1
  38. package/dist/features/blog/components/BlogPostView.js +2 -1
  39. package/dist/features/cart/components/CartDrawer.js +2 -1
  40. package/dist/features/categories/components/BrandDetailPageView.js +2 -1
  41. package/dist/features/categories/components/BundleCollage.js +2 -3
  42. package/dist/features/categories/components/CategoriesIndexListing.js +1 -5
  43. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  44. package/dist/features/categories/components/CategoryGrid.js +2 -1
  45. package/dist/features/categories/components/CategoryProductsView.d.ts +1 -1
  46. package/dist/features/categories/components/CategoryProductsView.js +1 -1
  47. package/dist/features/categories/components/ConcernCard.js +2 -1
  48. package/dist/features/collections/components/CollectionCard.js +2 -1
  49. package/dist/features/consultation/components/ConsultationForm.js +0 -1
  50. package/dist/features/contact/components/ContactInfoSidebar.js +2 -2
  51. package/dist/features/copilot/components/AdminCopilotView.js +0 -9
  52. package/dist/features/events/components/EventFormDrawer.d.ts +1 -1
  53. package/dist/features/events/components/EventFormDrawer.js +1 -1
  54. package/dist/features/events/components/EventPollWidget.js +2 -2
  55. package/dist/features/events/components/EventsIndexListing.js +1 -1
  56. package/dist/features/faq/actions/faq-actions.d.ts +2 -10
  57. package/dist/features/faq/actions/faq-actions.js +0 -4
  58. package/dist/features/homepage/components/AdSlot.js +2 -1
  59. package/dist/features/homepage/components/CustomCardsSection.js +2 -3
  60. package/dist/features/homepage/components/FeaturedBundlesSection.js +2 -3
  61. package/dist/features/homepage/components/GoogleReviewsSection.js +4 -3
  62. package/dist/features/homepage/components/HeroBanner.js +0 -1
  63. package/dist/features/homepage/components/SectionCarousel.d.ts +1 -1
  64. package/dist/features/homepage/components/SectionCarousel.js +1 -2
  65. package/dist/features/homepage/components/SocialFeedSection.js +2 -5
  66. package/dist/features/homepage/components/SocialPostCard.js +4 -3
  67. package/dist/features/media/MediaImage.d.ts +10 -1
  68. package/dist/features/media/MediaImage.js +6 -2
  69. package/dist/features/media/MediaPickerModal.js +2 -3
  70. package/dist/features/media/upload/MediaUploadList.js +2 -2
  71. package/dist/features/orders/components/OrdersList.js +2 -1
  72. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -0
  73. package/dist/features/products/api/route.js +1 -2
  74. package/dist/features/products/components/CompareOverlay.js +2 -3
  75. package/dist/features/products/components/CustomSectionTabContent.js +2 -2
  76. package/dist/features/products/components/FeatureBadge.js +0 -6
  77. package/dist/features/products/components/GroupSettingsPanel.js +2 -3
  78. package/dist/features/products/components/MarketplaceBundleCard.js +2 -1
  79. package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -3
  80. package/dist/features/products/components/PrizeDrawCollage.js +2 -3
  81. package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -1
  82. package/dist/features/products/components/PrizeDrawEntryActions.js +3 -3
  83. package/dist/features/products/components/PrizeRevealModal.js +3 -4
  84. package/dist/features/products/components/ProductDetailActions.d.ts +1 -1
  85. package/dist/features/products/components/ProductDetailActions.js +1 -1
  86. package/dist/features/products/components/ProductDetailPageView.d.ts +3 -1
  87. package/dist/features/products/components/ProductDetailPageView.js +3 -2
  88. package/dist/features/products/components/ProductFeatureBadges.d.ts +7 -1
  89. package/dist/features/products/components/ProductFeatureBadges.js +16 -3
  90. package/dist/features/products/components/ProductGradingTab.js +2 -2
  91. package/dist/features/products/components/ProductsIndexPageView.js +2 -2
  92. package/dist/features/products/components/SublistingCarouselSection.js +3 -4
  93. package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -1
  94. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  95. package/dist/features/reviews/components/ReviewDetailShell.js +4 -3
  96. package/dist/features/reviews/components/ReviewModal.js +2 -1
  97. package/dist/features/reviews/components/ReviewsIndexListing.js +1 -1
  98. package/dist/features/reviews/components/ReviewsList.js +2 -1
  99. package/dist/features/scams/components/ScamAwarenessModal.js +2 -2
  100. package/dist/features/search/components/SearchView.d.ts +1 -1
  101. package/dist/features/search/components/SearchView.js +1 -1
  102. package/dist/features/seller/components/SellerAddressesView.js +1 -5
  103. package/dist/features/seller/components/SellerPreOrdersView.js +3 -2
  104. package/dist/features/seller/components/SellerPrizeDrawsView.js +3 -2
  105. package/dist/features/seller/components/SellerProductsView.js +2 -1
  106. package/dist/features/seller/components/SellerSidebar.js +5 -4
  107. package/dist/features/shell/StepForm.js +1 -1
  108. package/dist/features/stores/components/InteractiveStoreCard.js +2 -3
  109. package/dist/features/stores/components/StoreAboutView.js +2 -2
  110. package/dist/features/stores/components/StoreCapabilitiesGuideView.js +2 -2
  111. package/dist/features/stores/components/StoreFinanceGuideView.js +2 -2
  112. package/dist/features/stores/components/StoreGuideHubView.js +2 -2
  113. package/dist/features/stores/components/StoreHeader.js +2 -1
  114. package/dist/features/stores/components/StoreListingsGuideView.js +2 -2
  115. package/dist/features/stores/components/StoreNavTabs.js +2 -2
  116. package/dist/features/stores/components/StoreOrdersGuideView.js +2 -2
  117. package/dist/features/stores/components/StoreSettingsGuideView.js +3 -3
  118. package/dist/features/wishlist/components/WishlistPage.js +2 -1
  119. package/dist/features/wishlist/components/WishlistView.js +0 -7
  120. package/dist/index.d.ts +2 -0
  121. package/dist/index.js +3 -0
  122. package/dist/next/routing/route-map.d.ts +2 -0
  123. package/dist/next/routing/route-map.js +1 -0
  124. package/dist/seed/factories/product.factory.js +0 -32
  125. package/dist/seed/faq-seed-data.js +23 -4
  126. package/dist/styles.css +4 -1
  127. package/dist/tailwind-utilities.css +1 -1
  128. package/dist/tokens/index.js +6 -458
  129. package/dist/ui/DataTable.js +0 -21
  130. package/dist/ui/components/Anchor.d.ts +3 -1
  131. package/dist/ui/components/Anchor.js +1 -0
  132. package/dist/ui/components/Avatar.style.css +3 -0
  133. package/dist/ui/components/Breadcrumb.js +2 -1
  134. package/dist/ui/components/FormActionBar.js +2 -1
  135. package/dist/ui/components/ListingLayout.d.ts +1 -1
  136. package/dist/ui/components/ListingLayout.js +1 -1
  137. package/dist/ui/components/Motion.js +1 -1
  138. package/dist/ui/components/QuickCreateModal.js +2 -1
  139. package/dist/ui/components/SlottedListingView.d.ts +1 -1
  140. package/dist/ui/components/SlottedListingView.js +1 -2
  141. package/package.json +1 -1
@@ -5,7 +5,6 @@ import { Button, Row, Select } from "../../../ui";
5
5
  import { Form } from "../../../ui/components/Form";
6
6
  import { FieldInput } from "../../../ui/forms/FieldInput";
7
7
  import { FieldTextarea } from "../../../ui/forms/FieldTextarea";
8
- const CLS_INPUT = "w-full rounded-md border border-neutral-300 px-3 py-2 text-sm";
9
8
  const TIME_SLOTS = [
10
9
  "9:00 AM",
11
10
  "10:00 AM",
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Div, Heading, Row, Span, Stack, Text } from "../../../ui";
2
+ import { Anchor, Div, Heading, Row, Span, Stack, Text } from "../../../ui";
3
3
  const __P = {
4
4
  p4: "p-4",
5
5
  };
6
6
  export function ContactInfoSidebar({ infoItems, labels = {}, renderActions, }) {
7
- return (_jsxs(Stack, { as: "aside", gap: "lg", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-4", children: labels.title ?? "Contact Information" }), _jsx(Stack, { gap: "md", children: infoItems.map((item) => (_jsxs(Row, { align: "start", gap: "3", children: [_jsx(Span, { size: "xl", className: "flex-shrink-0 mt-0.5", children: item.icon }), _jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide mb-0.5", color: "muted", size: "xs", weight: "medium", transform: "uppercase", children: item.label }), item.href ? (_jsx("a", { href: item.href, className: "text-sm text-primary-800 dark:text-primary-300 hover:underline", children: item.value })) : (_jsx(Text, { size: "sm", children: item.value }))] })] }, item.label))) })] }), (labels.businessHoursLabel || labels.responseTimeLabel) && (_jsxs(Stack, { gap: "sm", border: "default", surface: "muted", className: __P.p4, rounded: "xl", children: [labels.businessHoursLabel && labels.businessHoursValue && (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: labels.businessHoursLabel }), _jsx(Text, { className: "mt-0.5", size: "sm", children: labels.businessHoursValue })] })), labels.responseTimeLabel && labels.responseTimeValue && (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: labels.responseTimeLabel }), _jsx(Text, { className: "mt-0.5", size: "sm", children: labels.responseTimeValue })] }))] })), renderActions?.()] }));
7
+ return (_jsxs(Stack, { as: "aside", gap: "lg", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-4", children: labels.title ?? "Contact Information" }), _jsx(Stack, { gap: "md", children: infoItems.map((item) => (_jsxs(Row, { align: "start", gap: "3", children: [_jsx(Span, { size: "xl", className: "flex-shrink-0 mt-0.5", children: item.icon }), _jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide mb-0.5", color: "muted", size: "xs", weight: "medium", transform: "uppercase", children: item.label }), item.href ? (_jsx(Anchor, { href: item.href, size: "sm", underline: "hover", children: item.value })) : (_jsx(Text, { size: "sm", children: item.value }))] })] }, item.label))) })] }), (labels.businessHoursLabel || labels.responseTimeLabel) && (_jsxs(Stack, { gap: "sm", border: "default", surface: "muted", className: __P.p4, rounded: "xl", children: [labels.businessHoursLabel && labels.businessHoursValue && (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: labels.businessHoursLabel }), _jsx(Text, { className: "mt-0.5", size: "sm", children: labels.businessHoursValue })] })), labels.responseTimeLabel && labels.responseTimeValue && (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: labels.responseTimeLabel }), _jsx(Text, { className: "mt-0.5", size: "sm", children: labels.responseTimeValue })] }))] })), renderActions?.()] }));
8
8
  }
@@ -18,15 +18,6 @@ function MessageBubble({ msg }) {
18
18
  const isUser = msg.role === "user";
19
19
  return (_jsx(Div, { layout: "flex", justify: isUser ? "end" : "start", children: _jsxs(Div, { textSize: "sm", className: `max-w-[80%] py-[0.625rem] ${isUser ? "bg-primary text-white" : "bg-[var(--appkit-color-surface)] dark:bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text)]"}`, padding: "x-md", style: { borderRadius: isUser ? "1.5rem 0.125rem 1.5rem 1.5rem" : "0.125rem 1.5rem 1.5rem 1.5rem" }, children: [_jsx(Text, { className: "whitespace-pre-wrap leading-relaxed", size: "sm", children: msg.content }), msg.durationMs ? (_jsxs(Span, { size: "xs", className: "mt-1 block opacity-60", children: [(msg.durationMs / 1000).toFixed(1), "s"] })) : null] }) }));
20
20
  }
21
- function renderCopilotChatPanel(props) {
22
- const { messages, conversationId, isLoading, error, input, setInput, labels, messagesEndRef, handleSubmit } = props;
23
- return (_jsxs(Stack, { className: "border border-neutral-200 h-[calc(100vh-300px)]", surface: "default", rounded: "xl", children: [_jsx(Div, { border: "bottom", className: __P.p3, children: _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", weight: "medium", children: [labels?.conversationId ?? LBL_CONVERSATION_ID, ": ", conversationId] }) }), _jsxs(Stack, { className: `flex-1 ${__O.yAuto} ${__P.p4}`, gap: "md", children: [messages.length === 0 ? (_jsxs(Stack, { justify: "center", className: "h-full text-center", align: "center", children: [_jsx(Text, { weight: "medium", children: labels?.noMessages ?? "No messages yet" }), _jsx(Text, { variant: "secondary", className: "mt-1", size: "sm", children: labels?.noMessagesDesc ?? "Ask anything to get started" })] })) : null, messages.map((msg) => _jsx(MessageBubble, { msg: msg }, msg.id)), isLoading ? (_jsx(Row, { justify: "start", children: _jsx(Div, { paddingY: "y-xs-tall", surface: "muted", className: "", padding: "x-md", style: { borderRadius: "0.125rem 1.5rem 1.5rem 1.5rem" }, children: _jsx(Span, { size: "sm", className: "opacity-60 animate-pulse", children: "Thinking..." }) }) })) : null, error ? _jsx(Text, { className: CLS_ERROR_TEXT, children: labels?.errorLabel ?? "An error occurred. Please try again." }) : null, _jsx(Div, { ref: messagesEndRef })] }), _jsx(Div, { border: "top", className: __P.p3, children: _jsxs(Form, { onSubmit: handleSubmit, gap: "xs", className: "flex", children: [_jsx(Input, { type: "text", bare: true, value: input, onChange: (e) => setInput(e.target.value), placeholder: labels?.inputPlaceholder ?? "Ask anything...", disabled: isLoading, className: "flex-1 h-10" }), _jsx(Button, { type: "submit", variant: "primary", disabled: isLoading || !input.trim(), className: "shrink-0", children: isLoading ? labels?.sendingButton ?? "Sending..." : labels?.sendButton ?? "Send" })] }) })] }));
24
- }
25
- function renderCopilotHistoryPanel(props) {
26
- const { historyQuery, conversationInput, setConversationInput, loadConversation, labels } = props;
27
- return (_jsxs(Stack, { surface: "card", padding: "sm", gap: "3", children: [_jsx(Heading, { level: 3, size: "sm", weight: "semibold", children: labels?.historyTitle ?? "Conversation History" }), _jsxs(Form, { spacing: "sm", onSubmit: (e) => { e.preventDefault(); if (!conversationInput.trim())
28
- return; loadConversation(conversationInput.trim()); }, children: [_jsx(Input, { label: labels?.conversationId ?? LBL_CONVERSATION_ID, value: conversationInput, onChange: (e) => setConversationInput(e.target.value), placeholder: "conv_..." }), _jsx(Button, { type: "submit", variant: "secondary", size: "sm", className: "w-full", children: labels?.loadConversation ?? "Load conversation" })] }), historyQuery.error ? (_jsx(Alert, { variant: "warning", title: "History unavailable", children: historyQuery.error instanceof Error ? historyQuery.error.message : "Could not load history" })) : null, _jsxs(Stack, { className: `max-h-72 ${__O.yAuto}`, gap: "sm", children: [(historyQuery.data?.messages ?? []).map((log, index) => (_jsxs(Div, { className: "border border-neutral-200", rounded: "lg", padding: "xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", weight: "medium", children: new Date(log.createdAt).toLocaleString() }), _jsxs(Text, { className: `mt-1`, truncate: 3, size: "sm", children: ["Q: ", log.prompt] }), _jsxs(Text, { className: `mt-1`, color: "muted", truncate: 3, size: "sm", children: ["A: ", log.response] })] }, `${log.createdAt}-${index}`))), !historyQuery.isLoading && (historyQuery.data?.messages?.length ?? 0) === 0 ? (_jsx(Text, { variant: "secondary", size: "sm", children: "No server history found for this conversation yet." })) : null] })] }));
29
- }
30
21
  export function AdminCopilotView({ endpoint, historyEndpoint = "/api/copilot/history", renderHeader, labels = {}, className = "", }) {
31
22
  const [input, setInput] = useState("");
32
23
  const [conversationInput, setConversationInput] = useState("");
@@ -19,4 +19,4 @@ export interface EventFormDrawerProps {
19
19
  renderFooter?: () => React.ReactNode;
20
20
  className?: string;
21
21
  }
22
- export declare function EventFormDrawer({ isOpen, onClose, renderHeader, renderBaseFields, renderMediaFields, renderTypeConfig, renderTypeSelector, renderFooter, className, }: EventFormDrawerProps): React.JSX.Element | null;
22
+ export declare function EventFormDrawer({ isOpen, onClose: _onClose, renderHeader, renderBaseFields, renderMediaFields, renderTypeConfig, renderTypeSelector, renderFooter, className, }: EventFormDrawerProps): React.JSX.Element | null;
@@ -1,6 +1,6 @@
1
1
  import { jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Stack } from "../../../ui";
3
- export function EventFormDrawer({ isOpen, onClose, renderHeader, renderBaseFields, renderMediaFields, renderTypeConfig, renderTypeSelector, renderFooter, className = "", }) {
3
+ export function EventFormDrawer({ isOpen, onClose: _onClose, renderHeader, renderBaseFields, renderMediaFields, renderTypeConfig, renderTypeSelector, renderFooter, className = "", }) {
4
4
  if (!isOpen)
5
5
  return null;
6
6
  return (_jsxs(Stack, { className: `${className}`, gap: "lg", children: [renderHeader?.(), renderTypeSelector?.(), renderBaseFields?.(), renderMediaFields?.(), renderTypeConfig?.(), renderFooter?.()] }));
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { useAuth } from "../../../react/contexts/SessionContext";
5
5
  import { ROUTES } from "../../../next";
6
- import { Div, LoginRequiredModal, Span, Stack, Text } from "../../../ui";
6
+ import { Div, LoginRequiredModal, Span, Stack, Text, TextLink } from "../../../ui";
7
7
  import { normalizeError } from "../../../errors/normalize";
8
8
  const CLS_THANKS_BOX = "rounded-xl border border-success dark:border-success bg-success-surface px-6 py-8 text-center space-y-2";
9
9
  export function EventPollWidget({ eventId, pollConfig, eventStatus, totalEntries, entriesEndpoint, className = "", }) {
@@ -62,7 +62,7 @@ export function EventPollWidget({ eventId, pollConfig, eventStatus, totalEntries
62
62
  return (_jsxs(Stack, { paddingX: "x-5", className: className, gap: "3", rounded: "xl", padding: "y-md", border: "default", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "This poll has closed." }), totalEntries !== undefined && (_jsxs(Text, { size: "sm", color: "faint", children: [totalEntries.toLocaleString(), " vote", totalEntries !== 1 ? "s" : "", " cast"] })), _jsx(Stack, { gap: "sm", children: pollConfig.options.map((opt) => (_jsx(Div, { textSize: "sm", paddingY: "y-xs-tall", color: "muted", border: "subtle", padding: "x-md", rounded: "lg", children: opt.label }, opt.id))) })] }));
63
63
  }
64
64
  if (pollConfig.requireLogin && !user) {
65
- return (_jsxs(Stack, { className: `text-center ${className}`, gap: "3", rounded: "xl", paddingY: "y-xl", paddingX: "x-lg", border: "default", children: [_jsx(Text, { weight: "semibold", color: "primary", children: "Login to vote" }), _jsx(Text, { size: "sm", color: "muted", children: "You need an account to participate in this poll." }), _jsx("a", { href: String(ROUTES.AUTH.LOGIN), className: "inline-block rounded-xl bg-primary px-6 py-2.5 text-sm font-semibold text-white hover:bg-primary-600", children: "Log In" })] }));
65
+ return (_jsxs(Stack, { className: `text-center ${className}`, gap: "3", rounded: "xl", paddingY: "y-xl", paddingX: "x-lg", border: "default", children: [_jsx(Text, { weight: "semibold", color: "primary", children: "Login to vote" }), _jsx(Text, { size: "sm", color: "muted", children: "You need an account to participate in this poll." }), _jsx(TextLink, { variant: "bare", href: String(ROUTES.AUTH.LOGIN), rounded: "xl", size: "sm", weight: "semibold", className: "inline-block bg-primary px-6 py-2.5 text-white hover:bg-primary-600", children: "Log In" })] }));
66
66
  }
67
67
  if (isSubmitted) {
68
68
  return (_jsxs(Div, { className: `${CLS_THANKS_BOX} ${className}`, children: [_jsx(Text, { className: "text-success", weight: "semibold", children: "Vote recorded!" }), _jsx(Text, { size: "sm", color: "muted", children: "Thanks for participating." })] }));
@@ -116,7 +116,7 @@ export function EventsIndexListing({ initialData }) {
116
116
  sort: table.get(TABLE_KEYS.SORT) || sortBy(EVENT_FIELDS.STARTS_AT, "ASC"),
117
117
  filters: filterParts.length > 0 ? sieveAnd(...filterParts) : undefined,
118
118
  };
119
- const { events, total, totalPages, isLoading } = useEvents(params, { initialData });
119
+ const { events, totalPages, isLoading } = useEvents(params, { initialData });
120
120
  const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
121
121
  const commitSearch = useCallback(() => {
122
122
  table.set(TABLE_KEYS.QUERY, searchInput.trim());
@@ -161,17 +161,10 @@ export declare const faqUpdateSchema: z.ZodObject<{
161
161
  }>;
162
162
  export type FaqCreateInput = z.infer<typeof faqCreateSchema>;
163
163
  export type FaqUpdateInput = z.infer<typeof faqUpdateSchema>;
164
- declare const voteSchema: z.ZodObject<{
165
- faqId: z.ZodString;
166
- vote: z.ZodEnum<["helpful", "not-helpful"]>;
167
- }, "strip", z.ZodTypeAny, {
168
- faqId: string;
169
- vote: "helpful" | "not-helpful";
170
- }, {
164
+ export interface VoteFaqActionInput {
171
165
  faqId: string;
172
166
  vote: "helpful" | "not-helpful";
173
- }>;
174
- export type VoteFaqActionInput = z.infer<typeof voteSchema>;
167
+ }
175
168
  export interface VoteFaqActionResult {
176
169
  helpful: number;
177
170
  notHelpful: number;
@@ -188,4 +181,3 @@ export declare function listFaqs(params?: {
188
181
  }): Promise<FirebaseSieveResult<FAQDocument>>;
189
182
  export declare function listPublicFaqs(category?: string, limit?: number): Promise<FAQDocument[]>;
190
183
  export declare function getFaqById(id: string): Promise<FAQDocument | null>;
191
- export {};
@@ -30,10 +30,6 @@ export const faqCreateSchema = faqBaseSchema.refine((data) => {
30
30
  return matches.length <= 10;
31
31
  }, { message: "Too many template variables (max 10)" });
32
32
  export const faqUpdateSchema = faqBaseSchema.partial();
33
- const voteSchema = z.object({
34
- faqId: z.string().min(1),
35
- vote: z.enum(["helpful", "not-helpful"]),
36
- });
37
33
  // --- Actions --------------------------------------------------------------
38
34
  export async function voteFaq(input) {
39
35
  const faq = await faqsRepository.findById(input.faqId);
@@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from "react";
5
5
  import { getAdSlot, isAdSlotRenderable } from "../ad-registry";
6
6
  import { useActiveAd } from "../hooks/useActiveAd";
7
7
  import { Anchor, Div, Span, Text } from "../../../ui";
8
+ import { MediaImage } from "../../media/MediaImage";
8
9
  // ---------------------------------------------------------------------------
9
10
  // Sub-renderers
10
11
  // ---------------------------------------------------------------------------
@@ -33,7 +34,7 @@ function ManualAdBanner({ ad }) {
33
34
  const { creative } = ad;
34
35
  if (!creative)
35
36
  return null;
36
- return (_jsxs(Anchor, { href: creative.ctaHref || "#", target: creative.ctaHref ? "_blank" : undefined, rel: "noopener noreferrer", underline: "none", layout: "flex", gap: "lg", rounded: "lg", padding: "inline", hoverSurface: "subtle", className: "w-full overflow-hidden bg-[var(--appkit-color-surface)]", children: [creative.imageUrl ? (_jsx("img", { src: creative.imageUrl, alt: creative.title || "Advertisement", className: "h-14 w-14 rounded object-cover flex-shrink-0", loading: "lazy" })) : null, _jsxs(Div, { className: "flex-1 min-w-0", children: [creative.title ? (_jsx(Text, { className: "truncate", color: "primary", size: "sm", weight: "semibold", children: creative.title })) : null, creative.body ? (_jsx(Text, { className: "truncate", color: "muted", size: "xs", children: creative.body })) : null] }), creative.ctaLabel ? (_jsx(Span, { color: "inverse", size: "xs", weight: "semibold", className: "flex-shrink-0 bg-primary py-[0.375rem]", rounded: "md", padding: "x-sm", children: creative.ctaLabel })) : null] }));
37
+ return (_jsxs(Anchor, { href: creative.ctaHref || "#", target: creative.ctaHref ? "_blank" : undefined, rel: "noopener noreferrer", underline: "none", layout: "flex", gap: "lg", rounded: "lg", padding: "inline", hoverSurface: "subtle", className: "w-full overflow-hidden bg-[var(--appkit-color-surface)]", children: [creative.imageUrl ? (_jsx(Div, { className: "relative h-14 w-14 flex-shrink-0 overflow-hidden", rounded: "default", children: _jsx(MediaImage, { src: creative.imageUrl, alt: creative.title || "Advertisement", size: "thumbnail" }) })) : null, _jsxs(Div, { className: "flex-1 min-w-0", children: [creative.title ? (_jsx(Text, { className: "truncate", color: "primary", size: "sm", weight: "semibold", children: creative.title })) : null, creative.body ? (_jsx(Text, { className: "truncate", color: "muted", size: "xs", children: creative.body })) : null] }), creative.ctaLabel ? (_jsx(Span, { color: "inverse", size: "xs", weight: "semibold", className: "flex-shrink-0 bg-primary py-[0.375rem]", rounded: "md", padding: "x-sm", children: creative.ctaLabel })) : null] }));
37
38
  }
38
39
  // ---------------------------------------------------------------------------
39
40
  // Primary component
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Row } from "@mohasinac/appkit/ui";
4
- import { Div, Heading, HorizontalScroller, Section, Stack, Text } from "../../../ui";
4
+ import { Anchor, Div, Heading, HorizontalScroller, Section, Stack, Text } from "../../../ui";
5
5
  import { DynamicBgDiv } from "../../../ui/components/DynamicBgDiv";
6
6
  import { THEMED_TEXT_PRIMARY } from "../../../_internal/shared/styles/themed";
7
7
  import { SectionCarousel } from "./SectionCarousel";
@@ -45,7 +45,7 @@ const VARIANT_CLASS = {
45
45
  function CardItem({ card }) {
46
46
  const radiusClass = RADIUS_CLASS[card.borderRadius ?? "lg"] ?? "rounded-lg";
47
47
  const shadowClass = SHADOW_CLASS[card.shadowLevel ?? "sm"] ?? "shadow-sm";
48
- return (_jsxs(DynamicBgDiv, { color: card.backgroundColor, textColor: card.textColor, className: `flex flex-col overflow-hidden ${radiusClass} ${shadowClass} bg-[var(--appkit-color-surface)]`, children: [card.image && (_jsx(Div, { className: `relative w-full aspect-video ${__O.hidden}`, children: _jsx(MediaImage, { src: card.image, alt: card.imageAlt ?? "", size: "card" }) })), _jsxs(Stack, { className: `${__P.p4} flex-1`, gap: "sm", children: [card.eyebrow && (_jsx(Text, { className: "tracking-widest opacity-70", size: "xs", weight: "semibold", transform: "uppercase", children: card.eyebrow })), card.title && (_jsx(Text, { className: "leading-snug", size: "base", weight: "bold", children: card.title })), card.body && (_jsx(Text, { className: "opacity-80 leading-relaxed flex-1", size: "sm", children: card.body })), card.buttons && card.buttons.length > 0 && (_jsx(Row, { gap: "sm", wrap: true, className: "mt-2", children: card.buttons.map((btn, i) => (_jsx("a", { href: btn.href, target: btn.target ?? "_self", rel: btn.target === "_blank" ? "noopener noreferrer" : undefined, className: VARIANT_CLASS[btn.variant] ?? VARIANT_CLASS.primary, children: btn.label }, i))) })), card.formEmbed && (_jsx("iframe", { src: card.formEmbed, sandbox: "allow-forms allow-scripts", loading: "lazy", className: "w-full mt-3 border-0 rounded-md min-h-[200px]", title: "Embedded form" }))] })] }));
48
+ return (_jsxs(DynamicBgDiv, { color: card.backgroundColor, textColor: card.textColor, className: `flex flex-col overflow-hidden ${radiusClass} ${shadowClass} bg-[var(--appkit-color-surface)]`, children: [card.image && (_jsx(Div, { className: `relative w-full aspect-video ${__O.hidden}`, children: _jsx(MediaImage, { src: card.image, alt: card.imageAlt ?? "", size: "card" }) })), _jsxs(Stack, { className: `${__P.p4} flex-1`, gap: "sm", children: [card.eyebrow && (_jsx(Text, { className: "tracking-widest opacity-70", size: "xs", weight: "semibold", transform: "uppercase", children: card.eyebrow })), card.title && (_jsx(Text, { className: "leading-snug", size: "base", weight: "bold", children: card.title })), card.body && (_jsx(Text, { className: "opacity-80 leading-relaxed flex-1", size: "sm", children: card.body })), card.buttons && card.buttons.length > 0 && (_jsx(Row, { gap: "sm", wrap: true, className: "mt-2", children: card.buttons.map((btn, i) => (_jsx(Anchor, { href: btn.href, target: btn.target ?? "_self", rel: btn.target === "_blank" ? "noopener noreferrer" : undefined, tone: "none", underline: "none", className: VARIANT_CLASS[btn.variant] ?? VARIANT_CLASS.primary, children: btn.label }, i))) })), card.formEmbed && (_jsx("iframe", { src: card.formEmbed, sandbox: "allow-forms allow-scripts", loading: "lazy", className: "w-full mt-3 border-0 rounded-md min-h-[200px]", title: "Embedded form" }))] })] }));
49
49
  }
50
50
  // --- Section Header ----------------------------------------------------------
51
51
  function SectionHeader({ title }) {
@@ -56,7 +56,6 @@ function SectionHeader({ title }) {
56
56
  }
57
57
  export function CustomCardsSection(config) {
58
58
  const { title, layout, columns = 3, cards, autoScroll, scrollIntervalMs } = config;
59
- const themed = { textPrimary: THEMED_TEXT_PRIMARY };
60
59
  if (!cards?.length)
61
60
  return null;
62
61
  // autoScroll: wrap all cards in SectionCarousel (client carousel)
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Link from "next/link";
3
3
  import { Badge, Div, Heading, Row, Section, Stack, Text } from "../../../ui";
4
+ import { MediaImage } from "../../media/MediaImage";
4
5
  import { ROUTES } from "../../../next/routing/route-map";
5
6
  import { formatCurrency } from "../../../utils/number.formatter";
6
7
  import { BUNDLE_COPY, BUNDLE_STOCK_VARIANT, } from "../../../_internal/shared/features/categories/bundle-copy";
@@ -21,9 +22,7 @@ function FeaturedBundleCard({ bundle, onBuyNow }) {
21
22
  const memberCount = bundle.bundleProductIds?.length ?? 0;
22
23
  const cover = bundle.display?.coverImage;
23
24
  const href = String(ROUTES.PUBLIC.BUNDLE_DETAIL?.(bundle.slug) ?? "#");
24
- return (_jsxs(Stack, { gap: "none", surface: "default", rounded: "xl", border: "default", children: [_jsxs(Link, { href: href, className: "group block p-3 hover:no-underline", children: [_jsx(Div, { className: `mb-2 aspect-square ${__O.hidden}`, rounded: "lg", surface: "subtle", children: cover ? (
25
- // eslint-disable-next-line @next/next/no-img-element
26
- _jsx("img", { src: cover, alt: bundle.name, className: "h-full w-full object-cover transition-transform group-hover:scale-105", loading: "lazy" })) : (_jsx(Row, { textSize: "3xl", className: "h-full w-full", align: "center", justify: "center", children: PLACEHOLDER_EMOJI })) }), _jsx(Text, { className: "line-clamp-2", size: "sm", weight: "semibold", children: bundle.name }), _jsxs(Row, { gap: "sm", align: "center", className: "mt-1", wrap: true, children: [_jsx(Text, { size: "sm", weight: "bold", children: bundle.bundlePriceInPaise
25
+ return (_jsxs(Stack, { gap: "none", surface: "default", rounded: "xl", border: "default", children: [_jsxs(Link, { href: href, className: "group block p-3 hover:no-underline", children: [_jsx(Div, { className: `relative mb-2 aspect-square ${__O.hidden}`, rounded: "lg", surface: "subtle", children: cover ? (_jsx(MediaImage, { src: cover, alt: bundle.name, size: "card", className: "transition-transform group-hover:scale-105" })) : (_jsx(Row, { textSize: "3xl", className: "h-full w-full", align: "center", justify: "center", children: PLACEHOLDER_EMOJI })) }), _jsx(Text, { className: "line-clamp-2", size: "sm", weight: "semibold", children: bundle.name }), _jsxs(Row, { gap: "sm", align: "center", className: "mt-1", wrap: true, children: [_jsx(Text, { size: "sm", weight: "bold", children: bundle.bundlePriceInPaise
27
26
  ? formatCurrency(bundle.bundlePriceInPaise / 100, "INR")
28
27
  : BUNDLE_COPY.featured.priceFallback }), _jsxs(Text, { size: "xs", color: "muted", children: ["\u00B7 ", BUNDLE_COPY.featured.itemCount(memberCount)] }), stock !== "in_stock" && (_jsx(Badge, { variant: BUNDLE_STOCK_VARIANT[stock], children: BUNDLE_COPY.stockBadge.listVariantOutOfStock }))] })] }), onBuyNow && (_jsx(Div, { border: "subtle", className: `border-t ${__P.p3}`, padding: "t-xs", children: _jsx(BundleBuyNowCta, { bundleSlug: bundle.slug, outOfStock: stock === "out_of_stock", onBuyNow: onBuyNow, compact: true }) }))] }));
29
28
  }
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { normalizeError } from "../../../errors/normalize";
3
- import { Div, Heading, Row, Section, Span, Stack, Text } from "../../../ui";
3
+ import { Anchor, Div, Heading, Row, Section, Span, Stack, Text } from "../../../ui";
4
+ import { MediaImage } from "../../media/MediaImage";
4
5
  import { siteSettingsRepository } from "../../admin/repository/site-settings.repository";
5
6
  import { fetchGoogleReviews } from "../lib/google-reviews-fetcher";
6
7
  const __P = {
@@ -26,7 +27,7 @@ function ReviewCard({ review, showRating, showDate, }) {
26
27
  day: "numeric",
27
28
  })
28
29
  : null;
29
- return (_jsxs(Stack, { className: `${__P.p4} bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)]`, gap: "3", rounded: "xl", shadow: "sm", children: [_jsxs(Row, { align: "center", gap: "3", children: [review.authorPhotoUrl ? (_jsx("img", { src: review.authorPhotoUrl, alt: review.authorName, className: "w-9 h-9 rounded-full object-cover flex-shrink-0", loading: "lazy", referrerPolicy: "no-referrer" })) : (_jsx(Row, { className: "w-9 h-9 bg-[var(--appkit-color-primary)] flex-shrink-0", align: "center", justify: "center", rounded: "full", children: _jsx(Span, { color: "inverse", size: "sm", weight: "bold", children: review.authorName.charAt(0).toUpperCase() }) })), _jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: "truncate", size: "sm", weight: "semibold", children: review.authorName }), dateStr && (_jsx(Text, { size: "xs", color: "muted", children: dateStr }))] })] }), showRating && _jsx(StarRating, { rating: review.rating }), review.text && (_jsx(Text, { className: "leading-relaxed line-clamp-4", color: "muted", size: "sm", children: review.text })), _jsx(GoogleBadge, {})] }));
30
+ return (_jsxs(Stack, { className: `${__P.p4} bg-[var(--appkit-color-surface)] border border-[var(--appkit-color-border)]`, gap: "3", rounded: "xl", shadow: "sm", children: [_jsxs(Row, { align: "center", gap: "3", children: [review.authorPhotoUrl ? (_jsx(Div, { className: "relative w-9 h-9 flex-shrink-0 overflow-hidden", rounded: "full", children: _jsx(MediaImage, { src: review.authorPhotoUrl, alt: review.authorName, size: "avatar" }) })) : (_jsx(Row, { className: "w-9 h-9 bg-[var(--appkit-color-primary)] flex-shrink-0", align: "center", justify: "center", rounded: "full", children: _jsx(Span, { color: "inverse", size: "sm", weight: "bold", children: review.authorName.charAt(0).toUpperCase() }) })), _jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: "truncate", size: "sm", weight: "semibold", children: review.authorName }), dateStr && (_jsx(Text, { size: "xs", color: "muted", children: dateStr }))] })] }), showRating && _jsx(StarRating, { rating: review.rating }), review.text && (_jsx(Text, { className: "leading-relaxed line-clamp-4", color: "muted", size: "sm", children: review.text })), _jsx(GoogleBadge, {})] }));
30
31
  }
31
32
  // --- Empty / Not Configured States -------------------------------------------
32
33
  function NotConfiguredState() {
@@ -67,5 +68,5 @@ export async function GoogleReviewsSection(config) {
67
68
  : "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4";
68
69
  const cardClass = layout === "carousel" ? "flex-shrink-0 w-[85%] sm:w-72 snap-start" : "";
69
70
  const mapsHref = googleMapsUrl || (placeId ? `https://search.google.com/local/reviews?placeid=${placeId}` : "");
70
- return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto", padding: "x-md", children: [_jsxs(Row, { className: "mb-8", align: "end", justify: "between", gap: "md", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, children: "What Our Customers Say" }), aggregateRating > 0 && (_jsxs(Row, { className: "mt-1", align: "center", gap: "sm", children: [_jsx(StarRating, { rating: Math.round(aggregateRating) }), _jsxs(Text, { size: "sm", variant: "muted", children: [aggregateRating.toFixed(1), " \u00B7 ", totalRatings.toLocaleString(), " reviews"] })] }))] }), linkToGoogleMaps && mapsHref && (_jsx("a", { href: mapsHref, target: "_blank", rel: "noopener noreferrer", className: CLS_GMAPS_LINK, children: "View on Google \u2192" }))] }), reviews.length === 0 ? (_jsx(EmptyState, {})) : (_jsx(Div, { className: gridClass, children: reviews.map((review, i) => (_jsx(Div, { className: cardClass, children: _jsx(ReviewCard, { review: review, showRating: showRating, showDate: showDate }) }, `${review.authorName}-${i}`))) }))] }) }));
71
+ return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto", padding: "x-md", children: [_jsxs(Row, { className: "mb-8", align: "end", justify: "between", gap: "md", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, children: "What Our Customers Say" }), aggregateRating > 0 && (_jsxs(Row, { className: "mt-1", align: "center", gap: "sm", children: [_jsx(StarRating, { rating: Math.round(aggregateRating) }), _jsxs(Text, { size: "sm", variant: "muted", children: [aggregateRating.toFixed(1), " \u00B7 ", totalRatings.toLocaleString(), " reviews"] })] }))] }), linkToGoogleMaps && mapsHref && (_jsx(Anchor, { href: mapsHref, tone: "none", underline: "hover", className: CLS_GMAPS_LINK, children: "View on Google \u2192" }))] }), reviews.length === 0 ? (_jsx(EmptyState, {})) : (_jsx(Div, { className: gridClass, children: reviews.map((review, i) => (_jsx(Div, { className: cardClass, children: _jsx(ReviewCard, { review: review, showRating: showRating, showDate: showDate }) }, `${review.authorName}-${i}`))) }))] }) }));
71
72
  }
@@ -4,7 +4,6 @@ import { useState, useEffect, useRef, useCallback } from "react";
4
4
  import Image from "next/image";
5
5
  import Link from "next/link";
6
6
  import { Button, Div, Heading, Row, Span, Stack } from "../../../ui";
7
- const FONT_BANGERS = "var(--font-bangers, Bangers, cursive)";
8
7
  export function HeroBanner({ banners, autoplayMs = 5000 }) {
9
8
  const [current, setCurrent] = useState(0);
10
9
  const [animating, setAnimating] = useState(false);
@@ -72,4 +72,4 @@ export interface SectionCarouselProps<T = unknown> {
72
72
  */
73
73
  minItemWidth?: number;
74
74
  }
75
- export declare function SectionCarousel<T = unknown>({ title, description, headingVariant, pillLabel, backgroundImage, viewMoreHref, viewMoreLabel, items, renderItem, perView, gap, autoScroll, autoScrollInterval, keyExtractor, rows, className, isLoading, skeletonCount, lightText, showPeek, minItemWidth, }: SectionCarouselProps<T>): React.JSX.Element | null;
75
+ export declare function SectionCarousel<T = unknown>({ title, description, headingVariant, pillLabel, backgroundImage, viewMoreHref, viewMoreLabel, items, renderItem, perView, gap, autoScroll, autoScrollInterval, keyExtractor, rows, className, isLoading, skeletonCount, lightText, showPeek: _showPeek, minItemWidth, }: SectionCarouselProps<T>): React.JSX.Element | null;
@@ -9,11 +9,10 @@ const __O = {
9
9
  };
10
10
  // --- Skeleton ----------------------------------------------------------------
11
11
  function CarouselSkeleton({ count }) {
12
- const themed = { bgSecondary: THEMED_BG_SECONDARY, textPrimary: THEMED_TEXT_PRIMARY, textSecondary: THEMED_TEXT_SECONDARY };
13
12
  return (_jsx(Div, { layout: "flex", gap: "4", className: `${__O.hidden}`, padding: "x-md", children: Array.from({ length: count }).map((_, i) => (_jsxs(Stack, { className: "flex-none min-w-[clamp(150px,18vw,260px)] max-w-[clamp(240px,36vw,380px)] h-[clamp(180px,26vh,260px)]", gap: "sm", children: [_jsx(Div, { className: `aspect-square ${skeleton.image}`, rounded: "xl" }), _jsx(Div, { className: `${skeleton.text} w-3/4` }), _jsx(Div, { className: `${skeleton.text} w-1/2` })] }, i))) }));
14
13
  }
15
14
  // --- Component ---------------------------------------------------------------
16
- export function SectionCarousel({ title, description, headingVariant = "editorial", pillLabel, backgroundImage, viewMoreHref, viewMoreLabel = "View all →", items, renderItem, perView = CAROUSEL_PER_VIEW.standard, gap = 16, autoScroll = false, autoScrollInterval = 3500, keyExtractor, rows = 1, className = "", isLoading = false, skeletonCount = 4, lightText, showPeek = false, minItemWidth = 220, }) {
15
+ export function SectionCarousel({ title, description, headingVariant = "editorial", pillLabel, backgroundImage, viewMoreHref, viewMoreLabel = "View all →", items, renderItem, perView = CAROUSEL_PER_VIEW.standard, gap = 16, autoScroll = false, autoScrollInterval = 3500, keyExtractor, rows = 1, className = "", isLoading = false, skeletonCount = 4, lightText, showPeek: _showPeek = false, minItemWidth = 220, }) {
17
16
  if (!isLoading && items.length === 0)
18
17
  return null;
19
18
  const hasBg = Boolean(backgroundImage);
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { normalizeError } from "../../../errors/normalize";
3
- import { Div, Heading, Row, Section, Stack, Text } from "../../../ui";
3
+ import { Anchor, Div, Heading, Row, Section, Stack, Text } from "../../../ui";
4
4
  import { SocialPostCard } from "./SocialPostCard";
5
5
  import { fetchInstagramPosts, fetchFacebookPosts, fetchTikTokPosts, fetchDeviantArtPosts, } from "../lib/social-feed-fetcher";
6
6
  import { siteSettingsRepository } from "../../admin/repository/site-settings.repository";
@@ -22,9 +22,6 @@ function platformProfileUrl(platform, handle) {
22
22
  }
23
23
  }
24
24
  // --- Skeleton ----------------------------------------------------------------
25
- function SocialFeedSkeleton({ count }) {
26
- return (_jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4", children: Array.from({ length: count }).map((_, i) => (_jsx(Div, { className: "aspect-square animate-pulse", surface: "subtle", rounded: "xl" }, i))) }));
27
- }
28
25
  // --- Empty state -------------------------------------------------------------
29
26
  function SocialFeedEmpty({ platform }) {
30
27
  return (_jsxs(Stack, { justify: "center", color: "faint", padding: "y-4xl", align: "center", children: [_jsxs("svg", { className: "w-10 h-10 mb-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "3" }), _jsx("path", { d: "M9 9h6M9 12h6M9 15h4" })] }), _jsxs(Text, { size: "sm", children: ["No posts from ", PLATFORM_LABELS[platform], " yet."] })] }));
@@ -102,5 +99,5 @@ export async function SocialFeedSection(config) {
102
99
  : layout === "masonry"
103
100
  ? "break-inside-avoid"
104
101
  : "";
105
- return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto", padding: "x-md", children: [_jsxs(Row, { className: "mb-8", align: "end", justify: "between", gap: "md", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, className: "mb-1", children: title || `${PLATFORM_LABELS[platform]} Feed` }), subtitle && (_jsx(Text, { size: "sm", variant: "muted", children: subtitle }))] }), profileUrl && (_jsxs("a", { href: profileUrl, target: "_blank", rel: "noopener noreferrer", className: "shrink-0 text-sm font-medium text-[var(--appkit-color-primary)] hover:opacity-80", children: ["Follow on ", PLATFORM_LABELS[platform], " \u2192"] }))] }), error ? (_jsx(Div, { textSize: "sm", className: "text-center", color: "faint", padding: "y-3xl", children: error })) : posts.length === 0 ? (_jsx(SocialFeedEmpty, { platform: platform })) : (_jsx(Div, { className: gridClass, children: posts.slice(0, count).map((post) => (_jsx(Div, { className: cardClass, children: _jsx(SocialPostCard, { post: post, showCaption: showCaption, showStats: showStats }) }, post.id))) }))] }) }));
102
+ return (_jsx(Section, { padding: "y-3xl", surface: "muted", children: _jsxs(Div, { className: "w-full max-w-7xl mx-auto", padding: "x-md", children: [_jsxs(Row, { className: "mb-8", align: "end", justify: "between", gap: "md", children: [_jsxs(_Fragment, { children: [_jsx(Heading, { level: 2, className: "mb-1", children: title || `${PLATFORM_LABELS[platform]} Feed` }), subtitle && (_jsx(Text, { size: "sm", variant: "muted", children: subtitle }))] }), profileUrl && (_jsxs(Anchor, { href: profileUrl, tone: "none", underline: "none", className: "shrink-0 text-sm font-medium text-[var(--appkit-color-primary)] hover:opacity-80", children: ["Follow on ", PLATFORM_LABELS[platform], " \u2192"] }))] }), error ? (_jsx(Div, { textSize: "sm", className: "text-center", color: "faint", padding: "y-3xl", children: error })) : posts.length === 0 ? (_jsx(SocialFeedEmpty, { platform: platform })) : (_jsx(Div, { className: gridClass, children: posts.slice(0, count).map((post) => (_jsx(Div, { className: cardClass, children: _jsx(SocialPostCard, { post: post, showCaption: showCaption, showStats: showStats }) }, post.id))) }))] }) }));
106
103
  }
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Div, Row, Span, Stack, Text } from "../../../ui";
2
+ import { Anchor, Div, Row, Span, Stack, Text } from "../../../ui";
3
+ import { MediaImage } from "../../media/MediaImage";
3
4
  const __P = {
4
5
  p3: "p-3",
5
6
  };
@@ -46,12 +47,12 @@ function YouTubeCard({ post, showCaption }) {
46
47
  const thumbnail = `https://img.youtube.com/vi/${post.videoId}/maxresdefault.jpg`;
47
48
  const href = `https://youtu.be/${post.videoId}`;
48
49
  const meta = PLATFORM_META.youtube;
49
- return (_jsxs("a", { href: href, target: "_blank", rel: "noopener noreferrer", className: "group relative block aspect-video w-full overflow-hidden rounded-xl bg-zinc-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--appkit-color-youtube)]", "aria-label": post.caption ?? `Watch on YouTube`, children: [_jsx("img", { src: thumbnail, alt: post.caption ?? "YouTube video", className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105", loading: "lazy" }), _jsx(Row, { className: "absolute inset-0", align: "center", justify: "center", children: _jsx(Row, { className: "h-14 w-14 bg-[var(--appkit-color-youtube)] transition-transform duration-200 group-hover:scale-110", align: "center", justify: "center", rounded: "full", shadow: "lg", children: _jsx("svg", { className: "ml-1 h-6 w-6 text-white", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M8 5v14l11-7z" }) }) }) }), _jsxs(Row, { textWeight: "medium", textSize: "xs", className: `absolute right-2 top-2 text-white px-[0.375rem] ${meta.colorClass}`, padding: "y-2xs", align: "center", gap: "xs", rounded: "default", children: [meta.icon, _jsx(Span, { className: "hidden sm:inline", children: meta.label })] }), post.channelName && (_jsx(Div, { textSize: "xs", surface: "overlay-lg", className: "absolute bottom-2 left-2 py-[0.125rem] text-white backdrop-blur-sm", padding: "x-xs", rounded: "default", children: post.channelName })), showCaption && post.caption && (_jsx(Stack, { justify: "end", surface: "overlay-lg", className: `absolute inset-0 ${__P.p3} opacity-0 transition-opacity duration-200 group-hover:opacity-100`, children: _jsx(Text, { color: "inverse", className: "line-clamp-2", size: "xs", children: post.caption }) }))] }));
50
+ return (_jsxs(Anchor, { href: href, tone: "none", underline: "none", className: "group relative block aspect-video w-full overflow-hidden rounded-xl bg-zinc-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--appkit-color-youtube)]", "aria-label": post.caption ?? `Watch on YouTube`, children: [_jsx(MediaImage, { src: thumbnail, alt: post.caption ?? "YouTube video", size: "card", className: "transition-transform duration-300 group-hover:scale-105" }), _jsx(Row, { className: "absolute inset-0", align: "center", justify: "center", children: _jsx(Row, { className: "h-14 w-14 bg-[var(--appkit-color-youtube)] transition-transform duration-200 group-hover:scale-110", align: "center", justify: "center", rounded: "full", shadow: "lg", children: _jsx("svg", { className: "ml-1 h-6 w-6 text-white", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M8 5v14l11-7z" }) }) }) }), _jsxs(Row, { textWeight: "medium", textSize: "xs", className: `absolute right-2 top-2 text-white px-[0.375rem] ${meta.colorClass}`, padding: "y-2xs", align: "center", gap: "xs", rounded: "default", children: [meta.icon, _jsx(Span, { className: "hidden sm:inline", children: meta.label })] }), post.channelName && (_jsx(Div, { textSize: "xs", surface: "overlay-lg", className: "absolute bottom-2 left-2 py-[0.125rem] text-white backdrop-blur-sm", padding: "x-xs", rounded: "default", children: post.channelName })), showCaption && post.caption && (_jsx(Stack, { justify: "end", surface: "overlay-lg", className: `absolute inset-0 ${__P.p3} opacity-0 transition-opacity duration-200 group-hover:opacity-100`, children: _jsx(Text, { color: "inverse", className: "line-clamp-2", size: "xs", children: post.caption }) }))] }));
50
51
  }
51
52
  export function SocialPostCard({ post, showCaption = true, showStats = true }) {
52
53
  if (post.platform === "youtube") {
53
54
  return _jsx(YouTubeCard, { post: post, showCaption: showCaption });
54
55
  }
55
56
  const meta = PLATFORM_META[post.platform];
56
- return (_jsxs("a", { href: post.permalink, target: "_blank", rel: "noopener noreferrer", className: "group relative block aspect-square overflow-hidden rounded-xl bg-[var(--appkit-color-surface)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--appkit-color-primary)] bg-[var(--appkit-color-surface-elevated)]", "aria-label": post.caption ?? `View on ${meta.label}`, children: [post.imageUrl ? (_jsx("img", { src: post.imageUrl, alt: post.caption ?? `${meta.label} post`, className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-105", loading: "lazy" })) : (_jsx(Row, { className: "h-full w-full", surface: "subtle", align: "center", justify: "center", children: _jsx(Span, { size: "sm", color: "faint", children: "No preview" }) })), post.mediaType === "video" && (_jsx(Row, { className: "pointer-events-none absolute inset-0", align: "center", justify: "center", children: _jsx(Row, { surface: "overlay-sm", className: "h-12 w-12 backdrop-blur-sm", align: "center", justify: "center", rounded: "full", children: _jsx("svg", { className: "ml-1 h-5 w-5 text-white", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M8 5v14l11-7z" }) }) }) })), post.mediaType === "carousel" && (_jsx(Div, { className: "pointer-events-none absolute right-2 top-2", children: _jsx(Row, { surface: "overlay-sm", className: "h-6 w-6 backdrop-blur-sm", rounded: "default", align: "center", justify: "center", children: _jsxs("svg", { className: "h-3.5 w-3.5 text-white", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "2", y: "3", width: "15", height: "15", rx: "2" }), _jsx("path", { d: "M7 8h14M7 12h14M7 16h14" })] }) }) })), _jsxs(Row, { textWeight: "medium", textSize: "xs", className: `absolute left-2 top-2 px-[0.375rem] text-white ${meta.colorClass}`, padding: "y-2xs", align: "center", gap: "xs", rounded: "default", children: [meta.icon, _jsx(Span, { className: "hidden sm:inline", children: meta.label })] }), _jsxs(Stack, { justify: "end", surface: "overlay-xl", className: `pointer-events-none absolute inset-0 ${__P.p3} opacity-0 transition-opacity duration-200 group-hover:opacity-100`, children: [showCaption && post.caption && (_jsx(Text, { color: "inverse", className: "mb-2 line-clamp-3", size: "xs", children: post.caption })), showStats && (_jsxs(Row, { textSize: "xs", className: "text-white/80", align: "center", gap: "3", children: [post.stats.likes != null && (_jsxs(Span, { layout: "flex", gap: "xs", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" }) }), formatCount(post.stats.likes)] })), post.stats.views != null && (_jsxs(Span, { layout: "flex", gap: "xs", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" }) }), formatCount(post.stats.views)] })), post.stats.comments != null && (_jsxs(Span, { layout: "flex", gap: "xs", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }), formatCount(post.stats.comments)] }))] }))] })] }));
57
+ return (_jsxs(Anchor, { href: post.permalink, tone: "none", underline: "none", className: "group relative block aspect-square overflow-hidden rounded-xl bg-[var(--appkit-color-surface)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--appkit-color-primary)] bg-[var(--appkit-color-surface-elevated)]", "aria-label": post.caption ?? `View on ${meta.label}`, children: [post.imageUrl ? (_jsx(MediaImage, { src: post.imageUrl, alt: post.caption ?? `${meta.label} post`, size: "card", className: "transition-transform duration-300 group-hover:scale-105" })) : (_jsx(Row, { className: "h-full w-full", surface: "subtle", align: "center", justify: "center", children: _jsx(Span, { size: "sm", color: "faint", children: "No preview" }) })), post.mediaType === "video" && (_jsx(Row, { className: "pointer-events-none absolute inset-0", align: "center", justify: "center", children: _jsx(Row, { surface: "overlay-sm", className: "h-12 w-12 backdrop-blur-sm", align: "center", justify: "center", rounded: "full", children: _jsx("svg", { className: "ml-1 h-5 w-5 text-white", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M8 5v14l11-7z" }) }) }) })), post.mediaType === "carousel" && (_jsx(Div, { className: "pointer-events-none absolute right-2 top-2", children: _jsx(Row, { surface: "overlay-sm", className: "h-6 w-6 backdrop-blur-sm", rounded: "default", align: "center", justify: "center", children: _jsxs("svg", { className: "h-3.5 w-3.5 text-white", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("rect", { x: "2", y: "3", width: "15", height: "15", rx: "2" }), _jsx("path", { d: "M7 8h14M7 12h14M7 16h14" })] }) }) })), _jsxs(Row, { textWeight: "medium", textSize: "xs", className: `absolute left-2 top-2 px-[0.375rem] text-white ${meta.colorClass}`, padding: "y-2xs", align: "center", gap: "xs", rounded: "default", children: [meta.icon, _jsx(Span, { className: "hidden sm:inline", children: meta.label })] }), _jsxs(Stack, { justify: "end", surface: "overlay-xl", className: `pointer-events-none absolute inset-0 ${__P.p3} opacity-0 transition-opacity duration-200 group-hover:opacity-100`, children: [showCaption && post.caption && (_jsx(Text, { color: "inverse", className: "mb-2 line-clamp-3", size: "xs", children: post.caption })), showStats && (_jsxs(Row, { textSize: "xs", className: "text-white/80", align: "center", gap: "3", children: [post.stats.likes != null && (_jsxs(Span, { layout: "flex", gap: "xs", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" }) }), formatCount(post.stats.likes)] })), post.stats.views != null && (_jsxs(Span, { layout: "flex", gap: "xs", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" }) }), formatCount(post.stats.views)] })), post.stats.comments != null && (_jsxs(Span, { layout: "flex", gap: "xs", children: [_jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "currentColor", children: _jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) }), formatCount(post.stats.comments)] }))] }))] })] }));
57
58
  }
@@ -20,6 +20,15 @@ export interface MediaImageProps {
20
20
  * Falls back to the per-size default icon.
21
21
  */
22
22
  fallback?: string;
23
+ /**
24
+ * Called when the resolved image fails to load (broken URL, 404, etc).
25
+ * Use this when the consumer needs a bespoke fallback (e.g. initials
26
+ * avatar) instead of MediaImage's own emoji placeholder — the emoji
27
+ * placeholder can render as an illegible "tiny dot" inside very small
28
+ * (≤40px) boxes. MediaImage still renders its own fallback state
29
+ * internally; this callback is purely a notification hook.
30
+ */
31
+ onError?: () => void;
23
32
  /**
24
33
  * Extra Tailwind classes applied to the absolute-fill wrapper div.
25
34
  * Use for hover animations, e.g. `group-hover:scale-110 transition-transform duration-300`.
@@ -42,5 +51,5 @@ export interface MediaImageProps {
42
51
  media?: string;
43
52
  }>;
44
53
  }
45
- export declare function MediaImage({ src, alt, size, priority, loading, objectFit, fallback, className, sources, }: MediaImageProps): import("react").JSX.Element;
54
+ export declare function MediaImage({ src, alt, size, priority, loading, objectFit, fallback, className, sources, onError, }: MediaImageProps): import("react").JSX.Element;
46
55
  export default MediaImage;
@@ -40,12 +40,16 @@ function resolveSrcSet(input) {
40
40
  .filter(Boolean)
41
41
  .join(", ");
42
42
  }
43
- export function MediaImage({ src, alt, size = "card", priority = false, loading, objectFit = "cover", fallback, className, sources, }) {
43
+ export function MediaImage({ src, alt, size = "card", priority = false, loading, objectFit = "cover", fallback, className, sources, onError, }) {
44
44
  const [hasError, setHasError] = useState(false);
45
45
  const [isLoaded, setIsLoaded] = useState(false);
46
46
  const icon = fallback ?? FALLBACK_ICONS[size];
47
47
  const fitClass = objectFit === "contain" ? "object-contain" : "object-cover";
48
48
  const resolvedSrc = resolveMediaUrl(src);
49
+ const handleError = () => {
50
+ setHasError(true);
51
+ onError?.();
52
+ };
49
53
  if (!resolvedSrc || hasError) {
50
54
  return (_jsx(Row, { className: `relative w-full h-full overflow-hidden text-zinc-400 text-4xl${className ? ` ${className}` : ""}`, align: "center", justify: "center", surface: "subtle", role: "img", "aria-label": alt, children: _jsx(Span, { "aria-hidden": "true", children: icon }) }));
51
55
  }
@@ -59,7 +63,7 @@ export function MediaImage({ src, alt, size = "card", priority = false, loading,
59
63
  return (_jsxs(Div, { className: `relative w-full h-full overflow-hidden${className ? ` ${className}` : ""}`, children: [!isLoaded && (_jsx(Div, { className: "absolute inset-0 animate-pulse", surface: "subtle", "aria-hidden": "true" })), _jsxs("picture", { children: [sources.map((source, index) => (_jsx("source", { srcSet: resolveSrcSet(source.srcSet), type: source.type, media: source.media }, `${source.media ?? ""}-${source.type ?? ""}-${index}`))), _jsx("img", { src: resolvedSrc, alt: alt, loading: loading ?? (priority ? "eager" : "lazy"), decoding: "async",
60
64
  // catalogued primitive for media (variant catalogue allows the inner
61
65
  // <img> here). Object-fit + className come from typed props.
62
- className: `absolute inset-0 w-full h-full ${fitClass}`, onLoad: () => setIsLoaded(true), onError: () => setHasError(true) })] })] }));
66
+ className: `absolute inset-0 w-full h-full ${fitClass}`, onLoad: () => setIsLoaded(true), onError: handleError })] })] }));
63
67
  }
64
68
  return (_jsxs(Div, { className: `relative w-full h-full overflow-hidden${className ? ` ${className}` : ""}`, children: [!isLoaded && (_jsx(Div, { className: "absolute inset-0 animate-pulse", surface: "subtle", "aria-hidden": "true" })), _jsx(Image, { src: resolvedSrc, alt: alt, fill: true, priority: priority, loading: loading ?? (priority ? "eager" : "lazy"), className: fitClass, sizes: SIZE_HINTS[size], onLoad: () => setIsLoaded(true), onError: () => setHasError(true), unoptimized: isSvg })] }));
65
69
  }
@@ -4,6 +4,7 @@ import { normalizeError } from "../../errors/normalize";
4
4
  import React, { useState, useCallback } from "react";
5
5
  import { Modal } from "../../ui/components/Modal";
6
6
  import { Button, Div, Input, Label, Row, Stack, Text, Alert } from "../../ui";
7
+ import { MediaImage } from "./MediaImage";
7
8
  import { MediaUploadField } from "./upload/MediaUploadField";
8
9
  import { useMediaUpload, useMediaCleanup } from "./index";
9
10
  const MEDIA_LIST_ENDPOINT = "/api/admin/media";
@@ -128,9 +129,7 @@ export function MediaPickerModal({ isOpen, onClose, onSelect, accept = "image/*"
128
129
  isSelected
129
130
  ? "border-primary-500 ring-2 ring-primary-300"
130
131
  : "border-[var(--appkit-color-border)]",
131
- ].join(" "), "aria-pressed": isSelected, children: [_jsx(Row, { className: "aspect-square w-full", surface: "subtle", align: "center", justify: "center", children: isImage ? (
132
- // eslint-disable-next-line @next/next/no-img-element
133
- _jsx("img", { src: f.downloadURL, alt: f.name, className: "h-full w-full object-cover", loading: "lazy" })) : (_jsx(Text, { size: "sm", color: "muted", children: f.contentType ?? "file" })) }), _jsx(Text, { className: "block truncate p-1.5 font-mono", size: "xs", children: f.name.split("/").pop() })] }, f.name));
132
+ ].join(" "), "aria-pressed": isSelected, children: [_jsx(Row, { className: "relative aspect-square w-full", surface: "subtle", align: "center", justify: "center", children: isImage ? (_jsx(MediaImage, { src: f.downloadURL, alt: f.name, size: "thumbnail" })) : (_jsx(Text, { size: "sm", color: "muted", children: f.contentType ?? "file" })) }), _jsx(Text, { className: "block truncate p-1.5 font-mono", size: "xs", children: f.name.split("/").pop() })] }, f.name));
134
133
  }), existingLoading && (_jsx(Text, { paddingY: "lg", className: "col-span-full", align: "center", color: "muted", children: "Loading\u2026" })), !existingLoading && filteredExisting.length === 0 && (_jsx(Text, { paddingY: "lg", className: "col-span-full", align: "center", color: "muted", children: "No files match." }))] })] })), tab === "upload" && (_jsx(MediaUploadField, { label: "File", value: uploadedUrl, onChange: setUploadedUrl, onUpload: handleUpload, accept: accept, maxSizeMB: maxSizeMB, onAbort: handleAbort })), tab === "url" && (_jsxs(Stack, { gap: "sm", children: [_jsx(Label, { htmlFor: "media-picker-url", children: "Image or video URL" }), _jsx(Input, { id: "media-picker-url", value: externalUrl, onChange: (e) => {
135
134
  setExternalUrl(e.target.value);
136
135
  setUrlError("");
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
- import { Alert, Button, Div, Label, Row, Spinner, Stack, Text } from "../../../ui";
4
+ import { Alert, Anchor, Button, Div, Label, Row, Spinner, Stack, Text } from "../../../ui";
5
5
  import { MediaImage } from "../MediaImage";
6
6
  import { MediaVideo } from "../MediaVideo";
7
7
  import { inferMediaTypeFromMime } from "../types/index";
@@ -107,5 +107,5 @@ export function MediaUploadList({ label, value, onChange, onUpload, accept = "im
107
107
  unstageUrl(target.url);
108
108
  }
109
109
  };
110
- return (_jsxs(Stack, { gap: "sm", children: [_jsx(Label, { className: "block", color: "muted", size: "sm", weight: "medium", children: label }), value.length > 0 && (_jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-1 sm:grid-cols-2", children: value.map((item, index) => (_jsxs(Div, { rounded: "lg", padding: "sm", border: "default", surface: "muted", children: [item.type === "video" ? (_jsx(Div, { className: `relative aspect-square max-h-24 ${__O.hidden}`, rounded: "lg", children: _jsx(MediaVideo, { src: item.url, alt: item.alt || `Media ${index + 1}`, controls: true }) })) : item.type === "image" ? (_jsx(Div, { className: `relative aspect-square max-h-24 ${__O.hidden}`, rounded: "lg", children: _jsx(MediaImage, { src: item.url, alt: item.alt || `Media ${index + 1}`, size: "card", objectFit: "contain" }) })) : (_jsx("a", { href: item.url, target: "_blank", rel: "noopener noreferrer", className: CLS_PDF_LINK, children: fileNameFromUrl(item.url) })), !disabled && (_jsx(Div, { className: "mt-2", children: _jsx(Button, { type: "button", size: "sm", variant: "danger", onClick: () => removeAt(index), children: "Remove" }) }))] }, `${item.url}-${index}`))) })), !disabled && value.length < maxItems && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => inputRef.current?.click(), disabled: isLoading, children: isLoading ? "Uploading..." : `+ Add Files (${value.length}/${maxItems})` })), isLoading && (_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Spinner, { size: "sm" }), _jsx(Text, { size: "sm", variant: "secondary", children: "Uploading" })] })), _jsx("input", { ref: inputRef, type: "file", multiple: true, accept: accept, onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-list-input" }), helperText && !error && (_jsx(Text, { variant: "secondary", size: "xs", children: helperText })), error && _jsx(Alert, { variant: "error", children: error })] }));
110
+ return (_jsxs(Stack, { gap: "sm", children: [_jsx(Label, { className: "block", color: "muted", size: "sm", weight: "medium", children: label }), value.length > 0 && (_jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-1 sm:grid-cols-2", children: value.map((item, index) => (_jsxs(Div, { rounded: "lg", padding: "sm", border: "default", surface: "muted", children: [item.type === "video" ? (_jsx(Div, { className: `relative aspect-square max-h-24 ${__O.hidden}`, rounded: "lg", children: _jsx(MediaVideo, { src: item.url, alt: item.alt || `Media ${index + 1}`, controls: true }) })) : item.type === "image" ? (_jsx(Div, { className: `relative aspect-square max-h-24 ${__O.hidden}`, rounded: "lg", children: _jsx(MediaImage, { src: item.url, alt: item.alt || `Media ${index + 1}`, size: "card", objectFit: "contain" }) })) : (_jsx(Anchor, { href: item.url, tone: "none", underline: "none", className: CLS_PDF_LINK, children: fileNameFromUrl(item.url) })), !disabled && (_jsx(Div, { className: "mt-2", children: _jsx(Button, { type: "button", size: "sm", variant: "danger", onClick: () => removeAt(index), children: "Remove" }) }))] }, `${item.url}-${index}`))) })), !disabled && value.length < maxItems && (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => inputRef.current?.click(), disabled: isLoading, children: isLoading ? "Uploading..." : `+ Add Files (${value.length}/${maxItems})` })), isLoading && (_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Spinner, { size: "sm" }), _jsx(Text, { size: "sm", variant: "secondary", children: "Uploading" })] })), _jsx("input", { ref: inputRef, type: "file", multiple: true, accept: accept, onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-list-input" }), helperText && !error && (_jsx(Text, { variant: "secondary", size: "xs", children: helperText })), error && _jsx(Alert, { variant: "error", children: error })] }));
111
111
  }
@@ -1,5 +1,6 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Div, Pagination, Row, Span, Stack, Text } from "../../../ui";
3
+ import { MediaImage } from "../../media/MediaImage";
3
4
  import { formatCurrency } from "../../../utils/number.formatter";
4
5
  import { getDefaultLocale } from "../../../core/baseline-resolver";
5
6
  const CLS_PRIZE_BADGE = "inline-flex items-center rounded-full bg-fuchsia-100 dark:bg-fuchsia-900/30 px-2.5 py-0.5 text-fuchsia-700 dark:text-fuchsia-300";
@@ -34,7 +35,7 @@ export function OrderCard({ order, onClick, labels = {} }) {
34
35
  .sort()[0];
35
36
  return (_jsxs(Div, { padding: "5", role: onClick ? "button" : undefined, tabIndex: onClick ? 0 : undefined, onKeyDown: onClick
36
37
  ? (e) => (e.key === "Enter" || e.key === " ") && onClick(order)
37
- : undefined, onClick: onClick ? () => onClick(order) : undefined, className: `border border-neutral-200 ${onClick ? "cursor-pointer transition hover:shadow-md" : ""}`, rounded: "xl", surface: "default", children: [_jsxs(Row, { wrap: true, align: "start", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: ["Order #", order.id.slice(-8).toUpperCase()] }), date && (_jsx(Text, { className: "mt-0.5", color: "faint", size: "xs", children: date }))] }), _jsx(Span, { size: "xs", weight: "semibold", className: `capitalize ${statusColor}`, rounded: "full", padding: "pill-md", children: labels[order.orderStatus] ?? order.orderStatus.replace(/_/g, " ") })] }), revealsRemaining > 0 && (_jsxs(Row, { gap: "sm", className: "mt-2", children: [_jsxs(Span, { size: "xs", weight: "semibold", className: CLS_PRIZE_BADGE, children: [revealsRemaining, " ", revealsRemaining === 1 ? "reveal" : "reveals", " pending"] }), earliestDeadline && (_jsxs(Span, { size: "xs", color: "muted", children: ["before ", new Date(earliestDeadline).toLocaleDateString(getDefaultLocale(), { month: "short", day: "numeric" })] }))] })), _jsxs(Row, { wrap: true, gap: "3", className: "mt-4", children: [order.items.slice(0, 3).map((item, i) => (_jsxs(Row, { gap: "sm", children: [item.image && (_jsx("img", { src: item.image, alt: item.title, className: "h-10 w-10 object-cover rounded-lg" })), _jsxs(Div, { children: [_jsx(Text, { truncate: true, size: "sm", weight: "medium", color: "primary", children: item.title }), _jsxs(Text, { size: "xs", color: "faint", children: ["\u00D7", item.quantity] })] })] }, i))), order.items.length > 3 && (_jsxs(Span, { size: "xs", className: "self-center", color: "faint", children: ["+", order.items.length - 3, " more"] }))] }), _jsxs(Row, { justify: "between", className: "mt-4 border-t border-neutral-100", padding: "t-sm", children: [_jsxs(Span, { size: "sm", className: "text-[var(--appkit-color-text-muted)]", children: [order.currency ?? "", " Total"] }), _jsx(Span, { weight: "semibold", className: "text-[var(--appkit-color-text)]", children: formatCurrency(order.total, order.currency) })] })] }));
38
+ : undefined, onClick: onClick ? () => onClick(order) : undefined, className: `border border-neutral-200 ${onClick ? "cursor-pointer transition hover:shadow-md" : ""}`, rounded: "xl", surface: "default", children: [_jsxs(Row, { wrap: true, align: "start", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: ["Order #", order.id.slice(-8).toUpperCase()] }), date && (_jsx(Text, { className: "mt-0.5", color: "faint", size: "xs", children: date }))] }), _jsx(Span, { size: "xs", weight: "semibold", className: `capitalize ${statusColor}`, rounded: "full", padding: "pill-md", children: labels[order.orderStatus] ?? order.orderStatus.replace(/_/g, " ") })] }), revealsRemaining > 0 && (_jsxs(Row, { gap: "sm", className: "mt-2", children: [_jsxs(Span, { size: "xs", weight: "semibold", className: CLS_PRIZE_BADGE, children: [revealsRemaining, " ", revealsRemaining === 1 ? "reveal" : "reveals", " pending"] }), earliestDeadline && (_jsxs(Span, { size: "xs", color: "muted", children: ["before ", new Date(earliestDeadline).toLocaleDateString(getDefaultLocale(), { month: "short", day: "numeric" })] }))] })), _jsxs(Row, { wrap: true, gap: "3", className: "mt-4", children: [order.items.slice(0, 3).map((item, i) => (_jsxs(Row, { gap: "sm", children: [item.image && (_jsx(Div, { className: "relative h-10 w-10 flex-shrink-0 overflow-hidden", rounded: "lg", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) })), _jsxs(Div, { children: [_jsx(Text, { truncate: true, size: "sm", weight: "medium", color: "primary", children: item.title }), _jsxs(Text, { size: "xs", color: "faint", children: ["\u00D7", item.quantity] })] })] }, i))), order.items.length > 3 && (_jsxs(Span, { size: "xs", className: "self-center", color: "faint", children: ["+", order.items.length - 3, " more"] }))] }), _jsxs(Row, { justify: "between", className: "mt-4 border-t border-neutral-100", padding: "t-sm", children: [_jsxs(Span, { size: "sm", className: "text-[var(--appkit-color-text-muted)]", children: [order.currency ?? "", " Total"] }), _jsx(Span, { weight: "semibold", className: "text-[var(--appkit-color-text)]", children: formatCurrency(order.total, order.currency) })] })] }));
38
39
  }
39
40
  export function OrdersList({ orders, isLoading, onOrderClick, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No orders found", }) {
40
41
  if (isLoading) {
@@ -54,6 +54,7 @@ function PreOrderInfoSection({ title, productionStatus, maxPerUser, deliveryDate
54
54
  returnable: "Cancellable",
55
55
  freeShipping: "Free Shipping",
56
56
  codAvailable: "Cash on Delivery",
57
+ emiAvailable: "EMI Available",
57
58
  wishlistCount: (n) => `${n} wishlisted`,
58
59
  categoryProductCount: (n, cat) => `${n} in ${cat}`,
59
60
  } }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && (_jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryName || category })), !category && categoryName && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: categoryName })), brand && brandSlug && (_jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand })), brand && !brandSlug && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand }))] })), productFeatures && features.length > 0 && (_jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures })), !productFeatures && features.length > 0 && (_jsxs(Div, { border: "subtle", surface: "muted", padding: "inline", rounded: "xl", children: [_jsx(Text, { className: "mb-2 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "About this product" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: features.map((f, i) => (_jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i))) })] })), descriptionHtml && (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-sm leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" })), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Sold by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && (_jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-3 py-1.5 text-xs font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" }))] }) }))] }));
@@ -184,9 +184,8 @@ export const POST = createRouteHandler({
184
184
  auth: true,
185
185
  roles: ["seller", "moderator", "admin"],
186
186
  schema: productMutateSchema,
187
- handler: async ({ user, body }) => {
187
+ handler: async ({ body }) => {
188
188
  const payload = body;
189
- const userRecord = (user ?? {});
190
189
  const { db } = getProviders();
191
190
  if (!db) {
192
191
  return NextResponse.json({ success: false, error: "Database provider not registered" }, { status: 503 });
@@ -13,6 +13,7 @@ const CLS_REMOVE_BTN = "shrink-0 rounded-full p-1 text-zinc-400 hover:bg-[var(--
13
13
  import Link from "next/link";
14
14
  import { X } from "lucide-react";
15
15
  import { Button, Div, Heading, Row, Span, Stack, Text } from "../../../ui";
16
+ import { MediaImage } from "../../media/MediaImage";
16
17
  import { useSwipe } from "../../../react/hooks/useSwipe";
17
18
  import { formatCurrency } from "../../../utils/number.formatter";
18
19
  import { COMPARE_MAX_ITEMS } from "../constants/action-defs";
@@ -57,9 +58,7 @@ function FieldRow({ label, children }) {
57
58
  function CompareColumn({ item, productType, labels, onRemove, onClose }) {
58
59
  const href = detailHref(item, productType);
59
60
  const img = item.mainImage ?? item.images?.[0];
60
- return (_jsxs(Stack, { gap: "md", className: COLUMN_CARD_CLASS, children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", children: [_jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, className: "text-sm font-semibold text-[var(--appkit-color-text)] hover:underline line-clamp-2", children: item.title ?? item.id }), onRemove && (_jsx("button", { type: "button", onClick: () => onRemove(item.id), "aria-label": `${labels.remove} ${item.title ?? item.id}`, className: CLS_REMOVE_BTN, children: _jsx(X, { className: "h-4 w-4" }) }))] }), _jsx(FieldRow, { label: labels.field.image, children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, children: _jsx(Div, { className: `relative aspect-square ${__O.hidden}`, rounded: "lg", surface: "subtle", children: img ? (
61
- // eslint-disable-next-line @next/next/no-img-element
62
- _jsx("img", { src: img, alt: item.title ?? item.id, className: "h-full w-full object-cover" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { size: "base", weight: "semibold", color: "primary", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx(Span, { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx(Span, { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) || (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) ? (_jsx(Span, { className: CHIP_CLASS, children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) ?? (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { size: "sm", color: "muted", children: item.storeName ?? "—" }) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, onClick: onClose, children: labels.view }) })] }));
61
+ return (_jsxs(Stack, { gap: "md", className: COLUMN_CARD_CLASS, children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", children: [_jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, className: "text-sm font-semibold text-[var(--appkit-color-text)] hover:underline line-clamp-2", children: item.title ?? item.id }), onRemove && (_jsx("button", { type: "button", onClick: () => onRemove(item.id), "aria-label": `${labels.remove} ${item.title ?? item.id}`, className: CLS_REMOVE_BTN, children: _jsx(X, { className: "h-4 w-4" }) }))] }), _jsx(FieldRow, { label: labels.field.image, children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, children: _jsx(Div, { className: `relative aspect-square ${__O.hidden}`, rounded: "lg", surface: "subtle", children: img ? (_jsx(MediaImage, { src: img, alt: item.title ?? item.id, size: "card" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { size: "base", weight: "semibold", color: "primary", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx(Span, { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx(Span, { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) || (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) ? (_jsx(Span, { className: CHIP_CLASS, children: (Array.isArray(item.categoryNames) ? item.categoryNames[0] : item.categoryName) ?? (Array.isArray(item.categorySlugs) ? item.categorySlugs[0] : item.category) })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { size: "sm", color: "muted", children: item.storeName ?? "—" }) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, onClick: onClose, children: labels.view }) })] }));
63
62
  }
64
63
  function useFetchedProducts(productIds, passedItems, isOpen) {
65
64
  const [fetched, setFetched] = useState([]);
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Row } from "@mohasinac/appkit/ui";
3
- import { Dd, Dl, Dt, RichText, Stack, Text } from "../../../ui";
3
+ import { Anchor, Dd, Dl, Dt, RichText, Stack, Text } from "../../../ui";
4
4
  import { normalizeRichTextHtml } from "../../../utils/string.formatter";
5
5
  function renderFieldValue(f) {
6
6
  if (f.type === "boolean")
@@ -10,5 +10,5 @@ function renderFieldValue(f) {
10
10
  export function CustomSectionTabContent({ section }) {
11
11
  const html = section.text ? normalizeRichTextHtml(section.text) : null;
12
12
  const fields = section.fields?.filter((f) => f.key && f.value) ?? [];
13
- return (_jsxs(Stack, { gap: "md", children: [html && (_jsx(RichText, { html: html, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })), fields.length > 0 && (_jsx(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden", children: fields.map((f, i) => (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { weight: "medium", className: "w-36 flex-shrink-0 capitalize", color: "primary", children: f.key }), _jsx(Dd, { className: "flex-1", color: "muted", children: f.type === "url" ? (_jsx("a", { href: f.value, target: "_blank", rel: "noopener noreferrer", className: "text-primary-600 hover:underline dark:text-primary-400 break-all", children: f.value })) : (renderFieldValue(f)) })] }, i))) })), !html && fields.length === 0 && (_jsx(Text, { size: "sm", color: "faint", children: "No content in this section." }))] }));
13
+ return (_jsxs(Stack, { gap: "md", children: [html && (_jsx(RichText, { html: html, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })), fields.length > 0 && (_jsx(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden", children: fields.map((f, i) => (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { weight: "medium", className: "w-36 flex-shrink-0 capitalize", color: "primary", children: f.key }), _jsx(Dd, { className: "flex-1", color: "muted", children: f.type === "url" ? (_jsx(Anchor, { href: f.value, tone: "none", underline: "hover", className: "text-primary-600 dark:text-primary-400 break-all", children: f.value })) : (renderFieldValue(f)) })] }, i))) })), !html && fields.length === 0 && (_jsx(Text, { size: "sm", color: "faint", children: "No content in this section." }))] }));
14
14
  }