diff --git a/src/App.js b/src/App.js
index d8b0484..52848b8 100644
--- a/src/App.js
+++ b/src/App.js
@@ -8,6 +8,7 @@ import Items from './components/Items';
import Navbar from './components/Navbar'; // Correct import here
import Admin from './components/Admin'; // Correct import here
import { createContext } from 'react';
+import ErrorBoundary from './components/ErrorBoundary';
import './styles.css'
export const AppContext = createContext();
@@ -26,10 +27,12 @@ function App() {
return (
-
-
-
-
+
+
+
+
+
+
);
}
diff --git a/src/components/ErrorBoundary.js b/src/components/ErrorBoundary.js
new file mode 100644
index 0000000..09bb2d1
--- /dev/null
+++ b/src/components/ErrorBoundary.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { Alert, Button, Container } from '@mui/material';
+
+class ErrorBoundary extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { hasError: false, error: null };
+ }
+
+ static getDerivedStateFromError(error) {
+ return { hasError: true, error };
+ }
+
+ render() {
+ if (this.state.hasError) {
+ return (
+
+
+ Something went wrong
+
+
+
+ );
+ }
+ return this.props.children;
+ }
+}
+
+export default ErrorBoundary;
\ No newline at end of file
diff --git a/src/components/Items.js b/src/components/Items.js
index 74a6025..8af44ee 100644
--- a/src/components/Items.js
+++ b/src/components/Items.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useState, useRef } from 'react';
+import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
import {
Container,
Button,
@@ -27,6 +27,30 @@ import { useApiCall } from './hooks/useApiCall';
import { api } from '../services/api';
import ItemDetails from './ItemDetails';
+const Item = React.memo(({ item, onDelete, onEdit, itemImages }) => (
+
+
+
+
+ {item.name}
+ {item.description}
+
+
+
+ onEdit(item)} size="large" sx={{ mr: 1 }}>
+
+
+
+
+ onDelete(item.ID)} size="large" color="error">
+
+
+
+
+
+
+));
+
export default function Items({ token }) {
const { id: boxId } = useParams();
const [items, setItems] = useState([]);
@@ -53,7 +77,7 @@ export default function Items({ token }) {
const getItems = async () => {
try {
const response = await fetchItems(() =>
- api.items.getAll(token, boxId)
+ boxId ? api.items.getByBox(token, boxId) : api.items.getAll(token)
);
setItems(response.data);
@@ -103,7 +127,7 @@ export default function Items({ token }) {
return imageName;
};
- const handleSaveNewItem = async () => {
+ const handleSaveNewItem = useCallback(async () => {
try {
const newItemResponse = await createItem(() =>
api.items.create(token, {
@@ -112,7 +136,7 @@ export default function Items({ token }) {
box_id: parseInt(boxId, 10)
})
);
-
+
if (newItemResponse.status === 200 && fileInputRef.current?.files?.[0]) {
const imageFile = fileInputRef.current.files[0];
const newImageName = generateUniqueImageName(imageFile.name);
@@ -120,34 +144,49 @@ export default function Items({ token }) {
formData.append('image', new File([imageFile], newImageName, {
type: imageFile.type,
}));
-
+
await uploadImage(() =>
api.items.uploadImage(token, newItemResponse.data.id, formData)
);
+ if (newItemResponse.data.id) {
+ try {
+ const imageResponse = await api.items.getImage(token, newItemResponse.data.id);
+ const reader = new FileReader();
+ reader.onload = () => {
+ setItemImages(prev => ({
+ ...prev,
+ [newItemResponse.data.id]: reader.result
+ }));
+ };
+ reader.readAsDataURL(imageResponse.data);
+ } catch (err) {
+ setItemImages(prev => ({
+ ...prev,
+ [newItemResponse.data.id]: '/default.jpg'
+ }));
+ }
+ }
}
-
+
handleCloseAddItemDialog();
- // Refresh items list
const response = await fetchItems(() =>
- api.items.getAll(token, boxId)
+ boxId ? api.items.getByBox(token, boxId) : api.items.getAll(token)
);
setItems(response.data);
} catch (err) {}
- };
+ }, [token, boxId, newItemName, newItemDescription, createItem, uploadImage, fetchItems]);
- const handleDeleteItem = async (itemId) => {
+ const handleDeleteItem = useCallback(async (itemId) => {
try {
- await deleteItem(() =>
- api.items.delete(token, itemId)
- );
- setItems(items.filter(item => item.ID !== itemId));
+ await deleteItem(() => api.items.delete(token, itemId));
+ setItems(prev => prev.filter(item => item.ID !== itemId));
} catch (err) {}
- };
+ }, [token, deleteItem]);
- const handleEditItem = (item) => {
+ const handleEditItem = useCallback((item) => {
setEditingItem(item);
- };
+ }, []);
const handleSaveEdit = async () => {
setEditingItem(null);
@@ -157,9 +196,12 @@ export default function Items({ token }) {
setItems(response.data);
};
- const filteredItems = items.filter(item =>
- item.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
- item.description.toLowerCase().includes(searchQuery.toLowerCase())
+ const filteredItems = useMemo(() =>
+ items.filter(item =>
+ item.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ item.description.toLowerCase().includes(searchQuery.toLowerCase())
+ ),
+ [items, searchQuery]
);
return (
@@ -251,40 +293,17 @@ export default function Items({ token }) {
Actions
-
- {filteredItems.map((item) => (
-
-
-
-
- {item.name}
- {item.description}
-
-
-
- handleEditItem(item)}
- size="large"
- sx={{ mr: 1 }}
- >
-
-
-
-
- handleDeleteItem(item.ID)}
- size="large"
- color="error"
- disabled={deletingItem}
- >
- {deletingItem ? : }
-
-
-
-
-
- ))}
-
+
+ {filteredItems.map((item) => (
+
+ ))}
+
)}