From dd877eaa51354c5dd9d0a95cd96430c9cb187159 Mon Sep 17 00:00:00 2001 From: Gabriel Gordon-Hall Date: Fri, 26 Sep 2025 16:34:52 +0100 Subject: [PATCH] feat: add task buttons to Kanban headers (#863) * wip: add task * add handler * i18n * add button styles --------- Co-authored-by: Louis Knight-Webb --- .../src/components/tasks/TaskKanbanBoard.tsx | 3 ++ .../components/ui/shadcn-io/kanban/index.tsx | 53 +++++++++++++++---- frontend/src/i18n/locales/en/tasks.json | 3 ++ frontend/src/i18n/locales/es/tasks.json | 3 ++ frontend/src/i18n/locales/ja/tasks.json | 3 ++ frontend/src/pages/project-tasks.tsx | 2 + 6 files changed, 57 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/tasks/TaskKanbanBoard.tsx b/frontend/src/components/tasks/TaskKanbanBoard.tsx index 82da649c..7d9a6f14 100644 --- a/frontend/src/components/tasks/TaskKanbanBoard.tsx +++ b/frontend/src/components/tasks/TaskKanbanBoard.tsx @@ -22,6 +22,7 @@ interface TaskKanbanBoardProps { onDuplicateTask?: (task: Task) => void; onViewTaskDetails: (task: Task) => void; selectedTask?: Task; + onCreateTask?: () => void; } function TaskKanbanBoard({ @@ -32,6 +33,7 @@ function TaskKanbanBoard({ onDuplicateTask, onViewTaskDetails, selectedTask, + onCreateTask, }: TaskKanbanBoardProps) { return ( @@ -40,6 +42,7 @@ function TaskKanbanBoard({ {statusTasks.map((task, index) => ( diff --git a/frontend/src/components/ui/shadcn-io/kanban/index.tsx b/frontend/src/components/ui/shadcn-io/kanban/index.tsx index 425e7047..10ba49ba 100644 --- a/frontend/src/components/ui/shadcn-io/kanban/index.tsx +++ b/frontend/src/components/ui/shadcn-io/kanban/index.tsx @@ -1,6 +1,12 @@ 'use client'; import { Card } from '@/components/ui/card'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@/components/ui/tooltip'; import { cn } from '@/lib/utils'; import type { DragEndEvent, Modifier } from '@dnd-kit/core'; import { @@ -13,9 +19,12 @@ import { useSensors, } from '@dnd-kit/core'; import { type ReactNode, type Ref, type KeyboardEvent } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Plus } from 'lucide-react'; import type { ClientRect } from '@dnd-kit/core'; import type { Transform } from '@dnd-kit/utilities'; +import { Button } from '../../button'; export type { DragEndEvent } from '@dnd-kit/core'; export type Status = { @@ -140,15 +149,20 @@ export type KanbanHeaderProps = name: Status['name']; color: Status['color']; className?: string; + onAddTask?: () => void; }; -export const KanbanHeader = (props: KanbanHeaderProps) => - 'children' in props ? ( - props.children - ) : ( +export const KanbanHeader = (props: KanbanHeaderProps) => { + const { t } = useTranslation('tasks'); + + if ('children' in props) { + return props.children; + } + + return ( backgroundImage: `linear-gradient(hsl(var(${props.color}) / 0.03), hsl(var(${props.color}) / 0.03))`, }} > -
-

{props.name}

+ +
+ +

{props.name}

+ + + + + + + {t('actions.addTask')} + + ); +}; function restrictToBoundingRectWithRightPadding( transform: Transform, diff --git a/frontend/src/i18n/locales/en/tasks.json b/frontend/src/i18n/locales/en/tasks.json index e2f01e41..7fc49892 100644 --- a/frontend/src/i18n/locales/en/tasks.json +++ b/frontend/src/i18n/locales/en/tasks.json @@ -4,5 +4,8 @@ "noTasks": "No tasks found for this project.", "createFirst": "Create First Task", "noSearchResults": "No tasks match your search." + }, + "actions": { + "addTask": "Add task" } } diff --git a/frontend/src/i18n/locales/es/tasks.json b/frontend/src/i18n/locales/es/tasks.json index 4286cb13..27d01c8a 100644 --- a/frontend/src/i18n/locales/es/tasks.json +++ b/frontend/src/i18n/locales/es/tasks.json @@ -4,5 +4,8 @@ "noTasks": "No se encontraron tareas para este proyecto.", "createFirst": "Crear Primera Tarea", "noSearchResults": "Ninguna tarea coincide con tu búsqueda." + }, + "actions": { + "addTask": "Agregar tarea" } } diff --git a/frontend/src/i18n/locales/ja/tasks.json b/frontend/src/i18n/locales/ja/tasks.json index 1fcb3576..5485a03e 100644 --- a/frontend/src/i18n/locales/ja/tasks.json +++ b/frontend/src/i18n/locales/ja/tasks.json @@ -4,5 +4,8 @@ "noTasks": "このプロジェクトにタスクが見つかりません。", "createFirst": "最初のタスクを作成", "noSearchResults": "検索条件に一致するタスクがありません。" + }, + "actions": { + "addTask": "タスクを追加" } } diff --git a/frontend/src/pages/project-tasks.tsx b/frontend/src/pages/project-tasks.tsx index b65b529b..3cea4abd 100644 --- a/frontend/src/pages/project-tasks.tsx +++ b/frontend/src/pages/project-tasks.tsx @@ -61,6 +61,7 @@ export function ProjectTasks() { const [project, setProject] = useState(null); const [error, setError] = useState(null); + // Helper functions to open task forms const handleCreateTask = () => { if (project?.id) { @@ -540,6 +541,7 @@ export function ProjectTasks() { onDuplicateTask={handleDuplicateTaskCallback} onViewTaskDetails={handleViewTaskDetails} selectedTask={selectedTask || undefined} + onCreateTask={handleCreateNewTask} />
)}