@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.
Files changed (140) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  2. package/dist/_internal/client/features/seller/print-center/InventoryLabel.d.ts +12 -0
  3. package/dist/_internal/client/features/seller/print-center/InventoryLabel.js +49 -0
  4. package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.d.ts +7 -0
  5. package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.js +32 -0
  6. package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.d.ts +28 -0
  7. package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.js +33 -0
  8. package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.d.ts +12 -0
  9. package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.js +30 -0
  10. package/dist/_internal/client/features/seller/print-center/PrintCenterView.d.ts +12 -0
  11. package/dist/_internal/client/features/seller/print-center/PrintCenterView.js +102 -0
  12. package/dist/_internal/client/features/seller/print-center/PrintGrid.d.ts +12 -0
  13. package/dist/_internal/client/features/seller/print-center/PrintGrid.js +13 -0
  14. package/dist/_internal/client/features/seller/print-center/StoreCard.d.ts +19 -0
  15. package/dist/_internal/client/features/seller/print-center/StoreCard.js +30 -0
  16. package/dist/_internal/client/features/seller/print-center/WebsiteCard.d.ts +11 -0
  17. package/dist/_internal/client/features/seller/print-center/WebsiteCard.js +25 -0
  18. package/dist/_internal/client/features/seller/print-center/index.d.ts +11 -0
  19. package/dist/_internal/client/features/seller/print-center/index.js +10 -0
  20. package/dist/_internal/client/features/seller/print-center/types.d.ts +20 -0
  21. package/dist/_internal/client/features/seller/print-center/types.js +20 -0
  22. package/dist/_internal/client/features/seller/print-center/useInventoryPdf.d.ts +18 -0
  23. package/dist/_internal/client/features/seller/print-center/useInventoryPdf.js +76 -0
  24. package/dist/_internal/server/jobs/core/draftPrune.d.ts +2 -0
  25. package/dist/_internal/server/jobs/core/draftPrune.js +15 -0
  26. package/dist/_internal/server/jobs/core/onBidPlaced.js +1 -1
  27. package/dist/_internal/server/jobs/core/promotions.d.ts +1 -0
  28. package/dist/_internal/server/jobs/core/promotions.js +21 -6
  29. package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +18 -1
  30. package/dist/_internal/server/jobs/handlers/draftPrune.d.ts +2 -0
  31. package/dist/_internal/server/jobs/handlers/draftPrune.js +2 -0
  32. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  33. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  34. package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +15 -1
  35. package/dist/_internal/shared/actions/action-registry.js +32 -0
  36. package/dist/_internal/shared/features/blog/schema.d.ts +8 -8
  37. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +10 -10
  38. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  39. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  40. package/dist/_internal/shared/features/promotions/schema.d.ts +6 -6
  41. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  42. package/dist/client.d.ts +11 -1
  43. package/dist/client.js +6 -1
  44. package/dist/configs/next.js +1 -0
  45. package/dist/constants/api-endpoints.d.ts +6 -0
  46. package/dist/constants/api-endpoints.js +2 -0
  47. package/dist/core/server-action.d.ts +4 -0
  48. package/dist/errors/error-codes.d.ts +18 -0
  49. package/dist/errors/error-codes.js +18 -0
  50. package/dist/errors/index.d.ts +1 -1
  51. package/dist/errors/index.js +1 -1
  52. package/dist/errors/messages.d.ts +1 -0
  53. package/dist/errors/messages.js +1 -0
  54. package/dist/features/account/components/UserSidebar.js +1 -1
  55. package/dist/features/account/schemas/index.d.ts +12 -12
  56. package/dist/features/admin/components/AdminSidebar.js +1 -1
  57. package/dist/features/auctions/actions/bid-actions.js +13 -6
  58. package/dist/features/auctions/components/PlaceBidFormClient.js +10 -2
  59. package/dist/features/auctions/repository/bid.repository.d.ts +7 -0
  60. package/dist/features/auctions/repository/bid.repository.js +16 -0
  61. package/dist/features/auctions/schemas/index.d.ts +18 -18
  62. package/dist/features/auth/schemas/index.d.ts +2 -2
  63. package/dist/features/before-after/schemas/index.d.ts +2 -2
  64. package/dist/features/blog/actions/blog-actions.d.ts +32 -32
  65. package/dist/features/blog/components/BlogPostForm.js +1 -1
  66. package/dist/features/blog/schemas/index.d.ts +38 -38
  67. package/dist/features/categories/schemas/index.d.ts +10 -10
  68. package/dist/features/collections/schemas/index.d.ts +4 -4
  69. package/dist/features/consultation/schemas/index.d.ts +9 -9
  70. package/dist/features/corporate/schemas/index.d.ts +4 -4
  71. package/dist/features/events/schemas/index.d.ts +50 -50
  72. package/dist/features/faq/actions/faq-actions.d.ts +6 -6
  73. package/dist/features/faq/schemas/index.d.ts +8 -8
  74. package/dist/features/homepage/actions/homepage-section-actions.d.ts +4 -4
  75. package/dist/features/layout/AppLayoutShell.js +1 -1
  76. package/dist/features/layout/AutoBreadcrumbs.js +1 -1
  77. package/dist/features/media/AvatarUpload.js +1 -1
  78. package/dist/features/media/types/index.d.ts +4 -4
  79. package/dist/features/media/upload/ImageUpload.js +18 -9
  80. package/dist/features/media/upload/MediaUploadList.js +2 -2
  81. package/dist/features/orders/schemas/firestore.d.ts +6 -0
  82. package/dist/features/orders/schemas/index.d.ts +10 -10
  83. package/dist/features/orders/types/index.d.ts +5 -0
  84. package/dist/features/payments/schemas/index.d.ts +4 -4
  85. package/dist/features/pre-orders/schemas/index.d.ts +8 -8
  86. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  87. package/dist/features/products/components/ProductForm.js +20 -2
  88. package/dist/features/products/repository/products.repository.d.ts +3 -1
  89. package/dist/features/products/repository/products.repository.js +12 -1
  90. package/dist/features/products/schemas/firestore.d.ts +7 -0
  91. package/dist/features/products/schemas/index.d.ts +34 -34
  92. package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
  93. package/dist/features/products/types/index.d.ts +6 -0
  94. package/dist/features/promotions/schemas/index.d.ts +14 -14
  95. package/dist/features/reviews/schemas/index.d.ts +18 -18
  96. package/dist/features/seller/actions/offer-actions.js +10 -1
  97. package/dist/features/seller/components/PhysicalLocationModal.d.ts +12 -0
  98. package/dist/features/seller/components/PhysicalLocationModal.js +30 -0
  99. package/dist/features/seller/components/PrintCenterView.d.ts +40 -0
  100. package/dist/features/seller/components/PrintCenterView.js +6 -0
  101. package/dist/features/seller/components/SellerOrdersView.js +48 -3
  102. package/dist/features/seller/components/SellerProductShell.d.ts +1 -0
  103. package/dist/features/seller/components/SellerProductShell.js +26 -4
  104. package/dist/features/seller/components/SellerProductsView.js +54 -13
  105. package/dist/features/seller/components/SellerSidebar.js +1 -1
  106. package/dist/features/seller/components/index.d.ts +1 -0
  107. package/dist/features/seller/components/index.js +1 -0
  108. package/dist/features/seller/schemas/firestore.d.ts +2 -0
  109. package/dist/features/seller/schemas/firestore.js +1 -0
  110. package/dist/features/seller/schemas/index.d.ts +22 -22
  111. package/dist/features/shell/FormShell.js +1 -1
  112. package/dist/features/shell/StepForm.d.ts +3 -1
  113. package/dist/features/shell/StepForm.js +3 -3
  114. package/dist/features/stores/schemas/index.d.ts +4 -4
  115. package/dist/index.d.ts +9 -1
  116. package/dist/index.js +6 -1
  117. package/dist/next/routing/route-map.d.ts +8 -0
  118. package/dist/next/routing/route-map.js +3 -0
  119. package/dist/tailwind-utilities.css +1 -1
  120. package/dist/ui/components/RowActionMenu.js +45 -10
  121. package/dist/ui/components/SideDrawer.style.css +2 -2
  122. package/dist/ui/forms/FieldCheckbox.d.ts +13 -0
  123. package/dist/ui/forms/FieldCheckbox.js +25 -0
  124. package/dist/ui/forms/FieldInput.d.ts +11 -0
  125. package/dist/ui/forms/FieldInput.js +25 -0
  126. package/dist/ui/forms/FieldSelect.d.ts +17 -0
  127. package/dist/ui/forms/FieldSelect.js +25 -0
  128. package/dist/ui/forms/FormShell.d.ts +48 -0
  129. package/dist/ui/forms/FormShell.js +176 -0
  130. package/dist/ui/forms/FormShell.style.css +287 -0
  131. package/dist/ui/forms/index.d.ts +8 -0
  132. package/dist/ui/forms/index.js +4 -0
  133. package/dist/ui/index.d.ts +9 -0
  134. package/dist/ui/index.js +5 -0
  135. package/dist/utils/action-response.d.ts +6 -0
  136. package/dist/utils/action-response.js +22 -0
  137. package/dist/utils/index.d.ts +1 -0
  138. package/dist/utils/index.js +1 -0
  139. package/dist/validation/schemas.d.ts +10 -10
  140. 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
