import React, { useState, useEffect, useRef } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; import { CalendarIcon, Loader2, X, ChevronDown, Check, Search } from "lucide-react"; import { format } from "date-fns"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Calendar } from "@/components/ui/calendar"; import { toast } from "@/lib/custom-toast"; import { tasksApi, TaskCreateRequest } from "@/services/tasksApi"; import { Employee } from "@/types"; import { cn } from "@/lib/utils"; import SlateRichTextEditor from "@/components/custom/SlateRichTextEditor"; // Form schema const formSchema = z.object({ title: z.string().min(1, "Title is required"), description: z.string().min(1, "Description is required"), type: z.string().min(1, "Type is required"), priority: z.string().min(1, "Priority is required"), status: z.string().min(1, "Status is required"), assignedTo: z.number().nullable(), estimates: z.string().optional(), stateDate: z.date(), endDate: z.date().nullable().optional(), }); // Custom dropdown component interface DropdownOption { value: string; label: string; } interface CustomDropdownProps { options: DropdownOption[]; value: string; onChange: (value: string) => void; placeholder?: string; } const CustomDropdown: React.FC = ({ options, value, onChange, placeholder = "Select option" }) => { const [isOpen, setIsOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const dropdownRef = useRef(null); const searchInputRef = useRef(null); // Close dropdown when clicking outside 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); }; }, []); // Focus search input when dropdown opens useEffect(() => { if (isOpen && searchInputRef.current && options.length > 5) { setTimeout(() => { searchInputRef.current?.focus(); }, 10); } }, [isOpen, options.length]); // Clear search when dropdown closes useEffect(() => { if (!isOpen) { setSearchQuery(''); } }, [isOpen]); const selectedOption = options.find(option => option.value === value); // Filter options based on search query const filteredOptions = searchQuery ? options.filter(option => option.label.toLowerCase().includes(searchQuery.toLowerCase()) ) : options; return (
{isOpen && (
{options.length > 5 && (
setSearchQuery(e.target.value)} onClick={e => e.stopPropagation()} className="flex w-full h-8 bg-transparent py-1 text-sm outline-none placeholder:text-muted-foreground" /> {searchQuery && ( )}
)}
    {filteredOptions.length === 0 ? (
  • No options found
  • ) : ( filteredOptions.map((option) => (
  • { onChange(option.value); setIsOpen(false); }} className={cn( "flex items-center px-3 py-1.5 text-sm cursor-pointer hover:bg-accent hover:text-accent-foreground", value === option.value && "bg-accent/50" )} > {option.label} {value === option.value && }
  • )) )}
)}
); }; // Custom DatePicker component interface CustomDatePickerProps { value: Date | null; onChange: (date: Date | null) => void; placeholder?: string; } const CustomDatePicker: React.FC = ({ value, onChange, placeholder = "Pick a date" }) => { const [isOpen, setIsOpen] = useState(false); const datePickerRef = useRef(null); // Close date picker when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (datePickerRef.current && !datePickerRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, []); return (
{isOpen && (

Select Date

{ onChange(date); setIsOpen(false); }} initialFocus className="rounded border" />
)}
); }; interface SimpleAddTaskDialogProps { issueId: number; onTaskAdded: () => void; employees: Employee[]; isOpen: boolean; onClose: () => void; } const SimpleAddTaskDialog: React.FC = ({ issueId, onTaskAdded, employees, isOpen, onClose, }) => { const [isSubmitting, setIsSubmitting] = useState(false); console.log("Simple Dialog isOpen state:", isOpen); // Debug log // Initialize the form const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { title: "", description: "", type: "Task", priority: "Medium", status: "New", assignedTo: null, estimates: "", stateDate: new Date(), endDate: null, }, }); // Reset form when dialog opens useEffect(() => { if (isOpen) { console.log("Simple Dialog opened, resetting form"); // Debug log form.reset(); } }, [isOpen, form]); // Handle form submission const onSubmit = async (values: z.infer) => { setIsSubmitting(true); try { // Prepare task data const taskData: TaskCreateRequest = { title: values.title, description: values.description, type: values.type, priority: values.priority, status: values.status, assignedTo: values.assignedTo, estimates: values.estimates || "0", esUnit: 1, // Default to hours stateDate: values.stateDate.toISOString().split('T')[0], endDate: values.endDate ? values.endDate.toISOString().split('T')[0] : null, remainingHr: 0, sprintName: null, issuesId: issueId, }; console.log("Creating task with data:", taskData); // Debug log // Create the task await tasksApi.create(taskData); // Show success message toast.success("Task created successfully"); // Close the dialog and refresh the tasks list onClose(); onTaskAdded(); } catch (error) { console.error("Error creating task:", error); toast.error("Failed to create task. Please try again."); } finally { setIsSubmitting(false); } }; // Dropdown options const typeOptions = [ { value: "Task", label: "Task" } ]; const priorityOptions = [ { value: "Low", label: "Low" }, { value: "Medium", label: "Medium" }, { value: "High", label: "High" }, { value: "Critical", label: "Critical" }, ]; const statusOptions = [ { value: "New", label: "New" }, { value: "In Progress", label: "In Progress" }, { value: "In Review", label: "In Review" }, { value: "Completed", label: "Completed" }, { value: "Closed", label: "Closed" }, ]; const assigneeOptions = [ { value: "null", label: "Unassigned" }, ...employees.map(emp => ({ value: emp.id.toString(), label: `${emp.firstName} ${emp.lastName}` })) ]; if (!isOpen) return null; return (

Add New Task

Create a new task linked to this issue.

( Title )} /> ( Description field.onChange(value)} placeholder="Task description" minHeight="150px" /> )} />
( Type )} /> ( Priority )} />
( Status )} /> ( Assigned To field.onChange(value === "null" ? null : parseInt(value))} placeholder="Select assignee" /> )} />
( Start Date )} /> ( Due Date )} />
( Estimated Hours )} />
); }; export default SimpleAddTaskDialog;