# Aptolix Chatbot Widget

Embeddable chat widget for websites. Install from npm or load from the jsDelivr CDN.

```text
npm install aptolix_chatbot
```

```html
<script type="module">
  import AptolixChatbot from 'https://cdn.jsdelivr.net/npm/aptolix_chatbot/dist/web.mjs';
</script>
```

Use **`chatbotId`**. The old `chatflowid` name is not used.

---

## What you must pass

Only two values are required. Everything else can come from the database (API), a previous local cache, or built-in defaults.

| Parameter   | Required | Description |
| ----------- | -------- | ----------- |
| `apiHost`   | Yes      | API base URL, no trailing slash. Example: `https://api.aptolix.com` |
| `chatbotId` | Yes      | Chatbot UUID from the Aptolix dashboard |

Minimum embed:

```html
<script type="module">
  import AptolixChatbot from 'https://cdn.jsdelivr.net/npm/aptolix_chatbot/dist/web.mjs';

  AptolixChatbot.init({
    apiHost: 'https://api.aptolix.com',
    chatbotId: '1035dff6-4085-48cd-9c3d-71641fed7db7'
  });
</script>
```

That opens a floating bubble in the bottom-right. Appearance (name, color, greeting, flags) is loaded from the API.

---

## How settings are chosen

Each appearance field is resolved in this order. The first **defined** value wins.

```text
1. Value you pass to init()          ← page override
2. Value from the API (database)     ← live dashboard settings
3. Value cached in localStorage      ← only if the API request fails
4. Built-in defaultConfig            ← last resort
```

```mermaid
flowchart TD
  A[AptolixChatbot.init] --> B{apiHost and chatbotId?}
  B -->|no| Z[Log error and stop]
  B -->|yes| C[GET /chatbot/chatbotId/appearance]
  C -->|200 OK| D[Save that config to localStorage]
  C -->|fail or offline| E{localStorage has a cached config?}
  E -->|yes| F[Use cached config]
  E -->|no| G[Use built-in defaults]
  D --> H[Merge: init override then API then defaults]
  F --> H
  G --> H
  H --> I{mode}
  I -->|bubble default| J[Floating bubble plus iframe chat]
  I -->|section| K[Embed chat into target element]
```

### When each source is used

| Situation | What the widget uses |
| --------- | -------------------- |
| You only pass `apiHost` + `chatbotId`, API is up | Database settings for every optional field |
| You also pass `botname` or `botaccentColor` | Those page values win; the rest still come from the API |
| API is down, visitor opened this chatbot before | Last successful API config from `localStorage` |
| API is down and there is no cache | Built-in defaults (`AI Assistant`, purple accent, etc.) |
| You pass `allowRating: false` | Rating stays off even if the database has it on |

`false` is a real override. Booleans are not treated as “missing” the way an omitted field is.

### Example: API / database only

Dashboard has bot name “Support”, accent `#0f766e`, greeting “How can we help?”.

```javascript
AptolixChatbot.init({
  apiHost: 'https://api.aptolix.com',
  chatbotId: '1035dff6-4085-48cd-9c3d-71641fed7db7'
});
```

Result: Support / teal / that greeting. No page-level appearance code needed.

### Example: page override on top of the database

```javascript
AptolixChatbot.init({
  apiHost: 'https://api.aptolix.com',
  chatbotId: '1035dff6-4085-48cd-9c3d-71641fed7db7',
  botname: 'Sales Bot',          // overrides database name
  botaccentColor: '#2563eb'      // overrides database color
  // greeting, flags, logo still come from the API
});
```

Result: name and color come from this page. Greeting, rating, expand, and the rest still come from the database.

### Example: API down, cache exists

1. Yesterday the API returned `{ botname: "Support", botaccentColor: "#0f766e" }` and it was saved under `aptolix_chatbot_config_<chatbotId>`.
2. Today the appearance request fails.
3. The widget uses that cached object, then applies any `init()` overrides, then defaults for anything still missing.

### Example: API down, no cache

