diff --git a/frontend/components/Projects.tsx b/frontend/components/Projects.tsx index 21f082d..91959ae 100644 --- a/frontend/components/Projects.tsx +++ b/frontend/components/Projects.tsx @@ -1,9 +1,10 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { MagnifyingGlassIcon, FolderIcon, Squares2X2Icon, Bars3Icon, + ChevronDownIcon, } from '@heroicons/react/24/solid'; import ConfirmDialog from './Shared/ConfirmDialog'; import ProjectModal from './Project/ProjectModal'; @@ -35,6 +36,71 @@ const getPriorityStyles = (priority: PriorityType) => { } }; +// Reusable dropdown component +interface DropdownOption { + value: string; + label: string; +} + +interface DropdownProps { + label: string; + value: string; + options: DropdownOption[]; + onChange: (value: string) => void; + placeholder?: string; +} + +const Dropdown: React.FC = ({ label, value, options, onChange, placeholder }) => { + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + const selectedOption = options.find(option => option.value === value); + + return ( +
+ + + {isOpen && ( +
+ {options.map((option) => ( + + ))} +
+ )} +
+ ); +}; + const Projects: React.FC = () => { const { t } = useTranslation(); const { @@ -64,6 +130,7 @@ const Projects: React.FC = () => { const [activeDropdown, setActiveDropdown] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [viewMode, setViewMode] = useState<'cards' | 'list'>('cards'); + const [isSearchExpanded, setIsSearchExpanded] = useState(false); const [searchParams, setSearchParams] = useSearchParams(); const activeFilter = searchParams.get('active') || 'all'; @@ -100,6 +167,7 @@ const Projects: React.FC = () => { loadProjects(); }, [activeFilter, areaFilter]); + const handleSaveProject = async (project: Project) => { setProjectsLoading(true); try { @@ -266,65 +334,54 @@ const Projects: React.FC = () => { > + + {/* Search Toggle Button */} +
-
- - -
+ {/* Status Dropdown */} + handleActiveFilterChange({target: {value}} as any)} + /> -
- - -
+ {/* Area Dropdown */} + ({ + value: area.id?.toString() || '', + label: area.name + })) + ]} + onChange={(value) => handleAreaFilterChange({target: {value}} as any)} + />
- {/* Search Bar */} -
+ {/* Collapsible Search Bar */} +
= ({ {task.name} {isOverdue && ( - - overdue - + )}
{/* Project, tags, due date, and recurrence in same row, with spacing when they exist */} @@ -278,12 +277,10 @@ const TaskHeader: React.FC = ({
{task.name} {isOverdue && ( - - overdue - + )}