commit 0a8dbfe964eae4d6d15c48a8935bc41168b84a67 Author: Deep Koluguri Date: Tue May 12 23:13:32 2026 -0400 Initial commit: CuraFlow WhatsApp-First HMS with Multi-Vertical support 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 +
+
+
+
+
+ +