The widget still starts. You get defaults:

- Name: `AI Assistant`
- Icon: 💬
- Accent: `#4a57e2ff`
- Welcome hint after 2 seconds
- Greeting: `Welcome! Let me know if you have any questions.`
- Download, expand, reset, and rating on
- Rating first shown after 3 bot replies, then every 4 more

Chat messages still need a working `apiHost`. Cached appearance does not replace the chat API.

---

## Modes

### Bubble (default)

Floating button on the host page. The chat window runs in an iframe so host CSS cannot restyle it.

```javascript
AptolixChatbot.init({
  apiHost: 'https://api.aptolix.com',
  chatbotId: '1035dff6-4085-48cd-9c3d-71641fed7db7',
  mode: 'bubble' // optional; this is the default
});
```

### Section

Renders inside a container. No bubble. Close / expand controls are hidden.

```html
<div id="chat-slot" style="height: 600px;"></div>
<script type="module">
  import AptolixChatbot from 'https://cdn.jsdelivr.net/npm/aptolix_chatbot/dist/web.mjs';

  AptolixChatbot.init({
    apiHost: 'https://api.aptolix.com',
    chatbotId: '1035dff6-4085-48cd-9c3d-71641fed7db7',
    mode: 'section',
    target: '#chat-slot'
  });
</script>
```

`target` can be a CSS selector or a DOM element. The custom element always uses section mode:

```html
<aptolix-chatbot
  apihost="https://api.aptolix.com"
  chatbotid="1035dff6-4085-48cd-9c3d-71641fed7db7">
</aptolix-chatbot>
```

HTML attributes are lowercase (`apihost`, `chatbotid`, `botaccentcolor`). Booleans must be the string `"true"`.

---

## All `init()` settings

| Parameter | Type | Default (if API/cache empty) | Used by the widget |
| --------- | ---- | ---------------------------- | ------------------ |
| `apiHost` | string | — | Yes. Required. |
| `chatbotId` | string | — | Yes. Required. |
| `mode` | `'bubble'` \| `'section'` | `'bubble'` | Yes |
| `target` | selector or element | — | Yes, section mode only |
| `botname` | string | `AI Assistant` | Yes. Header and messages. |
| `widgetIcon` | emoji, text, or SVG | 💬 | Yes. Bubble button. |
| `botaccentColor` | CSS color | `#4a57e2ff` | Yes. Theme for header, bubble, user bubbles. |
| `companyLogo` | image URL or SVG | none | Yes. Header logo. Invalid URL/SVG is dropped. |
| `botAvatar` | image URL or SVG | default robot SVG | Yes. Message avatars. |
| `greetingMessage` | string | Welcome text above | Yes. First bot line when history is empty. |
| `floatingWelcomeMessage` | string | Hint text above | Yes. Bubble mode hint. |
| `floatingWelcomeDelay` | number (seconds) | `2` | Yes. Hint delay. |
| `allowDownloadTranscript` | boolean | `true` | Yes. Menu item. |
| `allowExpandChatWindow` | boolean | `true` | Yes. Expand control (bubble). |
| `allowReset` | boolean | `true` | Yes. Menu item. |
| `allowRating` | boolean | `true` | Yes. Footer stars. |
| `ratingThreshold` | number | `3` | Yes. First prompt after this many bot replies. |
| `ratingInterval` | number | `4` | Yes. Next prompt after this many more bot replies. |
| `inputCharLimit` | number | `350` | Yes. Input maxlength. |
| `companyLogoUrl` | string | `''` | Accepted, not applied. Use `companyLogo`. |
| `botavatarUrl` | string | `''` | Accepted, not applied. Use `botAvatar`. |
| `allowFullPageChat` | boolean | `false` | Accepted, not applied. |

`username` and `userAvatar` are not `init()` arguments. They come from the appearance API (`username`, `user_avatar`) or defaults (`You` and the default user SVG).

### Full example (every applied setting)

