widget leantime refactor

This commit is contained in:
alma 2026-01-15 22:46:31 +01:00
parent 9416cca90a
commit 334bfd04e3

View File

@ -120,57 +120,82 @@ async function fetchTwentyTasks(): Promise<TwentyTask[]> {
return [];
}
// Log raw response for debugging
logger.debug('[TWENTY_CRM_TASKS] Raw GraphQL response', {
hasData: !!data.data,
dataKeys: data.data ? Object.keys(data.data) : [],
tasksEdgesCount: data.data?.tasks?.edges?.length || 0,
sampleResponse: JSON.stringify(data.data).substring(0, 500),
});
// Try different possible response structures
// Twenty CRM may use different field names depending on version
let activitiesData = null;
if (data.data?.tasks?.edges) {
activitiesData = data.data.tasks.edges;
logger.debug('[TWENTY_CRM_TASKS] Found tasks.edges', { count: activitiesData.length });
} else if (data.data?.activities?.edges) {
activitiesData = data.data.activities.edges;
logger.debug('[TWENTY_CRM_TASKS] Found activities.edges', { count: activitiesData.length });
} else if (data.data?.findManyTasks?.edges) {
activitiesData = data.data.findManyTasks.edges;
logger.debug('[TWENTY_CRM_TASKS] Found findManyTasks.edges', { count: activitiesData.length });
} else if (data.data?.findManyActivities?.edges) {
activitiesData = data.data.findManyActivities.edges;
logger.debug('[TWENTY_CRM_TASKS] Found findManyActivities.edges', { count: activitiesData.length });
} else if (Array.isArray(data.data?.tasks)) {
// Direct array response
activitiesData = data.data.tasks.map((node: any) => ({ node }));
logger.debug('[TWENTY_CRM_TASKS] Found tasks as array', { count: activitiesData.length });
} else if (Array.isArray(data.data?.activities)) {
// Direct array response
activitiesData = data.data.activities.map((node: any) => ({ node }));
logger.debug('[TWENTY_CRM_TASKS] Found activities as array', { count: activitiesData.length });
} else if (Array.isArray(data.data)) {
// Root level array
activitiesData = data.data.map((node: any) => ({ node }));
logger.debug('[TWENTY_CRM_TASKS] Found data as array', { count: activitiesData.length });
}
if (!activitiesData) {
logger.warn('[TWENTY_CRM_TASKS] Unexpected response format from Twenty CRM', {
dataKeys: Object.keys(data.data || {}),
fullData: JSON.stringify(data.data).substring(0, 1000),
fullData: JSON.stringify(data.data).substring(0, 2000),
});
return [];
}
logger.debug('[TWENTY_CRM_TASKS] Activities data extracted', {
count: activitiesData.length,
sample: activitiesData.length > 0 ? {
firstTask: {
id: activitiesData[0]?.node?.id || activitiesData[0]?.id,
title: activitiesData[0]?.node?.title || activitiesData[0]?.title,
status: activitiesData[0]?.node?.status || activitiesData[0]?.status,
dueAt: activitiesData[0]?.node?.dueAt || activitiesData[0]?.dueAt,
}
} : null,
});
// Transform Twenty CRM tasks to match our Task interface
// Filter client-side for overdue tasks (dueAt < today) and not completed (status !== 'Done')
const tasks: TwentyTask[] = activitiesData
.map((edge: any) => {
const node = edge.node || edge; // Handle both edge.node and direct node
const allTasks: TwentyTask[] = activitiesData.map((edge: any) => {
const node = edge.node || edge; // Handle both edge.node and direct node
// Extract text from bodyV2 (RichTextV2 type)
let bodyText = null;
if (node.bodyV2) {
// bodyV2 has blocknote and markdown subfields
bodyText = node.bodyV2.markdown || node.bodyV2.blocknote || null;
}
return {
id: node.id,
title: node.title || 'Untitled Task',
bodyV2: node.bodyV2 || null,
dueAt: node.dueAt || null,
status: node.status || null,
type: node.type || 'Task',
assigneeId: node.assigneeId || null,
return {
id: node.id,
title: node.title || 'Untitled Task',
bodyV2: node.bodyV2 || null,
dueAt: node.dueAt || null,
status: node.status || null,
type: node.type || 'Task',
assigneeId: node.assigneeId || null,
assignee: node.assignee ? {
id: node.assignee.id,
name: node.assignee.name ? {
@ -178,26 +203,53 @@ async function fetchTwentyTasks(): Promise<TwentyTask[]> {
lastName: node.assignee.name.lastName || null,
} : null,
} : null,
// Store extracted body text for easier access
_bodyText: bodyText,
};
})
// Store extracted body text for easier access
_bodyText: bodyText,
};
});
// Log all tasks before filtering for debugging
logger.debug('[TWENTY_CRM_TASKS] All tasks before filtering', {
count: allTasks.length,
tasks: allTasks.map(t => ({
id: t.id,
title: t.title,
status: t.status,
dueAt: t.dueAt,
hasDueDate: !!t.dueAt,
})),
});
// Filter client-side for overdue tasks (dueAt < today) and not completed (status !== 'Done')
const today = new Date();
today.setHours(0, 0, 0, 0);
const tasks: TwentyTask[] = allTasks
.filter((task: TwentyTask) => {
// Filter: only overdue tasks (dueAt < today) and not completed
if (task.status === 'Done') {
logger.debug('[TWENTY_CRM_TASKS] Filtering out Done task', { id: task.id, status: task.status });
return false;
}
if (!task.dueAt) {
logger.debug('[TWENTY_CRM_TASKS] Filtering out task without due date', { id: task.id });
return false; // Exclude tasks without due date
}
const taskDueDate = new Date(task.dueAt);
taskDueDate.setHours(0, 0, 0, 0);
const today = new Date();
today.setHours(0, 0, 0, 0);
return taskDueDate < today; // Only overdue tasks
const isOverdue = taskDueDate < today;
logger.debug('[TWENTY_CRM_TASKS] Task date check', {
id: task.id,
dueAt: task.dueAt,
taskDueDate: taskDueDate.toISOString(),
today: today.toISOString(),
isOverdue,
});
return isOverdue; // Only overdue tasks
})
.sort((a: TwentyTask, b: TwentyTask) => {
// Sort by dueAt (oldest first)
@ -207,6 +259,16 @@ async function fetchTwentyTasks(): Promise<TwentyTask[]> {
return dateA - dateB;
});
logger.debug('[TWENTY_CRM_TASKS] Tasks after filtering', {
count: tasks.length,
tasks: tasks.map(t => ({
id: t.id,
title: t.title,
status: t.status,
dueAt: t.dueAt,
})),
});
logger.debug('[TWENTY_CRM_TASKS] Successfully fetched tasks from Twenty CRM', {
count: tasks.length,
});