diff --git a/curaflow/.gitea/workflows/build.yaml b/curaflow/.gitea/workflows/build.yaml
index 89ce024..f112a6d 100644
--- a/curaflow/.gitea/workflows/build.yaml
+++ b/curaflow/.gitea/workflows/build.yaml
@@ -1,4 +1,4 @@
-name: Build CuraFlow HMS
+name: Build Curio HMS
on: [push]
jobs:
@@ -16,8 +16,8 @@ jobs:
docker run --rm \
--volumes-from "$JOB_CONTAINER" \
gcr.io/kaniko-project/executor:latest \
- --context=dir://"$GITHUB_WORKSPACE/curaflow" \
- --dockerfile="$GITHUB_WORKSPACE/curaflow/Dockerfile" \
- --destination=192.168.8.250:5000/agentic-os/curaflow:latest \
+ --context=dir://"$GITHUB_WORKSPACE" \
+ --dockerfile="$GITHUB_WORKSPACE/Dockerfile" \
+ --destination=192.168.8.250:5000/Curio:latest \
--insecure \
--skip-tls-verify
diff --git a/curaflow/backend/botLogic.js b/curaflow/backend/botLogic.js
index 09ea471..09d1ffd 100644
--- a/curaflow/backend/botLogic.js
+++ b/curaflow/backend/botLogic.js
@@ -19,7 +19,7 @@ class BotLogic {
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 (తెలుగు)",
+ reply: "Welcome to Curio! Please select your language / भाषा चुनें / భాషను ఎంచుకోండి:\n\n1. English\n2. Hindi (हिंदी)\n3. Telugu (తెలుగు)",
buttons: ["English", "Hindi", "Telugu"]
};
}
@@ -56,7 +56,7 @@ class BotLogic {
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`,
+ ` \n\n💳 *Payment Required*: Please pay ₹${result.fee} to activate your token: http://razorpay.me/Curio_mock`,
};
} else {
return {
diff --git a/curaflow/backend/configManager.js b/curaflow/backend/configManager.js
index 4103afa..64dec09 100644
--- a/curaflow/backend/configManager.js
+++ b/curaflow/backend/configManager.js
@@ -1,30 +1,46 @@
/**
- * ConfigManager: Central configuration for hospital-wide pricing and slots.
+ * ConfigManager: Central configuration for multi-tenant hospital settings and white-labeling.
*/
class ConfigManager {
constructor() {
- this.config = {
- consultationFee: 500,
- slotDuration: 30, // minutes
- onlineSlotLimit: 3,
- walkinSlotLimit: 5,
- hospitalName: "Dr. Sharma's Clinic",
- currency: "₹"
+ this.tenants = {
+ 'default': {
+ name: "Curio Clinic",
+ logo: "C",
+ primaryColor: "#0F172A",
+ secondaryColor: "#38BDF8",
+ consultationFee: 500,
+ currency: "₹"
+ },
+ 'sharma-clinic': {
+ name: "Dr. Sharma's Cardiology",
+ logo: "S",
+ primaryColor: "#1E3A8A", // Deep Blue
+ secondaryColor: "#60A5FA",
+ consultationFee: 800,
+ currency: "₹"
+ },
+ 'apollo-hospitals': {
+ name: "Apollo Multispecialty",
+ logo: "A",
+ primaryColor: "#065F46", // Dark Green
+ secondaryColor: "#34D399",
+ consultationFee: 1200,
+ currency: "₹"
+ }
};
}
- get(key) {
- return this.config[key];
+ getTenantConfig(tenantId) {
+ return this.tenants[tenantId] || this.tenants['default'];
}
- set(key, value) {
- this.config[key] = value;
- console.log(`Config updated: ${key} = ${value}`);
- }
-
- getAll() {
- return this.config;
+ updateTenantConfig(tenantId, newConfig) {
+ if (!this.tenants[tenantId]) {
+ this.tenants[tenantId] = { ...this.tenants['default'] };
+ }
+ this.tenants[tenantId] = { ...this.tenants[tenantId], ...newConfig };
}
}
diff --git a/curaflow/backend/i18n.js b/curaflow/backend/i18n.js
index e0749aa..6b53621 100644
--- a/curaflow/backend/i18n.js
+++ b/curaflow/backend/i18n.js
@@ -1,5 +1,5 @@
/**
- * i18n: Translation manager for CuraFlow WhatsApp Bot.
+ * i18n: Translation manager for Curio WhatsApp Bot.
* Supports English (en), Hindi (hi), and Telugu (te).
*/
diff --git a/curaflow/backend/queueManager.js b/curaflow/backend/queueManager.js
index 0c65c9c..a66abcc 100644
--- a/curaflow/backend/queueManager.js
+++ b/curaflow/backend/queueManager.js
@@ -1,5 +1,5 @@
/**
- * QueueManager: Handles the hybrid slot logic for CuraFlow
+ * QueueManager: Handles the hybrid slot logic for Curio
* - 30-min slots
* - Max 3 online bookings (Hard Cap)
* - 5 Walk-in slots
@@ -9,65 +9,68 @@ const configManager = require('./configManager');
class QueueManager {
constructor() {
- this.slots = {}; // Key: "YYYY-MM-DD:HH:mm"
+ this.slots = {}; // Key: "tenantId:YYYY-MM-DD:HH:mm"
}
- getSlotKey(date, time) {
- // Round time to nearest slot duration
- const duration = configManager.get('slotDuration');
+ getSlotKey(tenantId, date, time) {
+ const config = configManager.getTenantConfig(tenantId);
+ const duration = config.slotDuration || 30;
const [hours, minutes] = time.split(':');
const roundedMins = parseInt(minutes) < duration ? '00' : duration;
- return `${date}:${hours}:${roundedMins}`;
+ return `${tenantId}:${date}:${hours}:${roundedMins}`;
}
- getSlotStatus(date, time) {
- const key = this.getSlotKey(date, time);
+ getSlotStatus(tenantId, date, time) {
+ const key = this.getSlotKey(tenantId, date, time);
if (!this.slots[key]) {
+ const config = configManager.getTenantConfig(tenantId);
this.slots[key] = {
online: 0,
walkin: 0,
- maxOnline: configManager.get('onlineSlotLimit'),
- maxWalkin: configManager.get('walkinSlotLimit')
+ maxOnline: config.onlineSlotLimit || 3,
+ maxWalkin: config.walkinSlotLimit || 5
};
}
return this.slots[key];
}
- bookOnline(date, time) {
- const status = this.getSlotStatus(date, time);
+ bookOnline(date, time, tenantId = 'default') {
+ const status = this.getSlotStatus(tenantId, date, time);
+ const config = configManager.getTenantConfig(tenantId);
if (status.online < status.maxOnline) {
status.online++;
return {
success: true,
token: `ON-${status.online}`,
- slot: this.getSlotKey(date, time),
+ slot: this.getSlotKey(tenantId, date, time),
status: 'PENDING_PAYMENT',
- fee: configManager.get('consultationFee')
+ fee: config.consultationFee
};
}
return { success: false, message: "Slot full for online booking. Try next slot." };
}
- bookWalkin(date, time) {
- const status = this.getSlotStatus(date, time);
+ bookWalkin(date, time, tenantId = 'default') {
+ const status = this.getSlotStatus(tenantId, date, time);
+ const config = configManager.getTenantConfig(tenantId);
if (status.walkin < status.maxWalkin) {
status.walkin++;
return {
success: true,
token: `WK-${status.walkin}`,
status: 'PENDING_PAYMENT',
- fee: configManager.get('consultationFee')
+ fee: config.consultationFee
};
}
return { success: false, message: "Clinic is at full capacity for this slot." };
}
- getDailyUpdates(patientId) {
- // Mock data for daily updates
+ getDailyUpdates(patientId, tenantId = 'default') {
+ const config = configManager.getTenantConfig(tenantId);
return [
- "Good morning! Your appointment with Dr. Sharma is at 10:30 AM today.",
+ `Good morning! Your appointment with ${config.name} is scheduled for today.`,
"Queue Update: The clinic is running 10 mins behind schedule. Please plan accordingly.",
- "Reminder: Don't forget to bring your previous blood reports."
+ "Reminder: Don't forget to bring your previous reports."
];
}
}
diff --git a/curaflow/backend/server.js b/curaflow/backend/server.js
index 7f131f5..9efe3f2 100644
--- a/curaflow/backend/server.js
+++ b/curaflow/backend/server.js
@@ -1,6 +1,7 @@
const express = require('express');
const path = require('path');
const botLogic = require('./botLogic');
+const configManager = require('./configManager');
const app = express();
const port = 3000;
@@ -12,6 +13,12 @@ app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '../login.html'));
});
+// Multi-tenant Config API
+app.get('/api/config/:tenantId', (req, res) => {
+ const config = configManager.getTenantConfig(req.params.tenantId);
+ res.json(config);
+});
+
// Mock WhatsApp Webhook
app.post('/whatsapp/webhook', async (req, res) => {
const { from, body } = req.body;
@@ -28,5 +35,5 @@ app.post('/whatsapp/webhook', async (req, res) => {
});
app.listen(port, '0.0.0.0', () => {
- console.log(`CuraFlow WhatsApp Mock Server running at http://0.0.0.0:${port}`);
+ console.log(`Curio WhatsApp Mock Server running at http://0.0.0.0:${port}`);
});
diff --git a/curaflow/backend_tests.js b/curaflow/backend_tests.js
new file mode 100644
index 0000000..7027732
--- /dev/null
+++ b/curaflow/backend_tests.js
@@ -0,0 +1,44 @@
+const queueManager = require('./backend/queueManager');
+const botLogic = require('./backend/botLogic');
+const configManager = require('./backend/configManager');
+
+console.log("=== STARTING BACKEND UNIT TESTS ===");
+
+// 1. Test QueueManager - Online Booking Limits
+console.log("\nTesting QueueManager: Online Booking Limits");
+const date = "2026-05-13";
+const time = "10:00";
+
+for (let i = 0; i < 4; i++) {
+ const res = queueManager.bookOnline(date, time);
+ console.log(`Booking ${i+1}:`, res.success ? `Success - ${res.token}` : `Failed - ${res.message}`);
+}
+
+// 2. Test QueueManager - Walkin Booking
+console.log("\nTesting QueueManager: Walk-in Booking");
+const walkRes = queueManager.bookWalkin(date, time);
+console.log("Walkin result:", walkRes.token);
+
+// 3. Test Bot Logic - WhatsApp Simulation
+console.log("\nTesting BotLogic: WhatsApp Interactions");
+async function testBot() {
+ const msg1 = await botLogic.handleMessage("9876543210", "Hi");
+ console.log("User: Hi -> Bot:", msg1.reply);
+
+ const msg2 = await botLogic.handleMessage("9876543210", "Book Token");
+ console.log("User: Book Token -> Bot:", msg2.reply);
+ console.log("Buttons:", msg2.buttons);
+}
+
+testBot();
+
+// 4. Test ConfigManager - Multi-tenancy
+console.log("\nTesting ConfigManager: Multi-tenancy");
+const sharmaConfig = configManager.getTenantConfig('sharma-clinic');
+console.log("Sharma Clinic Name:", sharmaConfig.name);
+
+// 5. Test Multi-tenant Booking
+console.log("\nTesting Multi-tenant Booking (Sharma Clinic)");
+const sharmaRes = queueManager.bookOnline(date, time, 'sharma-clinic');
+console.log("Sharma Booking Result:", sharmaRes.success ? `Success - ${sharmaRes.token} (Fee: ${sharmaRes.fee})` : "Failed");
+
diff --git a/curaflow/billing.html b/curaflow/billing.html
index 5f0ae36..de2486d 100644
--- a/curaflow/billing.html
+++ b/curaflow/billing.html
@@ -3,7 +3,7 @@
- CuraFlow Billing | Hospital Revenue & Accounts
+ Curio Billing | Hospital Revenue & Accounts
@@ -13,7 +13,7 @@
diff --git a/curaflow/dashboard.html b/curaflow/dashboard.html
index a3fe7f8..9dc4268 100644
--- a/curaflow/dashboard.html
+++ b/curaflow/dashboard.html
@@ -3,21 +3,22 @@
- CuraFlow Dashboard | Dr. Sharma's Clinic
+ Dashboard | Curio
+
@@ -44,101 +46,142 @@
-
-
- In Queue
-
12 Patients
- ↑ 4 since last hour
-
-
-
Current Slot (10:00-10:30)
-
-
-
Online: 3/3 (CLOSED)
-
-
-
-
Walk-in: 4/5
-
+
+
+
+ In Queue
+
12 Patients
+ ↑ 4 since last hour
+
+
+
Current Slot (10:00-10:30)
+
+
+
Online: 3/3 (CLOSED)
+
+
+
-
-
- Avg. Wait Time
-
18 Mins
- Target: 15 Mins
-
-
+
+ Avg. Wait Time
+
18 Mins
+ Target: 15 Mins
+
+
-
-
-
+
+
+
+
+
+
+
+
Basic
+
₹1,499/mo
+
+ Up to 500 tokens/mo
+ Basic WhatsApp Queue
+ Digital Prescriptions
+ Single Doctor Support
+
+
Choose Basic
+
+
+
Pro
+
₹3,999/mo
+
+ Unlimited Tokens
+ AI Triage & Summaries
+ Pharmacy & Lab Integration
+ Multi-Doctor Support
+
+
Get Started Pro
+
+
+
Enterprise
+
Custom
+
+ Multi-Clinic Chain
+ Custom AI Training
+ White-label WhatsApp Bot
+ Dedicated Account Manager
+
+
Contact Sales
@@ -145,7 +190,7 @@
diff --git a/curaflow/index.js b/curaflow/index.js
index 30f9902..d5d9412 100644
--- a/curaflow/index.js
+++ b/curaflow/index.js
@@ -47,7 +47,7 @@ function handleOptionClick() {
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?`;
+ botDiv.innerHTML = `✅ Confirmed! Your token is *#ON-3* for the ${userMsg} slot. 💳 *Payment Required*: Please pay ₹500 to activate your token: razorpay.me/Curio 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.";
diff --git a/curaflow/k8s/apisix-route.yaml b/curaflow/k8s/apisix-route.yaml
new file mode 100644
index 0000000..6ce80fe
--- /dev/null
+++ b/curaflow/k8s/apisix-route.yaml
@@ -0,0 +1,17 @@
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: curio-route
+ namespace: curaflow
+spec:
+ http:
+ - name: Curio-rule
+ match:
+ hosts:
+ - curaflow.applaude.net
+ - curio.applaude.net
+ paths:
+ - /*
+ backends:
+ - serviceName: Curio-app
+ servicePort: 80
diff --git a/curaflow/k8s/app.yaml b/curaflow/k8s/app.yaml
index 2ce2ef8..d629c1b 100644
--- a/curaflow/k8s/app.yaml
+++ b/curaflow/k8s/app.yaml
@@ -1,55 +1,35 @@
apiVersion: apps/v1
kind: Deployment
metadata:
- name: curaflow-app
+ name: curio-app
+ namespace: curaflow
spec:
replicas: 2
selector:
matchLabels:
- app: curaflow-app
+ app: Curio-app
template:
metadata:
labels:
- app: curaflow-app
+ app: Curio-app
spec:
containers:
- - name: curaflow
- image: 192.168.8.250:5000/agentic-os/curaflow:latest
+ - name: Curio
+ image: 192.168.8.250:5000/Curio:latest
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
- value: "postgres://postgres:curaflow_secret@curaflow-db:5432/curaflow"
+ value: "postgres://postgres:Curio_secret@Curio-db:5432/Curio"
---
apiVersion: v1
kind: Service
metadata:
- name: curaflow-app
+ name: curio-app
+ namespace: curaflow
spec:
selector:
- app: curaflow-app
+ app: Curio-app
ports:
- port: 80
targetPort: 3000
----
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- name: curaflow-ingress
- namespace: curaflow
- annotations:
- cert-manager.io/cluster-issuer: letsencrypt-prod
- k8s.apisix.apache.org/enable-websocket: "true"
-spec:
- ingressClassName: apisix
- rules:
- - host: curaflow.applaude.net
- http:
- paths:
- - path: /
- pathType: Prefix
- backend:
- service:
- name: curaflow-app
- port:
- number: 80
diff --git a/curaflow/k8s/database.yaml b/curaflow/k8s/database.yaml
index 7cd56fe..942c79b 100644
--- a/curaflow/k8s/database.yaml
+++ b/curaflow/k8s/database.yaml
@@ -1,33 +1,33 @@
apiVersion: apps/v1
kind: Deployment
metadata:
- name: curaflow-db
+ name: Curio-db
spec:
selector:
matchLabels:
- app: curaflow-db
+ app: Curio-db
template:
metadata:
labels:
- app: curaflow-db
+ app: Curio-db
spec:
containers:
- name: postgres
image: postgres:15-alpine
env:
- name: POSTGRES_PASSWORD
- value: "curaflow_secret"
+ value: "Curio_secret"
- name: POSTGRES_DB
- value: "curaflow"
+ value: "Curio"
ports:
- containerPort: 5432
---
apiVersion: v1
kind: Service
metadata:
- name: curaflow-db
+ name: Curio-db
spec:
selector:
- app: curaflow-db
+ app: Curio-db
ports:
- port: 5432
diff --git a/curaflow/lab.html b/curaflow/lab.html
index 2abc16a..1fff1d9 100644
--- a/curaflow/lab.html
+++ b/curaflow/lab.html
@@ -3,7 +3,7 @@
- CuraFlow Lab | Diagnostics & Reports
+ Curio Lab | Diagnostics & Reports
@@ -13,7 +13,7 @@
diff --git a/curaflow/login.html b/curaflow/login.html
index cbdcad7..17ab344 100644
--- a/curaflow/login.html
+++ b/curaflow/login.html
@@ -3,17 +3,18 @@
- Login | CuraFlow HMS
+ Login | Curio HMS
+
C
-
CuraFlow
+ Curio
Welcome Back
Login to manage your clinic OPD and accounts.
@@ -30,14 +31,21 @@
Login to Dashboard
-
+
diff --git a/curaflow/pharmacy.html b/curaflow/pharmacy.html
index 12f347f..395b373 100644
--- a/curaflow/pharmacy.html
+++ b/curaflow/pharmacy.html
@@ -3,7 +3,7 @@
- CuraFlow Pharmacy | Inventory & Billing
+ Curio Pharmacy | Inventory & Billing
@@ -13,7 +13,7 @@
diff --git a/curaflow/register.html b/curaflow/register.html
index 2301be5..c431315 100644
--- a/curaflow/register.html
+++ b/curaflow/register.html
@@ -3,7 +3,7 @@
- Clinic Registration | CuraFlow HMS
+ Clinic Registration | Curio HMS
@@ -13,7 +13,7 @@
C
-
CuraFlow
+ Curio
Register Your Clinic
Join Bharat's first WhatsApp-native HMS.
diff --git a/curaflow/settings.html b/curaflow/settings.html
index 257b137..367a462 100644
--- a/curaflow/settings.html
+++ b/curaflow/settings.html
@@ -3,7 +3,7 @@
-
CuraFlow Settings | Hospital Configuration
+
Curio Settings | Hospital Configuration
@@ -13,7 +13,7 @@
diff --git a/curaflow/staff.html b/curaflow/staff.html
index edb1bca..6a8f2f1 100644
--- a/curaflow/staff.html
+++ b/curaflow/staff.html
@@ -3,7 +3,7 @@
- CuraFlow Staff | Personnel & Payroll
+ Curio Staff | Personnel & Payroll
@@ -13,7 +13,7 @@
diff --git a/curaflow/tenantLoader.js b/curaflow/tenantLoader.js
new file mode 100644
index 0000000..fb1e31f
--- /dev/null
+++ b/curaflow/tenantLoader.js
@@ -0,0 +1,47 @@
+/**
+ * tenantLoader.js: Dynamically applies branding and multi-tenant settings.
+ */
+
+async function loadTenantBranding() {
+ // Determine tenant from URL (e.g., ?tenant=sharma-clinic) or localStorage
+ const urlParams = new URLSearchParams(window.location.search);
+ let tenantId = urlParams.get('tenant') || localStorage.getItem('Curio_tenant') || 'default';
+
+ // Save to localStorage for persistence across pages
+ localStorage.setItem('Curio_tenant', tenantId);
+
+ try {
+ const response = await fetch(`/api/config/${tenantId}`);
+ const config = await response.json();
+
+ applyBranding(config);
+ } catch (error) {
+ console.error("Failed to load tenant branding:", error);
+ }
+}
+
+function applyBranding(config) {
+ // 1. Apply CSS Variables for dynamic coloring
+ document.documentElement.style.setProperty('--primary', config.primaryColor);
+ document.documentElement.style.setProperty('--secondary', config.secondaryColor);
+
+ // 2. Update UI Elements (Logo, Name)
+ const logoIcons = document.querySelectorAll('.logo-icon');
+ const logoTexts = document.querySelectorAll('.logo-text, .header-title h1, .auth-logo h1');
+ const hospitalNames = document.querySelectorAll('.hospital-name, .logo-text');
+
+ logoIcons.forEach(icon => icon.innerText = config.logo);
+
+ // Update Document Title
+ document.title = `${config.name} | Curio`;
+
+ // Update instances of hospital name in text
+ document.querySelectorAll('[data-tenant-name]').forEach(el => {
+ el.innerText = config.name;
+ });
+
+ console.log(`Branding applied for: ${config.name}`);
+}
+
+// Auto-load on script include
+document.addEventListener('DOMContentLoaded', loadTenantBranding);