Image zoom works, save image works in both places.

This commit is contained in:
Steve White 2024-10-11 17:10:06 -05:00
parent b3a966e834
commit c5640b54fd
2 changed files with 156 additions and 64 deletions

View File

@ -2,7 +2,7 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { TextField, Button, Container, Avatar } from '@mui/material'; import { TextField, Button, Container, Avatar } from '@mui/material';
import axios from 'axios'; import axios from 'axios';
import { useNavigate } from 'react-router-dom'; // Import useNavigate //import { useNavigate } from 'react-router-dom'; // Import useNavigate
export default function ItemDetails({ item, token, onSave, onClose, boxId }) { export default function ItemDetails({ item, token, onSave, onClose, boxId }) {
const [name, setName] = useState(item.name); const [name, setName] = useState(item.name);
@ -11,7 +11,7 @@ export default function ItemDetails({ item, token, onSave, onClose, boxId }) {
const [imageSrc, setImageSrc] = useState('/images/default.jpg'); // Initial default image const [imageSrc, setImageSrc] = useState('/images/default.jpg'); // Initial default image
const fileInputRef = useRef(null); // Add this line to define fileInputRef const fileInputRef = useRef(null); // Add this line to define fileInputRef
const [imageOverlayVisible, setImageOverlayVisible] = useState(false); const [imageOverlayVisible, setImageOverlayVisible] = useState(false);
const navigate = useNavigate(); // Initialize useNavigate // const navigate = useNavigate(); // Initialize useNavigate
useEffect(() => { useEffect(() => {
// Function to fetch image similar to getImageSrc in Items.js // Function to fetch image similar to getImageSrc in Items.js
@ -55,24 +55,25 @@ export default function ItemDetails({ item, token, onSave, onClose, boxId }) {
getImageSrc(item.ID).then(dataUrl => setImageSrc(dataUrl)); getImageSrc(item.ID).then(dataUrl => setImageSrc(dataUrl));
}, [item.ID, token]); }, [item.ID, token]);
const handleCloseItemDetails = () => { // const handleCloseItemDetails = () => {
onClose(); // Call the onClose prop to close the modal // onClose(); // Call the onClose prop to close the modal
navigate(`/boxes/${boxId}/items`); // Navigate back to the items list // navigate(`/boxes/${boxId}/items`); // Navigate back to the items list
}; // };
const handleImageUpload = async () => { const handleImageUpload = async () => {
const formData = new FormData(); const formData = new FormData();
formData.append('image', fileInputRef.current.files[0]); formData.append('image', fileInputRef.current.files[0]);
try { try {
await axios.post(`${process.env.REACT_APP_API_URL}/items/${item.ID}/upload`, formData, { const response = await axios.post(`${process.env.REACT_APP_API_URL}/items/${item.ID}/upload`, formData, {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'
} }
}); });
// Handle successful upload (e.g., show a success message) // Handle successful upload (e.g., show a success message)
console.log('Image uploaded successfully!'); console.log('Image uploaded successfully!', response.data.imagePath);
return response.data.imagePath; // Indicate successful upload
} catch (error) { } catch (error) {
// Handle upload error (e.g., show an error message) // Handle upload error (e.g., show an error message)
console.error('Image upload failed:', error); console.error('Image upload failed:', error);
@ -80,15 +81,16 @@ export default function ItemDetails({ item, token, onSave, onClose, boxId }) {
}; };
const handleSave = async () => { const handleSave = async () => {
let imagePath;
// 1. Handle image upload first if a new image is selected // 1. Handle image upload first if a new image is selected
if (fileInputRef.current.files[0]) { if (fileInputRef.current.files[0]) {
await handleImageUpload(); imagePath = await handleImageUpload();
} }
// 2. Update item details (name, description, etc.) // 2. Update item details (name, description, etc.)
try { try {
await axios.put(`${process.env.REACT_APP_API_URL}/items/${item.ID}`, await axios.put(`${process.env.REACT_APP_API_URL}/items/${item.ID}`,
{ name, description, image_path: imagePath }, // You might remove image_path here { name, description, image_path: imagePath }, // Use teh uploaded image path
{ {
headers: { Authorization: `Bearer ${token}` } headers: { Authorization: `Bearer ${token}` }
} }

View File

@ -10,6 +10,10 @@ import {
Typography, Typography,
Avatar, Avatar,
ListItemAvatar, ListItemAvatar,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
} from '@mui/material'; } from '@mui/material';
import { Delete as DeleteIcon, Edit as EditIcon } from '@mui/icons-material'; import { Delete as DeleteIcon, Edit as EditIcon } from '@mui/icons-material';
import axios from 'axios'; import axios from 'axios';
@ -22,19 +26,94 @@ export default function Items({ token }) {
const [items, setItems] = useState([]); const [items, setItems] = useState([]);
const [newItemName, setNewItemName] = useState(''); const [newItemName, setNewItemName] = useState('');
const [newItemDescription, setNewItemDescription] = useState(''); const [newItemDescription, setNewItemDescription] = useState('');
const [newItemImagePath, setNewItemImagePath] = useState('/images/default.jpg'); // const [newItemImagePath, setNewItemImagePath] = useState('/images/default.jpg');
const [editingItem, setEditingItem] = useState(null); const [editingItem, setEditingItem] = useState(null);
const location = useLocation(); const location = useLocation();
const boxName = location.state?.boxName || 'Unknown Box'; const boxName = location.state?.boxName || 'Unknown Box';
const [itemImages, setItemImages] = useState({}); const [itemImages, setItemImages] = useState({});
const fileInputRef = useRef(null); const fileInputRef = useRef(null);
const [openAddItemDialog, setOpenAddItemDialog] = useState(false); // For Add Item dialog
// const handleSelectItem = (item) => { // const handleSelectItem = (item) => {
// setSelectedItem(item); // setSelectedItem(item);
// }; // };
const handleAddItem = () => {
setOpenAddItemDialog(true);
};
const [selectedItem, setSelectedItem] = React.useState(null); const handleCloseAddItemDialog = () => {
setOpenAddItemDialog(false);
setNewItemName('');
setNewItemDescription('');
// setNewItemImagePath('');
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
const handleImageUpload = async (itemId, imageFile) => {
const formData = new FormData();
formData.append('image', imageFile);
try {
const response = await axios.post(`${process.env.REACT_APP_API_URL}/items/${itemId}/upload`, formData, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'multipart/form-data'
}
});
console.log('Image uploaded successfully!');
return response.data.imagePath; // Indicate successful upload
} catch (error) {
console.error('Image upload failed:', error);
return null; // Indicate upload failure
}
};
const handleSaveNewItem = async () => {
try {
// 1. Create the item first
const newItemResponse = await axios.post(`${process.env.REACT_APP_API_URL}/items`, {
name: newItemName,
description: newItemDescription,
box_id: parseInt(boxId, 10)
}, {
headers: {
Authorization: `Bearer ${token}`
}
});
console.log('New item created:', newItemResponse.status);
// 2. If item creation is successful, upload the image
if (newItemResponse.status === 200 && fileInputRef.current.files[0]) {
const newItemId = newItemResponse.data.id;
const uploadedImagePath = await handleImageUpload(newItemId, fileInputRef.current.files[0]);
if (uploadedImagePath) {
console.log("Image path to save:", uploadedImagePath);
// You might want to update your item in the backend with the image path
// For example:
// await axios.put(...);
} else {
// Handle image upload failure
console.error('Failed to upload image for the new item.');
}
}
handleCloseAddItemDialog();
fetchItems();
} catch (error) {
console.error('Error adding item:', error);
}
};
//const [selectedItem, setSelectedItem] = React.useState(null);
const handleCloseItemDetails = () => { const handleCloseItemDetails = () => {
setEditingItem(null); // Close the ItemDetails modal setEditingItem(null); // Close the ItemDetails modal
@ -112,32 +191,32 @@ export default function Items({ token }) {
fetchItems(); fetchItems();
}, [boxId, token, fetchItems]); }, [boxId, token, fetchItems]);
const handleAddItem = () => { // const handleAddItem = () => {
const formData = new FormData(); // const formData = new FormData();
formData.append('name', newItemName); // formData.append('name', newItemName);
formData.append('description', newItemDescription); // formData.append('description', newItemDescription);
formData.append('box_id', parseInt(boxId, 10)); // formData.append('box_id', parseInt(boxId, 10));
// Append image only if a new one is selected // // Append image only if a new one is selected
if (fileInputRef.current.files[0]) { // if (fileInputRef.current.files[0]) {
formData.append('image', fileInputRef.current.files[0]); // formData.append('image', fileInputRef.current.files[0]);
} // }
axios.post(`${process.env.REACT_APP_API_URL}/items`, formData, { // axios.post(`${process.env.REACT_APP_API_URL}/items`, formData, {
headers: { // headers: {
Authorization: `Bearer ${token}`, // Authorization: `Bearer ${token}`,
'Content-Type': 'multipart/form-data' // Important for file uploads // 'Content-Type': 'multipart/form-data' // Important for file uploads
} // }
}).then(() => { // }).then(() => {
setNewItemName(''); // setNewItemName('');
setNewItemDescription(''); // setNewItemDescription('');
setNewItemImagePath(''); // setNewItemImagePath('');
// Clear the file input // // Clear the file input
if (fileInputRef.current) { // if (fileInputRef.current) {
fileInputRef.current.value = ''; // fileInputRef.current.value = '';
} // }
fetchItems(); // fetchItems();
}); // });
}; // };
const handleDeleteItem = (itemId) => { const handleDeleteItem = (itemId) => {
axios.delete(`${process.env.REACT_APP_API_URL}/items/${itemId}`, { axios.delete(`${process.env.REACT_APP_API_URL}/items/${itemId}`, {
@ -160,6 +239,25 @@ export default function Items({ token }) {
<Container> <Container>
<h2>Items in Box: {boxName}</h2> <h2>Items in Box: {boxName}</h2>
<input
type="file"
accept="image/*"
ref={fileInputRef}
style={{ display: 'none' }}
id="newItemImageUpload" // Unique ID
/>
<Button variant="contained" color="primary" onClick={handleAddItem}>
Add Item
</Button>
<Button variant="contained" color="primary" onClick={handleAddItem}>
Add Item
</Button>
{/* Dialog for adding new item */}
<Dialog open={openAddItemDialog} onClose={handleCloseAddItemDialog}>
<DialogTitle>Add New Item</DialogTitle>
<DialogContent>
<TextField <TextField
label="Item Name" label="Item Name"
variant="outlined" variant="outlined"
@ -176,29 +274,21 @@ export default function Items({ token }) {
value={newItemDescription} value={newItemDescription}
onChange={(e) => setNewItemDescription(e.target.value)} onChange={(e) => setNewItemDescription(e.target.value)}
/> />
<TextField
label="Item Image Path"
variant="outlined"
fullWidth
margin="normal"
value={newItemImagePath}
onChange={(e) => setNewItemImagePath(e.target.files[0].name)}
/>
<input <input
type="file" type="file"
accept="image/*" accept="image/*"
ref={fileInputRef} ref={fileInputRef}
style={{ display: 'none' }} style={{ display: 'block', margin: '10px 0' }} // Style as needed
id="newItemImageUpload" // Unique ID id="newItemImageUpload"
/> />
<Button variant="contained" component="label" htmlFor="newItemImageUpload"> </DialogContent>
Upload Image <DialogActions>
{/* ... rest of your button code ... */} <Button onClick={handleCloseAddItemDialog}>Cancel</Button>
<Button onClick={handleSaveNewItem} color="primary">
Save
</Button> </Button>
<Button variant="contained" color="primary" onClick={handleAddItem}> </DialogActions>
Add Item </Dialog>
</Button>
{editingItem ? ( {editingItem ? (
<ItemDetails <ItemDetails
item={editingItem} item={editingItem}