diff --git a/src/components/Dashboard/ManageItems.tsx b/src/components/Dashboard/ManageItems.tsx index 1b6b967..5ffd0db 100644 --- a/src/components/Dashboard/ManageItems.tsx +++ b/src/components/Dashboard/ManageItems.tsx @@ -25,21 +25,36 @@ export default function ManageItems() { name: string; description: string; image_url: string; + quantity: number; + price: number; } const [items, setItems] = useState([]); const { isLoggedIn, authToken } = useLoginStore.getState(); const apiUrl = import.meta.env.VITE_API_URL; const schema = yup.object().shape({ name: yup.string().required('Store name is required'), + price: yup + .number() + .required('Price is required') + .positive('Price must be positive'), }); const [formValues, setFormValues] = useState({ name: '', description: '', + image_url: '', + quantity: 0, + price: 0, }); + + const [imageFile, setImageFile] = useState(null); const [success, setSuccess] = useState(null); const [error, setError] = useState<{ [key: string]: string | null }>({}); - useEffect(() => { - const fetchItemsData = async () => { + const [open, setOpen] = useState(false); + const [isEditMode, setIsEditMode] = useState(false); + const [itemToEdit, setItemToEdit] = useState(null); + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [itemToDelete, setItemToDelete] = useState(null); +const fetchItemsData = async () => { try { const response = await axios.get(`${apiUrl}/api/stores/items`, { headers: { @@ -53,36 +68,161 @@ export default function ManageItems() { console.error('Error fetching store data', err); } }; + useEffect(() => { + fetchItemsData(); }, [apiUrl, authToken]); - - const [open, setOpen] = useState(false); const openCreateItemDialog = () => { - setSuccess(''); - setError({}); + resetForm(); setOpen(true); }; const closeCreateItemDialog = () => { + resetForm(); setOpen(false); }; - const handleEdit = () => {}; // TODO: handleEdit - const handleDelete = () => {}; // TODO: handleDelete + const handleEdit = (item: Item) => { + setItemToEdit(item); + setFormValues({ + name: item.name, + description: item.description, + image_url: item.image_url, + quantity: item.quantity, + price: item.price, + }); + setIsEditMode(true); + setOpen(true); + }; + + + const openDeleteDialog = (item: Item) => { + setItemToDelete(item); + setDeleteDialogOpen(true); + }; + + const closeDeleteDialog = () => { + setDeleteDialogOpen(false); + setItemToDelete(null); + }; + + const handleDelete = async () => { + if (itemToDelete) { + try { + await axios.delete( + `${apiUrl}/api/stores/items/${itemToDelete.item_id}`, + { + headers: { + Authorization: `Bearer ${authToken}`, + }, + } + ); + setItems((prevItems) => + prevItems.filter( + (item) => item.item_id !== itemToDelete.item_id + ) + ); + setSuccess('Item deleted successfully!'); + } catch (err) { + console.error('Error deleting item', err); + setError({ + general: 'An error occurred while deleting the item.', + }); + } finally { + closeDeleteDialog(); + } + } + }; + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + + setFormValues((prev) => ({ + ...prev, + [name]: + (name === 'price' || name === 'quantity') && value === '' + ? 0 + : value, + })); + + setError((prev) => ({ + ...prev, + [name]: null, + })); + }; const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); try { await schema.validate(formValues, { abortEarly: false }); - await axios.post(`${apiUrl}/api/stores/items`, formValues, { - headers: { - Authorization: `Bearer ${authToken}`, - }, - }); + const formData = new FormData(); + formData.append('name', formValues.name); + formData.append('description', formValues.description); + formData.append('quantity', formValues.quantity.toString()); + + if (imageFile) { + formData.append('file', imageFile); + } + + const uploadResponse = imageFile + ? await axios.post(`${apiUrl}/api/files/upload`, formData, { + headers: { + Authorization: `Bearer ${authToken}`, + 'Content-Type': 'multipart/form-data', + }, + }) + : null; + + const imageUrl = uploadResponse ? uploadResponse.data.url : null; + + if (isEditMode && itemToEdit) { + // Update existing item + await axios.put( + `${apiUrl}/api/stores/items/${itemToEdit.item_id}`, + { + name: formValues.name, + description: formValues.description, + image_url: imageUrl, + price: formValues.price, + quantity: formValues.quantity, + }, + { + headers: { + Authorization: `Bearer ${authToken}`, + }, + } + ); + + setItems((prevItems) => + prevItems.map((item) => + item.item_id === itemToEdit.item_id + ? { ...item, ...formValues, image_url: imageUrl || item.image_url} + : item + ) + ); + + setSuccess('Item updated successfully!'); + } else { + // Create new item + await axios.post( + `${apiUrl}/api/stores/items`, + { + ...formValues, + image_url: imageUrl, + }, + { + headers: { + Authorization: `Bearer ${authToken}`, + }, + } + ); + resetForm(); + setSuccess('Item created successfully! Close the window'); + fetchItemsData(); + } - setSuccess('Item created successfully! Close the window'); setError({}); } catch (err) { - let messageError = 'An error occurred while creating an item.'; + let messageError = 'An error occurred while saving the item.'; if (axios.isAxiosError(err)) { messageError = err.response?.data?.message || 'An error occurred.'; @@ -95,31 +235,42 @@ export default function ManageItems() { return; } setError({ general: messageError }); + } finally { + closeCreateItemDialog(); + setIsEditMode(false); + setItemToEdit(null); } }; - - const handleChange = (e: React.ChangeEvent) => { - const { name, value } = e.target; - setFormValues((prev) => ({ ...prev, [name]: value })); - setError((prev) => ({ ...prev, [name]: null })); + const resetForm = () => { + setFormValues({ + name: '', + description: '', + image_url: '', + quantity: 0, + price: 0, + }); + setImageFile(null); + setSuccess(null); + setError({}); }; return ( Manage Items {isLoggedIn ? ( <> - + @@ -176,7 +334,7 @@ export default function ManageItems() { ) : ( - No items found. + No items found. )} - Create an item + + {isEditMode ? 'Edit item' : 'Create an item'} + {error.general && ( - + {error.general} )} {success && ( - + {success} )} + + Item Image (JPG, GIF, PNG, max 10 MB) + + + + {imageFile && ( + {imageFile.name} + )} + + + + + + + + + - + + Delete Item + + + Are you sure you want to delete this item? + + + + + + + ) : ( 'Please log in'