From 849480b3cfd01c7e4be332c58031f253bfcddff8 Mon Sep 17 00:00:00 2001 From: Alma Date: Sat, 12 Apr 2025 14:55:57 +0200 Subject: [PATCH] working leantime widget 44 --- app/api/leantime/tasks/route.ts | 100 ++++++++++++++++++++++++++++++++ components/flow.tsx | 64 +++++++++++++------- 2 files changed, 143 insertions(+), 21 deletions(-) create mode 100644 app/api/leantime/tasks/route.ts diff --git a/app/api/leantime/tasks/route.ts b/app/api/leantime/tasks/route.ts new file mode 100644 index 00000000..40b4eb9f --- /dev/null +++ b/app/api/leantime/tasks/route.ts @@ -0,0 +1,100 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getServerSession } from "next-auth/next"; +import { authOptions } from "@/app/api/auth/[...nextauth]/route"; + +interface Task { + id: string; + headline: string; + projectName: string; + projectId: number; + status: number; + dueDate: string | null; + milestone: string | null; + details: string | null; +} + +async function getLeantimeUserId(email: string): Promise { + try { + const response = await fetch(`${process.env.LEANTIME_API_URL}/api/jsonrpc`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${process.env.LEANTIME_TOKEN}`, + }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'leantime.rpc.users.getAll', + id: 1, + }), + }); + + if (!response.ok) { + console.error('Failed to fetch Leantime users'); + return null; + } + + const data = await response.json(); + const users = data.result; + const user = users.find((u: any) => u.email === email); + return user ? user.id : null; + } catch (error) { + console.error('Error fetching Leantime user ID:', error); + return null; + } +} + +export async function GET(request: NextRequest) { + try { + const session = await getServerSession(authOptions); + if (!session?.user?.email) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const userId = await getLeantimeUserId(session.user.email); + if (!userId) { + return NextResponse.json({ error: "User not found in Leantime" }, { status: 404 }); + } + + // Fetch tasks assigned to the user + const response = await fetch(`${process.env.LEANTIME_API_URL}/api/jsonrpc`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${process.env.LEANTIME_TOKEN}`, + }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'leantime.rpc.tickets.getAll', + params: { + userId: userId, + status: "all", + }, + id: 1, + }), + }); + + if (!response.ok) { + throw new Error('Failed to fetch tasks from Leantime'); + } + + const data = await response.json(); + const tasks = data.result.map((task: any) => ({ + id: task.id.toString(), + headline: task.headline, + projectName: task.projectName, + projectId: task.projectId, + status: task.status, + dueDate: task.dateToFinish || null, + milestone: task.milestoneName || null, + details: task.description || null, + })); + + return NextResponse.json({ tasks }); + } catch (error) { + console.error('Error in tasks route:', error); + return NextResponse.json( + { error: "Failed to fetch tasks" }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/components/flow.tsx b/components/flow.tsx index 29acf531..8a46e475 100644 --- a/components/flow.tsx +++ b/components/flow.tsx @@ -10,7 +10,7 @@ interface Task { headline: string; projectName: string; projectId: number; - status: string; + status: number; dueDate: string | null; milestone: string | null; details: string | null; @@ -59,9 +59,9 @@ export function Flow() { }, {}); // Convert to array and sort tasks by due date within each project - const sortedProjectTasks = Object.entries(groupedTasks).map(([projectName, tasks]) => ({ + const sortedProjectTasks = Object.entries(groupedTasks as { [key: string]: Task[] }).map(([projectName, tasks]) => ({ projectName, - tasks: tasks.sort((a, b) => { + tasks: tasks.sort((a: Task, b: Task) => { if (!a.dueDate) return 1; if (!b.dueDate) return -1; return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime(); @@ -108,6 +108,22 @@ export function Flow() { }); }; + const getStatusInfo = (status: number): { label: string; className: string } => { + switch (status) { + case 1: + return { label: 'NEW', className: 'bg-blue-100 text-blue-800' }; + case 2: + return { label: 'IN PROGRESS', className: 'bg-yellow-100 text-yellow-800' }; + case 3: + return { label: 'DONE', className: 'bg-green-100 text-green-800' }; + case 4: + return { label: 'BLOCKED', className: 'bg-red-100 text-red-800' }; + case 0: + default: + return { label: 'UNKNOWN', className: 'bg-gray-100 text-gray-800' }; + } + }; + return ( @@ -137,25 +153,31 @@ export function Flow() {

{project.projectName}

- {project.tasks.map((task) => ( -
- - {task.headline} - -
- - {task.status} - - - {formatDate(task.dueDate)} - + {project.tasks.map((task) => { + const statusInfo = getStatusInfo(task.status); + return ( +
+
+ + {task.headline} + + {task.milestone && ( + + {task.milestone} + + )} +
+
+ + {statusInfo.label} + + + {formatDate(task.dueDate)} + +
-
- ))} + ); + })}
))}