diff --git a/src/components/Admin.js b/src/components/Admin.js index 58b82d7..101f15a 100644 --- a/src/components/Admin.js +++ b/src/components/Admin.js @@ -1,8 +1,8 @@ // src/components/Admin.js import React, { useState, useEffect, useRef } from 'react'; -import axios from 'axios'; import { Link, useNavigate } from 'react-router-dom'; import { PRIMARY_COLOR, SECONDARY_COLOR } from '../App'; +import { api } from '../services/api'; import './Admin.css'; // Import the CSS file import { Typography, @@ -22,120 +22,86 @@ import { } from '@mui/material'; -export default function Admin() { - const [users, setUsers] = useState([]); - const [username, setUsername] = useState(''); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const navigate = useNavigate(); - const fileInputRef = useRef(null); - - - useEffect(() => { - axios.get(`${process.env.REACT_APP_API_URL}/api/v1/admin/user`, { - headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } - }) - .then(response => { - setUsers(response.data); - }) - .catch(error => { - console.error(error); - }); - }, []); - - const fetchUsers = async () => { - try { - const response = await axios.get(`${process.env.REACT_APP_API_URL}/api/v1/admin/user`, { - headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } - }); - setUsers(response.data); - } catch (error) { - console.error('Error fetching users:', error); - // Optionally, set an error message - // setErrorMessage('Failed to fetch users. Please try again.'); - } - }; - - const handleCreateUser = async (e) => { - e.preventDefault(); - try { - const response = await axios.post(`${process.env.REACT_APP_API_URL}/api/v1/admin/user`, { - username, - password, - email - }, { - headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } - }); - setUsers([...users, response.data]); - setUsername(''); - setPassword(''); - setEmail(''); - } catch (error) { - console.error(error); - } - }; - - const handleDeleteUser = async (id) => { - try { - await axios.delete(`${process.env.REACT_APP_API_URL}/api/v1/admin/user/${id}`, { - headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } - }); - setUsers(users.filter(user => user.id !== id)); - //setUsers(prevUsers => prevUsers.filter(user => user.id !== id)); + export default function Admin() { + const [users, setUsers] = useState([]); + const [username, setUsername] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const navigate = useNavigate(); + const fileInputRef = useRef(null); + + useEffect(() => { fetchUsers(); - - } catch (error) { - console.error(error); - } - }; - - const handleBackupDatabase = async () => { - try { - const response = await axios.get(`${process.env.REACT_APP_API_URL}/api/v1/admin/db`, { - headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }, - responseType: 'blob' - }); - const blob = new Blob([response.data], { type: 'application/x-sqlite3' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'database.db'; - a.click(); - } catch (error) { - console.error(error); - } - }; - - const handleRestoreDatabase = async (e) => { - e.preventDefault(); - try { - const file = fileInputRef.current.files[0]; - const formData = new FormData(); - formData.append('database', file); - console.log("sending request to restore db") + }, []); - const token = localStorage.getItem('token'); - if (!token) { - throw new Error('No token found in local storage'); + const fetchUsers = async () => { + try { + const response = await api.admin.getUsers(localStorage.getItem('token')); + setUsers(response.data); + } catch (error) { + console.error('Error fetching users:', error); } + }; - const response = await axios.post(`${process.env.REACT_APP_API_URL}/api/v1/admin/db`, formData, { - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'multipart/form-data' + const handleCreateUser = async (e) => { + e.preventDefault(); + try { + const response = await api.admin.createUser(localStorage.getItem('token'), { + username, + password, + email + }); + setUsers([...users, response.data]); + setUsername(''); + setPassword(''); + setEmail(''); + } catch (error) { + console.error(error); + } + }; + + const handleDeleteUser = async (id) => { + try { + await api.admin.deleteUser(localStorage.getItem('token'), id); + setUsers(users.filter(user => user.id !== id)); + fetchUsers(); + } catch (error) { + console.error(error); + } + }; + + const handleBackupDatabase = async () => { + try { + const response = await api.admin.backupDb(localStorage.getItem('token')); + const blob = new Blob([response.data], { type: 'application/x-sqlite3' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'database.db'; + a.click(); + } catch (error) { + console.error(error); + } + }; + + const handleRestoreDatabase = async (e) => { + e.preventDefault(); + try { + const file = fileInputRef.current.files[0]; + const formData = new FormData(); + formData.append('database', file); + + const response = await api.admin.restoreDb(localStorage.getItem('token'), formData); + + if (response.status === 200) { + alert('Database restored successfully'); + navigate('/admin'); } - }); - - if (response.status === 200) { - alert('Database restored successfully'); - navigate('/admin'); - } else { - throw new Error(`Failed to restore database: ${response.statusText}`); + } catch (error) { + console.error(error); } - } catch (error) { - console.error(error); - } - }; + }; + return ( diff --git a/src/components/Boxes.js b/src/components/Boxes.js index f3dc573..0ae3ea1 100644 --- a/src/components/Boxes.js +++ b/src/components/Boxes.js @@ -3,50 +3,43 @@ import React, { useEffect, useState } from 'react'; import { Container, Button, TextField, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material'; import { Delete as DeleteIcon } from '@mui/icons-material'; import { Link as RouterLink } from 'react-router-dom'; // Import Link from react-router-dom -import axios from 'axios'; import { PRIMARY_COLOR, SECONDARY_COLOR } from '../App'; +import { api } from '../services/api'; export default function Boxes({ token }) { const [boxes, setBoxes] = useState([]); const [newBoxName, setNewBoxName] = useState(''); - const apiUrl = `${process.env.REACT_APP_API_URL}/boxes`; - - const debugApi = () => { - if (process.env.DEBUG_API) { - console.log("URL is " + apiUrl); - } - }; - debugApi(); useEffect(() => { - //console.log('Token:' + token); - axios.get(`${process.env.REACT_APP_API_URL}/api/v1/boxes`, { - headers: { Authorization: `Bearer ${token}` } - }).then(response => { - setBoxes(response.data); - }); + fetchBoxes(); }, [token]); - // Log boxes state changes outside the useEffect - useEffect(() => { - //console.log('Boxes updated:', boxes); - }, [boxes]); - - const handleCreateBox = () => { - axios.post(`${process.env.REACT_APP_API_URL}/api/v1/boxes`, { name: newBoxName }, { - headers: { Authorization: `Bearer ${token}` } - }).then(response => { - setBoxes([...boxes, response.data]); - setNewBoxName(''); - }); + const fetchBoxes = async () => { + try { + const response = await api.boxes.getAll(token); + setBoxes(response.data); + } catch (error) { + console.error('Error fetching boxes:', error); + } }; - const handleDeleteBox = (id) => { - axios.delete(`${process.env.REACT_APP_API_URL}/api/v1/boxes/${id}`, { - headers: { Authorization: `Bearer ${token}` } - }).then(() => { + const handleCreateBox = async () => { + try { + const response = await api.boxes.create(token, { name: newBoxName }); + setBoxes([...boxes, response.data]); + setNewBoxName(''); + } catch (error) { + console.error('Error creating box:', error); + } + }; + + const handleDeleteBox = async (id) => { + try { + await api.boxes.delete(token, id); setBoxes(boxes.filter(box => box.ID !== id)); - }); + } catch (error) { + console.error('Error deleting box:', error); + } }; return ( diff --git a/src/components/ItemDetails.js b/src/components/ItemDetails.js index 713d8cb..2659a19 100644 --- a/src/components/ItemDetails.js +++ b/src/components/ItemDetails.js @@ -1,33 +1,24 @@ // src/components/ItemDetails.js import React, { useState, useEffect, useRef, useCallback } from 'react'; import { TextField, Button, Container, Avatar, Tooltip } from '@mui/material'; -import axios from 'axios'; import { PRIMARY_COLOR, SECONDARY_COLOR } from '../App'; -//import { useNavigate } from 'react-router-dom'; // Import useNavigate +import { api } from '../services/api'; export default function ItemDetails({ item, token, onSave, onClose, boxId }) { const [name, setName] = useState(item.name); const [description, setDescription] = useState(item.description); const [imagePath, setImagePath] = useState(item.image_path || ''); - const [imageSrc, setImageSrc] = useState('/images/default.jpg'); // Initial default image - const fileInputRef = useRef(null); // Add this line to define fileInputRef + const [imageSrc, setImageSrc] = useState('/images/default.jpg'); + const fileInputRef = useRef(null); const [imageOverlayVisible, setImageOverlayVisible] = useState(false); - // const navigate = useNavigate(); // Initialize useNavigate - // eslint says boxName is defined but never used, but when I remove it it fails to compile - // because boxName is undefined - // eslint-disable-next-line const [boxName, setBoxName] = useState(''); const [boxes, setBoxes] = useState([]); const [selectedBoxId, setSelectedBoxId] = useState(item.box_id); - //console.log("item.box_id: " + item.box_id); - useEffect(() => { const fetchBoxes = async () => { try { - const response = await axios.get(`${process.env.REACT_APP_API_URL}/api/v1/boxes`, { - headers: { Authorization: `Bearer ${token}` } - }); + const response = await api.boxes.getAll(token); setBoxes(response.data); } catch (error) { console.error('Error fetching boxes:', error); @@ -36,15 +27,12 @@ export default function ItemDetails({ item, token, onSave, onClose, boxId }) { fetchBoxes(); }, [token]); - const handleBoxChange = (event) => { const newBoxId = event.target.value; - setSelectedBoxId(newBoxId); // Update only this state - //console.log('Selected box ID:', newBoxId); + setSelectedBoxId(newBoxId); }; useEffect(() => { - // Fetch box details only when the selectedBoxId changes const getBoxDetails = async (boxId) => { try { const boxIdNumber = +boxId; @@ -52,134 +40,71 @@ export default function ItemDetails({ item, token, onSave, onClose, boxId }) { console.error('Invalid boxId:', boxId); return; } - const response = await axios.get(`${process.env.REACT_APP_API_URL}/api/v1/boxes/${boxIdNumber}`, { - headers: { Authorization: `Bearer ${token}` } - }); - setBoxName(response.data.name); + const response = await api.boxes.getAll(token); + const box = response.data.find(b => b.ID === boxIdNumber); + if (box) { + setBoxName(box.name); + } } catch (error) { console.error('Error fetching box details:', error); } }; if (selectedBoxId !== item.box_id) { - getBoxDetails(selectedBoxId); // Fetch when selected box changes - //console.log("selectedBoxId:", selectedBoxId); + getBoxDetails(selectedBoxId); } else if (item.box_id) { - getBoxDetails(item.box_id); // Fetch when boxId exists and selectedBoxId is empty - //console.log("item.box_id:", item.box_id); + getBoxDetails(item.box_id); } - }, [selectedBoxId, token, item.box_id]); // Removed `boxId` from dependencies + }, [selectedBoxId, token, item.box_id]); useEffect(() => { - // Function to fetch image similar to getImageSrc in Items.js - const getImageSrc = (itemId) => { - return axios.get(`${process.env.REACT_APP_API_URL}/api/v1/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; - }); - }); + const fetchItemImage = async () => { + try { + const response = await api.items.getImage(token, item.ID); + const reader = new FileReader(); + reader.onload = () => setImageSrc(reader.result); + reader.readAsDataURL(response.data); + } catch (error) { + setImageSrc('/default.jpg'); + } }; - - // Fetch the image when the component mounts or the item changes - getImageSrc(item.ID).then(dataUrl => setImageSrc(dataUrl)); + fetchItemImage(); }, [item.ID, token]); - // const handleCloseItemDetails = () => { - // onClose(); // Call the onClose prop to close the modal - // navigate(`/boxes/${boxId}/items`); // Navigate back to the items list - // }; - const handleImageUpload = useCallback(async () => { + if (!fileInputRef.current.files[0]) return; + const formData = new FormData(); formData.append('image', fileInputRef.current.files[0]); try { - const response = await axios.post(`${process.env.REACT_APP_API_URL}/api/v1/items/${item.ID}/upload`, formData, { - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'multipart/form-data' - } - }); - // Handle successful upload (e.g., show a success message) - //console.log('Image uploaded successfully!', response.data.imagePath); - return response.data.imagePath; // Indicate successful upload + const response = await api.items.uploadImage(token, item.ID, formData); + return response.data.imagePath; } catch (error) { - // Handle upload error (e.g., show an error message) console.error('Image upload failed:', error); } }, [item.ID, token]); - // eslint-disable-next-line - const updateItemBoxId = async () => { - try { - // eslint-disable-next-line - const response = await axios.put(`${process.env.REACT_APP_API_URL}/api/v1/items/${item.id}`, { - box_id: selectedBoxId, - }, { - headers: { Authorization: `Bearer ${token}` } - }); - // Update the item's boxId - item.box_id = selectedBoxId; - } catch (error) { - console.error('Error updating item boxId:', error); - } - }; - - const handleSave = useCallback( async () => { + const handleSave = useCallback(async () => { let imagePath; - // 1. Handle image upload first if a new image is selected if (fileInputRef.current.files[0]) { - // eslint-disable-next-line imagePath = await handleImageUpload(); } - //console.log("Selected box ID:", selectedBoxId) - // 2. Update item details (name, description, etc.) try { - await axios.put(`${process.env.REACT_APP_API_URL}/api/v1/items/${item.ID}`, { + await api.items.update(token, item.ID, { name, description, - box_id: +selectedBoxId, // Ensure the updated selected box is saved - }, { - headers: { Authorization: `Bearer ${token}` } + box_id: +selectedBoxId, }); - onSave(); // Notify parent to refresh items + onSave(); } catch (error) { - // Handle update error console.error('Item update failed:', error); } }, [item.ID, name, description, selectedBoxId, token, onSave, handleImageUpload]); const handleImageError = (e) => { - e.target.src = '/images/default.jpg'; // Fallback to default image on error + e.target.src = '/images/default.jpg'; }; const handleAvatarClick = () => { @@ -194,13 +119,12 @@ export default function ItemDetails({ item, token, onSave, onClose, boxId }) {

Edit Item: {item.name}

- {/* Display the item image as an avatar */} @@ -242,14 +166,11 @@ export default function ItemDetails({ item, token, onSave, onClose, boxId }) { accept="image/*" ref={fileInputRef} style={{ display: 'none' }} - id="editItemImageUpload" // Unique ID + id="editItemImageUpload" />