148 lines
6.8 KiB
TypeScript
148 lines
6.8 KiB
TypeScript
"use client"
|
|
import { cn } from "@/lib/utils"
|
|
import { useSortable } from "@dnd-kit/sortable";
|
|
import { CSS } from "@dnd-kit/utilities";
|
|
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
|
import { Task } from "./data";
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
import { Button } from "@/components/ui/button";
|
|
import { MoreVertical, SquarePen, Trash2 } from "lucide-react";
|
|
import { Progress } from "@/components/ui/progress";
|
|
import DeleteConfirmationDialog from "@/components/delete-confirmation-dialog";
|
|
import { useState } from "react";
|
|
import EditTask from "./edit-task";
|
|
import { Icon } from "@/components/ui/icon";
|
|
|
|
function TaskCard({ task }: { task: Task }) {
|
|
const { projectLogo, title, desc, startDate, endDate, progress, assignee, remainingDays } = task;
|
|
const [open, setOpen] = useState<boolean>(false);
|
|
const [editTaskOpen, setEditTaskOpen] = useState<boolean>(false);
|
|
const {
|
|
setNodeRef,
|
|
attributes,
|
|
listeners,
|
|
transform,
|
|
transition,
|
|
isDragging,
|
|
} = useSortable({
|
|
id: task.id,
|
|
data: {
|
|
type: "Task",
|
|
task,
|
|
},
|
|
});
|
|
|
|
const style = {
|
|
transition,
|
|
transform: CSS.Transform.toString(transform),
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<DeleteConfirmationDialog
|
|
open={open}
|
|
onClose={() => setOpen(false)}
|
|
/>
|
|
<EditTask
|
|
open={editTaskOpen}
|
|
setOpen={setEditTaskOpen}
|
|
/>
|
|
<Card
|
|
className={cn("", {
|
|
"opacity-10 bg-primary/50 ": isDragging,
|
|
})}
|
|
ref={setNodeRef}
|
|
style={style}
|
|
{...attributes}
|
|
{...listeners}
|
|
>
|
|
<CardHeader className="flex-row gap-1 p-2.5 items-center space-y-0">
|
|
<Avatar className="flex-none h-8 w-8 rounded bg-default-200 text-default hover:bg-default-200">
|
|
<AvatarImage src={projectLogo} />
|
|
<AvatarFallback className="uppercase"> {title.charAt(0) + title.charAt(1)}</AvatarFallback>
|
|
</Avatar>
|
|
<h3 className="flex-1 text-default-800 text-lg font-medium max-w-[160px] truncate text-center capitalize ">{title}</h3>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
size="icon"
|
|
className="flex-none ring-offset-transparent bg-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent w-6"
|
|
>
|
|
<MoreVertical className="h-4 w-4 text-default-900" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent className="p-0 overflow-hidden" align="end" >
|
|
<DropdownMenuItem
|
|
className="py-2 border-b border-default-200 text-default-600 focus:bg-default focus:text-default-foreground rounded-none cursor-pointer"
|
|
onClick={() => setEditTaskOpen(true)}
|
|
>
|
|
<SquarePen className="w-3.5 h-3.5 me-1" />
|
|
Edit
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
className="py-2 bg-destructive/30 text-destructive focus:bg-destructive focus:text-destructive-foreground rounded-none cursor-pointer"
|
|
onClick={() => setOpen(true)}
|
|
>
|
|
<Trash2 className="w-3.5 h-3.5 me-1" />
|
|
Delete
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</CardHeader>
|
|
<CardContent className="p-2.5 pt-1">
|
|
<div className="text-default-600 text-sm">{desc}</div>
|
|
<div className="flex gap-4 mt-6">
|
|
<div>
|
|
<div className="text-xs text-default-400 mb-1">Start Date</div>
|
|
<div className="text-xs text-default-600 font-medium">{startDate}</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-xs text-default-400 mb-1">End Date</div>
|
|
<div className="text-xs text-default-600 font-medium">{endDate}</div>
|
|
</div>
|
|
</div>
|
|
<div className="mt-1">
|
|
<div className="text-end text-xs text-default-600 mb-1.5 font-medium">{progress}%</div>
|
|
<Progress value={progress} color="primary" size="sm" />
|
|
</div>
|
|
<div className="flex mt-5">
|
|
<div className="flex-1">
|
|
<div className="text-default-400 text-sm font-normal mb-3">Assigned to</div>
|
|
<div className="flex items-center -space-x-1">
|
|
{
|
|
assignee?.map((user, index) => (
|
|
<Avatar
|
|
key={`user-${index}`}
|
|
className="h-6 w-6"
|
|
>
|
|
<AvatarImage src={user.image} />
|
|
<AvatarFallback> {user.name.charAt(0) + user.name.charAt(1)}</AvatarFallback>
|
|
</Avatar>
|
|
))
|
|
}
|
|
<div className="bg-card text-default-900 text-xs ring-2 ring-default-100 rounded-full h-6 w-6 flex flex-col justify-center items-center">
|
|
+2
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex-none">
|
|
<div className="flex items-center gap-1 bg-destructive/10 text-destructive rounded-full px-2 py-0.5 text-sm mt-1">
|
|
<Icon icon="heroicons-outline:clock" />
|
|
{remainingDays} days left
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default TaskCard;
|