JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 31
  • Score
    100M100P100Q88731F
  • License UNLICENSED

Medos SDK for managing appointments, meetings, and calendars in apps

Package Exports

  • medos-sdk
  • medos-sdk/core
  • medos-sdk/react
  • medos-sdk/vanilla
  • medos-sdk/widget

Readme

Medos SDK

npm version License TypeScript

A JavaScript/TypeScript SDK for integrating healthcare appointment booking into your applications. Built for clinics, hospitals, and healthcare platforms.

✨ Features

  • 🎨 Powerful Theming System - Customize appearance with built-in themes or create your own
  • ⚛️ React & Vanilla JS - Works with React or pure JavaScript
  • 🔒 Secure Authentication - API key and session token support
  • 📱 Responsive Design - Mobile-first, works on all screen sizes
  • 🎯 TypeScript-First - Full type safety and IntelliSense support
  • Accessible - WCAG compliant components

Installation

npm install medos-sdk

Table of Contents

Quick Start

React

import { MedosThemeProvider, AppointmentCalender } from "medos-sdk/react";

function App() {
  return (
    <MedosThemeProvider theme="modern">
      <AppointmentCalender onError={(err) => console.error(err)} />
    </MedosThemeProvider>
  );
}

Vanilla JavaScript

import { initAppointmentCalendar } from "medos-sdk/vanilla";

initAppointmentCalendar({
  containerId: "appointment-widget",
  apiKey: "your-api-key",
  theme: "modern", // or 'default'
});

HTML/CSS/JS

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="path/to/widget.css" />
  </head>
  <body>
    <div id="appointment-calendar"></div>
    <script src="path/to/widget.js"></script>
    <script>
      window.MedosAppointmentCalendar.init({
        containerId: "appointment-calendar",
        apiKey: "your-api-key",
      });
    </script>
  </body>
</html>

Getting Started

Authentication

The SDK supports two authentication methods:

Server-Side (API Key)

Use this method for backend services and server-side rendering.

import { MedosClient } from "medos-sdk";

await MedosClient.init({
  apiKey: "your-api-key",
});

Client-Side (Session Token)

Use this method for frontend applications. Obtain the session token from your backend.

import { MedosClient } from "medos-sdk";

await MedosClient.initWithSession({
  sessionToken: "user-session-token",
});

Usage

Fetch Clinic Addresses and Doctors

Retrieve all available clinic locations with their associated doctors.

const result = await MedosClient.fetchAllAddressesAndDoctors();

Response:

{
  totalAddresses: 5,
  totalDoctors: 12,
  workspaceId: "workspace-123",
  addresses: [
    {
      id: "addr-1",
      completeAddress: "123 Medical Center, New York, NY 10001",
      addressLine1: "123 Medical Center",
      city: "New York",
      state: "NY",
      country: "USA",
      zipcode: "10001",
      doctors: [
        {
          id: "doc-1",
          name: "Dr. John Smith",
          email: "john.smith@example.com",
          specialty: "Cardiology"
        }
      ]
    }
  ]
}

Fetch Available Slots

Get available appointment time slots for a specific doctor.

const slots = await MedosClient.fetchAppointments(
  "workspace-123", // workspaceId
  "addr-1", // addressId
  "doc-1", // doctorId
  "2025-11-16" // appointmentDate (YYYY-MM-DD)
);

Response:

[
  {
    start: "2025-11-16T10:00:00",
    end: "2025-11-16T10:30:00",
    id: "slot-1",
  },
  {
    start: "2025-11-16T11:00:00",
    end: "2025-11-16T11:30:00",
    id: "slot-2",
  },
];

Book an Appointment

Create a new appointment booking.

import { AppointmentService } from "medos-sdk";

const appointment = await AppointmentService.createAppointment({
  workspaceAddressId: "addr-1",
  doctorId: "doc-1",
  mode: "OFFLINE",
  appointmentDate: "2025-11-16",
  fromDateTimeTs: "10:00",
  toDateTimeTs: "10:30",
  consultationCharge: "100",
  type: "CONSULTATION",
  source: "SDK_POWERED_WEBSITE",
  patientPayload: {
    firstName: "Jane",
    lastName: "Doe",
    email: "jane.doe@example.com",
    countryCode: "+1",
    phoneNumber: "5551234567",
    age: 30,
    gender: "FEMALE",
  },
  patientAddress: {
    addressLine1: "456 Patient Street",
    city: "New York",
    state: "NY",
    country: "USA",
    zipcode: "10001",
    landmark: "Near Central Park",
  },
});

