@wisdomai/react 0.0.8
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.
- package/AGENTS.md +111 -0
- package/LICENSE +21 -0
- package/README.md +225 -0
- package/dist/WisdomAuthContext.d.ts +12 -0
- package/dist/WisdomProvider.d.ts +13 -0
- package/dist/client.d.ts +10 -0
- package/dist/dashboard/Dashboard.d.ts +12 -0
- package/dist/dashboard/DashboardContext.d.ts +53 -0
- package/dist/dashboard/DashboardFilters.d.ts +6 -0
- package/dist/dashboard/DashboardHeader.d.ts +4 -0
- package/dist/dashboard/DashboardProvider.d.ts +15 -0
- package/dist/dashboard/DashboardWidget.d.ts +6 -0
- package/dist/dashboard/DashboardWidgets.d.ts +3 -0
- package/dist/dashboard/SdkMarkdown.d.ts +12 -0
- package/dist/dashboard/SdkSummaryWidget.d.ts +5 -0
- package/dist/dashboard/SummaryContent.d.ts +6 -0
- package/dist/dashboard/WidgetActionsMenu.d.ts +6 -0
- package/dist/dashboard/WidgetCard.d.ts +13 -0
- package/dist/dashboard/WisdomChart.d.ts +6 -0
- package/dist/dashboard/WisdomMetric.d.ts +5 -0
- package/dist/dashboard/WisdomTable.d.ts +10 -0
- package/dist/dashboard/WisdomText.d.ts +5 -0
- package/dist/dashboard/WisdomVisualization.d.ts +7 -0
- package/dist/dashboard/exportWidgetAsImage.d.ts +6 -0
- package/dist/dashboard/filterForms/BoolFilterForm.d.ts +8 -0
- package/dist/dashboard/filterForms/CurrentDateInput.d.ts +11 -0
- package/dist/dashboard/filterForms/DateFilterForm.d.ts +21 -0
- package/dist/dashboard/filterForms/EnumFilterForm.d.ts +13 -0
- package/dist/dashboard/filterForms/FilterPill.d.ts +12 -0
- package/dist/dashboard/filterForms/LeftIntervalSelect.d.ts +7 -0
- package/dist/dashboard/filterForms/NextDateInput.d.ts +13 -0
- package/dist/dashboard/filterForms/NumberFilterForm.d.ts +9 -0
- package/dist/dashboard/filterForms/PreviousDateInput.d.ts +15 -0
- package/dist/dashboard/filterForms/QuarterInputField.d.ts +6 -0
- package/dist/dashboard/filterForms/RangeDateInput.d.ts +25 -0
- package/dist/dashboard/filterForms/RelativeDateInput.d.ts +20 -0
- package/dist/dashboard/filterForms/RightIntervalSelect.d.ts +7 -0
- package/dist/dashboard/filterForms/SingleDateInput.d.ts +20 -0
- package/dist/dashboard/filterForms/StringFilterForm.d.ts +18 -0
- package/dist/dashboard/filterForms/WeekInputField.d.ts +6 -0
- package/dist/dashboard/filterForms/YearInputField.d.ts +6 -0
- package/dist/dashboard/filterForms/buildFilter.d.ts +56 -0
- package/dist/dashboard/filterForms/columnInput.d.ts +2 -0
- package/dist/dashboard/filterForms/dateLabels.d.ts +7 -0
- package/dist/dashboard/filterForms/getGranularityOptions.d.ts +5 -0
- package/dist/dashboard/filterForms/granularityLabels.d.ts +2 -0
- package/dist/dashboard/filterForms/useStringFilterSuggestions.d.ts +14 -0
- package/dist/dashboard/useDashboardFilters.d.ts +23 -0
- package/dist/dashboard/useDashboardWidget.d.ts +25 -0
- package/dist/dashboard/useDashboardsList.d.ts +21 -0
- package/dist/dashboard/useSummaryWidget.d.ts +25 -0
- package/dist/dashboard/widgetFilterStateAccessors.d.ts +15 -0
- package/dist/dashboard/widgetStore.d.ts +115 -0
- package/dist/errors.d.ts +16 -0
- package/dist/generated/graphql/gql.d.ts +146 -0
- package/dist/generated/graphql/graphql.d.ts +16533 -0
- package/dist/generated/graphql/index.d.ts +1 -0
- package/dist/graphql/fragments/column.fragment.d.ts +1 -0
- package/dist/graphql/fragments/dashboard-filter-definition.fragment.d.ts +1 -0
- package/dist/graphql/fragments/dashboard-filter-operation.fragment.d.ts +1 -0
- package/dist/graphql/fragments/dashboard-filter-spec.fragment.d.ts +1 -0
- package/dist/graphql/fragments/dashboard-filter-widget-state.fragment.d.ts +1 -0
- package/dist/graphql/fragments/dashboard-pinned-filter.fragment.d.ts +1 -0
- package/dist/graphql/fragments/dashboard-widget-with-data.fragment.d.ts +7 -0
- package/dist/graphql/fragments/dashboard-widget.fragment.d.ts +1 -0
- package/dist/graphql/fragments/dashboard.fragment.d.ts +1 -0
- package/dist/graphql/fragments/expression.fragment.d.ts +1 -0
- package/dist/graphql/fragments/parsed-filter.fragment.d.ts +2 -0
- package/dist/graphql/fragments/visualization.fragment.d.ts +1 -0
- package/dist/graphql/fragments/viz-chart-config.fragment.d.ts +1 -0
- package/dist/graphql/fragments/widget-data.fragment.d.ts +1 -0
- package/dist/graphql/mutations/update-cached-summary.mutation.d.ts +5 -0
- package/dist/graphql/queries/dashboard-list.query.d.ts +4 -0
- package/dist/graphql/queries/dashboard.query.d.ts +4 -0
- package/dist/graphql/queries/domain-filter-columns.query.d.ts +3 -0
- package/dist/graphql/queries/possible-column-literals.query.d.ts +4 -0
- package/dist/graphql/queries/widget-data.query.d.ts +6 -0
- package/dist/graphql/subscriptions/dashboard-summary.subscription.d.ts +9 -0
- package/dist/graphql/subscriptions/update-filter-value.subscription.d.ts +9 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +20234 -0
- package/dist/internal/initHighcharts.d.ts +2 -0
- package/dist/resources/dashboards.d.ts +51 -0
- package/dist/subscriptionTransport.d.ts +42 -0
- package/dist/theme.d.ts +15 -0
- package/dist/transport.d.ts +27 -0
- package/package.json +58 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# @wisdomai/react — agent setup guide
|
|
2
|
+
|
|
3
|
+
Instructions for an AI coding agent integrating `@wisdomai/react` into a React
|
|
4
|
+
app. This SDK renders dashboards authored on the Wisdom platform, with live data
|
|
5
|
+
and interactive filtering. This file is a condensed, actionable companion to
|
|
6
|
+
`README.md` — consult the README for full detail.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @wisdomai/react
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The host app must provide these peer dependencies: `react` (>=18), `react-dom`
|
|
15
|
+
(>=18), `@mui/material` (^7), `@mui/x-date-pickers` (^7), `@emotion/react` (^11),
|
|
16
|
+
`@emotion/styled` (^11), `highcharts` (^12), `highcharts-react-official` (^3),
|
|
17
|
+
`graphql` (^16), `luxon` (^3).
|
|
18
|
+
|
|
19
|
+
`@wisdomai/react` is **ESM-only**. Pure-CommonJS callers must use dynamic
|
|
20
|
+
`import()`.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
### 1. Serve a token endpoint from the app's backend
|
|
25
|
+
|
|
26
|
+
Add `POST /auth-token` on the **same origin** as the app, returning
|
|
27
|
+
`{ jwt: string, baseUrl: string }`. The backend exchanges a long-lived,
|
|
28
|
+
server-side access token for a short-lived browser JWT.
|
|
29
|
+
|
|
30
|
+
Hard requirements — do not skip:
|
|
31
|
+
|
|
32
|
+
- **Protect the route with the app's own authentication.** Only signed-in users
|
|
33
|
+
may call it; an open `/auth-token` lets anyone mint a Wisdom JWT for the org.
|
|
34
|
+
- **`baseUrl` must include the scheme** (e.g. `https://<tenant>.wisdom.ai`). A
|
|
35
|
+
bare host throws `SubscriptionTransport: unrecognized baseUrl protocol`.
|
|
36
|
+
- Keep the access token (`WISDOM_ACCESS_TOKEN`) server-side only — never send it
|
|
37
|
+
to the browser.
|
|
38
|
+
|
|
39
|
+
Zero-dependency backend (any framework) via the GraphQL `exchangeAccessToken`
|
|
40
|
+
query:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// Pseudocode — wrap in the app's existing auth/session check first.
|
|
44
|
+
app.post('/auth-token', requireAuth, async () => {
|
|
45
|
+
const res = await fetch(`${process.env.WISDOM_BASE_URL}/graphql`, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: { 'Content-Type': 'application/json' },
|
|
48
|
+
body: JSON.stringify({
|
|
49
|
+
query: 'query Exchange($t: String!) { exchangeAccessToken(accessToken: $t) }',
|
|
50
|
+
variables: { t: process.env.WISDOM_ACCESS_TOKEN },
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
const { data } = await res.json();
|
|
54
|
+
return { jwt: data.exchangeAccessToken, baseUrl: process.env.WISDOM_BASE_URL };
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`exchangeAccessToken` issues an **org-level** JWT. When each end user should see
|
|
59
|
+
only their own data, mint a **per-user** JWT with `impersonateUser` instead and
|
|
60
|
+
return it as `jwt`. The node SDK's `getAuthToken()` wraps `exchangeAccessToken`
|
|
61
|
+
if a Node helper is preferred.
|
|
62
|
+
|
|
63
|
+
### 2. Wrap the app
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { WisdomProvider } from '@wisdomai/react';
|
|
67
|
+
|
|
68
|
+
function App() {
|
|
69
|
+
return (
|
|
70
|
+
<WisdomProvider>
|
|
71
|
+
<YourDashboards />
|
|
72
|
+
</WisdomProvider>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`<WisdomProvider>` calls `POST /auth-token`, exposes the client via context, and
|
|
78
|
+
silently refreshes the JWT before it expires. Pass a `getAuthToken` callback to
|
|
79
|
+
bypass the endpoint when the app already holds a Wisdom JWT. Pass a `theme` prop
|
|
80
|
+
to style SDK-rendered components.
|
|
81
|
+
|
|
82
|
+
### 3. Render dashboards
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { Dashboard, DashboardWidget, useWisdomClient } from '@wisdomai/react';
|
|
86
|
+
|
|
87
|
+
// A whole dashboard:
|
|
88
|
+
<Dashboard dashboardId="dashboard-id-123" />
|
|
89
|
+
|
|
90
|
+
// Or a single widget:
|
|
91
|
+
<DashboardWidget dashboardId="dashboard-id-123" widgetId="widget-id-456" />
|
|
92
|
+
|
|
93
|
+
// Or the typed client directly (null until auth succeeds):
|
|
94
|
+
const client = useWisdomClient();
|
|
95
|
+
const dashboard = await client?.dashboards.get('dashboard-id-123');
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## What renders
|
|
99
|
+
|
|
100
|
+
- **Visualizations**: all chart types, data tables (server-side paging),
|
|
101
|
+
single- and multi-value metric cards, and text.
|
|
102
|
+
- **Filters**: interactive string, numeric (integer/float), enum, and date —
|
|
103
|
+
via row-click, the filter drawer, or programmatically with `setFilterValue`.
|
|
104
|
+
|
|
105
|
+
## Common errors
|
|
106
|
+
|
|
107
|
+
- `SubscriptionTransport: unrecognized baseUrl protocol` → `baseUrl` is missing
|
|
108
|
+
the `https://` scheme.
|
|
109
|
+
- `WisdomAuthError` → JWT rejected/expired; the provider's `refresh()` re-fetches.
|
|
110
|
+
- `WisdomDashboardNotFoundError` → the dashboard id doesn't exist or the user
|
|
111
|
+
lacks access.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wisdom AI, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# @wisdomai/react
|
|
2
|
+
|
|
3
|
+
React SDK for Wisdom AI. Ships a React context provider, auth hook, and a typed GraphQL client:
|
|
4
|
+
|
|
5
|
+
- `<WisdomProvider>` — fetches a short-lived JWT + the tenant `baseUrl` from your backend's `/auth-token` endpoint and exposes them via context. Accepts an optional `theme` prop (see [Theming](#theming)) and an optional `getAuthToken` callback to bypass the default endpoint (see [Custom auth token source](#custom-auth-token-source)).
|
|
6
|
+
- `useWisdomAuth()` — hook returning `{ jwt, baseUrl, client, isLoading, error, refresh }`.
|
|
7
|
+
- `useWisdomClient()` — hook returning a ready-to-use `WisdomClient` (null until auth succeeds).
|
|
8
|
+
- `useWisdomTheme()` — hook returning the active `WisdomTheme` (the prop passed to `<WisdomProvider>`, or the default).
|
|
9
|
+
- `WisdomClient` — typed GraphQL client. Currently exposes `client.dashboards.get(id)`.
|
|
10
|
+
|
|
11
|
+
## Capabilities
|
|
12
|
+
|
|
13
|
+
The SDK renders dashboards authored on the Wisdom platform directly in your React app — live data, your theme, and interactive filtering. Render a full dashboard with `<Dashboard>`, or drop in individual widgets with `<DashboardWidget>`.
|
|
14
|
+
|
|
15
|
+
- **Visualizations** — all chart types, data tables (with server-side paging), single- and multi-value metric cards, and text.
|
|
16
|
+
- **Filters** — interactive string, numeric (integer and float), enum, and date filters, driven by row-click, the filter drawer, or programmatically via `setFilterValue` (see [Dashboard filters](#dashboard-filters)).
|
|
17
|
+
|
|
18
|
+
The layout, data, and filter specs come from the dashboard you built in Wisdom; the SDK renders them client-side and keeps them in sync with the platform.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @wisdomai/react
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Provide these peer dependencies from your app: `react` (>=18), `react-dom` (>=18), `@mui/material` (^7), `@mui/x-date-pickers` (^7), `@emotion/react` (^11), `@emotion/styled` (^11), `highcharts` (^12), `highcharts-react-official` (^3), `graphql` (^16), `luxon` (^3).
|
|
27
|
+
|
|
28
|
+
> Using an AI coding agent? The package ships an [`AGENTS.md`](./AGENTS.md) with condensed, copy-ready setup steps — point your agent at it to scaffold the integration.
|
|
29
|
+
|
|
30
|
+
## Quick start: authentication
|
|
31
|
+
|
|
32
|
+
The SDK never sees your long-lived Wisdom access token. Your backend mints a short-lived JWT from that access token and serves it; `<WisdomProvider>` fetches the JWT in the browser and refreshes it before it expires.
|
|
33
|
+
|
|
34
|
+
### 1. Serve a token endpoint from your backend
|
|
35
|
+
|
|
36
|
+
Expose `POST /auth-token` on the **same origin** as your app, returning `{ jwt, baseUrl }`. Keep `WISDOM_ACCESS_TOKEN` server-side only.
|
|
37
|
+
|
|
38
|
+
> **Protect this route with your app's own authentication.** It exchanges your server-side access token for browser credentials, so only signed-in users of your app should be able to call it — an open `/auth-token` lets anyone mint a Wisdom JWT for your org. The examples below omit that guard for brevity; add your existing session/authz check before the exchange (and mint a per-user token with [`impersonateUser`](https://docs.wisdom.ai/integrations/graphql-api/mutations/auth/impersonate-user) when each user should see only their own data).
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { WisdomAI } from '@wisdomai/node';
|
|
42
|
+
import Fastify from 'fastify';
|
|
43
|
+
|
|
44
|
+
const wisdom = new WisdomAI({
|
|
45
|
+
accessToken: process.env.WISDOM_ACCESS_TOKEN!,
|
|
46
|
+
baseUrl: process.env.WISDOM_BASE_URL!,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const app = Fastify();
|
|
50
|
+
|
|
51
|
+
// Returns { jwt, baseUrl } — a short-lived token plus your tenant API URL.
|
|
52
|
+
app.post('/auth-token', () => wisdom.getAuthToken());
|
|
53
|
+
|
|
54
|
+
await app.listen({ port: 3000 });
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Not on Node?** `getAuthToken()` is just a thin wrapper over the GraphQL [`exchangeAccessToken`](https://docs.wisdom.ai/integrations/graphql-api/queries/auth/exchange-access-token) query, so any backend can do the same exchange — `POST {baseUrl}/graphql` with your access key, then pair the returned JWT with your tenant `baseUrl`:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// exchangeAccessToken(accessToken: String!): String! — returns a bare ~60-min JWT.
|
|
61
|
+
app.post('/auth-token', async () => {
|
|
62
|
+
const res = await fetch(`${process.env.WISDOM_BASE_URL}/graphql`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
query: 'query Exchange($t: String!) { exchangeAccessToken(accessToken: $t) }',
|
|
67
|
+
variables: { t: process.env.WISDOM_ACCESS_TOKEN },
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
const { data } = await res.json();
|
|
71
|
+
return { jwt: data.exchangeAccessToken, baseUrl: process.env.WISDOM_BASE_URL };
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`exchangeAccessToken` issues an **org-level** JWT (the same identity for every caller). For multi-user production embeds where each end user should see only their own data, mint a **per-user** JWT with [`impersonateUser`](https://docs.wisdom.ai/integrations/graphql-api/mutations/auth/impersonate-user) instead and return that as `jwt` — the provider contract (`{ jwt, baseUrl }`) is the same.
|
|
76
|
+
|
|
77
|
+
### 2. Wrap your app and read a dashboard
|
|
78
|
+
|
|
79
|
+
`<WisdomProvider>` calls `POST /auth-token`, exposes a ready-to-use client via `useWisdomClient()` (null until auth succeeds), and silently re-fetches the JWT shortly before it expires and when the tab regains focus.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { WisdomProvider, useWisdomClient } from '@wisdomai/react';
|
|
83
|
+
import { useEffect, useState } from 'react';
|
|
84
|
+
|
|
85
|
+
function App() {
|
|
86
|
+
return (
|
|
87
|
+
<WisdomProvider>
|
|
88
|
+
<DashboardName id="dashboard-id-123" />
|
|
89
|
+
</WisdomProvider>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function DashboardName({ id }: { id: string }) {
|
|
94
|
+
const client = useWisdomClient();
|
|
95
|
+
const [name, setName] = useState<string | null>(null);
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (!client) return;
|
|
99
|
+
client.dashboards.get(id).then((dashboard) => setName(dashboard.name));
|
|
100
|
+
}, [client, id]);
|
|
101
|
+
|
|
102
|
+
if (!client) return <p>Authenticating…</p>;
|
|
103
|
+
return <p>{name ?? 'Loading…'}</p>;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If your app already holds a Wisdom JWT (e.g. the SDK is mounted inside an authenticated app), skip the endpoint entirely with the [`getAuthToken` callback](#custom-auth-token-source). For the exact endpoint contract and error handling, see [Requirements on the partner backend](#requirements-on-the-partner-backend).
|
|
108
|
+
|
|
109
|
+
## Usage
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { WisdomProvider, useWisdomClient, WisdomDashboardNotFoundError } from '@wisdomai/react';
|
|
113
|
+
import { useEffect, useState } from 'react';
|
|
114
|
+
|
|
115
|
+
function App() {
|
|
116
|
+
return (
|
|
117
|
+
<WisdomProvider>
|
|
118
|
+
<DashboardView />
|
|
119
|
+
</WisdomProvider>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function DashboardView() {
|
|
124
|
+
const client = useWisdomClient();
|
|
125
|
+
const [name, setName] = useState<string | null>(null);
|
|
126
|
+
const [notFound, setNotFound] = useState(false);
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (!client) return;
|
|
130
|
+
client.dashboards
|
|
131
|
+
.get('dashboard-id-123')
|
|
132
|
+
.then((dashboard) => setName(dashboard.name))
|
|
133
|
+
.catch((err) => {
|
|
134
|
+
if (err instanceof WisdomDashboardNotFoundError) setNotFound(true);
|
|
135
|
+
else throw err;
|
|
136
|
+
});
|
|
137
|
+
}, [client]);
|
|
138
|
+
|
|
139
|
+
if (!client) return <p>Authenticating…</p>;
|
|
140
|
+
if (notFound) return <p>Dashboard not found</p>;
|
|
141
|
+
return <p>{name ?? 'Loading…'}</p>;
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Requirements on the partner backend
|
|
146
|
+
|
|
147
|
+
`WisdomProvider` sends `POST /auth-token` to the same origin and expects a JSON response of shape `{ jwt: string, baseUrl: string }`. This matches the `sdk-playground` Fastify server exactly. If your backend hosts the token endpoint at a different path, you'll need a dev proxy or a reverse proxy in front of your app.
|
|
148
|
+
|
|
149
|
+
> **`baseUrl` must include the scheme.** Use the full origin, e.g. `https://canary.gowisdom.ai` — not a bare host (`canary.gowisdom.ai`). The SDK derives its WebSocket endpoint from `baseUrl`, and a value without an `http(s)://` prefix throws `SubscriptionTransport: unrecognized baseUrl protocol`.
|
|
150
|
+
|
|
151
|
+
### Custom auth token source
|
|
152
|
+
|
|
153
|
+
If your app already has access to a JWT (e.g., the SDK is mounted inside an app that holds the user's session), pass a `getAuthToken` callback to bypass the default `POST /auth-token` fetch. The callback must resolve to `{ jwt, baseUrl }` (typed as `WisdomAuthTokenResult`), where `baseUrl` is the Wisdom tenant API URL (the same value the partner backend would otherwise return from `/auth-token`) — not the host app's origin, unless the host app reverse-proxies `/graphql` and the WS endpoints to Wisdom.
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import { WisdomProvider, type WisdomAuthTokenResult } from '@wisdomai/react';
|
|
157
|
+
import { useCallback } from 'react';
|
|
158
|
+
import { getAuthToken } from '@/services/auth';
|
|
159
|
+
|
|
160
|
+
const WISDOM_BASE_URL = 'https://your-tenant.wisdom.ai';
|
|
161
|
+
|
|
162
|
+
function App() {
|
|
163
|
+
const resolveAuth = useCallback(async (): Promise<WisdomAuthTokenResult> => {
|
|
164
|
+
const jwt = await getAuthToken();
|
|
165
|
+
return { jwt, baseUrl: WISDOM_BASE_URL };
|
|
166
|
+
}, []);
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<WisdomProvider getAuthToken={resolveAuth}>
|
|
170
|
+
<DashboardView />
|
|
171
|
+
</WisdomProvider>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
When `getAuthToken` is set, the SDK does not call `POST /auth-token` — your callback is the sole token source. Throw from the callback to surface an auth error via `useWisdomAuth().error`. Wrap the callback in `useCallback` (or define it at module scope) so its identity is stable across renders; the provider re-fetches whenever the callback reference changes.
|
|
177
|
+
|
|
178
|
+
## Theming
|
|
179
|
+
|
|
180
|
+
Pass a `theme` prop to `<WisdomProvider>` to control the colors, fonts, and chart palette used by SDK-rendered components (`Dashboard`, `DashboardWidgets`, `DashboardFilters`, `WisdomChart`). The theme is exposed to your own components via `useWisdomTheme()` so partner UI can stay visually consistent.
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
import { WisdomProvider, type WisdomTheme } from '@wisdomai/react';
|
|
184
|
+
|
|
185
|
+
const theme: WisdomTheme = {
|
|
186
|
+
primaryTextColor: '#ffffff',
|
|
187
|
+
secondaryTextColor: '#a0a0a0',
|
|
188
|
+
background: '#1a1a1a',
|
|
189
|
+
border: '#2a2a2a',
|
|
190
|
+
fontFamily: 'Inter, sans-serif',
|
|
191
|
+
chartColors: ['#4f46e5', '#10b981', '#f59e0b'],
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
<WisdomProvider theme={theme}>
|
|
195
|
+
<DashboardView />
|
|
196
|
+
</WisdomProvider>;
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`fontFamily` and `chartColors` are optional; everything else is required. If `theme` is omitted, a light default is used. The SDK does not ship light/dark presets — partner apps own their full color system.
|
|
200
|
+
|
|
201
|
+
You can pass `theme` as an inline object literal without causing chart re-renders on every parent render; the provider stabilizes its identity by content.
|
|
202
|
+
|
|
203
|
+
## Dashboard filters
|
|
204
|
+
|
|
205
|
+
Interactive filtering (row-click, drawer, or programmatic) goes through `setFilterValue(filterId, filter)` from the dashboard context. Two parts of the contract are easy to get wrong:
|
|
206
|
+
|
|
207
|
+
### `filterId` is the lookup key
|
|
208
|
+
|
|
209
|
+
`setFilterValue` matches the `filterId` against `dashboard.filterSpecs[*].filterId` exactly. A `filterId` that doesn't match any spec throws `Filter not found on dashboard: <filterId>`. The same `filterId` keys highlight/active-filter lookups, so it must be the spec's `filterId` verbatim — not the `parameterName`, `displayName`, or column name.
|
|
210
|
+
|
|
211
|
+
### The `columnRef` vs `column` shape asymmetry
|
|
212
|
+
|
|
213
|
+
A `FilterSpec` exposes its column under **`spec.columnRefs[0]`** (a `ColumnRef`). But the serialized filter `lhs` reads the column from **`lhs.expression.flattened[0].column`** (a `DataframeColumn`) when building the GraphQL input. These are different shapes at different layers — the SDK maps between them in `buildLhsFromSpec`. If you hand-assemble an `lhs`, you must populate `expression.flattened[0].column`; leaving it empty throws a `malformed lhs` error naming the `filterId` (it does not silently render a blank chart).
|
|
214
|
+
|
|
215
|
+
### Filtering a column the widgets don't query
|
|
216
|
+
|
|
217
|
+
A filter only changes a widget if that widget's underlying query references the filtered column (`WidgetFilterStatus.WIDGET_FILTER_STATUS_ADDED`). If you set a filter whose column no visible widget queries — common for chat-authored widgets whose SQL never selected the entity column — `setFilterValue` resolves successfully but nothing visibly changes. The SDK emits a `console.warn` in this case so the no-op is observable during development.
|
|
218
|
+
|
|
219
|
+
## Errors
|
|
220
|
+
|
|
221
|
+
All errors thrown by the client extend `WisdomError`:
|
|
222
|
+
|
|
223
|
+
- `WisdomAuthError` — JWT rejected or expired (HTTP 401/403, or GraphQL `UNAUTHENTICATED` / `INVALID_TOKEN`). Consumers should call `refresh()` to re-fetch a token.
|
|
224
|
+
- `WisdomDashboardNotFoundError` — `dashboards.get(id)` could not return a dashboard because the ID does not exist or the caller lacks access (server emits `NOT_FOUND` or `FORBIDDEN`).
|
|
225
|
+
- `WisdomError` — everything else (network failure, 5xx, malformed response, uncategorized backend errors). Carries an optional `cause` and, for GraphQL-level failures, an `errors` array.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WisdomClient } from './client';
|
|
2
|
+
export interface WisdomAuthContextValue {
|
|
3
|
+
jwt: string | null;
|
|
4
|
+
baseUrl: string | null;
|
|
5
|
+
client: WisdomClient | null;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
error: Error | null;
|
|
8
|
+
refresh: () => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare const WisdomAuthContext: import("react").Context<WisdomAuthContextValue | null>;
|
|
11
|
+
export declare function useWisdomAuth(): WisdomAuthContextValue;
|
|
12
|
+
export declare function useWisdomClient(): WisdomClient | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { WisdomTheme } from './theme';
|
|
3
|
+
export interface WisdomAuthTokenResult {
|
|
4
|
+
jwt: string;
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
}
|
|
7
|
+
interface WisdomProviderProps {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
theme?: Partial<WisdomTheme>;
|
|
10
|
+
getAuthToken?: () => Promise<WisdomAuthTokenResult>;
|
|
11
|
+
}
|
|
12
|
+
export declare function WisdomProvider({ children, theme, getAuthToken }: WisdomProviderProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DashboardsAPI } from './resources/dashboards';
|
|
2
|
+
import { SubscriptionTransport, type SubscriptionTransportOptions } from './subscriptionTransport';
|
|
3
|
+
import { type TransportOptions } from './transport';
|
|
4
|
+
export type WisdomClientOptions = TransportOptions & Partial<Pick<SubscriptionTransportOptions, 'webSocketImpl'>>;
|
|
5
|
+
export declare class WisdomClient {
|
|
6
|
+
readonly dashboards: DashboardsAPI;
|
|
7
|
+
readonly subscriptions: SubscriptionTransport;
|
|
8
|
+
constructor(options: WisdomClientOptions);
|
|
9
|
+
dispose(): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DashboardScope } from '../generated/graphql/graphql';
|
|
2
|
+
import type { Dashboard as DashboardData } from '../resources/dashboards';
|
|
3
|
+
type DashboardProps = {
|
|
4
|
+
dashboardId: string;
|
|
5
|
+
dashboard?: never;
|
|
6
|
+
scope?: DashboardScope;
|
|
7
|
+
} | {
|
|
8
|
+
dashboardId?: never;
|
|
9
|
+
dashboard: DashboardData;
|
|
10
|
+
};
|
|
11
|
+
export declare function Dashboard(props: DashboardProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ParsedVisualizationFilter } from '@wisdomai/visualization/sdk-filters';
|
|
2
|
+
import type { Dashboard, DomainFilterColumn } from '../resources/dashboards';
|
|
3
|
+
import type { WidgetStore } from './widgetStore';
|
|
4
|
+
export interface DashboardActions {
|
|
5
|
+
refetch: () => Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Apply a filter value (or pass `null` to clear). Resolves when the
|
|
8
|
+
* server has finished re-executing all affected widgets.
|
|
9
|
+
*
|
|
10
|
+
* Throws if the dashboard has `useFiltersV2: false` (v1 is not supported
|
|
11
|
+
* by the SDK).
|
|
12
|
+
*/
|
|
13
|
+
setFilterValue: (filterId: string, filter: ParsedVisualizationFilter | null) => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export interface LastRefreshedFields {
|
|
16
|
+
/**
|
|
17
|
+
* Earliest `min(visualization.collectedAt ?? dataRefreshedAt)` across the
|
|
18
|
+
* dashboard's visualization widgets, or `null` while widgets are still
|
|
19
|
+
* loading or no visualization widget exposes a usable timestamp. Reflects
|
|
20
|
+
* data staleness, not the last refresh action.
|
|
21
|
+
*/
|
|
22
|
+
lastRefreshedAt: Date | null;
|
|
23
|
+
/** True once every visualization widget has settled to `success` or `error`. */
|
|
24
|
+
allWidgetsReady: boolean;
|
|
25
|
+
}
|
|
26
|
+
export type DashboardContextValue = ({
|
|
27
|
+
status: 'loading';
|
|
28
|
+
dashboard: Dashboard | null;
|
|
29
|
+
error: null;
|
|
30
|
+
activeFilterId: null;
|
|
31
|
+
widgetStore: WidgetStore;
|
|
32
|
+
} & LastRefreshedFields & DashboardActions) | ({
|
|
33
|
+
status: 'error';
|
|
34
|
+
dashboard: null;
|
|
35
|
+
error: Error;
|
|
36
|
+
activeFilterId: null;
|
|
37
|
+
widgetStore: WidgetStore;
|
|
38
|
+
} & LastRefreshedFields & DashboardActions) | ({
|
|
39
|
+
status: 'success';
|
|
40
|
+
dashboard: Dashboard;
|
|
41
|
+
error: null;
|
|
42
|
+
/** Filter currently being mutated, or null when idle. */
|
|
43
|
+
activeFilterId: string | null;
|
|
44
|
+
widgetStore: WidgetStore;
|
|
45
|
+
/**
|
|
46
|
+
* Domain columns (with allowed-value metadata) for the dashboard's
|
|
47
|
+
* filter specs, fetched eagerly by domainId. Empty until that fetch
|
|
48
|
+
* resolves; used to classify fresh enum filters as closed vs. dynamic.
|
|
49
|
+
*/
|
|
50
|
+
filterColumns: DomainFilterColumn[];
|
|
51
|
+
} & LastRefreshedFields & DashboardActions);
|
|
52
|
+
export declare const DashboardContext: import("react").Context<DashboardContextValue | null>;
|
|
53
|
+
export declare function useDashboard(): DashboardContextValue;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { DashboardScope } from '../generated/graphql/graphql';
|
|
3
|
+
import type { Dashboard } from '../resources/dashboards';
|
|
4
|
+
type DashboardProviderProps = {
|
|
5
|
+
dashboardId: string;
|
|
6
|
+
dashboard?: never;
|
|
7
|
+
scope?: DashboardScope;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
} | {
|
|
10
|
+
dashboardId?: never;
|
|
11
|
+
dashboard: Dashboard;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
export declare function DashboardProvider(props: DashboardProviderProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface DashboardWidgetProps {
|
|
2
|
+
widgetId: string;
|
|
3
|
+
/** Optional — when omitted, reads the id from `<DashboardProvider>`. */
|
|
4
|
+
dashboardId?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function DashboardWidget({ widgetId, dashboardId: dashboardIdProp }: DashboardWidgetProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Components } from 'react-markdown';
|
|
2
|
+
export interface SdkMarkdownProps {
|
|
3
|
+
text: string;
|
|
4
|
+
components?: Components;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Lean markdown renderer bundled into the SDK. Generic on purpose — no math,
|
|
8
|
+
* no app-internal link routing, no LLM-output preprocessing (those live in the
|
|
9
|
+
* frontend's LlmMarkdown). `remark-gfm` covers tables/strikethrough/autolinks,
|
|
10
|
+
* which dashboard summaries do use.
|
|
11
|
+
*/
|
|
12
|
+
export declare function SdkMarkdown({ text, components }: SdkMarkdownProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface SummaryContentProps {
|
|
2
|
+
summary: string;
|
|
3
|
+
/** True while a fresh summary is streaming — keeps the view pinned to the newest text. */
|
|
4
|
+
enableAutoScroll: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function SummaryContent({ summary, enableAutoScroll }: SummaryContentProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export interface WidgetActionsMenuProps {
|
|
3
|
+
captureRef: RefObject<HTMLElement | null>;
|
|
4
|
+
title: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function WidgetActionsMenu({ captureRef, title }: WidgetActionsMenuProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface WidgetCardProps {
|
|
3
|
+
title: string;
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
headerActions?: ReactNode;
|
|
6
|
+
titleLoading?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function WidgetCard({ title, children, headerActions, titleLoading }: WidgetCardProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function WidgetPlaceholderBody(): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function WidgetErrorBody({ message }: {
|
|
11
|
+
message: string;
|
|
12
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function WidgetLoadingBody(): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Visualization } from '@wisdomai/visualization';
|
|
2
|
+
export interface WisdomChartProps {
|
|
3
|
+
visualization: Visualization;
|
|
4
|
+
}
|
|
5
|
+
/** @deprecated Use `<WisdomVisualization>` — it routes to the right renderer based on `visualization.type`. */
|
|
6
|
+
export declare function WisdomChart({ visualization }: WisdomChartProps): import("@emotion/react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Visualization } from '@wisdomai/visualization';
|
|
2
|
+
export interface WisdomTableProps {
|
|
3
|
+
visualization: Visualization;
|
|
4
|
+
/**
|
|
5
|
+
* Server-side paging; omitted when the table isn't backed by a widget fetch.
|
|
6
|
+
* Resolves to the number of rows loaded after the fetch.
|
|
7
|
+
*/
|
|
8
|
+
fetchMore?: (nextLimit: number) => Promise<number>;
|
|
9
|
+
}
|
|
10
|
+
export declare function WisdomTable({ visualization, fetchMore }: WisdomTableProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Visualization } from '@wisdomai/visualization';
|
|
2
|
+
export interface WisdomVisualizationProps {
|
|
3
|
+
visualization: Visualization;
|
|
4
|
+
/** Server-side paging for table widgets; omitted for charts/metrics/text. */
|
|
5
|
+
fetchMore?: (nextLimit: number) => Promise<number>;
|
|
6
|
+
}
|
|
7
|
+
export declare function WisdomVisualization({ visualization, fetchMore }: WisdomVisualizationProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type ImageFormat = 'png' | 'jpg';
|
|
2
|
+
export interface ExportImageOptions {
|
|
3
|
+
backgroundColor?: string;
|
|
4
|
+
pixelRatio?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function exportWidgetAsImage(element: HTMLElement | null, baseName: string, format: ImageFormat, options?: ExportImageOptions): Promise<void>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BoolFilter } from '@wisdomai/visualization/sdk-filters';
|
|
2
|
+
interface BoolFilterFormProps {
|
|
3
|
+
filter: BoolFilter;
|
|
4
|
+
close: () => void;
|
|
5
|
+
onSubmit: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function BoolFilterForm({ filter, close, onSubmit }: BoolFilterFormProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DataframeFunctionType, DateFilter } from '@wisdomai/visualization/sdk-filters';
|
|
2
|
+
import { DateFilterType } from './DateFilterForm';
|
|
3
|
+
type Props = {
|
|
4
|
+
currentDateType: DataframeFunctionType;
|
|
5
|
+
filterType: DateFilterType | undefined;
|
|
6
|
+
setFilterType: (filterType: DateFilterType) => void;
|
|
7
|
+
setCurrentDateType: (currentDateType: DataframeFunctionType) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare const getDefaultCurrentDateType: (filter: DateFilter) => DataframeFunctionType;
|
|
10
|
+
export declare const CurrentDateInput: ({ currentDateType, filterType, setFilterType, setCurrentDateType }: Props) => import("@emotion/react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|