- scope: "seller" | "admin";
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
- scope: "seller" | "admin";
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 { Badge, Button, Div, FilterChipGroup, Heading, Input, ListingToolbar, Pagination, ListingViewShell, Select, SideDrawer, Stack, Text } from "../../../ui";
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
  }
@@ -10,6 +10,7 @@ export interface SellerProductDraft {
10
10
  tags?: string[];
11
11
  mainImage?: string;
12
12
  images?: string[];
13
+ video?: string;
13
14
  youtubeId?: string;
14
15
  price?: number;
15
16
  compareAtPrice?: number;
@@ -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(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" })] }));
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
- return (_jsx(_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) }) })), _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"
207
- ? `No ${listingKind} listings found`
208
- : "No products listed yet", getRowHref: (row) => row.listingKind === "auction"
209
- ? String(ROUTES.STORE.AUCTIONS_EDIT(row.id))
210
- : row.listingKind === "pre-order"
211
- ? String(ROUTES.STORE.PRE_ORDERS_EDIT(row.id))
212
- : row.listingKind === "prize-draw"
213
- ? String(ROUTES.STORE.PRIZE_DRAWS_EDIT(row.id))
214
- : String(ROUTES.STORE.PRODUCTS_EDIT(row.id)), renderRowActions: onDeleteProduct
215
- ? (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" }) })] }))
216
- : 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})` : ""] }) })] })] }))] }) }));
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
  }