Files
helix-engage/src/lib/transforms.ts
saridsa2 72cb192447
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fix(appointments): preload clinic + keep saved time on edit
The appointment-edit form opened with clinic/time blank even for
well-formed appointments because the pipeline never carried clinicId
end-to-end. Four-layer audit + fix:

1. APPOINTMENTS_QUERY now fetches clinicId + clinic { id clinicName }
   + doctor.fullName (was only doctor.id).
2. transformAppointments populates real clinicId + clinicName from the
   relation instead of faking clinicName=department.
3. Appointment type gets clinicId: string | null.
4. context-panel passes clinicId through to AppointmentForm's
   existingAppointment prop; form initial-states clinic from it.

Also on edit: if the saved timeSlot isn't in the fresh slot list
(past-slot filter, schedule change, clinic mismatch) we inject it as
"HH:MM (current)" so the dropdown displays the existing value instead
of looking cleared.

Historical appointments with clinicId=null on the platform still fall
through to the auto-select-from-slot logic; a maint backfill for those
is a separate task.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:56:51 +05:30

200 lines
7.4 KiB
TypeScript

// Transform platform GraphQL responses → frontend entity types
// Platform remaps field names during sync — this layer normalizes them
import type { Lead, Campaign, Ad, FollowUp, LeadActivity, Call, Patient, Appointment } from '@/types/entities';
type PlatformNode = Record<string, any>;
function extractEdges(data: any, entityName: string): PlatformNode[] {
return data?.[entityName]?.edges?.map((e: any) => e.node) ?? [];
}
export function transformLeads(data: any): Lead[] {
return extractEdges(data, 'leads').map((n) => ({
id: n.id,
createdAt: n.createdAt,
updatedAt: n.updatedAt,
contactName: n.contactName ?? { firstName: '', lastName: '' },
contactPhone: n.contactPhone?.primaryPhoneNumber
? [{ number: n.contactPhone.primaryPhoneNumber, callingCode: n.contactPhone.primaryPhoneCallingCode ?? '+91' }]
: [],
contactEmail: n.contactEmail?.primaryEmail
? [{ address: n.contactEmail.primaryEmail }]
: [],
leadSource: n.source,
leadStatus: n.status,
priority: n.priority ?? 'NORMAL',
interestedService: n.interestedService,
assignedAgent: n.assignedAgent,
utmSource: n.utmSource,
utmMedium: n.utmMedium,
utmCampaign: n.utmCampaign,
utmContent: n.utmContent,
utmTerm: n.utmTerm,
landingPageUrl: n.landingPage?.primaryLinkUrl ?? null,
referrerUrl: n.referrerUrl,
leadScore: n.leadScore,
spamScore: n.spamScore ?? 0,
isSpam: n.isSpam ?? false,
isDuplicate: n.isDuplicate ?? false,
duplicateOfLeadId: null,
firstContactedAt: n.firstContacted,
lastContactedAt: n.lastContacted,
contactAttempts: n.contactAttempts ?? 0,
convertedAt: n.convertedAt,
patientId: n.patientId,
campaignId: n.campaignId,
adId: null,
aiSummary: n.aiSummary,
aiSuggestedAction: n.aiSuggestedAction,
}));
}
export function transformCampaigns(data: any): Campaign[] {
return extractEdges(data, 'campaigns').map((n) => ({
id: n.id,
createdAt: n.createdAt,
updatedAt: n.updatedAt,
campaignName: n.campaignName ?? n.name,
campaignType: n.typeCustom,
campaignStatus: n.status,
platform: n.platform,
startDate: n.startDate,
endDate: n.endDate,
budget: n.budget ? { amountMicros: n.budget.amountMicros, currencyCode: n.budget.currencyCode } : null,
amountSpent: n.amountSpent ? { amountMicros: n.amountSpent.amountMicros, currencyCode: n.amountSpent.currencyCode } : null,
impressionCount: n.impressions ?? 0,
clickCount: n.clicks ?? 0,
targetCount: n.targetCount ?? 0,
contactedCount: n.contacted ?? 0,
convertedCount: n.converted ?? 0,
leadCount: n.leadsGenerated ?? 0,
externalCampaignId: n.externalCampaignId,
platformUrl: n.platformUrl?.primaryLinkUrl ?? null,
}));
}
export function transformAds(data: any): Ad[] {
return extractEdges(data, 'ads').map((n) => ({
id: n.id,
createdAt: n.createdAt,
updatedAt: n.updatedAt,
adName: n.adName ?? n.name,
externalAdId: n.externalAdId,
adStatus: n.status,
adFormat: n.format,
headline: n.headline,
adDescription: n.adDescription,
destinationUrl: n.destinationUrl?.primaryLinkUrl ?? null,
previewUrl: n.previewUrl?.primaryLinkUrl ?? null,
impressions: n.impressions ?? 0,
clicks: n.clicks ?? 0,
conversions: n.conversions ?? 0,
spend: n.spend ? { amountMicros: n.spend.amountMicros, currencyCode: n.spend.currencyCode } : null,
campaignId: n.campaignId,
}));
}
export function transformFollowUps(data: any): FollowUp[] {
return extractEdges(data, 'followUps').map((n) => ({
id: n.id,
createdAt: n.createdAt,
followUpType: n.typeCustom,
followUpStatus: n.status,
scheduledAt: n.scheduledAt,
completedAt: n.completedAt,
priority: n.priority ?? 'NORMAL',
assignedAgent: n.assignedAgent,
patientId: n.patientId,
callId: null,
patientName: undefined,
patientPhone: undefined,
description: n.name,
}));
}
export function transformLeadActivities(data: any): LeadActivity[] {
return extractEdges(data, 'leadActivities').map((n) => ({
id: n.id,
createdAt: n.createdAt,
activityType: n.activityType,
summary: n.summary,
occurredAt: n.occurredAt,
performedBy: n.performedBy,
previousValue: n.previousValue,
newValue: n.newValue,
channel: n.channel,
durationSeconds: n.durationSec ?? 0,
outcome: n.outcome,
activityNotes: null,
leadId: n.leadId,
}));
}
export function transformCalls(data: any): Call[] {
return extractEdges(data, 'calls').map((n) => ({
id: n.id,
createdAt: n.createdAt,
callDirection: n.direction,
callStatus: n.callStatus,
callerNumber: n.callerNumber?.primaryPhoneNumber
? [{ number: n.callerNumber.primaryPhoneNumber, callingCode: '+91' }]
: [],
agentName: n.agentName,
startedAt: n.startedAt,
endedAt: n.endedAt,
durationSeconds: n.durationSec ?? 0,
recordingUrl: n.recording?.primaryLinkUrl || null,
disposition: n.disposition,
callNotes: null,
patientId: n.patientId,
appointmentId: n.appointmentId,
leadId: n.leadId,
agentId: n.agentId ?? null,
agent: n.agent ?? null,
transferredTo: n.transferredTo ?? null,
transferType: n.transferType ?? null,
}));
}
export function transformAppointments(data: any): Appointment[] {
return extractEdges(data, 'appointments').map((n) => {
// Doctor name: prefer the relation's fullName (authoritative — pulled
// from the Doctor entity). Fall back to the denormalized doctorName
// field for legacy rows that predate the doctor relation being fetched.
const doctorFullName = n.doctor?.fullName
? `${n.doctor.fullName.firstName ?? ''} ${n.doctor.fullName.lastName ?? ''}`.trim()
: '';
return {
id: n.id,
createdAt: n.createdAt,
scheduledAt: n.scheduledAt,
durationMinutes: n.durationMin ?? 30,
appointmentType: n.appointmentType,
appointmentStatus: n.status,
doctorName: doctorFullName || n.doctorName || null,
doctorId: n.doctor?.id ?? null,
department: n.department,
reasonForVisit: n.reasonForVisit,
patientId: n.patient?.id ?? null,
patientName: n.patient?.fullName ? `${n.patient.fullName.firstName} ${n.patient.fullName.lastName}`.trim() : null,
patientPhone: n.patient?.phones?.primaryPhoneNumber ?? null,
clinicId: n.clinicId ?? n.clinic?.id ?? null,
clinicName: n.clinic?.clinicName ?? null,
};
});
}
export function transformPatients(data: any): Patient[] {
return extractEdges(data, 'patients').map((n) => ({
id: n.id,
createdAt: n.createdAt,
fullName: n.fullName ?? null,
phones: n.phones ?? null,
emails: n.emails ?? null,
dateOfBirth: n.dateOfBirth ?? null,
gender: n.gender ?? null,
patientType: n.patientType ?? null,
}));
}