<div style="text-align: center" align="center">
 <img src="https://ns.clubmed.com/fbs/RWD/branding2023/Logo/MicrosoftTeams-image%20(9).png" width="200" alt="Club Med"/>
</div>

<div align="center">
   <h1>@clubmed/usg-chat-ui</h1>
   <hr />

[![npm version](https://badge.fury.io/js/%40clubmed%2Ftrident-ui.svg)](https://badge.fury.io/js/%40clubmed%2Fusg-chat-ui)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)

</div>

Reusable chat experience extracted from the ClubMed USG project. Everything is published as ESM-only output so it can be consumed by any Vite/Next/CRA-style React host.

## Installation

```bash
npm install @clubmed/usg-chat-ui
```

## Provider setup

Drop the ready-made `<Chat />` organism into your page and pass the minimal props (`apiUrl`, `accessToken`, `user`, optional `logout`). Internally it wires the chat provider, HTTP client, and feature contexts so you don’t need to juggle multiple wrappers.

```tsx
import type {ChatUser} from "@clubmed/usg-chat-ui";
import {Chat} from "@clubmed/usg-chat-ui/organisms/Chat";;

const session = useSession(); // host-specific auth hook

export function ChatPanel() {
  const user: ChatUser | null = session
    ? {
      userId: session.userId,
      userName: session.name,
      userEmail: session.email,
    }
    : null;

  return (
    <ChatProvider>
      apiUrl={process.env.API_URL!}
      accessToken={session.accessToken}
      user={user}
      logout={session.logout}
    >
      <Chat />
    </ChatProvider>
  );
}
```

### Configuration keys

| Field                | Description                                                                             |
|----------------------|-----------------------------------------------------------------------------------------|
| `config.apiUrl`      | Base URL for server-to-server requests (e.g. `https://backend.internal`).               |
| `config.fetcher`     | Optional custom `fetch` implementation (for SSR polyfills, logging, etc.).              |
| `config.accessToken` | Latest access token. When omitted/`null`, requests are sent anonymously.                |
| `config.user`        | Lightweight `{ userId, userName, userEmail }` object propagated via headers.            |
| `logout`             | Optional handler wired into settings so users can disconnect directly from the chat UI. |

If you need to customize HTTP transport or headers, provide a `fetcher` override or extend the HTTP client directly—`ChatProvider` keeps the config surface minimal (`apiUrl`, `fetcher`, `accessToken`, `user`).

## Labels & Accessibility

The chat UI exposes a `labels` prop on `<Chat />` so hosts can override empty‑state copy, greeting templates, and every aria-label used throughout the experience without editing component logic.

```tsx
<Chat
  labels={{
    greetingTemplate: "Hola {{firstName}} 👋",
    emptyStateTitle: "¡Bienvenido a tu copiloto!",
    chatSettingsButtonAriaLabel: "Preferencias",
  }}
/>
```

Under the hood, the `ChatLabelsProvider` and `useChatLabels()` hook distribute these strings to all molecules. The hook exposes two helpers:

- `get(key, fallback)` → returns a plain string, falling back to the provided default when the host didn’t override it.
- `p(key, fallback, values)` → interpolates templates (e.g. `{{firstName}}`) and trims whitespace. Returns `undefined` when the final string is empty.

### Available label keys

| Key                                                                                                | Purpose / Example                                                     |
|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|
| `greetingTemplate`                                                                                 | Template for the empty-state greeting (`"Bonjour {{firstName}} 👋"`). |
| `emptyStateTitle` / `emptyStateDescription`                                                        | Marketing copy for the empty state.                                   |
| `assistantThinkingIndicator`                                                                       | Pulsing text while the assistant is streaming.                        |
| `chatSettingsButtonAriaLabel`                                                                      | Settings button aria label.                                           |
| `sidebarOpenHistoryAriaLabel`, `conversationRefreshAriaLabel`, `conversationCloseHistoryAriaLabel` | Sidebar toggle & controls.                                            |
| `messageCopyAriaLabel`, `messageRegenerateAriaLabel`, `messageOpenCanvasAriaLabel`                 | Message action buttons (aria).                                        |
| `messageCopyButtonText`, `messageCopySuccessText`, `messageRegenerateButtonText`, `messageOpenCanvasButtonText` | Visible labels beside those buttons.                               |
| `messageThumbsUpAriaLabel`, `messageThumbsDownAriaLabel`, `messageCloseFeedbackAriaLabel`          | Feedback modal controls (aria).                                       |
| `messageFeedbackPositiveTitle`, `messageFeedbackNegativeTitle`, `messageFeedbackPromptText`, `messageFeedbackTextareaPlaceholder`, `messageFeedbackSkipButtonText`, `messageFeedbackSubmitButtonText`, `messageFeedbackSubmittingText` | Copy rendered inside the feedback modal. |
| `messageCopyToastTitle`, `messageCopyToastDescription`, `messageCopyErrorTitle`, `messageCopyErrorDescription`, `messageFeedbackToastTitle`, `messageFeedbackToastDescription`, `messageFeedbackToastErrorTitle`, `messageFeedbackToastErrorDescription` | Toast notifications for copy/feedback flows. |
| `canvasReduceAriaLabel`, `canvasExpandAriaLabel`, `canvasCloseAriaLabel`                           | Canvas panel controls.                                                |
| `branchPreviousAriaLabel`, `branchNextAriaLabel`                                                   | Multi-branch navigation buttons.                                      |
| `promptSubmitAriaLabel`, `promptComposerPlaceholder`                                               | Prompt composer button aria + placeholder text.                       |

> **Implementation convention:** All new user-facing strings or aria labels **must** use `useChatLabels()` (`const {get, p} = useChatLabels();`) instead of hard-coded literals. Declare the key inside `ChatLabelsConfig` with a short JSDoc example, then consume it via `get/p` with a sensible fallback so the UI remains resilient when a host doesn’t override that value yet.

## Storybook

Storybook v10 is configured at the workspace root (`usg-ui/.storybook`). Run `yarn storybook` from `usg-ui/` to browse molecules and verify appearance tokens in isolation. The build output (`storybook-static`) can be published alongside the npm package for visual regression if desired.
