333 lines
11 KiB
TypeScript
333 lines
11 KiB
TypeScript
export interface PageInfo {
|
|
pageSize: number;
|
|
totalRows: number;
|
|
isFirstPage: boolean;
|
|
isLastPage: boolean;
|
|
page: number;
|
|
}
|
|
|
|
export interface InterestsResponse {
|
|
list: Interest[];
|
|
PageInfo: PageInfo;
|
|
}
|
|
|
|
export enum Table {
|
|
Interest = "mbs9hjauug4eseo",
|
|
}
|
|
|
|
export const getNocoDbConfiguration = () => {
|
|
const config = useRuntimeConfig().nocodb;
|
|
console.log('[nocodb] Configuration URL:', config.url);
|
|
return config;
|
|
};
|
|
|
|
export const createTableUrl = (table: Table) => {
|
|
const url = `${getNocoDbConfiguration().url}/api/v2/tables/${table}/records`;
|
|
console.log('[nocodb] Table URL:', url);
|
|
return url;
|
|
};
|
|
|
|
export const getInterests = async () =>
|
|
$fetch<InterestsResponse>(createTableUrl(Table.Interest), {
|
|
headers: {
|
|
"xc-token": getNocoDbConfiguration().token,
|
|
},
|
|
params: {
|
|
limit: 1000,
|
|
},
|
|
});
|
|
|
|
export const getInterestById = async (id: string) =>
|
|
$fetch<Interest>(`${createTableUrl(Table.Interest)}/${id}`, {
|
|
headers: {
|
|
"xc-token": getNocoDbConfiguration().token,
|
|
},
|
|
});
|
|
|
|
export const updateInterest = async (id: string, data: Partial<Interest>, retryCount = 0): Promise<Interest> => {
|
|
console.log('[nocodb.updateInterest] Updating interest:', id, 'Retry:', retryCount);
|
|
console.log('[nocodb.updateInterest] Data fields:', Object.keys(data));
|
|
|
|
// First, try to verify the record exists
|
|
if (retryCount === 0) {
|
|
try {
|
|
console.log('[nocodb.updateInterest] Verifying record exists...');
|
|
const existingRecord = await getInterestById(id);
|
|
console.log('[nocodb.updateInterest] Record exists with ID:', existingRecord.Id);
|
|
} catch (verifyError: any) {
|
|
console.error('[nocodb.updateInterest] Failed to verify record:', verifyError);
|
|
if (verifyError.statusCode === 404 || verifyError.status === 404) {
|
|
console.error('[nocodb.updateInterest] Record verification failed - record not found');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create a clean data object that matches the InterestsRequest schema
|
|
// Remove any properties that are not in the schema or shouldn't be sent
|
|
const cleanData: Record<string, any> = {};
|
|
|
|
// Only include fields that are part of the InterestsRequest schema
|
|
// Removed webhook fields: "Request More Information", "Request More Info - To Sales", "EOI Send to Sales"
|
|
const allowedFields = [
|
|
"Full Name",
|
|
"Yacht Name",
|
|
"Length",
|
|
"Address",
|
|
"Email Address",
|
|
"Sales Process Level",
|
|
"Phone Number",
|
|
"Extra Comments",
|
|
"Berth Size Desired",
|
|
"LOI-NDA Document",
|
|
"Date Added",
|
|
"Width",
|
|
"Depth",
|
|
"Created At",
|
|
"Source",
|
|
"Contact Method Preferred",
|
|
"Request Form Sent",
|
|
"Berth Number",
|
|
"EOI Time Sent",
|
|
"Lead Category",
|
|
"Time LOI Sent",
|
|
"EOI Status",
|
|
"Berth Info Sent Status",
|
|
"Contract Sent Status",
|
|
"Deposit 10% Status",
|
|
"Contract Status",
|
|
// Add the EOI link fields
|
|
"EOI Client Link",
|
|
"EOI David Link",
|
|
"EOI Oscar Link",
|
|
"EOI Document",
|
|
// Add the new signature link fields
|
|
"Signature Link Client",
|
|
"Signature Link CC",
|
|
"Signature Link Developer"
|
|
];
|
|
|
|
// Filter the data to only include allowed fields
|
|
for (const field of allowedFields) {
|
|
if (field in data) {
|
|
const value = (data as any)[field];
|
|
|
|
// Skip webhook-type fields and other object fields that shouldn't be sent
|
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
console.log(`[nocodb.updateInterest] Skipping object field: ${field}`, value);
|
|
continue;
|
|
}
|
|
|
|
cleanData[field] = value;
|
|
}
|
|
}
|
|
|
|
console.log('[nocodb.updateInterest] Clean data fields:', Object.keys(cleanData));
|
|
|
|
// PATCH requires ID in the body (not in URL)
|
|
// Ensure ID is an integer
|
|
cleanData.Id = parseInt(id);
|
|
|
|
const url = createTableUrl(Table.Interest);
|
|
console.log('[nocodb.updateInterest] URL:', url);
|
|
|
|
try {
|
|
console.log('[nocodb.updateInterest] Sending PATCH request with headers:', {
|
|
"xc-token": getNocoDbConfiguration().token ? "***" + getNocoDbConfiguration().token.slice(-4) : "not set"
|
|
});
|
|
console.log('[nocodb.updateInterest] Request body:', JSON.stringify(cleanData, null, 2));
|
|
|
|
// Try sending as a single object first (as shown in the API docs)
|
|
const result = await $fetch<Interest>(url, {
|
|
method: "PATCH",
|
|
headers: {
|
|
"xc-token": getNocoDbConfiguration().token,
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: cleanData
|
|
});
|
|
console.log('[nocodb.updateInterest] Update successful for ID:', id);
|
|
return result;
|
|
} catch (error: any) {
|
|
console.error('[nocodb.updateInterest] Update failed:', error);
|
|
console.error('[nocodb.updateInterest] Error details:', error instanceof Error ? error.message : 'Unknown error');
|
|
|
|
// If it's a 404 error and we haven't retried too many times, wait and retry
|
|
if ((error.statusCode === 404 || error.status === 404) && retryCount < 3) {
|
|
console.error('[nocodb.updateInterest] 404 Error - Record not found. This might be a sync delay.');
|
|
console.error(`Retrying in ${(retryCount + 1) * 1000}ms... (Attempt ${retryCount + 1}/3)`);
|
|
|
|
// Wait with exponential backoff
|
|
await new Promise(resolve => setTimeout(resolve, (retryCount + 1) * 1000));
|
|
|
|
// Retry the update
|
|
return updateInterest(id, data, retryCount + 1);
|
|
}
|
|
|
|
// If it's still a 404 after retries, provide detailed error
|
|
if (error.statusCode === 404 || error.status === 404) {
|
|
console.error('[nocodb.updateInterest] 404 Error - Record not found after 3 retries. This might happen if:');
|
|
console.error('1. The record ID is incorrect');
|
|
console.error('2. The record was deleted');
|
|
console.error('3. There is a synchronization issue with the database');
|
|
console.error('Attempted URL:', url);
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const createInterest = async (data: Partial<Interest>) => {
|
|
console.log('[nocodb.createInterest] Creating interest with fields:', Object.keys(data));
|
|
|
|
// Create a clean data object that matches the InterestsRequest schema
|
|
const cleanData: Record<string, any> = {};
|
|
|
|
// Only include fields that are part of the InterestsRequest schema
|
|
const allowedFields = [
|
|
"Full Name",
|
|
"Yacht Name",
|
|
"Length",
|
|
"Address",
|
|
"Email Address",
|
|
"Sales Process Level",
|
|
"Phone Number",
|
|
"Extra Comments",
|
|
"Berth Size Desired",
|
|
"Date Added",
|
|
"Width",
|
|
"Depth",
|
|
"Source",
|
|
"Contact Method Preferred",
|
|
"Lead Category",
|
|
"EOI Status",
|
|
"Berth Info Sent Status",
|
|
"Contract Sent Status",
|
|
"Deposit 10% Status",
|
|
"Contract Status",
|
|
];
|
|
|
|
// Filter the data to only include allowed fields
|
|
for (const field of allowedFields) {
|
|
if (field in data) {
|
|
cleanData[field] = (data as any)[field];
|
|
}
|
|
}
|
|
|
|
// Remove any computed or relation fields that shouldn't be sent
|
|
delete cleanData.Id;
|
|
delete cleanData.Berths;
|
|
delete cleanData["Berth Recommendations"];
|
|
delete cleanData.Berth;
|
|
|
|
console.log('[nocodb.createInterest] Clean data fields:', Object.keys(cleanData));
|
|
const url = createTableUrl(Table.Interest);
|
|
console.log('[nocodb.createInterest] URL:', url);
|
|
|
|
try {
|
|
const result = await $fetch<Interest>(url, {
|
|
method: "POST",
|
|
headers: {
|
|
"xc-token": getNocoDbConfiguration().token,
|
|
},
|
|
body: cleanData,
|
|
});
|
|
console.log('[nocodb.createInterest] Created interest with ID:', result.Id);
|
|
return result;
|
|
} catch (error) {
|
|
console.error('[nocodb.createInterest] Create failed:', error);
|
|
console.error('[nocodb.createInterest] Error details:', error instanceof Error ? error.message : 'Unknown error');
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const deleteInterest = async (id: string) => {
|
|
const startTime = Date.now();
|
|
console.log('[nocodb.deleteInterest] =========================');
|
|
console.log('[nocodb.deleteInterest] DELETE operation started at:', new Date().toISOString());
|
|
console.log('[nocodb.deleteInterest] Target ID:', id);
|
|
|
|
const url = createTableUrl(Table.Interest);
|
|
console.log('[nocodb.deleteInterest] URL:', url);
|
|
|
|
const requestBody = {
|
|
"Id": parseInt(id)
|
|
};
|
|
|
|
console.log('[nocodb.deleteInterest] Request configuration:');
|
|
console.log(' Method: DELETE');
|
|
console.log(' URL:', url);
|
|
console.log(' Headers:', {
|
|
"xc-token": getNocoDbConfiguration().token ? "***" + getNocoDbConfiguration().token.slice(-4) : "not set",
|
|
"Content-Type": "application/json"
|
|
});
|
|
console.log(' Body:', JSON.stringify(requestBody, null, 2));
|
|
|
|
try {
|
|
// According to NocoDB API docs, DELETE requires ID in the body
|
|
const result = await $fetch(url, {
|
|
method: "DELETE",
|
|
headers: {
|
|
"xc-token": getNocoDbConfiguration().token,
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: requestBody
|
|
});
|
|
|
|
console.log('[nocodb.deleteInterest] DELETE successful');
|
|
console.log('[nocodb.deleteInterest] Response:', JSON.stringify(result, null, 2));
|
|
console.log('[nocodb.deleteInterest] Duration:', Date.now() - startTime, 'ms');
|
|
console.log('[nocodb.deleteInterest] =========================');
|
|
|
|
return result;
|
|
} catch (error: any) {
|
|
console.error('[nocodb.deleteInterest] =========================');
|
|
console.error('[nocodb.deleteInterest] DELETE FAILED');
|
|
console.error('[nocodb.deleteInterest] Error type:', error.constructor.name);
|
|
console.error('[nocodb.deleteInterest] Error message:', error.message);
|
|
console.error('[nocodb.deleteInterest] Error status:', error.statusCode || error.status || 'unknown');
|
|
console.error('[nocodb.deleteInterest] Error data:', error.data);
|
|
console.error('[nocodb.deleteInterest] Error stack:', error.stack || 'No stack trace');
|
|
console.error('[nocodb.deleteInterest] Full error:', JSON.stringify(error, null, 2));
|
|
console.error('[nocodb.deleteInterest] Duration:', Date.now() - startTime, 'ms');
|
|
console.error('[nocodb.deleteInterest] =========================');
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export const triggerWebhook = async (url: string, payload: any) =>
|
|
$fetch(url, {
|
|
method: "POST",
|
|
body: payload,
|
|
});
|
|
|
|
export const updateInterestEOIDocument = async (id: string, documentData: any) => {
|
|
console.log('[nocodb.updateInterestEOIDocument] Updating EOI document for interest:', id);
|
|
|
|
// Get existing EOI Document array or create new one
|
|
const interest = await getInterestById(id);
|
|
const existingDocuments = interest['EOI Document'] || [];
|
|
|
|
// Add the new document to the array
|
|
const updatedDocuments = [...existingDocuments, documentData];
|
|
|
|
// Update the interest with the new EOI Document array
|
|
return updateInterest(id, {
|
|
'EOI Document': updatedDocuments
|
|
});
|
|
};
|
|
|
|
export const getInterestByFieldAsync = async (fieldName: string, value: any): Promise<Interest | null> => {
|
|
try {
|
|
const response = await getInterests();
|
|
const interests = response.list || [];
|
|
|
|
// Find interest where the field matches the value
|
|
const interest = interests.find(i => (i as any)[fieldName] === value);
|
|
|
|
return interest || null;
|
|
} catch (error) {
|
|
console.error('Error fetching interest by field:', error);
|
|
return null;
|
|
}
|
|
};
|