Optional Fields:

Field Default Value Description
mode "OFFLINE" Consultation mode: "OFFLINE" or "ONLINE"
consultationCharge "0" Consultation fee as string
type "CONSULTATION" Appointment type
source "SDK_POWERED_WEBSITE" Source identifier

Phone Verification

Implement OTP-based phone number verification for patients.

Send OTP

await MedosClient.sendPhoneVerificationOtp({
  countryCode: "+1",
  phoneNumber: "5551234567",
});

Verify OTP

const result = await MedosClient.verifyPhoneVerificationOtp({
  countryCode: "+1",
  phoneNumber: "5551234567",
  otp: "123456",
});

TypeScript

The SDK is written in TypeScript and provides comprehensive type definitions.

import {
  MedosClient,
  AppointmentService,
  BookAppointmentPayload,
  AddressesResponse,
  PatientPayload,
  PatientAddressPayload,
} from "medos-sdk";

const payload: BookAppointmentPayload = {
  workspaceAddressId: "addr-1",
  doctorId: "doc-1",
  appointmentDate: "2025-11-16",
  fromDateTimeTs: "10:00",
  toDateTimeTs: "10:30",
  patientPayload: {
    firstName: "John",
    lastName: "Doe",
    countryCode: "+1",
    phoneNumber: "5551234567",
  },
  patientAddress: {
    addressLine1: "123 Main Street",
    city: "New York",
    state: "NY",
    country: "USA",
    zipcode: "10001",
  },
};

const appointment = await AppointmentService.createAppointment(payload);

Type Definitions

BookAppointmentPayload

{
  workspaceAddressId: string | number;
  doctorId: string | number;
  appointmentDate: string;           // Format: "YYYY-MM-DD"
  fromDateTimeTs: string;            // Format: "HH:MM"
  toDateTimeTs: string;              // Format: "HH:MM"
  mode?: "OFFLINE" | "ONLINE";       // Default: "OFFLINE"
  consultationCharge?: string;       // Default: "0"
  type?: "CONSULTATION" | string;    // Default: "CONSULTATION"
  source?: string;                   // Default: "SDK_POWERED_WEBSITE"
  patientPayload: PatientPayload;
  patientAddress: PatientAddressPayload;  // Required
}

PatientPayload

{
  firstName: string;
  lastName: string;
  countryCode: string;
  phoneNumber: string;
  email?: string;
  age?: number;
  gender?: "MALE" | "FEMALE" | "OTHER";
}

PatientAddressPayload

{
  addressLine1?: string;
  city?: string;
  state?: string;
  country?: string;
  zipcode?: string;
  landmark?: string;
}

React Integration

Pre-built appointment calendar component for React applications.

import { AppointmentCalender } from "medos-sdk";

function BookingPage() {
  return <AppointmentCalender />;
}

The component automatically uses the initialized MedosClient instance.

Using React-specific exports

For better tree-shaking and to avoid React dependency conflicts:

import { AppointmentCalender } from "medos-sdk/react";

Vanilla JavaScript / HTML Integration

For websites using plain HTML, CSS, and JavaScript (no React), use the vanilla widget. The SDK provides two widgets: Appointment Booking and Enquiry Form.

Installation Options

npm install medos-sdk

After building, you'll find the widget files in:

  • dist/vanilla/widget.js - JavaScript bundle (UMD format)
  • dist/vanilla/widget.css - Base stylesheet
  • dist/vanilla/widget-themed.css - Advanced theming stylesheet

Option 2: CDN Integration

Load directly from CDN (replace with actual CDN URL when available):

<!-- CSS -->
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/medos-sdk@latest/dist/vanilla/widget.css"
/>

<!-- JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/medos-sdk@latest/dist/vanilla/widget.js"></script>

Appointment Booking Widget

