pmtool / src /components /tasks /AddTaskDialog.tsx
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
15.4 kB
import React, { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import { CalendarIcon, Loader2 } from "lucide-react";
import { format } from "date-fns";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
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 {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Calendar } from "@/components/ui/calendar";
import { toast } from "@/lib/custom-toast";
import { tasksApi, TaskCreateRequest } from "@/services/tasksApi";
import { Employee } from "@/types";
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(),
});
interface AddTaskDialogProps {
issueId: number;
onTaskAdded: () => void;
employees: Employee[];
isOpen: boolean;
onOpenChange: (open: boolean) => void;
}
const AddTaskDialog: React.FC<AddTaskDialogProps> = ({
issueId,
onTaskAdded,
employees,
isOpen,
onOpenChange,
}) => {
const [isSubmitting, setIsSubmitting] = useState(false);
console.log("Dialog isOpen state:", isOpen); // Debug log
// Initialize the form
const form = useForm<z.infer<typeof formSchema>>({
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("Dialog opened, resetting form"); // Debug log
form.reset();
}
}, [isOpen, form]);
// Handle form submission
const onSubmit = async (values: z.infer<typeof formSchema>) => {
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
onOpenChange(false);
onTaskAdded();
} catch (error) {
console.error("Error creating task:", error);
toast.error("Failed to create task. Please try again.");
} finally {
setIsSubmitting(false);
}
};
const handleOpenChange = (newOpen: boolean) => {
console.log("Dialog openChange triggered:", newOpen); // Debug log
onOpenChange(newOpen);
};
return (
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
<DialogContent className="sm:max-w-[550px]">
<DialogHeader>
<DialogTitle>Add New Task</DialogTitle>
<DialogDescription>
Create a new task linked to this issue.
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-5">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel>Title</FormLabel>
<FormControl>
<Input placeholder="Task title" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<SlateRichTextEditor
initialValue={field.value || ''}
onChange={(value) => field.onChange(value)}
placeholder="Task description"
minHeight="150px"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem>
<FormLabel>Type</FormLabel>
<FormControl>
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger>
<SelectValue placeholder="Select type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="Task">Task</SelectItem>
<SelectItem value="Bug">Bug</SelectItem>
<SelectItem value="Documentation">Documentation</SelectItem>
<SelectItem value="Enhancement">Enhancement</SelectItem>
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="priority"
render={({ field }) => (
<FormItem>
<FormLabel>Priority</FormLabel>
<FormControl>
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger>
<SelectValue placeholder="Select priority" />
</SelectTrigger>
<SelectContent>
<SelectItem value="Low">Low</SelectItem>
<SelectItem value="Medium">Medium</SelectItem>
<SelectItem value="High">High</SelectItem>
<SelectItem value="Critical">Critical</SelectItem>
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<FormField
control={form.control}
name="status"
render={({ field }) => (
<FormItem>
<FormLabel>Status</FormLabel>
<FormControl>
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger>
<SelectValue placeholder="Select status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="New">New</SelectItem>
<SelectItem value="In Progress">In Progress</SelectItem>
<SelectItem value="In Review">In Review</SelectItem>
<SelectItem value="Completed">Completed</SelectItem>
<SelectItem value="Closed">Closed</SelectItem>
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="assignedTo"
render={({ field }) => (
<FormItem>
<FormLabel>Assigned To</FormLabel>
<FormControl>
<Select
value={field.value ? field.value.toString() : "null"}
onValueChange={(value) => field.onChange(value === "null" ? null : parseInt(value))}
>
<SelectTrigger>
<SelectValue placeholder="Select assignee" />
</SelectTrigger>
<SelectContent>
<SelectItem value="null">Unassigned</SelectItem>
{employees.map((employee) => (
<SelectItem key={employee.id} value={employee.id.toString()}>
{employee.firstName} {employee.lastName}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<FormField
control={form.control}
name="stateDate"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Start Date</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
type="button"
variant="outline"
className={`w-full pl-3 text-left font-normal ${!field.value ? "text-muted-foreground" : ""}`}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="endDate"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Due Date</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
type="button"
variant="outline"
className={`w-full pl-3 text-left font-normal ${!field.value ? "text-muted-foreground" : ""}`}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value || undefined}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="estimates"
render={({ field }) => (
<FormItem>
<FormLabel>Estimated Hours</FormLabel>
<FormControl>
<Input
type="number"
placeholder="Estimated hours to complete"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<DialogFooter className="pt-2">
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
disabled={isSubmitting}
>
Cancel
</Button>
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Creating...
</>
) : (
"Create Task"
)}
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
};
export default AddTaskDialog;