pantry-tracker / app / pantry / page.tsx
page.tsx
Raw
'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"
              />
            )} */