@mohasinac/appkit 4.33.0 → 4.34.0
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/server/features/checkout/actions.js +9 -1
- package/dist/_internal/server/features/checkout/expand-order-items.js +9 -0
- package/dist/_internal/server/features/media/contextGuards.d.ts +3 -3
- package/dist/_internal/server/features/media/contextGuards.js +19 -5
- package/dist/_internal/server/features/orders/actions.d.ts +9 -0
- package/dist/_internal/server/features/orders/actions.js +47 -4
- package/dist/_internal/server/features/orders/adapters.js +10 -0
- package/dist/_internal/server/features/refunds/actions.d.ts +9 -1
- package/dist/_internal/server/features/refunds/actions.js +36 -3
- package/dist/_internal/server/features/site-settings/adapters.js +1 -1
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +12 -0
- package/dist/_internal/shared/features/orders/refund-copy.js +12 -0
- package/dist/_internal/shared/features/orders/return-reasons.d.ts +59 -0
- package/dist/_internal/shared/features/orders/return-reasons.js +96 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +30 -0
- package/dist/_internal/shared/features/orders/schema.js +20 -0
- package/dist/_internal/shared/features/products/schema.d.ts +12 -0
- package/dist/_internal/shared/features/products/schema.js +4 -1
- package/dist/_internal/shared/media/limits.d.ts +14 -0
- package/dist/_internal/shared/media/limits.js +29 -0
- package/dist/client.d.ts +9 -1
- package/dist/client.js +19 -1
- package/dist/features/about/components/PolicyPageView.js +1 -1
- package/dist/features/admin/components/AdminProductEditorView.js +6 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +2 -6
- package/dist/features/admin/components/index.d.ts +0 -2
- package/dist/features/admin/components/index.js +0 -1
- package/dist/features/admin/schemas/admin-editor-forms.js +1 -1
- package/dist/features/admin/schemas/firestore.d.ts +0 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +4 -3
- package/dist/features/contact/components/ContactForm.js +46 -31
- package/dist/features/media/upload/MediaUploadList.d.ts +16 -1
- package/dist/features/media/upload/MediaUploadList.js +58 -8
- package/dist/features/orders/components/RefundRequestView.d.ts +26 -10
- package/dist/features/orders/components/RefundRequestView.js +108 -24
- package/dist/features/orders/schemas/firestore.d.ts +42 -0
- package/dist/features/orders/types/index.d.ts +24 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +5 -3
- package/dist/features/products/components/ProductDetailPageView.js +14 -2
- package/dist/features/products/components/ProductFeatureBadges.d.ts +9 -1
- package/dist/features/products/components/ProductFeatureBadges.js +17 -1
- package/dist/features/products/components/ProductForm.js +46 -10
- package/dist/features/products/components/ProductGrid.js +10 -1
- package/dist/features/products/constants/final-sale.d.ts +40 -0
- package/dist/features/products/constants/final-sale.js +40 -0
- package/dist/features/products/index.d.ts +1 -0
- package/dist/features/products/index.js +1 -0
- package/dist/features/products/schemas/firestore.d.ts +19 -2
- package/dist/features/products/schemas/firestore.js +4 -0
- package/dist/features/products/types/index.d.ts +2 -0
- package/dist/features/seller/components/SellerProductShell.d.ts +7 -0
- package/dist/features/seller/components/SellerProductShell.js +82 -11
- package/dist/features/seller/index.d.ts +1 -0
- package/dist/features/seller/index.js +1 -0
- package/dist/features/seller/schemas/product-form.d.ts +304 -0
- package/dist/features/seller/schemas/product-form.js +132 -0
- package/dist/features/seller/utils/product-draft-mapping.d.ts +117 -0
- package/dist/features/seller/utils/product-draft-mapping.js +147 -0
- package/dist/features/shell/SectionForm.js +62 -7
- package/dist/features/shell/form-sections.js +4 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +200 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +12 -3
- package/dist/next/routing/route-map.d.ts +2 -0
- package/dist/next/routing/route-map.js +1 -0
- package/dist/schemas/registry.d.ts +271 -0
- package/dist/schemas/registry.js +9 -0
- package/dist/seed/_helpers/final-sale-fixtures.d.ts +35 -0
- package/dist/seed/_helpers/final-sale-fixtures.js +61 -0
- package/dist/seed/products-art-seed-data.js +9 -1
- package/dist/seed/products-auctions-seed-data.js +2 -1
- package/dist/seed/products-classifieds-seed-data.js +2 -1
- package/dist/seed/products-digital-codes-seed-data.js +2 -1
- package/dist/seed/products-live-items-seed-data.js +2 -1
- package/dist/seed/products-preorders-seed-data.d.ts +2 -138
- package/dist/seed/products-preorders-seed-data.js +2 -1
- package/dist/seed/products-prize-draws-seed-data.js +2 -1
- package/dist/seed/products-standard-seed-data.js +2 -1
- package/dist/seed/products-stickers-seed-data.js +9 -1
- package/dist/server.d.ts +6 -2
- package/dist/server.js +15 -2
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/motion.d.ts +12 -0
- package/dist/tokens/motion.js +12 -0
- package/dist/ui/components/CollapsibleSection.d.ts +18 -1
- package/dist/ui/components/CollapsibleSection.js +20 -3
- package/dist/ui/components/Motion.js +17 -1
- package/package.json +1 -1
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The seller listing form's DRAFT schema — the flat, form-shaped view of a
|
|
4
|
+
* product, registered as `SCHEMAS.forms.product`.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists as a separate file
|
|
7
|
+
*
|
|
8
|
+
* It is registered so `roundtrip-diff` can check it. That tool reads
|
|
9
|
+
* `SCHEMAS.forms`, parses real documents through each registered schema, and
|
|
10
|
+
* reports every key the schema would DELETE — and its own header names
|
|
11
|
+
* `productBaseSchema` eating the `classified*` / `liveItem*` fields as the
|
|
12
|
+
* reason it was written.
|
|
13
|
+
*
|
|
14
|
+
* `product` was never registered. So the one tool built to catch exactly this
|
|
15
|
+
* bug could not see the entity the bug was about, and the registry's own
|
|
16
|
+
* docstring said why: *"an entity absent from it has no such protection,
|
|
17
|
+
* which is the whole reason to register early."*
|
|
18
|
+
*
|
|
19
|
+
* ## Draft-shaped, deliberately
|
|
20
|
+
*
|
|
21
|
+
* This models what the user TYPES — flat `classifiedCity`, `liveSpecies`,
|
|
22
|
+
* `printSize` — not what is stored. `draftToProductInput` translates between
|
|
23
|
+
* the two. `roundtrip-diff` lists `product` in its `DRAFT_SHAPED` set for the
|
|
24
|
+
* same reason `blog` and `event` are there: real documents legitimately fail
|
|
25
|
+
* to parse against a draft, and only the DROPPED-KEY half of the check is
|
|
26
|
+
* meaningful. That half is the half that matters.
|
|
27
|
+
*
|
|
28
|
+
* Kept permissive (`.passthrough()`, almost everything optional): a draft is
|
|
29
|
+
* saved continuously while half-filled, so this must accept an in-progress
|
|
30
|
+
* listing. The strict rules live in `productCreateSchema` at the write
|
|
31
|
+
* boundary, which is where refusing a bad listing is useful.
|
|
32
|
+
*/
|
|
33
|
+
export declare const productDraftSchema: z.ZodObject<{
|
|
34
|
+
title: z.ZodOptional<z.ZodString>;
|
|
35
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
36
|
+
description: z.ZodOptional<z.ZodString>;
|
|
37
|
+
category: z.ZodOptional<z.ZodString>;
|
|
38
|
+
brand: z.ZodOptional<z.ZodString>;
|
|
39
|
+
condition: z.ZodOptional<z.ZodString>;
|
|
40
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
41
|
+
mainImage: z.ZodOptional<z.ZodString>;
|
|
42
|
+
images: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
43
|
+
video: z.ZodOptional<z.ZodObject<{
|
|
44
|
+
url: z.ZodString;
|
|
45
|
+
type: z.ZodOptional<z.ZodLiteral<"video">>;
|
|
46
|
+
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
47
|
+
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
48
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
49
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
50
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
51
|
+
url: z.ZodString;
|
|
52
|
+
type: z.ZodOptional<z.ZodLiteral<"video">>;
|
|
53
|
+
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
54
|
+
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
55
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
56
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
57
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
58
|
+
url: z.ZodString;
|
|
59
|
+
type: z.ZodOptional<z.ZodLiteral<"video">>;
|
|
60
|
+
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
61
|
+
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
62
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
63
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
65
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
66
|
+
externalVideoUrl: z.ZodOptional<z.ZodString>;
|
|
67
|
+
price: z.ZodOptional<z.ZodNumber>;
|
|
68
|
+
compareAtPrice: z.ZodOptional<z.ZodNumber>;
|
|
69
|
+
stockQuantity: z.ZodOptional<z.ZodNumber>;
|
|
70
|
+
featured: z.ZodOptional<z.ZodBoolean>;
|
|
71
|
+
isPromoted: z.ZodOptional<z.ZodBoolean>;
|
|
72
|
+
isNew: z.ZodOptional<z.ZodBoolean>;
|
|
73
|
+
isOnSale: z.ZodOptional<z.ZodBoolean>;
|
|
74
|
+
allowOffers: z.ZodOptional<z.ZodBoolean>;
|
|
75
|
+
minOfferPercent: z.ZodOptional<z.ZodNumber>;
|
|
76
|
+
shippingPaidBy: z.ZodOptional<z.ZodEnum<["buyer", "seller"]>>;
|
|
77
|
+
pickupAddressId: z.ZodOptional<z.ZodString>;
|
|
78
|
+
insurance: z.ZodOptional<z.ZodBoolean>;
|
|
79
|
+
insuranceCost: z.ZodOptional<z.ZodNumber>;
|
|
80
|
+
gstRate: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<5>, z.ZodLiteral<12>, z.ZodLiteral<18>, z.ZodLiteral<28>]>>;
|
|
81
|
+
hsnCode: z.ZodOptional<z.ZodString>;
|
|
82
|
+
finalSale: z.ZodOptional<z.ZodBoolean>;
|
|
83
|
+
returnPolicy: z.ZodOptional<z.ZodString>;
|
|
84
|
+
status: z.ZodOptional<z.ZodEnum<["draft", "published"]>>;
|
|
85
|
+
seoTitle: z.ZodOptional<z.ZodString>;
|
|
86
|
+
seoDescription: z.ZodOptional<z.ZodString>;
|
|
87
|
+
barcodeId: z.ZodOptional<z.ZodString>;
|
|
88
|
+
startingBid: z.ZodOptional<z.ZodNumber>;
|
|
89
|
+
reservePrice: z.ZodOptional<z.ZodNumber>;
|
|
90
|
+
buyNowPrice: z.ZodOptional<z.ZodNumber>;
|
|
91
|
+
minBidIncrement: z.ZodOptional<z.ZodNumber>;
|
|
92
|
+
auctionEndDate: z.ZodOptional<z.ZodString>;
|
|
93
|
+
auctionShippingPaidBy: z.ZodOptional<z.ZodEnum<["winner", "seller"]>>;
|
|
94
|
+
autoExtendable: z.ZodOptional<z.ZodBoolean>;
|
|
95
|
+
auctionExtensionMinutes: z.ZodOptional<z.ZodNumber>;
|
|
96
|
+
preOrderDeliveryDate: z.ZodOptional<z.ZodString>;
|
|
97
|
+
preOrderDepositPercent: z.ZodOptional<z.ZodNumber>;
|
|
98
|
+
preOrderMaxQuantity: z.ZodOptional<z.ZodNumber>;
|
|
99
|
+
preOrderProductionStatus: z.ZodOptional<z.ZodEnum<["upcoming", "in_production", "ready_to_ship"]>>;
|
|
100
|
+
preOrderCancellable: z.ZodOptional<z.ZodBoolean>;
|
|
101
|
+
classifiedCity: z.ZodOptional<z.ZodString>;
|
|
102
|
+
classifiedLocality: z.ZodOptional<z.ZodString>;
|
|
103
|
+
classifiedPincode: z.ZodOptional<z.ZodString>;
|
|
104
|
+
classifiedContactMethod: z.ZodOptional<z.ZodEnum<["chat", "phone", "both"]>>;
|
|
105
|
+
classifiedAcceptsShipping: z.ZodOptional<z.ZodBoolean>;
|
|
106
|
+
classifiedNegotiable: z.ZodOptional<z.ZodBoolean>;
|
|
107
|
+
digitalCodeDelivery: z.ZodOptional<z.ZodEnum<["auto-claim", "manual-email"]>>;
|
|
108
|
+
digitalCodePoolSize: z.ZodOptional<z.ZodNumber>;
|
|
109
|
+
digitalCodeRedemptionInstructions: z.ZodOptional<z.ZodString>;
|
|
110
|
+
digitalCodeExpiresAt: z.ZodOptional<z.ZodString>;
|
|
111
|
+
liveSpecies: z.ZodOptional<z.ZodString>;
|
|
112
|
+
liveAgeMonths: z.ZodOptional<z.ZodNumber>;
|
|
113
|
+
liveSex: z.ZodOptional<z.ZodEnum<["male", "female", "unknown", "n/a"]>>;
|
|
114
|
+
liveCareInfo: z.ZodOptional<z.ZodString>;
|
|
115
|
+
liveTransportMethod: z.ZodOptional<z.ZodEnum<["courier", "in-person", "specialist"]>>;
|
|
116
|
+
liveHandlingFee: z.ZodOptional<z.ZodNumber>;
|
|
117
|
+
liveJurisdictions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
118
|
+
liveCites: z.ZodOptional<z.ZodBoolean>;
|
|
119
|
+
printSize: z.ZodOptional<z.ZodString>;
|
|
120
|
+
printMaterial: z.ZodOptional<z.ZodString>;
|
|
121
|
+
printFinish: z.ZodOptional<z.ZodString>;
|
|
122
|
+
printEditionSize: z.ZodOptional<z.ZodNumber>;
|
|
123
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
124
|
+
title: z.ZodOptional<z.ZodString>;
|
|
125
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
126
|
+
description: z.ZodOptional<z.ZodString>;
|
|
127
|
+
category: z.ZodOptional<z.ZodString>;
|
|
128
|
+
brand: z.ZodOptional<z.ZodString>;
|
|
129
|
+
condition: z.ZodOptional<z.ZodString>;
|
|
130
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
131
|
+
mainImage: z.ZodOptional<z.ZodString>;
|
|
132
|
+
images: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
133
|
+
video: z.ZodOptional<z.ZodObject<{
|
|
134
|
+
url: z.ZodString;
|
|
135
|
+
type: z.ZodOptional<z.ZodLiteral<"video">>;
|
|
136
|
+
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
137
|
+
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
138
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
139
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
140
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
141
|
+
url: z.ZodString;
|
|
142
|
+
type: z.ZodOptional<z.ZodLiteral<"video">>;
|
|
143
|
+
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
144
|
+
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
145
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
146
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
147
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
148
|
+
url: z.ZodString;
|
|
149
|
+
type: z.ZodOptional<z.ZodLiteral<"video">>;
|
|
150
|
+
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
151
|
+
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
152
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
153
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
154
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
155
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
156
|
+
externalVideoUrl: z.ZodOptional<z.ZodString>;
|
|
157
|
+
price: z.ZodOptional<z.ZodNumber>;
|
|
158
|
+
compareAtPrice: z.ZodOptional<z.ZodNumber>;
|
|
159
|
+
stockQuantity: z.ZodOptional<z.ZodNumber>;
|
|
160
|
+
featured: z.ZodOptional<z.ZodBoolean>;
|
|
161
|
+
isPromoted: z.ZodOptional<z.ZodBoolean>;
|
|
162
|
+
isNew: z.ZodOptional<z.ZodBoolean>;
|
|
163
|
+
isOnSale: z.ZodOptional<z.ZodBoolean>;
|
|
164
|
+
allowOffers: z.ZodOptional<z.ZodBoolean>;
|
|
165
|
+
minOfferPercent: z.ZodOptional<z.ZodNumber>;
|
|
166
|
+
shippingPaidBy: z.ZodOptional<z.ZodEnum<["buyer", "seller"]>>;
|
|
167
|
+
pickupAddressId: z.ZodOptional<z.ZodString>;
|
|
168
|
+
insurance: z.ZodOptional<z.ZodBoolean>;
|
|
169
|
+
insuranceCost: z.ZodOptional<z.ZodNumber>;
|
|
170
|
+
gstRate: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<5>, z.ZodLiteral<12>, z.ZodLiteral<18>, z.ZodLiteral<28>]>>;
|
|
171
|
+
hsnCode: z.ZodOptional<z.ZodString>;
|
|
172
|
+
finalSale: z.ZodOptional<z.ZodBoolean>;
|
|
173
|
+
returnPolicy: z.ZodOptional<z.ZodString>;
|
|
174
|
+
status: z.ZodOptional<z.ZodEnum<["draft", "published"]>>;
|
|
175
|
+
seoTitle: z.ZodOptional<z.ZodString>;
|
|
176
|
+
seoDescription: z.ZodOptional<z.ZodString>;
|
|
177
|
+
barcodeId: z.ZodOptional<z.ZodString>;
|
|
178
|
+
startingBid: z.ZodOptional<z.ZodNumber>;
|
|
179
|
+
reservePrice: z.ZodOptional<z.ZodNumber>;
|
|
180
|
+
buyNowPrice: z.ZodOptional<z.ZodNumber>;
|
|
181
|
+
minBidIncrement: z.ZodOptional<z.ZodNumber>;
|
|
182
|
+
auctionEndDate: z.ZodOptional<z.ZodString>;
|
|
183
|
+
auctionShippingPaidBy: z.ZodOptional<z.ZodEnum<["winner", "seller"]>>;
|
|
184
|
+
autoExtendable: z.ZodOptional<z.ZodBoolean>;
|
|
185
|
+
auctionExtensionMinutes: z.ZodOptional<z.ZodNumber>;
|
|
186
|
+
preOrderDeliveryDate: z.ZodOptional<z.ZodString>;
|
|
187
|
+
preOrderDepositPercent: z.ZodOptional<z.ZodNumber>;
|
|
188
|
+
preOrderMaxQuantity: z.ZodOptional<z.ZodNumber>;
|
|
189
|
+
preOrderProductionStatus: z.ZodOptional<z.ZodEnum<["upcoming", "in_production", "ready_to_ship"]>>;
|
|
190
|
+
preOrderCancellable: z.ZodOptional<z.ZodBoolean>;
|
|
191
|
+
classifiedCity: z.ZodOptional<z.ZodString>;
|
|
192
|
+
classifiedLocality: z.ZodOptional<z.ZodString>;
|
|
193
|
+
classifiedPincode: z.ZodOptional<z.ZodString>;
|
|
194
|
+
classifiedContactMethod: z.ZodOptional<z.ZodEnum<["chat", "phone", "both"]>>;
|
|
195
|
+
classifiedAcceptsShipping: z.ZodOptional<z.ZodBoolean>;
|
|
196
|
+
classifiedNegotiable: z.ZodOptional<z.ZodBoolean>;
|
|
197
|
+
digitalCodeDelivery: z.ZodOptional<z.ZodEnum<["auto-claim", "manual-email"]>>;
|
|
198
|
+
digitalCodePoolSize: z.ZodOptional<z.ZodNumber>;
|
|
199
|
+
digitalCodeRedemptionInstructions: z.ZodOptional<z.ZodString>;
|
|
200
|
+
digitalCodeExpiresAt: z.ZodOptional<z.ZodString>;
|
|
201
|
+
liveSpecies: z.ZodOptional<z.ZodString>;
|
|
202
|
+
liveAgeMonths: z.ZodOptional<z.ZodNumber>;
|
|
203
|
+
liveSex: z.ZodOptional<z.ZodEnum<["male", "female", "unknown", "n/a"]>>;
|
|
204
|
+
liveCareInfo: z.ZodOptional<z.ZodString>;
|
|
205
|
+
liveTransportMethod: z.ZodOptional<z.ZodEnum<["courier", "in-person", "specialist"]>>;
|
|
206
|
+
liveHandlingFee: z.ZodOptional<z.ZodNumber>;
|
|
207
|
+
liveJurisdictions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
208
|
+
liveCites: z.ZodOptional<z.ZodBoolean>;
|
|
209
|
+
printSize: z.ZodOptional<z.ZodString>;
|
|
210
|
+
printMaterial: z.ZodOptional<z.ZodString>;
|
|
211
|
+
printFinish: z.ZodOptional<z.ZodString>;
|
|
212
|
+
printEditionSize: z.ZodOptional<z.ZodNumber>;
|
|
213
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
214
|
+
title: z.ZodOptional<z.ZodString>;
|
|
215
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
216
|
+
description: z.ZodOptional<z.ZodString>;
|
|
217
|
+
category: z.ZodOptional<z.ZodString>;
|
|
218
|
+
brand: z.ZodOptional<z.ZodString>;
|
|
219
|
+
condition: z.ZodOptional<z.ZodString>;
|
|
220
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
221
|
+
mainImage: z.ZodOptional<z.ZodString>;
|
|
222
|
+
images: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
223
|
+
video: z.ZodOptional<z.ZodObject<{
|
|
224
|
+
url: z.ZodString;
|
|
225
|
+
type: z.ZodOptional<z.ZodLiteral<"video">>;
|
|
226
|
+
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
227
|
+
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
228
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
229
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
230
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
231
|
+
url: z.ZodString;
|
|
232
|
+
type: z.ZodOptional<z.ZodLiteral<"video">>;
|
|
233
|
+
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
234
|
+
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
235
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
236
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
237
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
238
|
+
url: z.ZodString;
|
|
239
|
+
type: z.ZodOptional<z.ZodLiteral<"video">>;
|
|
240
|
+
thumbnailUrl: z.ZodOptional<z.ZodString>;
|
|
241
|
+
source: z.ZodOptional<z.ZodEnum<["upload", "youtube", "external"]>>;
|
|
242
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
243
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
244
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
245
|
+
youtubeId: z.ZodOptional<z.ZodString>;
|
|
246
|
+
externalVideoUrl: z.ZodOptional<z.ZodString>;
|
|
247
|
+
price: z.ZodOptional<z.ZodNumber>;
|
|
248
|
+
compareAtPrice: z.ZodOptional<z.ZodNumber>;
|
|
249
|
+
stockQuantity: z.ZodOptional<z.ZodNumber>;
|
|
250
|
+
featured: z.ZodOptional<z.ZodBoolean>;
|
|
251
|
+
isPromoted: z.ZodOptional<z.ZodBoolean>;
|
|
252
|
+
isNew: z.ZodOptional<z.ZodBoolean>;
|
|
253
|
+
isOnSale: z.ZodOptional<z.ZodBoolean>;
|
|
254
|
+
allowOffers: z.ZodOptional<z.ZodBoolean>;
|
|
255
|
+
minOfferPercent: z.ZodOptional<z.ZodNumber>;
|
|
256
|
+
shippingPaidBy: z.ZodOptional<z.ZodEnum<["buyer", "seller"]>>;
|
|
257
|
+
pickupAddressId: z.ZodOptional<z.ZodString>;
|
|
258
|
+
insurance: z.ZodOptional<z.ZodBoolean>;
|
|
259
|
+
insuranceCost: z.ZodOptional<z.ZodNumber>;
|
|
260
|
+
gstRate: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<5>, z.ZodLiteral<12>, z.ZodLiteral<18>, z.ZodLiteral<28>]>>;
|
|
261
|
+
hsnCode: z.ZodOptional<z.ZodString>;
|
|
262
|
+
finalSale: z.ZodOptional<z.ZodBoolean>;
|
|
263
|
+
returnPolicy: z.ZodOptional<z.ZodString>;
|
|
264
|
+
status: z.ZodOptional<z.ZodEnum<["draft", "published"]>>;
|
|
265
|
+
seoTitle: z.ZodOptional<z.ZodString>;
|
|
266
|
+
seoDescription: z.ZodOptional<z.ZodString>;
|
|
267
|
+
barcodeId: z.ZodOptional<z.ZodString>;
|
|
268
|
+
startingBid: z.ZodOptional<z.ZodNumber>;
|
|
269
|
+
reservePrice: z.ZodOptional<z.ZodNumber>;
|
|
270
|
+
buyNowPrice: z.ZodOptional<z.ZodNumber>;
|
|
271
|
+
minBidIncrement: z.ZodOptional<z.ZodNumber>;
|
|
272
|
+
auctionEndDate: z.ZodOptional<z.ZodString>;
|
|
273
|
+
auctionShippingPaidBy: z.ZodOptional<z.ZodEnum<["winner", "seller"]>>;
|
|
274
|
+
autoExtendable: z.ZodOptional<z.ZodBoolean>;
|
|
275
|
+
auctionExtensionMinutes: z.ZodOptional<z.ZodNumber>;
|
|
276
|
+
preOrderDeliveryDate: z.ZodOptional<z.ZodString>;
|
|
277
|
+
preOrderDepositPercent: z.ZodOptional<z.ZodNumber>;
|
|
278
|
+
preOrderMaxQuantity: z.ZodOptional<z.ZodNumber>;
|
|
279
|
+
preOrderProductionStatus: z.ZodOptional<z.ZodEnum<["upcoming", "in_production", "ready_to_ship"]>>;
|
|
280
|
+
preOrderCancellable: z.ZodOptional<z.ZodBoolean>;
|
|
281
|
+
classifiedCity: z.ZodOptional<z.ZodString>;
|
|
282
|
+
classifiedLocality: z.ZodOptional<z.ZodString>;
|
|
283
|
+
classifiedPincode: z.ZodOptional<z.ZodString>;
|
|
284
|
+
classifiedContactMethod: z.ZodOptional<z.ZodEnum<["chat", "phone", "both"]>>;
|
|
285
|
+
classifiedAcceptsShipping: z.ZodOptional<z.ZodBoolean>;
|
|
286
|
+
classifiedNegotiable: z.ZodOptional<z.ZodBoolean>;
|
|
287
|
+
digitalCodeDelivery: z.ZodOptional<z.ZodEnum<["auto-claim", "manual-email"]>>;
|
|
288
|
+
digitalCodePoolSize: z.ZodOptional<z.ZodNumber>;
|
|
289
|
+
digitalCodeRedemptionInstructions: z.ZodOptional<z.ZodString>;
|
|
290
|
+
digitalCodeExpiresAt: z.ZodOptional<z.ZodString>;
|
|
291
|
+
liveSpecies: z.ZodOptional<z.ZodString>;
|
|
292
|
+
liveAgeMonths: z.ZodOptional<z.ZodNumber>;
|
|
293
|
+
liveSex: z.ZodOptional<z.ZodEnum<["male", "female", "unknown", "n/a"]>>;
|
|
294
|
+
liveCareInfo: z.ZodOptional<z.ZodString>;
|
|
295
|
+
liveTransportMethod: z.ZodOptional<z.ZodEnum<["courier", "in-person", "specialist"]>>;
|
|
296
|
+
liveHandlingFee: z.ZodOptional<z.ZodNumber>;
|
|
297
|
+
liveJurisdictions: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
298
|
+
liveCites: z.ZodOptional<z.ZodBoolean>;
|
|
299
|
+
printSize: z.ZodOptional<z.ZodString>;
|
|
300
|
+
printMaterial: z.ZodOptional<z.ZodString>;
|
|
301
|
+
printFinish: z.ZodOptional<z.ZodString>;
|
|
302
|
+
printEditionSize: z.ZodOptional<z.ZodNumber>;
|
|
303
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
304
|
+
export type ProductDraftInput = z.infer<typeof productDraftSchema>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { PRODUCT_MAX_IMAGES } from "../../../_internal/shared/media/limits";
|
|
3
|
+
/**
|
|
4
|
+
* The seller listing form's DRAFT schema — the flat, form-shaped view of a
|
|
5
|
+
* product, registered as `SCHEMAS.forms.product`.
|
|
6
|
+
*
|
|
7
|
+
* ## Why this exists as a separate file
|
|
8
|
+
*
|
|
9
|
+
* It is registered so `roundtrip-diff` can check it. That tool reads
|
|
10
|
+
* `SCHEMAS.forms`, parses real documents through each registered schema, and
|
|
11
|
+
* reports every key the schema would DELETE — and its own header names
|
|
12
|
+
* `productBaseSchema` eating the `classified*` / `liveItem*` fields as the
|
|
13
|
+
* reason it was written.
|
|
14
|
+
*
|
|
15
|
+
* `product` was never registered. So the one tool built to catch exactly this
|
|
16
|
+
* bug could not see the entity the bug was about, and the registry's own
|
|
17
|
+
* docstring said why: *"an entity absent from it has no such protection,
|
|
18
|
+
* which is the whole reason to register early."*
|
|
19
|
+
*
|
|
20
|
+
* ## Draft-shaped, deliberately
|
|
21
|
+
*
|
|
22
|
+
* This models what the user TYPES — flat `classifiedCity`, `liveSpecies`,
|
|
23
|
+
* `printSize` — not what is stored. `draftToProductInput` translates between
|
|
24
|
+
* the two. `roundtrip-diff` lists `product` in its `DRAFT_SHAPED` set for the
|
|
25
|
+
* same reason `blog` and `event` are there: real documents legitimately fail
|
|
26
|
+
* to parse against a draft, and only the DROPPED-KEY half of the check is
|
|
27
|
+
* meaningful. That half is the half that matters.
|
|
28
|
+
*
|
|
29
|
+
* Kept permissive (`.passthrough()`, almost everything optional): a draft is
|
|
30
|
+
* saved continuously while half-filled, so this must accept an in-progress
|
|
31
|
+
* listing. The strict rules live in `productCreateSchema` at the write
|
|
32
|
+
* boundary, which is where refusing a bad listing is useful.
|
|
33
|
+
*/
|
|
34
|
+
export const productDraftSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
title: z.string().max(200).optional(),
|
|
37
|
+
slug: z.string().optional(),
|
|
38
|
+
description: z.string().max(10000).optional(),
|
|
39
|
+
category: z.string().optional(),
|
|
40
|
+
brand: z.string().optional(),
|
|
41
|
+
condition: z.string().optional(),
|
|
42
|
+
tags: z.array(z.string()).optional(),
|
|
43
|
+
// Media
|
|
44
|
+
mainImage: z.string().optional(),
|
|
45
|
+
images: z.array(z.string()).max(PRODUCT_MAX_IMAGES).optional(),
|
|
46
|
+
/*
|
|
47
|
+
* Spelled out rather than `z.record(z.unknown())`: a draft's video can
|
|
48
|
+
* come from an upload, a YouTube id or an external URL, and each source
|
|
49
|
+
* fills a different subset. Naming the fields is what lets this schema
|
|
50
|
+
* report a video the form would fail to carry.
|
|
51
|
+
*/
|
|
52
|
+
video: z
|
|
53
|
+
.object({
|
|
54
|
+
url: z.string(),
|
|
55
|
+
type: z.literal("video").optional(),
|
|
56
|
+
thumbnailUrl: z.string().optional(),
|
|
57
|
+
source: z.enum(["upload", "youtube", "external"]).optional(),
|
|
58
|
+
youtubeId: z.string().optional(),
|
|
59
|
+
duration: z.number().optional(),
|
|
60
|
+
})
|
|
61
|
+
.passthrough()
|
|
62
|
+
.optional(),
|
|
63
|
+
youtubeId: z.string().optional(),
|
|
64
|
+
externalVideoUrl: z.string().optional(),
|
|
65
|
+
// Pricing
|
|
66
|
+
price: z.number().optional(),
|
|
67
|
+
compareAtPrice: z.number().optional(),
|
|
68
|
+
stockQuantity: z.number().optional(),
|
|
69
|
+
featured: z.boolean().optional(),
|
|
70
|
+
isPromoted: z.boolean().optional(),
|
|
71
|
+
isNew: z.boolean().optional(),
|
|
72
|
+
isOnSale: z.boolean().optional(),
|
|
73
|
+
allowOffers: z.boolean().optional(),
|
|
74
|
+
minOfferPercent: z.number().optional(),
|
|
75
|
+
// Shipping
|
|
76
|
+
shippingPaidBy: z.enum(["buyer", "seller"]).optional(),
|
|
77
|
+
pickupAddressId: z.string().optional(),
|
|
78
|
+
insurance: z.boolean().optional(),
|
|
79
|
+
insuranceCost: z.number().optional(),
|
|
80
|
+
gstRate: z.union([z.literal(0), z.literal(5), z.literal(12), z.literal(18), z.literal(28)]).optional(),
|
|
81
|
+
hsnCode: z.string().optional(),
|
|
82
|
+
// Returns
|
|
83
|
+
finalSale: z.boolean().optional(),
|
|
84
|
+
returnPolicy: z.string().max(1000).optional(),
|
|
85
|
+
// Publish
|
|
86
|
+
status: z.enum(["draft", "published"]).optional(),
|
|
87
|
+
seoTitle: z.string().optional(),
|
|
88
|
+
seoDescription: z.string().optional(),
|
|
89
|
+
barcodeId: z.string().optional(),
|
|
90
|
+
// Auction
|
|
91
|
+
startingBid: z.number().optional(),
|
|
92
|
+
reservePrice: z.number().optional(),
|
|
93
|
+
buyNowPrice: z.number().optional(),
|
|
94
|
+
minBidIncrement: z.number().optional(),
|
|
95
|
+
auctionEndDate: z.string().optional(),
|
|
96
|
+
auctionShippingPaidBy: z.enum(["winner", "seller"]).optional(),
|
|
97
|
+
autoExtendable: z.boolean().optional(),
|
|
98
|
+
auctionExtensionMinutes: z.number().optional(),
|
|
99
|
+
// Pre-order
|
|
100
|
+
preOrderDeliveryDate: z.string().optional(),
|
|
101
|
+
preOrderDepositPercent: z.number().optional(),
|
|
102
|
+
preOrderMaxQuantity: z.number().optional(),
|
|
103
|
+
preOrderProductionStatus: z.enum(["upcoming", "in_production", "ready_to_ship"]).optional(),
|
|
104
|
+
preOrderCancellable: z.boolean().optional(),
|
|
105
|
+
// Classified — FLAT here, nested on the document.
|
|
106
|
+
classifiedCity: z.string().optional(),
|
|
107
|
+
classifiedLocality: z.string().optional(),
|
|
108
|
+
classifiedPincode: z.string().optional(),
|
|
109
|
+
classifiedContactMethod: z.enum(["chat", "phone", "both"]).optional(),
|
|
110
|
+
classifiedAcceptsShipping: z.boolean().optional(),
|
|
111
|
+
classifiedNegotiable: z.boolean().optional(),
|
|
112
|
+
// Digital code — FLAT here, nested on the document.
|
|
113
|
+
digitalCodeDelivery: z.enum(["auto-claim", "manual-email"]).optional(),
|
|
114
|
+
digitalCodePoolSize: z.number().optional(),
|
|
115
|
+
digitalCodeRedemptionInstructions: z.string().optional(),
|
|
116
|
+
digitalCodeExpiresAt: z.string().optional(),
|
|
117
|
+
// Live item — FLAT here, nested on the document.
|
|
118
|
+
liveSpecies: z.string().optional(),
|
|
119
|
+
liveAgeMonths: z.number().optional(),
|
|
120
|
+
liveSex: z.enum(["male", "female", "unknown", "n/a"]).optional(),
|
|
121
|
+
liveCareInfo: z.string().optional(),
|
|
122
|
+
liveTransportMethod: z.enum(["courier", "in-person", "specialist"]).optional(),
|
|
123
|
+
liveHandlingFee: z.number().optional(),
|
|
124
|
+
liveJurisdictions: z.array(z.string()).optional(),
|
|
125
|
+
liveCites: z.boolean().optional(),
|
|
126
|
+
// Print meta (art + stickers) — FLAT here, nested on the document.
|
|
127
|
+
printSize: z.string().optional(),
|
|
128
|
+
printMaterial: z.string().optional(),
|
|
129
|
+
printFinish: z.string().optional(),
|
|
130
|
+
printEditionSize: z.number().optional(),
|
|
131
|
+
})
|
|
132
|
+
.passthrough();
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { SellerProductDraft } from "../components/SellerProductShell";
|
|
2
|
+
/**
|
|
3
|
+
* Seller listing form draft <-> product write payload.
|
|
4
|
+
*
|
|
5
|
+
* ## The bug this closes
|
|
6
|
+
*
|
|
7
|
+
* `SellerProductDraft` is FLAT — `classifiedCity`, `liveSpecies`,
|
|
8
|
+
* `digitalCodeDelivery`, `printSize` — because that is the shape a form wants:
|
|
9
|
+
* one key per control, no nesting to thread through `onChange`.
|
|
10
|
+
*
|
|
11
|
+
* `productCreateSchema` is NESTED — `classified.meetupArea.city`,
|
|
12
|
+
* `liveItem.species`, `digitalCode.codeDeliveryMethod`, `printMeta.size` —
|
|
13
|
+
* because that is the shape `ProductDocument` stores.
|
|
14
|
+
*
|
|
15
|
+
* Nothing translated between them. Every `store/*\/new/page.tsx` spread the
|
|
16
|
+
* draft straight into `createSellerProductAction`, and `productBaseSchema` is
|
|
17
|
+
* a plain `z.object()` with no `.passthrough()`, so **every flat per-type key
|
|
18
|
+
* was stripped before the write**. A seller filled in a classified's meetup
|
|
19
|
+
* city, a live animal's species and permitted jurisdictions, a digital code's
|
|
20
|
+
* delivery method, or an art print's size and edition — pressed Publish, got a
|
|
21
|
+
* success toast — and the listing was saved without the fields that made it
|
|
22
|
+
* that KIND of listing.
|
|
23
|
+
*
|
|
24
|
+
* ## Why this lives in the ACTION, not in the pages
|
|
25
|
+
*
|
|
26
|
+
* There are ~10 `new/page.tsx` and ~10 `[id]/edit/page.tsx` files and every
|
|
27
|
+
* one of them does `{ ...draft, listingType, status }`. Applying the mapping
|
|
28
|
+
* at each is 20 chances to forget, and a new listing type's page would start
|
|
29
|
+
* out broken in exactly the way this fixes. `createSellerProductAction` and
|
|
30
|
+
* `sellerUpdateProductAction` are the single choke point every seller write
|
|
31
|
+
* already passes through, so the mapping happens once and cannot be bypassed
|
|
32
|
+
* by a page that did not know about it (the lesson of Root Cause #75).
|
|
33
|
+
*
|
|
34
|
+
* ## Round-trip
|
|
35
|
+
*
|
|
36
|
+
* `draftToProductInput` and `productToDraft` are a matched pair in one file so
|
|
37
|
+
* they cannot drift: a field added to one and not the other is visible in a
|
|
38
|
+
* single screen of code. The edit pages need the inverse to seed the form from
|
|
39
|
+
* a stored document.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* The parts of a stored product this mapper reads.
|
|
43
|
+
*
|
|
44
|
+
* Declared structurally rather than importing `ProductDocument`: the inbound
|
|
45
|
+
* value comes from a server action's `JsonValue` payload where dates are
|
|
46
|
+
* strings, and every block is optional because documents written before the
|
|
47
|
+
* nested shapes existed carry none of them.
|
|
48
|
+
*/
|
|
49
|
+
export interface StoredProductBlocks {
|
|
50
|
+
finalSale?: boolean;
|
|
51
|
+
returnPolicy?: string;
|
|
52
|
+
classified?: {
|
|
53
|
+
meetupArea?: {
|
|
54
|
+
city?: string;
|
|
55
|
+
locality?: string;
|
|
56
|
+
pincode?: string;
|
|
57
|
+
};
|
|
58
|
+
acceptsShipping?: boolean;
|
|
59
|
+
negotiable?: boolean;
|
|
60
|
+
};
|
|
61
|
+
digitalCode?: {
|
|
62
|
+
codeDeliveryMethod?: string;
|
|
63
|
+
codePoolSize?: number;
|
|
64
|
+
redemptionInstructions?: string;
|
|
65
|
+
expiresAt?: string;
|
|
66
|
+
};
|
|
67
|
+
liveItem?: {
|
|
68
|
+
species?: string;
|
|
69
|
+
ageMonths?: number;
|
|
70
|
+
sex?: string;
|
|
71
|
+
careInfo?: string;
|
|
72
|
+
transport?: {
|
|
73
|
+
method?: string;
|
|
74
|
+
handlingFee?: number;
|
|
75
|
+
};
|
|
76
|
+
jurisdictionAllowed?: string[];
|
|
77
|
+
cites?: string;
|
|
78
|
+
};
|
|
79
|
+
printMeta?: {
|
|
80
|
+
size?: string;
|
|
81
|
+
material?: string;
|
|
82
|
+
finish?: string;
|
|
83
|
+
editionSize?: number;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The nested blocks this mapper WRITES, plus whatever else the draft carried.
|
|
88
|
+
* `ProductWritePayload` is deliberately not `ProductDocument`: the draft holds
|
|
89
|
+
* form-shaped values (ISO date strings, a `MediaField` video) that the write
|
|
90
|
+
* schema coerces, so typing it as the document would be a lie the compiler
|
|
91
|
+
* would then make everyone cast around.
|
|
92
|
+
*/
|
|
93
|
+
export interface ProductWritePayload {
|
|
94
|
+
classified?: NonNullable<StoredProductBlocks["classified"]>;
|
|
95
|
+
digitalCode?: NonNullable<StoredProductBlocks["digitalCode"]>;
|
|
96
|
+
liveItem?: NonNullable<StoredProductBlocks["liveItem"]>;
|
|
97
|
+
printMeta?: NonNullable<StoredProductBlocks["printMeta"]>;
|
|
98
|
+
[key: string]: unknown;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Fold the draft's flat per-type keys into the nested blocks the write schema
|
|
102
|
+
* expects. Every other key passes through untouched.
|
|
103
|
+
*
|
|
104
|
+
* A block is emitted only when the draft actually carries something for it, so
|
|
105
|
+
* saving a standard listing does not write an empty `classified: {}`.
|
|
106
|
+
*/
|
|
107
|
+
export declare function draftToProductInput(draft: SellerProductDraft & {
|
|
108
|
+
[key: string]: unknown;
|
|
109
|
+
}): ProductWritePayload;
|
|
110
|
+
/**
|
|
111
|
+
* The inverse — seed the form from a stored document.
|
|
112
|
+
*
|
|
113
|
+
* Tolerant of documents written before the nested blocks existed: a missing
|
|
114
|
+
* block yields undefined flat keys, which render as empty inputs rather than
|
|
115
|
+
* throwing.
|
|
116
|
+
*/
|
|
117
|
+
export declare function productToDraft(product: StoredProductBlocks): Partial<SellerProductDraft>;
|