diff --git a/app/(dashboard)/tasks/actions.ts b/app/(dashboard)/tasks/actions.ts index fe40329..c3026ab 100644 --- a/app/(dashboard)/tasks/actions.ts +++ b/app/(dashboard)/tasks/actions.ts @@ -11,6 +11,11 @@ function cleanText(value: FormDataEntryValue | null) { return text.length > 0 ? text : null; } +function cleanRelationId(value: FormDataEntryValue | null) { + const id = cleanText(value); + return id && id !== "__none" ? id : null; +} + function readStatus(value: FormDataEntryValue | null) { const status = typeof value === "string" ? value : "todo"; return TASK_STATUSES.includes(status as (typeof TASK_STATUSES)[number]) @@ -50,8 +55,8 @@ function readPayload(formData: FormData) { description: cleanText(formData.get("description")), status: readStatus(formData.get("status")), priority: readPriority(formData.get("priority")), - client_id: cleanText(formData.get("client_id")), - project_id: cleanText(formData.get("project_id")), + client_id: cleanRelationId(formData.get("client_id")), + project_id: cleanRelationId(formData.get("project_id")), due_at: cleanText(formData.get("due_at")), estimated_minutes: readMinutes(formData.get("estimated_minutes")), actual_minutes: readMinutes(formData.get("actual_minutes")), @@ -122,6 +127,27 @@ export async function completeTaskRecord(formData: FormData) { revalidatePath("/tasks"); } +export async function updateTaskStatusRecord(taskId: string, status: string) { + const { supabase, userId } = await getCurrentUserId(); + const nextStatus = readStatus(status); + + if (!taskId) { + throw new Error("Durumu güncellenecek görev bulunamadı."); + } + + const { error } = await supabase + .from("tasks") + .update({ status: nextStatus }) + .eq("id", taskId) + .eq("user_id", userId); + + if (error) { + throw new Error(`Görev durumu güncellenemedi: ${error.message}`); + } + + revalidatePath("/tasks"); +} + export async function deleteTaskRecord(formData: FormData) { const { supabase, userId } = await getCurrentUserId(); const id = cleanText(formData.get("id")); diff --git a/app/(dashboard)/tasks/tasks-client.tsx b/app/(dashboard)/tasks/tasks-client.tsx index 2a4801b..8a7eca3 100644 --- a/app/(dashboard)/tasks/tasks-client.tsx +++ b/app/(dashboard)/tasks/tasks-client.tsx @@ -4,6 +4,7 @@ import { completeTaskRecord, createTaskRecord, deleteTaskRecord, + updateTaskStatusRecord, updateTaskRecord, } from "@/app/(dashboard)/tasks/actions"; import { Badge, Button, Card, CardContent, Input, Label, Textarea } from "poyraz-ui/atoms"; @@ -30,7 +31,7 @@ import { Plus, Trash2, } from "lucide-react"; -import { useState } from "react"; +import { useEffect, useState, useTransition, type DragEvent } from "react"; export type TaskRelationOption = { id: string; @@ -81,20 +82,48 @@ type TasksClientProps = { }; export function TasksClient({ tasks, clients, projects }: TasksClientProps) { + const [localTasks, setLocalTasks] = useState(tasks); const [query, setQuery] = useState(""); + const [projectFilter, setProjectFilter] = useState("__all"); const [view, setView] = useState<"list" | "kanban">("list"); + const [, startTransition] = useTransition(); + + useEffect(() => { + setLocalTasks(tasks); + }, [tasks]); + + function handleTaskStatusChange(taskId: string, status: TaskListItem["status"]) { + const previousTasks = localTasks; + + setLocalTasks((currentTasks) => + currentTasks.map((task) => (task.id === taskId ? { ...task, status } : task)), + ); + + startTransition(() => { + void updateTaskStatusRecord(taskId, status).catch(() => { + setLocalTasks(previousTasks); + }); + }); + } + const normalizedQuery = query.trim().toLowerCase(); + const filteredByProject = + projectFilter === "__all" + ? localTasks + : projectFilter === "__none" + ? localTasks.filter((task) => !task.project_id) + : localTasks.filter((task) => task.project_id === projectFilter); const filteredTasks = normalizedQuery - ? tasks.filter((task) => + ? filteredByProject.filter((task) => [task.title, task.description, task.clientName, task.projectName] .filter(Boolean) .some((value) => value!.toLowerCase().includes(normalizedQuery)), ) - : tasks; + : filteredByProject; - const doneCount = tasks.filter((task) => task.status === "done").length; - const overdueCount = tasks.filter((task) => isOverdue(task)).length; - const urgentCount = tasks.filter((task) => task.priority === "urgent").length; + const doneCount = localTasks.filter((task) => task.status === "done").length; + const overdueCount = localTasks.filter((task) => isOverdue(task)).length; + const urgentCount = localTasks.filter((task) => task.priority === "urgent").length; return (