@mohasinac/appkit 2.7.57 → 2.7.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +7 -1
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -2
- package/dist/_internal/server/features/scams/og.js +1 -1
- package/dist/_internal/shared/actions/bulk-helpers.d.ts +3 -0
- package/dist/_internal/shared/actions/bulk-helpers.js +19 -0
- package/dist/_internal/shared/config/schema.d.ts +4 -4
- package/dist/client.d.ts +4 -1
- package/dist/client.js +3 -1
- package/dist/configs/next.js +11 -0
- package/dist/configs/tailwind.d.ts +16 -3
- package/dist/configs/tailwind.js +18 -5
- package/dist/features/account/components/AddressBook.js +1 -1
- package/dist/features/account/components/AddressesIndexListing.js +3 -4
- package/dist/features/account/components/BuyerAccountGuideView.js +7 -7
- package/dist/features/account/components/BuyerAuctionsGuideView.js +7 -7
- package/dist/features/account/components/BuyerOrdersGuideView.js +6 -6
- package/dist/features/account/components/BuyerShoppingGuideView.js +7 -7
- package/dist/features/account/components/UserOffersPanel.js +1 -1
- package/dist/features/account/components/UserSidebar.js +14 -10
- package/dist/features/account/components/UserSupportView.js +4 -4
- package/dist/features/admin/components/AdminAdsView.js +1 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -10
- package/dist/features/admin/components/AdminAnalyticsGuideView.js +3 -3
- package/dist/features/admin/components/AdminBidsView.js +3 -9
- package/dist/features/admin/components/AdminBlogView.js +4 -9
- package/dist/features/admin/components/AdminBrandEditorView.js +14 -17
- package/dist/features/admin/components/AdminBrandsView.js +4 -9
- package/dist/features/admin/components/AdminBundlesView.js +40 -59
- package/dist/features/admin/components/AdminCarouselView.js +8 -21
- package/dist/features/admin/components/AdminCartsView.js +3 -4
- package/dist/features/admin/components/AdminCatalogGuideView.js +5 -5
- package/dist/features/admin/components/AdminCategoriesView.js +4 -9
- package/dist/features/admin/components/AdminCategoryEditorView.js +1 -5
- package/dist/features/admin/components/AdminContactEditorView.js +2 -2
- package/dist/features/admin/components/AdminContactView.js +7 -25
- package/dist/features/admin/components/AdminContentGuideView.js +6 -6
- package/dist/features/admin/components/AdminCouponEditorView.js +14 -17
- package/dist/features/admin/components/AdminCouponsView.js +8 -16
- package/dist/features/admin/components/AdminFaqEditorView.js +14 -17
- package/dist/features/admin/components/AdminFaqsView.js +18 -69
- package/dist/features/admin/components/AdminFeatureEditorView.js +7 -9
- package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -1
- package/dist/features/admin/components/AdminFeaturesView.js +1 -1
- package/dist/features/admin/components/AdminGuideHubView.js +1 -1
- package/dist/features/admin/components/AdminHistoryView.js +1 -1
- package/dist/features/admin/components/AdminListingScaffold.js +1 -1
- package/dist/features/admin/components/AdminNewsletterView.js +5 -15
- package/dist/features/admin/components/AdminNotificationsView.js +3 -9
- package/dist/features/admin/components/AdminOrdersGuideView.js +5 -5
- package/dist/features/admin/components/AdminOrdersView.js +3 -9
- package/dist/features/admin/components/AdminPayoutsView.js +3 -9
- package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -9
- package/dist/features/admin/components/AdminProductEditorView.js +1 -5
- package/dist/features/admin/components/AdminProductsView.js +5 -10
- package/dist/features/admin/components/AdminReturnRequestsView.js +1 -1
- package/dist/features/admin/components/AdminReviewsView.js +3 -9
- package/dist/features/admin/components/AdminScammerEditorView.js +3 -3
- package/dist/features/admin/components/AdminScammersView.js +6 -12
- package/dist/features/admin/components/AdminSectionsView.js +1 -1
- package/dist/features/admin/components/AdminSessionsView.js +3 -9
- package/dist/features/admin/components/AdminSiteSettingsView.js +1 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +1 -1
- package/dist/features/admin/components/AdminStoresGuideView.js +6 -6
- package/dist/features/admin/components/AdminStoresView.js +3 -9
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +3 -7
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +17 -20
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +4 -4
- package/dist/features/admin/components/AdminSupportTicketsView.js +9 -15
- package/dist/features/admin/components/AdminTeamGuideView.js +4 -4
- package/dist/features/admin/components/AdminTeamView.js +6 -15
- package/dist/features/admin/components/AdminTrustGuideView.js +5 -5
- package/dist/features/admin/components/AdminUserEditorView.js +1 -1
- package/dist/features/admin/components/AdminUsersGuideView.js +5 -5
- package/dist/features/admin/components/AdminUsersView.js +3 -9
- package/dist/features/admin/components/AdminViewCards.js +1 -1
- package/dist/features/admin/components/AdminWishlistsView.js +1 -1
- package/dist/features/admin/components/DataTable.js +1 -1
- package/dist/features/admin/hooks/useAdminListing.d.ts +58 -0
- package/dist/features/admin/hooks/useAdminListing.js +71 -0
- package/dist/features/auctions/components/AuctionBidsTable.js +1 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +2 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +1 -1
- package/dist/features/auth/components/VerifyEmailView.js +1 -1
- package/dist/features/auth/schemas/firestore.d.ts +1 -0
- package/dist/features/blog/components/BlogIndexListing.js +3 -6
- package/dist/features/blog/components/BlogListView.js +1 -1
- package/dist/features/blog/components/BlogPostView.js +1 -1
- package/dist/features/cart/components/CartDrawer.js +1 -1
- package/dist/features/categories/components/CategoriesIndexListing.js +3 -6
- package/dist/features/categories/components/CategoryDetailPageView.js +1 -1
- package/dist/features/checkout/components/CheckoutStepper.js +1 -1
- package/dist/features/collections/components/CollectionCard.js +1 -1
- package/dist/features/copilot/components/AdminCopilotView.js +2 -2
- package/dist/features/events/components/AdminEventsView.js +4 -4
- package/dist/features/events/components/EventPollWidget.js +2 -2
- package/dist/features/events/components/EventStatusBadge.js +4 -4
- package/dist/features/events/components/EventsIndexListing.js +3 -4
- package/dist/features/events/components/SpinWheelView.js +1 -1
- package/dist/features/faq/components/FAQAccordion.js +2 -2
- package/dist/features/forms/Input.js +3 -3
- package/dist/features/forms/Select.js +2 -2
- package/dist/features/forms/Textarea.js +2 -2
- package/dist/features/forms/utils.d.ts +1 -1
- package/dist/features/forms/utils.js +1 -1
- package/dist/features/homepage/components/AnnouncementBar.d.ts +4 -1
- package/dist/features/homepage/components/AnnouncementBar.js +51 -4
- package/dist/features/homepage/components/CharacterHotspotForm.js +2 -2
- package/dist/features/homepage/components/MarketplaceHomepageView.d.ts +3 -1
- package/dist/features/homepage/components/MarketplaceHomepageView.js +2 -2
- package/dist/features/homepage/components/index.d.ts +1 -1
- package/dist/features/homepage/components/index.js +1 -1
- package/dist/features/homepage/lib/section-renderer.d.ts +2 -2
- package/dist/features/homepage/lib/section-renderer.js +2 -2
- package/dist/features/layout/BottomActions.js +2 -2
- package/dist/features/layout/ListingLayout.js +3 -3
- package/dist/features/layout/TitleBarLayout.js +1 -1
- package/dist/features/media/index.d.ts +2 -0
- package/dist/features/media/index.js +1 -0
- package/dist/features/media/modals/ImageCropModal.js +1 -1
- package/dist/features/media/modals/ImageEditor.d.ts +12 -0
- package/dist/features/media/modals/ImageEditor.js +73 -0
- package/dist/features/media/upload/ImageUpload.d.ts +5 -1
- package/dist/features/media/upload/ImageUpload.js +33 -8
- package/dist/features/media/upload/MediaUploadField.d.ts +5 -1
- package/dist/features/media/upload/MediaUploadField.js +26 -12
- package/dist/features/orders/components/OrdersList.js +7 -7
- package/dist/features/orders/components/RefundHistoryTable.js +2 -2
- package/dist/features/orders/components/RefundRequestView.js +3 -3
- package/dist/features/orders/repository/orders.repository.d.ts +0 -1
- package/dist/features/orders/repository/orders.repository.js +0 -1
- package/dist/features/orders/server.d.ts +1 -1
- package/dist/features/orders/server.js +1 -1
- package/dist/features/pre-orders/components/PreOrderActionsClient.js +1 -1
- package/dist/features/pre-orders/components/PreOrderBadge.js +4 -4
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/pre-orders/components/PreorderCard.js +2 -2
- package/dist/features/products/components/CustomSectionsEditor.js +1 -1
- package/dist/features/products/components/GroupSettingsPanel.js +64 -48
- package/dist/features/products/components/MakeOfferButton.js +2 -2
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
- package/dist/features/products/components/PrizeDrawItemsEditor.js +3 -3
- package/dist/features/products/components/PrizeRevealModal.js +3 -3
- package/dist/features/products/components/ProductDetailPageView.js +4 -4
- package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
- package/dist/features/products/components/ProductGrid.js +3 -3
- package/dist/features/products/repository/products.repository.d.ts +0 -2
- package/dist/features/products/repository/products.repository.js +0 -2
- package/dist/features/products/server.d.ts +1 -1
- package/dist/features/products/server.js +1 -1
- package/dist/features/promotions/components/CouponCard.js +7 -7
- package/dist/features/promotions/components/CouponsIndexListing.js +3 -2
- package/dist/features/promotions/components/PromotionsView.js +1 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +2 -2
- package/dist/features/reviews/components/ReviewsIndexListing.js +3 -4
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/reviews/repository/reviews.repository.d.ts +0 -1
- package/dist/features/reviews/repository/reviews.repository.js +0 -1
- package/dist/features/reviews/server.d.ts +1 -1
- package/dist/features/reviews/server.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +11 -21
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +2 -2
- package/dist/features/seller/components/SellerAuctionsView.js +14 -23
- package/dist/features/seller/components/SellerBidsView.js +6 -6
- package/dist/features/seller/components/SellerBundlesView.js +13 -21
- package/dist/features/seller/components/SellerClassifiedView.js +14 -22
- package/dist/features/seller/components/SellerCouponEditorView.js +2 -2
- package/dist/features/seller/components/SellerCouponsView.js +14 -20
- package/dist/features/seller/components/SellerDigitalCodesView.js +13 -21
- package/dist/features/seller/components/SellerFeaturesView.js +1 -1
- package/dist/features/seller/components/SellerGroupedListingsView.js +6 -10
- package/dist/features/seller/components/SellerLiveView.js +15 -23
- package/dist/features/seller/components/SellerOffersPanel.js +2 -2
- package/dist/features/seller/components/SellerOffersView.js +4 -5
- package/dist/features/seller/components/SellerOrdersView.js +9 -23
- package/dist/features/seller/components/SellerPayoutMethodsView.js +13 -25
- package/dist/features/seller/components/SellerPayoutsView.js +4 -5
- package/dist/features/seller/components/SellerPreOrdersView.js +13 -22
- package/dist/features/seller/components/SellerPrizeDrawsView.js +13 -22
- package/dist/features/seller/components/SellerProductsCards.js +1 -1
- package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +1 -1
- package/dist/features/seller/components/SellerProductsFilterDrawer.js +3 -6
- package/dist/features/seller/components/SellerProductsView.js +10 -23
- package/dist/features/seller/components/SellerReviewsView.js +1 -1
- package/dist/features/seller/components/SellerShippingConfigsView.js +14 -26
- package/dist/features/seller/components/SellerStoreCategoriesView.js +16 -35
- package/dist/features/seller/components/SellerTemplatesView.js +27 -43
- package/dist/features/seller/components/analytics/SellerTopProducts.js +1 -1
- package/dist/features/stores/components/StoreFinanceGuideView.js +2 -2
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/stores/components/StoreListingsGuideView.js +3 -3
- package/dist/features/stores/components/StoreOrdersGuideView.js +4 -4
- package/dist/features/stores/components/StoreReviewsListing.js +3 -4
- package/dist/features/stores/components/StoreSettingsGuideView.js +2 -2
- package/dist/features/stores/components/StoresIndexListing.js +3 -4
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +4 -4
- package/dist/features/wishlist/components/WishlistPage.js +1 -1
- package/dist/index.d.ts +4 -6
- package/dist/index.js +6 -12
- package/dist/react/contexts/SessionContext.d.ts +2 -0
- package/dist/react/contexts/SessionContext.js +2 -0
- package/dist/react/hooks/useEntityDelete.d.ts +15 -0
- package/dist/react/hooks/useEntityDelete.js +39 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +2 -0
- package/dist/repositories/index.d.ts +3 -3
- package/dist/repositories/index.js +3 -3
- package/dist/styles.css +286 -3
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/motion.d.ts +162 -0
- package/dist/tokens/motion.js +89 -0
- package/dist/ui/DataTable.js +3 -2
- package/dist/ui/components/Accordion.js +2 -1
- package/dist/ui/components/Card.d.ts +5 -2
- package/dist/ui/components/Card.js +29 -11
- package/dist/ui/components/Div.d.ts +2 -16
- package/dist/ui/components/Div.js +2 -16
- package/dist/ui/components/Layout.d.ts +9 -19
- package/dist/ui/components/Layout.js +9 -7
- package/dist/ui/components/ListingFilterDrawer.d.ts +10 -0
- package/dist/ui/components/ListingFilterDrawer.js +8 -0
- package/dist/ui/components/Modal.js +7 -4
- package/dist/ui/components/Motion.d.ts +75 -0
- package/dist/ui/components/Motion.js +122 -0
- package/dist/ui/components/Semantic.d.ts +81 -14
- package/dist/ui/components/Semantic.js +75 -10
- package/dist/ui/components/Semantic.style.css +261 -2
- package/dist/ui/components/SideDrawer.d.ts +1 -1
- package/dist/ui/components/SideDrawer.js +8 -6
- package/dist/ui/components/SideModal.d.ts +1 -1
- package/dist/ui/components/SideModal.js +11 -9
- package/dist/ui/components/StackedViewShell.js +6 -1
- package/dist/ui/components/Toast.js +5 -2
- package/dist/ui/components/Typography.d.ts +44 -3
- package/dist/ui/components/Typography.js +15 -30
- package/dist/ui/components/Typography.style.css +24 -0
- package/dist/ui/components/VacationBanner.js +2 -1
- package/dist/ui/components/surface-tokens.d.ts +70 -0
- package/dist/ui/components/surface-tokens.js +68 -0
- package/dist/ui/forms/FormShell.d.ts +6 -1
- package/dist/ui/forms/FormShell.js +32 -4
- package/dist/ui/index.d.ts +11 -2
- package/dist/ui/index.js +5 -2
- package/dist/validation/schemas.d.ts +28 -28
- package/package.json +3 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { Plus
|
|
4
|
+
import { Plus } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
7
|
import { AdminViewCards } from "../../admin/components/AdminViewCards";
|
|
8
8
|
import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
9
|
-
import { BulkActionBar, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, SideDrawer } from "../../../ui";
|
|
9
|
+
import { BulkActionBar, Button, FilterChipGroup, ListingFilterDrawer, ListingToolbar, Pagination, ListingViewShell, SideDrawer } from "../../../ui";
|
|
10
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
11
|
import { ADMIN_EVENT_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
12
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../../admin/hooks/useAdminListingData";
|
|
@@ -108,5 +108,5 @@ export function AdminEventsView({ children, getRowHref, ...props }) {
|
|
|
108
108
|
] } } : {});
|
|
109
109
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events by title or type", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: openCreatePanel, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Event"] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
110
110
|
{ id: "delete", label: "Delete Selected", variant: "secondary", onClick: () => { selection.clearSelection(); } },
|
|
111
|
-
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-
|
|
111
|
+
] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-error-surface px-4 py-3 text-sm text-error dark:border-red-900/60", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No events found", onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No events found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsxs(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: [_jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Type", tabs: TYPE_OPTIONS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) })] }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Event" : "Edit Event", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminEventEditorView, { eventId: editId ?? undefined, onSaved: closePanel, embedded: true })) })] }));
|
|
112
112
|
}
|
|
@@ -62,7 +62,7 @@ export function EventPollWidget({ eventId, pollConfig, eventStatus, totalEntries
|
|
|
62
62
|
return (_jsxs("div", { className: `rounded-xl border border-zinc-200 dark:border-zinc-700 px-6 py-8 text-center space-y-3 ${className}`, "data-section": "eventpollwidget-div-4", children: [_jsx(Text, { className: "font-semibold text-zinc-900 dark:text-zinc-100", children: "Login to vote" }), _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", 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" })] }));
|
|
63
63
|
}
|
|
64
64
|
if (isSubmitted) {
|
|
65
|
-
return (_jsxs("div", { className: `rounded-xl border border-green-200 dark:border-green-800 bg-
|
|
65
|
+
return (_jsxs("div", { className: `rounded-xl border border-green-200 dark:border-green-800 bg-success-surface px-6 py-8 text-center space-y-2 ${className}`, "data-section": "eventpollwidget-div-5", children: [_jsx(Text, { className: "font-semibold text-success", children: "Vote recorded!" }), _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "Thanks for participating." })] }));
|
|
66
66
|
}
|
|
67
|
-
return (_jsxs("div", { className: `space-y-4 ${className}`, "data-section": "eventpollwidget-div-6", children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to vote in this poll. Please log in or create an account to continue." }), _jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-200", children: isMulti ? "Select all that apply:" : "Choose one:" }), _jsx("div", { className: "space-y-2", "data-section": "eventpollwidget-div-7", children: pollConfig.options.map((opt) => (_jsxs("label", { className: "flex items-center gap-3 cursor-pointer rounded-lg border border-zinc-200 dark:border-zinc-700 px-4 py-3 hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors", children: [_jsx("input", { type: isMulti ? "checkbox" : "radio", name: `poll-${eventId}`, value: opt.id, checked: selectedVotes.includes(opt.id), onChange: () => toggleVote(opt.id), className: "accent-primary" }), _jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: opt.label })] }, opt.id))) }), pollConfig.allowComment && (_jsx("textarea", { value: comment, onChange: (e) => setComment(e.target.value), placeholder: "Add a comment (optional)", rows: 3, className: "w-full rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-800 dark:text-zinc-200 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary" })), error && _jsx(Text, { className: "text-sm text-
|
|
67
|
+
return (_jsxs("div", { className: `space-y-4 ${className}`, "data-section": "eventpollwidget-div-6", children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to vote in this poll. Please log in or create an account to continue." }), _jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-200", children: isMulti ? "Select all that apply:" : "Choose one:" }), _jsx("div", { className: "space-y-2", "data-section": "eventpollwidget-div-7", children: pollConfig.options.map((opt) => (_jsxs("label", { className: "flex items-center gap-3 cursor-pointer rounded-lg border border-zinc-200 dark:border-zinc-700 px-4 py-3 hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors", children: [_jsx("input", { type: isMulti ? "checkbox" : "radio", name: `poll-${eventId}`, value: opt.id, checked: selectedVotes.includes(opt.id), onChange: () => toggleVote(opt.id), className: "accent-primary" }), _jsx("span", { className: "text-sm text-zinc-700 dark:text-zinc-300", children: opt.label })] }, opt.id))) }), pollConfig.allowComment && (_jsx("textarea", { value: comment, onChange: (e) => setComment(e.target.value), placeholder: "Add a comment (optional)", rows: 3, className: "w-full rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-800 dark:text-zinc-200 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary" })), error && _jsx(Text, { className: "text-sm text-error", children: error }), _jsx("button", { type: "button", onClick: handleSubmit, disabled: isLoading || selectedVotes.length === 0, className: "w-full rounded-xl bg-primary px-6 py-3 text-sm font-semibold text-white hover:bg-primary-600 disabled:opacity-60", children: isLoading ? "Submitting…" : "Submit Vote" })] }));
|
|
68
68
|
}
|
|
@@ -2,10 +2,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { Span } from "../../../ui";
|
|
3
3
|
const STATUS_MAP = {
|
|
4
4
|
draft: { label: "Draft", color: "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300" },
|
|
5
|
-
active: { label: "Active", color: "bg-
|
|
6
|
-
paused: { label: "Paused", color: "bg-
|
|
7
|
-
ended: { label: "Ended", color: "bg-
|
|
8
|
-
cancelled: { label: "Cancelled", color: "bg-
|
|
5
|
+
active: { label: "Active", color: "bg-success-surface text-success" },
|
|
6
|
+
paused: { label: "Paused", color: "bg-warning-surface text-warning" },
|
|
7
|
+
ended: { label: "Ended", color: "bg-error-surface text-error" },
|
|
8
|
+
cancelled: { label: "Cancelled", color: "bg-error-surface text-error" },
|
|
9
9
|
};
|
|
10
10
|
export function EventStatusBadge({ status, className = "", }) {
|
|
11
11
|
const { label, color } = STATUS_MAP[status] ?? STATUS_MAP.draft;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useMemo } from "react";
|
|
4
|
-
import { X } from "lucide-react";
|
|
5
4
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
5
|
import { useEvents } from "../hooks/useEvents";
|
|
7
|
-
import { ListingToolbar, Pagination, Text } from "../../../ui";
|
|
6
|
+
import { ListingFilterDrawer, ListingToolbar, Pagination, Text } from "../../../ui";
|
|
8
7
|
import { EventCard } from "./EventCard";
|
|
9
8
|
import { EventFilters, EVENT_PUBLIC_SORT_OPTIONS } from "./EventFilters";
|
|
10
9
|
import { EVENT_FIELDS } from "../../../constants/field-names";
|
|
@@ -116,5 +115,5 @@ export function EventsIndexListing({ initialData }) {
|
|
|
116
115
|
const commitSearch = useCallback(() => {
|
|
117
116
|
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
118
117
|
}, [searchInput, table]);
|
|
119
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || sortBy(EVENT_FIELDS.STARTS_AT, "ASC"), sortOptions: EVENT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-4 space-y-2", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded mt-2" })] })] }, i))) })) : events.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No events found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) }),
|
|
118
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || sortBy(EVENT_FIELDS.STARTS_AT, "ASC"), sortOptions: EVENT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-4 space-y-2", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded mt-2" })] })] }, i))) })) : events.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No events found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(EventFilters, { table: pendingTable, variant: "public" }) })] }));
|
|
120
119
|
}
|
|
@@ -69,5 +69,5 @@ export function SpinWheelView({ eventId, prizes, alreadyUsed, initialPrizeId, in
|
|
|
69
69
|
0% { transform: rotate(0deg); }
|
|
70
70
|
100% { transform: rotate(2160deg); }
|
|
71
71
|
}
|
|
72
|
-
` })] }), wonPrize ? (_jsxs(Div, { className: "rounded-xl border border-emerald-300 bg-
|
|
72
|
+
` })] }), wonPrize ? (_jsxs(Div, { className: "rounded-xl border border-emerald-300 bg-success-surface dark:border-emerald-700 p-4 text-center", children: [_jsx(Text, { className: "text-sm uppercase tracking-wide text-success", children: l.wonHeadline }), _jsx(Text, { className: "mt-1 text-lg font-bold text-emerald-900 dark:text-emerald-100", children: wonPrize.label }), resultCoupon ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "mt-2 text-sm text-emerald-800 dark:text-emerald-200", children: [l.couponHint, " ", _jsx(Span, { className: "font-mono font-semibold", children: resultCoupon })] }), _jsx(Div, { className: "mt-3 flex justify-center", children: _jsx(ClaimCouponButton, { couponCode: resultCoupon, size: "sm", source: "spin" }) })] })) : null] })) : null, error ? (_jsx(Text, { className: "text-sm text-rose-600 dark:text-rose-400 text-center", children: error })) : null, _jsx(Div, { className: "flex justify-center", children: _jsx(Button, { variant: "primary", size: "lg", onClick: handleSpin, disabled: disabled, children: spinning ? l.spinningButton : l.spinButton }) }), alreadyUsed ? (_jsx(Text, { className: "text-center text-sm text-zinc-500 dark:text-zinc-400", children: l.alreadySpun })) : !inWindow ? (_jsx(Text, { className: "text-center text-sm text-zinc-500 dark:text-zinc-400", children: l.outsideWindow })) : null] }));
|
|
73
73
|
}
|
|
@@ -8,9 +8,9 @@ function FAQAccordionItem({ faq, isOpen, onToggle, renderExpandedFooter, }) {
|
|
|
8
8
|
export function FAQAccordion({ faqs, className = "", renderExpandedFooter, labels, }) {
|
|
9
9
|
const [openId, setOpenId] = useState(null);
|
|
10
10
|
if (faqs.length === 0 && labels?.noResults) {
|
|
11
|
-
return (_jsx(Div, {
|
|
11
|
+
return (_jsx(Div, { surface: "card", padding: "xl", className: "text-center", children: _jsx(Text, { className: "text-neutral-600 dark:text-zinc-300", children: labels.noResults }) }));
|
|
12
12
|
}
|
|
13
|
-
return (_jsx(Div, { className: `divide-y divide-neutral-100 dark:divide-slate-700
|
|
13
|
+
return (_jsx(Div, { surface: "card", className: `divide-y divide-neutral-100 dark:divide-slate-700 px-5 ${className}`, children: faqs.map((faq) => (_jsx(FAQAccordionItem, { faq: faq, isOpen: openId === faq.id, onToggle: () => setOpenId(openId === faq.id ? null : faq.id), renderExpandedFooter: renderExpandedFooter }, faq.id))) }));
|
|
14
14
|
}
|
|
15
15
|
export function FAQCategoryTabs({ categories, active, onSelect, labels = {}, }) {
|
|
16
16
|
return (_jsxs(Row, { wrap: true, gap: "sm", className: "scrollbar-none", children: [_jsx(Button, { onClick: () => onSelect(null), className: `rounded-full px-4 py-1.5 text-sm font-medium transition ${!active ? "bg-neutral-900 text-white" : "bg-neutral-100 dark:bg-slate-800 text-neutral-600 dark:text-zinc-300 hover:bg-neutral-200 dark:hover:bg-slate-700"}`, children: labels.all ?? "All" }), categories.map((cat) => (_jsx(Button, { onClick: () => onSelect(cat), className: `whitespace-nowrap rounded-full px-4 py-1.5 text-sm font-medium capitalize transition ${active === cat ? "bg-neutral-900 text-white" : "bg-neutral-100 dark:bg-slate-800 text-neutral-600 dark:text-zinc-300 hover:bg-neutral-200 dark:hover:bg-slate-700"}`, children: labels[cat] ?? cat.replace(/_/g, " ") }, cat)))] }));
|
|
@@ -10,14 +10,14 @@ export const Input = React.forwardRef(function Input({ label, error, helperText,
|
|
|
10
10
|
: disabled
|
|
11
11
|
? INPUT_DISABLED
|
|
12
12
|
: "";
|
|
13
|
-
return (_jsxs(Div, { className: "w-full", children: [label && (_jsxs("label", { className: LABEL_BASE, children: [label, required && (_jsx(Span, { className: "text-
|
|
14
|
-
? "text-
|
|
13
|
+
return (_jsxs(Div, { className: "w-full", children: [label && (_jsxs("label", { className: LABEL_BASE, children: [label, required && (_jsx(Span, { className: "text-error ml-1", "aria-hidden": "true", children: "*" }))] })), _jsxs(Div, { className: "relative group", children: [icon && (_jsx(Div, { className: cn("absolute left-3 top-1/2 -translate-y-1/2 transition-colors duration-150", error
|
|
14
|
+
? "text-error"
|
|
15
15
|
: "text-zinc-400 dark:text-zinc-400", "group-focus-within:text-primary-500 dark:group-focus-within:text-secondary-400"), children: icon })), _jsx("input", { ref: ref, className: cn("w-full", INPUT_BASE, icon ? INPUT_WITH_ICON : "", rightIcon ? "pr-10" : "", stateClasses, className), disabled: disabled, "aria-invalid": error ? "true" : undefined, "aria-describedby": error
|
|
16
16
|
? `${props.id}-error`
|
|
17
17
|
: helperText
|
|
18
18
|
? `${props.id}-helper`
|
|
19
19
|
: undefined, ...props }), rightIcon && (_jsx(Div, { className: cn("absolute right-3 top-1/2 -translate-y-1/2 transition-colors duration-150", error
|
|
20
|
-
? "text-
|
|
20
|
+
? "text-error"
|
|
21
21
|
: "text-zinc-400 dark:text-zinc-400"), children: rightIcon }))] }), error && (_jsx(Text, { id: `${props.id}-error`, className: ERROR_BASE, role: "alert", children: error })), !error && helperText && (_jsx(Text, { id: `${props.id}-helper`, className: HELPER_BASE, children: helperText }))] }));
|
|
22
22
|
});
|
|
23
23
|
Input.displayName = "Input";
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Div, Span, Text } from "../../ui";
|
|
3
3
|
import { cn, INPUT_BASE, INPUT_ERROR, LABEL_BASE, HELPER_BASE, ERROR_BASE, } from "./utils";
|
|
4
4
|
export function Select({ label, error, helperText, options, placeholder, className = "", required, ...props }) {
|
|
5
|
-
return (_jsxs(Div, { className: "w-full", children: [label && (_jsxs("label", { className: LABEL_BASE, children: [label, required && (_jsx(Span, { className: "text-
|
|
6
|
-
? "text-
|
|
5
|
+
return (_jsxs(Div, { className: "w-full", children: [label && (_jsxs("label", { className: LABEL_BASE, children: [label, required && (_jsx(Span, { className: "text-error ml-1", "aria-hidden": "true", children: "*" }))] })), _jsxs(Div, { className: "relative group", children: [_jsxs("select", { className: cn(INPUT_BASE, "pr-10 appearance-none cursor-pointer", error ? INPUT_ERROR : "", className), "aria-invalid": error ? "true" : undefined, ...props, children: [placeholder && (_jsx("option", { value: "", disabled: true, children: placeholder })), options.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value)))] }), _jsx(Div, { className: "absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none transition-colors duration-150", children: _jsx("svg", { className: cn("w-5 h-5 transition-transform duration-200", error
|
|
6
|
+
? "text-error"
|
|
7
7
|
: "text-zinc-400 dark:text-zinc-400"), viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) }) })] }), error && (_jsx(Text, { className: ERROR_BASE, role: "alert", children: error })), !error && helperText && _jsx(Text, { className: HELPER_BASE, children: helperText })] }));
|
|
8
8
|
}
|
|
@@ -8,8 +8,8 @@ export const Textarea = React.forwardRef(function Textarea({ label, error, helpe
|
|
|
8
8
|
: typeof defaultValue === "string"
|
|
9
9
|
? defaultValue.length
|
|
10
10
|
: 0;
|
|
11
|
-
return (_jsxs(Div, { className: "w-full", children: [label && (_jsxs("label", { className: LABEL_BASE, children: [label, required && (_jsx(Span, { className: "text-
|
|
12
|
-
? "text-
|
|
11
|
+
return (_jsxs(Div, { className: "w-full", children: [label && (_jsxs("label", { className: LABEL_BASE, children: [label, required && (_jsx(Span, { className: "text-error ml-1", "aria-hidden": "true", children: "*" }))] })), _jsx("textarea", { ref: ref, className: cn(INPUT_BASE, "resize-y min-h-[80px]", error ? INPUT_ERROR : "", className), "aria-invalid": error ? "true" : undefined, maxLength: maxLength, value: value, defaultValue: defaultValue, ...props }), _jsxs(Div, { className: "flex items-center justify-between mt-1.5", children: [_jsxs(Div, { className: "flex-1", children: [error && (_jsx(Text, { className: ERROR_BASE, role: "alert", children: error })), !error && helperText && (_jsx(Text, { className: HELPER_BASE, children: helperText }))] }), showCharCount && maxLength && (_jsxs("p", { className: cn("text-xs tabular-nums ml-2 flex-shrink-0", charCount >= maxLength
|
|
12
|
+
? "text-error"
|
|
13
13
|
: "text-zinc-400 dark:text-zinc-400"), children: [charCount, " / ", maxLength] }))] })] }));
|
|
14
14
|
});
|
|
15
15
|
Textarea.displayName = "Textarea";
|
|
@@ -7,4 +7,4 @@ export declare const INPUT_DISABLED = "bg-zinc-50 dark:bg-slate-800/30 text-zinc
|
|
|
7
7
|
export declare const INPUT_WITH_ICON = "pl-10";
|
|
8
8
|
export declare const LABEL_BASE = "block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1.5";
|
|
9
9
|
export declare const HELPER_BASE = "text-xs text-zinc-500 dark:text-zinc-400 mt-1";
|
|
10
|
-
export declare const ERROR_BASE = "text-xs text-
|
|
10
|
+
export declare const ERROR_BASE = "text-xs text-error mt-1";
|
|
@@ -10,4 +10,4 @@ export const INPUT_DISABLED = "bg-zinc-50 dark:bg-slate-800/30 text-zinc-400 dar
|
|
|
10
10
|
export const INPUT_WITH_ICON = "pl-10";
|
|
11
11
|
export const LABEL_BASE = "block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1.5";
|
|
12
12
|
export const HELPER_BASE = "text-xs text-zinc-500 dark:text-zinc-400 mt-1";
|
|
13
|
-
export const ERROR_BASE = "text-xs text-
|
|
13
|
+
export const ERROR_BASE = "text-xs text-error mt-1";
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
export declare function hashBannerMessage(msg: string): string;
|
|
1
2
|
export interface AnnouncementBarProps {
|
|
2
3
|
message: string;
|
|
3
4
|
className?: string;
|
|
5
|
+
/** Called when the user dismisses — consumer wires this to a server action to persist. */
|
|
6
|
+
onDismiss?: (hash: string) => void;
|
|
4
7
|
}
|
|
5
|
-
export declare function AnnouncementBar({ message, className }: AnnouncementBarProps): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
export declare function AnnouncementBar({ message, className, onDismiss }: AnnouncementBarProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,10 +1,57 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from "react";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
4
|
import { X } from "lucide-react";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { useAuth } from "../../../react/contexts/SessionContext";
|
|
6
|
+
const STORAGE_KEY = "letitrip:announcement-dismissed";
|
|
7
|
+
const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000;
|
|
8
|
+
export function hashBannerMessage(msg) {
|
|
9
|
+
let h = 0;
|
|
10
|
+
for (let i = 0; i < msg.length; i++) {
|
|
11
|
+
h = (Math.imul(31, h) + msg.charCodeAt(i)) | 0;
|
|
12
|
+
}
|
|
13
|
+
return h.toString(36);
|
|
14
|
+
}
|
|
15
|
+
function isLocallyDismissed(message) {
|
|
16
|
+
try {
|
|
17
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
18
|
+
if (!raw)
|
|
19
|
+
return false;
|
|
20
|
+
const { hash, at } = JSON.parse(raw);
|
|
21
|
+
if (hash !== hashBannerMessage(message))
|
|
22
|
+
return false;
|
|
23
|
+
return Date.now() - at < ONE_WEEK_MS;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function saveLocalDismissal(message) {
|
|
30
|
+
try {
|
|
31
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify({ hash: hashBannerMessage(message), at: Date.now() }));
|
|
32
|
+
}
|
|
33
|
+
catch { /* quota exceeded — ignore */ }
|
|
34
|
+
}
|
|
35
|
+
export function AnnouncementBar({ message, className = "", onDismiss }) {
|
|
36
|
+
const { user } = useAuth();
|
|
37
|
+
const bannerHash = hashBannerMessage(message);
|
|
38
|
+
const userAlreadyDismissed = !!user?.dismissedBannerHash && user.dismissedBannerHash === bannerHash;
|
|
39
|
+
const [dismissed, setDismissed] = useState(userAlreadyDismissed);
|
|
40
|
+
const [mounted, setMounted] = useState(userAlreadyDismissed);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (userAlreadyDismissed) {
|
|
43
|
+
setDismissed(true);
|
|
44
|
+
}
|
|
45
|
+
else if (isLocallyDismissed(message)) {
|
|
46
|
+
setDismissed(true);
|
|
47
|
+
}
|
|
48
|
+
setMounted(true);
|
|
49
|
+
}, [message, userAlreadyDismissed]);
|
|
7
50
|
if (dismissed)
|
|
8
51
|
return null;
|
|
9
|
-
return (_jsx("div", { className: `relative left-1/2 right-1/2 w-screen -translate-x-1/2 bg-primary-700 ${className}`, role: "banner", "data-section": "announcementbar-div-310", children: _jsx("div", { className: "container mx-auto max-w-[1920px] px-4 sm:px-6 lg:px-8", "data-section": "announcementbar-div-311", children: _jsxs("div", { className: "relative flex items-center justify-center py-2 text-center text-sm font-medium text-white", "data-section": "announcementbar-div-312", children: [_jsx("span", { children: message }), _jsx("button", { type: "button", onClick: () =>
|
|
52
|
+
return (_jsx("div", { className: `relative left-1/2 right-1/2 w-screen -translate-x-1/2 bg-primary-700 transition-opacity duration-200 ${mounted ? "opacity-100" : "opacity-0"} ${className}`, role: "banner", "data-section": "announcementbar-div-310", children: _jsx("div", { className: "container mx-auto max-w-[1920px] px-4 sm:px-6 lg:px-8", "data-section": "announcementbar-div-311", children: _jsxs("div", { className: "relative flex items-center justify-center py-2 text-center text-sm font-medium text-white", "data-section": "announcementbar-div-312", children: [_jsx("span", { children: message }), _jsx("button", { type: "button", onClick: () => {
|
|
53
|
+
saveLocalDismissal(message);
|
|
54
|
+
onDismiss?.(bannerHash);
|
|
55
|
+
setDismissed(true);
|
|
56
|
+
}, className: "absolute right-0 top-1/2 -translate-y-1/2 rounded p-0.5 hover:bg-white/20 focus:outline-none focus:ring-2 focus:ring-white", "aria-label": "Dismiss announcement", children: _jsx(X, { className: "h-4 w-4" }) })] }) }) }));
|
|
10
57
|
}
|
|
@@ -146,7 +146,7 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
|
|
|
146
146
|
}, children: "Next: Place Pins \u2192" })] })] })), step === "place" && (_jsxs(Div, { className: CLS_STEP, style: {
|
|
147
147
|
borderColor: "var(--border-ink)",
|
|
148
148
|
background: "var(--surface-elevated)",
|
|
149
|
-
}, children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Place a Pin" }), _jsxs(Text, { className: "text-sm", style: { color: "var(--color-muted)" }, children: [_jsx("
|
|
149
|
+
}, children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Place a Pin" }), _jsxs(Text, { className: "text-sm", style: { color: "var(--color-muted)" }, children: [_jsx(Span, { weight: "bold", children: "Click anywhere on the image" }), " to drop a pin, or enter exact coordinates below."] }), _jsxs(Div, { ref: containerRef, className: CLS_IMG_WRAP, style: {
|
|
150
150
|
paddingTop: "56.25%",
|
|
151
151
|
cursor: "crosshair",
|
|
152
152
|
background: "#111",
|
|
@@ -220,7 +220,7 @@ export function CharacterHotspotForm({ initial, onUploadImage, onSave, onAfterSa
|
|
|
220
220
|
}, children: "Continue: Add Details \u2192" })] })] })] })), step === "details" && (_jsxs(Div, { className: CLS_STEP, style: {
|
|
221
221
|
borderColor: "var(--border-ink)",
|
|
222
222
|
background: "var(--surface-elevated)",
|
|
223
|
-
}, children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Pin Details" }), _jsxs(Text, { className: "text-sm", style: { color: "var(--color-muted)" }, children: ["Fill in the details for the pin at", " ", _jsxs("
|
|
223
|
+
}, children: [_jsx(Heading, { level: 2, className: "text-lg font-bold", children: "Pin Details" }), _jsxs(Text, { className: "text-sm", style: { color: "var(--color-muted)" }, children: ["Fill in the details for the pin at", " ", _jsxs(Span, { weight: "bold", children: [draftPos?.xPct, "%, ", draftPos?.yPct, "%"] }), "."] }), _jsx(Div, { className: "grid grid-cols-2 gap-3", children: [
|
|
224
224
|
{
|
|
225
225
|
label: "Name *",
|
|
226
226
|
value: draftName,
|
|
@@ -3,5 +3,7 @@ export type { MarketplaceHomepageViewAdSlots } from "../lib/section-renderer";
|
|
|
3
3
|
export interface MarketplaceHomepageViewProps {
|
|
4
4
|
adSlots?: import("../lib/section-renderer").MarketplaceHomepageViewAdSlots;
|
|
5
5
|
newsletterFormSlot?: React.ReactNode;
|
|
6
|
+
/** Callback when user dismisses announcement banner — wire to a server action to persist. */
|
|
7
|
+
onBannerDismiss?: (hash: string) => void;
|
|
6
8
|
}
|
|
7
|
-
export declare function MarketplaceHomepageView({ adSlots, newsletterFormSlot, }?: MarketplaceHomepageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
9
|
+
export declare function MarketplaceHomepageView({ adSlots, newsletterFormSlot, onBannerDismiss, }?: MarketplaceHomepageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
@@ -23,7 +23,7 @@ function toCarouselSlides(docs) {
|
|
|
23
23
|
updatedAt: updatedAt instanceof Date ? updatedAt.toISOString() : undefined,
|
|
24
24
|
}));
|
|
25
25
|
}
|
|
26
|
-
export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, } = {}) {
|
|
26
|
+
export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, onBannerDismiss, } = {}) {
|
|
27
27
|
const slides = await carouselRepository.getActiveSlides().catch(() => []);
|
|
28
28
|
const siteSettings = await siteSettingsRepository.getSingleton().catch(() => null);
|
|
29
29
|
const announcementMessage = siteSettings?.announcementBar?.message?.trim() ||
|
|
@@ -113,5 +113,5 @@ export async function MarketplaceHomepageView({ adSlots, newsletterFormSlot, } =
|
|
|
113
113
|
: undefined,
|
|
114
114
|
};
|
|
115
115
|
const carouselSlides = toCarouselSlides(slides);
|
|
116
|
-
return (_jsxs(Main, { children: [showAnnouncement ? _jsx(AnnouncementBar, { message: announcementMessage }) : null, orderedSections.map((section) => renderSection(section, adSlots, newsletterFormSlot ?? null, faqItems, carouselSlides, liveStats, sectionData))] }));
|
|
116
|
+
return (_jsxs(Main, { children: [showAnnouncement ? _jsx(AnnouncementBar, { message: announcementMessage, onDismiss: onBannerDismiss }) : null, orderedSections.map((section) => renderSection(section, adSlots, newsletterFormSlot ?? null, faqItems, carouselSlides, liveStats, sectionData))] }));
|
|
117
117
|
}
|
|
@@ -53,7 +53,7 @@ export { FeaturedResultsSection } from "./FeaturedResultsSection";
|
|
|
53
53
|
export type { HowItWorksInfoStep, HowItWorksInfoDetail, HowItWorksInfoViewProps, } from "./HowItWorksInfoView";
|
|
54
54
|
export { HowItWorksInfoView } from "./HowItWorksInfoView";
|
|
55
55
|
export type { AnnouncementBarProps } from "./AnnouncementBar";
|
|
56
|
-
export { AnnouncementBar } from "./AnnouncementBar";
|
|
56
|
+
export { AnnouncementBar, hashBannerMessage } from "./AnnouncementBar";
|
|
57
57
|
export type { ShopByCategorySectionProps } from "./ShopByCategorySection";
|
|
58
58
|
export { ShopByCategorySection } from "./ShopByCategorySection";
|
|
59
59
|
export type { FeaturedProductsSectionProps } from "./FeaturedProductsSection";
|
|
@@ -26,7 +26,7 @@ export { WhatsAppCommunitySection } from "./WhatsAppCommunitySection";
|
|
|
26
26
|
export { HomepageSkeleton } from "./HomepageSkeleton";
|
|
27
27
|
export { FeaturedResultsSection } from "./FeaturedResultsSection";
|
|
28
28
|
export { HowItWorksInfoView } from "./HowItWorksInfoView";
|
|
29
|
-
export { AnnouncementBar } from "./AnnouncementBar";
|
|
29
|
+
export { AnnouncementBar, hashBannerMessage } from "./AnnouncementBar";
|
|
30
30
|
export { ShopByCategorySection } from "./ShopByCategorySection";
|
|
31
31
|
export { FeaturedProductsSection } from "./FeaturedProductsSection";
|
|
32
32
|
export { FeaturedAuctionsSection } from "./FeaturedAuctionsSection";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { LiveStatsMap } from "./live-stats";
|
|
3
|
-
import { AnnouncementBar } from "../components/AnnouncementBar";
|
|
3
|
+
import { AnnouncementBar, hashBannerMessage } from "../components/AnnouncementBar";
|
|
4
4
|
import type { CarouselSlide } from "../types/index";
|
|
5
5
|
import type { HomepageSectionDocument } from "../schemas";
|
|
6
6
|
import type { ProductItem } from "../../products/types";
|
|
@@ -33,4 +33,4 @@ export type FaqItem = {
|
|
|
33
33
|
category: string;
|
|
34
34
|
};
|
|
35
35
|
export declare function renderSection(section: HomepageSectionDocument, adSlots: MarketplaceHomepageViewAdSlots | undefined, newsletterFormSlot: React.ReactNode, faqItems: FaqItem[], slides: CarouselSlide[], liveStats: LiveStatsMap, sectionData?: SectionData): React.ReactNode;
|
|
36
|
-
export { AnnouncementBar };
|
|
36
|
+
export { AnnouncementBar, hashBannerMessage };
|
|
@@ -3,7 +3,7 @@ import React from "react";
|
|
|
3
3
|
import { ROUTES } from "../../../next";
|
|
4
4
|
import { cleanTitle, parseWelcomeDescription } from "./section-helpers";
|
|
5
5
|
import { DEFAULT_TRUST_FEATURES, DEFAULT_SECURITY_ITEMS } from "./section-defaults";
|
|
6
|
-
import { AnnouncementBar } from "../components/AnnouncementBar";
|
|
6
|
+
import { AnnouncementBar, hashBannerMessage } from "../components/AnnouncementBar";
|
|
7
7
|
import { HeroCarousel } from "../components/HeroCarousel";
|
|
8
8
|
import { StatsCounterSection } from "../components/StatsCounterSection";
|
|
9
9
|
import { TrustFeaturesSection } from "../components/TrustFeaturesSection";
|
|
@@ -202,4 +202,4 @@ export function renderSection(section, adSlots, newsletterFormSlot, faqItems, sl
|
|
|
202
202
|
const adSlotKey = AD_SLOT_MAP[section.type];
|
|
203
203
|
return (_jsxs(React.Fragment, { children: [sectionElement, adSlots && adSlotKey !== undefined && adSlotKey in adSlots && adSlots[adSlotKey]] }, section.id));
|
|
204
204
|
}
|
|
205
|
-
export { AnnouncementBar };
|
|
205
|
+
export { AnnouncementBar, hashBannerMessage };
|
|
@@ -55,7 +55,7 @@ function BulkPickerPanel({ bulkActions, selectedActionId, pickerOpen, onSelect,
|
|
|
55
55
|
i > 0 ? "border-t border-zinc-100/80 dark:border-slate-800/80" : "",
|
|
56
56
|
isSelected ? "bg-zinc-50 dark:bg-slate-800/60" : "",
|
|
57
57
|
action.variant === "danger"
|
|
58
|
-
? "text-
|
|
58
|
+
? "text-error hover:bg-error-surface"
|
|
59
59
|
: "text-zinc-800 dark:text-zinc-100 hover:bg-zinc-50 dark:hover:bg-slate-800/60",
|
|
60
60
|
action.disabled || action.loading ? "opacity-50 cursor-not-allowed" : "",
|
|
61
61
|
]
|
|
@@ -133,7 +133,7 @@ export default function BottomActions() {
|
|
|
133
133
|
"bg-zinc-50 hover:bg-zinc-100 active:bg-zinc-200 dark:bg-slate-800/60 dark:hover:bg-slate-700/60",
|
|
134
134
|
"border-zinc-200 dark:border-slate-700",
|
|
135
135
|
selectedAction?.variant === "danger"
|
|
136
|
-
? "text-
|
|
136
|
+
? "text-error"
|
|
137
137
|
: "text-zinc-800 dark:text-zinc-100",
|
|
138
138
|
]
|
|
139
139
|
.filter(Boolean)
|
|
@@ -69,7 +69,7 @@ export function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filte
|
|
|
69
69
|
sidebarOpen
|
|
70
70
|
? "bg-primary/10 border-primary/30 text-primary dark:bg-primary/15 dark:border-primary/40"
|
|
71
71
|
: "border-zinc-200 dark:border-slate-700 text-zinc-600 dark:text-slate-300 hover:border-zinc-300 dark:hover:border-slate-600 hover:bg-zinc-50 dark:hover:bg-slate-800/60",
|
|
72
|
-
].join(" "), children: [l.filtersTitle, filterActiveCount > 0 && (_jsx(Span, { className: "inline-flex items-center justify-center w-4 h-4 text-[10px] font-bold rounded-full bg-primary text-white", children: filterActiveCount }))] })), searchSlot && _jsx(Div, { className: "flex-1 min-w-0", children: searchSlot }), _jsxs(Row, { className: "gap-1.5 flex-shrink-0", children: [sortSlot, viewToggleSlot && (_jsx(Row, { className: "gap-0.5 rounded-full
|
|
72
|
+
].join(" "), children: [l.filtersTitle, filterActiveCount > 0 && (_jsx(Span, { className: "inline-flex items-center justify-center w-4 h-4 text-[10px] font-bold rounded-full bg-primary text-white", children: filterActiveCount }))] })), searchSlot && _jsx(Div, { className: "flex-1 min-w-0", children: searchSlot }), _jsxs(Row, { className: "gap-1.5 flex-shrink-0", children: [sortSlot, viewToggleSlot && (_jsx(Row, { surface: "card", className: "gap-0.5 rounded-full p-0.5", children: viewToggleSlot })), actionsSlot] }), toolbarPaginationSlot && (_jsx(Div, { className: "ml-auto flex-shrink-0 pl-3 border-l border-zinc-200/70 dark:border-slate-700/70", children: toolbarPaginationSlot }))] }), _jsxs(Div, { className: "flex flex-col gap-2 md:hidden", children: [_jsxs(Row, { className: "gap-2", children: [hasFilter && (_jsxs(Button, { type: "button", onClick: () => setMobileFilterOpen(true), "aria-label": l.filtersTitle, variant: "ghost", size: "sm", className: [
|
|
73
73
|
"flex-shrink-0 flex items-center gap-1.5",
|
|
74
74
|
"rounded-full h-9 px-3 text-sm font-medium",
|
|
75
75
|
"border border-zinc-200 dark:border-slate-700",
|
|
@@ -85,7 +85,7 @@ export function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filte
|
|
|
85
85
|
sidebarOpen
|
|
86
86
|
? "w-60 xl:w-64 2xl:w-72 opacity-100"
|
|
87
87
|
: "w-0 opacity-0 pointer-events-none",
|
|
88
|
-
].join(" "), children: _jsxs(Div, { className: "w-60 xl:w-64 2xl:w-72
|
|
88
|
+
].join(" "), children: _jsxs(Div, { surface: "elevated", className: "w-60 xl:w-64 2xl:w-72 overflow-hidden", children: [_jsxs(Row, { justify: "between", className: "px-4 py-3 border-b border-zinc-100 dark:border-slate-800", children: [_jsx(Div, { className: "font-semibold text-sm", children: panelTitle }), filterActiveCount > 0 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onFilterClear, className: "text-xs text-primary hover:text-primary/80 hover:underline p-0 h-auto leading-none font-medium", children: l.clearAll }))] }), _jsx(Div, { className: "px-3 pt-5 pb-3 max-h-[calc(100vh-15rem)] overflow-y-auto space-y-4", children: filterContent }), _jsx(Div, { className: "px-3 pb-3 pt-2 border-t border-zinc-100 dark:border-slate-800", children: _jsx(Button, { type: "button", variant: "primary", className: "w-full rounded-xl bg-primary text-white py-2 px-3", onClick: onFilterApply, children: l.applyFilters }) })] }) })), _jsxs(Div, { className: "flex-1 min-w-0 space-y-3", children: [activeFiltersSlot, errorSlot ? (errorSlot) : (_jsxs(_Fragment, { children: [children, paginationSlot && _jsx(Div, { className: "pt-2", children: paginationSlot })] }))] })] }), toolbarPaginationSlot && (_jsx(Nav, { "aria-label": "Pagination", className: [
|
|
89
89
|
"fixed left-0 right-0 md:hidden",
|
|
90
90
|
isDashboard
|
|
91
91
|
? "bottom-0"
|
|
@@ -97,7 +97,7 @@ export function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filte
|
|
|
97
97
|
"shadow-[0_-2px_8px_rgba(0,0,0,0.06)] dark:shadow-[0_-2px_8px_rgba(0,0,0,0.20)]",
|
|
98
98
|
"h-10 flex items-center justify-center px-3 overflow-x-auto pb-px",
|
|
99
99
|
"[scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
100
|
-
].join(" "), children: toolbarPaginationSlot })), hasFilter && mobileFilterOpen && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "fixed inset-0 z-40 bg-black/40 lg:hidden", onClick: () => setMobileFilterOpen(false), "aria-hidden": "true" }), _jsxs(Div, { ref: mobileOverlayRef, className: "fixed inset-0 z-50 flex flex-col lg:hidden bg-white dark:bg-slate-950", role: "dialog", "aria-modal": "true", "aria-label": panelTitle, children: [_jsxs(Row, { justify: "between", className: "px-4 py-3 border-b border-zinc-100 dark:border-slate-800 flex-shrink-0", children: [_jsxs(Div, { className: "font-semibold", children: [panelTitle, filterActiveCount > 0 && (_jsx(Span, { className: "ml-2 inline-flex items-center justify-center w-5 h-5 text-[11px] font-bold rounded-full bg-primary text-white", children: filterActiveCount }))] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setMobileFilterOpen(false), "aria-label": l.close, className: "rounded-full w-8 h-8 p-0 flex items-center justify-center hover:bg-zinc-100 dark:hover:bg-slate-800", children: "x" })] }), _jsx(Div, { className: "flex-1 overflow-y-auto px-4 pt-6 pb-4 space-y-4", children: filterContent }), _jsxs(Div, { className: "flex-shrink-0 flex gap-3 px-4 py-4 border-t border-zinc-100 dark:border-slate-800
|
|
100
|
+
].join(" "), children: toolbarPaginationSlot })), hasFilter && mobileFilterOpen && (_jsxs(_Fragment, { children: [_jsx(Div, { className: "fixed inset-0 z-40 bg-black/40 lg:hidden", onClick: () => setMobileFilterOpen(false), "aria-hidden": "true" }), _jsxs(Div, { ref: mobileOverlayRef, className: "fixed inset-0 z-50 flex flex-col lg:hidden bg-white dark:bg-slate-950", role: "dialog", "aria-modal": "true", "aria-label": panelTitle, children: [_jsxs(Row, { justify: "between", className: "px-4 py-3 border-b border-zinc-100 dark:border-slate-800 flex-shrink-0", children: [_jsxs(Div, { className: "font-semibold", children: [panelTitle, filterActiveCount > 0 && (_jsx(Span, { className: "ml-2 inline-flex items-center justify-center w-5 h-5 text-[11px] font-bold rounded-full bg-primary text-white", children: filterActiveCount }))] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setMobileFilterOpen(false), "aria-label": l.close, className: "rounded-full w-8 h-8 p-0 flex items-center justify-center hover:bg-zinc-100 dark:hover:bg-slate-800", children: "x" })] }), _jsx(Div, { className: "flex-1 overflow-y-auto px-4 pt-6 pb-4 space-y-4", children: filterContent }), _jsxs(Div, { surface: "muted", className: "flex-shrink-0 flex gap-3 px-4 py-4 border-t border-zinc-100 dark:border-slate-800", children: [_jsx(Button, { type: "button", variant: "outline", className: "flex-1 rounded-xl bg-zinc-200 dark:bg-slate-700 py-2 px-3", onClick: () => {
|
|
101
101
|
onFilterClear?.();
|
|
102
102
|
}, children: l.clearAll }), _jsx(Button, { type: "button", variant: "primary", className: "flex-1 rounded-xl bg-primary text-white py-2 px-3", onClick: handleMobileApply, children: l.applyFilters })] })] })] }))] }));
|
|
103
103
|
}
|
|
@@ -31,7 +31,7 @@ export function TitleBarLayout({ onToggleSidebar, sidebarOpen, onSearchToggle, s
|
|
|
31
31
|
const profileEl = profileHref ? (_jsx(Link, { href: profileHref, "aria-label": user ? `Profile — ${user.displayName ?? user.email}` : "Sign in", className: `relative ${iconBtn}`, children: user?.photoURL ? (
|
|
32
32
|
// eslint-disable-next-line @next/next/no-img-element
|
|
33
33
|
_jsx("img", { src: user.photoURL, alt: user.displayName ?? "Profile", width: 28, height: 28, loading: "lazy", className: "w-7 h-7 rounded-full object-cover" })) : (_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0zM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632z" }) })) })) : null;
|
|
34
|
-
const authButtonsEl = !user && (loginHref || registerHref) ? (_jsxs(Row, { gap: "xs", className: "hidden lg:flex
|
|
34
|
+
const authButtonsEl = !user && (loginHref || registerHref) ? (_jsxs(Row, { gap: "xs", className: "hidden lg:flex", children: [loginHref && (_jsx(Link, { href: loginHref, className: "px-3 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-300 hover:text-primary-700 dark:hover:text-secondary-400 transition-colors rounded-lg hover:bg-primary-50 dark:hover:bg-slate-800", children: "Sign in" })), registerHref && (_jsx(Link, { href: registerHref, className: "px-3 py-1.5 text-sm font-semibold rounded-lg bg-primary text-white hover:bg-primary/90 transition-colors btn-glow shadow-sm", children: "Register" }))] })) : null;
|
|
35
35
|
const hasTb2 = !!(notificationsEl || wishlistEl || cartEl || profileEl);
|
|
36
36
|
// ── Render ───────────────────────────────────────────────────────────────────
|
|
37
37
|
return (_jsxs(BlockHeader, { id: id, className: `sticky top-0 z-50 bg-white/95 dark:bg-slate-950/95 backdrop-blur-md border-b border-zinc-100 dark:border-slate-800 shadow-sm ${className}`, children: [promoStripText && (_jsx(Div, { className: "bg-gradient-to-r from-primary-700 to-secondary-600 dark:from-primary-700 dark:to-cobalt-700 text-white text-xs py-1 text-center font-medium", children: promoStripText })), _jsxs(Div, { className: "container mx-auto px-4 sm:px-6 lg:px-8 max-w-[1920px]", children: [_jsxs(Row, { justify: "between", gap: "none", className: "relative h-14", children: [_jsx(Row, { gap: "3", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteLogo, { title: brandName, className: "h-7 md:h-9 lg:h-10" }) }) }), siteLogoUrl ? (_jsx(Div, { className: "absolute inset-y-0 left-1/2 -translate-x-1/2 flex items-center", children: _jsx(Link, { href: logoHref, "aria-label": brandName, className: "flex items-center transition-opacity hover:opacity-80", children: _jsx(SiteLogo, { src: siteLogoUrl, title: brandName, className: "h-7 md:h-9 lg:h-10" }) }) })) : (navSlot && _jsx(Div, { className: "hidden md:flex", children: navSlot })), _jsxs(Row, { gap: "xs", children: [devSlot, compareEl, notificationSlot, notificationsEl && _jsx(Div, { className: "hidden lg:flex", children: notificationsEl }), wishlistEl && _jsx(Div, { className: "hidden lg:flex", children: wishlistEl }), cartEl && _jsx(Div, { className: "hidden lg:flex", children: cartEl }), user
|
|
@@ -14,6 +14,8 @@ export { MediaPickerModal } from "./MediaPickerModal";
|
|
|
14
14
|
export type { MediaPickerModalProps } from "./MediaPickerModal";
|
|
15
15
|
export { ImageCropModal } from "./modals/ImageCropModal";
|
|
16
16
|
export type { ImageCropData, ImageCropModalProps, } from "./modals/ImageCropModal";
|
|
17
|
+
export { ImageEditor } from "./modals/ImageEditor";
|
|
18
|
+
export type { ImageEditorProps } from "./modals/ImageEditor";
|
|
17
19
|
export { VideoTrimModal } from "./modals/VideoTrimModal";
|
|
18
20
|
export type { VideoTrimModalProps } from "./modals/VideoTrimModal";
|
|
19
21
|
export { VideoThumbnailSelector } from "./modals/VideoThumbnailSelector";
|
|
@@ -11,6 +11,7 @@ export { useMediaUpload, useMediaAbort, useMediaCrop, useMediaTrim, useMediaClea
|
|
|
11
11
|
// Modals
|
|
12
12
|
export { MediaPickerModal } from "./MediaPickerModal";
|
|
13
13
|
export { ImageCropModal } from "./modals/ImageCropModal";
|
|
14
|
+
export { ImageEditor } from "./modals/ImageEditor";
|
|
14
15
|
export { VideoTrimModal } from "./modals/VideoTrimModal";
|
|
15
16
|
export { VideoThumbnailSelector } from "./modals/VideoThumbnailSelector";
|
|
16
17
|
// Upload components
|
|
@@ -95,5 +95,5 @@ export function ImageCropModal({ isOpen, imageUrl, onClose, onSave, initialCropD
|
|
|
95
95
|
left: `${position.x}%`,
|
|
96
96
|
top: `${position.y}%`,
|
|
97
97
|
transform: "translate(-50%, -50%)",
|
|
98
|
-
} }), _jsxs(Div, { className: "absolute inset-0 pointer-events-none", children: [_jsx(Div, { className: "absolute w-full h-full grid grid-cols-3 grid-rows-3", children: Array.from({ length: 9 }).map((_, i) => (_jsx(Div, { className: "border border-white/20 dark:border-slate-600/30" }, i))) }), _jsxs(Div, { className: "absolute inset-0 flex items-center justify-center", children: [_jsx(Div, { className: "w-6 h-px bg-white/50" }), _jsx(Div, { className: "absolute h-6 w-px bg-white/50" })] })] })] }), _jsxs(Div, { className: "space-y-1", children: [_jsxs(Row, { justify: "between", children: [_jsx(Text, { className: "text-xs font-medium", children: t("cropZoom") }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-slate-400", children: [Math.round(zoom * 100), "%"] })] }), _jsxs(Row, { className: "gap-3", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setZoom(Math.max(0.1, zoom - 0.1)), "aria-label": t("cropZoomOut"), children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7" }) }) }), _jsx(MediaSlider, { value: zoom, min: 0.1, max: 3, step: 0.01, onChange: (v) => setZoom(v), className: "flex-1" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setZoom(Math.min(3, zoom + 0.1)), "aria-label": t("cropZoomIn"), children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7" }) }) })] }), _jsx(Div, { className: "flex gap-2", children: [0.5, 1, 1.5, 2].map((preset) => (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setZoom(preset), className: "px-3 py-1 text-xs rounded-lg bg-zinc-100 dark:bg-slate-800", children: [preset * 100, "%"] }, preset))) })] }), showZoomWarning && (_jsxs(Alert, { variant: "warning", children: [_jsx("
|
|
98
|
+
} }), _jsxs(Div, { className: "absolute inset-0 pointer-events-none", children: [_jsx(Div, { className: "absolute w-full h-full grid grid-cols-3 grid-rows-3", children: Array.from({ length: 9 }).map((_, i) => (_jsx(Div, { className: "border border-white/20 dark:border-slate-600/30" }, i))) }), _jsxs(Div, { className: "absolute inset-0 flex items-center justify-center", children: [_jsx(Div, { className: "w-6 h-px bg-white/50" }), _jsx(Div, { className: "absolute h-6 w-px bg-white/50" })] })] })] }), _jsxs(Div, { className: "space-y-1", children: [_jsxs(Row, { justify: "between", children: [_jsx(Text, { className: "text-xs font-medium", children: t("cropZoom") }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-slate-400", children: [Math.round(zoom * 100), "%"] })] }), _jsxs(Row, { className: "gap-3", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setZoom(Math.max(0.1, zoom - 0.1)), "aria-label": t("cropZoomOut"), children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7" }) }) }), _jsx(MediaSlider, { value: zoom, min: 0.1, max: 3, step: 0.01, onChange: (v) => setZoom(v), className: "flex-1" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setZoom(Math.min(3, zoom + 0.1)), "aria-label": t("cropZoomIn"), children: _jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v3m0 0v3m0-3h3m-3 0H7" }) }) })] }), _jsx(Div, { className: "flex gap-2", children: [0.5, 1, 1.5, 2].map((preset) => (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setZoom(preset), className: "px-3 py-1 text-xs rounded-lg bg-zinc-100 dark:bg-slate-800", children: [preset * 100, "%"] }, preset))) })] }), showZoomWarning && (_jsxs(Alert, { variant: "warning", children: [_jsx(Span, { weight: "bold", children: t("cropWarningTitle") }), " ", t("cropWarningMessage")] })), _jsxs(Row, { justify: "between", className: "text-xs text-zinc-500 dark:text-slate-400", children: [_jsxs(Span, { children: [t("cropPosition"), ": ", Math.round(position.x), "%, ", Math.round(position.y), "%"] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleReset, className: "text-primary-600 dark:text-primary-400 hover:underline", children: t("cropReset") })] }), _jsxs(Div, { className: "flex gap-2 pt-2", children: [_jsx(Button, { onClick: handleSave, variant: "primary", className: "flex-1", children: t("cropSave") }), _jsx(Button, { onClick: onClose, variant: "secondary", className: "flex-1", children: t("cropCancel") })] })] }) }));
|
|
99
99
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "react-advanced-cropper/dist/style.css";
|
|
2
|
+
export interface ImageEditorProps {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
imageUrl: string;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
onSave: (croppedFile: File) => void;
|
|
7
|
+
aspectRatio?: number;
|
|
8
|
+
outputFormat?: "image/jpeg" | "image/png" | "image/webp";
|
|
9
|
+
outputQuality?: number;
|
|
10
|
+
maxOutputWidth?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function ImageEditor({ isOpen, imageUrl, onClose, onSave, aspectRatio, outputFormat, outputQuality, maxOutputWidth, }: ImageEditorProps): import("react/jsx-runtime").JSX.Element;
|