diff --git a/app/api/leantime/status-labels/route.ts b/app/api/leantime/status-labels/route.ts index 6e1fb88..2af81ed 100644 --- a/app/api/leantime/status-labels/route.ts +++ b/app/api/leantime/status-labels/route.ts @@ -26,8 +26,6 @@ async function getLeantimeUserId(email: string): Promise { } try { - console.log('Fetching Leantime user with token:', process.env.LEANTIME_TOKEN ? 'Token present' : 'Token missing'); - const response = await fetch('https://agilite.slm-lab.net/api/jsonrpc', { method: 'POST', headers: { @@ -45,18 +43,12 @@ async function getLeantimeUserId(email: string): Promise { }); if (!response.ok) { - console.error('Failed to fetch user from Leantime:', { - status: response.status, - statusText: response.statusText - }); throw new Error(`Failed to fetch user from Leantime: ${response.status} ${response.statusText}`); } const data = await response.json(); - console.log('Leantime user response:', data); if (!data.result || data.result === false) { - console.log('User not found in Leantime'); return null; } @@ -74,7 +66,6 @@ async function getLeantimeUserId(email: string): Promise { export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); - console.log('Session:', session ? 'Present' : 'Missing'); if (!session) { return NextResponse.json( @@ -90,11 +81,8 @@ export async function GET(request: NextRequest) { ); } - console.log('User email:', session.user.email); - // Get Leantime user ID const leantimeUserId = await getLeantimeUserId(session.user.email); - console.log('Leantime user ID:', leantimeUserId); if (!leantimeUserId) { return NextResponse.json( @@ -104,7 +92,6 @@ export async function GET(request: NextRequest) { } // Get all tasks assigned to the user - console.log('Fetching tasks for user:', leantimeUserId); const response = await fetch('https://agilite.slm-lab.net/api/jsonrpc', { method: 'POST', headers: { @@ -125,22 +112,16 @@ export async function GET(request: NextRequest) { }); if (!response.ok) { - console.error('Failed to fetch tasks:', { - status: response.status, - statusText: response.statusText - }); throw new Error(`Failed to fetch tasks: ${response.status} ${response.statusText}`); } const data = await response.json(); - console.log('Tasks response:', data); if (!data.result) { return NextResponse.json({ projects: [] }); } // Get project details to include project names - console.log('Fetching projects'); const projectsResponse = await fetch('https://agilite.slm-lab.net/api/jsonrpc', { method: 'POST', headers: { @@ -155,15 +136,10 @@ export async function GET(request: NextRequest) { }); if (!projectsResponse.ok) { - console.error('Failed to fetch projects:', { - status: projectsResponse.status, - statusText: projectsResponse.statusText - }); throw new Error(`Failed to fetch projects: ${projectsResponse.status} ${projectsResponse.statusText}`); } const projectsData = await projectsResponse.json(); - console.log('Projects response:', projectsData); // Create a map of projects with their tasks grouped by status const projectMap = new Map(); @@ -221,7 +197,6 @@ export async function GET(request: NextRequest) { // Convert the map to an array and sort projects by name const projects = Array.from(projectMap.values()).sort((a, b) => a.name.localeCompare(b.name)); - console.log('Final projects:', projects); return NextResponse.json({ projects }); } catch (error) { diff --git a/app/api/leantime/tasks/route.ts b/app/api/leantime/tasks/route.ts index 337f1b7..49667b5 100644 --- a/app/api/leantime/tasks/route.ts +++ b/app/api/leantime/tasks/route.ts @@ -3,6 +3,9 @@ import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/[...nextauth]/route"; import { prisma } from "@/lib/prisma"; +// Cache for Leantime user IDs +const userCache = new Map(); + interface Task { id: string; headline: string; @@ -20,70 +23,45 @@ interface Task { } async function getLeantimeUserId(email: string): Promise { + // Check cache first + if (userCache.has(email)) { + return userCache.get(email)!; + } + try { - if (!process.env.LEANTIME_TOKEN) { - console.error('LEANTIME_TOKEN is not set in environment variables'); - return null; - } - - console.log('Fetching Leantime users for email:', email); - console.log('API URL:', process.env.LEANTIME_API_URL); - console.log('Token length:', process.env.LEANTIME_TOKEN.length); - - const headers: Record = { - 'Content-Type': 'application/json', - 'X-API-Key': process.env.LEANTIME_TOKEN - }; - - const response = await fetch(`${process.env.LEANTIME_API_URL}/api/jsonrpc`, { + const response = await fetch('https://agilite.slm-lab.net/api/jsonrpc', { method: 'POST', - headers, + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': process.env.LEANTIME_TOKEN || '', + }, body: JSON.stringify({ jsonrpc: '2.0', - method: 'leantime.rpc.users.getAll', - id: 1 + method: 'leantime.rpc.Users.Users.getUserByEmail', + id: 1, + params: { + email: email + } }), }); - const responseText = await response.text(); - console.log('Raw Leantime response:', responseText); - if (!response.ok) { - console.error('Failed to fetch Leantime users:', { - status: response.status, - statusText: response.statusText, - headers: Object.fromEntries(response.headers.entries()) - }); + throw new Error(`Failed to fetch user from Leantime: ${response.status} ${response.statusText}`); + } + + const data = await response.json(); + + if (!data.result || data.result === false) { return null; } - let data; - try { - data = JSON.parse(responseText); - } catch (e) { - console.error('Failed to parse Leantime response:', e); - return null; - } - - console.log('Leantime users response:', data); - - if (!data.result || !Array.isArray(data.result)) { - console.error('Invalid response format from Leantime users API'); - return null; - } - - const users = data.result; - const user = users.find((u: any) => u.username === email); - - if (user) { - console.log('Found Leantime user:', { id: user.id, username: user.username }); - } else { - console.log('No Leantime user found for username:', email); - } - - return user ? user.id : null; + // Cache the user ID + userCache.set(email, data.result.id); + // Clear cache after 5 minutes + setTimeout(() => userCache.delete(email), 5 * 60 * 1000); + return data.result.id; } catch (error) { - console.error('Error fetching Leantime user ID:', error); + console.error('Error getting Leantime user ID:', error); return null; } } @@ -91,116 +69,101 @@ async function getLeantimeUserId(email: string): Promise { export async function GET(request: NextRequest) { try { const session = await getServerSession(authOptions); - if (!session?.user?.email) { - return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + + if (!session) { + return NextResponse.json( + { error: "Unauthorized", message: "No session found. Please sign in." }, + { status: 401 } + ); } - console.log('Fetching tasks for user:', session.user.email); - const userId = await getLeantimeUserId(session.user.email); + if (!session.user?.email) { + return NextResponse.json( + { error: "Unauthorized", message: "No email found in session. Please sign in again." }, + { status: 401 } + ); + } + + // Get Leantime user ID + const leantimeUserId = await getLeantimeUserId(session.user.email); - if (!userId) { - console.error('User not found in Leantime'); - return NextResponse.json({ error: "User not found in Leantime" }, { status: 404 }); + if (!leantimeUserId) { + return NextResponse.json( + { error: "User not found", message: "Could not find user in Leantime. Please check your email." }, + { status: 404 } + ); } - console.log('Fetching tasks for Leantime user ID:', userId); - const headers: Record = { - 'Content-Type': 'application/json', - 'X-API-Key': process.env.LEANTIME_TOKEN! - }; - - const response = await fetch(`${process.env.LEANTIME_API_URL}/api/jsonrpc`, { + // Get all tasks assigned to the user + const response = await fetch('https://agilite.slm-lab.net/api/jsonrpc', { method: 'POST', - headers, + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': process.env.LEANTIME_TOKEN || '', + }, body: JSON.stringify({ jsonrpc: '2.0', - method: 'leantime.rpc.tickets.getAll', + method: 'leantime.rpc.Tickets.Tickets.getAll', + id: 1, params: { - userId: userId, - status: "all" - }, - id: 1 - }), + projectId: 0, // 0 means all projects + userId: leantimeUserId, + status: "all", + limit: 100 + } + }) }); - const responseText = await response.text(); - console.log('Tasks API response status:', response.status); - if (!response.ok) { - console.error('Failed to fetch tasks from Leantime:', { - status: response.status, - statusText: response.statusText - }); - throw new Error('Failed to fetch tasks from Leantime'); + throw new Error(`Failed to fetch tasks: ${response.status} ${response.statusText}`); } - let data; - try { - data = JSON.parse(responseText); - } catch (e) { - console.error('Failed to parse tasks response'); - throw new Error('Invalid response format from Leantime'); + const data = await response.json(); + + if (!data.result) { + return NextResponse.json({ tasks: [] }); } - if (!data.result || !Array.isArray(data.result)) { - console.error('Invalid response format from Leantime tasks API'); - throw new Error('Invalid response format from Leantime'); - } + // Filter out tasks that are not in progress or new + const filteredTasks = data.result.filter((task: any) => { + const status = task.status.toString(); + return status === '1' || status === '2'; // 1 = new, 2 = in progress + }); - // Log only the number of tasks and their IDs - console.log('Received tasks count:', data.result.length); - console.log('Task IDs:', data.result.map((task: any) => task.id)); - - const tasks = data.result - .filter((task: any) => { - // Log raw task data for debugging - console.log('Raw task data:', { - id: task.id, - headline: task.headline, - status: task.status, - type: task.type, - dependingTicketId: task.dependingTicketId - }); - - // Filter out any task (main or subtask) that has status Done (5) - if (task.status === 5) { - console.log(`Filtering out Done task ${task.id} (type: ${task.type || 'main'}, status: ${task.status})`); - return false; - } - - // Convert both to strings for comparison to handle any type mismatches - const taskEditorId = String(task.editorId).trim(); - const currentUserId = String(userId).trim(); - - // Only show tasks where the user is the editor - const isUserEditor = taskEditorId === currentUserId; - console.log(`Task ${task.id}: status=${task.status}, type=${task.type || 'main'}, parentId=${task.dependingTicketId || 'none'}, isUserEditor=${isUserEditor}`); - return isUserEditor; + // Get project details to include project names + const projectsResponse = await fetch('https://agilite.slm-lab.net/api/jsonrpc', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': process.env.LEANTIME_TOKEN || '', + }, + body: JSON.stringify({ + jsonrpc: '2.0', + method: 'leantime.rpc.Projects.getAll', + id: 1 }) - .map((task: any) => ({ - id: task.id.toString(), - headline: task.headline, - projectName: task.projectName, - projectId: task.projectId, - status: task.status, - dateToFinish: task.dateToFinish || null, - milestone: task.type || null, - details: task.description || null, - createdOn: task.dateCreated, - editedOn: task.editedOn || null, - editorId: task.editorId, - editorFirstname: task.editorFirstname, - editorLastname: task.editorLastname, - type: task.type || null, // Added type field to identify subtasks - dependingTicketId: task.dependingTicketId || null // Added parent task reference - })); + }); - console.log(`Found ${tasks.length} tasks assigned to user ${userId}`); - return NextResponse.json(tasks); + if (!projectsResponse.ok) { + throw new Error(`Failed to fetch projects: ${projectsResponse.status} ${projectsResponse.statusText}`); + } + + const projectsData = await projectsResponse.json(); + + // Map tasks to include project names + const tasksWithProjects = filteredTasks.map((task: any) => { + const project = projectsData.result.find((p: any) => p.id === task.projectId); + return { + ...task, + projectName: project ? project.name : `Project ${task.projectId}` + }; + }); + + return NextResponse.json({ tasks: tasksWithProjects }); } catch (error) { - console.error('Error in tasks route:', error); + console.error('Error fetching tasks:', error); return NextResponse.json( - { error: "Failed to fetch tasks" }, + { error: "Failed to fetch tasks", message: error instanceof Error ? error.message : "Unknown error occurred" }, { status: 500 } ); }