Source: FullCloset.js

/**
 * FullCloset.js - View and manage all clothing items in the user's virtual closet.
 *
 * @fileoverview Allows searching, filtering by category, and viewing popular recommendations.
 * Integrates with backend APIs for dynamic content updates and user interaction tracking.
 *
 * @author
 * Peini SHE
 */

import React, { useState, useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { useLocation } from "react-router-dom";
import "./FullCloset.css";

/**
 * FullCloset component renders the full virtual closet interface with category filter, search,
 * user account dropdown, and popular item sidebar.
 *
 * @component
 * @param {Object} props
 * @param {string} props.username - Username for greeting in account dropdown.
 * @param {string} props.userId - ID of the currently logged-in user.
 * @returns {JSX.Element}
 */
const FullCloset = ({ username, userId }) => {
    const navigate = useNavigate();
    const timeoutRef = useRef(null);      
    const storedCategory = sessionStorage.getItem("selectedCategory") || "tops";  // Default category is "tops"
    
    const [category, setCategory] = useState(storedCategory); 
    const [showDropdown, setShowDropdown] = useState(false);
    const [closetImages, setClosetImages] = useState([]);
    const [searchingResult, setSearchingResult] = useState([]);
    const [recommendations, setRecommendations] = useState([]);

    // Retrieve user ID from localStorage if not provided as a prop
    const storedUserId = localStorage.getItem("user_id");
    const effectiveUserId = userId || storedUserId;
    const [error, setError] = useState("");    
    const [searchQuery, setSearchQuery] = useState("");
    const [isSearching, setIsSearching] = useState(false);
       
    const location = useLocation();
    const queryParams = new URLSearchParams(location.search);
    const query = queryParams.get("query"); 
  
    useEffect(() => {
        if (query) {
          fetch(`http://localhost:5000/search?query=${query}`)
            .then((res) => res.json())
            .then((data) => setSearchingResult(data.items || []))
            .catch(() => setSearchingResult([]));
          setIsSearching(true);
        }
    }, [query]);

    /**
   * Fetch clothing items from the backend based on selected category.
   */
    useEffect(() => {
        console.log("Checking userId in FullCloset:", effectiveUserId);
    }, [effectiveUserId]);

    useEffect(() => {
        if (!effectiveUserId) {
            console.warn("No userId provided. Unable to fetch closet.");
            return;
        }

        const fetchImages = async () => {
            try {
                const response = await fetch(`http://localhost:5000/api/clothes?category=${category}&user_id=${effectiveUserId}`);
                const data = await response.json();
                if (data.items) {
                    setClosetImages(data.items);
                    console.log("Fetched images:", data.items);
                } else {
                    console.warn("No closet items found.");
                    setClosetImages([]); // Ensure the state is reset when no items are found
                }
            } catch (error) {
                console.error("Error fetching images:", error);
                setClosetImages([]); // Ensure UI remains stable even if the fetch fails
            }
        };
        fetchImages();
    }, [category, effectiveUserId]);

  /**
   * Fetch top 5 most popular clothing recommendations.
   */    
    useEffect(() => {
        const getRecommendationsPopular = async () => {
            try {
                const response = await fetch(`http://127.0.0.1:5000/recommend/popular`);
                const data = await response.json();
                if (data.error) {
                    setError(data.error);
                    setRecommendations([]);
                } else {
                    setRecommendations(data.recommended_popular);
                    setError("");
                }
            } catch (err) {
                setError("Failed to fetch combinations");
            }
        };
        getRecommendationsPopular();
    }, []);

  /**
   * Handles keyword-based search.
   * @param {KeyboardEvent} e - Keyboard event from input.
   */
    const handleSearch = async (e) => {
        if (e.key === "Enter" && searchQuery.trim() !== "") {
            setIsSearching(true);
            try {
                const response = await fetch(`http://127.0.0.1:5000/search?query=${searchQuery}`);
                const data = await response.json();
                console.log("Search API Response:", data.items);
                setSearchingResult(data.items || []);
            } catch (err) {
                console.error("Search error:", err);
                setError("Failed to fetch search results");
                setSearchingResult([]);
            }
        }
    };

    return (
        <div className="tryon-container">
            {/* Header */}
            <header className="tryon-header">
                <h1 className="logo">OVDR <span className="title">All Clothes</span></h1>

                {/* Search Bar */}
                <input 
                    type="text" 
                    className="search-bar" 
                    placeholder="Search by keywords to access the clothes you like" 
                    value={searchQuery}
                    onChange={(e) => setSearchQuery(e.target.value)}
                    onKeyDown={handleSearch}
                />

                {/* Category Dropdown Selection */}
                <select className="category-dropdown" value={category} onChange={(e) => {
                    const newCategory = e.target.value;
                    setCategory(newCategory);
                    sessionStorage.setItem("selectedCategory", newCategory); // Store selection in session to persist after navigation
                }}>
                    <option value="tops">Tops</option>
                    <option value="dresses">Dresses</option>
                    <option value="bottoms">Bottoms</option>
                </select>

                {/* Account Dropdown */}
                <div className="account-container"
                    onMouseEnter={() => {
                        if (timeoutRef.current) {
                            clearTimeout(timeoutRef.current);
                        }
                        setShowDropdown(true);}}
                    onMouseLeave={() => {timeoutRef.current = setTimeout(() => {
                        setShowDropdown(false);
                    }, 500);}}
                >
                    <button className="account-btn">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2Z"></path>
                            <path d="M12 6a3 3 0 1 1-3 3 3 3 0 0 1 3-3ZM6 18a6 6 0 0 1 12 0"></path>
                        </svg>
                    </button>
                    {showDropdown && (
                        <div className="account-dropdown">
                            <div className="account-info">Hello {username}!</div>
                            <div className="dropdown-item" onClick={() => navigate(`/history?user_id=${userId}`)}>
                                View My History 
                            </div>
                            <div className="dropdown-item" onClick={() => navigate("/login")}>Log out</div>
                        </div>
                    )}
                </div>

                <button className="back-btn" onClick={() => navigate("/tryon")}>
                    Back to dressing room
                </button>
            </header>
            
            {/* Grid Layout for Clothes */}
            <div className="closet-grid">
                {isSearching ? (
                    searchingResult.length > 0 ? (
                        searchingResult.map((item, index) => (
                            <div 
                                className="closet-item" 
                                key={item.id || `search-${index}`}
                                onClick={() => navigate(`/detail/${item.id}`, { state: { item, category, userId: effectiveUserId } })}
                            >
                                <img src={item.image_path} className="closet-img" alt={item.title} />
                                <div className="closet-text">
                                    <h3>{item.title.length > 25 ? item.title.slice(0, 22) + "..." : item.title}</h3>
                                    <p>{item.closet_users} people added this to their Closet</p>
                                </div>
                            </div>
                        ))
                    ) : (
                        <p>No search results found.</p>
                    )
                ) : (
                    closetImages.length > 0 ? (
                        closetImages.map((item, index) => (
                            <div 
                                className="closet-item" 
                                key={item.id || `closet-${index}`} 
                                onClick={() => navigate(`/detail/${item.id}`, { state: { item, category, userId: effectiveUserId } })}
                            >
                                <img 
                                    src={item.image_path} 
                                    className="closet-img" 
                                    alt={item.title}
                                    onError={(e) => {
                                        console.error(`Failed to load image: ${item.image_path}`);
                                        e.target.src = "/images/placeholder.jpg";
                                    }}
                                />
                                <div className="closet-text">
                                    <h3>{item.title.length > 25 ? item.title.slice(0, 22) + "..." : item.title}</h3>
                                    <p>{item.closet_users} people added this to their Closet</p>
                                </div>
                            </div>
                        ))
                    ) : (
                        <p>No items found.</p>  
                    )  
                )}
            </div>

            {/* Popular Clothes Sidebar */}
            <div className="popular-list">
                <h3>Top 5 Popular Items</h3>
                {recommendations.length > 0 ? (
                    recommendations.map((item) => (
                        <div 
                            className="popular-item" 
                            key={item.id} 
                            onClick={() => navigate(`/detail/${item.id}`, { state: { item, category, userId: effectiveUserId } })}
                        >
                            <img 
                                src={item.url} 
                                alt={item.title} 
                                className="popular-img"
                                onError={(e) => {
                                    console.error(`Failed to load image: ${item.image_path}`);
                                    e.target.src = "/images/placeholder.jpg";
                                }}
                            />
                        </div>
                    ))
                ) : (
                    <p>No recommendations found.</p>
                )}
            </div>

            <footer className="tryon-footer">
            <a href="http://cslinux.nottingham.edu.cn/~Team202407/">About Us</a>
                <a href="/privacy.html" target="_blank" rel="noopener noreferrer">Privacy Policy</a>
                <a href="/docs/user_manual.pdf" target="_blank" rel="noopener noreferrer">Manual</a>
                <a href="/contact.html">Help and Contact</a>
                <p>Developed by TEAM2024.07</p>
            </footer>
        </div>
    );
};

export default FullCloset;