@jsdev_ninja/core 0.13.26 → 0.13.28

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 (55) hide show
  1. package/dist/core.cjs.js +1 -1
  2. package/dist/core.cjs.js.map +1 -1
  3. package/dist/core.es.js +149 -145
  4. package/dist/core.es.js.map +1 -1
  5. package/dist/core.umd.js +1 -1
  6. package/dist/core.umd.js.map +1 -1
  7. package/dist/lib/entities/Order.d.ts +3 -0
  8. package/dist/lib/entities/Order.d.ts.map +1 -1
  9. package/dist/lib/entities/Order.js +1 -0
  10. package/dist/lib/entities/Store.d.ts +46 -0
  11. package/dist/lib/entities/Store.d.ts.map +1 -1
  12. package/dist/lib/entities/Store.js +3 -0
  13. package/dist/tsconfig.app.tsbuildinfo +1 -1
  14. package/package.json +9 -2
  15. package/eslint.config.js +0 -28
  16. package/index.html +0 -13
  17. package/lib/entities/Address.ts +0 -13
  18. package/lib/entities/Atoms.ts +0 -11
  19. package/lib/entities/Cart.ts +0 -24
  20. package/lib/entities/Category.ts +0 -35
  21. package/lib/entities/Company.ts +0 -9
  22. package/lib/entities/DeliveryNote.ts +0 -70
  23. package/lib/entities/Discount/__tests__/engine.test.ts +0 -337
  24. package/lib/entities/Discount/__tests__/factory.test.ts +0 -91
  25. package/lib/entities/Discount/__tests__/integration.test.ts +0 -132
  26. package/lib/entities/Discount/__tests__/simple.test.ts +0 -215
  27. package/lib/entities/Discount/__tests__/utils.test.ts +0 -69
  28. package/lib/entities/Discount/engine.ts +0 -138
  29. package/lib/entities/Discount/factory.ts +0 -25
  30. package/lib/entities/Discount/index.ts +0 -10
  31. package/lib/entities/Discount/strategies/BundleStrategy.ts +0 -119
  32. package/lib/entities/Discount/strategies/__tests__/BundleStrategy.test.ts +0 -295
  33. package/lib/entities/Discount/strategy.ts +0 -6
  34. package/lib/entities/Discount/types.ts +0 -68
  35. package/lib/entities/Discount/utils.ts +0 -42
  36. package/lib/entities/FavoriteProduct.ts +0 -12
  37. package/lib/entities/Invoice.ts +0 -57
  38. package/lib/entities/Locale.ts +0 -8
  39. package/lib/entities/Order.ts +0 -66
  40. package/lib/entities/Organization.ts +0 -23
  41. package/lib/entities/Product.ts +0 -68
  42. package/lib/entities/Profile.ts +0 -56
  43. package/lib/entities/Store.ts +0 -23
  44. package/lib/entities/index.ts +0 -14
  45. package/lib/firebase-api/app.ts +0 -18
  46. package/lib/firebase-api/index.ts +0 -46
  47. package/lib/index.tsx +0 -3
  48. package/lib/utils/index.ts +0 -127
  49. package/src/main.tsx +0 -1
  50. package/src/vite-env.d.ts +0 -1
  51. package/tsconfig.app.json +0 -20
  52. package/tsconfig.json +0 -7
  53. package/tsconfig.node.json +0 -26
  54. package/vite.config.ts +0 -27
  55. package/vitest.config.ts +0 -10