Basic Usage

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="path/to/widget.css" />
  </head>
  <body>
    <div id="appointment-calendar"></div>

    <script src="path/to/widget.js"></script>
    <script>
      // Method 1: Global API (Recommended)
      window.MedosAppointmentCalendar.init({
        containerId: "appointment-calendar",
        apiKey: "your-api-key",
        onError: (err) => {
          console.error("Error:", err);
          alert("An error occurred. Please try again.");
        },
        onSuccess: () => {
          console.log("Appointment booked successfully!");
        },
      });

      // Method 2: Class-based API (Alternative)
      // const widget = new window.MedosAppointmentWidget('appointment-calendar', {
      //   apiKey: 'your-api-key'
      // });
    </script>
  </body>
</html>

CDN Integration Example

<!DOCTYPE html>
<html>
  <head>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/medos-sdk@latest/dist/vanilla/widget.css"
    />
  </head>
  <body>
    <div id="appointment-widget"></div>

    <script src="https://cdn.jsdelivr.net/npm/medos-sdk@latest/dist/vanilla/widget.js"></script>
    <script>
      window.MedosAppointmentCalendar.init({
        containerId: "appointment-widget",
        sessionToken: "your-session-token", // Obtained from your server
        baseURL: "https://api.medos.one/v1",
        onError: (err) => console.error(err),
        onSuccess: () => console.log("Success!"),
      });
    </script>
  </body>
</html>

Enquiry Form Widget

Basic Usage

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="path/to/widget.css" />
  </head>
  <body>
    <div id="enquiry-form"></div>

    <script src="path/to/widget.js"></script>
    <script>
      // Method 1: Global API (Recommended)
      window.MedosEnquiryForm.init({
        containerId: "enquiry-form",
        apiKey: "your-api-key",
        onError: (err) => console.error(err),
        onSuccess: (enquiry) => {
          console.log("Enquiry submitted:", enquiry);
          alert("Thank you! Your enquiry has been submitted.");
        },
      });

      // Method 2: Class-based API (Alternative)
      // const widget = new window.MedosEnquiryWidget('enquiry-form', {
      //   apiKey: 'your-api-key'
      // });
    </script>
  </body>
</html>

Widget Step Flow

Appointment Booking Steps

The appointment widget follows this exact step sequence (matching React component):

  1. Step 0: Phone Verification - Country code selection, phone input, OTP verification
  2. Step 1: Booking Option Selection - Session packs vs. new appointment (auto-skipped if no packages)
  3. Step 2: Location & Doctor Selection - Address and doctor selection with chief complaint
  4. Step 3: Date & Time Selection - Calendar, consultation mode, and time slot selection
  5. Step 4: Patient Selection - Choose existing patient or create new
  6. Step 5: Patient Details Form - Patient information and address (for new patients)
  7. Step 6: Appointment Summary - Review all details and cost breakdown
  8. Step 7: Success Confirmation - Confirmation with appointment details

Enquiry Form Steps

  1. Step 1: Contact Information - Name, email, phone number
  2. Step 2: Inquiry Details - Subject and detailed message
  3. Step 3: Contact Preference - Preferred contact method (email, phone, both)
  4. Step 4: Success Confirmation - Submission confirmation

Configuration Options

Complete API reference for widget initialization:

interface WidgetOptions {
  containerId: string; // Required: ID of container element
  apiKey?: string; // Optional: API key for authentication
  sessionToken?: string; // Optional: Session token (alternative to apiKey)
  baseURL?: string; // Optional: API base URL (default: https://api.medos.one/v1)
  onError?: (error: Error) => void; // Optional: Error callback
  onSuccess?: (data?: any) => void; // Optional: Success callback
  onStepChange?: (step: number) => void; // Optional: Step change callback
  theme?: "default" | "modern" | string; // Optional: Theme selection
}

Configuration Examples

Basic Configuration:

window.MedosAppointmentCalendar.init({
  containerId: "appointment-widget",
  apiKey: "your-api-key",
});

Advanced Configuration with Callbacks:

