diff --git a/app/api/twenty-crm/tasks/route.ts b/app/api/twenty-crm/tasks/route.ts index f4ff3ec..bc34424 100644 --- a/app/api/twenty-crm/tasks/route.ts +++ b/app/api/twenty-crm/tasks/route.ts @@ -23,10 +23,90 @@ interface TwentyTask { }; } +/** + * Get Twenty CRM workspace member ID by email + */ +async function getTwentyCrmUserId(email: string): Promise { + try { + if (!process.env.TWENTY_CRM_API_URL || !process.env.TWENTY_CRM_API_KEY) { + return null; + } + + const apiUrl = process.env.TWENTY_CRM_API_URL.endsWith('/graphql') + ? process.env.TWENTY_CRM_API_URL + : `${process.env.TWENTY_CRM_API_URL}/graphql`; + + // Query to find workspace member by email + const query = ` + query GetWorkspaceMemberByEmail { + workspaceMembers { + edges { + node { + id + user { + email + } + } + } + } + } + `; + + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${process.env.TWENTY_CRM_API_KEY}`, + }, + body: JSON.stringify({ query }), + }); + + if (!response.ok) { + logger.error('[TWENTY_CRM_TASKS] Failed to fetch workspace members', { + status: response.status, + }); + return null; + } + + const data = await response.json(); + + if (data.errors) { + logger.error('[TWENTY_CRM_TASKS] GraphQL errors fetching workspace members', { + errors: data.errors, + }); + return null; + } + + // Find workspace member with matching email + const members = data.data?.workspaceMembers?.edges || []; + const member = members.find((edge: any) => + edge.node?.user?.email?.toLowerCase() === email.toLowerCase() + ); + + if (member) { + logger.debug('[TWENTY_CRM_TASKS] Found workspace member', { + memberId: member.node.id, + emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12), + }); + return member.node.id; + } + + logger.warn('[TWENTY_CRM_TASKS] Workspace member not found', { + emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12), + }); + return null; + } catch (error) { + logger.error('[TWENTY_CRM_TASKS] Error fetching workspace member', { + error: error instanceof Error ? error.message : String(error), + }); + return null; + } +} + /** * Fetch tasks from Twenty CRM using GraphQL API */ -async function fetchTwentyTasks(): Promise { +async function fetchTwentyTasks(userId?: string): Promise { try { if (!process.env.TWENTY_CRM_API_URL) { logger.error('[TWENTY_CRM_TASKS] TWENTY_CRM_API_URL is not set in environment variables'); @@ -216,18 +296,40 @@ async function fetchTwentyTasks(): Promise { title: t.title, status: t.status, dueAt: t.dueAt, + assigneeId: t.assigneeId, hasDueDate: !!t.dueAt, })), }); - // Filter client-side for overdue tasks (dueAt < today) and not completed (status !== 'Done') + // Filter by assignee if userId is provided + let filteredByAssignee = allTasks; + if (userId) { + filteredByAssignee = allTasks.filter((task: TwentyTask) => { + const isAssignedToUser = task.assigneeId === userId; + if (!isAssignedToUser) { + logger.debug('[TWENTY_CRM_TASKS] Filtering out task not assigned to user', { + id: task.id, + assigneeId: task.assigneeId, + userId, + }); + } + return isAssignedToUser; + }); + logger.error('[TWENTY_CRM_TASKS] Tasks after assignee filter', { + before: allTasks.length, + after: filteredByAssignee.length, + userId, + }); + } + + // Filter client-side for overdue tasks (dueAt <= today) and not completed (status !== 'Done') // Use local date for comparison to avoid timezone issues const now = new Date(); const todayYear = now.getFullYear(); const todayMonth = now.getMonth(); const todayDay = now.getDate(); - const tasks: TwentyTask[] = allTasks + const tasks: TwentyTask[] = filteredByAssignee .filter((task: TwentyTask) => { // Filter: only overdue tasks (dueAt < today) and not completed if (task.status === 'Done') { @@ -311,7 +413,22 @@ export async function GET(request: NextRequest) { forceRefresh, }); - const tasks = await fetchTwentyTasks(); + // Get Twenty CRM user ID from email + const twentyCrmUserId = await getTwentyCrmUserId(session.user.email); + + if (!twentyCrmUserId) { + logger.warn('[TWENTY_CRM_TASKS] User not found in Twenty CRM, returning empty tasks', { + emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12), + }); + return NextResponse.json([]); + } + + logger.debug('[TWENTY_CRM_TASKS] Found Twenty CRM user ID', { + userId: twentyCrmUserId, + emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12), + }); + + const tasks = await fetchTwentyTasks(twentyCrmUserId); // Transform to match Leantime task format for consistency const transformedTasks = tasks.map((task) => ({