From 843205a52973280e9e2cbfd50fc0252ffdaa1576 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 17 Jun 2025 17:04:45 +0200 Subject: [PATCH] FEAT: Enhance getBerths and test-specific-berth functionality to improve interested parties handling and debugging --- server/api/test-specific-berth.ts | 121 ++++++++++++++++++++++++++++++ server/utils/nocodb.ts | 65 ++++++++++++---- 2 files changed, 172 insertions(+), 14 deletions(-) create mode 100644 server/api/test-specific-berth.ts diff --git a/server/api/test-specific-berth.ts b/server/api/test-specific-berth.ts new file mode 100644 index 0000000..b35aabd --- /dev/null +++ b/server/api/test-specific-berth.ts @@ -0,0 +1,121 @@ +import { requireAuth } from '~/server/utils/auth'; + +export default defineEventHandler(async (event) => { + console.log('[test-specific-berth] Testing specific berth data...'); + + // Check authentication + await requireAuth(event); + + try { + const query = getQuery(event); + const berthId = query.id as string; + + if (!berthId) { + // Get first few berths to find one to test + const config = useRuntimeConfig().nocodb; + const berthsResponse = await $fetch(`${config.url}/api/v2/tables/mczgos9hr3oa9qc/records`, { + headers: { + "xc-token": config.token, + }, + params: { + limit: 10, + fields: '*' + } + }); + + const berths = (berthsResponse as any).list || []; + return { + message: "No berth ID provided. Here are the first 10 berths:", + berths: berths.map((b: any) => ({ + id: b.Id, + mooringNumber: b['Mooring Number'], + hasInterestedParties: !!b['Interested Parties'], + interestedPartiesCount: Array.isArray(b['Interested Parties']) ? b['Interested Parties'].length : 0, + interestedPartiesRaw: b['Interested Parties'] + })) + }; + } + + const config = useRuntimeConfig().nocodb; + + // Fetch the specific berth + const berth = await $fetch(`${config.url}/api/v2/tables/mczgos9hr3oa9qc/records/${berthId}`, { + headers: { + "xc-token": config.token, + }, + params: { + fields: '*' + } + }); + + console.log('[test-specific-berth] Raw berth data:', JSON.stringify(berth, null, 2)); + + // Check all fields to find potential interested parties + const allFields = Object.keys(berth as any); + const potentialLinkFields = allFields.filter(field => { + const value = (berth as any)[field]; + return value && (Array.isArray(value) || (typeof value === 'object' && !field.includes('Created') && !field.includes('Updated'))); + }); + + // Try to fetch linked records using the links API + let linkedRecordsAttempts = []; + + // Try different possible field IDs for interested parties + const possibleFieldIds = [ + 'cj7v7bb9pa5eyo3', // This is used for "Berths" in interest table + // Add other possible field IDs here if we discover them + ]; + + for (const fieldId of possibleFieldIds) { + try { + const linkUrl = `${config.url}/api/v2/tables/mczgos9hr3oa9qc/links/${fieldId}/records/${berthId}`; + console.log('[test-specific-berth] Trying link URL:', linkUrl); + + const linkedRecords = await $fetch(linkUrl, { + headers: { + "xc-token": config.token, + } + }).catch(err => ({ + error: err.message || 'Failed', + fieldId: fieldId, + status: err.statusCode || err.status + })); + + linkedRecordsAttempts.push({ + fieldId, + result: linkedRecords + }); + } catch (error) { + console.error('[test-specific-berth] Link fetch error:', error); + } + } + + return { + berthId: (berth as any).Id, + mooringNumber: (berth as any)['Mooring Number'], + allFields: allFields, + potentialLinkFields: potentialLinkFields.map(field => ({ + fieldName: field, + value: (berth as any)[field], + type: typeof (berth as any)[field], + isArray: Array.isArray((berth as any)[field]), + length: Array.isArray((berth as any)[field]) ? (berth as any)[field].length : undefined + })), + interestedPartiesField: { + exists: 'Interested Parties' in (berth as any), + value: (berth as any)['Interested Parties'], + type: typeof (berth as any)['Interested Parties'], + isArray: Array.isArray((berth as any)['Interested Parties']), + length: Array.isArray((berth as any)['Interested Parties']) ? (berth as any)['Interested Parties'].length : undefined + }, + linkedRecordsAttempts: linkedRecordsAttempts, + rawBerth: berth + }; + } catch (error: any) { + console.error('[test-specific-berth] Error:', error); + return { + error: error.message || 'Unknown error', + details: error + }; + } +}); diff --git a/server/utils/nocodb.ts b/server/utils/nocodb.ts index ad3dc1b..006405d 100644 --- a/server/utils/nocodb.ts +++ b/server/utils/nocodb.ts @@ -446,26 +446,63 @@ export const getBerths = async () => { if (result.list && Array.isArray(result.list)) { console.log('[nocodb.getBerths] Processing berths to populate interested parties...'); + // Count berths with interested parties + const berthsWithParties = result.list.filter(b => + b['Interested Parties'] && Array.isArray(b['Interested Parties']) && b['Interested Parties'].length > 0 + ); + console.log('[nocodb.getBerths] Berths with interested parties:', berthsWithParties.length); + + // Log first berth with interested parties for debugging + if (berthsWithParties.length > 0) { + const firstBerth = berthsWithParties[0]; + if (firstBerth && firstBerth['Interested Parties']) { + console.log('[nocodb.getBerths] First berth with parties:', { + id: firstBerth.Id, + mooringNumber: firstBerth['Mooring Number'], + partiesCount: firstBerth['Interested Parties'].length, + firstParty: firstBerth['Interested Parties'][0] + }); + } + } + await Promise.all( result.list.map(async (berth) => { - if (berth['Interested Parties'] && Array.isArray(berth['Interested Parties'])) { - const interestedPartiesDetails = await Promise.all( - berth['Interested Parties'].map(async (party: any) => { - if (party && (party.Id || party.id)) { - const interestId = party.Id || party.id; + if (berth['Interested Parties'] && Array.isArray(berth['Interested Parties']) && berth['Interested Parties'].length > 0) { + console.log(`[nocodb.getBerths] Processing ${berth['Interested Parties'].length} parties for berth ${berth['Mooring Number']}`); + + // Extract IDs from various possible formats + const partyIds = berth['Interested Parties'].map((party: any) => { + // Handle different possible formats from NocoDB + if (typeof party === 'number') return party; + if (typeof party === 'string') return parseInt(party); + if (party && typeof party === 'object') { + // Check various possible ID field names + return party.Id || party.id || party.ID || party._id || null; + } + return null; + }).filter((id: any) => id !== null && !isNaN(id)); + + console.log(`[nocodb.getBerths] Extracted ${partyIds.length} valid IDs for berth ${berth['Mooring Number']}:`, partyIds); + + if (partyIds.length > 0) { + const interestedPartiesDetails = await Promise.all( + partyIds.map(async (partyId: number) => { try { - const interestDetails = await getInterestById(interestId.toString()); + console.log(`[nocodb.getBerths] Fetching interest ${partyId} for berth ${berth['Mooring Number']}`); + const interestDetails = await getInterestById(partyId.toString()); return interestDetails; } catch (error) { - console.error('[nocodb.getBerths] Failed to fetch interest details for ID:', interestId, error); - return party; // Return original party if fetch fails + console.error(`[nocodb.getBerths] Failed to fetch interest ${partyId}:`, error); + return { Id: partyId, 'Full Name': `Interest #${partyId}` } as any; } - } - return party; - }) - ); - - berth['Interested Parties'] = interestedPartiesDetails; + }) + ); + + berth['Interested Parties'] = interestedPartiesDetails; + console.log(`[nocodb.getBerths] Populated ${interestedPartiesDetails.length} parties for berth ${berth['Mooring Number']}`); + } else { + console.log(`[nocodb.getBerths] No valid party IDs found for berth ${berth['Mooring Number']}`); + } } }) );