Image zoom works, save image works in both places.
This commit is contained in:
		
							parent
							
								
									b3a966e834
								
							
						
					
					
						commit
						c5640b54fd
					
				| 
						 | 
					@ -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}` }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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} 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue