@mohasinac/appkit 2.7.53 → 2.7.55
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.js +1 -1
- package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -3
- package/dist/_internal/server/jobs/core/bundleStockSync.js +9 -7
- package/dist/_internal/server/jobs/core/onProductStockChange.d.ts +12 -3
- package/dist/_internal/server/jobs/core/onProductStockChange.js +12 -13
- package/dist/_internal/server/jobs/core/onProductWrite.js +1 -3
- package/dist/_internal/server/jobs/handlers/onProductStockChange.js +11 -2
- package/dist/_internal/server/jobs/runtime/adapters/firebase.js +5 -14
- package/dist/_internal/shared/actions/action-registry.js +827 -4
- package/dist/_internal/shared/features/events/schema.d.ts +4 -4
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/api-endpoints.d.ts +3 -0
- package/dist/constants/api-endpoints.js +1 -0
- package/dist/constants/field-names.d.ts +4 -3
- package/dist/constants/field-names.js +8 -8
- package/dist/errors/error-codes.d.ts +3 -0
- package/dist/errors/error-codes.js +3 -0
- package/dist/errors/messages.d.ts +3 -0
- package/dist/errors/messages.js +3 -0
- package/dist/features/account/components/UserOffersPanel.js +2 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -3
- package/dist/features/admin/components/AdminBidsView.js +5 -3
- package/dist/features/admin/components/AdminBlogView.js +3 -2
- package/dist/features/admin/components/AdminBrandsView.js +2 -1
- package/dist/features/admin/components/AdminBundleEditorView.js +4 -5
- package/dist/features/admin/components/AdminBundlesView.js +3 -3
- package/dist/features/admin/components/AdminCarouselView.d.ts +2 -1
- package/dist/features/admin/components/AdminCarouselView.js +14 -5
- package/dist/features/admin/components/AdminCategoriesView.js +2 -1
- package/dist/features/admin/components/AdminContactView.d.ts +4 -1
- package/dist/features/admin/components/AdminContactView.js +27 -7
- package/dist/features/admin/components/AdminCouponsView.d.ts +3 -1
- package/dist/features/admin/components/AdminCouponsView.js +17 -3
- package/dist/features/admin/components/AdminFaqsView.d.ts +3 -1
- package/dist/features/admin/components/AdminFaqsView.js +18 -4
- package/dist/features/admin/components/AdminFeaturesView.js +2 -1
- package/dist/features/admin/components/AdminMediaView.js +3 -2
- package/dist/features/admin/components/AdminNavigationView.js +3 -2
- package/dist/features/admin/components/AdminNewsletterView.d.ts +2 -1
- package/dist/features/admin/components/AdminNewsletterView.js +13 -4
- package/dist/features/admin/components/AdminNotificationsView.js +6 -4
- package/dist/features/admin/components/AdminOrdersView.js +4 -3
- package/dist/features/admin/components/AdminPayoutsView.js +2 -2
- package/dist/features/admin/components/AdminPrizeDrawsView.js +2 -1
- package/dist/features/admin/components/AdminProductsView.js +3 -3
- package/dist/features/admin/components/AdminReviewsView.js +4 -3
- package/dist/features/admin/components/AdminScammersView.js +2 -1
- package/dist/features/admin/components/AdminSessionsView.js +5 -3
- package/dist/features/admin/components/AdminStoresView.js +2 -2
- package/dist/features/admin/components/AdminSublistingCategoriesView.d.ts +5 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +12 -3
- package/dist/features/admin/components/AdminSupportTicketsView.js +2 -1
- package/dist/features/admin/components/AdminTeamView.d.ts +2 -1
- package/dist/features/admin/components/AdminTeamView.js +13 -4
- package/dist/features/admin/components/AdminUsersView.js +2 -2
- package/dist/features/admin/types/product.types.d.ts +1 -1
- package/dist/features/admin/types/product.types.js +2 -3
- package/dist/features/auctions/actions/bid-actions.d.ts +9 -0
- package/dist/features/auctions/actions/bid-actions.js +61 -0
- package/dist/features/auctions/components/AuctionDetailPageView.d.ts +2 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +5 -4
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +3 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +32 -2
- package/dist/features/auctions/types/index.d.ts +1 -1
- package/dist/features/layout/AppLayoutShell.js +1 -1
- package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -11
- package/dist/features/products/api/[id]/route.js +4 -8
- package/dist/features/products/api/route.js +1 -3
- package/dist/features/products/components/AuctionsIndexListing.js +3 -11
- package/dist/features/products/components/PrizeDrawsIndexListing.js +2 -10
- package/dist/features/products/components/ProductFilters.d.ts +33 -9
- package/dist/features/products/components/ProductFilters.js +6 -29
- package/dist/features/products/components/ProductForm.js +2 -3
- package/dist/features/products/constants/action-defs.d.ts +86 -7
- package/dist/features/products/constants/action-defs.js +99 -7
- package/dist/features/products/constants/sieve.d.ts +162 -0
- package/dist/features/products/constants/sieve.js +95 -0
- package/dist/features/products/repository/products.repository.d.ts +5 -0
- package/dist/features/products/repository/products.repository.js +23 -3
- package/dist/features/products/schemas/firestore.d.ts +4 -4
- package/dist/features/products/schemas/firestore.js +2 -3
- package/dist/features/products/schemas/index.d.ts +6 -6
- package/dist/features/products/schemas/index.js +2 -6
- package/dist/features/products/types/index.d.ts +1 -1
- package/dist/features/seller/actions/offer-actions.js +2 -2
- package/dist/features/seller/components/QuickProductForm.d.ts +13 -0
- package/dist/features/seller/components/QuickProductForm.js +50 -0
- package/dist/features/seller/components/SellerAuctionsView.d.ts +4 -1
- package/dist/features/seller/components/SellerAuctionsView.js +37 -3
- package/dist/features/seller/components/SellerBidsView.js +2 -1
- package/dist/features/seller/components/SellerBundlesView.d.ts +3 -1
- package/dist/features/seller/components/SellerBundlesView.js +38 -5
- package/dist/features/seller/components/SellerClassifiedView.d.ts +3 -1
- package/dist/features/seller/components/SellerClassifiedView.js +37 -4
- package/dist/features/seller/components/SellerDigitalCodesView.d.ts +3 -1
- package/dist/features/seller/components/SellerDigitalCodesView.js +37 -4
- package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -1
- package/dist/features/seller/components/SellerGroupedListingsView.js +3 -2
- package/dist/features/seller/components/SellerLiveView.d.ts +3 -1
- package/dist/features/seller/components/SellerLiveView.js +37 -4
- package/dist/features/seller/components/SellerOffersPanel.js +2 -1
- package/dist/features/seller/components/SellerOffersView.d.ts +4 -1
- package/dist/features/seller/components/SellerOffersView.js +8 -3
- package/dist/features/seller/components/SellerPayoutsView.d.ts +3 -1
- package/dist/features/seller/components/SellerPayoutsView.js +7 -3
- package/dist/features/seller/components/SellerPreOrdersView.d.ts +2 -1
- package/dist/features/seller/components/SellerPreOrdersView.js +30 -7
- package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +2 -1
- package/dist/features/seller/components/SellerPrizeDrawsView.js +30 -7
- package/dist/features/seller/components/SellerProductShell.js +12 -0
- package/dist/features/seller/components/SellerReviewsView.js +2 -1
- package/dist/features/seller/components/SellerShippingConfigsView.js +3 -2
- package/dist/features/seller/components/SellerStoreCategoriesView.js +6 -5
- package/dist/features/seller/components/SellerTemplatesView.js +4 -3
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +30 -16
- package/dist/features/wishlist/types/index.d.ts +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -4
- package/dist/seed/addresses-seed-data.d.ts +2 -18
- package/dist/seed/addresses-seed-data.js +137 -203
- package/dist/seed/bids-seed-data.d.ts +2 -8
- package/dist/seed/bids-seed-data.js +172 -648
- package/dist/seed/blog-posts-seed-data.d.ts +0 -5
- package/dist/seed/blog-posts-seed-data.js +341 -596
- package/dist/seed/carousel-slides-seed-data.d.ts +0 -5
- package/dist/seed/carousel-slides-seed-data.js +61 -192
- package/dist/seed/carousels-seed-data.js +18 -1
- package/dist/seed/cart-seed-data.d.ts +1 -18
- package/dist/seed/cart-seed-data.js +116 -293
- package/dist/seed/categories-seed-data.d.ts +0 -6
- package/dist/seed/categories-seed-data.js +1409 -1537
- package/dist/seed/claimed-coupons-seed-data.d.ts +0 -11
- package/dist/seed/claimed-coupons-seed-data.js +32 -29
- package/dist/seed/conversations-seed-data.d.ts +0 -6
- package/dist/seed/conversations-seed-data.js +132 -280
- package/dist/seed/coupon-usage-seed-data.d.ts +0 -16
- package/dist/seed/coupon-usage-seed-data.js +30 -44
- package/dist/seed/coupons-seed-data.d.ts +0 -6
- package/dist/seed/coupons-seed-data.js +141 -489
- package/dist/seed/events-seed-data.d.ts +0 -6
- package/dist/seed/events-seed-data.js +201 -379
- package/dist/seed/faq-seed-data.d.ts +0 -14
- package/dist/seed/faq-seed-data.js +609 -1184
- package/dist/seed/grouped-listings-seed-data.d.ts +119 -8
- package/dist/seed/grouped-listings-seed-data.js +148 -233
- package/dist/seed/history-seed-data.d.ts +0 -7
- package/dist/seed/history-seed-data.js +50 -54
- package/dist/seed/homepage-sections-seed-data.d.ts +0 -5
- package/dist/seed/homepage-sections-seed-data.js +83 -175
- package/dist/seed/index.d.ts +1 -1
- package/dist/seed/index.js +1 -1
- package/dist/seed/manifest.js +2 -2
- package/dist/seed/notifications-seed-data.d.ts +0 -6
- package/dist/seed/notifications-seed-data.js +309 -196
- package/dist/seed/offers-seed-data.d.ts +0 -8
- package/dist/seed/offers-seed-data.js +118 -197
- package/dist/seed/orders-seed-data.d.ts +2 -8
- package/dist/seed/orders-seed-data.js +248 -1510
- package/dist/seed/payouts-seed-data.d.ts +0 -6
- package/dist/seed/payouts-seed-data.js +102 -441
- package/dist/seed/products-auctions-seed-data.d.ts +1 -14
- package/dist/seed/products-auctions-seed-data.js +612 -1084
- package/dist/seed/products-preorders-seed-data.d.ts +119 -14
- package/dist/seed/products-preorders-seed-data.js +154 -455
- package/dist/seed/products-standard-seed-data.d.ts +1 -15
- package/dist/seed/products-standard-seed-data.js +1355 -4128
- package/dist/seed/reviews-seed-data.d.ts +2 -8
- package/dist/seed/reviews-seed-data.js +99 -1182
- package/dist/seed/scammers-seed-data.d.ts +0 -15
- package/dist/seed/scammers-seed-data.js +36 -37
- package/dist/seed/sessions-seed-data.d.ts +0 -13
- package/dist/seed/sessions-seed-data.js +58 -335
- package/dist/seed/site-settings-seed-data.d.ts +0 -4
- package/dist/seed/site-settings-seed-data.js +61 -52
- package/dist/seed/store-addresses-seed-data.js +27 -151
- package/dist/seed/store-extensions-seed-data.d.ts +0 -7
- package/dist/seed/store-extensions-seed-data.js +147 -261
- package/dist/seed/stores-seed-data.d.ts +0 -5
- package/dist/seed/stores-seed-data.js +222 -184
- package/dist/seed/sub-listings-seed-data.d.ts +119 -0
- package/dist/seed/sub-listings-seed-data.js +447 -0
- package/dist/seed/support-tickets-seed-data.d.ts +0 -7
- package/dist/seed/support-tickets-seed-data.js +82 -75
- package/dist/seed/users-seed-data.d.ts +0 -5
- package/dist/seed/users-seed-data.js +279 -634
- package/dist/seed/wishlists-seed-data.d.ts +0 -9
- package/dist/seed/wishlists-seed-data.js +36 -52
- package/dist/seo/json-ld.js +6 -4
- package/dist/styles.css +8052 -3
- package/dist/tailwind-utilities.css +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/search-tokens.d.ts +2 -0
- package/dist/utils/search-tokens.js +21 -0
- package/package.json +2 -2
- package/scripts/seed-cli.mjs +2 -2
|
@@ -13,7 +13,7 @@ export declare const eventInputSchema: z.ZodObject<{
|
|
|
13
13
|
isOnline: z.ZodDefault<z.ZodBoolean>;
|
|
14
14
|
entryFee: z.ZodDefault<z.ZodNumber>;
|
|
15
15
|
}, "strip", z.ZodTypeAny, {
|
|
16
|
-
type: "
|
|
16
|
+
type: "SALE" | "TOURNAMENT" | "CONVENTION" | "MEETUP";
|
|
17
17
|
title: string;
|
|
18
18
|
slug: string;
|
|
19
19
|
tags: string[];
|
|
@@ -26,7 +26,7 @@ export declare const eventInputSchema: z.ZodObject<{
|
|
|
26
26
|
maxEntries?: number | undefined;
|
|
27
27
|
imageUrl?: string | undefined;
|
|
28
28
|
}, {
|
|
29
|
-
type: "
|
|
29
|
+
type: "SALE" | "TOURNAMENT" | "CONVENTION" | "MEETUP";
|
|
30
30
|
title: string;
|
|
31
31
|
slug: string;
|
|
32
32
|
startsAt: string;
|
|
@@ -53,7 +53,7 @@ export declare const eventUpdateSchema: z.ZodObject<Omit<{
|
|
|
53
53
|
isOnline: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
54
54
|
entryFee: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
|
|
55
55
|
}, "slug">, "strip", z.ZodTypeAny, {
|
|
56
|
-
type?: "
|
|
56
|
+
type?: "SALE" | "TOURNAMENT" | "CONVENTION" | "MEETUP" | undefined;
|
|
57
57
|
title?: string | undefined;
|
|
58
58
|
description?: string | undefined;
|
|
59
59
|
tags?: string[] | undefined;
|
|
@@ -65,7 +65,7 @@ export declare const eventUpdateSchema: z.ZodObject<Omit<{
|
|
|
65
65
|
entryFee?: number | undefined;
|
|
66
66
|
isOnline?: boolean | undefined;
|
|
67
67
|
}, {
|
|
68
|
-
type?: "
|
|
68
|
+
type?: "SALE" | "TOURNAMENT" | "CONVENTION" | "MEETUP" | undefined;
|
|
69
69
|
title?: string | undefined;
|
|
70
70
|
description?: string | undefined;
|
|
71
71
|
tags?: string[] | undefined;
|
package/dist/client.d.ts
CHANGED
|
@@ -155,6 +155,8 @@ export type { MarketplaceBundleCardProps, MarketplaceBundleCardData, Marketplace
|
|
|
155
155
|
export { CompareOverlay } from "./features/products/components/CompareOverlay";
|
|
156
156
|
export type { CompareOverlayProps, CompareOverlayLabels, CompareProductLike, } from "./features/products/components/CompareOverlay";
|
|
157
157
|
export { COMPARE_MAX_ITEMS } from "./features/products/constants/action-defs";
|
|
158
|
+
export { STANDARD_SORT_OPTIONS, STANDARD_PUBLIC_SORT_OPTIONS, AUCTION_SORT_OPTIONS, AUCTION_PUBLIC_SORT_OPTIONS, PREORDER_SORT_OPTIONS, BUNDLE_SORT_OPTIONS, PRIZE_DRAW_SORT_OPTIONS, SORT_OPTIONS_BY_LISTING_TYPE, } from "./features/products/constants/sieve";
|
|
159
|
+
export type { SortOption } from "./features/products/constants/sieve";
|
|
158
160
|
export { MessagesView } from "./features/account/components/MessagesView";
|
|
159
161
|
export type { MessagesViewProps, MessagesViewLabels } from "./features/account/components/MessagesView";
|
|
160
162
|
export { ChatList } from "./features/account/components/ChatList";
|
package/dist/client.js
CHANGED
|
@@ -181,6 +181,7 @@ export { InteractiveProductCard } from "./features/products/index";
|
|
|
181
181
|
export { MarketplaceBundleCard } from "./features/products/components/MarketplaceBundleCard";
|
|
182
182
|
export { CompareOverlay } from "./features/products/components/CompareOverlay";
|
|
183
183
|
export { COMPARE_MAX_ITEMS } from "./features/products/constants/action-defs";
|
|
184
|
+
export { STANDARD_SORT_OPTIONS, STANDARD_PUBLIC_SORT_OPTIONS, AUCTION_SORT_OPTIONS, AUCTION_PUBLIC_SORT_OPTIONS, PREORDER_SORT_OPTIONS, BUNDLE_SORT_OPTIONS, PRIZE_DRAW_SORT_OPTIONS, SORT_OPTIONS_BY_LISTING_TYPE, } from "./features/products/constants/sieve";
|
|
184
185
|
// Messages — RTDB-pinged Firestore conversations (D5 + VC7)
|
|
185
186
|
export { MessagesView } from "./features/account/components/MessagesView";
|
|
186
187
|
export { ChatList } from "./features/account/components/ChatList";
|
|
@@ -323,6 +323,7 @@ export declare const DEMO_ENDPOINTS: {
|
|
|
323
323
|
export declare const WHATSAPP_SELLER_ENDPOINTS: {
|
|
324
324
|
readonly SETTINGS: "/api/store/whatsapp-settings";
|
|
325
325
|
readonly CATALOG_SYNC: "/api/store/whatsapp-settings/catalog-sync";
|
|
326
|
+
readonly CATALOG_IMPORT: "/api/store/whatsapp-settings/catalog-import";
|
|
326
327
|
};
|
|
327
328
|
export declare const API_ENDPOINTS: {
|
|
328
329
|
readonly LOGS: {
|
|
@@ -632,6 +633,7 @@ export declare const API_ENDPOINTS: {
|
|
|
632
633
|
readonly WHATSAPP_SELLER: {
|
|
633
634
|
readonly SETTINGS: "/api/store/whatsapp-settings";
|
|
634
635
|
readonly CATALOG_SYNC: "/api/store/whatsapp-settings/catalog-sync";
|
|
636
|
+
readonly CATALOG_IMPORT: "/api/store/whatsapp-settings/catalog-import";
|
|
635
637
|
};
|
|
636
638
|
readonly SUPPORT: {
|
|
637
639
|
readonly TICKETS: "/api/support/tickets";
|
|
@@ -953,6 +955,7 @@ export declare const API_ROUTES: {
|
|
|
953
955
|
readonly WHATSAPP_SELLER: {
|
|
954
956
|
readonly SETTINGS: "/api/store/whatsapp-settings";
|
|
955
957
|
readonly CATALOG_SYNC: "/api/store/whatsapp-settings/catalog-sync";
|
|
958
|
+
readonly CATALOG_IMPORT: "/api/store/whatsapp-settings/catalog-import";
|
|
956
959
|
};
|
|
957
960
|
readonly SUPPORT: {
|
|
958
961
|
readonly TICKETS: "/api/support/tickets";
|
|
@@ -442,6 +442,7 @@ export const DEMO_ENDPOINTS = {
|
|
|
442
442
|
export const WHATSAPP_SELLER_ENDPOINTS = {
|
|
443
443
|
SETTINGS: "/api/store/whatsapp-settings",
|
|
444
444
|
CATALOG_SYNC: "/api/store/whatsapp-settings/catalog-sync",
|
|
445
|
+
CATALOG_IMPORT: "/api/store/whatsapp-settings/catalog-import",
|
|
445
446
|
};
|
|
446
447
|
// ---------------------------------------------------------------------------
|
|
447
448
|
// Aggregate namespace export
|
|
@@ -22,6 +22,7 @@ export declare const PRODUCT_FIELDS: {
|
|
|
22
22
|
readonly CURRENCY: "currency";
|
|
23
23
|
readonly STOCK_QUANTITY: "stockQuantity";
|
|
24
24
|
readonly AVAILABLE_QUANTITY: "availableQuantity";
|
|
25
|
+
readonly IS_SOLD: "isSold";
|
|
25
26
|
readonly MAIN_IMAGE: "mainImage";
|
|
26
27
|
readonly IMAGES: "images";
|
|
27
28
|
readonly VIDEO: "video";
|
|
@@ -71,14 +72,14 @@ export declare const PRODUCT_FIELDS: {
|
|
|
71
72
|
readonly PRIZE_REVEAL_WINDOW_END: "prizeRevealWindowEnd";
|
|
72
73
|
readonly PRIZE_REVEAL_DEADLINE: "prizeRevealDeadline";
|
|
73
74
|
readonly IS_PART_OF_BUNDLE: "isPartOfBundle";
|
|
75
|
+
readonly SEARCH_TOKENS: "searchTokens";
|
|
74
76
|
readonly CREATED_AT: "createdAt";
|
|
75
77
|
readonly UPDATED_AT: "updatedAt";
|
|
76
78
|
readonly STATUS_VALUES: {
|
|
77
79
|
readonly DRAFT: "draft";
|
|
78
80
|
readonly PUBLISHED: "published";
|
|
79
|
-
readonly
|
|
80
|
-
readonly
|
|
81
|
-
readonly SOLD: "sold";
|
|
81
|
+
readonly IN_REVIEW: "in_review";
|
|
82
|
+
readonly ARCHIVED: "archived";
|
|
82
83
|
};
|
|
83
84
|
readonly CONDITION_VALUES: {
|
|
84
85
|
readonly NEW: "new";
|
|
@@ -25,6 +25,7 @@ export const PRODUCT_FIELDS = {
|
|
|
25
25
|
CURRENCY: "currency",
|
|
26
26
|
STOCK_QUANTITY: "stockQuantity",
|
|
27
27
|
AVAILABLE_QUANTITY: "availableQuantity",
|
|
28
|
+
IS_SOLD: "isSold",
|
|
28
29
|
MAIN_IMAGE: "mainImage",
|
|
29
30
|
IMAGES: "images",
|
|
30
31
|
VIDEO: "video",
|
|
@@ -76,14 +77,14 @@ export const PRODUCT_FIELDS = {
|
|
|
76
77
|
PRIZE_REVEAL_WINDOW_END: "prizeRevealWindowEnd",
|
|
77
78
|
PRIZE_REVEAL_DEADLINE: "prizeRevealDeadline",
|
|
78
79
|
IS_PART_OF_BUNDLE: "isPartOfBundle",
|
|
80
|
+
SEARCH_TOKENS: "searchTokens",
|
|
79
81
|
CREATED_AT: "createdAt",
|
|
80
82
|
UPDATED_AT: "updatedAt",
|
|
81
83
|
STATUS_VALUES: {
|
|
82
84
|
DRAFT: "draft",
|
|
83
85
|
PUBLISHED: "published",
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
SOLD: "sold",
|
|
86
|
+
IN_REVIEW: "in_review",
|
|
87
|
+
ARCHIVED: "archived",
|
|
87
88
|
},
|
|
88
89
|
CONDITION_VALUES: {
|
|
89
90
|
NEW: "new",
|
|
@@ -119,11 +120,10 @@ export const PRODUCT_FIELDS = {
|
|
|
119
120
|
},
|
|
120
121
|
};
|
|
121
122
|
export const PRODUCT_STATUS_TRANSITIONS = {
|
|
122
|
-
draft: ["published", "
|
|
123
|
-
published: ["draft", "
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
discontinued: ["draft"],
|
|
123
|
+
draft: ["published", "in_review"],
|
|
124
|
+
published: ["draft", "archived"],
|
|
125
|
+
in_review: ["published", "draft", "archived"],
|
|
126
|
+
archived: ["draft"],
|
|
127
127
|
};
|
|
128
128
|
// ============================================================================
|
|
129
129
|
// ORDER FIELDS
|
|
@@ -6,6 +6,9 @@ export declare const BID_ERROR_CODES: {
|
|
|
6
6
|
readonly INCREMENT_VIOLATED: "BID_INCREMENT_TOO_LOW";
|
|
7
7
|
readonly SELF_BID: "BID_SELF_BID";
|
|
8
8
|
readonly USER_BANNED: "BID_USER_BANNED";
|
|
9
|
+
readonly BUY_NOW_UNAVAILABLE: "BID_BUY_NOW_UNAVAILABLE";
|
|
10
|
+
readonly BUY_NOW_BIDS_STARTED: "BID_BUY_NOW_BIDS_STARTED";
|
|
11
|
+
readonly BUY_NOW_NO_PRICE: "BID_BUY_NOW_NO_PRICE";
|
|
9
12
|
};
|
|
10
13
|
export declare const OFFER_ERROR_CODES: {
|
|
11
14
|
readonly DISABLED: "OFFER_DISABLED";
|
|
@@ -6,6 +6,9 @@ export const BID_ERROR_CODES = {
|
|
|
6
6
|
INCREMENT_VIOLATED: "BID_INCREMENT_TOO_LOW",
|
|
7
7
|
SELF_BID: "BID_SELF_BID",
|
|
8
8
|
USER_BANNED: "BID_USER_BANNED",
|
|
9
|
+
BUY_NOW_UNAVAILABLE: "BID_BUY_NOW_UNAVAILABLE",
|
|
10
|
+
BUY_NOW_BIDS_STARTED: "BID_BUY_NOW_BIDS_STARTED",
|
|
11
|
+
BUY_NOW_NO_PRICE: "BID_BUY_NOW_NO_PRICE",
|
|
9
12
|
};
|
|
10
13
|
export const OFFER_ERROR_CODES = {
|
|
11
14
|
DISABLED: "OFFER_DISABLED",
|
|
@@ -347,6 +347,9 @@ export declare const ERROR_MESSAGES: {
|
|
|
347
347
|
readonly BID_TOO_LOW: "Your bid must be higher than the current bid";
|
|
348
348
|
readonly INCREMENT_TOO_LOW: "Your bid does not meet the minimum increment requirement";
|
|
349
349
|
readonly OWN_AUCTION: "You cannot bid on your own auction";
|
|
350
|
+
readonly BUY_NOW_UNAVAILABLE: "Buy Now is not available for this auction";
|
|
351
|
+
readonly BUY_NOW_BIDS_STARTED: "Buy Now is no longer available — bids have already been placed";
|
|
352
|
+
readonly BUY_NOW_NO_PRICE: "This auction does not have a Buy Now price";
|
|
350
353
|
};
|
|
351
354
|
readonly CONTACT: {
|
|
352
355
|
readonly SEND_FAILED: "Failed to send your message. Please try again.";
|
package/dist/errors/messages.js
CHANGED
|
@@ -374,6 +374,9 @@ export const ERROR_MESSAGES = {
|
|
|
374
374
|
BID_TOO_LOW: "Your bid must be higher than the current bid",
|
|
375
375
|
INCREMENT_TOO_LOW: "Your bid does not meet the minimum increment requirement",
|
|
376
376
|
OWN_AUCTION: "You cannot bid on your own auction",
|
|
377
|
+
BUY_NOW_UNAVAILABLE: "Buy Now is not available for this auction",
|
|
378
|
+
BUY_NOW_BIDS_STARTED: "Buy Now is no longer available — bids have already been placed",
|
|
379
|
+
BUY_NOW_NO_PRICE: "This auction does not have a Buy Now price",
|
|
377
380
|
},
|
|
378
381
|
CONTACT: {
|
|
379
382
|
SEND_FAILED: "Failed to send your message. Please try again.",
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useCallback, useEffect, useState, useTransition } from "react";
|
|
4
4
|
import { Alert, Badge, Button, Div, Heading, LoginRequiredModal, Spinner, Text } from "../../../ui";
|
|
5
5
|
import { isAuthError } from "../../../utils/auth-error";
|
|
6
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
6
7
|
function formatRupees(amount) {
|
|
7
8
|
if (amount === undefined || amount === null)
|
|
8
9
|
return "—";
|
|
@@ -89,5 +90,5 @@ export function UserOffersPanel({ fetchEndpoint = "/api/user/offers", onAcceptCo
|
|
|
89
90
|
function handleUpdate(id, patch) {
|
|
90
91
|
setOffers((prev) => prev.map((o) => (o.id === id ? { ...o, ...patch } : o)));
|
|
91
92
|
}
|
|
92
|
-
return (_jsxs(Div, { className: `space-y-4 ${className}`, children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to manage your offers. Please log in or create an account to continue." }), _jsxs(Div, { className: "flex items-center justify-between", children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-zinc-900 dark:text-zinc-100", children: "My Offers" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: loadOffers, disabled: loading, className: "border border-zinc-300 dark:border-zinc-600 text-xs", children: loading ? "Refreshing…" : "
|
|
93
|
+
return (_jsxs(Div, { className: `space-y-4 ${className}`, children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to manage your offers. Please log in or create an account to continue." }), _jsxs(Div, { className: "flex items-center justify-between", children: [_jsx(Heading, { level: 2, className: "text-lg font-semibold text-zinc-900 dark:text-zinc-100", children: "My Offers" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: loadOffers, disabled: loading, className: "border border-zinc-300 dark:border-zinc-600 text-xs", children: loading ? "Refreshing…" : ACTIONS.USER["refresh-offers"].label })] }), fetchError && _jsx(Alert, { variant: "error", children: _jsx(Text, { className: "text-sm", children: fetchError }) }), loading && (_jsx(Div, { className: "flex justify-center py-12", children: _jsx(Spinner, { size: "lg" }) })), !loading && offers.length === 0 && (_jsx(Div, { className: "text-center py-12", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500 text-sm", children: "No offers yet" }) })), !loading && offers.length > 0 && (_jsx(Div, { className: "space-y-3", children: offers.map((offer) => (_jsx(BuyerOfferCard, { offer: offer, onAcceptCounter: onAcceptCounter, onWithdraw: onWithdraw, onCheckout: onCheckout, onUpdate: handleUpdate, onNeedsLogin: () => setShowLoginModal(true) }, offer.id))) }))] }));
|
|
93
94
|
}
|
|
@@ -7,6 +7,7 @@ import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
8
|
import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu, useToast } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
11
|
import { ADMIN_EVENT_ENTRY_STATUS_TABS } from "../constants/filter-tabs";
|
|
11
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
12
13
|
import { DataTable } from "./DataTable";
|
|
@@ -103,9 +104,9 @@ export function AdminAllEventEntriesView({ children, ...props }) {
|
|
|
103
104
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by user name or event ID", 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 }), 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-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No entries found", renderRowActions: (row) => {
|
|
104
105
|
const er = row;
|
|
105
106
|
return (_jsx(RowActionMenu, { actions: [
|
|
106
|
-
{ label: "
|
|
107
|
-
{ label: "
|
|
108
|
-
{ label: "
|
|
107
|
+
{ label: ACTIONS.ADMIN["confirm-entry"].label, onClick: () => updateMutation.mutate({ id: er.id, status: "CONFIRMED" }) },
|
|
108
|
+
{ label: ACTIONS.ADMIN["waitlist-entry"].label, onClick: () => updateMutation.mutate({ id: er.id, status: "WAITLISTED" }) },
|
|
109
|
+
{ label: ACTIONS.ADMIN["cancel-entry"].label, destructive: true, onClick: () => updateMutation.mutate({ id: er.id, status: "CANCELLED" }) },
|
|
109
110
|
] }));
|
|
110
111
|
} })] }), _jsx(EntriesFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }));
|
|
111
112
|
}
|
|
@@ -8,6 +8,8 @@ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
|
8
8
|
import { BulkActionBar, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, ConfirmDeleteModal, RowActionMenu, useToast } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
10
|
import { ADMIN_BID_STATUS_TABS } from "../constants/filter-tabs";
|
|
11
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
12
|
+
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
11
13
|
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
12
14
|
import { DataTable } from "./DataTable";
|
|
13
15
|
import { apiClient } from "../../../http";
|
|
@@ -106,14 +108,14 @@ export function AdminBidsView({ children, ...props }) {
|
|
|
106
108
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
107
109
|
}
|
|
108
110
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search bids, products, or bidder IDs", 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 }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
109
|
-
{ id:
|
|
111
|
+
{ id: ROW_ACTION_ID.CANCEL, label: ACTIONS.ADMIN["cancel-bid"].label, variant: "secondary", onClick: () => { selection.clearSelection(); } },
|
|
110
112
|
] }), 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-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No bids found", renderRowActions: (row) => {
|
|
111
113
|
const bidRow = row;
|
|
112
114
|
const isCancelled = bidRow.status === "cancelled" || bidRow.status === "voided";
|
|
113
115
|
return (_jsx(RowActionMenu, { actions: [
|
|
114
116
|
{
|
|
115
|
-
label:
|
|
116
|
-
destructive:
|
|
117
|
+
label: ROW_ACTION_META[ROW_ACTION_ID.CANCEL].label,
|
|
118
|
+
destructive: ROW_ACTION_META[ROW_ACTION_ID.CANCEL].destructive,
|
|
117
119
|
disabled: isCancelled,
|
|
118
120
|
onClick: () => {
|
|
119
121
|
setSelectedRow(bidRow);
|
|
@@ -9,6 +9,7 @@ import { BulkActionBar, Button, FilterChipGroup, Heading, ListingToolbar, Listin
|
|
|
9
9
|
import { AdminViewCards } from "./AdminViewCards";
|
|
10
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
11
|
import { ADMIN_BLOG_STATUS_TABS } from "../constants/filter-tabs";
|
|
12
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
12
13
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
13
14
|
import { DataTable } from "./DataTable";
|
|
14
15
|
import { AdminBlogEditorView } from "./AdminBlogEditorView";
|
|
@@ -105,7 +106,7 @@ export function AdminBlogView({ children, getRowHref, ...props }) {
|
|
|
105
106
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
106
107
|
}
|
|
107
108
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "Blog Posts" }), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search articles, authors, or tags", 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" }), "New Post"] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
108
|
-
{ id: "publish", label: "
|
|
109
|
-
{ id: "draft", label: "
|
|
109
|
+
{ id: "publish", label: ACTIONS.ADMIN["publish-blog"].label, variant: "primary", onClick: () => { selection.clearSelection(); } },
|
|
110
|
+
{ id: "draft", label: ACTIONS.ADMIN["draft-blog"].label, variant: "secondary", onClick: () => { selection.clearSelection(); } },
|
|
110
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-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No blog posts found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No blog posts found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(BlogFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "New Post" : "Edit Post", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminBlogEditorView, { postId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
111
112
|
}
|
|
@@ -8,6 +8,7 @@ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
|
8
8
|
import { BulkActionBar, Button, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text } from "../../../ui";
|
|
9
9
|
import { AdminViewCards } from "./AdminViewCards";
|
|
10
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
12
13
|
import { DataTable } from "./DataTable";
|
|
13
14
|
import { AdminBrandEditorView } from "./AdminBrandEditorView";
|
|
@@ -85,7 +86,7 @@ export function AdminBrandsView({ children, ...props }) {
|
|
|
85
86
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
86
87
|
}
|
|
87
88
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search brands by name or slug", 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 Brand"] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
88
|
-
{ id: "edit", label: "
|
|
89
|
+
{ id: "edit", label: ACTIONS.ADMIN["edit-brand"].label, variant: "primary", onClick: () => { const id = selection.selectedIds[0]; if (id)
|
|
89
90
|
openEditPanel(id); selection.clearSelection(); } },
|
|
90
91
|
] }), 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-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No brands found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No brands found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(BrandsFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Brand" : "Edit Brand", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminBrandEditorView, { brandId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
91
92
|
}
|
|
@@ -13,7 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
* static).
|
|
14
14
|
*/
|
|
15
15
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
16
|
-
import { Button, Checkbox, Container, Heading, Input, Row, Section, Select, Stack, Text, Textarea, } from "../../../ui";
|
|
16
|
+
import { Button, Checkbox, ConfirmDeleteModal, Container, Heading, Input, Row, Section, Select, Stack, Text, Textarea, } from "../../../ui";
|
|
17
17
|
import { FieldInput, FormShellContext, useFormShellState } from "../../../ui/forms";
|
|
18
18
|
import { BundleItemsPicker, defaultBundleItemsFetch, } from "../../categories/components/BundleItemsPicker";
|
|
19
19
|
import { BundleDynamicRuleEditor } from "../../categories/components/BundleDynamicRuleEditor";
|
|
@@ -77,6 +77,7 @@ export function AdminBundleEditorView({ bundleId, onSaved, onDeleted, }) {
|
|
|
77
77
|
const [loading, setLoading] = useState(isEdit);
|
|
78
78
|
const [saving, setSaving] = useState(false);
|
|
79
79
|
const [deleting, setDeleting] = useState(false);
|
|
80
|
+
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
|
80
81
|
const [apiError, setApiError] = useState(null);
|
|
81
82
|
const [metadata, setMetadata] = useState({});
|
|
82
83
|
const { shellCtx, setFieldError, clearErrors } = useFormShellState();
|
|
@@ -188,15 +189,13 @@ export function AdminBundleEditorView({ bundleId, onSaved, onDeleted, }) {
|
|
|
188
189
|
const handleDelete = useCallback(async () => {
|
|
189
190
|
if (!bundleId)
|
|
190
191
|
return;
|
|
191
|
-
if (!window.confirm(BUNDLE_COPY.adminEditor.deleteConfirm)) {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
192
|
setDeleting(true);
|
|
195
193
|
setApiError(null);
|
|
196
194
|
try {
|
|
197
195
|
const res = await fetch(`/api/admin/bundles/${encodeURIComponent(bundleId)}`, { method: "DELETE" });
|
|
198
196
|
if (!res.ok)
|
|
199
197
|
throw new Error(`Delete failed: ${res.status}`);
|
|
198
|
+
setDeleteConfirmOpen(false);
|
|
200
199
|
onDeleted?.();
|
|
201
200
|
}
|
|
202
201
|
catch (err) {
|
|
@@ -214,5 +213,5 @@ export function AdminBundleEditorView({ bundleId, onSaved, onDeleted, }) {
|
|
|
214
213
|
}
|
|
215
214
|
return (_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(Section, { className: "py-10", children: _jsx(Container, { size: "lg", children: _jsxs(Stack, { gap: "lg", children: [_jsxs(Row, { gap: "sm", align: "center", justify: "between", className: "flex-wrap", children: [_jsx(Heading, { level: 1, className: "text-2xl font-semibold text-zinc-900 dark:text-zinc-100", children: isEdit
|
|
216
215
|
? BUNDLE_COPY.adminEditorTitleEdit
|
|
217
|
-
: BUNDLE_COPY.adminEditorTitleNew }), isEdit && (_jsx(Button, { variant: "danger", onClick:
|
|
216
|
+
: BUNDLE_COPY.adminEditorTitleNew }), isEdit && (_jsx(Button, { variant: "danger", onClick: () => setDeleteConfirmOpen(true), disabled: deleting, children: BUNDLE_COPY.adminEditor.deleteButton(deleting) }))] }), _jsx(ConfirmDeleteModal, { isOpen: deleteConfirmOpen, onClose: () => setDeleteConfirmOpen(false), onConfirm: handleDelete, title: "Delete this bundle?", message: BUNDLE_COPY.adminEditor.deleteConfirm, isDeleting: deleting }), apiError && (_jsx(Text, { color: "danger", role: "alert", children: apiError })), _jsxs(Stack, { gap: "md", children: [_jsx(FieldInput, { name: "name", label: BUNDLE_COPY.adminEditor.fields.nameLabel, value: form.name, onChange: (v) => setForm((f) => ({ ...f, name: v })), placeholder: BUNDLE_COPY.adminEditor.fields.namePlaceholder, disabled: saving, required: true }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: BUNDLE_COPY.adminEditor.fields.descriptionLabel }), _jsx(Textarea, { value: form.description, onChange: (e) => setForm((f) => ({ ...f, description: e.target.value })), placeholder: BUNDLE_COPY.adminEditor.fields.descriptionPlaceholder, rows: 4, disabled: saving })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(FieldInput, { name: "price", label: BUNDLE_COPY.adminEditor.fields.priceLabel, type: "number", inputMode: "decimal", min: 1, step: 1, value: form.priceRupees, onChange: (v) => setForm((f) => ({ ...f, priceRupees: v })), placeholder: BUNDLE_COPY.adminEditor.fields.pricePlaceholder, disabled: saving, required: true }), _jsx(Text, { size: "xs", color: "muted", children: BUNDLE_COPY.adminEditor.fields.pricePaiseHint(parsePriceRupees(form.priceRupees)) })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: BUNDLE_COPY.adminEditor.fields.coverImageLabel }), _jsx(Input, { type: "url", value: form.coverImage, onChange: (e) => setForm((f) => ({ ...f, coverImage: e.target.value })), placeholder: "https://\u2026", disabled: saving })] }), _jsx(Checkbox, { checked: form.isActive, onChange: (e) => setForm((f) => ({ ...f, isActive: e.target.checked })), disabled: saving, label: BUNDLE_COPY.adminEditor.fields.activeLabel }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: BUNDLE_COPY.adminEditor.ruleTypeLabel }), _jsx(Select, { options: RULE_TYPE_OPTIONS, value: form.ruleType, onValueChange: (next) => setForm((f) => ({ ...f, ruleType: next })), disabled: saving, "aria-label": BUNDLE_COPY.adminEditor.ruleTypeLabel })] }), form.ruleType === "static" ? (_jsx(BundleItemsPicker, { value: form.productIds, onChange: (next) => setForm((f) => ({ ...f, productIds: next })), fetchProducts: fetchProducts, initialMetadata: metadata })) : (_jsx(BundleDynamicRuleEditor, { value: form.dynamicRule, onChange: (next) => setForm((f) => ({ ...f, dynamicRule: next })), disabled: saving }))] }), _jsx(Row, { gap: "sm", align: "center", justify: "end", children: _jsx(Button, { variant: "primary", onClick: handleSave, disabled: saving, "aria-busy": saving, children: BUNDLE_COPY.adminEditor.saveButton(saving, isEdit) }) })] }) }) }) }));
|
|
218
217
|
}
|
|
@@ -152,7 +152,7 @@ export function AdminBundlesView({ getEditHref, newHref }) {
|
|
|
152
152
|
const bulkActions = [
|
|
153
153
|
{
|
|
154
154
|
id: "activate",
|
|
155
|
-
label: "
|
|
155
|
+
label: ACTIONS.ADMIN["activate-bundle"].label,
|
|
156
156
|
variant: "primary",
|
|
157
157
|
onClick: async () => {
|
|
158
158
|
await Promise.all(selection.selectedIds.map((id) => fetch(ADMIN_ENDPOINTS.BUNDLE_BY_ID(id), {
|
|
@@ -166,7 +166,7 @@ export function AdminBundlesView({ getEditHref, newHref }) {
|
|
|
166
166
|
},
|
|
167
167
|
{
|
|
168
168
|
id: "deactivate",
|
|
169
|
-
label: "
|
|
169
|
+
label: ACTIONS.ADMIN["deactivate-bundle"].label,
|
|
170
170
|
variant: "secondary",
|
|
171
171
|
onClick: async () => {
|
|
172
172
|
await Promise.all(selection.selectedIds.map((id) => fetch(ADMIN_ENDPOINTS.BUNDLE_BY_ID(id), {
|
|
@@ -180,7 +180,7 @@ export function AdminBundlesView({ getEditHref, newHref }) {
|
|
|
180
180
|
},
|
|
181
181
|
{
|
|
182
182
|
id: "delete",
|
|
183
|
-
label: "
|
|
183
|
+
label: ACTIONS.ADMIN["delete-bundle"].label,
|
|
184
184
|
variant: "danger",
|
|
185
185
|
onClick: async () => {
|
|
186
186
|
await Promise.all(selection.selectedIds.map((id) => fetch(ADMIN_ENDPOINTS.BUNDLE_BY_ID(id), { method: "DELETE" })));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ListingViewShellProps } from "../../../ui";
|
|
2
2
|
export interface AdminCarouselViewProps extends ListingViewShellProps {
|
|
3
|
+
onBulkDelete?: (ids: string[]) => Promise<void>;
|
|
3
4
|
}
|
|
4
|
-
export declare function AdminCarouselView({ children, ...props }: AdminCarouselViewProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function AdminCarouselView({ children, onBulkDelete, ...props }: AdminCarouselViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -5,8 +5,9 @@ import { X } from "lucide-react";
|
|
|
5
5
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { ListingToolbar, ListingViewShell, Pagination, Text } from "../../../ui";
|
|
8
|
+
import { BulkActionBar, ListingToolbar, ListingViewShell, Pagination, Text } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS, HOMEPAGE_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
11
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
11
12
|
import { DataTable } from "./DataTable";
|
|
12
13
|
import { RowActionMenu } from "../../../ui/components/RowActionMenu";
|
|
@@ -19,7 +20,7 @@ function CarouselFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, cl
|
|
|
19
20
|
return null;
|
|
20
21
|
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsxs("div", { className: "space-y-2", children: [_jsx(Text, { className: "text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400", children: "Status" }), _jsx("div", { className: "flex flex-wrap gap-2", children: [{ label: "All", value: "" }, { label: "Active", value: "true" }, { label: "Inactive", value: "false" }].map((opt) => (_jsx("button", { type: "button", onClick: () => setPendingFilters((p) => ({ ...p, active: opt.value })), className: `rounded-full px-3 py-1 text-xs font-medium border transition-colors ${(pendingFilters.active || "") === opt.value ? "bg-primary text-white border-primary" : "border-zinc-300 dark:border-slate-600 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-slate-800"}`, children: opt.label }, opt.label))) })] }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
21
22
|
}
|
|
22
|
-
export function AdminCarouselView({ children, ...props }) {
|
|
23
|
+
export function AdminCarouselView({ children, onBulkDelete, ...props }) {
|
|
23
24
|
const hasChildren = React.Children.count(children) > 0;
|
|
24
25
|
const [view, setView] = useState("table");
|
|
25
26
|
const [draggingId, setDraggingId] = useState(null);
|
|
@@ -134,6 +135,14 @@ export function AdminCarouselView({ children, ...props }) {
|
|
|
134
135
|
const currentPage = table.getNumber("page", 1);
|
|
135
136
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
136
137
|
const selection = useBulkSelection({ items: fetchedRows, keyExtractor: (r) => r.id });
|
|
138
|
+
const bulkActions = [
|
|
139
|
+
...(onBulkDelete ? [{
|
|
140
|
+
id: "bulk-delete",
|
|
141
|
+
label: ACTIONS.ADMIN["delete-carousel"].label,
|
|
142
|
+
variant: "danger",
|
|
143
|
+
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
|
|
144
|
+
}] : []),
|
|
145
|
+
];
|
|
137
146
|
if (hasChildren) {
|
|
138
147
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
139
148
|
}
|
|
@@ -171,14 +180,14 @@ export function AdminCarouselView({ children, ...props }) {
|
|
|
171
180
|
key: "actions",
|
|
172
181
|
header: "",
|
|
173
182
|
render: (row) => (_jsx(RowActionMenu, { actions: [
|
|
174
|
-
{ label: "
|
|
183
|
+
{ label: ACTIONS.ADMIN["edit-carousel"].label, onClick: () => { window.location.href = `/admin/carousel/${row.id}/edit`; } },
|
|
175
184
|
{
|
|
176
|
-
label: "
|
|
185
|
+
label: ACTIONS.ADMIN["delete-carousel"].label,
|
|
177
186
|
destructive: true,
|
|
178
187
|
onClick: () => handleDeleteSlide(row.id),
|
|
179
188
|
},
|
|
180
189
|
] })),
|
|
181
190
|
},
|
|
182
191
|
];
|
|
183
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search slide titles", onSearchChange: setSearchInput, onSearchCommit: commitSearch, showTableView: true, view: view, onViewChange: (v) => setView(v), 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) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: displayRows, columns: columns, isLoading: isLoading, emptyLabel: "No carousel slides \u2014 use 'New slide' to add one", getRowHref: (row) => `/admin/carousel/${row.id}/edit` })] }), _jsx(CarouselFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }));
|
|
192
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search slide titles", onSearchChange: setSearchInput, onSearchCommit: commitSearch, showTableView: true, view: view, onViewChange: (v) => setView(v), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: displayRows, columns: columns, isLoading: isLoading, emptyLabel: "No carousel slides \u2014 use 'New slide' to add one", getRowHref: (row) => `/admin/carousel/${row.id}/edit` })] }), _jsx(CarouselFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters })] }));
|
|
184
193
|
}
|
|
@@ -8,6 +8,7 @@ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
|
8
8
|
import { BulkActionBar, Button, Heading, ListingToolbar, ListingViewShell, Pagination, SideDrawer, Text } from "../../../ui";
|
|
9
9
|
import { AdminViewCards } from "./AdminViewCards";
|
|
10
10
|
import { CATEGORY_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
12
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
12
13
|
import { DataTable } from "./DataTable";
|
|
13
14
|
import { AdminCategoryEditorView } from "./AdminCategoryEditorView";
|
|
@@ -93,7 +94,7 @@ export function AdminCategoriesView({ children, getRowHref, ...props }) {
|
|
|
93
94
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
94
95
|
}
|
|
95
96
|
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(Heading, { level: 1, className: "sr-only", children: "Categories" }), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search categories, slugs, or parent category", 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 Category"] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
|
|
96
|
-
{ id: "edit", label: "
|
|
97
|
+
{ id: "edit", label: ACTIONS.ADMIN["edit-category"].label, variant: "primary", onClick: () => { const id = selection.selectedIds[0]; if (id)
|
|
97
98
|
openEditPanel(id); selection.clearSelection(); } },
|
|
98
99
|
] }), 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-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No categories found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), onRowClick: (row) => openEditPanel(row.id) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No categories found", onRowClick: (row) => openEditPanel(row.id), selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), _jsx(CategoriesFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingFilters: pendingFilters, setPendingFilters: setPendingFilters, applyFilters: applyFilters }), _jsx(SideDrawer, { isOpen: isCreateOpen || isEditOpen, onClose: closePanel, title: isCreateOpen ? "Add Category" : "Edit Category", mode: isCreateOpen ? "create" : "edit", children: (isCreateOpen || isEditOpen) && (_jsx(AdminCategoryEditorView, { categoryId: editId ?? undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })) })] }));
|
|
99
100
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { ListingViewShellProps } from "../../../ui";
|
|
2
2
|
export interface AdminContactViewProps extends ListingViewShellProps {
|
|
3
|
+
onBulkMarkRead?: (ids: string[]) => Promise<void>;
|
|
4
|
+
onBulkArchive?: (ids: string[]) => Promise<void>;
|
|
5
|
+
onBulkDelete?: (ids: string[]) => Promise<void>;
|
|
3
6
|
}
|
|
4
|
-
export declare function AdminContactView({ children, ...props }: AdminContactViewProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function AdminContactView({ children, onBulkMarkRead, onBulkArchive, onBulkDelete, ...props }: AdminContactViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -5,8 +5,10 @@ import { X } from "lucide-react";
|
|
|
5
5
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
7
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { ConfirmDeleteModal, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, useToast, } from "../../../ui";
|
|
8
|
+
import { BulkActionBar, ConfirmDeleteModal, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, useToast, } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
+
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
|
|
10
12
|
import { ADMIN_CONTACT_STATUS_TABS } from "../constants/filter-tabs";
|
|
11
13
|
import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
|
|
12
14
|
import { DataTable } from "./DataTable";
|
|
@@ -25,7 +27,7 @@ function ContactFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, cle
|
|
|
25
27
|
return null;
|
|
26
28
|
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
|
|
27
29
|
}
|
|
28
|
-
export function AdminContactView({ children, ...props }) {
|
|
30
|
+
export function AdminContactView({ children, onBulkMarkRead, onBulkArchive, onBulkDelete, ...props }) {
|
|
29
31
|
const hasChildren = React.Children.count(children) > 0;
|
|
30
32
|
const [view, setView] = useState("table");
|
|
31
33
|
const queryClient = useQueryClient();
|
|
@@ -121,26 +123,44 @@ export function AdminContactView({ children, ...props }) {
|
|
|
121
123
|
const currentPage = table.getNumber("page", 1);
|
|
122
124
|
const totalPages = Math.ceil(total / PAGE_SIZE);
|
|
123
125
|
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
126
|
+
const bulkActions = [
|
|
127
|
+
...(onBulkMarkRead ? [{
|
|
128
|
+
id: "bulk-mark-read",
|
|
129
|
+
label: ACTIONS.ADMIN["mark-contact-read"].label,
|
|
130
|
+
onClick: async () => { await onBulkMarkRead(selection.selectedIds); selection.clearSelection(); },
|
|
131
|
+
}] : []),
|
|
132
|
+
...(onBulkArchive ? [{
|
|
133
|
+
id: "bulk-archive",
|
|
134
|
+
label: ACTIONS.ADMIN["archive-contact"].label,
|
|
135
|
+
onClick: async () => { await onBulkArchive(selection.selectedIds); selection.clearSelection(); },
|
|
136
|
+
}] : []),
|
|
137
|
+
...(onBulkDelete ? [{
|
|
138
|
+
id: "bulk-delete",
|
|
139
|
+
label: ACTIONS.ADMIN["delete-contact"].label,
|
|
140
|
+
variant: "danger",
|
|
141
|
+
onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
|
|
142
|
+
}] : []),
|
|
143
|
+
];
|
|
124
144
|
if (hasChildren) {
|
|
125
145
|
return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
|
|
126
146
|
}
|
|
127
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by subject, name, or email", 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 }), 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-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No contact submissions found", renderRowActions: (row) => {
|
|
147
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by subject, name, or email", 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 }), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No contact submissions found", renderRowActions: (row) => {
|
|
128
148
|
const cr = row;
|
|
129
149
|
return (_jsx(RowActionMenu, { actions: [
|
|
130
|
-
{ label:
|
|
150
|
+
{ label: ROW_ACTION_META[ROW_ACTION_ID.VIEW].label, onClick: () => { setSelectedRow(cr); setDrawerOpen(true); } },
|
|
131
151
|
{
|
|
132
|
-
label: "
|
|
152
|
+
label: ACTIONS.ADMIN["mark-contact-read"].label,
|
|
133
153
|
disabled: cr.status === "read" || cr.status === "resolved",
|
|
134
154
|
onClick: () => actionMutation.mutate({ id: cr.id, action: "read" }),
|
|
135
155
|
},
|
|
136
156
|
{
|
|
137
|
-
label: "
|
|
157
|
+
label: ACTIONS.ADMIN["archive-contact"].label,
|
|
138
158
|
disabled: cr.status === "resolved",
|
|
139
159
|
onClick: () => actionMutation.mutate({ id: cr.id, action: "resolved" }),
|
|
140
160
|
},
|
|
141
161
|
{ separator: true, label: "", onClick: () => { } },
|
|
142
162
|
{
|
|
143
|
-
label: "
|
|
163
|
+
label: ACTIONS.ADMIN["delete-contact"].label,
|
|
144
164
|
destructive: true,
|
|
145
165
|
onClick: () => { setSelectedRow(cr); setDeleteOpen(true); },
|
|
146
166
|
},
|