feat: add task buttons to Kanban headers (#863)
* wip: add task * add handler * i18n * add button styles --------- Co-authored-by: Louis Knight-Webb <louis@bloop.ai>
This commit is contained in:
committed by
GitHub
parent
e3727e249d
commit
dd877eaa51
@@ -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 (
|
||||
<KanbanProvider onDragEnd={onDragEnd}>
|
||||
@@ -40,6 +42,7 @@ function TaskKanbanBoard({
|
||||
<KanbanHeader
|
||||
name={statusLabels[status as TaskStatus]}
|
||||
color={statusBoardColors[status as TaskStatus]}
|
||||
onAddTask={onCreateTask}
|
||||
/>
|
||||
<KanbanCards>
|
||||
{statusTasks.map((task, index) => (
|
||||
|
||||
@@ -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 (
|
||||
<Card
|
||||
className={cn(
|
||||
'sticky top-0 z-20 flex shrink-0 items-center gap-2 p-3 border-b border-dashed',
|
||||
'sticky top-0 z-20 flex shrink-0 items-center gap-2 p-3 border-b border-dashed flex gap-2',
|
||||
'bg-background',
|
||||
props.className
|
||||
)}
|
||||
@@ -156,13 +170,32 @@ export const KanbanHeader = (props: KanbanHeaderProps) =>
|
||||
backgroundImage: `linear-gradient(hsl(var(${props.color}) / 0.03), hsl(var(${props.color}) / 0.03))`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="h-2 w-2 rounded-full"
|
||||
style={{ backgroundColor: `hsl(var(${props.color}))` }}
|
||||
/>
|
||||
<p className="m-0 text-sm">{props.name}</p>
|
||||
<span className="flex-1 flex items-center gap-2">
|
||||
<div
|
||||
className="h-2 w-2 rounded-full"
|
||||
style={{ backgroundColor: `hsl(var(${props.color}))` }}
|
||||
/>
|
||||
|
||||
<p className="m-0 text-sm">{props.name}</p>
|
||||
</span>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="m-0 p-0 h-0 text-foreground/50 hover:text-foreground"
|
||||
onClick={props.onAddTask}
|
||||
aria-label={t('actions.addTask')}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">{t('actions.addTask')}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
function restrictToBoundingRectWithRightPadding(
|
||||
transform: Transform,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"noTasks": "このプロジェクトにタスクが見つかりません。",
|
||||
"createFirst": "最初のタスクを作成",
|
||||
"noSearchResults": "検索条件に一致するタスクがありません。"
|
||||
},
|
||||
"actions": {
|
||||
"addTask": "タスクを追加"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ export function ProjectTasks() {
|
||||
|
||||
const [project, setProject] = useState<Project | null>(null);
|
||||
const [error, setError] = useState<string | null>(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}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user