From 0a8dbfe964eae4d6d15c48a8935bc41168b84a67 Mon Sep 17 00:00:00 2001 From: Deep Koluguri Date: Tue, 12 May 2026 23:13:32 -0400 Subject: [PATCH] Initial commit: CuraFlow WhatsApp-First HMS with Multi-Vertical support --- Dockerfile | 12 + backend/billingManager.js | 39 +++ backend/botLogic.js | 108 ++++++++ backend/configManager.js | 31 +++ backend/i18n.js | 56 ++++ backend/labManager.js | 57 ++++ backend/pharmacyManager.js | 46 ++++ backend/prescriptionManager.js | 32 +++ backend/queueManager.js | 75 ++++++ backend/server.js | 28 ++ backend/staffManager.js | 39 +++ backend/triageEngine.js | 43 ++++ backend/voiceAI.js | 33 +++ billing.css | 76 ++++++ billing.html | 126 +++++++++ dashboard.css | 457 +++++++++++++++++++++++++++++++++ dashboard.html | 180 +++++++++++++ dashboard.js | 95 +++++++ index.css | 434 +++++++++++++++++++++++++++++++ index.html | 154 +++++++++++ index.js | 84 ++++++ k8s/app.yaml | 52 ++++ k8s/database.yaml | 33 +++ lab.css | 19 ++ lab.html | 109 ++++++++ login.css | 98 +++++++ login.html | 44 ++++ pharmacy.css | 35 +++ pharmacy.html | 120 +++++++++ register.html | 55 ++++ settings.css | 30 +++ settings.html | 94 +++++++ staff.css | 30 +++ staff.html | 118 +++++++++ 34 files changed, 3042 insertions(+) create mode 100644 Dockerfile create mode 100644 backend/billingManager.js create mode 100644 backend/botLogic.js create mode 100644 backend/configManager.js create mode 100644 backend/i18n.js create mode 100644 backend/labManager.js create mode 100644 backend/pharmacyManager.js create mode 100644 backend/prescriptionManager.js create mode 100644 backend/queueManager.js create mode 100644 backend/server.js create mode 100644 backend/staffManager.js create mode 100644 backend/triageEngine.js create mode 100644 backend/voiceAI.js create mode 100644 billing.css create mode 100644 billing.html create mode 100644 dashboard.css create mode 100644 dashboard.html create mode 100644 dashboard.js create mode 100644 index.css create mode 100644 index.html create mode 100644 index.js create mode 100644 k8s/app.yaml create mode 100644 k8s/database.yaml create mode 100644 lab.css create mode 100644 lab.html create mode 100644 login.css create mode 100644 login.html create mode 100644 pharmacy.css create mode 100644 pharmacy.html create mode 100644 register.html create mode 100644 settings.css create mode 100644 settings.html create mode 100644 staff.css create mode 100644 staff.html diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..192d02e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM node:18-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . + +EXPOSE 3000 + +CMD ["node", "backend/server.js"] diff --git a/backend/billingManager.js b/backend/billingManager.js new file mode 100644 index 0000000..7fa5c04 --- /dev/null +++ b/backend/billingManager.js @@ -0,0 +1,39 @@ +/** + * BillingManager: Aggregates revenue from OP, Pharmacy, and Lab. + */ + +class BillingManager { + constructor() { + this.dailyCollections = { + op: 4500, // Consultation fees + pharmacy: 12450, // Medicine sales + lab: 8200 // Diagnostic tests + }; + this.transactions = [ + { id: 'TXN-001', dept: 'OP', amount: 500, patient: 'Rahul Verma', status: 'PAID' }, + { id: 'TXN-002', dept: 'Pharmacy', amount: 1200, patient: 'Anjali Singh', status: 'PAID' }, + { id: 'TXN-003', dept: 'Lab', amount: 2500, patient: 'Suresh Kumar', status: 'PENDING' } + ]; + } + + getDailySummary() { + const total = this.dailyCollections.op + this.dailyCollections.pharmacy + this.dailyCollections.lab; + return { + ...this.dailyCollections, + total, + growth: '+12.5%' // Simulated growth vs yesterday + }; + } + + getTransactions() { + return this.transactions; + } + + addRevenue(dept, amount) { + if (this.dailyCollections[dept.toLowerCase()] !== undefined) { + this.dailyCollections[dept.toLowerCase()] += amount; + } + } +} + +module.exports = new BillingManager(); diff --git a/backend/botLogic.js b/backend/botLogic.js new file mode 100644 index 0000000..09ea471 --- /dev/null +++ b/backend/botLogic.js @@ -0,0 +1,108 @@ +const queueManager = require('./queueManager'); +const triageEngine = require('./triageEngine'); +const i18n = require('./i18n'); + +/** + * BotLogic: Simulates the WhatsApp conversational flow + */ +class BotLogic { + constructor() { + this.states = {}; // Track session state per patient + } + + async handleMessage(patientId, message) { + const text = message.toLowerCase(); + let state = this.states[patientId] || 'IDLE'; + let lang = this.states[`${patientId}_lang`] || 'en'; + + // 1. Language Selection (First time or reset) + if (text.includes('hi') && !this.states[`${patientId}_lang`]) { + this.states[patientId] = 'SELECT_LANG'; + return { + reply: "Welcome to CuraFlow! Please select your language / भाषा चुनें / భాషను ఎంచుకోండి:\n\n1. English\n2. Hindi (हिंदी)\n3. Telugu (తెలుగు)", + buttons: ["English", "Hindi", "Telugu"] + }; + } + + if (state === 'SELECT_LANG') { + if (text.includes('hindi')) lang = 'hi'; + if (text.includes('telugu')) lang = 'te'; + this.states[`${patientId}_lang`] = lang; + this.states[patientId] = 'IDLE'; + state = 'IDLE'; // Continue to welcome + } + + // 2. Initial Greeting / Booking Intent + if (state === 'IDLE') { + return { + reply: i18n.t(lang, 'welcome'), + buttons: i18n.getButtons(lang) + }; + } + + // 2. Booking Flow + if (text.includes('book for today')) { + return { + reply: "Great! Please select your preferred time slot:", + buttons: ["10:00 AM", "10:30 AM", "11:00 AM"] + }; + } + + if (text === '10:00 am' || text === '10:30 am') { + const date = new Date().toISOString().split('T')[0]; + const result = queueManager.bookOnline(date, text.replace(' am', '')); + + if (result.success) { + this.states[patientId] = 'TRIAGE_START'; + return { + reply: i18n.t(lang, 'triage_ask', { token: result.token }) + + ` \n\n💳 *Payment Required*: Please pay ₹${result.fee} to activate your token: http://razorpay.me/curaflow_mock`, + }; + } else { + return { + reply: "❌ Sorry, the 10:00 AM online slots are full (Max 3/slot). Would you like to try 11:30 AM?", + buttons: ["Try 11:30 AM", "Waitlist Me"] + }; + } + } + + // 3. Triage State Handling + if (this.states[patientId] === 'TRIAGE_START') { + const analysis = await triageEngine.analyzeSymptoms(text); + this.states[patientId] = 'TRIAGE_FOLLOWUP'; + this.states[`${patientId}_summary`] = analysis.summary; + + return { + reply: `Thank you. AI Analysis: _${analysis.category}_.\n\nQuick follow-up: ${analysis.followUp}` + }; + } + + if (this.states[patientId] === 'TRIAGE_FOLLOWUP') { + this.states[patientId] = 'IDLE'; + return { + reply: i18n.t(lang, 'triage_done') + }; + } + + // 4. Daily Updates / Status + if (text.includes('status') || text.includes('queue') || text.includes('कतार') || text.includes('స్థితి')) { + return { + reply: i18n.t(lang, 'status_update', { current: 'WK-3', pos: 'ON-1', wait: '8' }) + }; + } + + if (text.includes('tips') || text.includes('update')) { + const updates = queueManager.getDailyUpdates(patientId); + return { + reply: "🔔 *Daily Updates for You:*\n\n" + updates.join('\n\n') + }; + } + + // 4. Default Fallback + return { + reply: "I'm sorry, I didn't quite catch that. You can type 'Hi' to see the main menu." + }; + } +} + +module.exports = new BotLogic(); diff --git a/backend/configManager.js b/backend/configManager.js new file mode 100644 index 0000000..4103afa --- /dev/null +++ b/backend/configManager.js @@ -0,0 +1,31 @@ +/** + * ConfigManager: Central configuration for hospital-wide pricing and slots. + */ + +class ConfigManager { + constructor() { + this.config = { + consultationFee: 500, + slotDuration: 30, // minutes + onlineSlotLimit: 3, + walkinSlotLimit: 5, + hospitalName: "Dr. Sharma's Clinic", + currency: "₹" + }; + } + + get(key) { + return this.config[key]; + } + + set(key, value) { + this.config[key] = value; + console.log(`Config updated: ${key} = ${value}`); + } + + getAll() { + return this.config; + } +} + +module.exports = new ConfigManager(); diff --git a/backend/i18n.js b/backend/i18n.js new file mode 100644 index 0000000..e0749aa --- /dev/null +++ b/backend/i18n.js @@ -0,0 +1,56 @@ +/** + * i18n: Translation manager for CuraFlow WhatsApp Bot. + * Supports English (en), Hindi (hi), and Telugu (te). + */ + +const translations = { + en: { + welcome: "Welcome to Dr. Sharma's Clinic! 🏥 I can help you book a token or check your status.\n\nWould you like to:", + book: "Book for Today", + check: "Check Queue Status", + tips: "Daily Health Tips", + select_slot: "Great! Please select your preferred time slot:", + triage_ask: "✅ Confirmed! Your token is *{{token}}*.\n\nTo help Dr. Sharma prepare, could you briefly describe your symptoms?", + triage_done: "Got it. I've shared these details with Dr. Sharma. See you soon! 🏥", + status_update: "Current Status: \n📍 Doctor is seeing: #{{current}}\n⏳ Your position: #{{pos}}\n🚀 Estimated wait: {{wait}} mins." + }, + hi: { + welcome: "डॉ. शर्मा के क्लिनिक में आपका स्वागत है! 🏥 मैं टोकन बुक करने या आपकी स्थिति की जांच करने में आपकी मदद कर सकता हूँ।\n\nक्या आप चाहेंगे:", + book: "आज के लिए बुक करें", + check: "कतार की स्थिति जांचें", + tips: "स्वास्थ्य युक्तियाँ", + select_slot: "बहुत बढ़िया! कृपया अपना पसंदीदा समय स्लॉट चुनें:", + triage_ask: "✅ पुष्टि हो गई! आपका टोकन *{{token}}* है।\n\nडॉ. शर्मा को तैयारी में मदद करने के लिए, क्या आप अपने लक्षणों का संक्षेप में वर्णन कर सकते हैं?", + triage_done: "समझ गया। मैंने ये विवरण डॉ. शर्मा के साथ साझा कर दिए हैं। जल्द मिलते हैं! 🏥", + status_update: "वर्तमान स्थिति: \n📍 डॉक्टर देख रहे हैं: #{{current}}\n⏳ आपकी स्थिति: #{{pos}}\n🚀 अनुमानित प्रतीक्षा: {{wait}} मिनट।" + }, + te: { + welcome: "డాక్టర్ శర్మ క్లినిక్‌కి స్వాగతం! 🏥 టోకెన్ బుక్ చేసుకోవడంలో లేదా మీ స్థితిని తనిఖీ చేయడంలో నేను మీకు సహాయపడగలను.\n\nమీరు వీటిని చేయాలనుకుంటున్నారా:", + book: "ఈరోజు కోసం బుక్ చేయండి", + check: "క్యూ స్థితిని తనిఖీ చేయండి", + tips: "ఆరోగ్య చిట్కాలు", + select_slot: "అద్భుతం! దయచేసి మీకు నచ్చిన సమయాన్ని ఎంచుకోండి:", + triage_ask: "✅ ధృవీకరించబడింది! మీ టోకెన్ *{{token}}*.\n\nడాక్టర్ శర్మ సిద్ధం కావడానికి సహాయపడటానికి, మీరు మీ లక్షణాలను క్లుప్తంగా వివరించగలరా?", + triage_done: "అర్థమైంది. నేను ఈ వివరాలను డాక్టర్ శర్మతో పంచుకున్నాను. త్వరలో కలుద్దాం! 🏥", + status_update: "ప్రస్తుత స్థితి: \n📍 డాక్టర్ చూస్తున్నారు: #{{current}}\n⏳ మీ స్థానం: #{{pos}}\n🚀 అంచనా వేచి ఉండే సమయం: {{wait}} నిమిషాలు." + } +}; + +class I18nManager { + t(lang, key, params = {}) { + let text = translations[lang] ? translations[lang][key] : translations['en'][key]; + + for (const [pKey, pVal] of Object.entries(params)) { + text = text.replace(`{{${pKey}}}`, pVal); + } + + return text; + } + + getButtons(lang) { + const t = translations[lang] || translations['en']; + return [t.book, t.check, t.tips]; + } +} + +module.exports = new I18nManager(); diff --git a/backend/labManager.js b/backend/labManager.js new file mode 100644 index 0000000..c3744ea --- /dev/null +++ b/backend/labManager.js @@ -0,0 +1,57 @@ +/** + * LabManager: Handles test bookings, results, and AI explanations. + */ + +class LabManager { + constructor() { + this.tests = [ + { id: 'T01', name: 'Complete Blood Count (CBC)', price: 450 }, + { id: 'T02', name: 'Lipid Profile', price: 800 }, + { id: 'T03', name: 'Blood Sugar (F/PP)', price: 150 }, + { id: 'T04', name: 'Thyroid (T3 T4 TSH)', price: 650 }, + { id: 'T05', name: 'Liver Function Test', price: 1200 } + ]; + this.records = [ + { id: 'LAB-501', patientName: 'Suresh Kumar', testName: 'CBC + Blood Sugar', status: 'PROCESSING', urgency: 'URGENT' }, + { id: 'LAB-502', patientName: 'Anjali Singh', testName: 'Lipid Profile', status: 'COLLECTING', urgency: 'ROUTINE' } + ]; + } + + getAvailableTests() { + return this.tests; + } + + createTestRequest(patientName, testId) { + const test = this.tests.find(t => t.id === testId); + const record = { + id: `LAB-${Date.now()}`, + patientName, + testName: test.name, + status: 'COLLECTING_SAMPLE', + results: null, + timestamp: new Date().toISOString() + }; + this.records.push(record); + return record; + } + + updateResults(recordId, results) { + const record = this.records.find(r => r.id === recordId); + if (record) { + record.results = results; + record.status = 'COMPLETED'; + + // AI Explainer logic (Simulation) + const explanation = `Your ${record.testName} results show ${results.hemoglobin < 12 ? 'low hemoglobin (anemia)' : 'normal levels'}. Please discuss with Dr. Sharma.`; + + return { + success: true, + explanation, + whatsappMessage: `🔬 *Lab Report Ready*\n\n*Test:* ${record.testName}\n\n*AI Summary:* ${explanation}\n\n_Full report PDF attached._` + }; + } + return { success: false }; + } +} + +module.exports = new LabManager(); diff --git a/backend/pharmacyManager.js b/backend/pharmacyManager.js new file mode 100644 index 0000000..4f82133 --- /dev/null +++ b/backend/pharmacyManager.js @@ -0,0 +1,46 @@ +/** + * PharmacyManager: Handles inventory, prescriptions, and billing. + */ + +class PharmacyManager { + constructor() { + this.inventory = [ + { id: 'M01', name: 'Paracetamol 500mg', stock: 120, price: 5 }, + { id: 'M02', name: 'Amoxicillin 250mg', stock: 45, price: 12 }, + { id: 'M03', name: 'Cough Syrup', stock: 4, price: 85 }, + { id: 'M04', name: 'Vitamin C', stock: 200, price: 3 }, + { id: 'M05', name: 'Azithromycin', stock: 30, price: 150 }, + { id: 'M06', name: 'Insulin Glargine', stock: 12, price: 450 } + ]; + this.pendingOrders = [ + { id: 'ORD-901', patientName: 'Rahul Verma', medicines: 'Paracetamol, Cough Syrup', status: 'PENDING', timestamp: '2026-05-13T09:15:00Z' }, + { id: 'ORD-902', patientName: 'Anjali Singh', medicines: 'Amoxicillin', status: 'READY', timestamp: '2026-05-13T09:45:00Z' } + ]; + } + + getInventory() { + return this.inventory; + } + + addPendingOrder(patientName, medicines) { + this.pendingOrders.push({ + id: `ORD-${Date.now()}`, + patientName, + medicines, + status: 'PENDING', + timestamp: new Date().toISOString() + }); + } + + processOrder(orderId) { + const order = this.pendingOrders.find(o => o.id === orderId); + if (order) { + order.status = 'READY'; + // In a real app, update inventory stock here + return { success: true, message: `Order for ${order.patientName} is ready for pickup.` }; + } + return { success: false, message: "Order not found." }; + } +} + +module.exports = new PharmacyManager(); diff --git a/backend/prescriptionManager.js b/backend/prescriptionManager.js new file mode 100644 index 0000000..bd4f37f --- /dev/null +++ b/backend/prescriptionManager.js @@ -0,0 +1,32 @@ +/** + * PrescriptionManager: Handles digital prescription generation. + */ + +class PrescriptionManager { + constructor() { + this.prescriptions = []; + } + + generate(patientId, doctorId, medicines, diagnosis) { + const id = `RX-${Date.now()}`; + const prescription = { + id, + patientId, + doctorId, + date: new Date().toISOString(), + diagnosis, + medicines, // Array of { name, dosage, frequency, duration } + }; + + this.prescriptions.push(prescription); + + // In a real app, you'd generate a PDF here + return { + success: true, + prescriptionId: id, + whatsappMessage: `💊 *Digital Prescription from Dr. Sharma*\n\n*Diagnosis:* ${diagnosis}\n\n*Medicines:*\n${medicines.map(m => `- ${m.name}: ${m.dosage} (${m.frequency}) for ${m.duration}`).join('\n')}\n\n_You can show this message at our pharmacy for a 10% discount._` + }; + } +} + +module.exports = new PrescriptionManager(); diff --git a/backend/queueManager.js b/backend/queueManager.js new file mode 100644 index 0000000..0c65c9c --- /dev/null +++ b/backend/queueManager.js @@ -0,0 +1,75 @@ +/** + * QueueManager: Handles the hybrid slot logic for CuraFlow + * - 30-min slots + * - Max 3 online bookings (Hard Cap) + * - 5 Walk-in slots + */ + +const configManager = require('./configManager'); + +class QueueManager { + constructor() { + this.slots = {}; // Key: "YYYY-MM-DD:HH:mm" + } + + getSlotKey(date, time) { + // Round time to nearest slot duration + const duration = configManager.get('slotDuration'); + const [hours, minutes] = time.split(':'); + const roundedMins = parseInt(minutes) < duration ? '00' : duration; + return `${date}:${hours}:${roundedMins}`; + } + + getSlotStatus(date, time) { + const key = this.getSlotKey(date, time); + if (!this.slots[key]) { + this.slots[key] = { + online: 0, + walkin: 0, + maxOnline: configManager.get('onlineSlotLimit'), + maxWalkin: configManager.get('walkinSlotLimit') + }; + } + return this.slots[key]; + } + + bookOnline(date, time) { + const status = this.getSlotStatus(date, time); + if (status.online < status.maxOnline) { + status.online++; + return { + success: true, + token: `ON-${status.online}`, + slot: this.getSlotKey(date, time), + status: 'PENDING_PAYMENT', + fee: configManager.get('consultationFee') + }; + } + return { success: false, message: "Slot full for online booking. Try next slot." }; + } + + bookWalkin(date, time) { + const status = this.getSlotStatus(date, time); + if (status.walkin < status.maxWalkin) { + status.walkin++; + return { + success: true, + token: `WK-${status.walkin}`, + status: 'PENDING_PAYMENT', + fee: configManager.get('consultationFee') + }; + } + return { success: false, message: "Clinic is at full capacity for this slot." }; + } + + getDailyUpdates(patientId) { + // Mock data for daily updates + return [ + "Good morning! Your appointment with Dr. Sharma is at 10:30 AM today.", + "Queue Update: The clinic is running 10 mins behind schedule. Please plan accordingly.", + "Reminder: Don't forget to bring your previous blood reports." + ]; + } +} + +module.exports = new QueueManager(); diff --git a/backend/server.js b/backend/server.js new file mode 100644 index 0000000..b8fa98e --- /dev/null +++ b/backend/server.js @@ -0,0 +1,28 @@ +const express = require('express'); +const path = require('path'); +const botLogic = require('./botLogic'); +const app = express(); +const port = 3000; + +app.use(express.json()); +app.use(express.static(path.join(__dirname, '../'))); // Serve static files from parent dir + + +// Mock WhatsApp Webhook +app.post('/whatsapp/webhook', async (req, res) => { + const { from, body } = req.body; + console.log(`Received message from ${from}: ${body}`); + + const response = await botLogic.handleMessage(from, body); + + // In a real app, you'd call Twilio/Meta API here to send the reply + res.json({ + success: true, + reply: response.reply, + buttons: response.buttons || [] + }); +}); + +app.listen(port, () => { + console.log(`CuraFlow WhatsApp Mock Server running at http://localhost:${port}`); +}); diff --git a/backend/staffManager.js b/backend/staffManager.js new file mode 100644 index 0000000..28bf47f --- /dev/null +++ b/backend/staffManager.js @@ -0,0 +1,39 @@ +/** + * StaffManager: Handles staff records, attendance, and payroll. + */ + +class StaffManager { + constructor() { + this.staff = [ + { id: 'S001', name: 'Dr. Sharma', role: 'Doctor', salary: 120000, incentives: 4500, status: 'PRESENT' }, + { id: 'S002', name: 'Anjali Singh', role: 'Pharmacist', salary: 35000, incentives: 1200, status: 'PRESENT' }, + { id: 'S003', name: 'Vikram Goel', role: 'Lab Tech', salary: 28000, incentives: 800, status: 'ABSENT' }, + { id: 'S004', name: 'Priya Raj', role: 'Nurse', salary: 25000, incentives: 0, status: 'PRESENT' } + ]; + } + + getStaffList() { + return this.staff; + } + + calculatePayroll(staffId) { + const member = this.staff.find(s => s.id === staffId); + if (member) { + return { + base: member.salary, + incentives: member.incentives, + total: member.salary + member.incentives + }; + } + return null; + } + + updateStatus(staffId, status) { + const member = this.staff.find(s => s.id === staffId); + if (member) { + member.status = status; + } + } +} + +module.exports = new StaffManager(); diff --git a/backend/triageEngine.js b/backend/triageEngine.js new file mode 100644 index 0000000..7954f61 --- /dev/null +++ b/backend/triageEngine.js @@ -0,0 +1,43 @@ +/** + * TriageEngine: Analyzes symptoms and generates follow-up questions. + * In a real app, this would call Gemini 1.5 Flash. + */ + +class TriageEngine { + constructor() { + this.categories = { + URGENT: { label: "Urgent", color: "#EF4444" }, + MODERATE: { label: "Moderate", color: "#F59E0B" }, + ROUTINE: { label: "Routine", color: "#10B981" } + }; + } + + async analyzeSymptoms(symptoms) { + const text = symptoms.toLowerCase(); + + // Simple keyword-based triage (Simulation) + if (text.includes('chest pain') || text.includes('breath') || text.includes('dizzy')) { + return { + category: "URGENT", + summary: "Patient reporting possible cardiovascular or respiratory distress.", + followUp: "How long have you been feeling this pain? Is it spreading to your arm or neck?" + }; + } + + if (text.includes('fever') || text.includes('cough') || text.includes('stomach')) { + return { + category: "MODERATE", + summary: "Patient reporting common infection symptoms.", + followUp: "What is your current temperature? Do you have any other symptoms like body ache?" + }; + } + + return { + category: "ROUTINE", + summary: "General consultation or mild symptoms.", + followUp: "Are you here for a regular checkup or a specific issue?" + }; + } +} + +module.exports = new TriageEngine(); diff --git a/backend/voiceAI.js b/backend/voiceAI.js new file mode 100644 index 0000000..6941c6c --- /dev/null +++ b/backend/voiceAI.js @@ -0,0 +1,33 @@ +/** + * VoiceAI: Transcribes and extracts medical insights from doctor-patient conversations. + * Uses AI to filter out small talk and focus on health details. + */ + +class VoiceAI { + async extractInsights(transcript) { + // In a real app, this would be a prompt to Gemini: + // "Extract only medical advice, diagnosis, and next steps from this transcript. Ignore small talk." + + const smallTalkKeywords = ['weather', 'family', 'how are you', 'cricket', 'politics']; + + // Simulation of AI filtering + const lines = transcript.split('. '); + const medicalLines = lines.filter(line => { + const isSmallTalk = smallTalkKeywords.some(kw => line.toLowerCase().includes(kw)); + return !isSmallTalk && line.length > 10; + }); + + const summary = medicalLines.join('. ') + '.'; + + return { + summary, + whatsappMessage: `👨‍⚕️ *Doctor's Key Insights from Your Visit*\n\n${summary}\n\n_Stay healthy!_` + }; + } + + getMockTranscript() { + return "Hello Rahul, how is your family? The weather is very hot today. Regarding your cough, it seems like a viral infection. You should avoid cold drinks for 3 days. Also, did you watch the match yesterday? Take the paracetamol after meals. I'll see you in a week."; + } +} + +module.exports = new VoiceAI(); diff --git a/billing.css b/billing.css new file mode 100644 index 0000000..97caace --- /dev/null +++ b/billing.css @@ -0,0 +1,76 @@ +.total-card { + background: linear-gradient(135deg, var(--primary), #1E293B); + color: white; +} + +.total-card span { + color: #94A3B8; +} + +.revenue-grid { + display: grid; + grid-template-columns: 400px 1fr; + gap: 1.5rem; +} + +/* Chart Mockup */ +.donut-chart-simulation { + display: flex; + flex-direction: column; + align-items: center; + padding: 2rem 0; +} + +.chart-mock { + width: 200px; + height: 200px; + border-radius: 50%; + background: conic-gradient( + var(--secondary) 0% 18%, + var(--accent) 18% 68%, + #6366F1 68% 100% + ); + position: relative; + margin-bottom: 2rem; +} + +.chart-mock::after { + content: ''; + position: absolute; + width: 140px; + height: 140px; + background: white; + top: 30px; + left: 30px; + border-radius: 50%; +} + +.chart-legend { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + width: 100%; +} + +.legend-item { + font-size: 0.8rem; + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--text-muted); +} + +.dot { + width: 10px; + height: 10px; + border-radius: 50%; +} + +.dot.op { background: var(--secondary); } +.dot.phar { background: var(--accent); } +.dot.lab { background: #6366F1; } + +.status-pill.paid { + background: #DCFCE7; + color: #166534; +} diff --git a/billing.html b/billing.html new file mode 100644 index 0000000..5f0ae36 --- /dev/null +++ b/billing.html @@ -0,0 +1,126 @@ + + + + + + CuraFlow Billing | Hospital Revenue & Accounts + + + + + + + + +
+
+
+

Hospital Accounts

+

Comprehensive daily revenue summary for the Hospital Owner.

+
+
+ +
+
+ +
+
+ Total Revenue (Today) +

₹25,150

+ ↑ 12.5% vs Yesterday +
+
+ OP Consultation +

₹4,500

+ 9 Patients Paid +
+
+ Pharmacy Sales +

₹12,450

+ 18 Invoices +
+
+ Lab Collections +

₹8,200

+ 12 Tests +
+
+ +
+
+
+

Revenue Distribution

+
+
+ +
+
+
+
+
+
+
OP (18%)
+
Pharmacy (50%)
+
Lab (32%)
+
+
+
+ +
+
+

Recent Transactions

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Txn IDPatientDepartmentAmountStatus
#TXN-882Vikram GoelPharmacy₹1,240Paid
#TXN-881Ananya RaoLab₹2,500Paid
#TXN-880Rahul VermaOP₹500Pending
+
+
+
+ + diff --git a/dashboard.css b/dashboard.css new file mode 100644 index 0000000..9d9f1ad --- /dev/null +++ b/dashboard.css @@ -0,0 +1,457 @@ +.dashboard-body { + display: grid; + grid-template-columns: 280px 1fr; + height: 100vh; + background: #F1F5F9; + color: var(--text-main); +} + +/* Sidebar */ +.sidebar { + background: var(--primary); + color: white; + padding: 2rem; + display: flex; + flex-direction: column; +} + +.sidebar .logo { + color: white; + margin-bottom: 3rem; +} + +.side-nav { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.side-nav a { + color: #94A3B8; + text-decoration: none; + padding: 1rem; + border-radius: 12px; + display: flex; + align-items: center; + gap: 1rem; + transition: all 0.3s; +} + +.side-nav a:hover, .side-nav a.active { + background: rgba(255, 255, 255, 0.1); + color: white; +} + +.side-nav a.nav-disabled { + opacity: 0.5; + cursor: not-allowed; + font-size: 0.85rem; +} + +.side-nav a.nav-disabled small { + font-size: 0.6rem; + background: var(--accent); + color: white; + padding: 2px 6px; + border-radius: 4px; + margin-left: 4px; +} + +.user-profile { + display: flex; + align-items: center; + gap: 1rem; + padding-top: 2rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +.avatar { + width: 40px; + height: 40px; + background: var(--secondary); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; +} + +.info span { + display: block; + font-size: 0.75rem; + color: #94A3B8; +} + +/* Main Content */ +.dashboard-main { + padding: 2rem; + overflow-y: auto; +} + +.dash-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +.search-bar input { + width: 400px; + padding: 0.75rem 1.5rem; + border-radius: 99px; + border: 1px solid #E2E8F0; + outline: none; + font-size: 0.9rem; +} + +/* Stats */ +.stats-row { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + margin-bottom: 2rem; +} + +.stat-card { + background: white; + padding: 1.5rem; + border-radius: 20px; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); +} + +.stat-card span { + font-size: 0.875rem; + color: var(--text-muted); +} + +.quota-bars { + margin-top: 0.5rem; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.quota-item { + display: flex; + flex-direction: column; +} + +.quota-item small { + font-size: 0.7rem; + color: var(--text-muted); + margin-bottom: 2px; +} + +.bar { + height: 6px; + background: #E2E8F0; + border-radius: 99px; + overflow: hidden; +} + +.fill { + height: 100%; + background: var(--secondary); + border-radius: 99px; +} + +.stat-card h3 { + font-size: 1.75rem; + margin: 0.5rem 0; +} + +.text-accent { color: var(--accent); } + +/* Queue Table */ +.queue-section { + display: grid; + grid-template-columns: 1fr 300px; + gap: 1.5rem; +} + +.section-card { + background: white; + border-radius: 24px; + padding: 2rem; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); +} + +.card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; +} + +.queue-table { + width: 100%; + border-collapse: collapse; +} + +.queue-table th { + text-align: left; + color: var(--text-muted); + font-weight: 500; + font-size: 0.875rem; + padding-bottom: 1rem; + border-bottom: 1px solid #F1F5F9; +} + +.queue-table td { + padding: 1.25rem 0; + border-bottom: 1px solid #F1F5F9; +} + +.counter-badge { + background: #F1F5F9; + color: var(--primary); + border: 2px solid var(--primary); + padding: 0.6rem 1rem; + font-size: 0.9rem; + margin-right: 1rem; +} + +.status-pill { + padding: 0.4rem 0.8rem; + border-radius: 99px; + font-size: 0.75rem; + font-weight: 600; +} + +.status-pill.in-room { + background: #DBEAFE; + color: #1E40AF; +} + +.status-pill.waiting { + background: #FEF3C7; + color: #92400E; +} + +.status-pill.pending-pay { + background: #FFEDD5; + color: #C2410C; + border: 1px solid #F97316; +} + +.source-tag { + font-size: 0.75rem; + font-weight: 600; + padding: 0.25rem 0.6rem; + border-radius: 6px; +} + +.source-tag.online { + background: rgba(56, 189, 248, 0.1); + color: var(--secondary); +} + +.source-tag.walkin { + background: rgba(100, 116, 139, 0.1); + color: var(--text-muted); +} + +/* Modal Styles */ +.modal { + display: none; + position: fixed; + z-index: 2000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.5); + backdrop-filter: blur(4px); +} + +.modal-content { + background-color: white; + margin: 10% auto; + padding: 2rem; + width: 500px; + border-radius: 24px; + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); +} + +.modal-header { + display: flex; + justify-content: space-between; + margin-bottom: 2rem; +} + +.close-modal { + font-size: 1.5rem; + cursor: pointer; +} + +.input-group { + margin-bottom: 1.5rem; +} + +.input-group label { + display: block; + font-size: 0.875rem; + font-weight: 600; + margin-bottom: 0.5rem; +} + +.input-group input { + width: 100%; + padding: 0.75rem; + border-radius: 12px; + border: 1px solid var(--border); +} + +.med-row { + display: grid; + grid-template-columns: 2fr 1fr 1fr; + gap: 0.5rem; + margin-bottom: 0.5rem; +} + +.med-row input { + padding: 0.6rem; + border-radius: 8px; + border: 1px solid var(--border); + font-size: 0.85rem; +} + +.voice-section { + margin-bottom: 2rem; + padding: 1rem; + background: #F8FAFC; + border-radius: 16px; + border: 1px dashed var(--border); + text-align: center; +} + +.recording-status { + margin-top: 1rem; + color: #EF4444; + font-weight: 600; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.hidden { display: none !important; } + +.pulse { + width: 12px; + height: 12px; + background: #EF4444; + border-radius: 50%; + animation: pulse-red 1.5s infinite; +} + +@keyframes pulse-red { + 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); } + 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); } + 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } +} + +.ai-insights-box { + margin-top: 1rem; + background: white; + padding: 1rem; + border-radius: 12px; + border-left: 4px solid var(--accent); + text-align: left; +} + +.ai-insights-box h4 { + font-size: 0.8rem; + color: var(--accent); + margin-bottom: 0.5rem; +} + +.ai-insights-box p { + font-size: 0.85rem; + color: var(--text-main); + line-height: 1.4; +} + +.modal-footer { + margin-top: 2rem; + text-align: right; +} + +.triage-pill { + font-size: 0.75rem; + padding: 0.25rem 0.6rem; + border-radius: 6px; + font-weight: 500; +} + +.triage-pill.urgent { + background: #FEE2E2; + color: #991B1B; + border-left: 3px solid #EF4444; +} + +.triage-pill.moderate { + background: #FEF3C7; + color: #92400E; + border-left: 3px solid #F59E0B; +} + +.triage-pill.routine { + background: #DCFCE7; + color: #166534; + border-left: 3px solid #10B981; +} + +.lang-tag { + font-size: 0.7rem; + background: #F1F5F9; + color: var(--text-muted); + padding: 2px 6px; + border-radius: 4px; + font-weight: 700; +} + +.btn-small { + padding: 0.5rem 1rem; + border-radius: 8px; + border: none; + background: var(--primary); + color: white; + cursor: pointer; + font-size: 0.8rem; +} + +.btn-small.secondary { + background: #F1F5F9; + color: var(--primary); +} + +/* AI Insights Side Panel */ +.ai-insights { + background: linear-gradient(135deg, #0F172A, #1E293B); + color: white; +} + +.ai-insights h3 { + margin-bottom: 1rem; + font-size: 1.25rem; +} + +.ai-insights p { + font-size: 0.9rem; + opacity: 0.9; + margin-bottom: 1.5rem; +} + +.insight-item { + background: rgba(255, 255, 255, 0.05); + padding: 1rem; + border-radius: 12px; + border-left: 3px solid var(--accent); +} + +.insight-item small { + color: var(--accent); + font-weight: 600; +} diff --git a/dashboard.html b/dashboard.html new file mode 100644 index 0000000..a3fe7f8 --- /dev/null +++ b/dashboard.html @@ -0,0 +1,180 @@ + + + + + + CuraFlow Dashboard | Dr. Sharma's Clinic + + + + + + + +
+
+ +
+ Reception Counter: 1 + +
+
+ +
+
+ In Queue +

12 Patients

+ ↑ 4 since last hour +
+
+ Current Slot (10:00-10:30) +
+
+ Online: 3/3 (CLOSED) +
+
+
+ Walk-in: 4/5 +
+
+
+
+
+ Avg. Wait Time +

18 Mins

+ Target: 15 Mins +
+
+ +
+
+
+

Live Queue

+
+ All + Appointments + Walk-ins +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TokenPatient NameSourceLangTriage AIStatusArrivalActions
#012Rahul VermaOnlineHIHigh Fever / CoughIn Room09:15 AM
#013Anjali SinghWalk-inTEGeneral CheckupUnpaid (₹500)09:30 AM
#014Suresh KumarOnlineENBack PainPaid (Waiting)09:45 AM
+
+ +
+
+

✨ AI Insights

+

Predicted peak time: 11:30 AM. Suggesting 5-min break for staff now.

+
+ No-show Alert +

Token #015 (Vikram) hasn't replied to the 10-min reminder.

+
+
+
+
+ + + + + diff --git a/dashboard.js b/dashboard.js new file mode 100644 index 0000000..e4031d4 --- /dev/null +++ b/dashboard.js @@ -0,0 +1,95 @@ +const modal = document.getElementById("prescriptionModal"); +const addMedBtn = document.getElementById("addMed"); +const medList = document.getElementById("medicineList"); +const sendRxBtn = document.getElementById("sendRx"); +const closeModal = document.querySelector(".close-modal"); +const startRecordBtn = document.getElementById("startRecording"); +const recordingStatus = document.getElementById("recordingStatus"); +const aiInsightsBox = document.getElementById("aiInsights"); +const insightText = document.getElementById("insightText"); + +// Open modal when clicking 'Prescribe' +document.querySelectorAll(".btn-small").forEach(btn => { + if (btn.innerText === "Prescribe") { + btn.addEventListener("click", () => { + modal.style.display = "block"; + }); + } +}); + +closeModal.onclick = () => modal.style.display = "none"; +window.onclick = (event) => { + if (event.target == modal) modal.style.display = "none"; +}; + +// Add new medicine row +addMedBtn.onclick = () => { + const row = document.createElement("div"); + row.className = "med-row"; + row.innerHTML = ` + + + + `; + medList.appendChild(row); +}; + +// Handle sending prescription +sendRxBtn.onclick = () => { + const diagnosis = document.getElementById("rxDiagnosis").value; + const meds = []; + document.querySelectorAll(".med-row").forEach(row => { + const name = row.querySelector(".med-name").value; + if (name) { + meds.push({ + name, + dosage: row.querySelector(".med-dosage").value, + frequency: row.querySelector(".med-freq").value + }); + } + }); + + if (!diagnosis || meds.length === 0) { + alert("Please enter diagnosis and at least one medicine."); + return; + } + + // Simulate sending to backend + console.log("Sending Rx:", { diagnosis, meds }); + + // UI Feedback + sendRxBtn.innerText = "Sent! ✅"; + sendRxBtn.style.background = "#10B981"; + + setTimeout(() => { + modal.style.display = "none"; + sendRxBtn.innerText = "Send to WhatsApp"; + sendRxBtn.style.background = ""; + alert("Prescription has been sent to the patient's WhatsApp."); + }, 1500); +}; + +// Voice AI Logic +startRecordBtn.onclick = () => { + startRecordBtn.classList.add("hidden"); + recordingStatus.classList.remove("hidden"); + + // Simulate recording for 3 seconds + setTimeout(() => { + recordingStatus.classList.add("hidden"); + aiInsightsBox.classList.remove("hidden"); + + // Mock data filtering simulation + const mockTranscript = "Hello Rahul, how is your family? The weather is very hot today. Regarding your cough, it seems like a viral infection. You should avoid cold drinks for 3 days. Take the paracetamol after meals. I'll see you in a week."; + + // Filter out small talk (AI simulation) + const insights = "Viral infection detected. Avoid cold drinks for 3 days. Take paracetamol after meals. Follow-up in 1 week."; + + insightText.innerText = insights; + + // Auto-fill diagnosis if empty + if (!document.getElementById("rxDiagnosis").value) { + document.getElementById("rxDiagnosis").value = "Viral Infection"; + } + }, 3000); +}; diff --git a/index.css b/index.css new file mode 100644 index 0000000..4a14c32 --- /dev/null +++ b/index.css @@ -0,0 +1,434 @@ +:root { + --primary: #0F172A; + --secondary: #38BDF8; + --accent: #10B981; + --bg: #F8FAFC; + --surface: #FFFFFF; + --text-main: #1E293B; + --text-muted: #64748B; + --glass: rgba(255, 255, 255, 0.7); + --border: rgba(226, 232, 240, 0.8); + --shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Inter', sans-serif; + background-color: var(--bg); + color: var(--text-main); + line-height: 1.6; + overflow-x: hidden; +} + +h1, h2, h3 { + font-family: 'Outfit', sans-serif; + font-weight: 700; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 2rem; +} + +/* Glass Nav */ +.glass-nav { + position: fixed; + top: 0; + width: 100%; + height: 80px; + display: flex; + align-items: center; + background: var(--glass); + backdrop-filter: blur(12px); + border-bottom: 1px solid var(--border); + z-index: 1000; +} + +.glass-nav .container { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; +} + +.logo { + display: flex; + align-items: center; + gap: 0.75rem; + font-family: 'Outfit', sans-serif; + font-size: 1.5rem; + font-weight: 700; + color: var(--primary); +} + +.logo-icon { + background: linear-gradient(135deg, var(--secondary), var(--accent)); + color: white; + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 10px; + font-size: 1.2rem; +} + +.nav-links { + display: flex; + align-items: center; + gap: 2.5rem; +} + +.nav-links a { + text-decoration: none; + color: var(--text-muted); + font-weight: 500; + transition: color 0.3s; +} + +.nav-links a:hover { + color: var(--secondary); +} + +/* Hero Section */ +.hero { + padding: 180px 0 100px; + background: radial-gradient(circle at top right, rgba(56, 189, 248, 0.1), transparent); +} + +.hero-grid { + display: grid; + grid-template-columns: 1.2fr 1fr; + gap: 4rem; + align-items: center; +} + +.badge { + display: inline-block; + padding: 0.5rem 1rem; + background: rgba(16, 185, 129, 0.1); + color: var(--accent); + border-radius: 99px; + font-size: 0.875rem; + font-weight: 600; + margin-bottom: 1.5rem; +} + +h1 { + font-size: 4rem; + line-height: 1.1; + margin-bottom: 1.5rem; + color: var(--primary); +} + +.text-gradient { + background: linear-gradient(90deg, var(--secondary), var(--accent)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.hero p { + font-size: 1.25rem; + color: var(--text-muted); + margin-bottom: 2.5rem; + max-width: 90%; +} + +.hero-btns { + display: flex; + gap: 1rem; + margin-bottom: 3rem; +} + +.btn-primary { + background: var(--primary); + color: white; + padding: 1rem 2rem; + border-radius: 12px; + border: none; + font-weight: 600; + font-size: 1rem; + cursor: pointer; + transition: transform 0.2s, background 0.3s; +} + +.btn-primary:hover { + background: #1e293b; + transform: translateY(-2px); +} + +.btn-secondary { + background: white; + color: var(--primary); + padding: 1rem 2rem; + border-radius: 12px; + border: 1px solid var(--border); + font-weight: 600; + font-size: 1rem; + cursor: pointer; + transition: all 0.3s; +} + +.btn-secondary:hover { + background: var(--bg); + border-color: var(--text-muted); +} + +.btn-lg { + padding: 1.25rem 2.5rem; + font-size: 1.1rem; +} + +.hero-stats { + display: flex; + gap: 3rem; +} + +.stat { + display: flex; + flex-direction: column; +} + +.stat strong { + font-size: 2rem; + color: var(--primary); + font-family: 'Outfit'; +} + +.stat span { + color: var(--text-muted); + font-size: 0.875rem; +} + +.hero-visual .image-wrapper { + position: relative; + border-radius: 24px; + overflow: hidden; + box-shadow: var(--shadow); + border: 8px solid white; +} + +.hero-visual img { + width: 100%; + display: block; +} + +/* WhatsApp Demo */ +.whatsapp-demo { + padding: 100px 0; + background: white; +} + +.section-header { + text-align: center; + margin-bottom: 4rem; +} + +.section-header h2 { + font-size: 2.5rem; + margin-bottom: 1rem; +} + +.chat-container { + display: grid; + grid-template-columns: 350px 1fr; + gap: 5rem; + align-items: center; +} + +.phone-mockup { + background: #E5DDD5; + border: 12px solid #333; + border-radius: 40px; + height: 600px; + display: flex; + flex-direction: column; + overflow: hidden; + position: relative; + box-shadow: 20px 20px 60px #d1d9e6, -20px -20px 60px #ffffff; +} + +.chat-header { + background: #075E54; + color: white; + padding: 3rem 1.5rem 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.status-dot { + width: 8px; + height: 8px; + background: #25D366; + border-radius: 50%; +} + +.chat-body { + flex: 1; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; + background-image: url('https://user-images.githubusercontent.com/15075759/28719144-86dc0f70-73b1-11e7-911d-60d70fcded21.png'); + background-size: contain; +} + +.msg { + padding: 0.75rem 1rem; + border-radius: 12px; + max-width: 85%; + font-size: 0.9rem; + position: relative; +} + +.msg.bot { + background: white; + align-self: flex-start; + border-top-left-radius: 0; +} + +.msg-options { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding-top: 1rem; +} + +.opt { + background: rgba(255, 255, 255, 0.9); + border: 1px solid var(--accent); + color: var(--accent); + padding: 0.6rem; + border-radius: 99px; + font-weight: 600; + font-size: 0.85rem; + cursor: pointer; + transition: all 0.2s; +} + +.opt:hover { + background: var(--accent); + color: white; +} + +.feature-info { + display: grid; + gap: 2rem; +} + +.info-card { + padding: 2rem; + background: var(--bg); + border-radius: 20px; + border: 1px solid var(--border); + transition: transform 0.3s; +} + +.info-card:hover { + transform: translateX(10px); + border-color: var(--secondary); +} + +.info-card h3 { + margin-bottom: 0.75rem; + color: var(--primary); +} + +/* Daily Updates */ +.daily-updates-section { + padding: 60px 0; + background: #F1F5F9; +} + +.updates-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; +} + +.update-card { + background: white; + padding: 2rem; + border-radius: 20px; + border-top: 4px solid var(--secondary); + box-shadow: var(--shadow); +} + +.update-icon { + font-size: 2rem; + margin-bottom: 1rem; +} + +.update-card h4 { + margin-bottom: 0.5rem; + color: var(--primary); +} + +.update-card p { + font-size: 0.9rem; + color: var(--text-muted); + font-style: italic; +} + +/* Features Grid */ +.features-grid { + padding: 100px 0; +} + +.grid-layout { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; +} + +.feature-item { + padding: 3rem 2rem; + background: white; + border-radius: 24px; + box-shadow: var(--shadow); + text-align: center; + transition: transform 0.3s; +} + +.feature-item:hover { + transform: translateY(-10px); +} + +.feature-item .icon { + font-size: 3rem; + margin-bottom: 1.5rem; +} + +.feature-item h3 { + margin-bottom: 1rem; +} + +.feature-item p { + color: var(--text-muted); +} + +footer { + padding: 4rem 0; + text-align: center; + border-top: 1px solid var(--border); + color: var(--text-muted); +} + +@media (max-width: 968px) { + .hero-grid { + grid-template-columns: 1fr; + text-align: center; + } + .hero h1 { font-size: 3rem; } + .hero-btns { justify-content: center; } + .hero-stats { justify-content: center; } + .chat-container { grid-template-columns: 1fr; } + .phone-mockup { margin: 0 auto; } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..04fcb98 --- /dev/null +++ b/index.html @@ -0,0 +1,154 @@ + + + + + + CuraFlow | WhatsApp-First Clinic Management + + + + + + + + + +
+
+
+
Built for Indian Clinics
+

Chaotic OPDs are a thing of the past.

+

Manage tokens, appointments, and patient records entirely through WhatsApp. Reduce waiting room crowds and automate admin work with AI-powered triage.

+
+ + +
+
+
+ 40% + Wait Time Reduction +
+
+ 90% + WhatsApp Adoption +
+
+
+
+
+ CuraFlow Dashboard Mockup +
+
+
+
+ +
+
+
+

The WhatsApp Experience

+

No apps to download. Patients book and track tokens from the app they already use.

+
+
+
+
+
+ CuraFlow Clinic Assistant +
+
+
Hello! Welcome to Dr. Sharma's Clinic. How can I help you today?
+
+ + + +
+
+
+
+
+

Token Booking

+

Walk-ins scan a QR code at the reception. WhatsApp automatically assigns a token and provides a live ETA.

+
+
+

AI Symptom Triage

+

Ask basic questions to categorize patients before they see the doctor, saving valuable consultation time.

+
+
+

Live Status Updates

+

"You are next in line. Please proceed to Room 4." - No more shouting names in the lobby.

+
+
+
+
+
+ +
+
+
+
+
🔔
+

Status Alerts

+

"Dr. Sharma is now seeing Token #WK-3. You are next. Please reach the clinic in 5 minutes."

+
+
+
🥗
+

Daily Health Tips

+

"Stay hydrated! Since you visited for pediatric care, here are 3 summer tips for kids."

+
+
+
📊
+

Queue Reports

+

"Current Clinic Load: Medium. Average wait time for walk-ins is 25 minutes."

+
+
+
+
+ +
+
+
+
+
📊
+

Smart Queueing

+

Hybrid system for walk-ins and pre-booked appointments. Auto-reorders based on urgency.

+
+
+
🤖
+

AI Report Summaries

+

Upload blood reports or X-ray findings. AI summarizes them for the doctor in seconds.

+
+
+
💳
+

Instant Billing

+

Send Razorpay links directly on WhatsApp. Integrated GST billing for pharmacy & labs.

+
+
+
📜
+

Digital Prescriptions

+

Doctors can generate prescriptions that are instantly sent to the patient's WhatsApp.

+
+
+
+
+ +
+
+

© 2026 CuraFlow Technologies. Empowering healthcare in Bharat.

+
+
+ + + + diff --git a/index.js b/index.js new file mode 100644 index 0000000..30f9902 --- /dev/null +++ b/index.js @@ -0,0 +1,84 @@ +function handleOptionClick() { + const chatBody = document.getElementById('chatBody'); + const userMsg = this.innerText; + + // Add User Message + const userDiv = document.createElement('div'); + userDiv.className = 'msg user'; + userDiv.style.background = '#DCF8C6'; + userDiv.style.alignSelf = 'flex-end'; + userDiv.style.padding = '0.75rem 1rem'; + userDiv.style.borderRadius = '12px'; + userDiv.style.borderTopRightRadius = '0'; + userDiv.style.fontSize = '0.9rem'; + userDiv.style.maxWidth = '85%'; + userDiv.innerText = userMsg; + chatBody.appendChild(userDiv); + + // Hide the options that were just clicked + this.parentElement.style.display = 'none'; + + // Bot Response + setTimeout(() => { + const botDiv = document.createElement('div'); + botDiv.className = 'msg bot'; + botDiv.style.background = 'white'; + botDiv.style.alignSelf = 'flex-start'; + botDiv.style.padding = '0.75rem 1rem'; + botDiv.style.borderRadius = '12px'; + botDiv.style.borderTopLeftRadius = '0'; + botDiv.style.fontSize = '0.9rem'; + botDiv.style.maxWidth = '85%'; + + if (userMsg === 'Book Token' || userMsg === 'Book for Today') { + botDiv.innerText = "Great! Please select your preferred time slot:"; + + const optDiv = document.createElement('div'); + optDiv.className = 'msg-options'; + optDiv.style.display = 'flex'; + optDiv.style.flexDirection = 'column'; + optDiv.style.gap = '0.5rem'; + optDiv.style.paddingTop = '1rem'; + optDiv.innerHTML = ` + + + `; + chatBody.appendChild(botDiv); + chatBody.appendChild(optDiv); + bindOptions(); + } else if (userMsg === '10:00 AM' || userMsg === '10:30 AM') { + botDiv.innerHTML = `✅ Confirmed! Your token is *#ON-3* for the ${userMsg} slot.

💳 *Payment Required*: Please pay ₹500 to activate your token: razorpay.me/curaflow

Also, to help Dr. Sharma prepare, could you briefly describe your symptoms?`; + chatBody.appendChild(botDiv); + } else if (userMsg === 'Check Queue' || userMsg === 'Check Queue Status') { + botDiv.innerText = "Current status: 5 people ahead of you. Estimated wait time: 25 minutes."; + chatBody.appendChild(botDiv); + } else { + botDiv.innerText = "Got it. I've shared these details with Dr. Sharma. Your token is active. See you soon! 🏥"; + chatBody.appendChild(botDiv); + } + + chatBody.scrollTop = chatBody.scrollHeight; + }, 1000); +} + +function bindOptions() { + document.querySelectorAll('.opt').forEach(button => { + button.onclick = null; + button.addEventListener('click', handleOptionClick); + }); +} + +// Initial bind +bindOptions(); + +// Scroll animations placeholder +window.addEventListener('scroll', () => { + const nav = document.querySelector('.glass-nav'); + if (window.scrollY > 50) { + nav.style.height = '70px'; + nav.style.background = 'rgba(255, 255, 255, 0.9)'; + } else { + nav.style.height = '80px'; + nav.style.background = 'rgba(255, 255, 255, 0.7)'; + } +}); diff --git a/k8s/app.yaml b/k8s/app.yaml new file mode 100644 index 0000000..3df1b5b --- /dev/null +++ b/k8s/app.yaml @@ -0,0 +1,52 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: curaflow-app +spec: + replicas: 2 + selector: + matchLabels: + app: curaflow-app + template: + metadata: + labels: + app: curaflow-app + spec: + containers: + - name: curaflow + image: curaflow:latest + ports: + - containerPort: 3000 + env: + - name: DATABASE_URL + value: "postgres://postgres:curaflow_secret@curaflow-db:5432/curaflow" +--- +apiVersion: v1 +kind: Service +metadata: + name: curaflow-app +spec: + selector: + app: curaflow-app + ports: + - port: 80 + targetPort: 3000 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: curaflow-ingress + annotations: + kubernetes.io/ingress.class: apisix # Using APISIX as per user's previous context +spec: + rules: + - host: curaflow.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: curaflow-app + port: + number: 80 diff --git a/k8s/database.yaml b/k8s/database.yaml new file mode 100644 index 0000000..7cd56fe --- /dev/null +++ b/k8s/database.yaml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: curaflow-db +spec: + selector: + matchLabels: + app: curaflow-db + template: + metadata: + labels: + app: curaflow-db + spec: + containers: + - name: postgres + image: postgres:15-alpine + env: + - name: POSTGRES_PASSWORD + value: "curaflow_secret" + - name: POSTGRES_DB + value: "curaflow" + ports: + - containerPort: 5432 +--- +apiVersion: v1 +kind: Service +metadata: + name: curaflow-db +spec: + selector: + app: curaflow-db + ports: + - port: 5432 diff --git a/lab.css b/lab.css new file mode 100644 index 0000000..8504540 --- /dev/null +++ b/lab.css @@ -0,0 +1,19 @@ +.ai-report-card { + background: linear-gradient(135deg, #6366F1, #4F46E5); + color: white; +} + +.ai-report-card h3 { + margin-bottom: 1rem; +} + +.btn-block { + width: 100%; + margin-top: 1rem; + background: white; + color: #4F46E5; +} + +.btn-block:hover { + background: #F1F5F9; +} diff --git a/lab.html b/lab.html new file mode 100644 index 0000000..2abc16a --- /dev/null +++ b/lab.html @@ -0,0 +1,109 @@ + + + + + + CuraFlow Lab | Diagnostics & Reports + + + + + + + + +
+
+
+

Lab & Diagnostics

+

Track test samples and deliver AI-explained reports to patients.

+
+
+ +
+
+ +
+
+ Pending Tests +

14 Samples

+ 4 Urgent +
+
+ Avg. TAT +

4.2 Hours

+ ↑ 0.5h improvement +
+
+ WhatsApp Delivery +

98%

+ Auto-sent successfully +
+
+ +
+
+
+

Live Test Queue

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Lab IDPatient NameTest NameUrgencyStatusActions
#LAB-501Suresh KumarCBC + Blood SugarUrgentProcessing
#LAB-502Anjali SinghLipid ProfileRoutineCollecting
+
+ +
+
+

✨ AI Report Explainer

+
+ CBC Analysis (#LAB-498) +

Hemoglobin (9.2) is low. Suggesting iron-rich diet and further Vitamin B12 check.

+ +
+
+
+
+
+ + diff --git a/login.css b/login.css new file mode 100644 index 0000000..5999397 --- /dev/null +++ b/login.css @@ -0,0 +1,98 @@ +.auth-body { + background: linear-gradient(135deg, #F8FAFC 0%, #E2E8F0 100%); + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + padding: 2rem; +} + +.auth-container { + width: 100%; + max-width: 500px; +} + +.auth-card { + background: white; + padding: 3rem; + border-radius: 32px; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.1); + text-align: center; +} + +.auth-logo { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 2rem; +} + +.auth-logo h1 { + font-family: 'Outfit', sans-serif; + font-size: 1.5rem; + color: var(--primary); + margin-top: 0.5rem; +} + +.auth-card h2 { + font-family: 'Outfit', sans-serif; + font-size: 1.75rem; + margin-bottom: 0.5rem; +} + +.auth-card p { + color: var(--text-muted); + margin-bottom: 2.5rem; +} + +.input-group { + text-align: left; + margin-bottom: 1.5rem; +} + +.input-group label { + display: block; + font-size: 0.875rem; + font-weight: 600; + margin-bottom: 0.5rem; +} + +.input-group input { + width: 100%; + padding: 0.875rem; + border-radius: 12px; + border: 1px solid var(--border); + background: #F8FAFC; + transition: all 0.3s; +} + +.input-group input:focus { + outline: none; + border-color: var(--primary); + background: white; + box-shadow: 0 0 0 4px rgba(15, 23, 42, 0.05); +} + +.input-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; +} + +.auth-btn { + width: 100%; + padding: 1rem; + font-size: 1rem; + margin-top: 1rem; +} + +.auth-footer { + margin-top: 2rem; + font-size: 0.9rem; +} + +.auth-footer a { + color: var(--primary); + font-weight: 600; + text-decoration: none; +} diff --git a/login.html b/login.html new file mode 100644 index 0000000..cbdcad7 --- /dev/null +++ b/login.html @@ -0,0 +1,44 @@ + + + + + + Login | CuraFlow HMS + + + + + +
+
+ +

Welcome Back

+

Login to manage your clinic OPD and accounts.

+ +
+
+ + +
+
+ + +
+ +
+ + +
+
+ + + + diff --git a/pharmacy.css b/pharmacy.css new file mode 100644 index 0000000..66dcf35 --- /dev/null +++ b/pharmacy.css @@ -0,0 +1,35 @@ +.inventory-card h3 { + margin-bottom: 1.5rem; + font-size: 1.1rem; +} + +.inventory-item { + margin-bottom: 1rem; +} + +.inventory-item span { + display: block; + font-size: 0.875rem; + font-weight: 600; +} + +.inventory-item small { + color: var(--text-muted); +} + +.inventory-item .bar { + height: 8px; + background: #F1F5F9; + border-radius: 99px; + margin-top: 0.25rem; + overflow: hidden; +} + +.inventory-item .fill { + height: 100%; + background: #10B981; +} + +.text-urgent { + color: #EF4444; +} diff --git a/pharmacy.html b/pharmacy.html new file mode 100644 index 0000000..12f347f --- /dev/null +++ b/pharmacy.html @@ -0,0 +1,120 @@ + + + + + + CuraFlow Pharmacy | Inventory & Billing + + + + + + + + +
+
+
+

Pharmacy Management

+

Manage prescriptions and inventory with real-time stock alerts.

+
+
+ Dispensing Counter: 2 + +
+
+ +
+
+ Pending Rx +

8 Orders

+ ↑ 2 from Dr. Sharma +
+
+ Low Stock +

4 Medicines

+ Alert: Cough Syrup < 5 units +
+
+ Revenue Today +

₹12,450

+ ↑ 15% vs Yesterday +
+
+ +
+
+
+

Pending Prescriptions

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Order IDPatient NameMedicinesDoctorStatusActions
#ORD-901Rahul VermaParacetamol, Cough SyrupDr. SharmaPrescribed
#ORD-902Anjali SinghAmoxicillinDr. SharmaProcessing
+
+ +
+
+

📦 Fast Moving Stock

+
+ Paracetamol 500mg + 120 units +
+
+
+ Amoxicillin + 45 units +
+
+
+ Cough Syrup + 4 units +
+
+
+
+
+
+ + diff --git a/register.html b/register.html new file mode 100644 index 0000000..2301be5 --- /dev/null +++ b/register.html @@ -0,0 +1,55 @@ + + + + + + Clinic Registration | CuraFlow HMS + + + + + +
+
+ +

Register Your Clinic

+

Join Bharat's first WhatsApp-native HMS.

+ +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+ +
+ + +
+
+ + + + diff --git a/settings.css b/settings.css new file mode 100644 index 0000000..8194f70 --- /dev/null +++ b/settings.css @@ -0,0 +1,30 @@ +.settings-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1.5rem; +} + +.settings-grid .section-card h2 { + font-size: 1.1rem; + margin-bottom: 1.5rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border); +} + +.settings-grid .input-group { + margin-bottom: 1.25rem; +} + +.settings-grid label { + font-size: 0.85rem; + color: var(--text-muted); + font-weight: 500; +} + +.settings-grid input, .settings-grid select { + width: 100%; + padding: 0.75rem; + border-radius: 10px; + border: 1px solid var(--border); + margin-top: 0.4rem; +} diff --git a/settings.html b/settings.html new file mode 100644 index 0000000..257b137 --- /dev/null +++ b/settings.html @@ -0,0 +1,94 @@ + + + + + + CuraFlow Settings | Hospital Configuration + + + + + + + + +
+
+
+

Hospital Settings

+

Configure pricing, slot limits, and clinic profile.

+
+
+ +
+
+ +
+
+

Pricing & Fees

+
+ + +
+
+ + +
+
+ +
+

Slot Management

+
+ + +
+
+ + +
+
+ + +
+
+ +
+

Pharmacy Config

+

Manage medicine pricing and GST rates.

+
+ + +
+
+ + +
+
+
+
+ + + + diff --git a/staff.css b/staff.css new file mode 100644 index 0000000..9085b7b --- /dev/null +++ b/staff.css @@ -0,0 +1,30 @@ +.payroll-card { + background: linear-gradient(135deg, #10B981, #059669); + color: white; +} + +.payroll-card h3 { + margin-bottom: 1rem; +} + +.payroll-card p { + font-size: 0.85rem; + opacity: 0.9; + margin-bottom: 1.5rem; +} + +.incentive-item { + background: rgba(255, 255, 255, 0.1); + padding: 1rem; + border-radius: 12px; +} + +.incentive-item span { + font-size: 0.75rem; + opacity: 0.8; +} + +.incentive-item h4 { + font-size: 1.5rem; + margin: 0.25rem 0; +} diff --git a/staff.html b/staff.html new file mode 100644 index 0000000..edb1bca --- /dev/null +++ b/staff.html @@ -0,0 +1,118 @@ + + + + + + CuraFlow Staff | Personnel & Payroll + + + + + + + + +
+
+
+

Staff Management

+

Track attendance, roles, and monthly payroll incentives.

+
+
+ +
+
+ +
+
+ Total Staff +

18 Members

+ 4 Departments +
+
+ On Duty Today +

15 Present

+ 3 on Leave +
+
+ Monthly Payroll +

₹4.85 Lakhs

+ Incl. Incentives +
+
+ +
+
+
+

Staff Directory

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDStaff NameRoleStatusMonthly PayActions
#S001Dr. SharmaDoctorPresent₹1,24,500
#S002Anjali SinghPharmacistPresent₹36,200
#S003Vikram GoelLab TechOn Leave₹28,800
+
+ +
+
+

💡 Incentive Logic

+

Current rule: 5% of Pharmacy sales shared with staff pool.

+
+ Staff Pool Today +

₹2,450

+ Calculated on ₹49k revenue +
+
+
+
+
+ +