This commit is contained in:
Aerilyn Weber 2026-05-14 19:59:42 +09:00
parent a1801af63b
commit d2a7e652b3
16 changed files with 113 additions and 92 deletions

View file

@ -8,19 +8,19 @@ describe('MealPlan Schemas', () => {
type: MealType.DINNER,
recipeName: 'Chicken Salad',
servings: 2,
perServingNutrition: { calories: 300, protein: 30, carbs: 10, fat: 15 }
perServingNutrition: { calories: 300, protein: 30, carbs: 10, fat: 15 },
};
const validDay = {
date: '2026-05-18',
meals: [validMeal],
dailyNutritionTotal: { calories: 600, protein: 60, carbs: 20, fat: 30 }
dailyNutritionTotal: { calories: 600, protein: 60, carbs: 20, fat: 30 },
};
const validPlan = {
weekStartDate: '2026-05-18',
days: Array(7).fill(validDay),
status: MealPlanStatus.DRAFT
status: MealPlanStatus.DRAFT,
};
it('should validate a valid meal plan', () => {

View file

@ -7,7 +7,7 @@ describe('NutritionTarget Schemas', () => {
proteinG: 150,
carbsG: 200,
fatG: 70,
isActive: true
isActive: true,
};
it('should validate a valid target', () => {

View file

@ -16,15 +16,17 @@ export const CreatePriceRecordSchema = z.object({
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),
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({
@ -70,7 +72,7 @@ export const PriceAnalyticsResponseSchema = z.object({
storeName: z.string(),
avgTotal: z.number(),
tripCount: z.number(),
})
}),
),
priceAlerts: z.array(
z.object({
@ -82,20 +84,20 @@ export const PriceAnalyticsResponseSchema = z.object({
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(),
})
}),
),
});

View file

@ -22,16 +22,19 @@ export const ShoppingItemSchema = z.object({
export const CreateShoppingListSchema = z.object({
name: z.string().min(1).max(100).trim(),
preferredStoreId: z.string().optional(),
items: z.array(
z.object({
productId: z.string().optional(),
customName: z.string().optional(),
quantity: z.number().positive(),
unit: z.nativeEnum(ServingUnit),
notes: z.string().optional(),
category: z.nativeEnum(ProductCategory).optional(),
})
).optional().default([]),
items: z
.array(
z.object({
productId: z.string().optional(),
customName: z.string().optional(),
quantity: z.number().positive(),
unit: z.nativeEnum(ServingUnit),
notes: z.string().optional(),
category: z.nativeEnum(ProductCategory).optional(),
}),
)
.optional()
.default([]),
});
export const UpdateShoppingListSchema = z.object({
@ -41,17 +44,18 @@ export const UpdateShoppingListSchema = z.object({
items: z.array(ShoppingItemSchema).optional(),
});
export const AddShoppingItemSchema = z.object({
productId: z.string().optional(),
customName: z.string().optional(),
quantity: z.number().positive(),
unit: z.nativeEnum(ServingUnit),
notes: z.string().max(500).trim().optional(),
category: z.nativeEnum(ProductCategory).optional(),
}).refine(
(data) => data.productId || data.customName,
{ message: 'Must provide either a productId or customName' }
);
export const AddShoppingItemSchema = z
.object({
productId: z.string().optional(),
customName: z.string().optional(),
quantity: z.number().positive(),
unit: z.nativeEnum(ServingUnit),
notes: z.string().max(500).trim().optional(),
category: z.nativeEnum(ProductCategory).optional(),
})
.refine((data) => data.productId || data.customName, {
message: 'Must provide either a productId or customName',
});
export const UpdateShoppingItemSchema = z.object({
quantity: z.number().positive().optional(),
@ -68,10 +72,12 @@ export const ShoppingListResponseSchema = z.object({
name: z.string(),
status: z.nativeEnum(ShoppingListStatus),
items: z.array(ShoppingItemSchema),
createdFrom: z.object({
type: z.nativeEnum(ShoppingListSourceType),
referenceId: z.string().optional(),
}).optional(),
createdFrom: z
.object({
type: z.nativeEnum(ShoppingListSourceType),
referenceId: z.string().optional(),
})
.optional(),
mealPlanId: z.string().optional(),
totalEstimatedCost: z.number().optional(),
preferredStoreId: z.string().optional(),
@ -93,20 +99,22 @@ export const StoreComparisonResultSchema = z.object({
estimatedTotal: z.number(),
itemsCovered: z.number(),
itemsMissing: z.array(z.string()),
})
}),
),
splitStoreOption: z.object({
stores: z.array(
z.object({
storeId: z.string(),
storeName: z.string(),
items: z.array(z.string()),
subtotal: z.number(),
})
),
estimatedTotal: z.number(),
savingsVsBestSingleStore: z.number(),
}).optional(),
splitStoreOption: z
.object({
stores: z.array(
z.object({
storeId: z.string(),
storeName: z.string(),
items: z.array(z.string()),
subtotal: z.number(),
}),
),
estimatedTotal: z.number(),
savingsVsBestSingleStore: z.number(),
})
.optional(),
});
export const BasketStoreComparisonResponseSchema = StoreComparisonResultSchema;