@@ -1,295 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
2
- import { BundleDiscountStrategy } from "../BundleStrategy";
3
- import { TDiscount, DiscountContext } from "../../types";
4
-
5
- describe("BundleDiscountStrategy", () => {
6
- let strategy: BundleDiscountStrategy;
7
- let mockContext: DiscountContext;
8
-
9
- beforeEach(() => {
10
- strategy = new BundleDiscountStrategy();
11
-
12
- mockContext = {
13
- cart: [
14
- {
15
- amount: 2,
16
- product: {
17
- id: "product1",
18
- price: 10,
19
- },
20
- },
21
- {
22
- amount: 1,
23
- product: {
24
- id: "product2",
25
- price: 15,
26
- },
27
- },
28
- ],
29
- user: undefined,
30
- appliedDiscounts: [],
31
- };
32
- });
33
-
34
- describe("canApply", () => {
35
- it("should return true for valid bundle discount", () => {
36
- const discount: TDiscount = {
37
- type: "Discount",
38
- storeId: "store1",
39
- companyId: "company1",
40
- id: "bundle1",
41
- name: [{ lang: "he", value: "Buy 3 for $25" }],
42
- active: true,
43
- startDate: Date.now() - 1000,
44
- endDate: Date.now() + 1000,
45
- variant: {
46
- variantType: "bundle",
47
- productsId: ["product1", "product2"],
48
- requiredQuantity: 3,
49
- bundlePrice: 25,
50
- },
51
- conditions: {
52
- stackable: false,
53
- },
54
- };
55
-
56
- expect(strategy.canApply(discount, mockContext)).toBe(true);
57
- });
58
-
59
- it("should return true for bundle variant type", () => {
60
- const discount = {
61
- ...mockDiscount,
62
- variant: {
63
- variantType: "bundle" as const,
64
- productsId: ["product1", "product2"],
65
- requiredQuantity: 3,
66
- bundlePrice: 25,
67
- },
68
- };
69
-
70
- // This should work since it's a bundle type and we have 3 items total
71
- expect(strategy.canApply(discount, mockContext)).toBe(true);
72
- });
73
-
74
- it("should return false when not enough items", () => {
75
- const discount: TDiscount = {
76
- type: "Discount",
77
- storeId: "store1",
78
- companyId: "company1",
79
- id: "bundle1",
80
- name: [{ lang: "he", value: "Buy 5 for $40" }],
81
- active: true,
82
- startDate: Date.now() - 1000,
83
- endDate: Date.now() + 1000,
84
- variant: {
85
- variantType: "bundle",
86
- productsId: ["product1", "product2"],
87
- requiredQuantity: 5, // Need 5, but cart only has 3
88
- bundlePrice: 40,
89
- },
90
- conditions: {
91
- stackable: false,
92
- },
93
- };
94
-
95
- expect(strategy.canApply(discount, mockContext)).toBe(false);
96
- });
97
-
98
- it("should return false when discount is inactive", () => {
99
- const discount: TDiscount = {
100
- type: "Discount",
101
- storeId: "store1",
102
- companyId: "company1",
103
- id: "bundle1",
104
- name: [{ lang: "he", value: "Buy 3 for $25" }],
105
- active: false, // Inactive
106
- startDate: Date.now() - 1000,
107
- endDate: Date.now() + 1000,
108
- variant: {
109
- variantType: "bundle",
110
- productsId: ["product1", "product2"],
111
- requiredQuantity: 3,
112
- bundlePrice: 25,
113
- },
114
- conditions: {
115
- stackable: false,
116
- },
117
- };
118
-
119
- expect(strategy.canApply(discount, mockContext)).toBe(false);
120
- });
121
-
122
- it("should return false when discount is expired", () => {
123
- const discount: TDiscount = {
124
- type: "Discount",
125
- storeId: "store1",
126
- companyId: "company1",
127
- id: "bundle1",
128
- name: [{ lang: "he", value: "Buy 3 for $25" }],
129
- active: true,
130
- startDate: Date.now() - 2000,
131
- endDate: Date.now() - 1000, // Expired
132
- variant: {
133
- variantType: "bundle",
134
- productsId: ["product1", "product2"],
135
- requiredQuantity: 3,
136
- bundlePrice: 25,
137
- },
138
- conditions: {
139
- stackable: false,
140
- },
141
- };
142
-
143
- expect(strategy.canApply(discount, mockContext)).toBe(false);
144
- });
145
- });
146
-
147
- describe("calculate", () => {
148
- it("should calculate bundle discount correctly", () => {
149
- const discount: TDiscount = {
150
- type: "Discount",
151
- storeId: "store1",
152
- companyId: "company1",
153
- id: "bundle1",
154
- name: [{ lang: "he", value: "Buy 3 for $25" }],
155
- active: true,
156
- startDate: Date.now() - 1000,
157
- endDate: Date.now() + 1000,
158
- variant: {
159
- variantType: "bundle",
160
- productsId: ["product1", "product2"],
161
- requiredQuantity: 3,
162
- bundlePrice: 25,
163
- },
164
- conditions: {
165
- stackable: false,
166
- },
167
- };
168
-
169
- const result = strategy.calculate(discount, mockContext);
170
-
171
- expect(result.applicable).toBe(true);
172
- expect(result.discountAmount).toBe(10); // (2×10 + 1×15) - 25 = 35 - 25 = 10
173
- expect(result.affectedItems).toHaveLength(2);
174
-
175
- // Check product1 discount
176
- const product1Item = result.affectedItems.find(item => item.productId === "product1");
177
- expect(product1Item).toBeDefined();
178
- expect(product1Item!.discountAmount).toBeCloseTo(5.71, 2); // Proportional to 20/35
179
-
180
- // Check product2 discount
181
- const product2Item = result.affectedItems.find(item => item.productId === "product2");
182
- expect(product2Item).toBeDefined();
183
- expect(product2Item!.discountAmount).toBeCloseTo(4.29, 2); // Proportional to 15/35
184
- });
185
-
186
- it("should handle multiple bundles", () => {
187
- const contextWithMoreItems: DiscountContext = {
188
- cart: [
189
- {
190
- amount: 6, // 6 items
191
- product: {
192
- id: "product1",
193
- price: 10,
194
- },
195
- },
196
- ],
197
- user: undefined,
198
- appliedDiscounts: [],
199
- };
200
-
201
- const discount: TDiscount = {
202
- type: "Discount",
203
- storeId: "store1",
204
- companyId: "company1",
205
- id: "bundle1",
206
- name: [{ lang: "he", value: "Buy 3 for $25" }],
207
- active: true,
208
- startDate: Date.now() - 1000,
209
- endDate: Date.now() + 1000,
210
- variant: {
211
- variantType: "bundle",
212
- productsId: ["product1"],
213
- requiredQuantity: 3,
214
- bundlePrice: 25,
215
- },
216
- conditions: {
217
- stackable: false,
218
- },
219
- };
220
-
221
- const result = strategy.calculate(discount, contextWithMoreItems);
222
-
223
- expect(result.applicable).toBe(true);
224
- expect(result.discountAmount).toBe(10); // (6×10) - (2×25) = 60 - 50 = 10
225
- expect(result.affectedItems).toHaveLength(1);
226
- expect(result.affectedItems[0].discountAmount).toBe(10);
227
- });
228
-
229
- it("should return not applicable for invalid bundle", () => {
230
- const discount = {
231
- ...mockDiscount,
232
- variant: {
233
- variantType: "bundle" as const,
234
- productsId: ["product1"],
235
- requiredQuantity: 10, // Too many required
236
- bundlePrice: 25,
237
- },
238
- };
239
-
240
- const result = strategy.calculate(discount, mockContext);
241
-
242
- expect(result.applicable).toBe(false);
243
- expect(result.discountAmount).toBe(0);
244
- expect(result.affectedItems).toHaveLength(0);
245
- });
246
-
247
- it("should return not applicable when not enough items", () => {
248
- const discount: TDiscount = {
249
- type: "Discount",
250
- storeId: "store1",
251
- companyId: "company1",
252
- id: "bundle1",
253
- name: [{ lang: "he", value: "Buy 5 for $40" }],
254
- active: true,
255
- startDate: Date.now() - 1000,
256
- endDate: Date.now() + 1000,
257
- variant: {
258
- variantType: "bundle",
259
- productsId: ["product1", "product2"],
260
- requiredQuantity: 5,
261
- bundlePrice: 40,
262
- },
263
- conditions: {
264
- stackable: false,
265
- },
266
- };
267
-
268
- const result = strategy.calculate(discount, mockContext);
269
-
270
- expect(result.applicable).toBe(false);
271
- expect(result.discountAmount).toBe(0);
272
- expect(result.affectedItems).toHaveLength(0);
273
- });
274
- });
275
- });
276
-
277
- const mockDiscount: TDiscount = {
278
- type: "Discount",
279
- storeId: "store1",
280
- companyId: "company1",
281
- id: "bundle1",
282
- name: [{ lang: "he", value: "Buy 3 for $25" }],
283
- active: true,
284
- startDate: Date.now() - 1000,
285
- endDate: Date.now() + 1000,
286
- variant: {
287
- variantType: "bundle",
288
- productsId: ["product1", "product2"],
289
- requiredQuantity: 3,
290
- bundlePrice: 25,
291
- },
292
- conditions: {
293
- stackable: false,
294
- },
295
- };
@@ -1,6 +0,0 @@
1
- import { TDiscount, DiscountResult, DiscountContext } from "./types";
2
-
3
- export interface DiscountStrategy {
4
- canApply(discount: TDiscount, context: DiscountContext): boolean;
5
- calculate(discount: TDiscount, context: DiscountContext): DiscountResult;
6
- }
@@ -1,68 +0,0 @@
1
- import { z } from "zod";
2
-
3
- // Simple discount conditions
4
- export const DiscountConditionsSchema = z
5
- .object({
6
- minSpend: z.number().positive().optional(),
7
- stackable: z.boolean().default(false),
8
- })
9
- .optional();
10
-
11
- // Bundle discount - buy X items for Y price
12
- export const DiscountVariantSchema = z.discriminatedUnion("variantType", [
13
- z.object({
14
- variantType: z.literal("bundle"),
15
- productsId: z.array(z.string().nonempty()).min(1), // Which products are included
16
- requiredQuantity: z.number().positive(), // How many items needed (e.g., 3)
17
- bundlePrice: z.number().positive(), // Total price for the bundle (e.g., $25)
18
- }),
19
- ]);
20
-
21
- export const DiscountSchema = z.object({
22
- type: z.literal("Discount"),
23
- storeId: z.string().min(1),
24
- companyId: z.string().min(1),
25
- id: z.string().min(1),
26
- name: z.array(z.object({ lang: z.enum(["he"]), value: z.string().nonempty() })),
27
- active: z.boolean(),
28
- startDate: z.number(),
29
- endDate: z.number(),
30
- variant: DiscountVariantSchema,
31
- conditions: DiscountConditionsSchema,
32
- });
33
-
34
- export type TDiscount = z.infer<typeof DiscountSchema>;
35
- export type DiscountVariant = z.infer<typeof DiscountVariantSchema>;
36
- export type DiscountConditions = z.infer<typeof DiscountConditionsSchema>;
37
-
38
- // Simple result types
39
- export interface DiscountResult {
40
- applicable: boolean;
41
- discountAmount: number;
42
- affectedItems: Array<{
43
- productId: string;
44
- quantity: number;
45
- originalPrice: number;
46
- discountedPrice: number;
47
- discountAmount: number;
48
- }>;
49
- }
50
-
51
- export interface AppliedDiscount {
52
- discountId: string;
53
- discountName: string;
54
- discountAmount: number;
55
- affectedItems: DiscountResult["affectedItems"];
56
- }
57
-
58
- export interface DiscountContext {
59
- cart: Array<{
60
- amount: number;
61
- product: {
62
- id: string;
63
- price: number;
64
- };
65
- }>;
66
- user?: Record<string, unknown>;
67
- appliedDiscounts: AppliedDiscount[];
68
- }
@@ -1,42 +0,0 @@
1
- /**
2
- * Utility functions for discount calculations
3
- */
4
-
5
- /**
6
- * Formats a number to 2 decimal places and returns as a number
7
- * @param value - The number to format
8
- * @returns The formatted number with 2 decimal places
9
- */
10
- export function formatCurrency(value: number): number {
11
- return Number(value.toFixed(2));
12
- }
13
-
14
- /**
15
- * Formats a number to 2 decimal places and returns as a string
16
- * @param value - The number to format
17
- * @returns The formatted string with 2 decimal places
18
- */
19
- export function formatCurrencyString(value: number): string {
20
- return value.toFixed(2);
21
- }
22
-
23
- /**
24
- * Ensures a monetary value is never negative
25
- * @param value - The value to check
26
- * @returns The value, or 0 if negative
27
- */
28
- export function ensureNonNegative(value: number): number {
29
- return Math.max(0, formatCurrency(value));
30
- }
31
-
32
- /**
33
- * Calculates the percentage discount
34
- * @param originalPrice - The original price
35
- * @param discountedPrice - The discounted price
36
- * @returns The percentage discount (0-100)
37
- */
38
- export function calculatePercentageDiscount(originalPrice: number, discountedPrice: number): number {
39
- if (originalPrice <= 0) return 0;
40
- const discount = originalPrice - discountedPrice;
41
- return formatCurrency((discount / originalPrice) * 100);
42
- }
@@ -1,12 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const FavoriteProductSchema = z.object({
4
- type: z.literal("FavoriteProduct"),
5
- id: z.string().uuid(),
6
- companyId: z.string().uuid(),
7
- storeId: z.string().uuid(),
8
- userId: z.string().uuid(),
9
- productId: z.string().uuid(),
10
- });
11
-
12
- export type TFavoriteProduct = z.infer<typeof FavoriteProductSchema>;
@@ -1,57 +0,0 @@
1
- import { z } from "zod";
2
- import { CalculatedDataSchema } from "./DeliveryNote";
3
-
4
- // Main Invoice schema
5
- export const EzInvoiceSchema = z.object({
6
- doc_uuid: z.string().uuid("Document UUID must be a valid UUID"),
7
- pdf_link: z.string().url("PDF link must be a valid URL"),
8
- pdf_link_copy: z.string().url("PDF copy link must be a valid URL"),
9
- doc_number: z.string().min(1, "Document number is required"),
10
- sent_mails: z.array(z.string().email("Each email must be valid")),
11
- success: z.boolean(),
12
- ua_uuid: z.string().uuid("UA UUID must be a valid UUID"),
13
- calculatedData: CalculatedDataSchema,
14
- warning: z.string().optional(),
15
- date: z.number().optional(),
16
- });
17
-
18
- export const InvoiceSchema = z.object({
19
- id: z.string().min(1, "ID is required"),
20
- number: z.string().min(1, "Number is required"),
21
- date: z.string().min(1, "Date is required"),
22
- createdAt: z.number().min(1, "Created at is required"),
23
- status: z.enum(["pending", "paid", "cancelled"]),
24
- companyDetails: z
25
- .object({
26
- name: z.string().min(1, "Name is required").optional(),
27
- address: z.string().min(1, "Address is required").optional(),
28
- phone: z.string().min(1, "Phone is required").optional(),
29
- email: z.string().email("Email must be valid").optional(),
30
- })
31
- .optional(),
32
- clientDetails: z
33
- .object({
34
- name: z.string().min(1, "Name is required").optional(),
35
- address: z.string().min(1, "Address is required").optional(),
36
- phone: z.string().min(1, "Phone is required").optional(),
37
- email: z.string().email("Email must be valid").optional(),
38
- })
39
- .optional(),
40
- items: z
41
- .array(
42
- z.object({
43
- name: z.string().min(1, "Name is required").optional(),
44
- price: z.number().min(1, "Price is required").optional(),
45
- quantity: z.number().min(1, "Quantity is required").optional(),
46
- total: z.number().min(1, "Total is required").optional(),
47
- })
48
- )
49
- .optional(),
50
- total: z.number().min(1, "Total is required").optional(),
51
- vat: z.number().min(1, "VAT is required").optional(),
52
- link: z.string().url("Link must be a valid URL").optional(),
53
- });
54
-
55
- // Type inference
56
- export type TInvoice = z.infer<typeof InvoiceSchema>;
57
- export type TEzInvoice = z.infer<typeof EzInvoiceSchema>;
@@ -1,8 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const LocaleSchema = z.object({
4
- lang: z.enum(["he"]),
5
- value: z.string(),
6
- });
7
-
8
- export const LocaleValueSchema = z.array(LocaleSchema);
@@ -1,66 +0,0 @@
1
- import { z } from "zod";
2
- import { ProfileSchema } from "./Profile";
3
- import { notEmptyTextSchema } from "./Atoms";
4
- import { CartItemProductSchema } from "./Cart";
5
- import { DeliveryNoteSchema, EzDeliveryNoteSchema } from "./DeliveryNote";
6
- import { BillingAccountSchema } from "./Organization";
7
- import { EzInvoiceSchema, InvoiceSchema } from "./Invoice";
8
-
9
- // pending - order created / by user
10
- // processing order accepted by store by admin
11
- // delivered - order delivered by admin
12
- // canceled - order canceled by user/admin
13
- // completed - order paid by admin
14
-
15
- // type PaymentMethod = "credit_card" | "paypal" | "bank_transfer" | "cash_on_delivery";
16
-
17
- export const OrderSchema = z.object({
18
- type: z.literal("Order"),
19
- id: notEmptyTextSchema,
20
- companyId: notEmptyTextSchema,
21
- storeId: notEmptyTextSchema,
22
- userId: notEmptyTextSchema,
23
- status: z.enum([
24
- "draft", // before payment
25
- "pending", // after payment
26
- "processing", // after admin approve
27
- "in_delivery", //
28
- "delivered",
29
- "cancelled",
30
- "completed",
31
- "refunded",
32
- ]),
33
- paymentType: z.enum(["internal", "external"]).optional(),
34
- paymentStatus: z.enum(["pending", "pending_j5", "external", "completed", "failed", "refunded"]), //todo check if hyp support partial refund
35
- cart: z.object({
36
- id: z.string(),
37
- items: z.array(CartItemProductSchema),
38
- cartDiscount: z.number(),
39
- cartTotal: z.number(),
40
- cartVat: z.number(),
41
- deliveryPrice: z.number().optional(), // final delivery price for cart
42
- }),
43
- storeOptions: z
44
- .object({
45
- deliveryPrice: z.number().optional(),
46
- freeDeliveryPrice: z.number().optional(),
47
- isVatIncludedInPrice: z.boolean().optional(),
48
- })
49
- .optional(),
50
- orderDeliveryPrice: z.number().optional(), // delivery price for order
51
- originalAmount: z.number().positive().optional(), // what client pay
52
- actualAmount: z.number().positive().optional(), // what store charge
53
- date: z.number(),
54
- deliveryDate: z.coerce.number(),
55
- client: ProfileSchema.required({}),
56
- nameOnInvoice: z.string().optional(),
57
- clientComment: z.string().optional(),
58
- organizationId: z.string().optional(),
59
- billingAccount: BillingAccountSchema.optional(),
60
- deliveryNote: DeliveryNoteSchema.optional(),
61
- invoice: InvoiceSchema.optional(),
62
- ezInvoice: EzInvoiceSchema.optional(),
63
- ezDeliveryNote: EzDeliveryNoteSchema.optional(),
64
- });
65
-
66
- export type TOrder = z.infer<typeof OrderSchema>;
@@ -1,23 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const BillingAccountSchema = z.object({
4
- number: z.string(),
5
- name: z.string(),
6
- id: z.string(),
7
- });
8
-
9
- // client organization for clients
10
- export const OrganizationSchema = z.object({
11
- id: z.string(),
12
- name: z.string(),
13
- discountPercentage: z.number().positive().min(0).max(100).optional(),
14
- nameOnInvoice: z.string().optional(),
15
- billingAccounts: z.array(BillingAccountSchema),
16
- paymentType: z.enum(["default", "delayed"]),
17
- });
18
-
19
- export const NewOrganizationSchema = OrganizationSchema.omit({ id: true });
20
-
21
- export type TBillingAccount = z.infer<typeof BillingAccountSchema>;
22
- export type TNewOrganization = z.infer<typeof NewOrganizationSchema>;
23
- export type TOrganization = z.infer<typeof OrganizationSchema>;
@@ -1,68 +0,0 @@
1
- import { z } from "zod";
2
- import { LocaleSchema } from "./Locale";
3
- import { CategorySchema } from "./Category";
4
-
5
- const text = z.string().min(1);
6
-
7
- export const ProductSchema = z.object({
8
- type: z.literal("Product"),
9
- storeId: text,
10
- companyId: text,
11
- id: text,
12
- objectID: text,
13
- sku: text,
14
- name: z.array(LocaleSchema),
15
- description: z.array(LocaleSchema),
16
- isPublished: z.boolean(),
17
- vat: z.boolean(),
18
- priceType: z.object({
19
- type: z.enum(["unit", "kg", "gram", "liter", "ml"]),
20
- value: z.number(),
21
- }),
22
- price: z.number().positive(),
23
- purchasePrice: z.number().optional(),
24
- profitPercentage: z.number().optional(),
25
- currency: z.literal("ILS"),
26
- discount: z.object({
27
- type: z.enum(["number", "percent", "none"]),
28
- value: z.number(),
29
- }),
30
- isDiscountable: z.boolean({ description: "included in store discounts" }).optional(),
31
- weight: z.object({
32
- value: z.number(),
33
- unit: z.enum(["kg", "gram", "none"]),
34
- }),
35
- volume: z.object({
36
- value: z.number(),
37
- unit: z.enum(["liter", "ml", "none"]),
38
- }),
39
- images: z.array(z.object({ url: z.string().url(), id: z.string() })),
40
- manufacturer: z.string(),
41
- brand: z.string(),
42
- importer: z.string(),
43
- supplier: z.string(),
44
- ingredients: z.array(LocaleSchema),
45
- created_at: z.number(),
46
- updated_at: z.number(),
47
- categoryIds: z.array(z.string().nonempty()),
48
-
49
- // @deprecated
50
- categoryList: z.array(CategorySchema).optional(),
51
- // @deprecated
52
- categories: z.object({
53
- lvl0: z.array(z.string()),
54
- lvl1: z.array(z.string()),
55
- lvl2: z.array(z.string()),
56
- lvl3: z.array(z.string()),
57
- lvl4: z.array(z.string()),
58
- }).optional(),
59
- // @deprecated
60
- categoryNames: z.array(z.string()).optional(),
61
- });
62
- export type TProduct = z.infer<typeof ProductSchema>;
63
-
64
- export const NewProductSchema = ProductSchema.extend({
65
- image: z.instanceof(File).optional(),
66
- });
67
-
68
- export type TNewProduct = z.infer<typeof NewProductSchema>;