@@ -11,6 +11,8 @@ import { Button } from '@/components/ui/button';
1111import Select from ' @/components/Select.vue' ;
1212import Textarea from ' @/components/ui/textarea/Textarea.vue' ;
1313import { Spinner } from ' @/components/ui/spinner' ;
14+ import { X , Upload } from ' lucide-vue-next' ;
15+ import { useImagePreviews } from ' @/composables/useImagePreviews' ;
1416
1517const breadcrumbs: BreadcrumbItem [] = [
1618 {
@@ -33,6 +35,8 @@ const categories = [
3335 ' Paint and Finishes' ,
3436 ' Chemicals'
3537];
38+
39+ const { imagePreviews, handleImageSelect, removeImage } = useImagePreviews (3 );
3640 </script >
3741
3842<template >
@@ -41,7 +45,7 @@ const categories = [
4145
4246 <AppLayout :breadcrumbs =" breadcrumbs" >
4347 <div class =" flex h-full flex-1 flex-col gap-4 overflow-x-auto rounded-xl p-4" >
44- <div class =" flex justify-between items-center" >
48+ <div class =" flex justify-between items-center mb-4 " >
4549 <div >
4650 <h1 class =" text-2xl font-bold" >Add New Inventory Item</h1 >
4751 <p class =" text-sm text-muted-foreground" >Fill in the details to add a new item to the inventory.</p >
@@ -66,21 +70,21 @@ const categories = [
6670 <div class =" grid grid-cols-1 md:grid-cols-2 gap-4" >
6771 <!-- Item Name -->
6872 <div class =" space-y-2" >
69- <Label for =" item_name" >Item Name</Label >
73+ <Label for =" item_name" >Item Name<span class = " text-red-500 " >*</ span >< /Label >
7074 <Input id =" item_name" name =" item_name" placeholder =" Enter item name" required />
7175 <InputError :message =" errors.item_name" />
7276 </div >
7377
7478 <!-- Brand Name -->
7579 <div class =" space-y-2" >
76- <Label for =" brand_name" >Brand Name</Label >
80+ <Label for =" brand_name" >Brand Name<span class = " text-red-500 " >*</ span >< /Label >
7781 <Input id =" brand_name" name =" brand_name" placeholder =" Enter brand name" />
7882 <InputError :message =" errors.brand_name" />
7983 </div >
8084
8185 <!-- Category -->
8286 <div class =" space-y-2" >
83- <Label for =" category" >Category</Label >
87+ <Label for =" category" >Category<span class = " text-red-500 " >*</ span >< /Label >
8488 <Select name =" category" :options =" categories" placeholder =" Select a category" required />
8589 <InputError :message =" errors.category" />
8690 </div >
@@ -94,23 +98,27 @@ const categories = [
9498
9599 <!-- Unit Price -->
96100 <div class =" space-y-2" >
97- <Label for =" unit_price" >Unit Price</Label >
101+ <Label for =" unit_price" >Unit Price<span class = " text-red-500 " >*</ span >< /Label >
98102 <Input id =" unit_price" name =" unit_price" type =" number" step =" 0.01" placeholder =" 0.00"
103+ class =" [& ::-webkit-outer-spin-button]:appearance-none [& ::-webkit-inner-spin-button]:appearance-none"
99104 required />
100105 <InputError :message =" errors.unit_price" />
101106 </div >
102107
103108 <!-- Quantity -->
104109 <div class =" space-y-2" >
105- <Label for =" quantity" >Quantity</Label >
106- <Input id =" quantity" name =" quantity" type =" number" placeholder =" 0" required />
110+ <Label for =" quantity" >Quantity<span class =" text-red-500" >*</span ></Label >
111+ <Input id =" quantity" name =" quantity" type =" number" placeholder =" 0"
112+ class =" [& ::-webkit-outer-spin-button]:appearance-none [& ::-webkit-inner-spin-button]:appearance-none"
113+ required />
107114 <InputError :message =" errors.quantity" />
108115 </div >
109116
110117 <!-- Restock Threshold -->
111118 <div class =" space-y-2" >
112- <Label for =" restock_threshold" >Restock Threshold</Label >
113- <Input id =" restock_threshold" name =" restock_threshold" type =" number" placeholder =" 10" />
119+ <Label for =" restock_threshold" >Restock Threshold<span class =" text-red-500" >*</span ></Label >
120+ <Input id =" restock_threshold" name =" restock_threshold" type =" number" placeholder =" 10"
121+ class =" [& ::-webkit-outer-spin-button]:appearance-none [& ::-webkit-inner-spin-button]:appearance-none" />
114122 <InputError :message =" errors.restock_threshold" />
115123 </div >
116124 </div >
@@ -122,22 +130,81 @@ const categories = [
122130 <InputError :message =" errors.description" />
123131 </div >
124132
125- <!-- Images -->
126- <div class =" grid grid-cols-1 md:grid-cols-3 gap-4" >
127- <div class =" space-y-2" >
128- <Label for =" item_image_1" >Image 1</Label >
129- <Input id =" item_image_1" name =" item_image_1" type =" file" accept =" image/*" />
130- <InputError :message =" errors.item_image_1" />
131- </div >
132- <div class =" space-y-2" >
133- <Label for =" item_image_2" >Image 2</Label >
134- <Input id =" item_image_2" name =" item_image_2" type =" file" accept =" image/*" />
135- <InputError :message =" errors.item_image_2" />
136- </div >
137- <div class =" space-y-2" >
138- <Label for =" item_image_3" >Image 3</Label >
139- <Input id =" item_image_3" name =" item_image_3" type =" file" accept =" image/*" />
140- <InputError :message =" errors.item_image_3" />
133+ <!-- Images with Preview -->
134+ <div class =" space-y-2" >
135+ <Label >Product Images</Label >
136+ <div class =" grid grid-cols-1 md:grid-cols-3 gap-4" >
137+ <!-- Image 1 -->
138+ <div class =" space-y-2" >
139+ <div class =" relative group" >
140+ <div v-if =" imagePreviews[0]"
141+ class =" relative aspect-square rounded-lg border-2 border-border overflow-hidden bg-muted" >
142+ <img :src =" imagePreviews[0]" alt =" Preview 1" class =" w-full h-full object-cover" />
143+ <button type =" button" @click =" removeImage(0, 'item_image_1')"
144+ class =" absolute top-2 right-2 p-1.5 bg-destructive text-destructive-foreground rounded-full shadow-lg hover:bg-destructive/90 transition-colors"
145+ aria-label =" Remove image 1" >
146+ <X :size =" 16" />
147+ </button >
148+ </div >
149+ <label v-else for =" item_image_1"
150+ class =" flex flex-col items-center justify-center aspect-square rounded-lg border-2 border-dashed border-border hover:border-primary hover:bg-accent cursor-pointer transition-colors" >
151+ <Upload :size =" 32" class =" text-muted-foreground mb-2" />
152+ <span class =" text-sm text-muted-foreground" >Upload Image 1</span >
153+ <span class =" text-xs text-muted-foreground mt-1" >Click to browse</span >
154+ </label >
155+ <Input id =" item_image_1" name =" item_image_1" type =" file" accept =" image/*" class =" hidden"
156+ @change =" (e: Event) => handleImageSelect(e, 0)" />
157+ </div >
158+ <InputError :message =" errors.item_image_1" />
159+ </div >
160+
161+ <!-- Image 2 -->
162+ <div class =" space-y-2" >
163+ <div class =" relative group" >
164+ <div v-if =" imagePreviews[1]"
165+ class =" relative aspect-square rounded-lg border-2 border-border overflow-hidden bg-muted" >
166+ <img :src =" imagePreviews[1]" alt =" Preview 2" class =" w-full h-full object-cover" />
167+ <button type =" button" @click =" removeImage(1, 'item_image_2')"
168+ class =" absolute top-2 right-2 p-1.5 bg-destructive text-destructive-foreground rounded-full shadow-lg hover:bg-destructive/90 transition-colors"
169+ aria-label =" Remove image 2" >
170+ <X :size =" 16" />
171+ </button >
172+ </div >
173+ <label v-else for =" item_image_2"
174+ class =" flex flex-col items-center justify-center aspect-square rounded-lg border-2 border-dashed border-border hover:border-primary hover:bg-accent cursor-pointer transition-colors" >
175+ <Upload :size =" 32" class =" text-muted-foreground mb-2" />
176+ <span class =" text-sm text-muted-foreground" >Upload Image 2 (optional)</span >
177+ <span class =" text-xs text-muted-foreground mt-1" >Click to browse</span >
178+ </label >
179+ <Input id =" item_image_2" name =" item_image_2" type =" file" accept =" image/*" class =" hidden"
180+ @change =" (e: Event) => handleImageSelect(e, 1)" />
181+ </div >
182+ <InputError :message =" errors.item_image_2" />
183+ </div >
184+
185+ <!-- Image 3 -->
186+ <div class =" space-y-2" >
187+ <div class =" relative group" >
188+ <div v-if =" imagePreviews[2]"
189+ class =" relative aspect-square rounded-lg border-2 border-border overflow-hidden bg-muted" >
190+ <img :src =" imagePreviews[2]" alt =" Preview 3" class =" w-full h-full object-cover" />
191+ <button type =" button" @click =" removeImage(2, 'item_image_3')"
192+ class =" absolute top-2 right-2 p-1.5 bg-destructive text-destructive-foreground rounded-full shadow-lg hover:bg-destructive/90 transition-colors"
193+ aria-label =" Remove image 3" >
194+ <X :size =" 16" />
195+ </button >
196+ </div >
197+ <label v-else for =" item_image_3"
198+ class =" flex flex-col items-center justify-center aspect-square rounded-lg border-2 border-dashed border-border hover:border-primary hover:bg-accent cursor-pointer transition-colors" >
199+ <Upload :size =" 32" class =" text-muted-foreground mb-2" />
200+ <span class =" text-sm text-muted-foreground" >Upload Image 3 (optional)</span >
201+ <span class =" text-xs text-muted-foreground mt-1" >Click to browse</span >
202+ </label >
203+ <Input id =" item_image_3" name =" item_image_3" type =" file" accept =" image/*" class =" hidden"
204+ @change =" (e: Event) => handleImageSelect(e, 2)" />
205+ </div >
206+ <InputError :message =" errors.item_image_3" />
207+ </div >
141208 </div >
142209 </div >
143210
0 commit comments