Stricter api changes
This commit is contained in:
parent
211489ca55
commit
f2dee9ca30
6 changed files with 203 additions and 97 deletions
12
README.md
12
README.md
|
|
@ -98,3 +98,15 @@ Environment
|
|||
|
||||
Testing
|
||||
- Unit tests use MSW; the client defaults to a localhost base in tests for easy mocking
|
||||
|
||||
### CurrentShoppingList item kinds
|
||||
|
||||
The OpenAPI spec models current shopping list items as distinct kinds:
|
||||
- outstandingItems: ListIngredientItem[]
|
||||
- requestedMeals: RequestedMealItem[]
|
||||
- purchasedItems: ListIngredientItem[]
|
||||
|
||||
The SDK maps these to a domain DTO (`CurrentShoppingListDTO`) and may attach refs (`ingredient`, `recipe`, `meal`, `list`) for convenience. UI code should:
|
||||
- Prefer stable IDs (`ingredientId`, `mealId`, `recipeId`, `listId`) for actions and lookups
|
||||
- Treat attached refs as optional view helpers (never required)
|
||||
- Keep all normalization at the boundary (decoders); avoid casts and runtime type checks in app code
|
||||
|
|
|
|||
|
|
@ -1,12 +1,25 @@
|
|||
import { api } from '@/api/client'
|
||||
import type { components } from '@/api/types'
|
||||
import { toDate, decodeMeal, decodeRecipe, decodeIngredients, decodeShoppingList, decodeShoppingListItems, decodeIngredient, decodeLookup } from '@/domain/decoders'
|
||||
import {
|
||||
toDate,
|
||||
decodeMeal,
|
||||
decodeRecipe,
|
||||
decodeIngredients,
|
||||
decodeShoppingList,
|
||||
decodeShoppingListItems,
|
||||
decodeListIngredientItems,
|
||||
decodeRequestedMealItems,
|
||||
decodeIngredient,
|
||||
decodeLookup,
|
||||
} from '@/domain/decoders'
|
||||
import type {
|
||||
Recipe,
|
||||
Meal,
|
||||
Ingredient,
|
||||
ShoppingList,
|
||||
ShoppingListItemWithRefs,
|
||||
ListIngredientItemWithRefs,
|
||||
RequestedMealItemWithRefs,
|
||||
CurrentShoppingListDTO,
|
||||
PurchasedShoppingListDTO,
|
||||
ShoppingLookups,
|
||||
|
|
@ -24,34 +37,32 @@ function httpError(response: Response, error: unknown): Error {
|
|||
// Shopping list mapped view types now come from domain/types
|
||||
|
||||
function attachItemRefs(
|
||||
items: Array<ShoppingListItemWithRefs> | null | undefined,
|
||||
items: Array<ShoppingListItemWithRefs | ListIngredientItemWithRefs | RequestedMealItemWithRefs> | null | undefined,
|
||||
lookups: ShoppingLookups
|
||||
): void {
|
||||
if (!Array.isArray(items)) return
|
||||
for (const item of items) {
|
||||
const ingredientId = item.ingredientId ?? undefined
|
||||
const mealId = item.mealId ?? undefined
|
||||
const recipeId = item.recipeId ?? undefined
|
||||
const listId = item.listId ?? undefined
|
||||
const created = item.createdDate
|
||||
|
||||
if (ingredientId !== undefined && lookups.ingredientsLookup) {
|
||||
const v = lookups.ingredientsLookup[String(ingredientId)]
|
||||
// ingredient ref
|
||||
if ('ingredientId' in item && item.ingredientId !== undefined && lookups.ingredientsLookup) {
|
||||
const v = lookups.ingredientsLookup[String(item.ingredientId)]
|
||||
if (v !== undefined) item.ingredient = v
|
||||
}
|
||||
if (mealId !== undefined && lookups.mealsLookup) {
|
||||
const v = lookups.mealsLookup[String(mealId)]
|
||||
// meal ref (present on all item types)
|
||||
if ('mealId' in item && item.mealId !== undefined && lookups.mealsLookup) {
|
||||
const v = lookups.mealsLookup[String(item.mealId)]
|
||||
if (v !== undefined) item.meal = v
|
||||
}
|
||||
if (recipeId !== undefined && lookups.recipesLookup) {
|
||||
const v = lookups.recipesLookup[String(recipeId)]
|
||||
// recipe ref
|
||||
if ('recipeId' in item && item.recipeId !== undefined && lookups.recipesLookup) {
|
||||
const v = lookups.recipesLookup[String(item.recipeId)]
|
||||
if (v !== undefined) item.recipe = v
|
||||
}
|
||||
if (listId !== undefined && lookups.shoppingListLookup) {
|
||||
const v = lookups.shoppingListLookup[String(listId)]
|
||||
// list ref
|
||||
if ('listId' in item && item.listId !== undefined && lookups.shoppingListLookup) {
|
||||
const v = lookups.shoppingListLookup[String(item.listId)]
|
||||
if (v !== undefined) item.list = v
|
||||
}
|
||||
if (created !== undefined) item.createdDate = toDate(created)
|
||||
if ('createdDate' in item) item.createdDate = toDate(item.createdDate)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,9 +126,9 @@ export function mapCurrentShoppingList(dto: components['schemas']['CurrentShoppi
|
|||
}
|
||||
|
||||
const dtoOut: CurrentShoppingListDTO = {
|
||||
outstandingItems: decodeShoppingListItems(outstandingRaw ?? []),
|
||||
requestedMeals: decodeShoppingListItems(requestedRaw ?? []),
|
||||
purchasedItems: decodeShoppingListItems(purchasedRaw ?? []),
|
||||
outstandingItems: decodeListIngredientItems(outstandingRaw ?? []),
|
||||
requestedMeals: decodeRequestedMealItems(requestedRaw ?? []),
|
||||
purchasedItems: decodeListIngredientItems(purchasedRaw ?? []),
|
||||
...(ingredientsLookup && { ingredientsLookup }),
|
||||
...(mealsLookup && { mealsLookup }),
|
||||
...(recipesLookup && { recipesLookup }),
|
||||
|
|
@ -308,24 +319,18 @@ export async function purchaseShoppingList(
|
|||
): Promise<import('@/domain/types').ShoppingListWithRefs | null> {
|
||||
if (!Array.isArray(completedRequests) || completedRequests.length === 0) return null
|
||||
// Map incoming requests: if id provided and >= 0, use it; otherwise send identifiers for ingredient/recipe/meal
|
||||
const items: components['schemas']['ShoppingListItem'][] = completedRequests.map((i) =>
|
||||
i.type === 'existing'
|
||||
? { id: i.id, personId: i.personId, ingredientId: i.ingredientId ?? null }
|
||||
: {
|
||||
id: -1,
|
||||
const items: components['schemas']['IngredientPurchaseItemIn'][] = completedRequests.map((i) => ({
|
||||
personId: i.personId,
|
||||
ingredientId: i.ingredientId ?? null,
|
||||
recipeId: i.recipeId ?? null,
|
||||
mealId: i.mealId ?? null,
|
||||
}
|
||||
)
|
||||
ingredientId: i.ingredientId ?? -1,
|
||||
recipeId: i.type === 'refs' ? i.recipeId ?? null : null,
|
||||
mealId: i.type === 'refs' ? i.mealId ?? null : null,
|
||||
createdDate: null,
|
||||
}))
|
||||
|
||||
if (items.length === 0) return null
|
||||
|
||||
const body: components['schemas']['ShoppingList'] = {
|
||||
id: -1,
|
||||
const body: components['schemas']['PurchaseListIn'] = {
|
||||
storeName: '',
|
||||
purchasedById: -1,
|
||||
items,
|
||||
}
|
||||
const { data, error, response } = await api.POST('/api/v1/shopping', {
|
||||
|
|
|
|||
122
src/api/types.ts
122
src/api/types.ts
|
|
@ -340,11 +340,11 @@ export interface components {
|
|||
/** CurrentShoppingList */
|
||||
CurrentShoppingList: {
|
||||
/** Outstandingitems */
|
||||
outstandingItems: components["schemas"]["ShoppingListItem"][];
|
||||
outstandingItems: components["schemas"]["ListIngredientItem"][];
|
||||
/** Requestedmeals */
|
||||
requestedMeals: components["schemas"]["ShoppingListItem"][];
|
||||
requestedMeals: components["schemas"]["RequestedMealItem"][];
|
||||
/** Purchaseditems */
|
||||
purchasedItems?: components["schemas"]["ShoppingListItem"][];
|
||||
purchasedItems?: components["schemas"]["ListIngredientItem"][];
|
||||
/** Ingredientslookup */
|
||||
ingredientsLookup?: {
|
||||
[key: string]: components["schemas"]["Ingredient"];
|
||||
|
|
@ -355,7 +355,7 @@ export interface components {
|
|||
};
|
||||
/** Shoppinglistlookup */
|
||||
shoppingListLookup?: {
|
||||
[key: string]: components["schemas"]["ShoppingList"];
|
||||
[key: string]: components["schemas"]["ShoppingListOut"];
|
||||
};
|
||||
/** Recipeslookup */
|
||||
recipesLookup?: {
|
||||
|
|
@ -404,6 +404,49 @@ export interface components {
|
|||
mealId?: number | null;
|
||||
product?: components["schemas"]["Product"] | null;
|
||||
};
|
||||
/** IngredientPurchaseItemIn */
|
||||
IngredientPurchaseItemIn: {
|
||||
/** Ingredientid */
|
||||
ingredientId: number;
|
||||
/** Personid */
|
||||
personId: number;
|
||||
/** Createddate */
|
||||
createdDate?: string | null;
|
||||
/** Mealid */
|
||||
mealId?: number | null;
|
||||
/** Recipeid */
|
||||
recipeId?: number | null;
|
||||
};
|
||||
/** ListIngredientItem */
|
||||
ListIngredientItem: {
|
||||
/**
|
||||
* Kind
|
||||
* @default ingredient
|
||||
* @constant
|
||||
* @enum {string}
|
||||
*/
|
||||
kind: "ingredient";
|
||||
/**
|
||||
* Id
|
||||
* @default -1
|
||||
*/
|
||||
id: number;
|
||||
/** Ingredientid */
|
||||
ingredientId: number;
|
||||
/** Personid */
|
||||
personId: number;
|
||||
/**
|
||||
* Createddate
|
||||
* Format: date-time
|
||||
*/
|
||||
createdDate: string;
|
||||
/** Listid */
|
||||
listId?: number | null;
|
||||
/** Mealid */
|
||||
mealId?: number | null;
|
||||
/** Recipeid */
|
||||
recipeId?: number | null;
|
||||
};
|
||||
/** LoginBody */
|
||||
LoginBody: {
|
||||
/** Username */
|
||||
|
|
@ -585,9 +628,15 @@ export interface components {
|
|||
/** Tags */
|
||||
tags?: string[];
|
||||
};
|
||||
/** PurchaseListIn */
|
||||
PurchaseListIn: {
|
||||
storeName: components["schemas"]["StoreEnum"];
|
||||
/** Items */
|
||||
items: components["schemas"]["IngredientPurchaseItemIn"][];
|
||||
};
|
||||
/** PurchasedShoppingList */
|
||||
PurchasedShoppingList: {
|
||||
list: components["schemas"]["ShoppingList"];
|
||||
list: components["schemas"]["ShoppingListOut"];
|
||||
/** Mealslookup */
|
||||
mealsLookup?: {
|
||||
[key: string]: components["schemas"]["Meal-Output"];
|
||||
|
|
@ -667,54 +716,45 @@ export interface components {
|
|||
hiddenById?: number | null;
|
||||
hiddenBy?: components["schemas"]["Person"] | null;
|
||||
};
|
||||
/** ShoppingList */
|
||||
ShoppingList: {
|
||||
/** RequestedMealItem */
|
||||
RequestedMealItem: {
|
||||
/**
|
||||
* Kind
|
||||
* @default requestedMeal
|
||||
* @constant
|
||||
* @enum {string}
|
||||
*/
|
||||
kind: "requestedMeal";
|
||||
/**
|
||||
* Id
|
||||
* @default -1
|
||||
*/
|
||||
id: number;
|
||||
/** Personid */
|
||||
personId: number;
|
||||
/** Mealid */
|
||||
mealId: number;
|
||||
/**
|
||||
* Createddate
|
||||
* Format: date-time
|
||||
*/
|
||||
createdDate?: string;
|
||||
/** @default */
|
||||
storeName: components["schemas"]["StoreEnum"];
|
||||
createdDate: string;
|
||||
};
|
||||
/** ShoppingListOut */
|
||||
ShoppingListOut: {
|
||||
/** Id */
|
||||
id: number;
|
||||
/**
|
||||
* Purchasedbyid
|
||||
* @default -1
|
||||
* Createddate
|
||||
* Format: date-time
|
||||
*/
|
||||
createdDate: string;
|
||||
storeName: components["schemas"]["StoreEnum"];
|
||||
/** Purchasedbyid */
|
||||
purchasedById: number;
|
||||
purchasedBy?: components["schemas"]["Person"] | null;
|
||||
/** Items */
|
||||
items?: components["schemas"]["ShoppingListItem"][];
|
||||
};
|
||||
/** ShoppingListItem */
|
||||
ShoppingListItem: {
|
||||
/**
|
||||
* Id
|
||||
* @default -1
|
||||
*/
|
||||
id: number;
|
||||
/** Listid */
|
||||
listId?: number | null;
|
||||
/**
|
||||
* Personid
|
||||
* @default -1
|
||||
*/
|
||||
personId: number;
|
||||
/** Ingredientid */
|
||||
ingredientId?: number | null;
|
||||
/** Recipeid */
|
||||
recipeId?: number | null;
|
||||
/** Mealid */
|
||||
mealId?: number | null;
|
||||
/**
|
||||
* Createddate
|
||||
* Format: date-time
|
||||
*/
|
||||
createdDate?: string;
|
||||
items?: components["schemas"]["ListIngredientItem"][];
|
||||
};
|
||||
/**
|
||||
* StoreEnum
|
||||
|
|
@ -1293,7 +1333,7 @@ export interface operations {
|
|||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["ShoppingList"];
|
||||
"application/json": components["schemas"]["PurchaseListIn"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
|
|
@ -1415,7 +1455,7 @@ export interface operations {
|
|||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ShoppingListItem"];
|
||||
"application/json": components["schemas"]["RequestedMealItem"];
|
||||
};
|
||||
};
|
||||
404: components["responses"]["Problem404"];
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import * as sdk from '@/api/sdk'
|
||||
import type { ShoppingListItemWithRefs, Product, Meal } from '@/domain/types'
|
||||
import type { ListIngredientItemWithRefs, RequestedMealItemWithRefs, Product, Meal } from '@/domain/types'
|
||||
|
||||
export type GroupByProduct = { type: 'product'; product: Product; shoppingListItems: ShoppingListItemWithRefs[] }
|
||||
export type GroupByName = { type: 'name'; name: string; shoppingListItems: ShoppingListItemWithRefs[] }
|
||||
export type GroupByProduct = { type: 'product'; product: Product; shoppingListItems: ListIngredientItemWithRefs[] }
|
||||
export type GroupByName = { type: 'name'; name: string; shoppingListItems: ListIngredientItemWithRefs[] }
|
||||
export type Group = GroupByProduct | GroupByName
|
||||
|
||||
export function groupsToItems(groups: Group[]): ShoppingListItemWithRefs[] {
|
||||
export function groupsToItems(groups: Group[]): ListIngredientItemWithRefs[] {
|
||||
return groups.map((g) => g.shoppingListItems).flat()
|
||||
}
|
||||
|
||||
export function uniqueMeals(shoppingListItems: ShoppingListItemWithRefs[]): Meal[] {
|
||||
export function uniqueMeals(shoppingListItems: Array<ListIngredientItemWithRefs | RequestedMealItemWithRefs>): Meal[] {
|
||||
const mealsWithDuplicates = shoppingListItems.map((item) => item.meal).filter((m): m is Meal => !!m)
|
||||
const mealsLookup: Record<string | number, Meal> = mealsWithDuplicates.reduce<Record<string | number, Meal>>(
|
||||
(acc, meal) => {
|
||||
|
|
@ -21,7 +21,7 @@ export function uniqueMeals(shoppingListItems: ShoppingListItemWithRefs[]): Meal
|
|||
return Object.values(mealsLookup)
|
||||
}
|
||||
|
||||
export function itemsToGroups(shoppingListItems: ShoppingListItemWithRefs[]): Group[] {
|
||||
export function itemsToGroups(shoppingListItems: ListIngredientItemWithRefs[]): Group[] {
|
||||
const ingredients_by_product_id: Record<string | number, GroupByProduct> = {}
|
||||
const ingredients_by_name: Record<string, GroupByName> = {}
|
||||
for (const item of shoppingListItems) {
|
||||
|
|
@ -52,8 +52,8 @@ export function itemsToGroups(shoppingListItems: ShoppingListItemWithRefs[]): Gr
|
|||
}
|
||||
|
||||
export function useShopping() {
|
||||
const groupsFrom = (items?: ShoppingListItemWithRefs[]): Group[] => itemsToGroups(items ?? [])
|
||||
const mealsFrom = (items?: ShoppingListItemWithRefs[]): Meal[] => uniqueMeals(items ?? [])
|
||||
const groupsFrom = (items?: ListIngredientItemWithRefs[]): Group[] => itemsToGroups(items ?? [])
|
||||
const mealsFrom = (items?: Array<ListIngredientItemWithRefs | RequestedMealItemWithRefs>): Meal[] => uniqueMeals(items ?? [])
|
||||
return {
|
||||
getCurrentShoppingList: sdk.getCurrentShoppingList,
|
||||
getShoppingList: sdk.getShoppingList,
|
||||
|
|
@ -68,14 +68,14 @@ export function useShopping() {
|
|||
async purchaseFromGroups(groups: Group[]) {
|
||||
const items = groupsToItems(groups).map((i): sdk.PurchaseRequest => {
|
||||
if (typeof i.id === 'number' && i.id >= 0) {
|
||||
return { type: 'existing', id: i.id, personId: i.personId, ingredientId: i.ingredient?.id ?? null }
|
||||
return { type: 'existing', id: i.id, personId: i.personId, ingredientId: i.ingredientId ?? null }
|
||||
}
|
||||
return {
|
||||
type: 'refs',
|
||||
personId: i.personId,
|
||||
ingredientId: i.ingredient?.id ?? null,
|
||||
recipeId: i.recipe?.id ?? null,
|
||||
mealId: i.meal?.id ?? null,
|
||||
ingredientId: i.ingredientId ?? null,
|
||||
recipeId: i.recipeId ?? null,
|
||||
mealId: i.mealId ?? null,
|
||||
}
|
||||
})
|
||||
if (!items?.length) return null
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
import type { RecipeOut, Recipe, MealOut, Meal, MealRecipe, Ingredient as DomainIngredient, ShoppingList, ShoppingListItem, ShoppingListItemWithRefs, MealInput } from './types'
|
||||
import type {
|
||||
RecipeOut,
|
||||
Recipe,
|
||||
MealOut,
|
||||
Meal,
|
||||
MealRecipe,
|
||||
Ingredient as DomainIngredient,
|
||||
ShoppingList,
|
||||
ShoppingListItem,
|
||||
ShoppingListItemWithRefs,
|
||||
MealInput,
|
||||
ListIngredientItem,
|
||||
RequestedMealItem,
|
||||
ListIngredientItemWithRefs,
|
||||
RequestedMealItemWithRefs,
|
||||
} from './types'
|
||||
import type { components } from '@/api/types'
|
||||
|
||||
export function toDate(value: string | Date | null | undefined): Date | null {
|
||||
|
|
@ -70,7 +85,7 @@ export function decodeIngredients(list: components['schemas']['Ingredient'][] |
|
|||
return list.map((i) => decodeIngredient(i))
|
||||
}
|
||||
|
||||
function decodeShoppingListItem(i: components['schemas']['ShoppingListItem'] | null | undefined): ShoppingListItem {
|
||||
function decodeShoppingListItem(i: components['schemas']['ListIngredientItem'] | null | undefined): ShoppingListItem {
|
||||
if (!i) throw new Error('Invalid shopping list item payload')
|
||||
return {
|
||||
...i,
|
||||
|
|
@ -78,13 +93,13 @@ function decodeShoppingListItem(i: components['schemas']['ShoppingListItem'] | n
|
|||
}
|
||||
}
|
||||
|
||||
export function decodeShoppingListItems(list: components['schemas']['ShoppingListItem'][] | null | undefined): ShoppingListItemWithRefs[] {
|
||||
export function decodeShoppingListItems(list: components['schemas']['ListIngredientItem'][] | null | undefined): ShoppingListItemWithRefs[] {
|
||||
if (!Array.isArray(list)) return []
|
||||
// Build a new array with item clones to allow optional refs to be attached later
|
||||
return list.map((raw) => ({ ...decodeShoppingListItem(raw) }))
|
||||
}
|
||||
|
||||
export function decodeShoppingList(v: components['schemas']['ShoppingList'] | null | undefined): ShoppingList {
|
||||
export function decodeShoppingList(v: components['schemas']['ShoppingListOut'] | null | undefined): ShoppingList {
|
||||
if (!v) throw new Error('Invalid shopping list payload')
|
||||
const { items: rawItems, ...rest } = v
|
||||
const items = Array.isArray(rawItems) ? decodeShoppingListItems(rawItems) : undefined
|
||||
|
|
@ -95,6 +110,33 @@ export function decodeShoppingList(v: components['schemas']['ShoppingList'] | nu
|
|||
}
|
||||
}
|
||||
|
||||
// New item decoders for tightened CurrentShoppingList
|
||||
function decodeListIngredientItem(i: components['schemas']['ListIngredientItem'] | null | undefined): ListIngredientItem {
|
||||
if (!i) throw new Error('Invalid list ingredient item payload')
|
||||
return {
|
||||
...i,
|
||||
createdDate: toDate(i.createdDate),
|
||||
}
|
||||
}
|
||||
|
||||
export function decodeListIngredientItems(list: components['schemas']['ListIngredientItem'][] | null | undefined): ListIngredientItemWithRefs[] {
|
||||
if (!Array.isArray(list)) return []
|
||||
return list.map((raw) => ({ ...decodeListIngredientItem(raw) }))
|
||||
}
|
||||
|
||||
function decodeRequestedMealItem(i: components['schemas']['RequestedMealItem'] | null | undefined): RequestedMealItem {
|
||||
if (!i) throw new Error('Invalid requested meal item payload')
|
||||
return {
|
||||
...i,
|
||||
createdDate: toDate(i.createdDate),
|
||||
}
|
||||
}
|
||||
|
||||
export function decodeRequestedMealItems(list: components['schemas']['RequestedMealItem'][] | null | undefined): RequestedMealItemWithRefs[] {
|
||||
if (!Array.isArray(list)) return []
|
||||
return list.map((raw) => ({ ...decodeRequestedMealItem(raw) }))
|
||||
}
|
||||
|
||||
// Helper to convert domain Meal to MealInput, keeping Date→string conversion in boundary
|
||||
export function toMealInput(meal: Meal): MealInput {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -39,12 +39,19 @@ export type Meal = Replace<
|
|||
>
|
||||
|
||||
// Shopping domain shapes with dates normalized
|
||||
export type ShoppingListItem = WithDates<components['schemas']['ShoppingListItem'], 'createdDate'>
|
||||
type ShoppingListBase = WithDates<components['schemas']['ShoppingList'], 'createdDate'>
|
||||
// Items inside purchased lists and current lists share the ListIngredientItem shape
|
||||
export type ShoppingListItem = WithDates<components['schemas']['ListIngredientItem'], 'createdDate'>
|
||||
type ShoppingListBase = WithDates<components['schemas']['ShoppingListOut'], 'createdDate'>
|
||||
export type ShoppingList = Replace<ShoppingListBase, { items?: ShoppingListItem[] | undefined }>
|
||||
|
||||
// Refs attached to shopping list items
|
||||
// Current shopping list item types (tightened OpenAPI)
|
||||
export type ListIngredientItem = WithDates<components['schemas']['ListIngredientItem'], 'createdDate'>
|
||||
export type RequestedMealItem = WithDates<components['schemas']['RequestedMealItem'], 'createdDate'>
|
||||
|
||||
// Refs attached to items
|
||||
export type ShoppingListItemWithRefs = WithRefs<ShoppingListItem, { ingredient: Ingredient; recipe: Recipe; meal: Meal; list: ShoppingList }>
|
||||
export type ListIngredientItemWithRefs = WithRefs<ListIngredientItem, { ingredient: Ingredient; recipe: Recipe; meal: Meal; list: ShoppingList }>
|
||||
export type RequestedMealItemWithRefs = WithRefs<RequestedMealItem, { meal: Meal }>
|
||||
|
||||
export type ShoppingListWithRefs = Replace<ShoppingList, { items?: ShoppingListItemWithRefs[] }>
|
||||
|
||||
|
|
@ -58,9 +65,9 @@ export type ShoppingLookups = {
|
|||
|
||||
// DTO shapes returned by SDK for shopping pages
|
||||
export type CurrentShoppingListDTO = {
|
||||
outstandingItems: ShoppingListItemWithRefs[]
|
||||
requestedMeals: ShoppingListItemWithRefs[]
|
||||
purchasedItems: ShoppingListItemWithRefs[]
|
||||
outstandingItems: ListIngredientItemWithRefs[]
|
||||
requestedMeals: RequestedMealItemWithRefs[]
|
||||
purchasedItems: ListIngredientItemWithRefs[]
|
||||
} & ShoppingLookups
|
||||
|
||||
export type PurchasedShoppingListDTO = ShoppingLookups & {
|
||||
|
|
|
|||
Loading…
Reference in a new issue