Some checks failed
Deploy to Coolify / Deploy to Development (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Production (pull_request) Has been cancelled
Deploy to Coolify / Code Quality (pull_request) Has been cancelled
Deploy to Coolify / Run Tests (pull_request) Has been cancelled
Deploy to Coolify / Deploy to Test (pull_request) Has been cancelled
Pull Request Checks / Validate PR (pull_request) Has been cancelled
Week 2 core inventory management: **Composables:** - useInventory: Full CRUD operations for inventory items - useUnits: Unit fetching and conversion helpers - useTags: Tag fetching and category filtering **Components:** - InventoryList (#18): Grid view with loading/empty/error states - InventoryCard: Item card with image, quantity controls, tags, expiry - AddItemForm (#19): Form with tag picker, unit selector, validation - EditItemModal (#20): Modal form for editing existing items - Delete functionality (#21): Confirm dialog + cascade tag cleanup **Features:** - Quantity quick-actions (+/- buttons on cards) - Auto-delete when quantity reaches zero - Expiry date tracking with color-coded warnings - Tag selection by category in add form - Responsive grid layout (1-4 columns) - Product image display from barcode cache - Form validation and loading states Closes #18, #19, #20, #21
132 lines
3.2 KiB
Vue
132 lines
3.2 KiB
Vue
<template>
|
|
<div class="space-y-4">
|
|
<!-- Loading State -->
|
|
<div v-if="loading" class="text-center py-12">
|
|
<UIcon name="i-heroicons-arrow-path" class="w-8 h-8 text-gray-400 animate-spin mx-auto mb-2" />
|
|
<p class="text-gray-600">Loading inventory...</p>
|
|
</div>
|
|
|
|
<!-- Error State -->
|
|
<div v-else-if="error" class="text-center py-12">
|
|
<UIcon name="i-heroicons-exclamation-triangle" class="w-12 h-12 text-red-500 mx-auto mb-4" />
|
|
<p class="text-red-600 mb-4">{{ error }}</p>
|
|
<UButton @click="loadInventory" color="gray">Retry</UButton>
|
|
</div>
|
|
|
|
<!-- Empty State -->
|
|
<div v-else-if="!items || items.length === 0" class="text-center py-12">
|
|
<UIcon name="i-heroicons-inbox" class="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-2">
|
|
No items yet
|
|
</h3>
|
|
<p class="text-gray-600 mb-6">
|
|
Start by scanning a barcode or adding an item manually.
|
|
</p>
|
|
<div class="flex gap-2 justify-center">
|
|
<UButton
|
|
to="/scan"
|
|
color="primary"
|
|
icon="i-heroicons-qr-code"
|
|
>
|
|
Scan First Item
|
|
</UButton>
|
|
<UButton
|
|
@click="$emit('add-item')"
|
|
color="white"
|
|
icon="i-heroicons-plus"
|
|
>
|
|
Add Manually
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Inventory Grid -->
|
|
<div v-else class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
|
<InventoryCard
|
|
v-for="item in items"
|
|
:key="item.id"
|
|
:item="item"
|
|
@edit="$emit('edit-item', item)"
|
|
@delete="handleDelete(item.id)"
|
|
@update-quantity="handleQuantityUpdate"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const { getInventory, deleteInventoryItem, updateQuantity } = useInventory()
|
|
|
|
const props = defineProps<{
|
|
refresh?: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'add-item': []
|
|
'edit-item': [item: any]
|
|
}>()
|
|
|
|
const items = ref<any[]>([])
|
|
const loading = ref(true)
|
|
const error = ref<string | null>(null)
|
|
|
|
const loadInventory = async () => {
|
|
loading.value = true
|
|
error.value = null
|
|
|
|
const { data, error: fetchError } = await getInventory()
|
|
|
|
if (fetchError) {
|
|
error.value = 'Failed to load inventory. Please try again.'
|
|
loading.value = false
|
|
return
|
|
}
|
|
|
|
items.value = data || []
|
|
loading.value = false
|
|
}
|
|
|
|
const handleDelete = async (id: string) => {
|
|
if (!confirm('Are you sure you want to delete this item?')) {
|
|
return
|
|
}
|
|
|
|
const { error: deleteError } = await deleteInventoryItem(id)
|
|
|
|
if (deleteError) {
|
|
alert('Failed to delete item')
|
|
return
|
|
}
|
|
|
|
// Remove from local list
|
|
items.value = items.value.filter(item => item.id !== id)
|
|
}
|
|
|
|
const handleQuantityUpdate = async (id: string, change: number) => {
|
|
const result = await updateQuantity(id, change)
|
|
|
|
if (result.error) {
|
|
alert('Failed to update quantity')
|
|
return
|
|
}
|
|
|
|
// Reload inventory after update
|
|
await loadInventory()
|
|
}
|
|
|
|
// Load on mount
|
|
onMounted(loadInventory)
|
|
|
|
// Watch for refresh prop
|
|
watch(() => props.refresh, (newVal) => {
|
|
if (newVal) {
|
|
loadInventory()
|
|
}
|
|
})
|
|
|
|
// Expose reload method
|
|
defineExpose({
|
|
reload: loadInventory
|
|
})
|
|
</script>
|