'use client';
import { useAuthState } from 'react-firebase-hooks/auth';
import { auth, db } from '../firebase/config';
import { useRouter } from 'next/navigation';
import { signOut } from 'firebase/auth';
import { useEffect, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { doc, getDocs, setDoc, deleteDoc, collection, query, where, addDoc } from 'firebase/firestore';
export default function PantryInventory() {
const [user] = useAuthState(auth);
const router = useRouter();
const userSession = sessionStorage.getItem('user');
const [name, setName] = useState('');
const [quantity, setQuantity] = useState(1);
const [expirationDate, setExpirationDate] = useState('');
const [image, setImage] = useState(null);
const [categoryId, setCategoryId] = useState('canned_goods');
const [error, setError] = useState('');
const [items, setItems] = useState<any[]>([]);
const [searchName, setSearchName] = useState('');
const [recipe, setRecipe] = useState('');
useEffect(() => {
if (!user || !userSession) {
router.push('/');
} else {
fetchItems(user.uid);
ItemsNameQuantity(user.uid);
}
}, [user, router]);
const fetchItems = async (userId: string) => {
try {
const q = query(collection(db, 'users', userId, 'items'));
const querySnapshot = await getDocs(q);
const itemsList: any[] = [];
querySnapshot.forEach((doc) => {
itemsList.push({ ...doc.data(), id: doc.id });
});
setItems(itemsList);
} catch (error) {
console.error('Error fetching items: ', error);
setError('An error occurred while fetching items.');
}
};
const handleFileChange = (e: any) => {
if (e.target.files && e.target.files[0]) {
setImage(e.target.files[0]);
}
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setError('');
if (!name || quantity <= 0 || !expirationDate || !categoryId) {
setError('Please fill out all required fields.');
return;
}
try {
const itemId = uuidv4();
if (!user) {
setError('User is not authenticated.');
return;
}
const userId = user.uid;
let imageId = '';
if (image) {
// TODO: Upload image to storage service and set imageId
imageId = 'unique_image_id'; // Replace with actual ID after upload
}
const userItemsRef = collection(db, 'users', userId, 'items');
await addDoc(userItemsRef, {
itemId,
userId,
name,
quantity,
expirationDate: new Date(expirationDate),
image: imageId,
categoryId,
});
setName('');
setQuantity(1);
setExpirationDate('');
setImage(null);
setCategoryId('canned_goods');
fetchItems(userId);
ItemsNameQuantity(user.uid);
} catch (error) {
console.error('Error adding document: ', error);
setError('An error occurred while adding the item.');
}
};
const handleDelete = async (id: string) => {
if (!user) return;
try {
const userId = user.uid;
await deleteDoc(doc(db, 'users', userId, 'items', id));
fetchItems(userId);
ItemsNameQuantity(user.uid);
} catch (error) {
console.error('Error deleting item: ', error);
setError('An error occurred while deleting the item.');
}
};
const handleUpdate = async (id: string, updatedQuantity: number) => {
if (!user) return;
try {
const userId = user.uid;
await setDoc(doc(db, 'users', userId, 'items', id), { quantity: updatedQuantity }, { merge: true });
fetchItems(userId);
ItemsNameQuantity(user.uid);
} catch (error) {
console.error('Error updating item: ', error);
setError('An error occurred while updating the item.');
}
};
const filteredItems = items.filter((item) => {
// Check if item.name is defined and is a string before calling toLowerCase
const name = item.name ? item.name.toLowerCase() : '';
return name.includes(searchName.toLowerCase());
});
const ItemsNameQuantity = async (userId: string) => {
try{
const q = query(collection(db, 'users', userId, 'items'));
const querySnapshot = await getDocs(q);
const itemsList: { name: string; quantity: number }[] = [];
querySnapshot.forEach((doc) => {
const itemData = doc.data();
itemsList.push({
name: itemData.name,
quantity: itemData.quantity,
});
});
return itemsList;
} catch (error) {
console.error('Error fetching items: ', error);
setError('An error occurred while fetching items.');
}
};
const sendMessage = async () => {
if (!user) return;
const message = await ItemsNameQuantity(user.uid);
console.log('Message to be sent:', message);
try {
const response = await fetch('http://localhost:8080/send-message', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message }),
});
if (response.ok) {
const result = await response.json();
console.log('Backend response:', result);
// Fetch the recipe after sending the message
const recipeResponse = await fetch('http://localhost:8080/get-recipe');
if (recipeResponse.ok) {
const recipeData = await recipeResponse.json();
console.log('Recipe from backend:', recipeData.content);
setRecipe(recipeData.content);
} else {
console.error('Error fetching recipe');
}
} else {
console.error('Error sending the message');
}
} catch (error) {
console.error('Error:', error);
}
};
return (
<main className="flex min-h-screen flex-col items-center justify-between p-5">
<div className='w-full'>
<div className='flex justify-end mb-4'>
<button
onClick={() => {
signOut(auth);
sessionStorage.removeItem('user');
}}
className="bg-transparent border-none p-0 focus:outline-none flex items-right"
>
<img
src="https://svgshare.com/i/18us.svg" // Replace with your door image URL
alt="Log out"
className="w-8 h-8" // Adjust the size as needed
/>
</button>
</div>
</div>
<h1 className="text-4xl font-bold text-gray-800 mb-6">Pantry List</h1>
<form onSubmit={handleSubmit} className="w-full bg-white p-6 rounded-lg shadow-lg">
<h2 className="text-2xl font-bold mb-6 text-gray-700">Add Items</h2>
<div className='flex flex-col md:flex-row gap-4 mb-6'>
<div className="flex-1">
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Item Name"
className="w-full px-4 py-3 border border-gray-300 rounded-lg placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div className="flex-1">
<input
type="number"
id="quantity"
value={quantity}
onChange={(e) => setQuantity(Number(e.target.value))}
placeholder="Quantity"
className="w-full px-4 py-3 border border-gray-300 rounded-lg placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
</div>
<div className='flex flex-col md:flex-row gap-4 mb-6'>
<div className="flex-1">
<label className="block text-gray-600 mb-2" htmlFor="expirationDate">
Expiration Date
</label>
<input
type="date"
id="expirationDate"
value={expirationDate}
onChange={(e) => setExpirationDate(e.target.value)}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
required
/>
</div>
<div className="flex-1">
<label className="block text-gray-600 mb-2" htmlFor="image">
Image (Optional)
</label>
<input
type="file"
id="image"
accept="image/*"
onChange={handleFileChange}
className="w-full px-4 py-3 border border-gray-300 rounded-lg file:border-0 file:bg-gray-100 file:text-gray-700 file:rounded-lg focus:outline-none focus:ring-1 focus:ring-blue-500"
/>
</div>
</div>
<div className="mb-6">
<label className="block text-gray-600 mb-2" htmlFor="categoryId">
Category
</label>
<select
id="categoryId"
value={categoryId}
onChange={(e) => setCategoryId(e.target.value)}
className="w-full px-4 py-3 border border-gray-300 rounded-lg placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
required
>
<option value="canned_goods">Canned Goods</option>
<option value="baking_supplies">Baking Supplies</option>
<option value="dry_goods">Dry Goods</option>
<option value="snacks">Snacks</option>
<option value="beverages">Beverages</option>
<option value="others">Others</option>
</select>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-3 px-6 rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
>
Add Item
</button>
</form>
<div className="w-full bg-white p-6 mt-6 rounded-lg shadow-lg">
<h2 className="text-3xl font-bold mb-6 text-gray-800">Your Items</h2>
<input
type="text"
placeholder="Search by name"
value={searchName}
onChange={(e) => setSearchName(e.target.value)}
className="w-full mb-6 px-4 py-3 border border-gray-300 rounded-lg placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
{filteredItems.length === 0 ? (
<p className="text-gray-500">No items found.</p>
) : (
<div className="overflow-x-auto">
<table className="min-w-full table-auto bg-gray-50 rounded-lg overflow-hidden">
<thead className="bg-gray-200 text-gray-700">
<tr>
<th className="px-4 py-2 text-left text-sm md:text-base">Name</th>
<th className="px-4 py-2 text-left text-sm md:text-base">Quantity</th>
<th className="px-4 py-2 text-left text-sm md:text-base">Expiration Date</th>
<th className="px-4 py-2 text-left text-sm md:text-base">Image</th>
<th className="px-4 py-2 text-left text-sm md:text-base">Actions</th>
</tr>
</thead>
<tbody className="text-gray-700">
{filteredItems.map((item) => (
<tr key={item.id} className="border-t">
<td className="px-4 py-3 text-sm md:text-base">{item.name}</td>
<td className="px-4 py-3 text-sm md:text-base">
<input
type="number"
value={item.quantity}
onChange={(e) => handleUpdate(item.id, Number(e.target.value))}
min="1"
className="w-20 px-3 py-2 border border-gray-300 rounded-lg text-sm md:text-base focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</td>
<td className="px-4 py-3 text-sm md:text-base">
{item.expirationDate
? new Date(item.expirationDate.seconds * 1000).toLocaleDateString()
: 'N/A'}
</td>
<td className="px-4 py-3 text-sm md:text-base">
<img
src="https://svgshare.com/i/18rw.svg" // Replace with the actual link to the placeholder image
alt="Placeholder"
className="w-8 h-8 object-cover rounded-full"
/>
</td>
<td className="px-4 py-3 text-sm md:text-base">
<button
onClick={() => handleDelete(item.id)}
className="bg-red-500 text-white py-2 px-4 rounded-lg hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500"
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
<div className="w-full bg-white p-6 mt-6 rounded-lg shadow-lg">
<h2 className="text-3xl font-semibold mb-6 text-gray-900">Recipe Sugestions</h2>
<button
onClick={sendMessage}
className="bg-blue-600 text-white py-3 px-6 rounded-lg shadow-md hover:bg-blue-700 transition-colors"
>
Generate Recipe
</button>
<div className="mt-6">
{recipe ? (
<div className="p-4 border border-gray-200 rounded-lg bg-gray-50 shadow-inner">
<p className="text-gray-800 text-base">{recipe}</p>
</div>
) : (
<p className="text-gray-500 text-base mt-4">
No recipe available. Please generate one.
</p>
)}
</div>
</div>
</main>
);
}
// <td className="px-4 py-2">{new Date(item.expirationDate.seconds * 1000).toLocaleDateString()}</td>
/* {item.image ? (
<img
src={`path_to_image_storage/${item.image}`}
alt={item.name}
className="w-16 h-16 object-cover rounded"
/>
) : (
<img
src="https://svgshare.com/i/18rw.svg" // Replace with the actual link to the placeholder image
alt="Placeholder"
className="w-4 h-4 object-cover rounded"
/>
)} */