This commit is contained in:
Aerilyn Weber 2026-05-14 18:57:57 +09:00
parent e396f5088c
commit a1801af63b
36 changed files with 4783 additions and 31 deletions

View file

@ -0,0 +1,118 @@
import { z } from 'zod/v4';
import { ServingUnit } from '../enums/product.enums.js';
export const CreatePriceRecordSchema = z.object({
productId: z.string().min(1),
storeId: z.string().min(1),
price: z.number().positive(),
currency: z.string().min(1).max(10).default('USD'),
quantity: z.number().positive(),
unit: z.nativeEnum(ServingUnit),
date: z.iso.datetime().optional(),
notes: z.string().max(1000).trim().optional(),
receiptImageUrl: z.string().url().optional(),
});
export const BulkPriceRecordInputSchema = z.object({
storeId: z.string().min(1),
date: z.iso.datetime().optional(),
items: z.array(
z.object({
productId: z.string().min(1),
price: z.number().positive(),
quantity: z.number().positive(),
unit: z.nativeEnum(ServingUnit),
notes: z.string().max(1000).trim().optional(),
})
).min(1),
});
export const PriceHistoryQuerySchema = z.object({
storeId: z.string().optional(),
startDate: z.iso.datetime().optional(),
endDate: z.iso.datetime().optional(),
cursor: z.string().optional(),
limit: z.coerce.number().int().min(1).max(100).default(20),
});
export const PriceRecordResponseSchema = z.object({
_id: z.string(),
householdId: z.string(),
productId: z.string(),
productName: z.string(),
storeId: z.string(),
storeName: z.string(),
price: z.number(),
currency: z.string(),
quantity: z.number(),
unit: z.nativeEnum(ServingUnit),
pricePerUnit: z.number(),
date: z.string(),
receiptImageUrl: z.string().optional(),
notes: z.string().optional(),
createdBy: z.string(),
createdAt: z.string(),
});
export const StorePriceComparisonSchema = z.object({
storeId: z.string(),
storeName: z.string(),
latestPrice: z.number(),
latestPricePerUnit: z.number(),
currency: z.string(),
date: z.string(),
});
export const PriceAnalyticsResponseSchema = z.object({
averageBasketByStore: z.array(
z.object({
storeId: z.string(),
storeName: z.string(),
avgTotal: z.number(),
tripCount: z.number(),
})
),
priceAlerts: z.array(
z.object({
productId: z.string(),
productName: z.string(),
storeId: z.string(),
storeName: z.string(),
previousPrice: z.number(),
currentPrice: z.number(),
changePercent: z.number(),
date: z.string(),
})
),
spendingOverTime: z.array(
z.object({
period: z.string(),
total: z.number(),
})
),
spendingByCategory: z.array(
z.object({
category: z.string(),
total: z.number(),
avgPerItem: z.number(),
})
),
});
export const PriceHistoryResponseSchema = z.object({
data: z.array(PriceRecordResponseSchema),
pagination: z.object({
cursor: z.string().nullable(),
hasMore: z.boolean(),
}),
});
export const FoodStoreComparisonResponseSchema = z.object({
data: z.array(StorePriceComparisonSchema),
});
export const FoodSpendingAnalyticsResponseSchema = PriceAnalyticsResponseSchema;
export type CreatePriceRecordInput = z.infer<typeof CreatePriceRecordSchema>;
export type BulkPriceRecordInput = z.infer<typeof BulkPriceRecordInputSchema>;
export type PriceHistoryQueryInput = z.infer<typeof PriceHistoryQuerySchema>;