<div align="center">

## AHS CTI SDK

[![npm version](https://img.shields.io/npm/v/ahs-cti.svg?color=blue&style=for-the-badge)](https://www.npmjs.com/package/ahs-cti)
[![npm downloads](https://img.shields.io/npm/dm/ahs-cti.svg?color=green&style=for-the-badge)](https://www.npmjs.com/package/ahs-cti)
[![bundle size](https://img.shields.io/bundlephobia/minzip/ahs-cti?color=orange&style=for-the-badge)](https://bundlephobia.com/package/ahs-cti)

**A powerful, lightweight SDK for real-time call management with dynamic configuration and permission-based access control**

**🔑AUTHORIZED ACCESS REQUIRED - API Key & Tenant ID🔑**

</div>

### ⭐ **License Features**

- **API Key Authentication** - Secure access through authorized API keys
- **Tenant ID Management** - Multi-tenant support with isolated access
- **Permission-Based Access Control** - Entitlement-driven feature enablement
- **Cross-Platform Support** - Use across web, mobile, desktop, and server platforms
- **Usage Monitoring** - Real-time tracking and compliance monitoring
- **Flexible Deployment** - Deploy on authorized platforms with proper credentials

### 🚫 **Prohibited Activities**

- **No Unauthorized Access** - Cannot use without valid credentials
- **No Credential Sharing** - API keys and tenant IDs are confidential
- **No Reverse Engineering** - Cannot decompile or reverse engineer
- **No Platform Violations** - Must use only on authorized platforms
- **No Usage Abuse** - Cannot exceed subscription limits

## 📑 Table of Contents

- [✨ Key Features](#-key-features)
- [📦 Installation](#-installation)
- [🚀 Quick Start](#-quick-start)
  - [1️⃣ Initialize the SDK](#1️⃣-initialize-the-sdk)
  - [2️⃣ Add Call Control Panel](#2️⃣-add-call-control-panel)
  - [3️⃣ Use Call Management Hooks](#3️⃣-use-call-management-hooks)
- [⚙️ Dynamic Configuration](#️-dynamic-configuration)
  - [🔧 SDK Config](#-sdk-config)
  - [🌐 URL Config](#-url-config)
  - [🔐 Permission-Based Controls](#-permission-based-controls)
  - [📊 Configuration Merge Priority](#-configuration-merge-priority)
- [📚 API Reference](#-api-reference)
  - [🔧 Core Functions](#-core-functions)
  - [🪝 React Hooks](#-react-hooks)
- [🔒 Permissions System](#-permissions-system)
- [🔌 WebSocket Events](#-websocket-events)
- [📦 Data Types](#-data-types)
- [🌍 Browser Support](#-browser-support)
- [📄 License](#-license)

---

## ✨ Key Features

### 🎯 **Complete Call Control**

- Inbound and outbound call management
- Hold/Resume calls
- Mute/Unmute functionality
- Blind, Attended, and Warm call transfers
- Multi-line conference calling (up to 5 lines)
- Agent status management (Ready, Break, Wrapup)
- End call with disposition tracking
- Ringtone support for incoming calls

### ⚙️ **Dynamic Configuration**

- **Three-layer config merging** - Backend defaults, permission-based controls, and explicit SDK config
- **Permission-based feature toggling** - Controls auto-disabled based on entitlements
- **Runtime URL configuration** - All endpoints dynamically resolved from config
- **Customizable UI** - Material-UI SxProps for button styling

### 💾 **Smart State Management**

- Real-time call timer
- Persistent state storage via `@react-solutions/vault`
- Singleton state pattern
- Cross-component sync via observer pattern
- Full TypeScript support

## 📦 Installation

```bash
npm install ahs-cti
```

### 🔑 Required Peer Dependencies

```bash
npm install react react-dom axios @mui/material @mui/icons-material @emotion/react @emotion/styled @react-solutions/vault
```

---

## 🚀 Quick Start

### 1️⃣ **Initialize the SDK**

Initialize the SDK at the root of your application with the required credentials and optional dynamic configuration:

```tsx
import { useEffect } from "react";
import { initSDK } from "ahs-cti";

function App() {
  useEffect(() => {
    const initialize = async () => {
      try {
        await initSDK({
          // Required credentials
          apiKey: "your-api-key",
          tenantId: "your-tenant-id",
          agentId: "your-agent-id",

          // Optional: URL configuration for API endpoints
          urlConfig: {
            },

          // Optional: Feature configuration overrides
          sdkConfig: {
          },
        });
        console.log("✅ SDK initialized successfully");
      } catch (error) {
        console.error("❌ SDK initialization failed:", error);
      }
    };

    initialize();
  }, []);

  return <YourAppContent />;
}
```

### 2️⃣ **Add Call Control Panel**

Drop in the draggable call control panel anywhere in your app:

```tsx
import { CallControlPanel } from "ahs-cti";

export default function AgentDashboard() {
  const handleCallDataChange = (data) => {
    console.log("📞 Call data updated:", data);
  };

  return (
    <div>
      <h1>Agent Dashboard</h1>
      <CallControlPanel onDataChange={handleCallDataChange} />
    </div>
  );
}
```

### 3️⃣ **Use Call Management Hooks**

#### 🔴 **Start a Call**

```tsx
import { useClickToCall } from "ahs-cti";

export default function CallButton() {
  const { handleStartCall, isLoading, isSuccess, isError, error } =
    useClickToCall();

  const startCall = () => {
    handleStartCall({ mobileNumber: "1234567890" });
  };

  return (
    <button onClick={startCall} disabled={isLoading}>
      {isLoading ? "🔄 Calling..." : "📞 Start Call"}
    </button>
  );
}
```

#### 🔚 **End a Call**

```tsx
import { useEndCall } from "ahs-cti";

export default function EndCallButton() {
  const { handleEndCall, isLoading } = useEndCall();

  const endCall = () => {
    handleEndCall({ disposition: "RES" });
  };

  return (
    <button onClick={endCall} disabled={isLoading}>
      {isLoading ? "🔄 Ending..." : "🔚 End Call"}
    </button>
  );
}
```

#### 🚪 **Agent Logout**

```tsx
import { useLogout } from "ahs-cti";

export default function LogoutButton() {
  const { logout, isLoading } = useLogout();

  return (
    <button onClick={logout} disabled={isLoading}>
      {isLoading ? "🔄 Logging out..." : "🚪 Logout"}
    </button>
  );
}
```

#### 📞 **Get Caller Data**

The `useGetCallerData` hook provides reactive access to current call data independently of `CallControlPanel`:

```tsx
import { useGetCallerData } from "ahs-cti";

export default function CallStatusWidget() {
  const callerData = useGetCallerData();

  return (
    <div>
      <h3>Current Call Status</h3>
      {callerData.status === "ONCALL" ? (
        <div>
          <p>Active Call: {callerData.phone_number}</p>
          <p>Agent: {callerData.agent_id}</p>
          <p>Process: {callerData.process_name}</p>
          <p>Queue: {callerData.queue_name}</p>
        </div>
      ) : (
        <p>No active call</p>
      )}
    </div>
  );
}
```

#### 🔄 **Subscribe to SDK State**

```tsx
import { useSDKState } from "ahs-cti";

export default function AgentStatusDisplay() {
  const state = useSDKState();

  return (
    <div>
      <p>Agent: {state.agentId}</p>
      <p>Status: {state.agentStatus}</p>
      <p>Initialized: {state.isInitialized ? "Yes" : "No"}</p>
      <p>Hold: {state.hold ? "On Hold" : "Active"}</p>
      <p>Mute: {state.mute ? "Muted" : "Unmuted"}</p>
    </div>
  );
}
```

#### 🔐 **Get Authorization Token**

Retrieve the access token for external API calls:

```tsx
import { useGetAuthorizationToken } from "ahs-cti";

export default function ExternalAPICall() {
  const token = useGetAuthorizationToken();

  const fetchData = async () => {
    const response = await fetch("https://api.example.com/data", {
      headers: { Authorization: `Bearer ${token}` },
    });
    // handle response
  };

  return <button onClick={fetchData}>Fetch External Data</button>;
}
```

---

## ⚙️ Dynamic Configuration

The SDK supports a three-layer dynamic configuration system that determines which call control features are enabled.

### 🔧 SDK Config

Feature toggle options passed via `sdkConfig` in `initSDK()`:

### 🎨 **Configuration Options**

| Option                      | Type      | Default | Description                             |
| --------------------------- | --------- | ------- | --------------------------------------- |
| `disableEndCallButton`      | `boolean` | `false` | Disable the end call button             |
| `disabledDialButton`        | `boolean` | `false` | Disable the dial/outbound call button   |
| `disableCallTransferButton` | `boolean` | `false` | Disable the call transfer button        |
| `disableBlindTransfer`      | `boolean` | `false` | Disable blind transfer option           |
| `disableAttendedTransfer`   | `boolean` | `false` | Disable attended transfer option        |
| `disableWarmTransfer`       | `boolean` | `false` | Disable warm transfer option            |
| `disableConferenceButton`   | `boolean` | `false` | Disable the conference call button      |
| `disableHoldButton`         | `boolean` | `false` | Disable the hold functionality          |
| `disableMuteButton`         | `boolean` | `false` | Disable the mute functionality          |
| `disabledMoreOptionsButton` | `boolean` | `false` | Disable the more options menu           |
| `disableSoftPhone`          | `boolean` | `false` | Disable the embedded soft phone iframe  |
| `isDraggable`               | `boolean` | `true`  | Enable/disable panel dragging           |
| `enableSmsServices`         | `boolean` | `false` | Enable SMS services                     |
| `enableQueueName`           | `boolean` | `false` | Show queue name in call data            |
| `enableRingtone`            | `boolean` | `false` | Enable ringtone for incoming calls      |
| `auto_wrapup_time`          | `number`  | -       | Auto wrapup time in seconds             |
| `break_time`                | `number`  | -       | Break duration in seconds               |
| `disabled`                  | `SxProps` | -       | Custom MUI styles for disabled states   |
| `enabled`                   | `SxProps` | -       | Custom MUI styles for enabled states    |
| `outlined`                  | `SxProps` | -       | Custom MUI styles for outlined states   |

### 🌐 URL Config

All API endpoints are dynamically resolved from the URL configuration:

```typescript
interface URLConfig {
  id: string; // Configuration identifier
  baseURL: string; // Main API base URL
  coreBaseURL: string; // Core API URL for call control actions
  webSocketURL: string; // WebSocket URL for real-time events
  iframeURL: string; // Soft phone iframe URL
  iframeAPIURL: string; // Iframe API endpoint
  password: string; // Agent password for authentication
  accessToken?: string; // Pre-provided access token (optional)
}
```

If `baseURL` is not provided, the SDK falls back to `window.location.origin`.

### 🔐 Permission-Based Controls

The SDK implements a **fail-closed** permission model. After authentication, the backend returns entitlements that automatically determine which controls are enabled:

```typescript
// Entitlements structure from login response
{
  calls: {
    enabled: boolean,
    items: [
      { code: "call_inbound", enabled: boolean },
      { code: "call_outbound", enabled: boolean }
    ]
  },
  call_transfer: {
    enabled: boolean,
    items: [
      { code: "call_transfer_blind", enabled: boolean },
      { code: "call_transfer_attended", enabled: boolean },
      { code: "call_transfer_warm", enabled: boolean }
    ]
  },
  call_conference: {
    enabled: boolean,
    items: [
      { code: "call_conference", enabled: boolean }
    ]
  }
}
```

If permissions have not been loaded, **all controls are disabled by default**.

You can use the exported `SDK_PERMISSIONS` constants to reference permission codes:

```typescript
import { SDK_PERMISSIONS } from "ahs-cti";

// Permission categories
SDK_PERMISSIONS.CALLS; // "calls"
SDK_PERMISSIONS.CALL_TRANSFER; // "call_transfer"
SDK_PERMISSIONS.CALL_CONFERENCE; // "call_conference"

// Permission codes
SDK_PERMISSIONS.CALL_INBOUND; // "call_inbound"
SDK_PERMISSIONS.CALL_OUTBOUND; // "call_outbound"
SDK_PERMISSIONS.TRANSFER_BLIND; // "call_transfer_blind"
SDK_PERMISSIONS.TRANSFER_ATTENDED; // "call_transfer_attended"
SDK_PERMISSIONS.TRANSFER_WARM; // "call_transfer_warm"
SDK_PERMISSIONS.CONFERENCE; // "call_conference"
```

### 📊 Configuration Merge Priority

The SDK merges configuration from three sources (highest priority wins):

```
1. Explicit sdkConfig (passed to initSDK)       <- Highest priority
2. Permission-based controls (from entitlements) <- Medium priority
3. Backend callControls (from login response)    <- Lowest priority
```

This means:

- Backend defaults set the baseline configuration
- Entitlements override backend defaults based on the agent's permissions
- Explicit `sdkConfig` overrides everything, allowing host applications to force-enable or force-disable features

---

## 📚 API Reference

### 🔧 **Core Functions**

#### `initSDK(params)`

Initialize the SDK with credentials and configuration. **Must be called before using any components or hooks.** Returns a `Promise<void>`.

```typescript
await initSDK({
  apiKey: string,         // ✅ Required - API key for authentication
  tenantId: string,       // ✅ Required - Tenant identifier
  agentId: string,        // ✅ Required - Agent identifier
  sessionId?: string,     // 📎 Optional - Session identifier
  urlConfig?: URLConfig,  // 📎 Optional - URL configuration for endpoints
  sdkConfig?: SDKConfig,  // 📎 Optional - Feature configuration overrides
});
```

#### `isSDKInitialized()`

Check if the SDK has been successfully initialized.

```typescript
const initialized: boolean = isSDKInitialized();
```

#### `getSDKVersion()`

Get the current SDK version.

```typescript
const version: string = getSDKVersion();
```

#### `CallControlPanel`

Draggable control panel component for call management.

```tsx
<CallControlPanel
  onDataChange={(data: CallData) => {
    // Called when call status, phone number, or any call data changes
  }}
/>
```

### 🪝 **React Hooks**

| Hook                        | Description                                  | Returns                                                     |
| --------------------------- | -------------------------------------------- | ----------------------------------------------------------- |
| `useClickToCall()`          | Initiate outbound calls                      | `{ handleStartCall, isLoading, isSuccess, isError, error }` |
| `useEndCall()`              | End active calls with disposition             | `{ handleEndCall, isLoading, isSuccess, isError, error }`   |
| `useLogout()`               | Agent logout                                 | `{ logout, isLoading, isSuccess, isError, error }`          |
| `useGetCallerData()`        | Reactive access to current call data         | `CallData`                                                  |
| `useSDKState()`             | Subscribe to full SDK state changes          | `SDKState`                                                  |
| `useGetAuthorizationToken()`| Get the current access token                 | `string \| undefined`                                       |

---

## 🔌 WebSocket Events

The SDK connects to a WebSocket for real-time call events:

```
WebSocket URL: {urlConfig.webSocketURL}/api/v1/ws/agent/events?token={accessToken}
```

**Connection behavior:**
- Auto-reconnects with exponential backoff (base: 2s, max: 30s, up to 60 attempts)
- Sends ping every 30 seconds to keep the connection alive

**Event data structure:**

```typescript
{
  agent_id: number;
  status: CallStatus;           // IDLE, READY, ONCALL, RINGING, DIALING, WRAPUP, etc.
  type: string;                 // Call type
  event_time: string;
  phone_number: string;
  convox_id: string;
  process_id: number;
  process_name: string;
  hold: number;                 // 0 = not held, 1 = held
  mute: number;                 // 0 = not muted, 1 = muted
  mode: string;                 // 'manual' or automated
  queue_name: string;
  conferencestatus?: {          // Present during conference calls
    line_1_status: string;
    line_1_phonenumber: string;
    line_2_status: string;
    line_2_phonenumber: string;
    // ... up to line 5
  }
}
```

---

## 📦 Data Types

### `CallStatus` Enum

```typescript
enum CallStatus {
  IDLE = "IDLE",
  READY = "READY",
  BREAK = "BREAK",
  ONCALL = "ONCALL",
  WRAPUP = "WRAPUP",
  RINGING = "RINGING",
  DIALING = "DIALING",
  MISSED = "MISSED",
  HOLD = "HOLD",
  UNHOLD = "UNHOLD",
  MUTE = "MUTE",
  UNMUTE = "UNMUTE",
  DISCONNECTED = "DISCONNECTED",
  CONFERENCE = "CONFERENCE",
}
```

### `CallData`

```typescript
interface CallData {
  mobileNumber?: string;
  callReferenceId?: string;
  convoxId?: string;
  type?: string;
  status: string;
  agent_id: number;
  phone_number?: string;
  event_time?: string;
  convox_id?: string;
  process_id?: string;
  process_name?: string;
  mode?: string;
  queue_name?: string;
  mute?: number;          // 1 = muted, 0 = unmuted
  hold?: number;          // 1 = on hold, 0 = not on hold
}
```

### `ConferenceLineTypes`

```typescript
type ConferenceLineTypes = {
  line: number;                         // Line number (1-5)
  status: string;                       // ONCALL, IDLE, LINE USED, etc.
  type: "external" | "internal" | "";
  phone: string;
  isMute: boolean;
  isHold: boolean;
  isCallStart: boolean;
  isMergeCall: boolean;
};
```

### `StartCallPayload`

```typescript
interface StartCallPayload {
  mobileNumber?: string;
}
```

### `EndCallPayload`

```typescript
interface EndCallPayload {
  disposition?: string; // Default: "RES"
}
```

---

## 📤 Exported Types

```typescript
// Types
export type { CallData, CallControlPanelProps, SDKConfig, URLConfig };
export type { SDKPermissionState, ControlsFromPermissions, SDKPermissionContextValue };
export type { RequestResult, RequestOptions };
export type { UseGetRequest, UsePostRequest, UsePutRequest, UseDeleteRequest, UsePatchRequest };

// Enum
export { CallStatus };

// Constants
export { SDK_PERMISSIONS };

// Functions
export { initSDK, getSDKVersion, isSDKInitialized };

// Hooks
export { useLogout, useEndCall, useClickToCall, useGetCallerData, useGetAuthorizationToken, useSDKState };

// Components
export { CallControlPanel };
```

---

## 🌍 **Browser Support**

<div align="left">

| Browser                                                                       | Version | Status     |
| ----------------------------------------------------------------------------- | ------- | ---------- |
| ![Chrome](https://img.shields.io/badge/Chrome-60%2B-green?logo=google-chrome) | 60+     | ✅ Supported |
| ![Firefox](https://img.shields.io/badge/Firefox-60%2B-green?logo=firefox)     | 60+     | ✅ Supported |
| ![Safari](https://img.shields.io/badge/Safari-12%2B-green?logo=safari)        | 12+     | ✅ Supported |
| ![Edge](https://img.shields.io/badge/Edge-79%2B-green?logo=microsoft-edge)    | 79+     | ✅ Supported |

</div>

---

## 📄 **License**

<div align="center">

![license](https://img.shields.io/badge/License-Achala-blue?style=for-the-badge)
![license](https://img.shields.io/badge/License-Proprietary-red?style=for-the-badge)

</div>
