diff --git a/frontend/src/components/HomeComponents/Tasks/AddMultiSelect.tsx b/frontend/src/components/HomeComponents/Tasks/AddMultiSelect.tsx new file mode 100644 index 00000000..9a753bed --- /dev/null +++ b/frontend/src/components/HomeComponents/Tasks/AddMultiSelect.tsx @@ -0,0 +1,159 @@ +import * as React from 'react'; +import { Check, ChevronDown, X } from 'lucide-react'; +import { cn } from '@/components/utils/utils'; +import { Input } from '@/components/ui/input'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Button } from '@/components/ui/button'; + +interface AddMultiSelectProps { + options: string[]; + selected: string[]; + onChange: (items: string[]) => void; + placeholder?: string; + portalContainer?: HTMLElement | null; +} + +export function AddMultiSelect({ + options, + selected, + onChange, + placeholder = 'Search or create..', + portalContainer, +}: AddMultiSelectProps) { + const [open, setOpen] = React.useState(false); + const [searchValue, setSearchValue] = React.useState(''); + + const filteredOptions = options.filter((option) => + option.toLowerCase().includes(searchValue.toLowerCase()) + ); + + const isNewItem = + searchValue.trim() !== '' && + !options.some( + (opt) => opt.toLowerCase() === searchValue.trim().toLowerCase() + ); + + const handleSelect = (item: string) => { + if (selected.includes(item)) { + onChange(selected.filter((s) => s !== item)); + } else { + onChange([...selected, item]); + } + }; + + const handleCreateItem = () => { + const newItem = searchValue.trim(); + if (newItem && !selected.includes(newItem)) { + onChange([...selected, newItem]); + setSearchValue(''); + } + }; + + const handleRemoveItem = (item: string, e: React.MouseEvent) => { + e.stopPropagation(); + onChange(selected.filter((s) => s !== item)); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && isNewItem) { + e.preventDefault(); + handleCreateItem(); + } + }; + + return ( +
+ + + + + + +
+ setSearchValue(e.target.value)} + onKeyDown={handleKeyDown} + className="h-9" + autoFocus + /> +
+ +
+ {isNewItem && ( +
+ + + Create "{searchValue.trim()}" +
+ )} + + {filteredOptions.length === 0 && !isNewItem ? ( +
+ No results found. +
+ ) : ( + filteredOptions.map((option) => { + const isSelected = selected.includes(option); + return ( +
handleSelect(option)} + > + + {option} +
+ ); + }) + )} +
+
+
+
+ ); +} diff --git a/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx b/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx index bbaad655..fadb9a67 100644 --- a/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx +++ b/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { useState, useEffect } from 'react'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; @@ -24,23 +25,24 @@ import { } from '@/components/ui/select'; import { AddTaskDialogProps } from '@/components/utils/types'; import { format } from 'date-fns'; +import { AddMultiSelect } from './AddMultiSelect'; export const AddTaskdialog = ({ isOpen, setIsOpen, newTask, setNewTask, - tagInput, - setTagInput, onSubmit, isCreatingNewProject, setIsCreatingNewProject, uniqueProjects = [], + uniqueTags = [], allTasks = [], }: AddTaskDialogProps) => { const [annotationInput, setAnnotationInput] = useState(''); const [dependencySearch, setDependencySearch] = useState(''); const [showDependencyResults, setShowDependencyResults] = useState(false); + const dialogContainerRef = React.useRef(null); const getFilteredTasks = () => { const availableTasks = allTasks.filter( @@ -102,20 +104,6 @@ export const AddTaskdialog = ({ }); }; - const handleAddTag = () => { - if (tagInput && !newTask.tags.includes(tagInput, 0)) { - setNewTask({ ...newTask, tags: [...newTask.tags, tagInput] }); - setTagInput(''); - } - }; - - const handleRemoveTag = (tagToRemove: string) => { - setNewTask({ - ...newTask, - tags: newTask.tags.filter((tag) => tag !== tagToRemove), - }); - }; - return ( @@ -129,6 +117,7 @@ export const AddTaskdialog = ({ +
@@ -194,6 +183,7 @@ export const AddTaskdialog = ({
setTagInput(e.target.value)} - onKeyDown={(e) => e.key === 'Enter' && handleAddTag()} - required - className="col-span-6" +
+ setNewTask({ ...newTask, tags })} + placeholder="Search or create tag.." + portalContainer={dialogContainerRef.current} />
- -
- {newTask.tags.length > 0 && ( -
-
-
- {newTask.tags.map((tag, index) => ( - - {tag} - - - ))} -
-
- )} -