@uidu/skills 0.5.0 → 0.6.1
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/package.json +1 -1
- package/skills/uidu/SKILL.md +275 -66
package/package.json
CHANGED
package/skills/uidu/SKILL.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: uidu
|
|
3
|
-
description: Use when building on the uidu platform — a public website or an app that runs inside uidu (a custom app in a Space or workspace); reading CMS pages, events, stories, donations, help center, forms; rendering WYSIWYG content; scaffolding projects; OR provisioning/authoring uidu content (create/update/delete) from the terminal via the @uidu/cli (`uidu`). Triggers on any task involving uidu, the @uidu/client SDK, @uidu/react bindings, or the uidu CLI.
|
|
3
|
+
description: Use when building on the uidu platform — a public website or an app that runs inside uidu (a custom app in a Space or workspace); reading CMS pages, events, stories, donations, help center, forms, or the workspace's own data (goals/OKRs, contacts, deals, people); rendering WYSIWYG content; scaffolding projects; OR provisioning/authoring uidu content (create/update/delete) from the terminal via the @uidu/cli (`uidu`). Triggers on any task involving uidu, the @uidu/client SDK, @uidu/react bindings, or the uidu CLI.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: uidu
|
|
7
|
-
version:
|
|
7
|
+
version: '0.6.0'
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# uidu SDK
|
|
@@ -13,7 +13,7 @@ The uidu SDK is a TypeScript GraphQL toolkit for building websites and apps powe
|
|
|
13
13
|
|
|
14
14
|
- **`@uidu/client`** — framework-agnostic GraphQL client. Safe for server-side use (RSC, route handlers, server actions, scripts).
|
|
15
15
|
- **`@uidu/app-bridge`** — the browser half of a custom app: the handshake with the uidu page that frames it, and its short-lived session token. Only for apps that run inside uidu.
|
|
16
|
-
- **`@uidu/react`** — React hooks (`useUiduClient`, `useQuery`, `useFields`, `toText`, `useUiduApp`) and components (`<UiduAppProvider>`, `<PageBlocks>`, `<BlockRenderer>`, `<RichText>`, `<DynamicForm>`). That list is the **complete** public surface — there is no `useForm`, `usePage` or `useChannel`; for anything else, call a `@uidu/client` function.
|
|
16
|
+
- **`@uidu/react`** — React hooks (`useUiduClient`, `useQuery`, `useFields`, `toText`, `useUiduApp`) and components (`<UiduAppProvider>`, `<PageBlocks>`, `<BlockRenderer>`, `<RichText>`, `<DynamicForm>`). That list is the **complete** public surface — there is no `useForm`, `usePage` or `useChannel`; for anything else, call a `@uidu/client` function. `@uidu/client` has one for each entity uidu already holds (goals, contacts, deals, people, events… — see [Workspace data](#workspace-data)): look there before writing a query or creating a Model.
|
|
17
17
|
|
|
18
18
|
`@uidu/api.js` is a legacy bridge package and is **deprecated** — use `@uidu/react` directly.
|
|
19
19
|
|
|
@@ -31,12 +31,12 @@ Use this skill when the user is:
|
|
|
31
31
|
|
|
32
32
|
## Package Map
|
|
33
33
|
|
|
34
|
-
| Package
|
|
35
|
-
|
|
36
|
-
| `@uidu/client`
|
|
37
|
-
| `@uidu/react`
|
|
38
|
-
| `@uidu/app-bridge` | An app that runs inside uidu (custom app)
|
|
39
|
-
| `@uidu/api.js`
|
|
34
|
+
| Package | When to use | Notes |
|
|
35
|
+
| ------------------ | -------------------------------------------------- | ---------------------------------------------- |
|
|
36
|
+
| `@uidu/client` | Server-side queries (RSC, server actions, scripts) | Framework-agnostic |
|
|
37
|
+
| `@uidu/react` | React hooks, providers, and components | Some hooks are client-only |
|
|
38
|
+
| `@uidu/app-bridge` | An app that runs inside uidu (custom app) | Browser only; in React use `<UiduAppProvider>` |
|
|
39
|
+
| `@uidu/api.js` | **Do not use for new code.** | Deprecated; re-exports `@uidu/react` |
|
|
40
40
|
|
|
41
41
|
Install:
|
|
42
42
|
|
|
@@ -72,22 +72,62 @@ uidu workspace credentials --json # api key + secret
|
|
|
72
72
|
uidu create my-app -t events # delegates to create-uidu-app
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
Entities
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
Entities and the verbs each supports (generated from the CLI's registry — a verb marked `—`
|
|
76
|
+
fails with `does not support <verb>`):
|
|
77
|
+
|
|
78
|
+
<!-- BEGIN sdk:cli-resources — generated by scripts/sdk-manifest.mjs, do not edit -->
|
|
79
|
+
|
|
80
|
+
| Entity | list | get | create | update | delete |
|
|
81
|
+
| --------------------- | :--: | :-: | :----: | :----: | :----: |
|
|
82
|
+
| `events` | ✓ | ✓ | ✓ | ✓ | — |
|
|
83
|
+
| `attendances` | — | — | ✓ | — | — |
|
|
84
|
+
| `stories` | ✓ | ✓ | ✓ | ✓ | — |
|
|
85
|
+
| `donations` | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
86
|
+
| `courses` | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
87
|
+
| `forms` | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
88
|
+
| `contacts` | ✓ | ✓ | ✓ | — | ✓ |
|
|
89
|
+
| `deals` | ✓ | ✓ | ✓ | ✓ | — |
|
|
90
|
+
| `goals` | ✓ | ✓ | — | ✓ | — |
|
|
91
|
+
| `timeframes` | ✓ | — | — | — | — |
|
|
92
|
+
| `employees` | ✓ | — | ✓ | ✓ | — |
|
|
93
|
+
| `employments` | — | — | ✓ | ✓ | ✓ |
|
|
94
|
+
| `offices` | ✓ | — | — | — | — |
|
|
95
|
+
| `roles` | ✓ | — | — | — | — |
|
|
96
|
+
| `ccnls` | ✓ | — | ✓ | — | — |
|
|
97
|
+
| `bookings` | ✓ | ✓ | — | — | — |
|
|
98
|
+
| `calls` | ✓ | ✓ | — | — | — |
|
|
99
|
+
| `jobs` | ✓ | ✓ | — | — | — |
|
|
100
|
+
| `applications` | ✓ | — | ✓ | — | — |
|
|
101
|
+
| `campaigns` | ✓ | ✓ | — | — | — |
|
|
102
|
+
| `kb-collections` | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
103
|
+
| `kb-articles` | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
104
|
+
| `channel` | ✓ | ✓ | ✓ | ✓ | ✓ |
|
|
105
|
+
| `time-clocks` | — | — | ✓ | — | — |
|
|
106
|
+
| `compensations` | — | — | ✓ | ✓ | ✓ |
|
|
107
|
+
| `tasks` | — | — | ✓ | ✓ | ✓ |
|
|
108
|
+
| `benefits` | — | — | ✓ | — | — |
|
|
109
|
+
| `benefit-enrollments` | — | — | ✓ | — | — |
|
|
110
|
+
| `notes` | — | — | ✓ | ✓ | ✓ |
|
|
111
|
+
| `projects` | ✓ | — | — | — | — |
|
|
112
|
+
| `sites` | — | — | ✓ | — | — |
|
|
113
|
+
| `spaces` | — | — | ✓ | ✓ | ✓ |
|
|
114
|
+
|
|
115
|
+
<!-- END sdk:cli-resources -->
|
|
116
|
+
|
|
117
|
+
Anything the backend exposes as a tool-flagged action but the CLI has no noun for yet:
|
|
118
|
+
`uidu tools call <ActionKey> --attributes '<json>'` (associations as GlobalIDs). Config resolves
|
|
119
|
+
**flags → env (`UIDU_*`) → `~/.uidu/config.json`** (written by `uidu login`).
|
|
80
120
|
|
|
81
121
|
## Who the app acts for — one rule
|
|
82
122
|
|
|
83
|
-
What an app
|
|
123
|
+
What an app _looks like_ (landing page, blog, dashboard, booking tool) changes nothing.
|
|
84
124
|
What changes the code is **who the request acts for**:
|
|
85
125
|
|
|
86
|
-
| Operation
|
|
87
|
-
|
|
88
|
-
| **Reads** for anyone (`get*` / `list*`, `<entity> list/get`)
|
|
89
|
-
| **Writes** as the workspace (`create*` / `update*` / `delete*`, `<entity> create/update/delete`) | account **Bearer** (`apiKey`)
|
|
90
|
-
| **Anything as the signed-in member**, inside uidu (a custom app)
|
|
126
|
+
| Operation | Auth | Where it runs |
|
|
127
|
+
| ------------------------------------------------------------------------------------------------ | ---------------------------------------------------- | ------------------------------------------- |
|
|
128
|
+
| **Reads** for anyone (`get*` / `list*`, `<entity> list/get`) | `publicToken` | anywhere — server, CLI, **and the browser** |
|
|
129
|
+
| **Writes** as the workspace (`create*` / `update*` / `delete*`, `<entity> create/update/delete`) | account **Bearer** (`apiKey`) | CLI and **server-side** only |
|
|
130
|
+
| **Anything as the signed-in member**, inside uidu (a custom app) | session token from the host, over `@uidu/app-bridge` | **the browser only** |
|
|
91
131
|
|
|
92
132
|
The write functions live in `@uidu/client`, so anything the CLI provisions you can also do
|
|
93
133
|
from a Next.js server action / route handler / RSC. **Never put the account Bearer token in a
|
|
@@ -111,10 +151,13 @@ that person can do, and no more. It changes five things:
|
|
|
111
151
|
needs a server credential uidu doesn't issue yet.
|
|
112
152
|
3. **Treat the token as opaque.** Never decode it. The expiry comes with it, the bridge refreshes
|
|
113
153
|
it, and `graphqlUrl` comes with it too: never build `https://<workspace>.uidu.org` from a slug.
|
|
114
|
-
4. **
|
|
115
|
-
items
|
|
116
|
-
|
|
117
|
-
|
|
154
|
+
4. **Read what the workspace already has; keep only new data in the app's own Models.** The
|
|
155
|
+
session reaches the app's own Models, fields and items (`node(id:)` and the data-engine
|
|
156
|
+
mutations) and, read-only, the workspace's **goals and timeframes** (`listGoals`, `getGoal`,
|
|
157
|
+
`listTimeframes`) — those of the workspace and of the Space the app sits in. Everything else in
|
|
158
|
+
the workspace comes back as an error for now: see [Workspace data](#workspace-data) for which
|
|
159
|
+
entity is reachable from where. Keep the app's own data in Models rather than in a database of
|
|
160
|
+
your own: it stays searchable and permissioned in uidu.
|
|
118
161
|
`listModelItems` reads uidu's **search index**, which catches up a moment after a write:
|
|
119
162
|
after `createModelItem` / `deleteModelItem`, update your list from the mutation payload
|
|
120
163
|
instead of re-listing, or the new item is missing. And load models once per app instance
|
|
@@ -129,24 +172,39 @@ import { useEffect, useState } from 'react';
|
|
|
129
172
|
import { UiduAppProvider, useUiduApp } from '@uidu/react';
|
|
130
173
|
import { DEFAULT_HOST_ORIGINS } from '@uidu/app-bridge';
|
|
131
174
|
import {
|
|
132
|
-
createModelItem,
|
|
133
|
-
|
|
175
|
+
createModelItem,
|
|
176
|
+
ensureModel,
|
|
177
|
+
listModelItems,
|
|
178
|
+
toFieldValuesAttributes,
|
|
179
|
+
type ModelItem,
|
|
180
|
+
type UiduClient,
|
|
134
181
|
} from '@uidu/client';
|
|
135
182
|
|
|
136
183
|
export function AppProvider({ children }: { children: React.ReactNode }) {
|
|
137
184
|
// https://*.uidu.org by default; add a local uidu or a custom domain
|
|
138
|
-
return
|
|
185
|
+
return (
|
|
186
|
+
<UiduAppProvider hostOrigins={[...DEFAULT_HOST_ORIGINS]}>
|
|
187
|
+
{children}
|
|
188
|
+
</UiduAppProvider>
|
|
189
|
+
);
|
|
139
190
|
}
|
|
140
191
|
|
|
141
192
|
async function loadBookings(client: UiduClient, workspaceAppId: string) {
|
|
142
|
-
// interim: the app creates its own model on first load (install-time manifest comes later)
|
|
193
|
+
// interim: the app creates its own model on first load (install-time manifest comes later).
|
|
194
|
+
// Bookings of a room are data uidu has no place for — hence a Model. Goals, contacts or
|
|
195
|
+
// people already exist: read them (see Workspace data).
|
|
143
196
|
const model = await ensureModel(client, {
|
|
144
197
|
workspaceAppId,
|
|
145
198
|
name: 'Booking',
|
|
146
199
|
fields: [{ shortname: 'room', name: 'Room', kind: 'string' }],
|
|
147
200
|
});
|
|
148
201
|
await createModelItem(client, {
|
|
149
|
-
input: {
|
|
202
|
+
input: {
|
|
203
|
+
attributes: {
|
|
204
|
+
modelId: model.id,
|
|
205
|
+
fieldValuesAttributes: toFieldValuesAttributes(model, { room: 'Blu' }),
|
|
206
|
+
},
|
|
207
|
+
},
|
|
150
208
|
});
|
|
151
209
|
return listModelItems(client, { modelId: model.id }); // item.fieldValuesByShortname.room
|
|
152
210
|
}
|
|
@@ -156,19 +214,83 @@ export function Bookings() {
|
|
|
156
214
|
const [items, setItems] = useState<ModelItem[]>([]);
|
|
157
215
|
|
|
158
216
|
useEffect(() => {
|
|
159
|
-
if (app.status === 'ready')
|
|
217
|
+
if (app.status === 'ready')
|
|
218
|
+
loadBookings(app.client, app.context.workspaceApp.id).then(setItems);
|
|
160
219
|
}, [app]);
|
|
161
220
|
|
|
162
|
-
if (app.status === 'error')
|
|
163
|
-
|
|
221
|
+
if (app.status === 'error')
|
|
222
|
+
return <p>Open this app from uidu ({app.error.code})</p>; // NOT_EMBEDDED, TIMEOUT…
|
|
223
|
+
return (
|
|
224
|
+
<ul>
|
|
225
|
+
{items.map((item) => (
|
|
226
|
+
<li key={item.id}>{item.fieldValuesByShortname?.room}</li>
|
|
227
|
+
))}
|
|
228
|
+
</ul>
|
|
229
|
+
);
|
|
164
230
|
}
|
|
165
231
|
```
|
|
166
232
|
|
|
233
|
+
Field `kind`s for `ensureModel` (and `public/uidu.app.json`): `string` (one line), `text`
|
|
234
|
+
(several lines), `number`, `currency`, `percent`, `date`, `datetime`, `checkbox`, `email`,
|
|
235
|
+
`phone`, `url`, `rating`, `member` (a person of the workspace). `singleSelect` /
|
|
236
|
+
`multipleSelect` need their options created too (`createFieldOption`) — prefer `string` unless
|
|
237
|
+
the choices matter. uidu knows more (`attachments`, `linkedRecord`, `formula`, `progress`,
|
|
238
|
+
`richText`…); leave them out unless the person asks for one.
|
|
239
|
+
|
|
167
240
|
`app.context` carries `user`, `space`, `workspaceApp`, `locale`, `theme` and `accent` (the page's colour, to set as `--primary`). Outside React: `connect()` from
|
|
168
241
|
`@uidu/app-bridge`, then `createClient(fromBridge(bridge))` from `@uidu/client`. Start a new
|
|
169
242
|
one with `npm create uidu-app@latest my-app -- -t custom-app`: it declares its Models in
|
|
170
243
|
`public/uidu.app.json` and sets `frame-ancestors` so only uidu can frame it.
|
|
171
244
|
|
|
245
|
+
## Workspace data
|
|
246
|
+
|
|
247
|
+
**A uidu workspace is not empty.** It already holds the organisation's goals, contacts, deals,
|
|
248
|
+
people, events, courses… An app that asks for "a dashboard of the OKRs behind schedule" or "our
|
|
249
|
+
open deals" is asking about data uidu has — read it. **Create a Model only for data uidu has no
|
|
250
|
+
place for** (the app's own bookings, a checklist, a log). Copying existing records into a Model
|
|
251
|
+
gives the person a second copy that drifts from the real one.
|
|
252
|
+
|
|
253
|
+
| The workspace's… | Functions (`@uidu/client`) | Inside a custom app |
|
|
254
|
+
| -------------------------------------- | ------------------------------------------------------------------------- | -------------------------- |
|
|
255
|
+
| Goals / OKRs (key results = subgoals) | `listGoals`, `getGoal`, `listTimeframes`; `updateGoal` | **read** (no `updateGoal`) |
|
|
256
|
+
| Contacts, organisations | `listContacts`, `getContact`; `createContact` | not yet |
|
|
257
|
+
| Deals (CRM) | `listDeals`, `getDeal`; `createDeal`, `updateDeal` | not yet |
|
|
258
|
+
| People (employees) | `listEmployees`, `getEmployee`, `listOffices`, `listCircles`, `listRoles` | not yet |
|
|
259
|
+
| Tasks, notes | `updateTask`, `createTask`, `updateNote`, `createNote` (no list) | not yet |
|
|
260
|
+
| Events, courses, bookings, calendars | `listEvents`, `listCourses`, `listBookings`, `listCalendars`, … | not yet |
|
|
261
|
+
| Forms, donations, stories, help center | see [Patterns by Scope](#patterns-by-scope) | not yet |
|
|
262
|
+
| The app's own Models | `ensureModel`, `listModelItems`, `createModelItem`, … | read and write |
|
|
263
|
+
|
|
264
|
+
On a server with an account token (`apiKey`) every row works; "not yet" means a custom app's
|
|
265
|
+
session token is refused for it. **When the data exists in uidu but is not reachable from a custom
|
|
266
|
+
app yet, tell the person** — don't rebuild it in a Model. If the functions you need aren't in the
|
|
267
|
+
table, look for `listX` / `getX` in `@uidu/client` before concluding they don't exist.
|
|
268
|
+
|
|
269
|
+
### Goals (OKRs)
|
|
270
|
+
|
|
271
|
+
An objective is a `Goal`; its key results are its `subgoals`; the period it runs over is its
|
|
272
|
+
`timeframe` (`startDate`, `endDate`). `metricKind` is `number`, `percentage`, `currency`,
|
|
273
|
+
`checkbox` or `subgoal` (measured by its key results); `status` is the owner's own judgement:
|
|
274
|
+
`on_track`, `needs_attention`, `off_track`, `accomplished`.
|
|
275
|
+
|
|
276
|
+
**Values are stored x100.** `initialValue`, `currentValue` and `targetValue` never hold the number
|
|
277
|
+
a person typed, and `progress` is percent x100 (10000 = done). Use the helpers, don't divide by hand:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
import { listGoals, goalValue, goalProgress, isGoalBehind } from '@uidu/client';
|
|
281
|
+
|
|
282
|
+
const goals = await listGoals(app.client); // all of them, objectives and key results
|
|
283
|
+
const objectives = goals.filter((g) => !g.parentId);
|
|
284
|
+
const behind = objectives.filter((g) => isGoalBehind(g, { tolerance: 0.1 }));
|
|
285
|
+
|
|
286
|
+
goalValue(goal, goal.targetValue); // 45 for a 45% target, 1200 for €1,200
|
|
287
|
+
goalProgress(goal); // 0.45 — done fraction
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`isGoalBehind` compares progress with the share of the timeframe already gone (`goalTimeElapsed`);
|
|
291
|
+
show the goal's `status` next to it, it says something different. `updateGoal` takes raw values:
|
|
292
|
+
`toGoalRaw(45)`. A goal's owner, members and activity are not readable from a custom app.
|
|
293
|
+
|
|
172
294
|
## Working transparently
|
|
173
295
|
|
|
174
296
|
The user often can't tell what you're doing when you drive the CLI. Stay legible:
|
|
@@ -215,23 +337,66 @@ import { createClient } from '@uidu/client';
|
|
|
215
337
|
export const uidu = createClient({
|
|
216
338
|
workspace: process.env.UIDU_WORKSPACE ?? '',
|
|
217
339
|
publicToken: process.env.UIDU_PUBLIC_TOKEN, // safe to expose
|
|
218
|
-
apiKey: process.env.UIDU_API_KEY,
|
|
340
|
+
apiKey: process.env.UIDU_API_KEY, // server-only secret
|
|
219
341
|
});
|
|
220
342
|
```
|
|
221
343
|
|
|
222
344
|
Required environment:
|
|
223
345
|
|
|
224
|
-
| Variable
|
|
225
|
-
|
|
226
|
-
| `UIDU_WORKSPACE`
|
|
227
|
-
| `UIDU_PUBLIC_TOKEN` | no
|
|
228
|
-
| `UIDU_API_KEY`
|
|
346
|
+
| Variable | Required | Notes |
|
|
347
|
+
| ------------------- | -------- | -------------------------------------- |
|
|
348
|
+
| `UIDU_WORKSPACE` | yes | Your workspace slug (e.g. `acme`) |
|
|
349
|
+
| `UIDU_PUBLIC_TOKEN` | no | Public read-only token, safe to expose |
|
|
350
|
+
| `UIDU_API_KEY` | no | Server-only secret for privileged ops |
|
|
229
351
|
|
|
230
352
|
## Patterns by Scope
|
|
231
353
|
|
|
232
|
-
### CMS
|
|
354
|
+
### CMS Sites (new CMS)
|
|
233
355
|
|
|
234
|
-
CMS
|
|
356
|
+
uidu has two CMS generations. **Sites** are the new one: a `Site` has page types (page-kind Models), pages (ModelItems of a page type) and, on each page, an ordered list of blocks (ModelItems of a block-kind Model). **Projects** (`getPage`/`listPages`, next section) are the legacy CMS — keep using them only for sites already deployed on a Project. Build new sites on Sites. Every read works with a `publicToken`, in RSC and in the browser.
|
|
357
|
+
|
|
358
|
+
```tsx
|
|
359
|
+
// src/app/[[...slug]]/page.tsx — Server Component
|
|
360
|
+
import { getPageBySlug, getSiteByDomain, type SiteBlock } from '@uidu/client';
|
|
361
|
+
import { notFound } from 'next/navigation';
|
|
362
|
+
import { uidu } from '@/lib/uidu';
|
|
363
|
+
|
|
364
|
+
const components: Record<string, React.ComponentType<{ block: SiteBlock }>> = {
|
|
365
|
+
Hero,
|
|
366
|
+
FAQ,
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
export default async function Page({
|
|
370
|
+
params,
|
|
371
|
+
}: {
|
|
372
|
+
params: Promise<{ slug?: string[] }>;
|
|
373
|
+
}) {
|
|
374
|
+
const { slug = ['home'] } = await params;
|
|
375
|
+
const site = await getSiteByDomain(uidu, {
|
|
376
|
+
domain: process.env.SITE_DOMAIN!,
|
|
377
|
+
});
|
|
378
|
+
const page =
|
|
379
|
+
site &&
|
|
380
|
+
(await getPageBySlug(uidu, { siteId: site.id, slug: slug.join('/') }));
|
|
381
|
+
if (!page) notFound();
|
|
382
|
+
return page.blocks.map((block) => {
|
|
383
|
+
const Component = components[block.model.name ?? ''];
|
|
384
|
+
return Component ? <Component key={block.id} block={block} /> : null;
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
- `getSiteByDomain(client, { domain })` / `getSite(client, { id })` — the Site (by id in previews, where there is no domain)
|
|
390
|
+
- `getPageBySlug(client, { siteId, slug, includeDrafts? })` → `{ id, slug, model, fields, blocks: [{ id, model, fields, position }] }`, published only by default
|
|
391
|
+
- `listSitePages(client, { siteId, pageType?, includeDrafts? })` — nav/sitemaps; published only, singletons excluded; `pageType` = Model id or name
|
|
392
|
+
- `getSingletonBlock(client, { siteId, shortname })` — header/footer/nav
|
|
393
|
+
- `fields` is already a `{ shortname: value }` map — no `useFields` needed.
|
|
394
|
+
- Block kinds have a `shortname`, but `Model.shortname` isn't exposed by the API yet: key component maps on `block.model.name` for now.
|
|
395
|
+
- Don't call the legacy `listPages`/`getPage` with a Site id — they read Projects only.
|
|
396
|
+
|
|
397
|
+
### CMS Pages (legacy Projects)
|
|
398
|
+
|
|
399
|
+
Legacy CMS pages are made of `pageBlocks`. Fetch a page server-side, pass blocks to `<PageBlocks>` with a component map keyed by the block's `shortname`:
|
|
235
400
|
|
|
236
401
|
```tsx
|
|
237
402
|
// src/app/page.tsx — Server Component
|
|
@@ -249,10 +414,7 @@ export default async function HomePage() {
|
|
|
249
414
|
if (!page) return <div>Not found</div>;
|
|
250
415
|
|
|
251
416
|
return (
|
|
252
|
-
<PageBlocks
|
|
253
|
-
pageBlocks={page.pageBlocks}
|
|
254
|
-
components={{ Hero, Feature }}
|
|
255
|
-
/>
|
|
417
|
+
<PageBlocks pageBlocks={page.pageBlocks} components={{ Hero, Feature }} />
|
|
256
418
|
);
|
|
257
419
|
}
|
|
258
420
|
```
|
|
@@ -296,19 +458,19 @@ const event = await getEvent(uidu, { id });
|
|
|
296
458
|
|
|
297
459
|
Event fields:
|
|
298
460
|
|
|
299
|
-
| Field
|
|
300
|
-
|
|
301
|
-
| `event.id`
|
|
302
|
-
| `event.name`
|
|
303
|
-
| `event.body`
|
|
304
|
-
| `event.cover`
|
|
305
|
-
| `event.description`
|
|
306
|
-
| `event.instance.beginsAt`
|
|
307
|
-
| `event.instance.finishesAt` | ISO datetime of end
|
|
308
|
-
| `event.instance.id`
|
|
309
|
-
| `event.primaryAddress`
|
|
310
|
-
| `event.currentCapacity`
|
|
311
|
-
| `event.isPaidEvent`
|
|
461
|
+
| Field | Notes |
|
|
462
|
+
| --------------------------- | -------------------------------------- |
|
|
463
|
+
| `event.id` | |
|
|
464
|
+
| `event.name` | Display name |
|
|
465
|
+
| `event.body` | WYSIWYG doc — render with `<RichText>` |
|
|
466
|
+
| `event.cover` | Image URL (string) or null |
|
|
467
|
+
| `event.description` | Plain string summary |
|
|
468
|
+
| `event.instance.beginsAt` | ISO datetime of start |
|
|
469
|
+
| `event.instance.finishesAt` | ISO datetime of end |
|
|
470
|
+
| `event.instance.id` | Instance identifier |
|
|
471
|
+
| `event.primaryAddress` | Location data |
|
|
472
|
+
| `event.currentCapacity` | Numeric capacity |
|
|
473
|
+
| `event.isPaidEvent` | Boolean |
|
|
312
474
|
|
|
313
475
|
### Stories (Blog)
|
|
314
476
|
|
|
@@ -395,10 +557,12 @@ later throws. `FieldValueAttributes` exposes no `value` key — `content` is the
|
|
|
395
557
|
|
|
396
558
|
```ts
|
|
397
559
|
// ✅ Correct — every answer wrapped
|
|
398
|
-
fieldValuesAttributes: [
|
|
560
|
+
fieldValuesAttributes: [
|
|
561
|
+
{ fieldId: 'f1', content: { value: 'hello@example.com' } },
|
|
562
|
+
];
|
|
399
563
|
|
|
400
564
|
// ❌ Wrong — silently loses the answer
|
|
401
|
-
fieldValuesAttributes: [{ fieldId: 'f1', content: 'hello@example.com' }]
|
|
565
|
+
fieldValuesAttributes: [{ fieldId: 'f1', content: 'hello@example.com' }];
|
|
402
566
|
```
|
|
403
567
|
|
|
404
568
|
It holds for **every** field kind, not just strings — `{ value: 42 }`, `{ value: true }`,
|
|
@@ -465,7 +629,9 @@ All `body` fields on Event, Story, DonationCampaign, etc. are structured documen
|
|
|
465
629
|
```tsx
|
|
466
630
|
import { RichText } from '@uidu/react';
|
|
467
631
|
|
|
468
|
-
{
|
|
632
|
+
{
|
|
633
|
+
story.body != null && <RichText doc={story.body} />;
|
|
634
|
+
}
|
|
469
635
|
```
|
|
470
636
|
|
|
471
637
|
The prop is `doc`, not `children`. The component handles paragraphs, headings, lists, links, and inline marks.
|
|
@@ -517,12 +683,12 @@ Or pick a template directly:
|
|
|
517
683
|
npm create uidu-app@latest my-app -- -t events
|
|
518
684
|
```
|
|
519
685
|
|
|
520
|
-
| Template
|
|
521
|
-
|
|
522
|
-
| `minimal`
|
|
523
|
-
| `events`
|
|
524
|
-
| `stories`
|
|
525
|
-
| `donations`
|
|
686
|
+
| Template | What it builds |
|
|
687
|
+
| ------------ | ------------------------------------------------------------------------------------------------ |
|
|
688
|
+
| `minimal` | CMS landing page with `PageBlocks` + Hero/Feature blocks |
|
|
689
|
+
| `events` | Event listing + `/event/[id]` detail |
|
|
690
|
+
| `stories` | Blog listing + `/story/[id]` detail (magazine layout) |
|
|
691
|
+
| `donations` | Campaigns listing + `/campaign/[id]` detail with progress bars |
|
|
526
692
|
| `custom-app` | An app inside a uidu Space: `<UiduAppProvider>`, data in its own Models (`public/uidu.app.json`) |
|
|
527
693
|
|
|
528
694
|
Every template ships with Next.js 16 App Router, Tailwind v4 and an `.env.example` (the CLI
|
|
@@ -530,6 +696,48 @@ offers to fill it in for you). The site templates add a configured `src/lib/uidu
|
|
|
530
696
|
server-rendered listing + dynamic detail page; `custom-app` has no token to configure and
|
|
531
697
|
reads everything in client components.
|
|
532
698
|
|
|
699
|
+
## Full API Index
|
|
700
|
+
|
|
701
|
+
Every `@uidu/client` function, by domain — generated from the source, so if a function isn't
|
|
702
|
+
here it doesn't exist (yet). Domains marked "reads need Bearer" hold private data: a public token
|
|
703
|
+
sees nothing. Per-function auth, signatures and descriptions: https://docs.uidu.org/docs/reference/client.
|
|
704
|
+
|
|
705
|
+
<!-- BEGIN sdk:domain-index — generated by scripts/sdk-manifest.mjs, do not edit -->
|
|
706
|
+
|
|
707
|
+
- **Client & utilities** — `createClient`, `fromBridge`, `normalizeFieldValueContent`, `normalizeFieldValuesAttributes`, `paginate`
|
|
708
|
+
- **CMS — Sites** · CLI: `sites` — `createSite`, `getPageBySlug`, `getSingletonBlock`, `getSite`, `getSiteByDomain`, `listSitePages`
|
|
709
|
+
- **CMS — Projects (legacy)** · CLI: `projects` — `createFieldValue`, `createPage`, `createPageBlock`, `createProject`, `getPage`, `getTemplate`, `listPages`, `listProjects`
|
|
710
|
+
- **Models (custom data)** — `createField`, `createFieldOption`, `createModel`, `createModelItem`, `deleteField`, `deleteFieldOption`, `deleteFieldValue`, `deleteModelItem`, `ensureModel`, `getModel`, `getModelItem`, `listModelItems`, `listModels`, `toFieldValuesAttributes`, `updateField`, `updateFieldOption`, `updateFieldValue`, `updateModel`, `updateModelItem`
|
|
711
|
+
- **Forms** · CLI: `forms` — `createForm`, `createFormResponse`, `deleteForm`, `getForm`, `listForms`, `updateForm`, `updateFormResponse`
|
|
712
|
+
- **Events** · CLI: `events`, `attendances` — `createAttendance`, `createEvent`, `getEvent`, `listEvents`, `updateEvent`
|
|
713
|
+
- **Calls** · CLI: `calls` — `getCall`, `listCalls`
|
|
714
|
+
- **Jobs & applications** · CLI: `jobs`, `applications` — `completeApplication`, `createApplication`, `getJob`, `listApplications`, `listJobs`, `updateApplication`
|
|
715
|
+
- **Donations** · CLI: `donations` — `createDonation`, `createDonationCampaign`, `deleteDonationCampaign`, `getDonationCampaign`, `listDonationCampaigns`, `updateDonationCampaign`
|
|
716
|
+
- **Stories** · CLI: `stories` — `createStory`, `getStory`, `listStories`, `updateStory`
|
|
717
|
+
- **Help center** · CLI: `channel` — `createChannel`, `deleteChannel`, `getChannel`, `listChannels`, `updateChannel`
|
|
718
|
+
- **Knowledge base** · CLI: `kb-collections`, `kb-articles` — `createKbArticle`, `createKbCollection`, `deleteKbArticle`, `deleteKbCollection`, `getKbArticle`, `getKbCollection`, `listKbArticles`, `listKbCollections`, `updateKbArticle`, `updateKbCollection`
|
|
719
|
+
- **Search** — `search`
|
|
720
|
+
- **Courses** · CLI: `courses` — `createCourse`, `deleteCourse`, `getCourse`, `getEnrollment`, `getLecture`, `listCourses`, `listEnrollments`, `listLectures`, `updateCourse`
|
|
721
|
+
- **Bookings & calendars** · reads need Bearer · CLI: `bookings` — `getBooking`, `getCalendar`, `getCalendarEvent`, `listBookings`, `listCalendarEvents`, `listCalendars`
|
|
722
|
+
- **Campaigns** · reads need Bearer · CLI: `campaigns` — `getCampaign`, `listCampaigns`, `listEmailCampaigns`
|
|
723
|
+
- **Contacts & deals** · reads need Bearer · CLI: `contacts`, `deals` — `createContact`, `createDeal`, `deleteContact`, `getContact`, `getDeal`, `listContacts`, `listDeals`, `updateDeal`
|
|
724
|
+
- **Goals (OKRs)** · reads need Bearer · CLI: `goals`, `timeframes` — `getGoal`, `goalProgress`, `goalTimeElapsed`, `goalValue`, `isGoalBehind`, `listGoals`, `listTimeframes`, `toGoalRaw`, `updateGoal`
|
|
725
|
+
- **People (HR)** · reads need Bearer · CLI: `employees`, `employments`, `offices`, `roles`, `ccnls`, `time-clocks`, `compensations`, `benefits`, `benefit-enrollments` — `contractEvents`, `createBenefit`, `createBenefitEnrollment`, `createCcnl`, `createCompensation`, `createEmployee`, `createEmployment`, `createTimeClock`, `deleteCompensation`, `deleteEmployment`, `getEmployee`, `listCcnls`, `listCircles`, `listEmployees`, `listEmploymentHistory`, `listKinds`, `listOffices`, `listRoles`, `terminateEmployment`, `updateCompensation`, `updateEmployee`, `updateEmployment`, `weekStartFor`
|
|
726
|
+
- **Spaces, tasks & notes** · CLI: `tasks`, `notes`, `spaces` — `createNote`, `createSpace`, `createTask`, `deleteNote`, `deleteSpace`, `deleteTask`, `updateNote`, `updateSpace`, `updateTask`
|
|
727
|
+
- **Provisioning & actions** — `createWorkspace`, `executeAction`, `generateWorkspaceApiCredentials`
|
|
728
|
+
|
|
729
|
+
<!-- END sdk:domain-index -->
|
|
730
|
+
|
|
731
|
+
`@uidu/react`:
|
|
732
|
+
|
|
733
|
+
<!-- BEGIN sdk:react-surface — generated by scripts/sdk-manifest.mjs, do not edit -->
|
|
734
|
+
|
|
735
|
+
- **Components:** `BlockRenderer` — Render exactly one block from a page's `pageBlocks` array, by shortname.; `DynamicForm` — Render a uidu form (from `getForm`, `call.form`, …) as HTML inputs and hand the parsed values to your `action`, typically a `createFormResponse` Server Action.; `PageBlocks` — Render a page's blocks in order, each with the component keyed by its shortname in `components`; blocks with no match render `fallback`, or nothing.; `RichText` — Render a Tiptap (ProseMirror) JSON document as React elements.; `UiduAppProvider` — Connects a custom app to the uidu page framing it (`@uidu/app-bridge`) and hands the tree its state through `useUiduApp`. Once connected, the children also sit inside a `<UiduProvider>` with a session-backed client, so `useUiduClient()` works as anywhere else.; `UiduProvider` — Provide a `UiduClient` to the tree, either a `client` you built or one made from `workspace`/`publicToken`/`endpoint`, plus an SWR fetcher backed by it.
|
|
736
|
+
- **Hooks:** `useFields` — Normalize a `fieldValues` array into a `{ shortname: value }` map so blocks can access their fields by name without walking the GraphQL shape.; `useQuery` — Generic client-side data-fetching hook: runs `document` against the `UiduClient` from the nearest `<UiduProvider>` via SWR, so calls are cached and deduplicated by operation name + variables.; `useUidu` — The nearest `UiduProvider`'s `{ client, endpoint }`; throws outside a provider.; `useUiduApp` — The custom app's bridge state: `connecting`, `ready` or `error`.; `useUiduClient` — The `UiduClient` from the nearest `UiduProvider`; throws outside a provider.
|
|
737
|
+
- **Utilities:** `formatSalaryRange` — Format a job's salary range (`salaryMin`/`salaryMax`) into a display string. Returns `null` when the job advertises no salary, so callers can hide the field. Handles open-ended ranges ("From …", "Up to …") and a single figure.; `getBlockShortname` — The shortname a page block is rendered by (its template block's, else its own), or null.; `toText` — Flatten any uidu field-content value into a plain string. Handles raw strings, numbers, Slate-style rich text nodes (`{ type, children }`, `{ text }`), arrays of nodes, and wrapper objects with a `value` field.
|
|
738
|
+
|
|
739
|
+
<!-- END sdk:react-surface -->
|
|
740
|
+
|
|
533
741
|
## See Also
|
|
534
742
|
|
|
535
743
|
- uidu docs: https://docs.uidu.org
|
|
@@ -537,3 +745,4 @@ reads everything in client components.
|
|
|
537
745
|
- `@uidu/client`: https://github.com/uidu-org/api.js/tree/main/packages/client
|
|
538
746
|
- `@uidu/react`: https://github.com/uidu-org/api.js/tree/main/packages/react
|
|
539
747
|
- `create-uidu-app`: https://github.com/uidu-org/api.js/tree/main/packages/create-uidu-app
|
|
748
|
+
- Machine-readable manifest of the whole SDK (exports, domains, auth, CLI verbs): https://github.com/uidu-org/api.js/blob/main/docs/sdk-manifest.json
|