@zyacreatives/shared 2.5.4 → 2.5.6

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.
@@ -4,35 +4,9 @@ import {
4
4
  PRICING_MODELS,
5
5
  PRODUCT_STATUS,
6
6
  WAGES_CURRENCY,
7
+ ACTIVITY_PARENT_TYPES, // Assumes this includes 'PRODUCT_COVER' and 'PRODUCT_DELIVERY'
7
8
  } from "../constants";
8
-
9
- export const CoverImageInputSchema = z.object({
10
- key: z.string().openapi({ description: "Storage key of the uploaded file" }),
11
- mimeType: z.string().openapi({ example: "image/jpeg" }),
12
- isThumbnail: z.boolean().default(false),
13
- order: z.number().int().default(0),
14
- });
15
-
16
- export const DeliveryFileInputSchema = z.object({
17
- key: z.string().openapi({ description: "Storage key of the uploaded file" }),
18
- mimeType: z.string().openapi({ example: "application/zip" }),
19
- order: z.number().int().default(0),
20
- });
21
-
22
- export const ProductCoverImageEntitySchema = z.object({
23
- fileId: z
24
- .cuid2()
25
- .openapi({ example: "f123e4567-e89b-12d3-a456-426614174000" }),
26
- isThumbnail: z.boolean().default(false),
27
- order: z.number().int().default(0),
28
- });
29
-
30
- export const ProductDeliveryFileEntitySchema = z.object({
31
- fileId: z
32
- .cuid2()
33
- .openapi({ example: "f987e6543-e89b-12d3-a456-426614174000" }),
34
- order: z.number().int().default(0),
35
- });
9
+ import { CreateFileInputSchema, FileEntitySchema } from "./file";
36
10
 
