diff --git a/backend/dist/index.js b/backend/dist/index.js index a7ba9be..66f3ca5 100644 --- a/backend/dist/index.js +++ b/backend/dist/index.js @@ -34429,10 +34429,10 @@ async function syncProjectsToAirtable() { const table = base(config.airtableProjectsTableId); const existingRecords = new Map; const approvedRecords = new Set; - const pendingUpdateRecords = new Set; + const pendingUpdateRecords = new Map; const airtableHoursMap = new Map; const airtableRecordsToDelete = []; - const urlRecordCounts = new Map; + const pendingRecordIds = new Map; const recordsToAutoApprove = []; await new Promise((resolve, reject) => { table.select({ @@ -34452,7 +34452,7 @@ async function syncProjectsToAirtable() { airtableHoursMap.set(url, Number(hours)); } } else { - urlRecordCounts.set(url, (urlRecordCounts.get(url) || 0) + 1); + pendingRecordIds.set(url, record.id); if (yswsRecordId) { recordsToAutoApprove.push(record.id); } @@ -34467,9 +34467,9 @@ async function syncProjectsToAirtable() { resolve(); }); }); - for (const url of urlRecordCounts.keys()) { + for (const [url, recordId] of pendingRecordIds.entries()) { if (approvedRecords.has(url)) { - pendingUpdateRecords.add(url); + pendingUpdateRecords.set(url, recordId); } } for (let i = 0;i < recordsToAutoApprove.length; i += 10) { @@ -34519,7 +34519,8 @@ async function syncProjectsToAirtable() { const roundedAirtableHours = airtableHours !== undefined ? Math.round(airtableHours * 10) / 10 : undefined; const isUnpaidUpdate = isApproved && !project.scrapsPaidAt; const isHoursUpdate = isApproved && roundedAirtableHours !== undefined && currentEffectiveHours > roundedAirtableHours; - const isUpdate = isUnpaidUpdate || isHoursUpdate; + const hasUpdateDescription = isApproved && !!project.updateDescription; + const isUpdate = isUnpaidUpdate || isHoursUpdate || hasUpdateDescription; if (isApproved && !isUpdate) continue; const previousOwner = seenCodeUrls.get(project.githubUrl); @@ -34585,8 +34586,6 @@ AI was used in this project. ${project.aiDescription}`); if (activityTimeline) { descriptionParts.push(activityTimeline); } - if (isUpdate && isApproved && pendingUpdateRecords.has(project.githubUrl)) - continue; if (isUpdate && isApproved) { const previousHours = roundedAirtableHours ?? 0; const deltaHours = Math.max(0, effectiveHours - previousHours); @@ -34646,7 +34645,12 @@ AI was used in this project. ${project.aiDescription}`); if (userIdentity?.birthday) { updateFields["Birthday"] = userIdentity.birthday; } - updateCreates.push(updateFields); + const existingPendingId = pendingUpdateRecords.get(project.githubUrl); + if (existingPendingId) { + toUpdate.push({ id: existingPendingId, fields: updateFields }); + } else { + updateCreates.push(updateFields); + } continue; } const fields = { diff --git a/backend/src/lib/airtable-sync.ts b/backend/src/lib/airtable-sync.ts index b6aa816..74f5ecc 100644 --- a/backend/src/lib/airtable-sync.ts +++ b/backend/src/lib/airtable-sync.ts @@ -162,10 +162,10 @@ export async function syncProjectsToAirtable(): Promise { // Fetch existing records from Airtable to find which ones to update vs create const existingRecords: Map = new Map() // github_url -> airtable record id const approvedRecords: Set = new Set() // github_urls that are already approved in Airtable - const pendingUpdateRecords: Set = new Set() // github_urls that have a pending (non-approved) record alongside an approved one + const pendingUpdateRecords: Map = new Map() // github_url -> pending airtable record id (for URLs that also have an approved record) const airtableHoursMap: Map = new Map() // github_url -> hours from approved records const airtableRecordsToDelete: string[] = [] // airtable record ids to delete (for rejected projects) - const urlRecordCounts: Map = new Map() // track how many records exist per Code URL + const pendingRecordIds: Map = new Map() // github_url -> non-approved airtable record id const recordsToAutoApprove: string[] = [] // airtable record ids that have YSWS Record ID but aren't Approved yet await new Promise((resolve, reject) => { table.select({ @@ -186,8 +186,8 @@ export async function syncProjectsToAirtable(): Promise { airtableHoursMap.set(url, Number(hours)) } } else { - // Non-approved record — if there's also an approved one, this is a pending update - urlRecordCounts.set(url, (urlRecordCounts.get(url) || 0) + 1) + // Track non-approved record id for this URL + pendingRecordIds.set(url, record.id) // Auto-approve records that have a YSWS Record ID but aren't marked Approved if (yswsRecordId) { recordsToAutoApprove.push(record.id) @@ -203,10 +203,10 @@ export async function syncProjectsToAirtable(): Promise { } ) }) - // Mark URLs that have both an approved record and a non-approved record - for (const url of urlRecordCounts.keys()) { + // Mark URLs that have both an approved record and a non-approved (pending) record + for (const [url, recordId] of pendingRecordIds.entries()) { if (approvedRecords.has(url)) { - pendingUpdateRecords.add(url) + pendingUpdateRecords.set(url, recordId) } } @@ -290,7 +290,8 @@ export async function syncProjectsToAirtable(): Promise { // For approved projects, check if this is an update that needs a new row const isUnpaidUpdate = isApproved && !project.scrapsPaidAt const isHoursUpdate = isApproved && roundedAirtableHours !== undefined && currentEffectiveHours > roundedAirtableHours - const isUpdate = isUnpaidUpdate || isHoursUpdate + const hasUpdateDescription = isApproved && !!project.updateDescription + const isUpdate = isUnpaidUpdate || isHoursUpdate || hasUpdateDescription if (isApproved && !isUpdate) continue @@ -360,10 +361,7 @@ export async function syncProjectsToAirtable(): Promise { descriptionParts.push(activityTimeline) } - // For approved projects that have been updated, create a NEW row - // with only the delta hours and an update-specific description - // Skip if an update row already exists (pending record alongside the approved one) - if (isUpdate && isApproved && pendingUpdateRecords.has(project.githubUrl)) continue + // For approved projects that have been updated, create a NEW row or update existing pending row if (isUpdate && isApproved) { const previousHours = roundedAirtableHours ?? 0 const deltaHours = Math.max(0, effectiveHours - previousHours) @@ -421,7 +419,13 @@ export async function syncProjectsToAirtable(): Promise { updateFields['Birthday'] = userIdentity.birthday } - updateCreates.push(updateFields) + // If a pending update row already exists, update it; otherwise create a new one + const existingPendingId = pendingUpdateRecords.get(project.githubUrl) + if (existingPendingId) { + toUpdate.push({ id: existingPendingId, fields: updateFields }) + } else { + updateCreates.push(updateFields) + } continue }