window.MedosAppointmentCalendar.init({
  containerId: "appointment-widget",
  sessionToken: "user-session-token",
  baseURL: "https://api.medos.one/v1",
  theme: "modern",
  onError: (error) => {
    console.error("Widget error:", error);
    // Show user-friendly error message
    document.getElementById("error-message").textContent =
      "Unable to load appointment booking. Please try again.";
    document.getElementById("error-message").style.display = "block";
  },
  onSuccess: (appointment) => {
    console.log("Appointment booked successfully", appointment);
    // Redirect or show success message
    window.location.href = "/appointment-confirmation";
  },
  onStepChange: (step) => {
    console.log("Current step:", step);
    // Track user progress for analytics
    if (typeof analytics !== "undefined") {
      analytics.track("appointment_step_change", { step });
    }
  },
});

Environment-Specific Configuration:

const isProduction = window.location.hostname !== "localhost";
const config = {
  containerId: "appointment-widget",
  baseURL: isProduction
    ? "https://api.medos.one/v1"
    : "https://dev-api.medos.one/v1",
  sessionToken: await getSessionToken(), // Fetch from your backend
  onError: (error) => handleWidgetError(error),
};

window.MedosAppointmentCalendar.init(config);

Authentication Methods

For better security, obtain the session token server-side:

// Fetch session token from your backend
async function initializeWidget() {
  try {
    const response = await fetch("/api/get-session-token");
    const { sessionToken } = await response.json();

    window.MedosAppointmentCalendar.init({
      containerId: "appointment-calendar",
      sessionToken: sessionToken,
      baseURL: "https://api.medos.one/v1",
      onError: (err) => console.error(err),
    });
  } catch (error) {
    console.error("Failed to get session token:", error);
  }
}

initializeWidget();

Using API Key (Development Only)

window.MedosAppointmentCalendar.init({
  containerId: "appointment-calendar",
  apiKey: "your-api-key", // Only for development/testing
  onError: (err) => console.error(err),
});

Server-Side Integration Examples

PHP Integration

<?php
// Get session token from your backend
function getSessionTokenFromBackend() {
    // Your implementation to get session token
    // This should call your backend API that exchanges API key for session token
    $apiKey = 'your-api-key';
    $response = file_get_contents('https://api.medos.one/v1/auth/session', false, stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => "Authorization: Bearer $apiKey\r\nContent-Type: application/json\r\n",
            'content' => json_encode(['apiKey' => $apiKey])
        ]
    ]));
    $data = json_decode($response, true);
    return $data['sessionToken'];
}

$sessionToken = getSessionTokenFromBackend();
?>
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="path/to/widget.css">
</head>
<body>
    <div id="appointment-calendar"></div>
    <script src="path/to/widget.js"></script>
    <script>
        window.MedosAppointmentCalendar.init({
            containerId: 'appointment-calendar',
            sessionToken: '<?php echo htmlspecialchars($sessionToken); ?>',
            onError: function(err) {
                console.error('Error:', err);
                alert('An error occurred. Please try again.');
            },
            onSuccess: function() {
                console.log('Appointment booked successfully!');
                // Redirect to confirmation page
                window.location.href = '/appointment-confirmation.php';
            }
        });
    </script>
</body>
</html>

Node.js/Express Integration

// Backend route to get session token
app.get("/api/session-token", async (req, res) => {
  try {
    const response = await axios.post("https://api.medos.one/v1/auth/session", {
      apiKey: process.env.MEDOS_API_KEY,
    });
    res.json({ sessionToken: response.data.sessionToken });
  } catch (error) {
    res.status(500).json({ error: "Failed to get session token" });
  }
});
<!-- Frontend HTML -->
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="/dist/vanilla/widget.css" />
  </head>
  <body>
    <div id="appointment-widget"></div>
    <script src="/dist/vanilla/widget.js"></script>
    <script>
      fetch("/api/session-token")
        .then((response) => response.json())
        .then((data) => {
          window.MedosAppointmentCalendar.init({
            containerId: "appointment-widget",
            sessionToken: data.sessionToken,
            onError: (err) => console.error(err),
            onSuccess: () => console.log("Success!"),
          });
        })
        .catch((error) => console.error("Failed to initialize widget:", error));
    </script>
  </body>
</html>

Multiple Widgets on Same Page