37
11
  export const ProductDiscountEntitySchema = z.object({
38
12
  discountType: z.enum(DISCOUNT_TYPES),
@@ -48,41 +22,8 @@ export const ProductLinkSchema = z.object({
48
22
  title: z.string().nullable(),
49
23
  url: z.string(),
50
24
  });
51
- export type ProductLink = z.infer<typeof ProductLinkSchema>;
52
-
53
- export const BaseProductSchema = z.object({
54
- id: z.cuid2(),
55
- createdAt: z.date(),
56
- updatedAt: z.date(),
57
- deletedAt: z.date().nullable(),
58
- status: z.enum(PRODUCT_STATUS).default("DRAFT"),
59
- sellerId: z.cuid2(),
60
- title: z.string(),
61
- description: z.string(),
62
- keyFeatures: z.string(),
63
25
 
64
- category: z.string(),
65
- subcategory: z.string().nullable(),
66
- tags: z.array(z.string()).nullable(),
67
-
68
- productLinks: z.array(ProductLinkSchema).nullable(),
69
- pricingModel: z.enum(PRICING_MODELS),
70
- currency: z.enum(WAGES_CURRENCY),
71
- price: z.number().nullable(),
72
- suggestedPrice: z.number().nullable(),
73
- discounts: z
74
- .array(
75
- z.object({
76
- discountType: z.enum(DISCOUNT_TYPES),
77
- amount: z.number(),
78
- discountCode: z.string().optional().nullable(),
79
- expiry: z.coerce.date().optional().nullable(),
80
- }),
81
- )
82
- .nullable(),
83
- supportEmail: z.string().nullable(),
84
- supportPhone: z.string().nullable(),
85
- });
26
+ // --- 3. Input Validation Schemas ---
86
27
 
87
28
  const ProductCoreInputSchema = z.object({
88
29
  id: z.cuid2().openapi({ description: "Client-generated ID for the product" }),
@@ -94,26 +35,19 @@ const ProductCoreInputSchema = z.object({
94
35
  category: z.string().min(1, "Category is required"),
95
36
  subcategory: z.string().optional(),
96
37
  tags: z.array(z.string()).max(10, "Keep tags to a maximum of 10").default([]),
97
-
98
- coverImages: z
99
- .array(CoverImageInputSchema)
100
- .min(1, "At least one cover image is required")
101
- .max(5, "Maximum of 5 cover images allowed"),
102
- currency: z.enum(WAGES_CURRENCY),
103
- productFiles: z.array(DeliveryFileInputSchema).default([]),
104
-
38
+ files: z.array(CreateFileInputSchema).default([]),
105
39
  productLinks: z.array(ProductLinkSchema).default([]),
106
40
 
41
+ currency: z.enum(WAGES_CURRENCY),
107
42
  pricingModel: z.enum(PRICING_MODELS).default(PRICING_MODELS.FIXED),
108
-
109
43
  price: z.number().int("Must be a whole number").min(0).optional(),
110
44
  suggestedPrice: z.number().int("Must be in cents").min(0).optional(),
111
-
112
45
  discounts: z.array(ProductDiscountEntitySchema).max(3).default([]),
113
46
  });
114
47
 
115
48
  export const CreateProductInputSchema = ProductCoreInputSchema.superRefine(
116
49
  (data, ctx) => {
50
+ // Pricing Validation
117
51
  if (
118
52
  data.pricingModel === PRICING_MODELS.FIXED &&
119
53
  (!data.price || data.price <= 0)
@@ -128,15 +62,14 @@ export const CreateProductInputSchema = ProductCoreInputSchema.superRefine(
128
62
  if (
129
63
  data.pricingModel === PRICING_MODELS.PWYW &&
130
64
  data.suggestedPrice !== undefined &&
131
- data.price !== undefined
65
+ data.price !== undefined &&
66
+ data.suggestedPrice < data.price
132
67
  ) {
133
- if (data.suggestedPrice < data.price) {
134
- ctx.addIssue({
135
- code: "custom",
136
- message: "Suggested price cannot be lower than the minimum price.",
137
- path: ["suggestedPrice"],
138
- });
139
- }
68
+ ctx.addIssue({
69
+ code: "custom",
70
+ message: "Suggested price cannot be lower than the minimum price.",
71
+ path: ["suggestedPrice"],
72
+ });
140
73
  }
141
74
 
142
75
  if (
@@ -151,22 +84,38 @@ export const CreateProductInputSchema = ProductCoreInputSchema.superRefine(
151
84
  });
152
85
  }
153
86
 
154
- if (data.productFiles.length === 0 && data.productLinks.length === 0) {
87
+ // File Validation based on parentType
88
+ const deliveryFiles = data.files.filter(
89
+ (f) => f.parentType === "PRODUCT_DELIVERY",
90
+ );
91
+ const coverImages = data.files.filter(
92
+ (f) => f.parentType === "PRODUCT_COVER",
93
+ );
94
+
95
+ if (deliveryFiles.length === 0 && data.productLinks.length === 0) {
155
96
  ctx.addIssue({
156
97
  code: "custom",
157
98
  message:
158
99
  "You must provide at least one product file or a link for the buyer to receive.",
159
- path: ["productFiles"],
100
+ path: ["files"],
160
101
  });
161
102
  }
162
103
 
163
- const thumbnails = data.coverImages.filter((img) => img.isThumbnail);
164
- if (thumbnails.length !== 1) {
104
+ if (coverImages.length < 1 || coverImages.length > 5) {
165
105
  ctx.addIssue({
166
106
  code: "custom",
167
- message: "Exactly one cover image must be set as the thumbnail.",
168
- path: ["coverImages"],
107
+ message: "Between 1 and 5 cover images are required.",
108
+ path: ["files"],
169
109
  });
110
+ } else {
111
+ const thumbnails = coverImages.filter((img) => img.isThumbnail);
112
+ if (thumbnails.length !== 1) {
113
+ ctx.addIssue({
114
+ code: "custom",
115
+ message: "Exactly one cover image must be set as the thumbnail.",
116
+ path: ["files"],
117
+ });
118
+ }
170
119
  }
171
120
 
172
121
  data.discounts.forEach((discount, index) => {
@@ -182,15 +131,17 @@ export const CreateProductInputSchema = ProductCoreInputSchema.superRefine(
182
131
  });
183
132
  }
184
133
 
185
- if (isFixed && data.price !== undefined) {
186
- if (discount.amount >= data.price) {
187
- ctx.addIssue({
188
- code: "custom",
189
- message:
190
- "Fixed discount amounts must be less than the product price.",
191
- path: ["discounts", index, "amount"],
192
- });
193
- }
134
+ if (
135
+ isFixed &&
136
+ data.price !== undefined &&
137
+ discount.amount >= data.price
138
+ ) {
139
+ ctx.addIssue({
140
+ code: "custom",
141
+ message:
142
+ "Fixed discount amounts must be less than the product price.",
143
+ path: ["discounts", index, "amount"],
144
+ });
194
145
  }
195
146
  });
196
147
  },
@@ -198,17 +149,13 @@ export const CreateProductInputSchema = ProductCoreInputSchema.superRefine(
198
149
 
199
150
  export const ProductServiceAndComplianceInputSchema = z.object({
200
151
  id: z.cuid2().openapi({ description: "ID of the product" }),
201
-
202
152
  supportEmail: z.email("A valid support email is required"),
203
153
  supportPhone: z.string().optional(),
204
-
205
154
  ownsRights: z.literal(
206
155
  true,
207
- "You must confirm you own the rights to this product.",
156
+ '"You must confirm you own the rights to this product."',
208
157
  ),
209
-
210
158
  noHarmfulContent: z.literal(true, "You must confirm no harmful content."),
211
-
212
159
  providesSupport: z.boolean(),
213
160
  agreesToTerms: z.literal(true, "You must agree to the Terms."),
214
161
  });
@@ -221,10 +168,11 @@ export const UpdateProductInputSchema = ProductCoreInputSchema.extend(
221
168
  id: z.cuid2().openapi({ description: "ID of the product being updated" }),
222
169
  });
223
170
 
171
+ // --- 4. Entity & Output Schemas ---
172
+
224
173
  export const ProductEntitySchema = z
225
174
  .object({
226
175
  id: z.cuid2(),
227
-
228
176
  sellerId: z.cuid2().openapi({ description: "ID of the creator/seller" }),
229
177
  sellerUsername: z.string(),
230
178
  sellerName: z.string(),
@@ -238,22 +186,23 @@ export const ProductEntitySchema = z
238
186
  subcategory: z.string().optional().nullable(),
239
187
  tags: z.array(z.string()),
240
188
 
241
- coverImages: z
242
- .array(ProductCoverImageEntitySchema.extend({ url: z.url() }))
243
- .default([]),
244
- productFiles: z
245
- .array(ProductDeliveryFileEntitySchema.extend({ url: z.url() }))
246
- .default([]),
189
+ // Unified files array for output
190
+ files: z.array(FileEntitySchema).default([]),
247
191
  productLinks: z.array(ProductLinkSchema).default([]),
192
+
248
193
  pricingModel: z.enum(PRICING_MODELS),
249
194
  currency: z.enum(WAGES_CURRENCY),
250
195
  price: z.number().int().optional().nullable(),
251
196
  suggestedPrice: z.number().int().optional().nullable(),
252
-
253
197
  discounts: z.array(ProductDiscountEntitySchema).default([]),
254
198
 
199
+ ownsRights: z.boolean(),
200
+ noHarmfulContent: z.boolean(),
201
+ providesSupport: z.boolean(),
202
+ agreesToTerms: z.boolean(),
255
203
  supportEmail: z.email().optional().nullable(),
256
204
  supportPhone: z.string().optional().nullable(),
205
+
257
206
  createdAt: z.coerce.date(),
258
207
  updatedAt: z.coerce.date(),
259
208
  deletedAt: z.coerce.date().optional().nullable(),
@@ -311,11 +260,9 @@ export const GetMarketplaceInfoOutputSchema = z.object({
311
260
  communityFavourites: z.array(ProductSearchDocumentSchema),
312
261
  });
313
262
 
263
+ export type ProductLink = z.infer<typeof ProductLinkSchema>;
314
264
  export type SearchProductInput = z.infer<typeof SearchProductInputSchema>;
315
265
  export type SearchProductOutput = z.infer<typeof SearchProductOutputSchema>;
316
- export type CoverImageInput = z.infer<typeof CoverImageInputSchema>;
317
- export type DeliveryFileInput = z.infer<typeof DeliveryFileInputSchema>;
318
-
319
266
  export type MarketplaceCategoryOutput = z.infer<
320
267
  typeof MarketplaceCategorySchema
321
268
  >;
@@ -323,21 +270,11 @@ export type GetMarketplaceInfoOutput = z.infer<
323
270
  typeof GetMarketplaceInfoOutputSchema
324
271
  >;
325
272
 
326
- export type ProductCoverImageEntity = z.infer<
327
- typeof ProductCoverImageEntitySchema
328
- >;
329
-
330
- export type ProductDeliveryFileEntity = z.infer<
331
- typeof ProductDeliveryFileEntitySchema
332
- >;
333
273
  export type ProductDiscountEntity = z.infer<typeof ProductDiscountEntitySchema>;
334
-
335
274
  export type CreateProductInputEntity = z.infer<typeof CreateProductInputSchema>;
336
275
  export type ProductServiceAndComplianceInputEntity = z.infer<
337
276
  typeof ProductServiceAndComplianceInputSchema
338
277
  >;
339
278
  export type UpdateProductInputEntity = z.infer<typeof UpdateProductInputSchema>;
340
279
  export type ProductEntity = z.infer<typeof ProductEntitySchema>;
341
-
342
- export type BaseProductEntity = z.infer<typeof BaseProductSchema>;
343
280
  export type ProductSearchDocument = z.infer<typeof ProductSearchDocumentSchema>;