added default.jpg and fixed image display code

This commit is contained in:
Steve White 2024-10-10 17:15:06 -05:00
parent 88ad1e7e1b
commit 3a54c5da58
3 changed files with 132 additions and 18 deletions

BIN
public/default.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

View File

@ -1,12 +1,55 @@
// src/components/ItemDetails.js // src/components/ItemDetails.js
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { TextField, Button, Container } from '@mui/material'; import { TextField, Button, Container, Avatar } from '@mui/material';
import axios from 'axios'; import axios from 'axios';
export default function ItemDetails({ item, token, onSave }) { export default function ItemDetails({ item, token, onSave }) {
const [name, setName] = useState(item.name); const [name, setName] = useState(item.name);
const [description, setDescription] = useState(item.description); const [description, setDescription] = useState(item.description);
const [imagePath, setImagePath] = useState(item.image_path || ''); const [imagePath, setImagePath] = useState(item.image_path || '');
const [imageSrc, setImageSrc] = useState('/images/default.jpg'); // Initial default image
useEffect(() => {
// Function to fetch image similar to getImageSrc in Items.js
const getImageSrc = (itemId) => {
return axios.get(`${process.env.REACT_APP_API_URL}/items/${itemId}/image`, {
headers: { Authorization: `Bearer ${token}` },
responseType: 'blob'
})
.then(response => {
if (response.status === 200) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(response.data);
});
} else {
throw new Error('Image fetch failed');
}
})
.catch(() => {
// Return the data URL of the default image if image fetch fails
return new Promise((resolve, reject) => {
const img = new Image();
img.src = '/default.jpg';
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const dataURL = canvas.toDataURL();
resolve(dataURL);
};
img.onerror = reject;
});
});
};
// Fetch the image when the component mounts or the item changes
getImageSrc(item.ID).then(dataUrl => setImageSrc(dataUrl));
}, [item.ID, token]);
const handleSave = () => { const handleSave = () => {
axios.put(`${process.env.REACT_APP_API_URL}/items/${item.ID}`, axios.put(`${process.env.REACT_APP_API_URL}/items/${item.ID}`,
@ -19,9 +62,22 @@ export default function ItemDetails({ item, token, onSave }) {
}); });
}; };
const handleImageError = (e) => {
e.target.src = '/images/default.jpg'; // Fallback to default image on error
};
return ( return (
<Container> <Container>
<h3>Edit Item: {item.name}</h3> <h3>Edit Item: {item.name}</h3>
{/* Display the item image as an avatar */}
<Avatar
src={imageSrc}
alt={name}
onError={handleImageError}
sx={{ width: 100, height: 100, marginBottom: '16px' }} // Style the Avatar
/>
<TextField <TextField
label="Item Name" label="Item Name"
variant="outlined" variant="outlined"

View File

@ -8,13 +8,13 @@ import {
Button, Button,
IconButton, IconButton,
Typography, Typography,
Avatar, // Import Avatar for image display Avatar,
ListItemAvatar // Import ListItemAvatar ListItemAvatar
} 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';
import { useParams, useLocation } from 'react-router-dom'; import { useParams, useLocation } from 'react-router-dom';
import ItemDetails from './ItemDetails'; // Import the new ItemDetails component import ItemDetails from './ItemDetails';
export default function Items({ token }) { export default function Items({ token }) {
const { id: boxId } = useParams(); const { id: boxId } = useParams();
@ -22,16 +22,76 @@ export default function Items({ token }) {
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); // Track which item is being edited 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 handleImageError = (e) => {
if (e.target.src.startsWith('data:image/')) {
console.error("Default image failed to load. Check the file path.");
return;
}
const reader = new FileReader();
reader.onload = () => {
e.target.onerror = null;
e.target.src = reader.result;
};
fetch('/default.jpg')
.then(res => res.blob())
.then(blob => reader.readAsDataURL(blob))
.catch(error => console.error("Error loading default image:", error));
};
const getImageSrc = (itemId) => {
return axios.get(`${process.env.REACT_APP_API_URL}/items/${itemId}/image`, {
headers: { Authorization: `Bearer ${token}` },
responseType: 'blob'
})
.then(response => {
if (response.status === 200) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(response.data);
});
} else {
throw new Error('Image fetch failed');
}
})
.catch(() => {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = '/default.jpg';
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
resolve(canvas.toDataURL());
};
img.onerror = reject;
});
});
};
const fetchItems = useCallback(() => { const fetchItems = useCallback(() => {
axios.get(`${process.env.REACT_APP_API_URL}/boxes/${boxId}/items`, { axios.get(`${process.env.REACT_APP_API_URL}/boxes/${boxId}/items`, {
headers: { Authorization: `Bearer ${token}` } headers: { Authorization: `Bearer ${token}` }
}).then(response => { }).then(response => {
setItems(response.data); setItems(response.data);
// Fetch images for each item
response.data.forEach(item => {
getImageSrc(item.ID).then(imageDataUrl => {
setItemImages(prevItemImages => ({
...prevItemImages,
[item.ID]: imageDataUrl
}));
});
});
}); });
}, [boxId, token]); }, [boxId, token]);
@ -66,19 +126,18 @@ export default function Items({ token }) {
}; };
const handleEditItem = (item) => { const handleEditItem = (item) => {
setEditingItem(item); // Set the item to be edited setEditingItem(item);
}; };
const handleSaveEdit = () => { const handleSaveEdit = () => {
setEditingItem(null); // Clear the editing state after saving setEditingItem(null);
fetchItems(); // Refresh the list after edit fetchItems();
}; };
return ( return (
<Container> <Container>
<h2>Items in Box: {boxName}</h2> <h2>Items in Box: {boxName}</h2>
{/* Add Item Form */}
<TextField <TextField
label="Item Name" label="Item Name"
variant="outlined" variant="outlined"
@ -107,7 +166,6 @@ export default function Items({ token }) {
Add Item Add Item
</Button> </Button>
{/* Conditionally render the ItemDetails component if editingItem is not null */}
{editingItem ? ( {editingItem ? (
<ItemDetails item={editingItem} token={token} onSave={handleSaveEdit} /> <ItemDetails item={editingItem} token={token} onSave={handleSaveEdit} />
) : ( ) : (
@ -123,13 +181,13 @@ export default function Items({ token }) {
</IconButton> </IconButton>
</> </>
}> }>
<ListItemAvatar> <ListItemAvatar>
<Avatar <Avatar
src= {axios.get(`${process.env.REACT_APP_API_URL}/items/${+item.ID}/image`, { src={itemImages[item.ID]}
headers: { Authorization: `Bearer ${token}` }})} alt={item.name}
alt={item.name} onError={handleImageError}
/> />
</ListItemAvatar> </ListItemAvatar>
<ListItemText <ListItemText
primary={item.name} primary={item.name}
secondary={ secondary={