You can use multiple widgets independently on the same page:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="path/to/widget.css" />
  </head>
  <body>
    <!-- Appointment Widget -->
    <div id="appointment-widget"></div>

    <!-- Enquiry Widget -->
    <div id="enquiry-widget"></div>

    <script src="path/to/widget.js"></script>
    <script>
      // Initialize appointment widget
      window.MedosAppointmentCalendar.init({
        containerId: "appointment-widget",
        apiKey: "your-api-key",
        onSuccess: () => console.log("Appointment booked!"),
      });

      // Initialize enquiry widget
      window.MedosEnquiryForm.init({
        containerId: "enquiry-widget",
        apiKey: "your-api-key",
        onSuccess: (enquiry) => console.log("Enquiry submitted:", enquiry),
      });
    </script>
  </body>
</html>

Global API Reference

After loading the widget script, these global objects are available:

// Global initialization functions
window.MedosAppointmentCalendar.init(options); // Appointment widget
window.MedosEnquiryForm.init(options); // Enquiry form widget

// Widget classes for advanced usage
window.MedosAppointmentWidget; // Appointment widget class
window.MedosEnquiryWidget; // Enquiry widget class

Widget Lifecycle Management

Destroying Widgets

// For class-based initialization
const widget = new window.MedosAppointmentWidget("container", options);
// Later...
widget.destroy(); // Clean up event listeners and DOM

// For global API initialization
// Widgets are automatically cleaned up when container is removed from DOM

Reinitializing Widgets

// Clear container and reinitialize
const container = document.getElementById("appointment-widget");
container.innerHTML = ""; // Clear existing widget

window.MedosAppointmentCalendar.init({
  containerId: "appointment-widget",
  apiKey: "your-api-key",
});

Theme Customization

The SDK provides comprehensive theming capabilities through CSS variables. Choose between two CSS files based on your customization needs:

Basic Theming (widget.css)

For simple customization with minimal variables:

<link rel="stylesheet" href="dist/vanilla/widget.css" />
<style>
  .my-theme {
    --medos-primary-color: #7c3aed;
    --medos-primary-color-hover: #6d28d9;
    --medos-bg-color: #ffffff;
    --medos-text-color: #1f2937;
  }
</style>
<div id="widget" class="my-theme"></div>

Advanced Theming (widget-themed.css)

For comprehensive design system customization:

<link rel="stylesheet" href="dist/vanilla/widget-themed.css" />
<style>
  .my-advanced-theme {
    /* Colors */
    --medos-color-primary: #7c3aed;
    --medos-color-background: #ffffff;
    --medos-color-text: #1f2937;

    /* Typography */
    --medos-typography-font-family: "Georgia", serif;
    --medos-typography-font-size-md: 18px;

    /* Spacing & Layout */
    --medos-spacing-md: 16px;
    --medos-radius-md: 20px;
    --medos-shadow-lg: 0 20px 40px rgba(124, 58, 237, 0.15);
  }
</style>
<div id="widget" class="my-advanced-theme"></div>

Dynamic Theme Switching

// Apply theme programmatically
const container = document.getElementById("widget-container");

// Method 1: CSS classes
container.classList.add("dark-theme");

// Method 2: Direct CSS variables
container.style.setProperty("--medos-color-primary", "#7c3aed");
container.style.setProperty("--medos-color-background", "#1f2937");

// Method 3: Theme objects
const themes = {
  purple: {
    "--medos-color-primary": "#7c3aed",
    "--medos-color-secondary": "#a855f7",
  },
  green: {
    "--medos-color-primary": "#10b981",
    "--medos-color-secondary": "#34d399",
  },
};

function applyTheme(themeName) {
  const theme = themes[themeName];
  Object.entries(theme).forEach(([property, value]) => {
    container.style.setProperty(property, value);
  });
}

Theme Examples

Explore comprehensive theming examples in the examples/ directory:

  • theme-customization-basic.html - Basic CSS variable overrides
  • theme-customization-advanced.html - Multiple theme comparisons
  • theme-customization-javascript.html - Dynamic theme switching
  • theme-comparison.html - Side-by-side CSS file comparison
  • enquiry-form-theming.html - Enquiry form specific themes

Available CSS Variables

