feat: wire frontend to platform data, migrate to Jotai + Vercel AI SDK

- Replace mock DataProvider with real GraphQL queries through sidecar
- Add queries.ts and transforms.ts for platform field name mapping
- Migrate SIP state from React Context to Jotai atoms (React 19 compat)
- Add singleton SIP manager to survive StrictMode remounts
- Remove hardcoded Olivia/Sienna accounts from nav menu
- Add password eye toggle, remember me checkbox, forgot password link
- Fix worklist hook to transform platform field names
- Add seed scripts for clinics, health packages, lab tests
- Update test harness for new doctor→clinic relation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-18 16:44:45 +05:30
parent e01a4d7747
commit 61901eb8fb
14 changed files with 1208 additions and 108 deletions

View File

@@ -0,0 +1,348 @@
/**
* Seed clinics, health packages, insurance partners, and link doctors to clinics.
* Run: cd helix-engage && npx tsx scripts/seed-new-entities.ts
*
* Prerequisites: doctors already seeded via seed-data.ts
*
* Platform field mapping (SDK name → platform name):
* Clinic: address→addressCustom, operatingHoursWeekday→weekdayHours,
* operatingHoursSaturday→saturdayHours, operatingHoursSunday→sundayHours,
* clinicStatus→status, onlineBookingEnabled→onlineBooking,
* arriveEarlyMinutes→arriveEarlyMin, paymentCash→acceptsCash,
* paymentCard→acceptsCard, paymentUpi→acceptsUpi
* HealthPackage: packageDepartment→department, durationMinutes→durationMin, isActive→active
* InsurancePartner: planTypes→planTypesAccepted
*/
const GQL = 'http://localhost:4000/graphql';
const SUB = 'fortytwo-dev';
const ORIGIN = 'http://fortytwo-dev.localhost:4010';
let token = '';
async function gql(query: string, variables?: any) {
const h: Record<string, string> = { 'Content-Type': 'application/json', 'X-Workspace-Subdomain': SUB };
if (token) h['Authorization'] = `Bearer ${token}`;
const r = await fetch(GQL, { method: 'POST', headers: h, body: JSON.stringify({ query, variables }) });
const d: any = await r.json();
if (d.errors) { console.error('❌', d.errors[0].message); throw new Error(d.errors[0].message); }
return d.data;
}
async function auth() {
const d1 = await gql(`mutation { getLoginTokenFromCredentials(email: "dev@fortytwo.dev", password: "tim@apple.dev", origin: "${ORIGIN}") { loginToken { token } } }`);
const lt = d1.getLoginTokenFromCredentials.loginToken.token;
const d2 = await gql(`mutation { getAuthTokensFromLoginToken(loginToken: "${lt}", origin: "${ORIGIN}") { tokens { accessOrWorkspaceAgnosticToken { token } } } }`);
token = d2.getAuthTokensFromLoginToken.tokens.accessOrWorkspaceAgnosticToken.token;
}
async function mk(entity: string, data: any): Promise<string> {
const cap = entity[0].toUpperCase() + entity.slice(1);
const d = await gql(`mutation($data: ${cap}CreateInput!) { create${cap}(data: $data) { id } }`, { data });
return d[`create${cap}`].id;
}
async function update(entity: string, id: string, data: any): Promise<void> {
const cap = entity[0].toUpperCase() + entity.slice(1);
await gql(`mutation($id: UUID!, $data: ${cap}UpdateInput!) { update${cap}(id: $id, data: $data) { id } }`, { id, data });
}
async function main() {
console.log('🌱 Seeding clinics, health packages, insurance partners...\n');
await auth();
console.log('✅ Auth OK\n');
// ═══════════════════════════════════════════
// CLINICS
// ═══════════════════════════════════════════
console.log('🏥 Clinics');
const koramangala = await mk('clinic', {
name: 'Global Hospital — Koramangala',
clinicName: 'Koramangala',
addressCustom: {
addressStreet1: '#45, 80 Feet Road',
addressCity: 'Bengaluru',
addressState: 'Karnataka',
addressPostcode: '560034',
addressCountry: 'India',
},
phone: { primaryPhoneNumber: '08041234567', primaryPhoneCallingCode: '+91', primaryPhoneCountryCode: 'IN' },
email: { primaryEmail: 'koramangala@globalhospital.com' },
weekdayHours: '8:00 AM 8:00 PM',
saturdayHours: '8:00 AM 8:00 PM',
sundayHours: '9:00 AM 2:00 PM',
status: 'ACTIVE',
walkInAllowed: true,
onlineBooking: true,
cancellationWindowHours: 4,
arriveEarlyMin: 15,
requiredDocuments: 'ID proof + medical records',
acceptsCash: 'YES',
acceptsCard: 'YES',
acceptsUpi: 'YES',
});
console.log(` Koramangala: ${koramangala}`);
const whitefield = await mk('clinic', {
name: 'Global Hospital — Whitefield',
clinicName: 'Whitefield',
addressCustom: {
addressStreet1: 'Prestige Shantiniketan',
addressCity: 'Bengaluru',
addressState: 'Karnataka',
addressPostcode: '560048',
addressCountry: 'India',
},
phone: { primaryPhoneNumber: '08041234568', primaryPhoneCallingCode: '+91', primaryPhoneCountryCode: 'IN' },
email: { primaryEmail: 'whitefield@globalhospital.com' },
weekdayHours: '8:00 AM 8:00 PM',
saturdayHours: '8:00 AM 8:00 PM',
sundayHours: 'Closed',
status: 'ACTIVE',
walkInAllowed: true,
onlineBooking: true,
cancellationWindowHours: 4,
arriveEarlyMin: 15,
requiredDocuments: 'ID proof + medical records',
acceptsCash: 'YES',
acceptsCard: 'YES',
acceptsUpi: 'YES',
});
console.log(` Whitefield: ${whitefield}`);
const indiranagar = await mk('clinic', {
name: 'Global Hospital — Indiranagar',
clinicName: 'Indiranagar',
addressCustom: {
addressStreet1: '#12, 100 Feet Road',
addressCity: 'Bengaluru',
addressState: 'Karnataka',
addressPostcode: '560038',
addressCountry: 'India',
},
phone: { primaryPhoneNumber: '08041234569', primaryPhoneCallingCode: '+91', primaryPhoneCountryCode: 'IN' },
email: { primaryEmail: 'indiranagar@globalhospital.com' },
weekdayHours: '9:00 AM 7:00 PM',
saturdayHours: '9:00 AM 7:00 PM',
sundayHours: '10:00 AM 1:00 PM',
status: 'ACTIVE',
walkInAllowed: true,
onlineBooking: true,
cancellationWindowHours: 4,
arriveEarlyMin: 15,
requiredDocuments: 'ID proof + medical records',
acceptsCash: 'YES',
acceptsCard: 'YES',
acceptsUpi: 'YES',
});
console.log(` Indiranagar: ${indiranagar}\n`);
await auth();
// ═══════════════════════════════════════════
// LINK DOCTORS TO CLINICS
// ═══════════════════════════════════════════
console.log('🔗 Linking doctors to clinics');
const doctors: Record<string, string> = {
'da5678f3-6b52-492e-87d3-c4707d105938': 'Dr. Sharma', // Koramangala
'b080cdf0-4527-46c7-b723-47f2eee623e4': 'Dr. Patel', // Indiranagar
'd780976a-7ddb-4a00-9a56-e7e3a77fa416': 'Dr. Kumar', // Whitefield
'bf77c148-438f-4b6f-9e5d-b1c1ff2e10f8': 'Dr. Reddy', // Koramangala
'e71c2c59-574f-4e81-b8cd-2d7b4b5da8e5': 'Dr. Singh', // Indiranagar
};
const doctorClinicMap: Record<string, string> = {
'da5678f3-6b52-492e-87d3-c4707d105938': koramangala,
'b080cdf0-4527-46c7-b723-47f2eee623e4': indiranagar,
'd780976a-7ddb-4a00-9a56-e7e3a77fa416': whitefield,
'bf77c148-438f-4b6f-9e5d-b1c1ff2e10f8': koramangala,
'e71c2c59-574f-4e81-b8cd-2d7b4b5da8e5': indiranagar,
};
for (const [docId, clinicId] of Object.entries(doctorClinicMap)) {
await update('doctor', docId, { clinicId });
console.log(` ${doctors[docId]}${clinicId === koramangala ? 'Koramangala' : clinicId === whitefield ? 'Whitefield' : 'Indiranagar'}`);
}
console.log('');
await auth();
// ═══════════════════════════════════════════
// HEALTH PACKAGES
// ═══════════════════════════════════════════
console.log('📦 Health Packages');
await mk('healthPackage', {
name: 'Master Health Checkup',
packageName: 'Master Health Checkup',
description: 'Comprehensive annual health screening — blood work, ECG, chest X-ray, BMI, vision, dental',
price: { amountMicros: 2_999_000_000, currencyCode: 'INR' },
department: 'PREVENTIVE',
inclusions: 'CBC, Lipid Profile, Liver Function, Kidney Function, Thyroid, Blood Sugar, ECG, Chest X-ray, BMI, Vision Test, Dental Check',
durationMin: 180,
eligibility: 'All adults above 18 years',
active: true,
clinicId: koramangala,
});
console.log(' Master Health Checkup — ₹2,999');
await mk('healthPackage', {
name: 'Cardiac Screening',
packageName: 'Cardiac Screening',
description: 'Heart health assessment — ECG, Echo, TMT, lipid panel, cardiac risk markers',
price: { amountMicros: 4_999_000_000, currencyCode: 'INR' },
department: 'CARDIOLOGY',
inclusions: 'ECG, 2D Echocardiogram, TMT (Treadmill Test), Lipid Profile, hs-CRP, Homocysteine, HbA1c, Cardiologist Consultation',
durationMin: 240,
eligibility: 'Adults above 30 or family history of heart disease',
active: true,
clinicId: koramangala,
});
console.log(' Cardiac Screening — ₹4,999');
await mk('healthPackage', {
name: "Women's Wellness Package",
packageName: "Women's Wellness Package",
description: 'Complete women\'s health screening — PAP smear, mammogram, hormone panel, bone density',
price: { amountMicros: 3_499_000_000, currencyCode: 'INR' },
department: 'GYNECOLOGY',
inclusions: 'PAP Smear, Mammogram, Pelvic Ultrasound, Thyroid Panel, Vitamin D, Calcium, CBC, Hormone Panel (FSH/LH/Estradiol), Bone Density Scan, Gynecologist Consultation',
durationMin: 210,
eligibility: 'Women above 25 years',
active: true,
clinicId: indiranagar,
});
console.log(" Women's Wellness — ₹3,499");
await mk('healthPackage', {
name: 'Orthopedic Assessment',
packageName: 'Orthopedic Assessment',
description: 'Joint and bone health evaluation — X-rays, bone density, vitamin levels, specialist consult',
price: { amountMicros: 1_999_000_000, currencyCode: 'INR' },
department: 'ORTHOPEDICS',
inclusions: 'Joint X-rays (knee/hip/spine), Bone Density Scan, Vitamin D, Calcium, Uric Acid, RA Factor, Orthopedic Consultation',
durationMin: 120,
eligibility: 'Adults with joint pain or age above 40',
active: true,
clinicId: whitefield,
});
console.log(' Orthopedic Assessment — ₹1,999');
await mk('healthPackage', {
name: 'IVF Consultation Package',
packageName: 'IVF Consultation Package',
description: 'Initial fertility assessment — hormone panel, ultrasound, semen analysis, specialist consult',
price: { amountMicros: 5_999_000_000, currencyCode: 'INR' },
discountedPrice: { amountMicros: 0, currencyCode: 'INR' },
department: 'GYNECOLOGY',
inclusions: 'AMH, FSH, LH, Estradiol, Prolactin, Thyroid Panel, Transvaginal Ultrasound, Semen Analysis, Fertility Specialist Consultation',
durationMin: 150,
eligibility: 'Couples planning IVF or facing infertility (1+ year)',
active: true,
clinicId: indiranagar,
});
console.log(' IVF Consultation — ₹5,999 (free first visit via campaign)\n');
await auth();
// ═══════════════════════════════════════════
// INSURANCE PARTNERS
// ═══════════════════════════════════════════
console.log('🛡️ Insurance Partners');
await mk('insurancePartner', {
name: 'Star Health Insurance',
insurerName: 'Star Health',
tpaName: 'Star Health & Allied Insurance',
planTypesAccepted: 'Family Health Optima, Comprehensive, Senior Citizen Red Carpet',
settlementType: 'BOTH',
empanelmentStatus: 'ACTIVE',
validUntil: '2027-03-31',
contactPerson: 'Rajesh M',
contactPhone: { primaryPhoneNumber: '9900200001', primaryPhoneCallingCode: '+91', primaryPhoneCountryCode: 'IN' },
clinicId: koramangala,
});
console.log(' Star Health — cashless + reimbursement');
await mk('insurancePartner', {
name: 'ICICI Lombard',
insurerName: 'ICICI Lombard',
tpaName: 'Medi Assist',
planTypesAccepted: 'iHealth, Complete Health Insurance, Group Mediclaim',
settlementType: 'CASHLESS',
empanelmentStatus: 'ACTIVE',
validUntil: '2027-06-30',
contactPerson: 'Priya K',
contactPhone: { primaryPhoneNumber: '9900200002', primaryPhoneCallingCode: '+91', primaryPhoneCountryCode: 'IN' },
clinicId: koramangala,
});
console.log(' ICICI Lombard — cashless');
await mk('insurancePartner', {
name: 'Bajaj Allianz',
insurerName: 'Bajaj Allianz',
tpaName: 'Health India TPA',
planTypesAccepted: 'Health Guard, Silver Health, Group Health',
settlementType: 'BOTH',
empanelmentStatus: 'ACTIVE',
validUntil: '2027-01-31',
contactPerson: 'Suresh V',
contactPhone: { primaryPhoneNumber: '9900200003', primaryPhoneCallingCode: '+91', primaryPhoneCountryCode: 'IN' },
clinicId: whitefield,
});
console.log(' Bajaj Allianz — cashless + reimbursement');
await mk('insurancePartner', {
name: 'HDFC Ergo',
insurerName: 'HDFC Ergo',
tpaName: 'Paramount Health Services',
planTypesAccepted: 'Optima Secure, Optima Restore, My Health Suraksha',
settlementType: 'CASHLESS',
empanelmentStatus: 'ACTIVE',
validUntil: '2027-09-30',
contactPerson: 'Anita S',
contactPhone: { primaryPhoneNumber: '9900200004', primaryPhoneCallingCode: '+91', primaryPhoneCountryCode: 'IN' },
clinicId: indiranagar,
});
console.log(' HDFC Ergo — cashless');
await mk('insurancePartner', {
name: 'Niva Bupa (Max Bupa)',
insurerName: 'Niva Bupa',
tpaName: 'Niva Bupa Health Insurance',
planTypesAccepted: 'Health Recharge, Health Premia, ReAssure 2.0',
settlementType: 'BOTH',
empanelmentStatus: 'ACTIVE',
validUntil: '2027-04-30',
contactPerson: 'Deepak R',
contactPhone: { primaryPhoneNumber: '9900200005', primaryPhoneCallingCode: '+91', primaryPhoneCountryCode: 'IN' },
clinicId: koramangala,
});
console.log(' Niva Bupa — cashless + reimbursement');
await mk('insurancePartner', {
name: 'New India Assurance',
insurerName: 'New India Assurance',
tpaName: 'Raksha TPA',
planTypesAccepted: 'Mediclaim Policy, Jan Arogya Bima, Group Mediclaim',
settlementType: 'REIMBURSEMENT',
empanelmentStatus: 'ACTIVE',
validUntil: '2027-12-31',
contactPerson: 'Mohan T',
contactPhone: { primaryPhoneNumber: '9900200006', primaryPhoneCallingCode: '+91', primaryPhoneCountryCode: 'IN' },
clinicId: whitefield,
});
console.log(' New India Assurance — reimbursement\n');
// ═══════════════════════════════════════════
// CLEANUP: Delete the empty doctor record
// ═══════════════════════════════════════════
try {
await gql(`mutation { deleteDoctor(id: "967e1959-9b85-497e-9f8e-cad65120c5de") { id } }`);
console.log('🧹 Cleaned up empty doctor record\n');
} catch {
// Ignore if already deleted
}
console.log('🎉 Done!');
console.log(' 3 clinics · 5 health packages · 6 insurance partners');
console.log(' 5 doctors linked to clinics');
}
main().catch(e => { console.error('💥', e.message); process.exit(1); });