@pixygon/chatbot-react 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +129 -43
  2. package/dist/index.d.ts +11 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,95 +1,181 @@
1
1
  # @pixygon/chatbot-react
2
2
 
3
- React + MUI + RTK Query components for the Pixygon chatbot.
3
+ Ready-to-mount **React 18/19 + MUI 6/7 + RTK Query** pages for the Pixygon
4
+ chatbot stack. Pair with [`@pixygon/chatbot-server`](https://www.npmjs.com/package/@pixygon/chatbot-server)
5
+ on the API side.
6
+
7
+ ```
8
+ +----------------------+ +----------------------+
9
+ | Host React app |----->| createChatbotComponents()
10
+ | (your baseApi here) | +----------------------+
11
+ +----------------------+ |
12
+ v
13
+ KnowledgePage · ChatPage · ChatAnalyticsPage · EmbedChatPage · ChatbotSettings
14
+ |
15
+ v
16
+ HTTP -> chatbot-server
17
+ ```
18
+
19
+ You configure once at boot, then mount the components in your router or
20
+ settings tree. Hooks close over the active tenant id, branding, and your
21
+ existing RTK Query base.
22
+
23
+ ---
4
24
 
5
25
  ## Install
6
26
 
7
27
  ```bash
8
28
  npm install @pixygon/chatbot-react
9
- # Peer deps the host already has:
10
- # - react ≥18
11
- # - @mui/material, @mui/icons-material, @emotion/react, @emotion/styled ≥11
12
- # - @reduxjs/toolkit ≥2, react-redux ≥9
13
29
  ```
14
30
 
15
- ## Usage
31
+ Peer dependencies the host already has:
32
+
33
+ | Peer | Range |
34
+ |---|---|
35
+ | `react` / `react-dom` | ^18 or ^19 |
36
+ | `@mui/material` / `@mui/icons-material` | ^6 or ^7 |
37
+ | `@emotion/react` / `@emotion/styled` | ^11 |
38
+ | `@reduxjs/toolkit` | ^2 |
39
+ | `react-redux` | ^9 |
40
+
41
+ The host's `baseApi` should include these RTK Query tag types so cache
42
+ invalidation works:
16
43
 
17
44
  ```ts
45
+ tagTypes: [..., "Knowledge", "ChatConversation"]
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Configure once
51
+
52
+ ```tsx
53
+ // src/chatbot.tsx
18
54
  import { createChatbotComponents } from "@pixygon/chatbot-react";
55
+ import { useParams } from "react-router";
19
56
  import { baseApi } from "@/apis/baseApi";
20
57
  import { useAppSelector } from "@/store/store";
58
+ import { useGetTenantQuery, useUpdateTenantMutation } from "@/apis/tenantApi";
21
59
 
22
- export const chatbot = createChatbotComponents({
60
+ const chatbot = createChatbotComponents({
23
61
  apiSlice: baseApi,
24
- pathPrefix: "/tenants", // or "/companies"
62
+ pathPrefix: "/tenants", // or "/companies"
25
63
  useTenantId: () => useAppSelector((s) => s.global.activeTenantId),
26
- apiBase: import.meta.env.VITE_API_URL,
64
+ apiBase: import.meta.env.VITE_API_URL, // public surface origin
27
65
  brand: {
28
66
  name: "TotalHMS",
29
67
  tagline: "Powered by TotalHMS",
30
68
  primaryColor: "#8FB7C9",
31
69
  },
32
70
  });
33
- ```
34
71
 
35
- Then drop into your router:
72
+ export const KnowledgePage = chatbot.KnowledgePage;
73
+ export const ChatPage = chatbot.ChatPage;
74
+ export const ChatAnalyticsPage = chatbot.ChatAnalyticsPage;
36
75
 
37
- ```tsx
38
- <Route path="/knowledge" element={<chatbot.KnowledgePage />} />
39
- <Route path="/chat" element={<chatbot.ChatPage />} />
40
- <Route path="/chat-analytics" element={<chatbot.ChatAnalyticsPage />} />
41
- <Route path="/embed/chat/:tenantSlug" element={<EmbedRoute />} />
76
+ // Small wrapper so the route can hand the slug from useParams down to the page.
77
+ export function EmbedChatPageRoute() {
78
+ const { tenantSlug } = useParams();
79
+ return <chatbot.EmbedChatPage tenantSlug={tenantSlug} />;
80
+ }
81
+
82
+ // Settings card — host supplies tenant + save handler (Tenant model lives here).
83
+ export function ChatbotSettings({ tenantId }: { tenantId: string }) {
84
+ const { data: tenant } = useGetTenantQuery(tenantId);
85
+ const [updateTenant] = useUpdateTenantMutation();
86
+ return (
87
+ <chatbot.ChatbotSettings
88
+ tenant={tenant ?? null}
89
+ onSave={(patch) =>
90
+ updateTenant({ tenantId, patch }).unwrap().then(() => undefined)
91
+ }
92
+ />
93
+ );
94
+ }
42
95
  ```
43
96
 
44
- `EmbedChatPage` reads `tenantSlug` from props, so the host wires it through:
97
+ ## Drop into routes
45
98
 
46
99
  ```tsx
47
- function EmbedRoute() {
48
- const { tenantSlug } = useParams();
49
- return <chatbot.EmbedChatPage tenantSlug={tenantSlug} />;
50
- }
100
+ import { KnowledgePage, ChatPage, ChatAnalyticsPage, EmbedChatPageRoute } from "@/chatbot";
101
+
102
+ <Route path="/embed/chat/:tenantSlug" element={<EmbedChatPageRoute />} />
103
+
104
+ <Route element={<DashboardLayout />}>
105
+ <Route path="/knowledge" element={<KnowledgePage />} />
106
+ <Route path="/chat" element={<ChatPage />} />
107
+ <Route path="/chat-analytics" element={<ChatAnalyticsPage />} />
108
+ </Route>
51
109
  ```
52
110
 
53
- Settings card (drops into the host's settings page, host supplies the
54
- tenant + save handler since the Tenant model lives in the host):
111
+ ## Drop into a settings page
55
112
 
56
113
  ```tsx
57
- <chatbot.ChatbotSettings
58
- tenant={activeTenant}
59
- onSave={(patch) => updateTenant({ tenantId, patch }).unwrap()}
60
- />
114
+ <ChatbotSettings tenantId={activeTenantId} />
61
115
  ```
62
116
 
117
+ ---
118
+
63
119
  ## What you get
64
120
 
65
- - `KnowledgePage` list + paste-text upload + delete
66
- - `ChatPage` — operator chat UI with sessionId-keyed threads
67
- - `ChatAnalyticsPage` KPI tiles, knowledge gaps, top questions, keywords,
68
- semantic clusters, cost timeseries, source usage, drill-down dialog
69
- - `EmbedChatPage` iframe-friendly chat UI for the public anonymous surface
70
- - `ChatbotSettings` cost cap input + copy-pasteable embed snippet
121
+ | Component | Purpose |
122
+ |---|---|
123
+ | `KnowledgePage` | List, paste/upload, delete knowledge documents per tenant |
124
+ | `ChatPage` | Operator chat UI with session-keyed threads and citations |
125
+ | `ChatAnalyticsPage` | KPI tiles · knowledge gaps · top questions · keywords · semantic clusters · cost timeseries · source usage · question drill-down |
126
+ | `EmbedChatPage` | Iframe-friendly anonymous chat for the public surface |
127
+ | `ChatbotSettings` | Per-tenant monthly cost cap + copy-pasteable embed snippet |
128
+
129
+ ---
130
+
131
+ ## Embed widget
71
132
 
72
- The bootstrap snippet is served as a separate static file at
73
- `@pixygon/chatbot-react/chatbot.js`. Symlink or copy to your app's `public/`:
133
+ `chatbot.js` is shipped as a static file inside the package. Drop it in your
134
+ app's `public/` so it's served at `/chatbot.js`:
74
135
 
75
136
  ```bash
76
137
  cp node_modules/@pixygon/chatbot-react/public/chatbot.js public/chatbot.js
77
138
  ```
78
139
 
79
- ## RTK Query tag types
140
+ Then anyone can embed your chatbot on any HTML page:
80
141
 
81
- The host's `baseApi` should include these tags so invalidation works:
82
-
83
- ```ts
84
- tagTypes: [..., "Knowledge", "ChatConversation"]
142
+ ```html
143
+ <script src="https://app.example.com/chatbot.js"
144
+ data-tenant-slug="acme"
145
+ defer></script>
85
146
  ```
86
147
 
148
+ The widget renders a floating launcher, opens an iframe pointed at
149
+ `/embed/chat/<slug>` on your dashboard, and `postMessage`s a
150
+ `pixygon-chatbot:close` event when the user closes it. The `ChatbotSettings`
151
+ card auto-generates the snippet with the right URL.
152
+
153
+ ---
154
+
87
155
  ## Hooks-only API
88
156
 
89
- If you only want the RTK Query hooks without the pages, use `injectChatbotEndpoints`:
157
+ Skip the pages if you want to build your own UI:
90
158
 
91
159
  ```ts
92
160
  import { injectChatbotEndpoints } from "@pixygon/chatbot-react";
93
- const { useListDocumentsQuery, useSendMessageMutation, ... } =
94
- injectChatbotEndpoints(baseApi, { pathPrefix: "/tenants" });
161
+
162
+ const {
163
+ useListDocumentsQuery,
164
+ useCreateDocumentMutation,
165
+ useDeleteDocumentMutation,
166
+ useSendMessageMutation,
167
+ useGetConversationQuery,
168
+ useRateMessageMutation,
169
+ useChatOverviewQuery,
170
+ useKnowledgeGapsQuery,
171
+ useSemanticClustersQuery,
172
+ // ...
173
+ } = injectChatbotEndpoints(baseApi, { pathPrefix: "/tenants" });
95
174
  ```
175
+
176
+ ---
177
+
178
+ ## Companion package
179
+
180
+ `@pixygon/chatbot-server` is the Express + Mongoose backend these components
181
+ expect. See its README for the API wire-up.
package/dist/index.d.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  import * as react from 'react';
2
- import { Api } from '@reduxjs/toolkit/query';
3
2
 
4
- /** RTK Query Api generics are invariant a host's concrete baseApi
5
- * (with literal `reducerPath` and specific tag types) cannot fit into a
6
- * narrower-looking `Api<BFn, {}, string, string>`. We therefore accept
7
- * any concrete Api shape and rely on injectChatbotEndpoints to do the
8
- * runtime work. The host's hooks (returned by createChatbotComponents)
9
- * remain fully typed inside the package; the host doesn't need a cast. */
10
- type AnyApi$1 = Api<any, any, any, any>;
3
+ /** RTK Query's `Api<...>` type carries `unique symbol` brand fields that
4
+ * make even `Api<any, any, any, any>` reject concrete host APIs. We accept
5
+ * `any` for `apiSlice` on the public surface and rely on the runtime
6
+ * injection inside `injectChatbotEndpoints` to do the wiring. The host's
7
+ * hooks (returned by `createChatbotComponents`) stay fully typed inside
8
+ * pages only the input is loose. */
9
+ type AnyApi$1 = any;
11
10
  interface ChatbotBrand {
12
11
  /** Display name shown in the embed header, system prompt, etc. */
13
12
  name: string;
@@ -39,10 +38,10 @@ interface TenantWithCap {
39
38
  chatCostCapUsdMonthly?: number | null;
40
39
  }
41
40
 
42
- /** See `context.ts` — RTK Query's Api generics are invariant, so we accept
43
- * the host's concrete Api shape via `any` parameters. The runtime cast
44
- * inside the function preserves all internal logic. */
45
- type AnyApi = Api<any, any, any, any>;
41
+ /** See `context.ts` — `apiSlice` is `any` on the public surface to dodge
42
+ * RTK Query's `unique symbol` brands. Internal logic still uses the host's
43
+ * baseApi at runtime; only the input type is loose. */
44
+ type AnyApi = any;
46
45
  interface KnowledgeDocument {
47
46
  _id: string;
48
47
  tenantId?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixygon/chatbot-react",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "React + MUI pages for the Pixygon chatbot: knowledge base admin, chat, analytics, embeddable widget.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",