Basic Variables (widget.css):

  • --medos-primary-color, --medos-primary-color-hover
  • --medos-bg-color, --medos-bg-color-secondary
  • --medos-text-color, --medos-text-color-secondary
  • --medos-border-color, --medos-border-radius

Complete Variables (widget-themed.css):

  • Colors: 15+ color tokens for comprehensive theming
  • Typography: Font family, sizes, weights
  • Spacing: Consistent spacing scale (xs, sm, md, lg, xl)
  • Layout: Border radius, shadows, transitions

See examples/README-THEME-CUSTOMIZATION.md for complete documentation.

Troubleshooting Guide

Common Issues and Solutions

1. Widget Not Loading

Problem: Widget container remains empty, no errors in console.

Solutions:

// Check if widget script loaded
if (typeof window.MedosAppointmentCalendar === "undefined") {
  console.error("Widget script not loaded. Check script src path.");
}

// Ensure container exists
const container = document.getElementById("your-container-id");
if (!container) {
  console.error("Container element not found. Check containerId.");
}

// Wait for DOM ready
document.addEventListener("DOMContentLoaded", function () {
  window.MedosAppointmentCalendar.init({
    containerId: "appointment-widget",
    apiKey: "your-api-key",
  });
});

2. Authentication Errors

Problem: "Invalid API key" or "Authentication failed" errors.

Solutions:

// Verify API key format
const apiKey = "your-api-key";
if (!apiKey || apiKey === "your-api-key") {
  console.error("Please replace with your actual API key");
}

// Use session token for production
window.MedosAppointmentCalendar.init({
  containerId: "widget",
  sessionToken: "your-session-token", // More secure
  baseURL: "https://api.medos.one/v1",
});

// Check API endpoint
fetch("https://api.medos.one/v1/health")
  .then((response) => console.log("API accessible:", response.ok))
  .catch((error) => console.error("API not accessible:", error));

3. CSS Styling Issues

Problem: Widget appears unstyled or has layout issues.

Solutions:

<!-- Ensure CSS is loaded before JavaScript -->
<link rel="stylesheet" href="path/to/widget.css" />
<script src="path/to/widget.js"></script>

<!-- Check for CSS conflicts -->
<style>
  /* Ensure widget container has proper styling */
  #appointment-widget {
    width: 100%;
    max-width: 800px;
    margin: 0 auto;
  }

  /* Reset any conflicting styles */
  .medos-appointment-container * {
    box-sizing: border-box;
  }
</style>

4. Network/CORS Errors

Problem: "CORS policy" or network request failures.

Solutions:

// Check baseURL configuration
window.MedosAppointmentCalendar.init({
  containerId: "widget",
  apiKey: "your-api-key",
  baseURL: "https://api.medos.one/v1", // Ensure correct URL
  onError: (error) => {
    if (error.message.includes("CORS")) {
      console.error("CORS error: Ensure your domain is whitelisted");
    }
    if (error.message.includes("Network")) {
      console.error("Network error: Check internet connection and API status");
    }
  },
});

// Test API connectivity
async function testAPIConnection() {
  try {
    const response = await fetch("https://api.medos.one/v1/health");
    console.log("API Status:", response.status);
  } catch (error) {
    console.error("Cannot reach API:", error);
  }
}

5. Multiple Widget Conflicts

Problem: Multiple widgets interfering with each other.

Solutions:

// Use unique container IDs
window.MedosAppointmentCalendar.init({
  containerId: "appointment-widget-1", // Unique ID
  apiKey: "your-api-key",
});

window.MedosEnquiryForm.init({
  containerId: "enquiry-widget-1", // Different unique ID
  apiKey: "your-api-key",
});

// Clean up widgets when needed
const widget = new window.MedosAppointmentWidget("container", options);
// Later...
widget.destroy(); // Prevents memory leaks

6. Mobile/Responsive Issues

Problem: Widget doesn't display properly on mobile devices.

Solutions:

<!-- Ensure viewport meta tag -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<style>
  /* Responsive container */
  .widget-container {
    width: 100%;
    max-width: 100vw;
    padding: 10px;
    box-sizing: border-box;
  }

  /* Mobile-specific adjustments */
  @media (max-width: 768px) {
    .widget-container {
      padding: 5px;
    }
  }
</style>

7. Theme Not Applying

