@mohasinac/appkit 2.7.36 → 2.7.37
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/client/features/seller/print-center/InventoryLabel.d.ts +12 -0
- package/dist/_internal/client/features/seller/print-center/InventoryLabel.js +49 -0
- package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.d.ts +7 -0
- package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.js +32 -0
- package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.d.ts +28 -0
- package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.js +33 -0
- package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.d.ts +12 -0
- package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.js +30 -0
- package/dist/_internal/client/features/seller/print-center/PrintCenterView.d.ts +12 -0
- package/dist/_internal/client/features/seller/print-center/PrintCenterView.js +102 -0
- package/dist/_internal/client/features/seller/print-center/PrintGrid.d.ts +12 -0
- package/dist/_internal/client/features/seller/print-center/PrintGrid.js +13 -0
- package/dist/_internal/client/features/seller/print-center/StoreCard.d.ts +19 -0
- package/dist/_internal/client/features/seller/print-center/StoreCard.js +30 -0
- package/dist/_internal/client/features/seller/print-center/WebsiteCard.d.ts +11 -0
- package/dist/_internal/client/features/seller/print-center/WebsiteCard.js +25 -0
- package/dist/_internal/client/features/seller/print-center/index.d.ts +11 -0
- package/dist/_internal/client/features/seller/print-center/index.js +10 -0
- package/dist/_internal/client/features/seller/print-center/types.d.ts +20 -0
- package/dist/_internal/client/features/seller/print-center/types.js +20 -0
- package/dist/_internal/client/features/seller/print-center/useInventoryPdf.d.ts +18 -0
- package/dist/_internal/client/features/seller/print-center/useInventoryPdf.js +76 -0
- package/dist/_internal/server/jobs/core/draftPrune.d.ts +2 -0
- package/dist/_internal/server/jobs/core/draftPrune.js +15 -0
- package/dist/_internal/server/jobs/core/onBidPlaced.js +1 -1
- package/dist/_internal/server/jobs/core/promotions.d.ts +1 -0
- package/dist/_internal/server/jobs/core/promotions.js +21 -6
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +18 -1
- package/dist/_internal/server/jobs/handlers/draftPrune.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/draftPrune.js +2 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +2 -0
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +15 -1
- package/dist/_internal/shared/actions/action-registry.js +32 -0
- package/dist/_internal/shared/features/blog/schema.d.ts +8 -8
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +10 -10
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/features/products/schema.d.ts +8 -8
- package/dist/_internal/shared/features/promotions/schema.d.ts +6 -6
- package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
- package/dist/client.d.ts +11 -1
- package/dist/client.js +6 -1
- package/dist/configs/next.js +1 -0
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/core/server-action.d.ts +4 -0
- package/dist/errors/error-codes.d.ts +18 -0
- package/dist/errors/error-codes.js +18 -0
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/errors/messages.d.ts +1 -0
- package/dist/errors/messages.js +1 -0
- package/dist/features/account/components/UserSidebar.js +1 -1
- package/dist/features/account/schemas/index.d.ts +12 -12
- package/dist/features/admin/components/AdminSidebar.js +1 -1
- package/dist/features/auctions/actions/bid-actions.js +13 -6
- package/dist/features/auctions/components/PlaceBidFormClient.js +10 -2
- package/dist/features/auctions/repository/bid.repository.d.ts +7 -0
- package/dist/features/auctions/repository/bid.repository.js +16 -0
- package/dist/features/auctions/schemas/index.d.ts +18 -18
- package/dist/features/auth/schemas/index.d.ts +2 -2
- package/dist/features/before-after/schemas/index.d.ts +2 -2
- package/dist/features/blog/actions/blog-actions.d.ts +32 -32
- package/dist/features/blog/components/BlogPostForm.js +1 -1
- package/dist/features/blog/schemas/index.d.ts +38 -38
- package/dist/features/categories/schemas/index.d.ts +10 -10
- package/dist/features/collections/schemas/index.d.ts +4 -4
- package/dist/features/consultation/schemas/index.d.ts +9 -9
- package/dist/features/corporate/schemas/index.d.ts +4 -4
- package/dist/features/events/schemas/index.d.ts +50 -50
- package/dist/features/faq/actions/faq-actions.d.ts +6 -6
- package/dist/features/faq/schemas/index.d.ts +8 -8
- package/dist/features/homepage/actions/homepage-section-actions.d.ts +4 -4
- package/dist/features/layout/AppLayoutShell.js +1 -1
- package/dist/features/layout/AutoBreadcrumbs.js +1 -1
- package/dist/features/media/AvatarUpload.js +1 -1
- package/dist/features/media/types/index.d.ts +4 -4
- package/dist/features/media/upload/ImageUpload.js +18 -9
- package/dist/features/media/upload/MediaUploadList.js +2 -2
- package/dist/features/orders/schemas/firestore.d.ts +6 -0
- package/dist/features/orders/schemas/index.d.ts +10 -10
- package/dist/features/orders/types/index.d.ts +5 -0
- package/dist/features/payments/schemas/index.d.ts +4 -4
- package/dist/features/pre-orders/schemas/index.d.ts +8 -8
- package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
- package/dist/features/products/components/ProductForm.js +20 -2
- package/dist/features/products/repository/products.repository.d.ts +3 -1
- package/dist/features/products/repository/products.repository.js +12 -1
- package/dist/features/products/schemas/firestore.d.ts +7 -0
- package/dist/features/products/schemas/index.d.ts +34 -34
- package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
- package/dist/features/products/types/index.d.ts +6 -0
- package/dist/features/promotions/schemas/index.d.ts +14 -14
- package/dist/features/reviews/schemas/index.d.ts +18 -18
- package/dist/features/seller/actions/offer-actions.js +10 -1
- package/dist/features/seller/components/PhysicalLocationModal.d.ts +12 -0
- package/dist/features/seller/components/PhysicalLocationModal.js +30 -0
- package/dist/features/seller/components/PrintCenterView.d.ts +40 -0
- package/dist/features/seller/components/PrintCenterView.js +6 -0
- package/dist/features/seller/components/SellerOrdersView.js +48 -3
- package/dist/features/seller/components/SellerProductShell.d.ts +1 -0
- package/dist/features/seller/components/SellerProductShell.js +26 -4
- package/dist/features/seller/components/SellerProductsView.js +54 -13
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/seller/components/index.d.ts +1 -0
- package/dist/features/seller/components/index.js +1 -0
- package/dist/features/seller/schemas/firestore.d.ts +2 -0
- package/dist/features/seller/schemas/firestore.js +1 -0
- package/dist/features/seller/schemas/index.d.ts +22 -22
- package/dist/features/shell/FormShell.js +1 -1
- package/dist/features/shell/StepForm.d.ts +3 -1
- package/dist/features/shell/StepForm.js +3 -3
- package/dist/features/stores/schemas/index.d.ts +4 -4
- package/dist/index.d.ts +9 -1
- package/dist/index.js +6 -1
- package/dist/next/routing/route-map.d.ts +8 -0
- package/dist/next/routing/route-map.js +3 -0
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/RowActionMenu.js +45 -10
- package/dist/ui/components/SideDrawer.style.css +2 -2
- package/dist/ui/forms/FieldCheckbox.d.ts +13 -0
- package/dist/ui/forms/FieldCheckbox.js +25 -0
- package/dist/ui/forms/FieldInput.d.ts +11 -0
- package/dist/ui/forms/FieldInput.js +25 -0
- package/dist/ui/forms/FieldSelect.d.ts +17 -0
- package/dist/ui/forms/FieldSelect.js +25 -0
- package/dist/ui/forms/FormShell.d.ts +48 -0
- package/dist/ui/forms/FormShell.js +176 -0
- package/dist/ui/forms/FormShell.style.css +287 -0
- package/dist/ui/forms/index.d.ts +8 -0
- package/dist/ui/forms/index.js +4 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.js +5 -0
- package/dist/utils/action-response.d.ts +6 -0
- package/dist/utils/action-response.js +22 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/validation/schemas.d.ts +10 -10
- package/package.json +2 -2
|
@@ -39,14 +39,14 @@ export declare const couponItemSchema: z.ZodObject<{
|
|
|
39
39
|
createdAt: z.ZodString;
|
|
40
40
|
updatedAt: z.ZodString;
|
|
41
41
|
}, "strip", z.ZodTypeAny, {
|
|
42
|
-
id: string;
|
|
43
|
-
createdAt: string;
|
|
44
|
-
updatedAt: string;
|
|
45
|
-
code: string;
|
|
46
42
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
47
|
-
|
|
43
|
+
code: string;
|
|
44
|
+
createdAt: string;
|
|
45
|
+
id: string;
|
|
48
46
|
description: string;
|
|
47
|
+
updatedAt: string;
|
|
49
48
|
name: string;
|
|
49
|
+
scope: "seller" | "admin";
|
|
50
50
|
isActive: boolean;
|
|
51
51
|
isPublic: boolean;
|
|
52
52
|
usageCount: number;
|
|
@@ -63,14 +63,14 @@ export declare const couponItemSchema: z.ZodObject<{
|
|
|
63
63
|
applicableProductIds?: string[] | undefined;
|
|
64
64
|
applicableCategoryIds?: string[] | undefined;
|
|
65
65
|
}, {
|
|
66
|
-
id: string;
|
|
67
|
-
createdAt: string;
|
|
68
|
-
updatedAt: string;
|
|
69
|
-
code: string;
|
|
70
66
|
type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
|
|
71
|
-
|
|
67
|
+
code: string;
|
|
68
|
+
createdAt: string;
|
|
69
|
+
id: string;
|
|
72
70
|
description: string;
|
|
71
|
+
updatedAt: string;
|
|
73
72
|
name: string;
|
|
73
|
+
scope: "seller" | "admin";
|
|
74
74
|
isActive: boolean;
|
|
75
75
|
isPublic: boolean;
|
|
76
76
|
usageCount: number;
|
|
@@ -97,18 +97,18 @@ export declare const promotionsListParamsSchema: z.ZodObject<{
|
|
|
97
97
|
sort: z.ZodOptional<z.ZodString>;
|
|
98
98
|
}, "strip", z.ZodTypeAny, {
|
|
99
99
|
sort?: string | undefined;
|
|
100
|
-
pageSize?: number | undefined;
|
|
101
100
|
storeId?: string | undefined;
|
|
102
|
-
scope?: "seller" | "admin" | undefined;
|
|
103
101
|
page?: number | undefined;
|
|
102
|
+
scope?: "seller" | "admin" | undefined;
|
|
103
|
+
pageSize?: number | undefined;
|
|
104
104
|
isActive?: boolean | undefined;
|
|
105
105
|
filters?: string | undefined;
|
|
106
106
|
}, {
|
|
107
107
|
sort?: string | undefined;
|
|
108
|
-
pageSize?: number | undefined;
|
|
109
108
|
storeId?: string | undefined;
|
|
110
|
-
scope?: "seller" | "admin" | undefined;
|
|
111
109
|
page?: number | undefined;
|
|
110
|
+
scope?: "seller" | "admin" | undefined;
|
|
111
|
+
pageSize?: number | undefined;
|
|
112
112
|
isActive?: boolean | undefined;
|
|
113
113
|
filters?: string | undefined;
|
|
114
114
|
}>;
|
|
@@ -19,15 +19,15 @@ export declare const reviewVideoSchema: z.ZodObject<{
|
|
|
19
19
|
trimEnd: z.ZodOptional<z.ZodNumber>;
|
|
20
20
|
}, "strip", z.ZodTypeAny, {
|
|
21
21
|
url: string;
|
|
22
|
+
thumbnailUrl?: string | undefined;
|
|
22
23
|
trimEnd?: number | undefined;
|
|
23
24
|
trimStart?: number | undefined;
|
|
24
|
-
thumbnailUrl?: string | undefined;
|
|
25
25
|
duration?: number | undefined;
|
|
26
26
|
}, {
|
|
27
27
|
url: string;
|
|
28
|
+
thumbnailUrl?: string | undefined;
|
|
28
29
|
trimEnd?: number | undefined;
|
|
29
30
|
trimStart?: number | undefined;
|
|
30
|
-
thumbnailUrl?: string | undefined;
|
|
31
31
|
duration?: number | undefined;
|
|
32
32
|
}>;
|
|
33
33
|
/**
|
|
@@ -73,15 +73,15 @@ export declare const reviewSchema: z.ZodObject<{
|
|
|
73
73
|
trimEnd: z.ZodOptional<z.ZodNumber>;
|
|
74
74
|
}, "strip", z.ZodTypeAny, {
|
|
75
75
|
url: string;
|
|
76
|
+
thumbnailUrl?: string | undefined;
|
|
76
77
|
trimEnd?: number | undefined;
|
|
77
78
|
trimStart?: number | undefined;
|
|
78
|
-
thumbnailUrl?: string | undefined;
|
|
79
79
|
duration?: number | undefined;
|
|
80
80
|
}, {
|
|
81
81
|
url: string;
|
|
82
|
+
thumbnailUrl?: string | undefined;
|
|
82
83
|
trimEnd?: number | undefined;
|
|
83
84
|
trimStart?: number | undefined;
|
|
84
|
-
thumbnailUrl?: string | undefined;
|
|
85
85
|
duration?: number | undefined;
|
|
86
86
|
}>>;
|
|
87
87
|
status: z.ZodEnum<["pending", "approved", "rejected"]>;
|
|
@@ -92,28 +92,28 @@ export declare const reviewSchema: z.ZodObject<{
|
|
|
92
92
|
createdAt: z.ZodOptional<z.ZodString>;
|
|
93
93
|
updatedAt: z.ZodOptional<z.ZodString>;
|
|
94
94
|
}, "strip", z.ZodTypeAny, {
|
|
95
|
-
id: string;
|
|
96
95
|
status: "pending" | "approved" | "rejected";
|
|
96
|
+
id: string;
|
|
97
97
|
productId: string;
|
|
98
98
|
rating: 3 | 1 | 2 | 4 | 5;
|
|
99
99
|
userId: string;
|
|
100
100
|
userName: string;
|
|
101
|
-
createdAt?: string | undefined;
|
|
102
|
-
updatedAt?: string | undefined;
|
|
103
|
-
title?: string | undefined;
|
|
104
101
|
video?: {
|
|
105
102
|
url: string;
|
|
103
|
+
thumbnailUrl?: string | undefined;
|
|
106
104
|
trimEnd?: number | undefined;
|
|
107
105
|
trimStart?: number | undefined;
|
|
108
|
-
thumbnailUrl?: string | undefined;
|
|
109
106
|
duration?: number | undefined;
|
|
110
107
|
} | undefined;
|
|
111
108
|
featured?: boolean | undefined;
|
|
109
|
+
createdAt?: string | undefined;
|
|
110
|
+
title?: string | undefined;
|
|
112
111
|
images?: {
|
|
113
112
|
url: string;
|
|
114
113
|
thumbnailUrl?: string | undefined;
|
|
115
114
|
}[] | undefined;
|
|
116
115
|
storeName?: string | undefined;
|
|
116
|
+
updatedAt?: string | undefined;
|
|
117
117
|
productTitle?: string | undefined;
|
|
118
118
|
storeSlug?: string | undefined;
|
|
119
119
|
userAvatar?: string | undefined;
|
|
@@ -122,28 +122,28 @@ export declare const reviewSchema: z.ZodObject<{
|
|
|
122
122
|
reportCount?: number | undefined;
|
|
123
123
|
verified?: boolean | undefined;
|
|
124
124
|
}, {
|
|
125
|
-
id: string;
|
|
126
125
|
status: "pending" | "approved" | "rejected";
|
|
126
|
+
id: string;
|
|
127
127
|
productId: string;
|
|
128
128
|
rating: 3 | 1 | 2 | 4 | 5;
|
|
129
129
|
userId: string;
|
|
130
130
|
userName: string;
|
|
131
|
-
createdAt?: string | undefined;
|
|
132
|
-
updatedAt?: string | undefined;
|
|
133
|
-
title?: string | undefined;
|
|
134
131
|
video?: {
|
|
135
132
|
url: string;
|
|
133
|
+
thumbnailUrl?: string | undefined;
|
|
136
134
|
trimEnd?: number | undefined;
|
|
137
135
|
trimStart?: number | undefined;
|
|
138
|
-
thumbnailUrl?: string | undefined;
|
|
139
136
|
duration?: number | undefined;
|
|
140
137
|
} | undefined;
|
|
141
138
|
featured?: boolean | undefined;
|
|
139
|
+
createdAt?: string | undefined;
|
|
140
|
+
title?: string | undefined;
|
|
142
141
|
images?: {
|
|
143
142
|
url: string;
|
|
144
143
|
thumbnailUrl?: string | undefined;
|
|
145
144
|
}[] | undefined;
|
|
146
145
|
storeName?: string | undefined;
|
|
146
|
+
updatedAt?: string | undefined;
|
|
147
147
|
productTitle?: string | undefined;
|
|
148
148
|
storeSlug?: string | undefined;
|
|
149
149
|
userAvatar?: string | undefined;
|
|
@@ -164,22 +164,22 @@ export declare const reviewListParamsSchema: z.ZodObject<{
|
|
|
164
164
|
perPage: z.ZodOptional<z.ZodNumber>;
|
|
165
165
|
sort: z.ZodOptional<z.ZodString>;
|
|
166
166
|
}, "strip", z.ZodTypeAny, {
|
|
167
|
+
status?: "pending" | "approved" | "rejected" | undefined;
|
|
167
168
|
sort?: string | undefined;
|
|
168
|
-
perPage?: number | undefined;
|
|
169
169
|
storeId?: string | undefined;
|
|
170
|
-
status?: "pending" | "approved" | "rejected" | undefined;
|
|
171
170
|
featured?: boolean | undefined;
|
|
172
171
|
page?: number | undefined;
|
|
172
|
+
perPage?: number | undefined;
|
|
173
173
|
productId?: string | undefined;
|
|
174
174
|
rating?: number | undefined;
|
|
175
175
|
userId?: string | undefined;
|
|
176
176
|
}, {
|
|
177
|
+
status?: "pending" | "approved" | "rejected" | undefined;
|
|
177
178
|
sort?: string | undefined;
|
|
178
|
-
perPage?: number | undefined;
|
|
179
179
|
storeId?: string | undefined;
|
|
180
|
-
status?: "pending" | "approved" | "rejected" | undefined;
|
|
181
180
|
featured?: boolean | undefined;
|
|
182
181
|
page?: number | undefined;
|
|
182
|
+
perPage?: number | undefined;
|
|
183
183
|
productId?: string | undefined;
|
|
184
184
|
rating?: number | undefined;
|
|
185
185
|
userId?: string | undefined;
|
|
@@ -13,7 +13,7 @@ import { userRepository } from "../../auth/repository/user.repository";
|
|
|
13
13
|
import { storeRepository } from "../../stores/repository/store.repository";
|
|
14
14
|
import { cartRepository } from "../../cart/repository/cart.repository";
|
|
15
15
|
import { maskOfferForSeller } from "../../../security";
|
|
16
|
-
import { ERROR_MESSAGES, AuthorizationError, ValidationError, NotFoundError, } from "../../../errors";
|
|
16
|
+
import { ERROR_MESSAGES, AuthorizationError, ValidationError, NotFoundError, OFFER_ERROR_CODES, } from "../../../errors";
|
|
17
17
|
import { OfferStatusValues } from "../schemas";
|
|
18
18
|
const ERR_OFFER_NOT_FOUND = "Offer not found";
|
|
19
19
|
const ERR_NOT_AUTHORISED = "Not authorized";
|
|
@@ -89,8 +89,12 @@ export async function respondToOffer(userId, input) {
|
|
|
89
89
|
if (new Date() > offer.expiresAt)
|
|
90
90
|
throw new ValidationError(ERROR_MESSAGES.OFFER.EXPIRED);
|
|
91
91
|
let updated;
|
|
92
|
+
const CHECKOUT_WINDOW_MS = 48 * 60 * 60 * 1000;
|
|
92
93
|
if (action === "accept") {
|
|
93
94
|
updated = await offerRepository.accept(offerId, offer.offerAmount, sellerNote);
|
|
95
|
+
await offerRepository.update(offerId, {
|
|
96
|
+
checkoutDeadline: new Date(Date.now() + CHECKOUT_WINDOW_MS),
|
|
97
|
+
});
|
|
94
98
|
}
|
|
95
99
|
else if (action === "decline") {
|
|
96
100
|
updated = await offerRepository.decline(offerId, sellerNote);
|
|
@@ -141,6 +145,9 @@ export async function acceptCounterOffer(userId, offerId) {
|
|
|
141
145
|
if (new Date() > offer.expiresAt)
|
|
142
146
|
throw new ValidationError(ERROR_MESSAGES.OFFER.EXPIRED);
|
|
143
147
|
const updated = await offerRepository.acceptCounter(offerId);
|
|
148
|
+
await offerRepository.update(offerId, {
|
|
149
|
+
checkoutDeadline: new Date(Date.now() + 48 * 60 * 60 * 1000),
|
|
150
|
+
});
|
|
144
151
|
const counterStore = offer.storeId ? await storeRepository.findById(offer.storeId) : null;
|
|
145
152
|
if (counterStore?.ownerId)
|
|
146
153
|
await sendNotification({
|
|
@@ -254,6 +261,8 @@ export async function checkoutOffer(userId, offerId) {
|
|
|
254
261
|
throw new ValidationError("Only accepted offers can be checked out.");
|
|
255
262
|
if (!offer.lockedPrice)
|
|
256
263
|
throw new ValidationError("Offer price not confirmed. Contact support.");
|
|
264
|
+
if (offer.checkoutDeadline && new Date() > offer.checkoutDeadline)
|
|
265
|
+
throw new ValidationError("This accepted offer has expired. Please make a new offer.", { code: OFFER_ERROR_CODES.CHECKOUT_EXPIRED });
|
|
257
266
|
const product = await productRepository.findById(offer.productId);
|
|
258
267
|
if (!product)
|
|
259
268
|
throw new NotFoundError(ERROR_MESSAGES.PRODUCT.NOT_FOUND);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface PhysicalLocation {
|
|
2
|
+
zone: string;
|
|
3
|
+
shelf: string;
|
|
4
|
+
bin: string;
|
|
5
|
+
}
|
|
6
|
+
interface PhysicalLocationModalProps {
|
|
7
|
+
count: number;
|
|
8
|
+
onSave: (loc: PhysicalLocation) => Promise<void>;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function PhysicalLocationModal({ count, onSave, onClose }: PhysicalLocationModalProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Modal } from "../../../ui/components/Modal";
|
|
5
|
+
import { Input } from "../../../ui/components/Input";
|
|
6
|
+
import { Button } from "../../../ui/components/Button";
|
|
7
|
+
import { Row } from "../../../ui/components/Layout";
|
|
8
|
+
import { Text } from "../../../ui/components/Typography";
|
|
9
|
+
export function PhysicalLocationModal({ count, onSave, onClose }) {
|
|
10
|
+
const [zone, setZone] = useState("");
|
|
11
|
+
const [shelf, setShelf] = useState("");
|
|
12
|
+
const [bin, setBin] = useState("");
|
|
13
|
+
const [saving, setSaving] = useState(false);
|
|
14
|
+
const [error, setError] = useState("");
|
|
15
|
+
const handleSave = async () => {
|
|
16
|
+
setSaving(true);
|
|
17
|
+
setError("");
|
|
18
|
+
try {
|
|
19
|
+
await onSave({ zone: zone.trim(), shelf: shelf.trim(), bin: bin.trim() });
|
|
20
|
+
onClose();
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
setError(e instanceof Error ? e.message : "Failed to save location");
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
setSaving(false);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return (_jsx(Modal, { isOpen: true, onClose: onClose, title: `Set Location — ${count} item${count !== 1 ? "s" : ""}`, size: "sm", actions: _jsxs(Row, { gap: "sm", justify: "end", children: [_jsx(Button, { variant: "ghost", onClick: onClose, disabled: saving, children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: handleSave, disabled: saving, children: saving ? "Saving..." : "Save" })] }), children: _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [_jsx(Text, { variant: "muted", children: "Assign a physical storage location to the selected items. All fields are optional." }), _jsx(Input, { label: "Zone (e.g. A, B, Storage-1)", value: zone, onChange: e => setZone(e.target.value), placeholder: "A" }), _jsx(Input, { label: "Shelf (e.g. 3, Top, Middle)", value: shelf, onChange: e => setShelf(e.target.value), placeholder: "3" }), _jsx(Input, { label: "Bin (e.g. Blue, Box-12)", value: bin, onChange: e => setBin(e.target.value), placeholder: "Blue" }), error && _jsx(Text, { variant: "error", children: error })] }) }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
interface PrintCenterStore {
|
|
2
|
+
id: string;
|
|
3
|
+
storeName: string;
|
|
4
|
+
storeDescription?: string;
|
|
5
|
+
storeLogoURL?: string;
|
|
6
|
+
storeCategory?: string;
|
|
7
|
+
}
|
|
8
|
+
interface PrintCenterProduct {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
price: number;
|
|
12
|
+
slug: string;
|
|
13
|
+
listingType?: string;
|
|
14
|
+
condition?: string;
|
|
15
|
+
stockCount?: number;
|
|
16
|
+
physicalLocation?: string;
|
|
17
|
+
}
|
|
18
|
+
interface PrintCenterOrder {
|
|
19
|
+
id: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
status: string;
|
|
22
|
+
buyerDisplayName?: string;
|
|
23
|
+
buyerCity?: string;
|
|
24
|
+
items: {
|
|
25
|
+
productName: string;
|
|
26
|
+
quantity: number;
|
|
27
|
+
price: number;
|
|
28
|
+
}[];
|
|
29
|
+
physicalLocation?: string;
|
|
30
|
+
}
|
|
31
|
+
interface PrintCenterViewProps {
|
|
32
|
+
store?: PrintCenterStore | null;
|
|
33
|
+
publicBaseUrl?: string;
|
|
34
|
+
isAdmin?: boolean;
|
|
35
|
+
brandName?: string;
|
|
36
|
+
initialProducts?: PrintCenterProduct[];
|
|
37
|
+
initialOrders?: PrintCenterOrder[];
|
|
38
|
+
}
|
|
39
|
+
export declare function PrintCenterView({ store, brandName }: PrintCenterViewProps): import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Heading, Text } from "../../../ui/components/Typography";
|
|
4
|
+
export function PrintCenterView({ store, brandName = "LetItRip" }) {
|
|
5
|
+
return (_jsxs("div", { className: "flex flex-col items-center justify-center py-24 text-center gap-4", children: [_jsxs(Heading, { level: 2, children: [brandName, " Print Center"] }), store && _jsx(Text, { size: "sm", variant: "muted", children: store.storeName }), _jsx(Text, { size: "sm", variant: "muted", children: "Print center features are coming soon." })] }));
|
|
6
|
+
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
|
-
import { X, Eye } from "lucide-react";
|
|
4
|
+
import { X, Eye, Printer, MapPin } from "lucide-react";
|
|
5
5
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
6
6
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
7
|
-
import {
|
|
7
|
+
import { useActionDispatch } from "../../../react/hooks/use-action-dispatch";
|
|
8
|
+
import { BulkActionBar, Badge, Button, Div, FilterChipGroup, Heading, Input, ListingToolbar, Pagination, ListingViewShell, Select, SideDrawer, Stack, Text } from "../../../ui";
|
|
8
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
10
|
import { SELLER_ORDER_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
11
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
12
|
+
import { PhysicalLocationModal } from "./PhysicalLocationModal";
|
|
13
|
+
import { ROUTES } from "../../../constants";
|
|
10
14
|
import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
|
|
11
15
|
import { DataTable } from "../../admin/components/DataTable";
|
|
12
16
|
// ---------------------------------------------------------------------------
|
|
@@ -111,6 +115,8 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
111
115
|
const hasChildren = React.Children.count(children) > 0;
|
|
112
116
|
const [view, setView] = useState("table");
|
|
113
117
|
const [selectedOrderId, setSelectedOrderId] = useState(null);
|
|
118
|
+
const [setLocationOpen, setSetLocationOpen] = useState(false);
|
|
119
|
+
const dispatch = useActionDispatch();
|
|
114
120
|
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
115
121
|
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
116
122
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
@@ -151,6 +157,7 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
151
157
|
q: table.get("q") || undefined,
|
|
152
158
|
mapRows: (response) => toRecordArray(response.orders).map((item, index) => {
|
|
153
159
|
const itemsArr = Array.isArray(item.items) ? item.items : [];
|
|
160
|
+
const loc = item.physicalLocation;
|
|
154
161
|
return {
|
|
155
162
|
id: toStringValue(item.id, `order-${index}`),
|
|
156
163
|
primary: toStringValue(item.id, "Order"),
|
|
@@ -160,6 +167,9 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
160
167
|
itemCount: itemsArr.length,
|
|
161
168
|
totalAmount: Number(item.totalAmount ?? item.total ?? 0),
|
|
162
169
|
buyerName: toStringValue(item.buyerName ?? item.buyerDisplayName, "Unknown buyer"),
|
|
170
|
+
physicalLocation: loc && typeof loc.zone === "string"
|
|
171
|
+
? { zone: loc.zone, shelf: loc.shelf ?? "", bin: loc.bin ?? "" }
|
|
172
|
+
: undefined,
|
|
163
173
|
};
|
|
164
174
|
}),
|
|
165
175
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
@@ -184,6 +194,12 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
184
194
|
className: "w-32",
|
|
185
195
|
render: (row) => (_jsx(Badge, { variant: STATUS_BADGE_VARIANT[row.status?.toUpperCase()] ?? "default", children: row.status })),
|
|
186
196
|
},
|
|
197
|
+
{
|
|
198
|
+
key: "physicalLocation",
|
|
199
|
+
header: "Staging",
|
|
200
|
+
className: "w-28",
|
|
201
|
+
render: (row) => row.physicalLocation ? (_jsxs("span", { className: "text-xs font-mono text-zinc-500 dark:text-zinc-400", children: [row.physicalLocation.zone, "/", row.physicalLocation.shelf, "/", row.physicalLocation.bin] })) : (_jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-600", children: "\u2014" })),
|
|
202
|
+
},
|
|
187
203
|
{
|
|
188
204
|
key: "updatedAt",
|
|
189
205
|
header: "Date",
|
|
@@ -193,8 +209,37 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
193
209
|
];
|
|
194
210
|
const renderRowActions = useCallback((row) => (_jsx("button", { type: "button", onClick: () => setSelectedOrderId(row.id), title: "View order details", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 hover:text-zinc-700 dark:hover:text-zinc-300 transition-colors", children: _jsx(Eye, { className: "h-4 w-4" }) })), []);
|
|
195
211
|
const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
|
|
212
|
+
const handlePrintPackingSlips = useCallback(() => {
|
|
213
|
+
const ids = selection.selectedIds.join(",");
|
|
214
|
+
void dispatch({
|
|
215
|
+
type: "NAVIGATE",
|
|
216
|
+
href: `${String(ROUTES.STORE.INVENTORY_PRINT)}?type=order&ids=${ids}&autoprint=1`,
|
|
217
|
+
});
|
|
218
|
+
}, [selection.selectedIds, dispatch]);
|
|
219
|
+
const handleSetLocation = useCallback(async (loc) => {
|
|
220
|
+
await fetch(SELLER_ENDPOINTS.ORDERS_BULK_LOCATION, {
|
|
221
|
+
method: "PATCH",
|
|
222
|
+
headers: { "Content-Type": "application/json" },
|
|
223
|
+
body: JSON.stringify({ orderIds: selection.selectedIds, physicalLocation: loc }),
|
|
224
|
+
});
|
|
225
|
+
setSetLocationOpen(false);
|
|
226
|
+
}, [selection.selectedIds]);
|
|
227
|
+
const bulkActions = [
|
|
228
|
+
{
|
|
229
|
+
id: ACTIONS.STORE["print-packing-slips"].id,
|
|
230
|
+
label: ACTIONS.STORE["print-packing-slips"].label,
|
|
231
|
+
icon: _jsx(Printer, { className: "w-4 h-4" }),
|
|
232
|
+
onClick: handlePrintPackingSlips,
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: ACTIONS.STORE["set-location"].id,
|
|
236
|
+
label: ACTIONS.STORE["set-location"].label,
|
|
237
|
+
icon: _jsx(MapPin, { className: "w-4 h-4" }),
|
|
238
|
+
onClick: () => setSetLocationOpen(true),
|
|
239
|
+
},
|
|
240
|
+
];
|
|
196
241
|
if (hasChildren) {
|
|
197
242
|
return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
|
|
198
243
|
}
|
|
199
|
-
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer name", 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 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No orders yet", renderRowActions: renderRowActions })] }), filterOpen && (_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 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-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), selectedOrderId && (_jsx(OrderDetailDrawer, { orderId: selectedOrderId, apiBase: orderDetailApiBase, onClose: () => setSelectedOrderId(null) }))] }));
|
|
244
|
+
return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by order ID or buyer name", 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.selectedIds.length > 0 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+88px)] z-20 px-3 sm:px-4 py-2 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700", children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _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 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No orders yet", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: () => selection.toggleAll(), renderRowActions: renderRowActions })] }), filterOpen && (_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 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-[var(--appkit-color-primary)] py-2.5 text-sm font-semibold text-white transition-colors active:scale-[0.98]", children: ["Apply", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] })), selectedOrderId && (_jsx(OrderDetailDrawer, { orderId: selectedOrderId, apiBase: orderDetailApiBase, onClose: () => setSelectedOrderId(null) })), setLocationOpen && (_jsx(PhysicalLocationModal, { count: selection.selectedIds.length, onSave: handleSetLocation, onClose: () => setSetLocationOpen(false) }))] }));
|
|
200
245
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useRef, useState } from "react";
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
|
-
import { FormShell, StepForm, useFormShell } from "../../shell";
|
|
5
|
+
import { FormShell, StepForm, StepFormActions, useFormShell } from "../../shell";
|
|
6
6
|
import { Alert, Button, Div, FormField, FormGroup, Heading, Section, Stack, Text, Toggle } from "../../../ui";
|
|
7
|
-
import { ImageUpload, MediaUploadList, useMediaUpload } from "../../media";
|
|
7
|
+
import { ImageUpload, MediaUploadField, MediaUploadList, useMediaUpload } from "../../media";
|
|
8
8
|
import { StoreAddressSelectorCreate } from "../../stores/components/StoreAddressSelectorCreate";
|
|
9
9
|
const CONDITION_OPTIONS = [
|
|
10
10
|
{ value: "new", label: "New" },
|
|
@@ -52,7 +52,11 @@ function StepMedia({ values, onChange, storeSlug = "store", }) {
|
|
|
52
52
|
store: storeSlug,
|
|
53
53
|
category: values.category ?? "uncategorized",
|
|
54
54
|
});
|
|
55
|
-
}, accept: "image/*", maxItems: 10, maxSizeMB: 10, helperText: "Show multiple angles, grading details, or box contents." }), _jsx(
|
|
55
|
+
}, accept: "image/*,video/*", maxItems: 10, maxSizeMB: 10, helperText: "Show multiple angles, grading details, or box contents." }), _jsx(MediaUploadField, { label: "Product Video (optional)", value: values.video ?? "", onChange: (url) => onChange({ video: url }), onUpload: (file) => upload(file, "products", true, {
|
|
56
|
+
type: "product-video",
|
|
57
|
+
name: values.title ?? "product",
|
|
58
|
+
store: storeSlug,
|
|
59
|
+
}), kind: "video", helperText: "MP4, WebM or QuickTime \u2014 max 50 MB" }), _jsx(FormField, { name: "youtubeId", label: "YouTube Video ID (optional)", type: "text", value: values.youtubeId ?? "", onChange: (v) => onChange({ youtubeId: v }), placeholder: "e.g. dQw4w9WgXcQ", helpText: "Paste the 11-character ID from the YouTube URL" })] }));
|
|
56
60
|
}
|
|
57
61
|
// ── Step 3: Auction Settings ───────────────────────────────────────────────
|
|
58
62
|
function StepAuctionSettings({ values, onChange, }) {
|
|
@@ -139,6 +143,7 @@ const EDIT_SECTIONS = [
|
|
|
139
143
|
export function SellerProductShell({ mode, listingType = "standard", initialValues, onSave, onPublish, onDiscard, isLoading = false, storeSlug, renderCategorySelector, renderBrandSelector, renderAddressSelector, renderTemplateSelector, onSaveAsTemplate, previewSlot, }) {
|
|
140
144
|
const [draft, setDraft] = useState(initialValues ?? { status: "draft", condition: "new" });
|
|
141
145
|
const [currentStep, setCurrentStep] = useState(0);
|
|
146
|
+
const [stepError, setStepError] = useState(null);
|
|
142
147
|
const { isDirty, markDirty, markClean } = useFormShell();
|
|
143
148
|
const router = useRouter();
|
|
144
149
|
const update = useCallback((partial) => {
|
|
@@ -225,12 +230,29 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
225
230
|
render: ({ values, onChange }) => (_jsx(StepPublish, { values: values, onChange: onChange })),
|
|
226
231
|
},
|
|
227
232
|
];
|
|
233
|
+
const handleNext = useCallback(async () => {
|
|
234
|
+
const step = steps[currentStep];
|
|
235
|
+
if (step?.validate) {
|
|
236
|
+
const err = step.validate(draft);
|
|
237
|
+
if (err) {
|
|
238
|
+
setStepError(err);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
setStepError(null);
|
|
243
|
+
if (currentStep < steps.length - 1) {
|
|
244
|
+
setCurrentStep((c) => c + 1);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
await handlePublish();
|
|
248
|
+
}
|
|
249
|
+
}, [currentStep, steps, draft, handlePublish]);
|
|
228
250
|
const breadcrumb = mode === "create" ? `Store / ${listingTypeLabel}s / New` : `Store / ${listingTypeLabel}s / Edit`;
|
|
229
251
|
const title = mode === "create"
|
|
230
252
|
? `New ${listingTypeLabel}`
|
|
231
253
|
: draft.title ?? `Edit ${listingTypeLabel}`;
|
|
232
254
|
if (mode === "create") {
|
|
233
|
-
return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, previewSlot: previewSlot, children: _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: handlePublish, completeLabel: `Publish ${listingTypeLabel}`, currentStep: currentStep, onStepChange: setCurrentStep, isLoading: isLoading }) }));
|
|
255
|
+
return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, previewSlot: previewSlot, renderBottomBar: () => (_jsxs("div", { className: "flex-shrink-0 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", children: [_jsx(StepFormActions, { currentStep: currentStep, totalSteps: steps.length, onNext: () => void handleNext(), onPrev: currentStep > 0 ? () => setCurrentStep((c) => c - 1) : undefined, completeLabel: `Publish ${listingTypeLabel}`, isLoading: isLoading && currentStep === steps.length - 1, disabled: isLoading }), stepError && (_jsx(Text, { className: "px-5 pb-3 text-sm text-[var(--appkit-color-error)]", children: stepError }))] })), children: _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: handlePublish, completeLabel: `Publish ${listingTypeLabel}`, currentStep: currentStep, onStepChange: setCurrentStep, isLoading: isLoading, hideActions: true }) }));
|
|
234
256
|
}
|
|
235
257
|
// Edit mode — FormShell with section nav + full form
|
|
236
258
|
const editSections = [
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useState, useCallback } from "react";
|
|
4
4
|
import { useActionDispatch } from "../../../react/hooks/use-action-dispatch";
|
|
5
|
-
import { X, Pencil, Trash2 } from "lucide-react";
|
|
5
|
+
import { X, Pencil, Trash2, Printer, MapPin } from "lucide-react";
|
|
6
|
+
import { PhysicalLocationModal } from "./PhysicalLocationModal";
|
|
6
7
|
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
8
|
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
8
|
-
import { Alert, Badge, Button, Div, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Row, Span, Text } from "../../../ui";
|
|
9
|
+
import { Alert, Badge, BulkActionBar, Button, Div, FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, Row, Span, Text } from "../../../ui";
|
|
9
10
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
11
|
import { SELLER_PRODUCT_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
11
12
|
import { ROUTES } from "../../../constants";
|
|
@@ -85,6 +86,12 @@ const PRODUCT_COLUMNS = [
|
|
|
85
86
|
className: "w-28",
|
|
86
87
|
render: (row) => (_jsx(Span, { className: "text-xs text-[var(--appkit-color-text-muted)]", children: row.updatedAt })),
|
|
87
88
|
},
|
|
89
|
+
{
|
|
90
|
+
key: "physicalLocation",
|
|
91
|
+
header: "Location",
|
|
92
|
+
className: "w-28",
|
|
93
|
+
render: (row) => row.physicalLocation ? (_jsxs(Span, { className: "text-xs font-mono text-[var(--appkit-color-text-muted)]", children: [row.physicalLocation.zone, "/", row.physicalLocation.shelf, "/", row.physicalLocation.bin] })) : (_jsx(Span, { className: "text-xs text-[var(--appkit-color-text-faint)]", children: "\u2014" })),
|
|
94
|
+
},
|
|
88
95
|
];
|
|
89
96
|
export function SellerProductsView({ onDeleteProduct, children, ...props }) {
|
|
90
97
|
const hasChildren = React.Children.count(children) > 0;
|
|
@@ -95,6 +102,7 @@ export function SellerProductsView({ onDeleteProduct, children, ...props }) {
|
|
|
95
102
|
const [filterOpen, setFilterOpen] = useState(false);
|
|
96
103
|
const [listingKind, setListingKind] = useState("all");
|
|
97
104
|
const [deletingId, setDeletingId] = useState(null);
|
|
105
|
+
const [setLocationOpen, setSetLocationOpen] = useState(false);
|
|
98
106
|
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
99
107
|
const openFilters = useCallback(() => {
|
|
100
108
|
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
@@ -172,6 +180,10 @@ export function SellerProductsView({ onDeleteProduct, children, ...props }) {
|
|
|
172
180
|
imageUrl: toStringValue(item.mainImage ?? item.images?.[0], undefined),
|
|
173
181
|
listingKind: kind,
|
|
174
182
|
price: priceRaw ? `₹${(priceRaw / 100).toLocaleString("en-IN")}` : "—",
|
|
183
|
+
physicalLocation: item.physicalLocation &&
|
|
184
|
+
typeof item.physicalLocation.zone === "string"
|
|
185
|
+
? item.physicalLocation
|
|
186
|
+
: undefined,
|
|
175
187
|
};
|
|
176
188
|
}),
|
|
177
189
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
@@ -203,15 +215,44 @@ export function SellerProductsView({ onDeleteProduct, children, ...props }) {
|
|
|
203
215
|
setDeletingId(null);
|
|
204
216
|
}
|
|
205
217
|
};
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
218
|
+
const handleBulkPrintLabels = useCallback(() => {
|
|
219
|
+
const ids = selection.selectedIds.join(",");
|
|
220
|
+
void dispatch({
|
|
221
|
+
type: "NAVIGATE",
|
|
222
|
+
href: `${String(ROUTES.STORE.INVENTORY_PRINT)}?type=product&ids=${ids}&autoprint=1`,
|
|
223
|
+
});
|
|
224
|
+
}, [selection.selectedIds, dispatch]);
|
|
225
|
+
const handleSetLocation = useCallback(async (loc) => {
|
|
226
|
+
await fetch(SELLER_ENDPOINTS.PRODUCTS_BULK_LOCATION, {
|
|
227
|
+
method: "PATCH",
|
|
228
|
+
headers: { "Content-Type": "application/json" },
|
|
229
|
+
body: JSON.stringify({ productIds: selection.selectedIds, physicalLocation: loc }),
|
|
230
|
+
});
|
|
231
|
+
setSetLocationOpen(false);
|
|
232
|
+
}, [selection.selectedIds]);
|
|
233
|
+
const bulkActions = [
|
|
234
|
+
{
|
|
235
|
+
id: ACTIONS.STORE["print-labels"].id,
|
|
236
|
+
label: ACTIONS.STORE["print-labels"].label,
|
|
237
|
+
icon: _jsx(Printer, { className: "w-4 h-4" }),
|
|
238
|
+
onClick: handleBulkPrintLabels,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: ACTIONS.STORE["set-location"].id,
|
|
242
|
+
label: ACTIONS.STORE["set-location"].label,
|
|
243
|
+
icon: _jsx(MapPin, { className: "w-4 h-4" }),
|
|
244
|
+
onClick: () => setSetLocationOpen(true),
|
|
245
|
+
},
|
|
246
|
+
];
|
|
247
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search products by name\u2026", 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(TypeChips, { active: listingKind, onChange: handleKindChange }), totalPages > 1 && (_jsx(Div, { className: "sticky z-10 flex justify-center bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)] px-3 py-1.5", style: { top: "calc(var(--header-height, 0px) + 44px)" }, children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), selection.selectedIds.length > 0 && (_jsx(Div, { className: "sticky z-20 px-3 lg:px-4 py-2 bg-[var(--appkit-color-surface)]/95 backdrop-blur-sm border-b border-[var(--appkit-color-border)]", style: { top: "calc(var(--header-height, 0px) + 88px)" }, children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _jsxs(Div, { className: "py-4 px-3 lg:px-4", children: [errorMessage && (_jsx(Alert, { variant: "error", className: "mb-4", children: errorMessage })), _jsx(DataTable, { columns: PRODUCT_COLUMNS, rows: rows, isLoading: isLoading, emptyLabel: listingKind !== "all"
|
|
248
|
+
? `No ${listingKind} listings found`
|
|
249
|
+
: "No products listed yet", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => selection.toggleAll(), getRowHref: (row) => row.listingKind === "auction"
|
|
250
|
+
? String(ROUTES.STORE.AUCTIONS_EDIT(row.id))
|
|
251
|
+
: row.listingKind === "pre-order"
|
|
252
|
+
? String(ROUTES.STORE.PRE_ORDERS_EDIT(row.id))
|
|
253
|
+
: row.listingKind === "prize-draw"
|
|
254
|
+
? String(ROUTES.STORE.PRIZE_DRAWS_EDIT(row.id))
|
|
255
|
+
: String(ROUTES.STORE.PRODUCTS_EDIT(row.id)), renderRowActions: onDeleteProduct
|
|
256
|
+
? (row) => (_jsxs(Row, { className: "gap-1", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: (e) => { e.stopPropagation(); handleEdit(row); }, "aria-label": ACTIONS.STORE["edit-listing"].ariaLabel, children: _jsx(Pencil, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", action: ACTIONS.STORE["delete-listing"], onClick: (e) => { e.stopPropagation(); void handleDelete(row); }, disabled: deletingId === row.id, className: "text-[var(--appkit-color-error)] hover:bg-[var(--appkit-color-border-subtle)]", children: _jsx(Trash2, { className: "w-4 h-4" }) })] }))
|
|
257
|
+
: undefined })] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx(Div, { role: "presentation", className: "fixed inset-0 z-40 bg-black/40", onClick: () => setFilterOpen(false) }), _jsxs(Div, { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-[var(--appkit-color-surface)] shadow-2xl", children: [_jsxs(Row, { justify: "between", className: "border-b border-[var(--appkit-color-border)] px-4 py-3.5", children: [_jsx(Text, { className: "text-base font-semibold text-[var(--appkit-color-text)]", children: "Filters" }), _jsxs(Row, { className: "gap-2", children: [activeFilterCount > 0 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: clearFilters, className: "text-xs text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-error)]", children: "Clear all" })), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setFilterOpen(false), "aria-label": "Close filters", 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-[var(--appkit-color-border)] px-4 py-3.5", children: _jsxs(Button, { variant: "primary", onClick: applyFilters, className: "w-full rounded-lg py-2.5 active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }))] }), setLocationOpen && (_jsx(PhysicalLocationModal, { count: selection.selectedIds.length, onSave: handleSetLocation, onClose: () => setSetLocationOpen(false) }))] }));
|
|
217
258
|
}
|