```javascript
import AptolixChatbot from 'https://cdn.jsdelivr.net/npm/aptolix_chatbot/dist/web.mjs';

AptolixChatbot.init({
  apiHost: 'https://api.aptolix.com',
  chatbotId: '1035dff6-4085-48cd-9c3d-71641fed7db7',

  mode: 'bubble',
  botname: 'Maya',
  widgetIcon: '💬',
  botaccentColor: '#4f46e5',
  companyLogo: 'https://example.com/logo.svg',
  botAvatar: 'https://example.com/bot.png',
  greetingMessage: 'Hi — ask me about orders or returns.',
  floatingWelcomeMessage: 'Need help? I am here.',
  floatingWelcomeDelay: 3,
  allowDownloadTranscript: true,
  allowExpandChatWindow: true,
  allowReset: true,
  allowRating: true,
  ratingThreshold: 3,
  ratingInterval: 4,
  inputCharLimit: 350
});
```

You do not need this full object in production. Pass overrides only when this page should differ from the dashboard.

---

## API the widget calls

Appearance (database settings):

```http
GET {apiHost}/chatbot/{chatbotId}/appearance
```

Expected JSON (snake_case). The widget maps these keys:

| API field | Widget field |
| --------- | ------------ |
| `bot_name` | `botname` |
| `widget_icon` | `widgetIcon` |
| `company_logo` | `companyLogo` |
| `bot_avatar` | `botAvatar` |
| `user_avatar` | `userAvatar` |
| `username` | `username` |
| `accent_color` | `botaccentColor` |
| `floating_welcome_message` | `floatingWelcomeMessage` |
| `floating_welcome_delay` | `floatingWelcomeDelay` |
| `greeting_message` | `greetingMessage` |
| `download_transcript` | `allowDownloadTranscript` |
| `expand_chat_window` | `allowExpandChatWindow` |
| `full_page_chat` | `allowFullPageChat` |
| `input_char_limit` | `inputCharLimit` |
| `allow_reset` | `allowReset` |
| `allow_rating` | `allowRating` |
| `rating_threshold` | `ratingThreshold` |
| `rating_interval` | `ratingInterval` |

Chat:

```http
POST {apiHost}/chatbot/{chatbotId}
Content-Type: application/json

{ "message": "...", "chatbot_id": "...", "visitor_id": "...", "session_id": "..." }
```

The API may return `visitor_id`, `session_id`, `response` / `message`, and `limitReachedDate`. A limit date disables input and shows a warning until that date.

Rating:

```http
POST {apiHost}/chatbot/{chatbotId}/rating
Content-Type: application/json

{ "chatbot_id": "...", "visitor_id": "...", "session_id": "...", "rating": 1-5 }
```

---

## What is stored in the browser

| Key | Purpose |
| --- | ------- |
| `aptolix_chatbot_config_<chatbotId>` | Last successful appearance payload (offline fallback) |
| `aptolix_chatbot_state_<chatbotId>` | `visitorId`, `sessionId`, today’s chat history, usage limit, ratings |

Chat history is dropped after midnight local time. Visitor id is kept so the same person is recognized.

---

## Multiple chatbots on one page

Each instance needs its own `chatbotId`. Reusing the same id shows a duplicate-chatbot error and does not start a second copy.

```javascript
AptolixChatbot.init({ apiHost, chatbotId: 'support-id', mode: 'bubble' });
AptolixChatbot.init({ apiHost, chatbotId: 'sales-id', mode: 'section', target: '#sales' });
```

```javascript
AptolixChatbot.getActiveInstances(); // ['support-id', 'sales-id']
AptolixChatbot.destroy('support-id');
AptolixChatbot.destroy();            // all instances
```

---

## Local development

```bash
npm install
npm run build
npm run serve
```

Open `http://localhost:3002/test.html`. That page can switch local vs production API and local `dist/` vs the published npm bundle.

Publish (maintainers):

```bash
npm run build
npm version patch
npm run publish:cdn
```

`publish:cdn` publishes to npm and purges the jsDelivr cache for `aptolix_chatbot@latest`.