Problem: Custom CSS variables not taking effect.

Solutions:

/* Ensure proper CSS specificity */
.my-theme .medos-appointment-container {
  --medos-color-primary: #7c3aed !important;
}

/* Or apply to widget container directly */
#appointment-widget {
  --medos-color-primary: #7c3aed;
  --medos-color-background: #ffffff;
}
// Apply theme programmatically
const container = document.getElementById("appointment-widget");
container.style.setProperty("--medos-color-primary", "#7c3aed");

// Verify theme variables are applied
const computedStyle = getComputedStyle(container);
const primaryColor = computedStyle.getPropertyValue("--medos-color-primary");
console.log("Applied primary color:", primaryColor);

Debugging Tips

Enable Debug Mode:

// Add debug logging
window.MedosAppointmentCalendar.init({
  containerId: "widget",
  apiKey: "your-api-key",
  debug: true, // Enable debug mode if available
  onError: (error) => {
    console.error("Widget Error Details:", {
      message: error.message,
      stack: error.stack,
      timestamp: new Date().toISOString(),
    });
  },
  onStepChange: (step) => {
    console.log("Step changed to:", step);
  },
});

Check Widget State:

// Inspect widget instance
const container = document.getElementById("appointment-widget");
const widgetInstance = container._medosWidget; // Internal reference
if (widgetInstance) {
  console.log("Widget state:", widgetInstance.getState());
}

// Check global objects
console.log("Available globals:", {
  MedosAppointmentCalendar: typeof window.MedosAppointmentCalendar,
  MedosEnquiryForm: typeof window.MedosEnquiryForm,
  MedosAppointmentWidget: typeof window.MedosAppointmentWidget,
  MedosEnquiryWidget: typeof window.MedosEnquiryWidget,
});

Network Debugging:

// Monitor API calls
const originalFetch = window.fetch;
window.fetch = function (...args) {
  console.log("API Request:", args[0]);
  return originalFetch
    .apply(this, args)
    .then((response) => {
      console.log("API Response:", response.status, response.url);
      return response;
    })
    .catch((error) => {
      console.error("API Error:", error);
      throw error;
    });
};

Performance Optimization

Lazy Loading:

// Load widget only when needed
function loadWidget() {
  if (!document.getElementById("widget-script")) {
    const script = document.createElement("script");
    script.id = "widget-script";
    script.src = "path/to/widget.js";
    script.onload = () => {
      window.MedosAppointmentCalendar.init({
        containerId: "appointment-widget",
        apiKey: "your-api-key",
      });
    };
    document.head.appendChild(script);
  }
}

// Load on user interaction
document
  .getElementById("book-appointment-btn")
  .addEventListener("click", loadWidget);

Resource Optimization:

<!-- Preload critical resources -->
<link rel="preload" href="path/to/widget.css" as="style" />
<link rel="preload" href="path/to/widget.js" as="script" />

<!-- Use appropriate CSS file for your needs -->
<!-- widget.css for basic theming (smaller file) -->
<link rel="stylesheet" href="dist/vanilla/widget.css" />

<!-- widget-themed.css for advanced theming (larger file) -->
<link rel="stylesheet" href="dist/vanilla/widget-themed.css" />

Getting Help

If you encounter issues not covered in this guide:

  1. Check Browser Console: Look for error messages and warnings
  2. Verify Network Tab: Check if API requests are successful
  3. Test with Examples: Compare with working examples in examples/ directory
  4. Check Version Compatibility: Ensure you're using compatible versions
  5. Review Documentation: Check docs/VANILLA_WIDGET.md for additional details

Common Error Messages:

  • "Container element not found" → Check containerId parameter
  • "Invalid API key" → Verify API key or use session token
  • "Network request failed" → Check internet connection and API status
  • "Widget already initialized" → Clear container before reinitializing
  • "Theme variables not applied" → Check CSS specificity and variable names

Package Exports

The SDK provides multiple entry points for different use cases:

  • medos-sdk - Default export (includes React components)
  • medos-sdk/react - React-specific exports
  • medos-sdk/vanilla - Vanilla JS exports (ES modules)
  • medos-sdk/core - Core services only (no framework dependencies)
  • medos-sdk/widget - Widget bundle with CSS

