import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); export const bulkImportExcelData = async (req, res) => { try { const { departments, doctors, timings, careers, inquiries, academics, appointments, candidates, news, } = req.body; console.log("🚀 Starting Robust Data Import..."); // 1. DEPARTMENTS if (departments) { for (const row of departments) { if (!row.SL_NO) continue; await prisma.department.upsert({ where: { departmentId: row.SL_NO.toString() }, update: { name: row.Department?.toString(), para1: row.para1?.toString() || null, para2: row.para2?.toString() || null, para3: row.para3?.toString() || null, facilities: row.facilities?.toString() || null, services: row.services?.toString() || null, }, create: { departmentId: row.SL_NO.toString(), name: row.Department?.toString(), para1: row.para1?.toString() || null, para2: row.para2?.toString() || null, para3: row.para3?.toString() || null, facilities: row.facilities?.toString() || null, services: row.services?.toString() || null, }, }); } } // 2. DOCTORS if (doctors) { for (const row of doctors) { if (!row.GG_ID) continue; const doctor = await prisma.doctor.upsert({ where: { doctorId: row.GG_ID.toString() }, update: { name: row.Name?.toString(), designation: row.Designation?.toString() || null, workingStatus: row["Working Status"]?.toString() || null, qualification: row.Qualification?.toString() || null, }, create: { doctorId: row.GG_ID.toString(), name: row.Name?.toString(), designation: row.Designation?.toString() || null, workingStatus: row["Working Status"]?.toString() || null, qualification: row.Qualification?.toString() || null, }, }); if (row.Department_ID) { const dept = await prisma.department.findUnique({ where: { departmentId: row.Department_ID.toString() }, }); if (dept) { await prisma.doctorDepartment.upsert({ where: { doctorId_departmentId: { doctorId: doctor.id, departmentId: dept.id, }, }, update: {}, create: { doctorId: doctor.id, departmentId: dept.id, }, }); } } } } // 3. TIMINGS if (timings) { for (const row of timings) { if (!row.GG_ID) continue; const doctor = await prisma.doctor.findUnique({ where: { doctorId: row.GG_ID.toString() }, include: { departments: true }, }); if (doctor && doctor.departments.length > 0) { const doctorDeptId = doctor.departments[0].id; const rawAdd = row.Additional?.toString() || ""; const rawMon = row.Monday?.toString() || ""; const isAppt = (val) => /appointment|basis|on call/i.test(val); let finalAdd = rawAdd; if (!finalAdd && isAppt(rawMon)) finalAdd = rawMon; await prisma.doctorTiming.upsert({ where: { doctorDepartmentId: doctorDeptId }, update: { monday: isAppt(rawMon) ? null : row.Monday?.toString() || null, tuesday: row.Tuesday?.toString() || null, wednesday: row.Wednesday?.toString() || null, thursday: row.Thursday?.toString() || null, friday: row.Friday?.toString() || null, saturday: row.Saturday?.toString() || null, sunday: row.Sunday?.toString() || null, additional: finalAdd || null, }, create: { doctorDepartmentId: doctorDeptId, monday: isAppt(rawMon) ? null : row.Monday?.toString() || null, tuesday: row.Tuesday?.toString() || null, wednesday: row.Wednesday || null, thursday: row.Thursday || null, friday: row.Friday || null, saturday: row.Saturday || null, sunday: row.Sunday || null, additional: finalAdd || null, }, }); } } } // 4. CAREERS if (careers) { for (const row of careers) { if (!row.Post) continue; const cId = row.Id ? parseInt(row.Id) : undefined; const data = { post: row.Post?.toString(), designation: row.Designation?.toString() || null, qualification: row.Qualification?.toString() || null, experienceNeed: row.ExperienceNeed?.toString() || null, email: row.HiringEmail?.toString() || null, number: row.Number?.toString() || null, status: row.Status?.toString() || "new", }; if (cId) { await prisma.career.upsert({ where: { id: cId }, update: data, create: { ...data, id: cId }, }); } else { await prisma.career.create({ data }); } } } // 5. INQUIRIES if (inquiries) { for (const row of inquiries) { if (!row.FullName) continue; await prisma.inquiry.create({ data: { fullName: row.FullName.toString(), number: row.Number?.toString() || "", emailId: row.EmailId?.toString() || null, subject: row.Subject?.toString() || null, message: row.Message?.toString() || null, createdAt: row.Date ? new Date(row.Date) : new Date(), }, }); } } // 6. ACADEMICS & RESEARCH (FIXED HERE) if (academics) { for (const row of academics) { if (!row.FullName) continue; await prisma.academicsResearch.create({ data: { fullName: row.FullName.toString(), number: row.Number?.toString() || "", emailId: row.EmailId?.toString() || null, subject: row.Subject?.toString() || null, // Force String courseName: row["Course Name"]?.toString() || null, message: row.Message?.toString() || null, createdAt: row.Date ? new Date(row.Date) : new Date(), }, }); } } // 7. APPOINTMENTS if (appointments) { for (const row of appointments) { if (!row.FullName) continue; const doctorName = row.Doctor?.toString(); const departmentName = row["Department Id"]?.toString(); const doctor = await prisma.doctor.findFirst({ where: { name: doctorName }, }); const department = await prisma.department.findFirst({ where: { name: departmentName }, }); const parseDate = (value) => { if (!value) return new Date(); // Excel numeric date if (typeof value === "number") { return new Date((value - 25569) * 86400 * 1000); } if (typeof value === "string") { const v = value.trim(); // Handle DD/MM/YYYY const ddmmyyyy = /^(\d{2})\/(\d{2})\/(\d{4})$/; const match = v.match(ddmmyyyy); if (match) { const [_, dd, mm, yyyy] = match; return new Date(`${yyyy}-${mm}-${dd}`); } // Fallback (ISO or other valid formats) const d = new Date(v); if (!isNaN(d.getTime()) && d.getFullYear() < 2100) { return d; } } console.warn("⚠️ Invalid date, using current date:", value); return new Date(); }; if (doctor && department) { await prisma.appointment.create({ data: { name: row.FullName.toString(), mobileNumber: row.Number?.toString() || "", email: row["Email Id"]?.toString() || null, message: row.Message?.toString() || null, date: parseDate(row.Date), doctorId: doctor.doctorId, departmentId: department.departmentId, }, }); } } } // 8. CANDIDATES if (candidates) { for (const row of candidates) { if (!row.FullName || !row.CareerId) continue; await prisma.candidate .create({ data: { fullName: row.FullName.toString(), mobile: row.Number?.toString() || "", email: row.EmailId?.toString() || "", subject: row.Subject?.toString() || "", coverLetter: row["Cover Letter"]?.toString() || "", careerId: parseInt(row.CareerId), createdAt: row.Date ? new Date(row.Date) : new Date(), }, }) .catch(() => {}); } } // 9. NEWS & MEDIA if (news) { for (const row of news) { if (!row.Headline) continue; await prisma.newsMedia.create({ data: { headline: row.Headline.toString(), content: row.Content?.toString() || null, firstPara: row.FirstPara?.toString() || null, secondPara: row.SecondPara?.toString() || null, author: row.Author?.toString() || null, date: row.Date ? new Date(row.Date) : null, }, }); } } res .status(200) .json({ success: true, message: "✅ Import completed successfully!" }); } catch (error) { console.error("❌ Bulk Import Error:", error); res.status(500).json({ success: false, error: error.message }); } };