feat: enhance agent creation with human-like naming and improve task assignment notifications

This commit is contained in:
Abhimanyu Saharan
2026-02-05 22:51:46 +05:30
parent cbf9fd1b0a
commit e09460a881
10 changed files with 1212 additions and 203 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useMemo } from "react";
import { useMemo, useState } from "react";
import { TaskCard } from "@/components/molecules/TaskCard";
import { cn } from "@/lib/utils";
@@ -21,6 +21,7 @@ type TaskBoardProps = {
onCreateTask: () => void;
isCreateDisabled?: boolean;
onTaskSelect?: (task: Task) => void;
onTaskMove?: (taskId: string, status: string) => void;
};
const columns = [
@@ -30,6 +31,7 @@ const columns = [
dot: "bg-slate-400",
accent: "hover:border-slate-400 hover:bg-slate-50",
text: "group-hover:text-slate-700 text-slate-500",
badge: "bg-slate-100 text-slate-600",
},
{
title: "In Progress",
@@ -37,6 +39,7 @@ const columns = [
dot: "bg-purple-500",
accent: "hover:border-purple-400 hover:bg-purple-50",
text: "group-hover:text-purple-600 text-slate-500",
badge: "bg-purple-100 text-purple-700",
},
{
title: "Review",
@@ -44,6 +47,7 @@ const columns = [
dot: "bg-indigo-500",
accent: "hover:border-indigo-400 hover:bg-indigo-50",
text: "group-hover:text-indigo-600 text-slate-500",
badge: "bg-indigo-100 text-indigo-700",
},
{
title: "Done",
@@ -51,6 +55,7 @@ const columns = [
dot: "bg-green-500",
accent: "hover:border-green-400 hover:bg-green-50",
text: "group-hover:text-green-600 text-slate-500",
badge: "bg-emerald-100 text-emerald-700",
},
];
@@ -69,7 +74,11 @@ export function TaskBoard({
onCreateTask,
isCreateDisabled = false,
onTaskSelect,
onTaskMove,
}: TaskBoardProps) {
const [draggingId, setDraggingId] = useState<string | null>(null);
const [activeColumn, setActiveColumn] = useState<string | null>(null);
const grouped = useMemo(() => {
const buckets: Record<string, Task[]> = {};
for (const column of columns) {
@@ -82,12 +91,67 @@ export function TaskBoard({
return buckets;
}, [tasks]);
const handleDragStart =
(task: Task) => (event: React.DragEvent<HTMLDivElement>) => {
setDraggingId(task.id);
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData(
"text/plain",
JSON.stringify({ taskId: task.id, status: task.status }),
);
};
const handleDragEnd = () => {
setDraggingId(null);
setActiveColumn(null);
};
const handleDrop =
(status: string) => (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
setActiveColumn(null);
const raw = event.dataTransfer.getData("text/plain");
if (!raw) return;
try {
const payload = JSON.parse(raw) as { taskId?: string; status?: string };
if (!payload.taskId || !payload.status) return;
if (payload.status === status) return;
onTaskMove?.(payload.taskId, status);
} catch {
// Ignore malformed payloads.
}
};
const handleDragOver =
(status: string) => (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
if (activeColumn !== status) {
setActiveColumn(status);
}
};
const handleDragLeave =
(status: string) => (_event: React.DragEvent<HTMLDivElement>) => {
if (activeColumn === status) {
setActiveColumn(null);
}
};
return (
<div className="grid grid-flow-col auto-cols-[minmax(260px,320px)] gap-4 overflow-x-auto pb-6">
{columns.map((column) => {
const columnTasks = grouped[column.status] ?? [];
return (
<div key={column.title} className="kanban-column min-h-[calc(100vh-260px)]">
<div
key={column.title}
className={cn(
"kanban-column min-h-[calc(100vh-260px)]",
activeColumn === column.status && "ring-2 ring-slate-200",
)}
onDrop={handleDrop(column.status)}
onDragOver={handleDragOver(column.status)}
onDragLeave={handleDragLeave(column.status)}
>
<div className="column-header sticky top-0 z-10 rounded-t-xl border border-b-0 border-slate-200 bg-white/80 px-4 py-3 backdrop-blur">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
@@ -96,37 +160,30 @@ export function TaskBoard({
{column.title}
</h3>
</div>
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-slate-100 text-xs font-semibold text-slate-600">
<span
className={cn(
"flex h-6 w-6 items-center justify-center rounded-full text-xs font-semibold",
column.badge,
)}
>
{columnTasks.length}
</span>
</div>
</div>
<div className="rounded-b-xl border border-t-0 border-slate-200 bg-white p-3">
{column.status === "inbox" ? (
<button
type="button"
onClick={onCreateTask}
disabled={isCreateDisabled}
className={cn(
"group mb-3 flex w-full items-center justify-center rounded-lg border-2 border-dashed border-slate-300 px-4 py-4 text-sm font-medium transition",
column.accent,
isCreateDisabled && "cursor-not-allowed opacity-60"
)}
>
<div className={cn("flex items-center gap-2", column.text)}>
<span className="text-sm font-medium">New task</span>
</div>
</button>
) : null}
<div className="space-y-3">
{columnTasks.map((task) => (
<TaskCard
key={task.id}
title={task.title}
status={column.status}
priority={task.priority}
assignee={task.assignee}
due={formatDueDate(task.due_at)}
onClick={() => onTaskSelect?.(task)}
draggable
isDragging={draggingId === task.id}
onDragStart={handleDragStart(task)}
onDragEnd={handleDragEnd}
/>
))}
</div>