Examples and Documentation

Comprehensive examples and documentation are available:

HTML Examples (examples/ directory)

  • vanilla-appointment-booking.html - Basic appointment widget with local files
  • cdn-appointment-booking.html - CDN integration example with fallback strategies
  • enquiry-form.html - Enquiry form widget with both local and CDN options
  • theme-customization-basic.html - Basic CSS variable overrides and theming
  • theme-customization-advanced.html - Advanced theming with multiple theme comparisons
  • theme-customization-javascript.html - Dynamic theme switching and programmatic theming
  • theme-comparison.html - Side-by-side comparison of CSS files
  • enquiry-form-theming.html - Enquiry form specific theming examples

Documentation (docs/ directory)

  • VANILLA_WIDGET.md - Comprehensive vanilla widget documentation
  • CDN_INTEGRATION.md - Complete CDN integration guide with best practices
  • BUILD.md - Build system and development setup
  • IMPLEMENTATION_STATUS.md - Current implementation status and roadmap

Theme Documentation

  • examples/README-THEME-CUSTOMIZATION.md - Complete theming guide with examples
  • CSS variable reference for both widget.css and widget-themed.css
  • Dynamic theme switching examples and best practices

API Reference

MedosClient

init(config)

Initialize the SDK with an API key (server-side).

Parameters:

  • config.apiKey (string, required) - Your Medos API key
  • config.baseURL (string, optional) - API base URL. Defaults to dev environment

Returns: Promise<void>


initWithSession(config)

Initialize the SDK with a session token (client-side).

Parameters:

  • config.sessionToken (string, required) - Session token
  • config.baseURL (string, optional) - API base URL. Defaults to dev environment

Returns: Promise<void>


fetchAllAddressesAndDoctors()

Fetch all clinic addresses and their associated doctors.

Returns: Promise<AddressesResponse>

Response Type:

{
  totalAddresses?: number;
  totalDoctors?: number;
  workspaceId?: number | string;
  addresses: Array<{
    id: string;
    completeAddress?: string;
    addressLine1?: string;
    city?: string;
    state?: string;
    country?: string;
    zipcode?: string;
    doctors?: Array<{
      id: string;
      name: string;
      email?: string;
      specialty?: string;
    }>;
  }>;
}

fetchAppointments(workspaceId, addressId, doctorId, appointmentDate)

Fetch available appointment slots for a specific doctor.

Parameters:

  • workspaceId (string | number) - Workspace identifier
  • addressId (string | number) - Address identifier
  • doctorId (string | number) - Doctor identifier
  • appointmentDate (string) - Date in YYYY-MM-DD format

Returns: Promise<Slot[]>

Response Type:

Array<{
  start: string; // ISO datetime format
  end: string; // ISO datetime format
  id?: string;
}>;

sendPhoneVerificationOtp(payload)

Send OTP to a phone number for verification.

Parameters:

  • payload.countryCode (string) - Country code with + prefix (e.g., "+1")
  • payload.phoneNumber (string) - Phone number without country code

Returns: Promise<any>


verifyPhoneVerificationOtp(payload)

Verify OTP for a phone number.

Parameters:

  • payload.countryCode (string) - Country code with + prefix
  • payload.phoneNumber (string) - Phone number without country code
  • payload.otp (string) - OTP code received

Returns: Promise<any>


AppointmentService

createAppointment(payload)

Create a new appointment booking.

Parameters:

  • payload (BookAppointmentPayload) - Appointment details

Returns: Promise<any>

Error Handling

All SDK methods return Promises and should be wrapped in try-catch blocks.

try {
  await MedosClient.init({ apiKey: "your-api-key" });
  const addresses = await MedosClient.fetchAllAddressesAndDoctors();
} catch (error) {
  console.error("Failed to fetch addresses:", error.message);
}

Requirements

  • Node.js: v14 or higher
  • Browsers: Modern browsers with ES6+ support
  • React: v19 or higher (only required for React components, not for vanilla widget)
  • TypeScript: v5 or higher (optional)

Support

For bug reports and feature requests, please visit our GitHub Issues.

License

UNLICENSED

Author

Pooranjoy Bhattacharya


Built for healthcare providers worldwide.