MeshiTrack/packages/web/src/services/shopping-lists.ts

133 lines
4.2 KiB
TypeScript
Raw Normal View History

2026-05-14 18:57:57 +09:00
import { apiClient } from './api-client';
import type { z } from 'zod/v4';
import type {
ShoppingListResponseSchema,
2026-05-19 16:15:15 +09:00
ShoppingItemSchema,
2026-05-14 18:57:57 +09:00
CreateShoppingListSchema,
UpdateShoppingListSchema,
AddShoppingItemSchema,
UpdateShoppingItemSchema,
ShoppingListSyncToPantryResponseSchema,
BasketStoreComparisonResponseSchema,
} from '@meshitrack/shared';
2026-05-19 16:15:15 +09:00
export type ShoppingListResponse = z.infer<typeof ShoppingListResponseSchema>;
export type ShoppingItem = z.infer<typeof ShoppingItemSchema>;
export type CreateShoppingListInput = z.infer<typeof CreateShoppingListSchema>;
export type UpdateShoppingListInput = z.infer<typeof UpdateShoppingListSchema>;
export type AddShoppingItemInput = z.infer<typeof AddShoppingItemSchema>;
export type UpdateShoppingItemInput = z.infer<typeof UpdateShoppingItemSchema>;
export type ShoppingListSyncToPantryResponse = z.infer<
typeof ShoppingListSyncToPantryResponseSchema
>;
export type BasketStoreComparisonResponse = z.infer<typeof BasketStoreComparisonResponseSchema>;
2026-05-14 18:57:57 +09:00
export async function getShoppingLists(householdId: string): Promise<ShoppingListResponse[]> {
return apiClient.get<ShoppingListResponse[]>(`/households/${householdId}/shopping-lists`);
}
2026-05-19 16:15:15 +09:00
export async function getShoppingList(
householdId: string,
id: string,
): Promise<ShoppingListResponse> {
2026-05-14 18:57:57 +09:00
return apiClient.get<ShoppingListResponse>(`/households/${householdId}/shopping-lists/${id}`);
}
export async function createShoppingList(
householdId: string,
2026-05-19 16:15:15 +09:00
data: CreateShoppingListInput,
2026-05-14 18:57:57 +09:00
): Promise<ShoppingListResponse> {
return apiClient.post<ShoppingListResponse>(`/households/${householdId}/shopping-lists`, data);
}
export async function updateShoppingList(
householdId: string,
id: string,
2026-05-19 16:15:15 +09:00
data: UpdateShoppingListInput,
2026-05-14 18:57:57 +09:00
): Promise<ShoppingListResponse> {
2026-05-19 16:15:15 +09:00
return apiClient.patch<ShoppingListResponse>(
`/households/${householdId}/shopping-lists/${id}`,
data,
);
2026-05-14 18:57:57 +09:00
}
export async function deleteShoppingList(householdId: string, id: string): Promise<void> {
return apiClient.delete<void>(`/households/${householdId}/shopping-lists/${id}`);
}
// -- Nested Item Operations --
export async function addShoppingItem(
householdId: string,
id: string,
2026-05-19 16:15:15 +09:00
data: AddShoppingItemInput,
2026-05-14 18:57:57 +09:00
): Promise<ShoppingListResponse> {
2026-05-19 16:15:15 +09:00
return apiClient.post<ShoppingListResponse>(
`/households/${householdId}/shopping-lists/${id}/items`,
data,
);
2026-05-14 18:57:57 +09:00
}
export async function updateShoppingItem(
householdId: string,
id: string,
itemId: string,
2026-05-19 16:15:15 +09:00
data: UpdateShoppingItemInput,
2026-05-14 18:57:57 +09:00
): Promise<ShoppingListResponse> {
return apiClient.patch<ShoppingListResponse>(
`/households/${householdId}/shopping-lists/${id}/items/${itemId}`,
2026-05-19 16:15:15 +09:00
data,
2026-05-14 18:57:57 +09:00
);
}
export async function removeShoppingItem(
householdId: string,
id: string,
2026-05-19 16:15:15 +09:00
itemId: string,
2026-05-14 18:57:57 +09:00
): Promise<ShoppingListResponse> {
return apiClient.delete<ShoppingListResponse>(
2026-05-19 16:15:15 +09:00
`/households/${householdId}/shopping-lists/${id}/items/${itemId}`,
2026-05-14 18:57:57 +09:00
);
}
// -- Workflows --
export async function generateFromMealPlan(
householdId: string,
2026-05-19 16:15:15 +09:00
mealPlanId: string,
2026-05-14 18:57:57 +09:00
): Promise<ShoppingListResponse> {
return apiClient.post<ShoppingListResponse>(
2026-05-19 16:15:15 +09:00
`/households/${householdId}/shopping-lists/from-meal-plan/${mealPlanId}`,
2026-05-14 18:57:57 +09:00
);
}
export async function syncToPantry(
householdId: string,
2026-05-19 16:15:15 +09:00
id: string,
2026-05-14 18:57:57 +09:00
): Promise<ShoppingListSyncToPantryResponse> {
return apiClient.post<ShoppingListSyncToPantryResponse>(
2026-05-19 16:15:15 +09:00
`/households/${householdId}/shopping-lists/${id}/sync-to-pantry`,
2026-05-14 18:57:57 +09:00
);
}
export async function getBasketStoreComparison(
householdId: string,
2026-05-19 16:15:15 +09:00
id: string,
2026-05-14 18:57:57 +09:00
): Promise<BasketStoreComparisonResponse> {
return apiClient.get<BasketStoreComparisonResponse>(
2026-05-19 16:15:15 +09:00
`/households/${householdId}/shopping-lists/${id}/stores`,
2026-05-14 18:57:57 +09:00
);
}
/**
* Generates direct WebSocket path for collaborative checklist syncing.
*/
export function getShoppingListSyncSocketUrl(householdId: string, id: string): string {
// Derive WS protocol based on configured API baseURL protocol (defaulting to unsafe ws for localhost)
const baseUrl = apiClient.baseUrl || '';
const isSecure = baseUrl.startsWith('https');
const cleanHost = baseUrl.replace(/^https?:\/\//, '');
const protocol = isSecure ? 'wss' : 'ws';
return `${protocol}://${cleanHost}/households/${householdId}/shopping-lists/${id}/sync`;
}