Phase 8
This commit is contained in:
parent
029940b079
commit
e396f5088c
36 changed files with 4199 additions and 22 deletions
131
packages/web/src/services/meal-plans.ts
Normal file
131
packages/web/src/services/meal-plans.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import { apiClient } from './api-client';
|
||||
import type { z } from 'zod/v4';
|
||||
import type {
|
||||
MealPlanResponseSchema,
|
||||
MealPlanListResponseSchema,
|
||||
CreateMealPlanInput,
|
||||
UpdateMealPlanInput,
|
||||
MealPlanStatus,
|
||||
} from '@meshitrack/shared';
|
||||
|
||||
export type MealPlanResponse = z.infer<typeof MealPlanResponseSchema>;
|
||||
export type MealPlanListResponse = z.infer<typeof MealPlanListResponseSchema>;
|
||||
|
||||
export interface MealPlanQuery {
|
||||
cursor?: string;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface RecipeSuggestion {
|
||||
recipeId: string;
|
||||
recipeName: string;
|
||||
totalScore: number;
|
||||
scores: {
|
||||
coverage: number;
|
||||
urgency: number;
|
||||
nutrition: number;
|
||||
variety: number;
|
||||
};
|
||||
reasoning: string[];
|
||||
}
|
||||
|
||||
export interface ShoppingGapReport {
|
||||
mealPlanId: string;
|
||||
missingItems: Array<{
|
||||
productId: string;
|
||||
productName: string;
|
||||
category: string;
|
||||
requiredQuantity: number;
|
||||
pantryQuantity: number;
|
||||
missingQuantity: number;
|
||||
unit: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export async function listMealPlans(
|
||||
householdId: string,
|
||||
query?: MealPlanQuery
|
||||
): Promise<MealPlanListResponse> {
|
||||
const params = new URLSearchParams();
|
||||
if (query?.cursor) params.set('cursor', query.cursor);
|
||||
if (query?.limit) params.set('limit', String(query.limit));
|
||||
const qs = params.toString();
|
||||
return apiClient.get<MealPlanListResponse>(
|
||||
`/households/${householdId}/meal-plans${qs ? `?${qs}` : ''}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function getMealPlanByWeek(
|
||||
householdId: string,
|
||||
weekStartDate: string
|
||||
): Promise<MealPlanResponse | { message: string }> {
|
||||
return apiClient.get<MealPlanResponse | { message: string }>(
|
||||
`/households/${householdId}/meal-plans/week/${weekStartDate}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function getMealPlan(
|
||||
householdId: string,
|
||||
id: string
|
||||
): Promise<MealPlanResponse> {
|
||||
return apiClient.get<MealPlanResponse>(
|
||||
`/households/${householdId}/meal-plans/${id}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function createMealPlan(
|
||||
householdId: string,
|
||||
data: CreateMealPlanInput
|
||||
): Promise<MealPlanResponse> {
|
||||
return apiClient.post<MealPlanResponse>(
|
||||
`/households/${householdId}/meal-plans`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateMealPlan(
|
||||
householdId: string,
|
||||
id: string,
|
||||
data: UpdateMealPlanInput
|
||||
): Promise<MealPlanResponse> {
|
||||
return apiClient.patch<MealPlanResponse>(
|
||||
`/households/${householdId}/meal-plans/${id}`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateMealPlanStatus(
|
||||
householdId: string,
|
||||
id: string,
|
||||
status: MealPlanStatus
|
||||
): Promise<MealPlanResponse> {
|
||||
return apiClient.patch<MealPlanResponse>(
|
||||
`/households/${householdId}/meal-plans/${id}/status`,
|
||||
{ status }
|
||||
);
|
||||
}
|
||||
|
||||
export async function deleteMealPlan(
|
||||
householdId: string,
|
||||
id: string
|
||||
): Promise<void> {
|
||||
return apiClient.delete(`/households/${householdId}/meal-plans/${id}`);
|
||||
}
|
||||
|
||||
export async function getSuggestions(
|
||||
householdId: string,
|
||||
limit = 5
|
||||
): Promise<RecipeSuggestion[]> {
|
||||
return apiClient.get<RecipeSuggestion[]>(
|
||||
`/households/${householdId}/meal-plans/suggestions?limit=${limit}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function getShoppingGap(
|
||||
householdId: string,
|
||||
id: string
|
||||
): Promise<ShoppingGapReport> {
|
||||
return apiClient.get<ShoppingGapReport>(
|
||||
`/households/${householdId}/meal-plans/${id}/gap`
|
||||
);
|
||||
}
|
||||
45
packages/web/src/services/nutrition-targets.ts
Normal file
45
packages/web/src/services/nutrition-targets.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { apiClient } from './api-client';
|
||||
import type { z } from 'zod/v4';
|
||||
import type {
|
||||
NutritionTargetResponseSchema,
|
||||
SetNutritionTargetInput,
|
||||
} from '@meshitrack/shared';
|
||||
|
||||
export type NutritionTargetResponse = z.infer<typeof NutritionTargetResponseSchema>;
|
||||
|
||||
export async function getActiveNutritionTarget(
|
||||
householdId: string
|
||||
): Promise<NutritionTargetResponse | { message: string }> {
|
||||
return apiClient.get<NutritionTargetResponse | { message: string }>(
|
||||
`/households/${householdId}/nutrition-targets`
|
||||
);
|
||||
}
|
||||
|
||||
export async function getNutritionTargetHistory(
|
||||
householdId: string
|
||||
): Promise<NutritionTargetResponse[]> {
|
||||
return apiClient.get<NutritionTargetResponse[]>(
|
||||
`/households/${householdId}/nutrition-targets/history`
|
||||
);
|
||||
}
|
||||
|
||||
export async function setNutritionTarget(
|
||||
householdId: string,
|
||||
data: SetNutritionTargetInput
|
||||
): Promise<NutritionTargetResponse> {
|
||||
return apiClient.post<NutritionTargetResponse>(
|
||||
`/households/${householdId}/nutrition-targets`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
export async function calculateTargetPreset(
|
||||
householdId: string,
|
||||
calories: number,
|
||||
strategy: 'maintenance' | 'loss' | 'gain'
|
||||
): Promise<SetNutritionTargetInput> {
|
||||
return apiClient.post<SetNutritionTargetInput>(
|
||||
`/households/${householdId}/nutrition-targets/presets`,
|
||||
{ calories, strategy }
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue