fix: align frontend InventoryItem type with actual API response
InventoryItemResponse returns flat fields (product_name, barcode, category) not a nested product object. Frontend interface and templates were using item.product.name / item.product.brand which threw TypeError on render, blanking the inventory tab. - InventoryItem: remove product:Product, add product_name/barcode/category - InventoryStats: fix total_products→available_items, expired→expired_items, items_by_location→locations - item IDs are int not string — update deleteItem/updateItem/consumeItem sigs - EditItemModal, RecipesView, InventoryList: fix all item.product.xxx refs
This commit is contained in:
parent
e11f91e14d
commit
828efede42
5 changed files with 26 additions and 25 deletions
|
|
@ -10,8 +10,8 @@
|
|||
<div class="form-group">
|
||||
<label>Product</label>
|
||||
<div class="product-info">
|
||||
<strong>{{ item.product.name }}</strong>
|
||||
<span v-if="item.product.brand" class="brand">({{ item.product.brand }})</span>
|
||||
<strong>{{ item.product_name || 'Unknown Product' }}</strong>
|
||||
<span v-if="item.category" class="brand">{{ item.category }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
<div class="stat-label">Total Items</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">{{ stats.total_products }}</div>
|
||||
<div class="stat-label">Unique Products</div>
|
||||
<div class="stat-value">{{ stats.available_items }}</div>
|
||||
<div class="stat-label">Available</div>
|
||||
</div>
|
||||
<div class="stat-card expiry-soon">
|
||||
<div class="stat-value">{{ store.expiringItems.length }}</div>
|
||||
|
|
@ -263,8 +263,8 @@
|
|||
:class="getItemClass(item)"
|
||||
>
|
||||
<div class="item-header">
|
||||
<h3 class="item-name">{{ item.product.name }}</h3>
|
||||
<span v-if="item.product.brand" class="item-brand">{{ item.product.brand }}</span>
|
||||
<h3 class="item-name">{{ item.product_name || 'Unknown Product' }}</h3>
|
||||
<span v-if="item.category" class="item-brand">{{ item.category }}</span>
|
||||
</div>
|
||||
|
||||
<div class="item-details">
|
||||
|
|
@ -485,11 +485,11 @@ async function handleSave() {
|
|||
|
||||
async function confirmDelete(item: InventoryItem) {
|
||||
showConfirm(
|
||||
`Are you sure you want to delete ${item.product.name}?`,
|
||||
`Are you sure you want to delete ${item.product_name || 'item'}?`,
|
||||
async () => {
|
||||
try {
|
||||
await store.deleteItem(item.id)
|
||||
showToast(`${item.product.name} deleted successfully`, 'success')
|
||||
showToast(`${item.product_name || 'item'} deleted successfully`, 'success')
|
||||
} catch (err) {
|
||||
showToast('Failed to delete item', 'error')
|
||||
}
|
||||
|
|
@ -504,12 +504,12 @@ async function confirmDelete(item: InventoryItem) {
|
|||
|
||||
async function markAsConsumed(item: InventoryItem) {
|
||||
showConfirm(
|
||||
`Mark ${item.product.name} as consumed?`,
|
||||
`Mark ${item.product_name || 'item'} as consumed?`,
|
||||
async () => {
|
||||
try {
|
||||
await inventoryAPI.consumeItem(item.id)
|
||||
await refreshItems()
|
||||
showToast(`${item.product.name} marked as consumed`, 'success')
|
||||
showToast(`${item.product_name || 'item'} marked as consumed`, 'success')
|
||||
} catch (err) {
|
||||
showToast('Failed to mark item as consumed', 'error')
|
||||
}
|
||||
|
|
@ -542,7 +542,7 @@ async function handleScannerGunInput() {
|
|||
const item = result.results[0]
|
||||
scannerResults.value.push({
|
||||
type: 'success',
|
||||
message: `✓ Added: ${item.product.name}${item.product.brand ? ' (' + item.product.brand + ')' : ''} to ${scannerLocation.value}`,
|
||||
message: `✓ Added: ${item.product_name || 'item'}${''} to ${scannerLocation.value}`,
|
||||
})
|
||||
await refreshItems()
|
||||
} else {
|
||||
|
|
@ -588,7 +588,7 @@ async function handleBarcodeImageSelect(e: Event) {
|
|||
const item = result.results[0]
|
||||
barcodeResults.value.push({
|
||||
type: 'success',
|
||||
message: `✓ Found: ${item.product.name}${item.product.brand ? ' (' + item.product.brand + ')' : ''}`,
|
||||
message: `✓ Found: ${item.product_name || 'item'}${''}`,
|
||||
})
|
||||
await refreshItems()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ const pantryItems = computed(() => {
|
|||
if (!b.expiration_date) return -1
|
||||
return new Date(a.expiration_date).getTime() - new Date(b.expiration_date).getTime()
|
||||
})
|
||||
return sorted.map((item) => item.product.name)
|
||||
return sorted.map((item) => item.product_name).filter(Boolean) as string[]
|
||||
})
|
||||
|
||||
// Grocery links relevant to a specific recipe's missing ingredients
|
||||
|
|
|
|||
|
|
@ -80,9 +80,11 @@ export interface Tag {
|
|||
}
|
||||
|
||||
export interface InventoryItem {
|
||||
id: string
|
||||
product_id: string
|
||||
product: Product
|
||||
id: number
|
||||
product_id: number
|
||||
product_name: string | null
|
||||
barcode: string | null
|
||||
category: string | null
|
||||
quantity: number
|
||||
unit: string
|
||||
location: string
|
||||
|
|
@ -109,11 +111,10 @@ export interface InventoryItemUpdate {
|
|||
|
||||
export interface InventoryStats {
|
||||
total_items: number
|
||||
total_products: number
|
||||
available_items: number
|
||||
expiring_soon: number
|
||||
expired: number
|
||||
items_by_location: Record<string, number>
|
||||
items_by_status: Record<string, number>
|
||||
expired_items: number
|
||||
locations: Record<string, number>
|
||||
}
|
||||
|
||||
export interface Receipt {
|
||||
|
|
@ -185,7 +186,7 @@ export const inventoryAPI = {
|
|||
/**
|
||||
* Update an inventory item
|
||||
*/
|
||||
async updateItem(itemId: string, update: InventoryItemUpdate): Promise<InventoryItem> {
|
||||
async updateItem(itemId: number, update: InventoryItemUpdate): Promise<InventoryItem> {
|
||||
const response = await api.patch(`/inventory/items/${itemId}`, update)
|
||||
return response.data
|
||||
},
|
||||
|
|
@ -193,7 +194,7 @@ export const inventoryAPI = {
|
|||
/**
|
||||
* Delete an inventory item
|
||||
*/
|
||||
async deleteItem(itemId: string): Promise<void> {
|
||||
async deleteItem(itemId: number): Promise<void> {
|
||||
await api.delete(`/inventory/items/${itemId}`)
|
||||
},
|
||||
|
||||
|
|
@ -234,7 +235,7 @@ export const inventoryAPI = {
|
|||
/**
|
||||
* Mark item as consumed
|
||||
*/
|
||||
async consumeItem(itemId: string): Promise<void> {
|
||||
async consumeItem(itemId: number): Promise<void> {
|
||||
await api.post(`/inventory/items/${itemId}/consume`)
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ export const useInventoryStore = defineStore('inventory', () => {
|
|||
}
|
||||
}
|
||||
|
||||
async function updateItem(itemId: string, update: InventoryItemUpdate) {
|
||||
async function updateItem(itemId: number, update: InventoryItemUpdate) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ export const useInventoryStore = defineStore('inventory', () => {
|
|||
}
|
||||
}
|
||||
|
||||
async function deleteItem(itemId: string) {
|
||||
async function deleteItem(itemId: number) {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue