120 lines
4 KiB
TypeScript
120 lines
4 KiB
TypeScript
|
|
import { apiClient } from './api-client';
|
||
|
|
import type { z } from 'zod/v4';
|
||
|
|
import type {
|
||
|
|
ShoppingListResponseSchema,
|
||
|
|
CreateShoppingListSchema,
|
||
|
|
UpdateShoppingListSchema,
|
||
|
|
AddShoppingItemSchema,
|
||
|
|
UpdateShoppingItemSchema,
|
||
|
|
ShoppingListSyncToPantryResponseSchema,
|
||
|
|
BasketStoreComparisonResponseSchema,
|
||
|
|
} from '@meshitrack/shared';
|
||
|
|
|
||
|
|
type ShoppingListResponse = z.infer<typeof ShoppingListResponseSchema>;
|
||
|
|
type CreateShoppingListInput = z.infer<typeof CreateShoppingListSchema>;
|
||
|
|
type UpdateShoppingListInput = z.infer<typeof UpdateShoppingListSchema>;
|
||
|
|
type AddShoppingItemInput = z.infer<typeof AddShoppingItemSchema>;
|
||
|
|
type UpdateShoppingItemInput = z.infer<typeof UpdateShoppingItemSchema>;
|
||
|
|
type ShoppingListSyncToPantryResponse = z.infer<typeof ShoppingListSyncToPantryResponseSchema>;
|
||
|
|
type BasketStoreComparisonResponse = z.infer<typeof BasketStoreComparisonResponseSchema>;
|
||
|
|
|
||
|
|
export async function getShoppingLists(householdId: string): Promise<ShoppingListResponse[]> {
|
||
|
|
return apiClient.get<ShoppingListResponse[]>(`/households/${householdId}/shopping-lists`);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getShoppingList(householdId: string, id: string): Promise<ShoppingListResponse> {
|
||
|
|
return apiClient.get<ShoppingListResponse>(`/households/${householdId}/shopping-lists/${id}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function createShoppingList(
|
||
|
|
householdId: string,
|
||
|
|
data: CreateShoppingListInput
|
||
|
|
): Promise<ShoppingListResponse> {
|
||
|
|
return apiClient.post<ShoppingListResponse>(`/households/${householdId}/shopping-lists`, data);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function updateShoppingList(
|
||
|
|
householdId: string,
|
||
|
|
id: string,
|
||
|
|
data: UpdateShoppingListInput
|
||
|
|
): Promise<ShoppingListResponse> {
|
||
|
|
return apiClient.patch<ShoppingListResponse>(`/households/${householdId}/shopping-lists/${id}`, data);
|
||
|
|
}
|
||
|
|
|
||
|
|
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,
|
||
|
|
data: AddShoppingItemInput
|
||
|
|
): Promise<ShoppingListResponse> {
|
||
|
|
return apiClient.post<ShoppingListResponse>(`/households/${householdId}/shopping-lists/${id}/items`, data);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function updateShoppingItem(
|
||
|
|
householdId: string,
|
||
|
|
id: string,
|
||
|
|
itemId: string,
|
||
|
|
data: UpdateShoppingItemInput
|
||
|
|
): Promise<ShoppingListResponse> {
|
||
|
|
return apiClient.patch<ShoppingListResponse>(
|
||
|
|
`/households/${householdId}/shopping-lists/${id}/items/${itemId}`,
|
||
|
|
data
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function removeShoppingItem(
|
||
|
|
householdId: string,
|
||
|
|
id: string,
|
||
|
|
itemId: string
|
||
|
|
): Promise<ShoppingListResponse> {
|
||
|
|
return apiClient.delete<ShoppingListResponse>(
|
||
|
|
`/households/${householdId}/shopping-lists/${id}/items/${itemId}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// -- Workflows --
|
||
|
|
|
||
|
|
export async function generateFromMealPlan(
|
||
|
|
householdId: string,
|
||
|
|
mealPlanId: string
|
||
|
|
): Promise<ShoppingListResponse> {
|
||
|
|
return apiClient.post<ShoppingListResponse>(
|
||
|
|
`/households/${householdId}/shopping-lists/from-meal-plan/${mealPlanId}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function syncToPantry(
|
||
|
|
householdId: string,
|
||
|
|
id: string
|
||
|
|
): Promise<ShoppingListSyncToPantryResponse> {
|
||
|
|
return apiClient.post<ShoppingListSyncToPantryResponse>(
|
||
|
|
`/households/${householdId}/shopping-lists/${id}/sync-to-pantry`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getBasketStoreComparison(
|
||
|
|
householdId: string,
|
||
|
|
id: string
|
||
|
|
): Promise<BasketStoreComparisonResponse> {
|
||
|
|
return apiClient.get<BasketStoreComparisonResponse>(
|
||
|
|
`/households/${householdId}/shopping-lists/${id}/stores`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 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`;
|
||
|
|
}
|