v1.0.0-beta3: Add Planner task creation with notes, fix folder name trimming, fix Excel folder link fallback

This commit is contained in:
2025-12-22 20:00:45 +00:00
parent 65fb9a52d9
commit 5d9abb4cae
3 changed files with 112 additions and 41 deletions
+111 -3
View File
@@ -273,7 +273,7 @@ function calculateJobFullName(record: any): string {
const companyClient = record.Company_Client || record.company_client || '';
const parts = [jobName, jobAddress, companyClient].filter(p => p && String(p).trim());
return parts.join(' - ');
return parts.map(p => String(p).trim()).join(' - ').trim();
}
/**
@@ -376,6 +376,84 @@ function calculateActive(record: any): boolean {
return activeStatuses.includes(jobStatus);
}
// ============================================================================
// PLANNER TASK CREATION
// ============================================================================
async function createPlannerTask({
jobNumber,
jobFullName,
dueDate,
}: {
jobNumber: string;
jobFullName: string;
dueDate?: string;
}) {
try {
const client = await getGraphClient();
// Hardcoded assignee for testing: aewing@cardoza.construction
const assigneeId = '1a6e9d10-138a-43e4-a09e-1f50463148c9';
const assigneeName = 'Alex Ewing'; // Display name
// Parse due date if provided (ISO format to Date object)
let dueDateObj: any = undefined;
if (dueDate) {
const d = new Date(dueDate);
if (!isNaN(d.getTime())) {
dueDateObj = d.toISOString();
}
}
// Task title: "Disposition New Job Folder for [Job_Number] [Job_Full_Name]"
const title = `Disposition New Job Folder for ${jobNumber} ${jobFullName}`;
// Create the Planner task
const groupId = process.env.PLANNER_GROUP_ID;
const planId = process.env.PLANNER_PLAN_ID;
const bucketId = process.env.PLANNER_BUCKET_ID;
if (!groupId || !planId || !bucketId) {
throw new Error('Missing Planner configuration: PLANNER_GROUP_ID, PLANNER_PLAN_ID, or PLANNER_BUCKET_ID');
}
const newTask = await client.api('/planner/tasks').post({
planId,
bucketId,
title,
assignments: {
[assigneeId]: {
'@odata.type': 'microsoft.graph.plannerAssignment',
orderHint: ' !',
},
},
...(dueDateObj ? { dueDateTime: dueDateObj } : {}),
});
// Set the Notes/Description via task details (match the working test script)
const taskDetails = await client.api(`/planner/tasks/${newTask.id}/details`).get();
const detailsEtag = taskDetails['@odata.etag'];
console.log(`[Planner] Task created ${newTask.id}, fetched details ETag: ${detailsEtag}`);
await client
.api(`/planner/tasks/${newTask.id}/details`)
.header('If-Match', detailsEtag)
.patch({
description: 'Move the new folder to the correct Job Pages folder.',
});
console.log(`[Planner] Successfully patched description`);
console.log(`✓ Planner task created: "${title}" assigned to ${assigneeName}`);
return { success: true, taskId: newTask.id, title };
} catch (error: any) {
console.error('✗ Planner task creation failed:', error.message);
await logError('Planner Task Creation', error, {
jobNumber,
jobFullName,
});
throw error;
}
}
// Initialize table cache on startup
async function initializeTableCache() {
const client = await getGraphClient();
@@ -542,8 +620,19 @@ async function updateExcelFolderLink(pbId: string, link: string) {
return vals && vals[pbIdColIndex] === pbId;
});
if (!target) {
console.warn('⚠️ Excel row not found for pb_id:', pbId);
return;
// Row was just inserted at index 0, try updating that directly
console.warn('⚠️ Excel row not found by pb_id, trying index 0 (just inserted)');
try {
await client.api(`${excelTablePath}/rows/itemAt(index=0)/range/cell(row=0,column=${linkColIndex})`)
.patch({
values: [[link || '']]
});
console.log('✓ Updated Excel Job_Folder_Link cell at index 0 for pb_id:', pbId);
return;
} catch (fallbackErr: any) {
console.error('⚠️ Fallback update at index 0 also failed:', fallbackErr.message);
return;
}
}
// Update only the Job_Folder_Link cell to preserve formulas in other columns (e.g., Active)
@@ -720,6 +809,25 @@ app.post('/api/submit', async (c) => {
await logError('PocketBase Final Update', pbFinalizeErr, { recordId: record.id, jobFolderLink });
}
// Create Planner task for the new job (assigned to aewing@cardoza.construction)
try {
const jobFullName = (record as any).Job_Full_Name || '';
await createPlannerTask({
jobNumber: newJobNumber,
jobFullName,
dueDate: data.Due_Date || undefined,
});
console.log('✓ Planner task created successfully');
} catch (plannerError: any) {
console.error('⚠️ Planner task creation failed, continuing:', plannerError.message);
await logError('Planner Task Creation', plannerError, {
jobNumber: newJobNumber,
recordId: record.id,
});
// Don't fail the response; Planner is optional
}
// Send Teams notification (fire and forget, don't block response)
notifyTeamsChannel(record).catch(err => {
console.error('⚠️ Teams notification error:', err.message);