// GraphQL queries for platform entities // Platform remaps SDK field names — all LINKS/PHONES fields need subfield selection. // // Each entity exports a query *builder* that accepts an optional `after` // cursor. The data-provider paginates until `hasNextPage=false` so the // dashboard KPIs reflect real totals instead of the first 100 rows. The // previous hardcoded `first: 100` caps caused supervisor KPI cards to // quietly plateau at 100 on busy tenants. // // `pageSize` is intentionally large (200) to keep round-trips low. The // platform Relay pagination accepts up to 1000 but 200 is a good balance // between latency per page and number of pages on active workspaces. const PAGE_SIZE = 200; const cursorArg = (after?: string): string => (after ? `, after: "${after}"` : ''); export const leadsQuery = (after?: string) => `{ leads(first: ${PAGE_SIZE}${cursorArg(after)}, orderBy: [{ createdAt: DescNullsLast }]) { edges { node { id name createdAt updatedAt contactName { firstName lastName } contactPhone { primaryPhoneNumber primaryPhoneCallingCode } contactEmail { primaryEmail } source status priority interestedService assignedAgent utmSource utmMedium utmCampaign utmContent utmTerm landingPage { primaryLinkUrl } referrerUrl leadScore spamScore isSpam isDuplicate firstContacted lastContacted contactAttempts convertedAt patientId campaignId aiSummary aiSuggestedAction } } pageInfo { hasNextPage endCursor } } }`; export const campaignsQuery = (after?: string) => `{ campaigns(first: ${PAGE_SIZE}${cursorArg(after)}, orderBy: [{ createdAt: DescNullsLast }]) { edges { node { id name createdAt updatedAt campaignName typeCustom status platform startDate endDate budget { amountMicros currencyCode } amountSpent { amountMicros currencyCode } impressions clicks targetCount contacted converted leadsGenerated externalCampaignId platformUrl { primaryLinkUrl } } } pageInfo { hasNextPage endCursor } } }`; export const adsQuery = (after?: string) => `{ ads(first: ${PAGE_SIZE}${cursorArg(after)}, orderBy: [{ createdAt: DescNullsLast }]) { edges { node { id name createdAt updatedAt adName externalAdId status format headline adDescription destinationUrl { primaryLinkUrl } previewUrl { primaryLinkUrl } impressions clicks conversions spend { amountMicros currencyCode } campaignId } } pageInfo { hasNextPage endCursor } } }`; export const followUpsQuery = (after?: string) => `{ followUps(first: ${PAGE_SIZE}${cursorArg(after)}, orderBy: [{ scheduledAt: DescNullsLast }]) { edges { node { id name createdAt typeCustom status scheduledAt completedAt priority assignedAgent patientId } } pageInfo { hasNextPage endCursor } } }`; export const leadActivitiesQuery = (after?: string) => `{ leadActivities(first: ${PAGE_SIZE}${cursorArg(after)}, orderBy: [{ occurredAt: DescNullsLast }]) { edges { node { id name createdAt activityType summary occurredAt performedBy previousValue newValue channel durationSec outcome leadId } } pageInfo { hasNextPage endCursor } } }`; export const callsQuery = (after?: string) => `{ calls(first: ${PAGE_SIZE}${cursorArg(after)}, orderBy: [{ startedAt: DescNullsLast }]) { edges { node { id name createdAt direction callStatus callerNumber { primaryPhoneNumber } agentName startedAt endedAt durationSec recording { primaryLinkUrl } disposition sla patientId appointmentId leadId agentId agent { id name ozonetelAgentId } transferredTo transferType } } pageInfo { hasNextPage endCursor } } }`; export const appointmentsQuery = (after?: string) => `{ appointments(first: ${PAGE_SIZE}${cursorArg(after)}, orderBy: [{ scheduledAt: DescNullsLast }]) { edges { node { id name createdAt scheduledAt durationMin appointmentType status doctorName department reasonForVisit patient { id fullName { firstName lastName } phones { primaryPhoneNumber } } doctor { id fullName { firstName lastName } } clinicId clinic { id clinicName } } } pageInfo { hasNextPage endCursor } } }`; export const patientsQuery = (after?: string) => `{ patients(first: ${PAGE_SIZE}${cursorArg(after)}) { edges { node { id name fullName { firstName lastName } phones { primaryPhoneNumber } emails { primaryEmail } dateOfBirth gender patientType } } pageInfo { hasNextPage endCursor } } }`; // Doctors are a small reference set (< 50 per workspace) — no pagination // needed. Left as a plain string for the single consumer that reads it. export const DOCTORS_QUERY = `{ doctors(first: 50) { edges { node { id name fullName { firstName lastName } department specialty qualifications yearsOfExperience visitingHours consultationFeeNew { amountMicros currencyCode } consultationFeeFollowUp { amountMicros currencyCode } active registrationNumber clinic { id name clinicName } } } } }`;