@norbix.ai/react-redux 1.3.0 → 1.4.0
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/README.md +218 -120
- package/dist/index.cjs +122 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -6
- package/dist/index.d.ts +31 -6
- package/dist/index.js +122 -16
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
|
|
24
24
|
`@norbix.ai/react-redux` is a thin, opinionated layer that wires the Norbix TypeScript SDK into a Redux Toolkit + RTK Query app. You get React hooks for the most-used Norbix endpoints with full caching, request dedup, and tag-based invalidation built in. Under the hood every hook calls the same typed `Norbix` SDK — so DTOs, auth, errors, and base URLs all stay consistent with the rest of your stack.
|
|
25
25
|
|
|
26
|
-
| You get
|
|
27
|
-
|
|
28
|
-
| `useGetUsersQuery`, `useFindCollectionQuery`, `useInsertOneMutation`, ... | this package
|
|
29
|
-
| Cache, dedup, polling, refetch on focus, optimistic updates
|
|
30
|
-
| Tree-shakeable typed methods, JWT auth, error mapping
|
|
26
|
+
| You get | Provided by |
|
|
27
|
+
| ------------------------------------------------------------------------- | ------------------------------------ |
|
|
28
|
+
| `useGetUsersQuery`, `useFindCollectionQuery`, `useInsertOneMutation`, ... | this package |
|
|
29
|
+
| Cache, dedup, polling, refetch on focus, optimistic updates | RTK Query (`@reduxjs/toolkit/query`) |
|
|
30
|
+
| Tree-shakeable typed methods, JWT auth, error mapping | `@norbix.ai/ts` SDK |
|
|
31
31
|
|
|
32
32
|
## Install
|
|
33
33
|
|
|
@@ -104,11 +104,10 @@ export function UsersList() {
|
|
|
104
104
|
|
|
105
105
|
return (
|
|
106
106
|
<ul>
|
|
107
|
-
{data?.list?.items.map((u) =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
>
|
|
107
|
+
{data?.list?.items.map((u) => (
|
|
108
|
+
<li key={u.id}>{u.email}</li>
|
|
109
|
+
))}
|
|
110
|
+
<button disabled={inviting} onClick={() => invite({ email: 'maya@team.io' })}>
|
|
112
111
|
Invite
|
|
113
112
|
</button>
|
|
114
113
|
</ul>
|
|
@@ -120,74 +119,115 @@ After `invite()` resolves, the `User/LIST` tag is invalidated and `useGetUsersQu
|
|
|
120
119
|
|
|
121
120
|
## What ships in the box
|
|
122
121
|
|
|
123
|
-
A
|
|
122
|
+
A _curated_ set of hooks for the most common endpoints. Every hook is end-to-end typed from the SDK's own DTOs.
|
|
124
123
|
|
|
125
124
|
### API surface — `norbix.api.*`
|
|
126
125
|
|
|
127
|
-
| Hook
|
|
128
|
-
|
|
129
|
-
| `useLoginMutation`
|
|
130
|
-
| `useLogoutMutation`
|
|
131
|
-
| `useAuthenticateMutation`
|
|
132
|
-
| `useGetUsersQuery`
|
|
133
|
-
| `useGetUserQuery`
|
|
134
|
-
| `useInviteUserMutation`
|
|
135
|
-
| `useUpdateUserMutation`
|
|
136
|
-
| `useBlockUserMutation`
|
|
137
|
-
| `useUnblockUserMutation`
|
|
138
|
-
| `useDeleteUserMutation`
|
|
139
|
-
| `useFindCollectionQuery`
|
|
140
|
-
| `useFindOneQuery`
|
|
141
|
-
| `useCountCollectionQuery`
|
|
142
|
-
| `useInsertOneMutation`
|
|
143
|
-
| `useUpdateOneMutation`
|
|
144
|
-
| `useReplaceOneMutation`
|
|
145
|
-
| `useDeleteOneMutation`
|
|
146
|
-
| `useFindTermsQuery`
|
|
147
|
-
| `useFindTermsChildrenQuery`
|
|
148
|
-
| `useFindTermTreeQuery`
|
|
149
|
-
| `useFindTaxonomyTreeQuery`
|
|
150
|
-
| `useGetApiKeysQuery`
|
|
151
|
-
| `useRegenerateApiKeysMutation`
|
|
152
|
-
| `useGetPublicFileQuery`
|
|
153
|
-
| `useTestFilesIntegrationApiMutation` | `api.files.testFilesIntegration` | invalidates `Files` + `FilesIntegrations`
|
|
126
|
+
| Hook | Wraps | Cache tags |
|
|
127
|
+
| ------------------------------------ | -------------------------------- | ------------------------------------------------------------ |
|
|
128
|
+
| `useLoginMutation` | `client.login(...)` | invalidates `User/LIST`, `AccountProfile/CURRENT` |
|
|
129
|
+
| `useLogoutMutation` | `client.logout()` | invalidates `User`, `AccountProfile`, `Collection`, `Schema` |
|
|
130
|
+
| `useAuthenticateMutation` | `api.auth.authenticate` | — |
|
|
131
|
+
| `useGetUsersQuery` | `api.membership.getUsers` | provides `User/LIST` |
|
|
132
|
+
| `useGetUserQuery` | `api.membership.getUser` | provides `User/<id>` |
|
|
133
|
+
| `useInviteUserMutation` | `api.membership.inviteUser` | invalidates `User/LIST` |
|
|
134
|
+
| `useUpdateUserMutation` | `api.membership.updateUser` | invalidates `User/<id>` + `User/LIST` |
|
|
135
|
+
| `useBlockUserMutation` | `api.membership.blockUser` | invalidates `User/LIST` |
|
|
136
|
+
| `useUnblockUserMutation` | `api.membership.unblockUser` | invalidates `User/LIST` |
|
|
137
|
+
| `useDeleteUserMutation` | `api.membership.deleteUser` | invalidates `User/LIST` |
|
|
138
|
+
| `useFindCollectionQuery` | `api.database.find` | provides `Collection/<name>` |
|
|
139
|
+
| `useFindOneQuery` | `api.database.findOne` | provides `Collection/<name>` |
|
|
140
|
+
| `useCountCollectionQuery` | `api.database.count` | provides `Collection/<name>` |
|
|
141
|
+
| `useInsertOneMutation` | `api.database.insertOne` | invalidates `Collection/<name>` |
|
|
142
|
+
| `useUpdateOneMutation` | `api.database.updateOne` | invalidates `Collection/<name>` |
|
|
143
|
+
| `useReplaceOneMutation` | `api.database.replaceOne` | invalidates `Collection/<name>` |
|
|
144
|
+
| `useDeleteOneMutation` | `api.database.deleteOne` | invalidates `Collection/<name>` |
|
|
145
|
+
| `useFindTermsQuery` | `api.database.findTerms` | provides `DatabaseTaxonomyTerms/<taxonomyName>` |
|
|
146
|
+
| `useFindTermsChildrenQuery` | `api.database.findTermsChildren` | provides `DatabaseTaxonomyTerms/<taxonomyName>` |
|
|
147
|
+
| `useFindTermTreeQuery` | `api.database.findTermTree` | provides `DatabaseTaxonomyTerms/<taxonomyName>` |
|
|
148
|
+
| `useFindTaxonomyTreeQuery` | `api.database.findTaxonomyTree` | provides `DatabaseTaxonomyTerms/ANY` |
|
|
149
|
+
| `useGetApiKeysQuery` | `api.apikeys.getApiKeys` | provides `ApiKey/LIST` |
|
|
150
|
+
| `useRegenerateApiKeysMutation` | `api.apikeys.regenerateApiKeys` | invalidates `ApiKey/LIST` |
|
|
151
|
+
| `useGetPublicFileQuery` | `api.files.getPublicFile` | provides `Files/PUBLIC:<publicId>` |
|
|
152
|
+
| `useTestFilesIntegrationApiMutation` | `api.files.testFilesIntegration` | invalidates `Files` + `FilesIntegrations` |
|
|
154
153
|
|
|
155
154
|
### Hub surface — `norbix.hub.*`
|
|
156
155
|
|
|
157
|
-
| Hook
|
|
158
|
-
|
|
159
|
-
| `useGetAccountProfileQuery`
|
|
160
|
-
| `useGetAccountStatusQuery`
|
|
161
|
-
| `useUpdateAccountProfileMutation`
|
|
162
|
-
| `useGetProjectsQuery`
|
|
163
|
-
| `useGetProjectQuery`
|
|
164
|
-
| `useCreateProjectMutation`
|
|
165
|
-
| `useDeleteProjectMutation`
|
|
166
|
-
| `useListRegionsQuery`
|
|
167
|
-
| `useUpdateProjectRegionsMutation`
|
|
168
|
-
| `useGetDatabaseSchemasQuery`
|
|
169
|
-
| `useGetDatabaseSchemaQuery`
|
|
170
|
-
| `useSaveDatabaseSchemaMutation`
|
|
171
|
-
| `useRenameDatabaseSchemaMutation`
|
|
172
|
-
| `usePublishDatabaseSchemaMutation` | `hub.database.publishDatabaseSchema`
|
|
173
|
-
| `useDeleteDatabaseSchemaMutation`
|
|
174
|
-
| `useGetEmailTemplatesQuery`
|
|
175
|
-
| `useGetEmailTemplateQuery`
|
|
176
|
-
| `useCreateEmailTemplateMutation`
|
|
177
|
-
| `useUpdateEmailTemplateMutation`
|
|
178
|
-
| `useDeleteEmailTemplateMutation`
|
|
179
|
-
| `useTestFilesIntegrationMutation`
|
|
180
|
-
| `useMakeFilePublicMutation`
|
|
181
|
-
| `useMakeFilePrivateMutation`
|
|
182
|
-
| `useMakeFolderPublicMutation`
|
|
183
|
-
| `useMakeFolderPrivateMutation`
|
|
156
|
+
| Hook | Wraps | Cache tags |
|
|
157
|
+
| ---------------------------------- | --------------------------------------- | ---------------------------------------------- |
|
|
158
|
+
| `useGetAccountProfileQuery` | `hub.account.getAccountProfile` | provides `AccountProfile/CURRENT` |
|
|
159
|
+
| `useGetAccountStatusQuery` | `hub.account.getAccountStatus` | provides `AccountProfile/STATUS` |
|
|
160
|
+
| `useUpdateAccountProfileMutation` | `hub.account.updateAccountProfile` | invalidates `AccountProfile/CURRENT` |
|
|
161
|
+
| `useGetProjectsQuery` | `hub.account.getProjects` | provides `AccountProfile/PROJECTS` |
|
|
162
|
+
| `useGetProjectQuery` | `hub.account.getProject` | provides `AccountProfile/PROJECT/<id>` |
|
|
163
|
+
| `useCreateProjectMutation` | `hub.account.createProject` | invalidates `AccountProfile/PROJECTS` |
|
|
164
|
+
| `useDeleteProjectMutation` | `hub.account.deleteProject` | invalidates `AccountProfile/PROJECTS` |
|
|
165
|
+
| `useListRegionsQuery` | `hub.regions.list` | provides `Regions` |
|
|
166
|
+
| `useUpdateProjectRegionsMutation` | `hub.regions.updateProjectRegions` | invalidates `Regions` + `Projects` |
|
|
167
|
+
| `useGetDatabaseSchemasQuery` | `hub.database.getDatabaseSchemas` | provides `Schema/LIST` |
|
|
168
|
+
| `useGetDatabaseSchemaQuery` | `hub.database.getDatabaseSchema` | provides `Schema/<id>` |
|
|
169
|
+
| `useSaveDatabaseSchemaMutation` | `hub.database.saveDatabaseSchema` | invalidates `Schema/LIST` |
|
|
170
|
+
| `useRenameDatabaseSchemaMutation` | `hub.database.renameDatabaseSchema` | invalidates `Schema/LIST` + `Schema/<id>` |
|
|
171
|
+
| `usePublishDatabaseSchemaMutation` | `hub.database.publishDatabaseSchema` | invalidates `Schema` + `Collection` |
|
|
172
|
+
| `useDeleteDatabaseSchemaMutation` | `hub.database.deleteDatabaseSchema` | invalidates `Schema/LIST` + `Collection` |
|
|
173
|
+
| `useGetEmailTemplatesQuery` | `hub.notifications.getEmailTemplates` | provides `Notification/EMAIL_TEMPLATE_LIST` |
|
|
174
|
+
| `useGetEmailTemplateQuery` | `hub.notifications.getEmailTemplate` | provides `Notification/EMAIL_TEMPLATE/<id>` |
|
|
175
|
+
| `useCreateEmailTemplateMutation` | `hub.notifications.createEmailTemplate` | invalidates `Notification/EMAIL_TEMPLATE_LIST` |
|
|
176
|
+
| `useUpdateEmailTemplateMutation` | `hub.notifications.updateEmailTemplate` | invalidates list + per-id |
|
|
177
|
+
| `useDeleteEmailTemplateMutation` | `hub.notifications.deleteEmailTemplate` | invalidates list |
|
|
178
|
+
| `useTestFilesIntegrationMutation` | `hub.files.testFilesIntegration` | — |
|
|
179
|
+
| `useMakeFilePublicMutation` | `hub.files.makeFilePublic` | invalidates `Files` |
|
|
180
|
+
| `useMakeFilePrivateMutation` | `hub.files.makeFilePrivate` | invalidates `Files` |
|
|
181
|
+
| `useMakeFolderPublicMutation` | `hub.files.makeFolderPublic` | invalidates `Files` |
|
|
182
|
+
| `useMakeFolderPrivateMutation` | `hub.files.makeFolderPrivate` | invalidates `Files` |
|
|
184
183
|
|
|
185
184
|
> **Need a hook we don't ship?** Two options. (1) Drop down to the SDK with `useNorbix()` for a one-off call. (2) Add a new file under `src/hooks/api/` or `src/hooks/hub/`, follow the pattern of the others, and open a PR.
|
|
186
185
|
|
|
186
|
+
### End-user AI chat — `norbix.api.ai`
|
|
187
|
+
|
|
188
|
+
For a signed-in project user. `useStartEndUserChatTurnMutation` answers at once with a `turnId`; the answer streams on the user's SSE channel `ai-chat:{projectId}:{authId}` — open it with `norbix.aiChat({ authId })` from `@norbix.ai/ts` (a foreign channel is refused with 403 `AiChatChannelRefused`, no retry). Needs `@norbix.ai/ts` >= 4.3.0.
|
|
189
|
+
|
|
190
|
+
| Hook | SDK method | Cache |
|
|
191
|
+
| ---------------------------------------- | ------------------------------------ | -------------------- |
|
|
192
|
+
| `useGetEndUserChatAvailabilityQuery` | `api.ai.getEndUserChatAvailability` | provides `AiChat` |
|
|
193
|
+
| `useListEndUserChatSessionsQuery` | `api.ai.listEndUserChatSessions` | provides `AiChat` |
|
|
194
|
+
| `useCreateEndUserChatSessionMutation` | `api.ai.createEndUserChatSession` | invalidates `AiChat` |
|
|
195
|
+
| `useGetEndUserChatSessionQuery` | `api.ai.getEndUserChatSession` | provides `AiChat` |
|
|
196
|
+
| `useRenameEndUserChatSessionMutation` | `api.ai.renameEndUserChatSession` | invalidates `AiChat` |
|
|
197
|
+
| `useDeleteEndUserChatSessionMutation` | `api.ai.deleteEndUserChatSession` | invalidates `AiChat` |
|
|
198
|
+
| `usePinEndUserChatSessionMutation` | `api.ai.pinEndUserChatSession` | invalidates `AiChat` |
|
|
199
|
+
| `useArchiveEndUserChatSessionMutation` | `api.ai.archiveEndUserChatSession` | invalidates `AiChat` |
|
|
200
|
+
| `useGetEndUserChatEntriesQuery` | `api.ai.getEndUserChatEntries` | provides `AiChat` |
|
|
201
|
+
| `useSetEndUserChatEntryFeedbackMutation` | `api.ai.setEndUserChatEntryFeedback` | invalidates `AiChat` |
|
|
202
|
+
| `useListEndUserChatAttachmentsQuery` | `api.ai.listEndUserChatAttachments` | provides `AiChat` |
|
|
203
|
+
| `useUploadEndUserChatAttachmentMutation` | `api.ai.uploadEndUserChatAttachment` | invalidates `AiChat` |
|
|
204
|
+
| `useDeleteEndUserChatAttachmentMutation` | `api.ai.deleteEndUserChatAttachment` | invalidates `AiChat` |
|
|
205
|
+
| `useListEndUserChatMemoryQuery` | `api.ai.listEndUserChatMemory` | provides `AiChat` |
|
|
206
|
+
| `useForgetEndUserChatMemoryMutation` | `api.ai.forgetEndUserChatMemory` | invalidates `AiChat` |
|
|
207
|
+
| `useStartEndUserChatTurnMutation` | `api.ai.startEndUserChatTurn` | invalidates `AiChat` |
|
|
208
|
+
|
|
209
|
+
### Project AI settings, assistants, usage, Admin Portal — `norbix.hub.account` / embeddings — `norbix.hub.ai`
|
|
210
|
+
|
|
211
|
+
| Hook | SDK method | Cache |
|
|
212
|
+
| --------------------------------------- | -------------------------------------- | ---------------------- |
|
|
213
|
+
| `useGetProjectAiSettingsQuery` | `hub.account.getProjectAiSettings` | provides `Projects` |
|
|
214
|
+
| `useUpdateProjectAiSettingsMutation` | `hub.account.updateProjectAiSettings` | invalidates `Projects` |
|
|
215
|
+
| `useCreateProjectAiAssistantMutation` | `hub.account.createProjectAiAssistant` | invalidates `Projects` |
|
|
216
|
+
| `useUpdateProjectAiAssistantMutation` | `hub.account.updateProjectAiAssistant` | invalidates `Projects` |
|
|
217
|
+
| `useDeleteProjectAiAssistantMutation` | `hub.account.deleteProjectAiAssistant` | invalidates `Projects` |
|
|
218
|
+
| `useGetProjectAiUsageQuery` | `hub.account.getProjectAiUsage` | provides `Projects` |
|
|
219
|
+
| `useSetAdminPortalEnabledMutation` | `hub.account.setAdminPortalEnabled` | invalidates `Projects` |
|
|
220
|
+
| `useGetEmbeddingIntegrationsQuery` | `hub.ai.getEmbeddingIntegrations` | provides `Ai` |
|
|
221
|
+
| `useSaveEmbeddingIntegrationMutation` | `hub.ai.saveEmbeddingIntegration` | invalidates `Ai` |
|
|
222
|
+
| `useGetEmbeddingIntegrationQuery` | `hub.ai.getEmbeddingIntegration` | provides `Ai` |
|
|
223
|
+
| `useDeleteEmbeddingIntegrationMutation` | `hub.ai.deleteEmbeddingIntegration` | invalidates `Ai` |
|
|
224
|
+
| `useTestEmbeddingIntegrationMutation` | `hub.ai.testEmbeddingIntegration` | invalidates `Ai` |
|
|
225
|
+
| `useSetLlmIntegrationAsDefaultMutation` | `hub.ai.setLlmIntegrationAsDefault` | invalidates `Ai` |
|
|
226
|
+
|
|
187
227
|
## Public file links
|
|
188
228
|
|
|
189
229
|
A file, or a whole folder prefix, can be made readable by anyone holding its
|
|
190
|
-
link. Publishing is a Hub action and needs the signed-in client;
|
|
230
|
+
link. Publishing is a Hub action and needs the signed-in client; _reading_ the
|
|
191
231
|
link needs nothing at all.
|
|
192
232
|
|
|
193
233
|
```tsx
|
|
@@ -221,10 +261,10 @@ small file, read it, list the folder, delete the file — and answer one item
|
|
|
221
261
|
per step (`UploadFile`, `GetFile`, `GetAllFiles`, `DeleteFile`) with `result`
|
|
222
262
|
`OK`, `FAILED` or `NOT_TESTED`:
|
|
223
263
|
|
|
224
|
-
| Hook
|
|
225
|
-
|
|
264
|
+
| Hook | Wraps | Route | Use it from |
|
|
265
|
+
| ------------------------------------ | -------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------ |
|
|
226
266
|
| `useTestFilesIntegrationApiMutation` | `api.files.testFilesIntegration` | `POST /{version}/files/{filesIntegrationId}/test` | your app, with an API key or a user session (needs `files:create`) |
|
|
227
|
-
| `useTestFilesIntegrationMutation`
|
|
267
|
+
| `useTestFilesIntegrationMutation` | `hub.files.testFilesIntegration` | `POST /{version}/files/integrations/test` | a dashboard, signed in to the Hub |
|
|
228
268
|
|
|
229
269
|
```tsx
|
|
230
270
|
const [testIntegration, { data, isLoading }] = useTestFilesIntegrationApiMutation();
|
|
@@ -279,12 +319,12 @@ section) — the shipped ones above are the typed variants.
|
|
|
279
319
|
|
|
280
320
|
A **taxonomy** is a named tree of **terms** (labels). A term can have one parent (a clean hierarchy) or several parents (the same item under many categories). There is one read hook per scenario — pick the one that matches what you want:
|
|
281
321
|
|
|
282
|
-
| I want to…
|
|
283
|
-
|
|
|
284
|
-
| Get a taxonomy's terms as a flat list
|
|
285
|
-
| Get only the children of one term
|
|
286
|
-
| Get a taxonomy's terms as a ready-made tree
|
|
287
|
-
| Get the taxonomy structure (e.g. Countries → Cities) | `useFindTaxonomyTreeQuery`
|
|
322
|
+
| I want to… | Hook | `data` holds |
|
|
323
|
+
| ---------------------------------------------------- | --------------------------- | ----------------------------------------------- |
|
|
324
|
+
| Get a taxonomy's terms as a flat list | `useFindTermsQuery` | a paginated `list` of terms |
|
|
325
|
+
| Get only the children of one term | `useFindTermsChildrenQuery` | a `list` of child terms (direct + multi-parent) |
|
|
326
|
+
| Get a taxonomy's terms as a ready-made tree | `useFindTermTreeQuery` | a `tree` of nested term nodes |
|
|
327
|
+
| Get the taxonomy structure (e.g. Countries → Cities) | `useFindTaxonomyTreeQuery` | a `tree` of taxonomy nodes |
|
|
288
328
|
|
|
289
329
|
All four share the same cache tag, so a write to a taxonomy refreshes every term view automatically. The examples below all use one example `services` taxonomy shaped like this:
|
|
290
330
|
|
|
@@ -310,13 +350,46 @@ const { data, isLoading } = useFindTermsQuery({ taxonomyName: 'services' });
|
|
|
310
350
|
{
|
|
311
351
|
"list": {
|
|
312
352
|
"items": [
|
|
313
|
-
{
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
353
|
+
{
|
|
354
|
+
"id": "term_indoors",
|
|
355
|
+
"taxonomyName": "services",
|
|
356
|
+
"parentId": null,
|
|
357
|
+
"order": 1,
|
|
358
|
+
"name": "Indoors"
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"id": "term_air_con",
|
|
362
|
+
"taxonomyName": "services",
|
|
363
|
+
"parentId": "term_indoors",
|
|
364
|
+
"order": 1,
|
|
365
|
+
"name": "Air conditioning"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"id": "term_wall",
|
|
369
|
+
"taxonomyName": "services",
|
|
370
|
+
"parentId": "term_air_con",
|
|
371
|
+
"order": 1,
|
|
372
|
+
"name": "Wall-mounted"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"id": "term_outdoors",
|
|
376
|
+
"taxonomyName": "services",
|
|
377
|
+
"parentId": null,
|
|
378
|
+
"order": 2,
|
|
379
|
+
"name": "Outdoors"
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"id": "term_solar",
|
|
383
|
+
"taxonomyName": "services",
|
|
384
|
+
"parentId": "term_outdoors",
|
|
385
|
+
"order": 1,
|
|
386
|
+
"name": "Solar panels"
|
|
387
|
+
}
|
|
318
388
|
],
|
|
319
|
-
"hasMore": false,
|
|
389
|
+
"hasMore": false,
|
|
390
|
+
"hasPrevious": false,
|
|
391
|
+
"startingAfter": null,
|
|
392
|
+
"endingBefore": null
|
|
320
393
|
},
|
|
321
394
|
"responseStatus": { "isSuccess": true }
|
|
322
395
|
}
|
|
@@ -341,10 +414,25 @@ const { data } = useFindTermsQuery({
|
|
|
341
414
|
{
|
|
342
415
|
"list": {
|
|
343
416
|
"items": [
|
|
344
|
-
{
|
|
345
|
-
|
|
417
|
+
{
|
|
418
|
+
"id": "term_indoors",
|
|
419
|
+
"taxonomyName": "services",
|
|
420
|
+
"parentId": null,
|
|
421
|
+
"order": 1,
|
|
422
|
+
"name": "Indoors"
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
"id": "term_outdoors",
|
|
426
|
+
"taxonomyName": "services",
|
|
427
|
+
"parentId": null,
|
|
428
|
+
"order": 2,
|
|
429
|
+
"name": "Outdoors"
|
|
430
|
+
}
|
|
346
431
|
],
|
|
347
|
-
"hasMore": false,
|
|
432
|
+
"hasMore": false,
|
|
433
|
+
"hasPrevious": false,
|
|
434
|
+
"startingAfter": null,
|
|
435
|
+
"endingBefore": null
|
|
348
436
|
},
|
|
349
437
|
"responseStatus": { "isSuccess": true }
|
|
350
438
|
}
|
|
@@ -356,7 +444,7 @@ const { data } = useFindTermsQuery({
|
|
|
356
444
|
|
|
357
445
|
### Get a term's children
|
|
358
446
|
|
|
359
|
-
**Goal:** the user expanded
|
|
447
|
+
**Goal:** the user expanded _Indoors_ — load what is directly under it.
|
|
360
448
|
|
|
361
449
|
```tsx
|
|
362
450
|
const { data } = useFindTermsChildrenQuery({
|
|
@@ -376,12 +464,17 @@ const { data } = useFindTermsChildrenQuery({
|
|
|
376
464
|
"order": 1,
|
|
377
465
|
"name": "Air conditioning",
|
|
378
466
|
"multiParents": [
|
|
379
|
-
{ "taxonomyId": "tax_service_types", "parentId": "term_indoors",
|
|
380
|
-
{
|
|
467
|
+
{ "taxonomyId": "tax_service_types", "parentId": "term_indoors", "name": "Indoors" },
|
|
468
|
+
{
|
|
469
|
+
"taxonomyId": "tax_service_types",
|
|
470
|
+
"parentId": "term_energy_efficient",
|
|
471
|
+
"name": "Energy efficient"
|
|
472
|
+
}
|
|
381
473
|
]
|
|
382
474
|
}
|
|
383
475
|
],
|
|
384
|
-
"hasMore": false,
|
|
476
|
+
"hasMore": false,
|
|
477
|
+
"hasPrevious": false
|
|
385
478
|
},
|
|
386
479
|
"responseStatus": { "isSuccess": true }
|
|
387
480
|
}
|
|
@@ -393,7 +486,7 @@ This returns **both** direct children (their `parentId` is `term_indoors`) **and
|
|
|
393
486
|
|
|
394
487
|
### Multi-parent: one product in several categories
|
|
395
488
|
|
|
396
|
-
**Goal:** in a `products` taxonomy, a
|
|
489
|
+
**Goal:** in a `products` taxonomy, a _Relaxing massage oil_ belongs to _For couples_, _Gift ideas_, **and** _Body care_. Listing the children of **any** of those categories returns it.
|
|
397
490
|
|
|
398
491
|
```tsx
|
|
399
492
|
const { data } = useFindTermsChildrenQuery({
|
|
@@ -412,12 +505,13 @@ const { data } = useFindTermsChildrenQuery({
|
|
|
412
505
|
"name": "Relaxing massage oil",
|
|
413
506
|
"multiParents": [
|
|
414
507
|
{ "taxonomyId": "tax_categories", "parentId": "term_for_couples", "name": "For couples" },
|
|
415
|
-
{ "taxonomyId": "tax_categories", "parentId": "term_gift_ideas",
|
|
416
|
-
{ "taxonomyId": "tax_categories", "parentId": "term_body_care",
|
|
508
|
+
{ "taxonomyId": "tax_categories", "parentId": "term_gift_ideas", "name": "Gift ideas" },
|
|
509
|
+
{ "taxonomyId": "tax_categories", "parentId": "term_body_care", "name": "Body care" }
|
|
417
510
|
]
|
|
418
511
|
}
|
|
419
512
|
],
|
|
420
|
-
"hasMore": false,
|
|
513
|
+
"hasMore": false,
|
|
514
|
+
"hasPrevious": false
|
|
421
515
|
},
|
|
422
516
|
"responseStatus": { "isSuccess": true }
|
|
423
517
|
}
|
|
@@ -447,9 +541,7 @@ const { data } = useFindTermTreeQuery({ taxonomyName: 'services' });
|
|
|
447
541
|
"id": "term_air_con",
|
|
448
542
|
"name": "Air conditioning",
|
|
449
543
|
"order": 1,
|
|
450
|
-
"children": [
|
|
451
|
-
{ "id": "term_wall", "name": "Wall-mounted", "order": 1, "children": null }
|
|
452
|
-
]
|
|
544
|
+
"children": [{ "id": "term_wall", "name": "Wall-mounted", "order": 1, "children": null }]
|
|
453
545
|
}
|
|
454
546
|
]
|
|
455
547
|
},
|
|
@@ -457,9 +549,7 @@ const { data } = useFindTermTreeQuery({ taxonomyName: 'services' });
|
|
|
457
549
|
"id": "term_outdoors",
|
|
458
550
|
"name": "Outdoors",
|
|
459
551
|
"order": 2,
|
|
460
|
-
"children": [
|
|
461
|
-
{ "id": "term_solar", "name": "Solar panels", "order": 1, "children": null }
|
|
462
|
-
]
|
|
552
|
+
"children": [{ "id": "term_solar", "name": "Solar panels", "order": 1, "children": null }]
|
|
463
553
|
}
|
|
464
554
|
],
|
|
465
555
|
"responseStatus": { "isSuccess": true }
|
|
@@ -472,7 +562,7 @@ Roots are in `tree`; each node carries its own `children`; a leaf has `children:
|
|
|
472
562
|
|
|
473
563
|
### Get only a sub-tree, capped by depth
|
|
474
564
|
|
|
475
|
-
**Goal:** start from
|
|
565
|
+
**Goal:** start from _Indoors_ and go at most 2 levels deep.
|
|
476
566
|
|
|
477
567
|
```tsx
|
|
478
568
|
const { data } = useFindTermTreeQuery({
|
|
@@ -498,7 +588,7 @@ const { data } = useFindTermTreeQuery({
|
|
|
498
588
|
}
|
|
499
589
|
```
|
|
500
590
|
|
|
501
|
-
With `depth: 2` you get
|
|
591
|
+
With `depth: 2` you get _Indoors_ (level 1) and _Air conditioning_ (level 2); _Wall-mounted_ (level 3) is cut off, so _Air conditioning_ shows `children: null`.
|
|
502
592
|
|
|
503
593
|
---
|
|
504
594
|
|
|
@@ -519,7 +609,14 @@ const { data } = useFindTaxonomyTreeQuery({});
|
|
|
519
609
|
"taxonomySlug": "countries",
|
|
520
610
|
"parentId": null,
|
|
521
611
|
"children": [
|
|
522
|
-
{
|
|
612
|
+
{
|
|
613
|
+
"viewId": "txn_cities",
|
|
614
|
+
"taxonomyName": "Cities",
|
|
615
|
+
"taxonomySlug": "cities",
|
|
616
|
+
"parentId": "txn_countries",
|
|
617
|
+
"children": null,
|
|
618
|
+
"terms": null
|
|
619
|
+
}
|
|
523
620
|
],
|
|
524
621
|
"terms": null
|
|
525
622
|
}
|
|
@@ -550,7 +647,7 @@ const { data } = useFindTaxonomyTreeQuery({ includeTerms: true });
|
|
|
550
647
|
"parentId": null,
|
|
551
648
|
"terms": [
|
|
552
649
|
{ "id": "term_lt", "name": "Lithuania", "order": 1, "children": null },
|
|
553
|
-
{ "id": "term_lv", "name": "Latvia",
|
|
650
|
+
{ "id": "term_lv", "name": "Latvia", "order": 2, "children": null }
|
|
554
651
|
],
|
|
555
652
|
"children": [
|
|
556
653
|
{
|
|
@@ -560,7 +657,7 @@ const { data } = useFindTaxonomyTreeQuery({ includeTerms: true });
|
|
|
560
657
|
"parentId": "txn_countries",
|
|
561
658
|
"terms": [
|
|
562
659
|
{ "id": "term_vilnius", "name": "Vilnius", "order": 1, "children": null },
|
|
563
|
-
{ "id": "term_kaunas",
|
|
660
|
+
{ "id": "term_kaunas", "name": "Kaunas", "order": 2, "children": null }
|
|
564
661
|
],
|
|
565
662
|
"children": null
|
|
566
663
|
}
|
|
@@ -571,7 +668,7 @@ const { data } = useFindTaxonomyTreeQuery({ includeTerms: true });
|
|
|
571
668
|
}
|
|
572
669
|
```
|
|
573
670
|
|
|
574
|
-
Now each taxonomy node's `terms` holds that taxonomy's full term tree (same shape as `useFindTermTreeQuery`) —
|
|
671
|
+
Now each taxonomy node's `terms` holds that taxonomy's full term tree (same shape as `useFindTermTreeQuery`) — _Countries_ carries its countries, _Cities_ carries its cities.
|
|
575
672
|
|
|
576
673
|
> All four hooks also accept the usual RTK Query options (e.g. `{ skip: !ready }`), and an optional `databaseIntegrationId` in the argument to target a non-default database.
|
|
577
674
|
|
|
@@ -627,7 +724,7 @@ For RTK Query to follow tenant changes, recreate the API slice or pass a tenant-
|
|
|
627
724
|
|
|
628
725
|
### Multi-region projects
|
|
629
726
|
|
|
630
|
-
A Norbix project spans one **primary region** plus any number of **additional regions**. Two concerns, two homes:
|
|
727
|
+
A Norbix project spans one **primary region** plus any number of **additional regions**. Two concerns, two homes: _which region your requests target_ is configured on the wrapped `norbix` client (this package inherits it — every hook just calls the client you passed to `createNorbixApi`), while _which regions a project spans_ is managed through the two hooks below.
|
|
631
728
|
|
|
632
729
|
**Configure the target region on the wrapped client.**
|
|
633
730
|
|
|
@@ -689,7 +786,7 @@ function RegionSettings({ projectId }: { projectId: string }) {
|
|
|
689
786
|
```
|
|
690
787
|
|
|
691
788
|
- `useListRegionsQuery` wraps `norbix.hub.regions.list` and **provides the `Regions` tag**. As with every query hook, a lazy variant — `useLazyListRegionsQuery` — is generated alongside it.
|
|
692
|
-
- `useUpdateProjectRegionsMutation` wraps `norbix.hub.regions.updateProjectRegions({ projectId, primaryRegion?, additionalRegions? })` and **invalidates `Regions` and `Projects`** — changing the regions a project spans updates the project DTO (`primaryRegion` / `additionalRegions`), so cached region lists
|
|
789
|
+
- `useUpdateProjectRegionsMutation` wraps `norbix.hub.regions.updateProjectRegions({ projectId, primaryRegion?, additionalRegions? })` and **invalidates `Regions` and `Projects`** — changing the regions a project spans updates the project DTO (`primaryRegion` / `additionalRegions`), so cached region lists _and_ project queries refetch automatically once the mutation resolves.
|
|
693
790
|
|
|
694
791
|
> `hub.account` carries `getAccountRegions` / `updateProjectRegions` aliases for the same wire endpoints (`useGetAccountRegionsQuery` tags `Account`). The `hub.regions` hooks above are the canonical pair — they are the ones wired to the `Regions` tag.
|
|
695
792
|
|
|
@@ -765,17 +862,17 @@ norbixApi.injectEndpoints({
|
|
|
765
862
|
|
|
766
863
|
This produces all hooks automatically — `useGetEmailIntegrationsQuery`, `useSaveEmailIntegrationMutation`, `useTestEmailIntegrationMutation`, `useConfirmEmailIntegrationHumanDeliveryMutation`, and the same for push/sms/files/payments/etc.
|
|
767
864
|
|
|
768
|
-
| Module
|
|
769
|
-
|
|
770
|
-
| Email
|
|
771
|
-
| Push
|
|
772
|
-
| Sms
|
|
773
|
-
| Files
|
|
774
|
-
| Payments
|
|
775
|
-
| Code
|
|
776
|
-
| Membership | `Membership`
|
|
777
|
-
| Logs
|
|
778
|
-
| Database
|
|
865
|
+
| Module | Prefix | Namespace | Tag | Notes |
|
|
866
|
+
| ---------- | ---------------------------------------- | --------------------- | ------------------------ | ----------------------------------- |
|
|
867
|
+
| Email | `Email` | `n.hub.notifications` | `EmailIntegrations` | has `test` + `confirmHumanDelivery` |
|
|
868
|
+
| Push | `Push` | `n.hub.notifications` | `PushIntegrations` | has `test` + `confirmHumanDelivery` |
|
|
869
|
+
| Sms | `Sms` | `n.hub.notifications` | `SmsIntegrations` | has `test` + `confirmHumanDelivery` |
|
|
870
|
+
| Files | `Files` | `n.hub.files` | `FilesIntegrations` | has `test` |
|
|
871
|
+
| Payments | `Payments` | `n.hub.payments` | `PaymentIntegrations` | has `test` + `confirmHumanDelivery` |
|
|
872
|
+
| Code | `Code` | `n.hub.code` | `CodeIntegrations` | has `test` |
|
|
873
|
+
| Membership | `Membership` | `n.hub.membership` | `MembershipIntegrations` | no `test` |
|
|
874
|
+
| Logs | `Logging` _(uses `Logging`, not `Logs`)_ | `n.hub.logs` | `LogsIntegrations` | no `test` |
|
|
875
|
+
| Database | `Database` | `n.hub.database` | `DatabaseIntegrations` | already wired in package |
|
|
779
876
|
|
|
780
877
|
**Trade-off.** The helper is terse but loses static request/response typing — generated hooks return `any` data, because endpoint keys are computed at runtime. If you need full types on a specific integration surface (autocompletion in your IDE), define those endpoints manually instead, following the canonical shape used by `hub.database` integrations in `src/hooks/hub/database.ts`.
|
|
781
878
|
|
|
@@ -791,9 +888,10 @@ import type { useNorbix } from '@norbix.ai/react-redux';
|
|
|
791
888
|
export const myCampaignsService = norbixApi.injectEndpoints({
|
|
792
889
|
endpoints: (builder) => ({
|
|
793
890
|
getEmailCampaigns: builder.query({
|
|
794
|
-
query: (args) => (norbix) =>
|
|
795
|
-
|
|
796
|
-
|
|
891
|
+
query: (args) => (norbix) =>
|
|
892
|
+
norbix.hub.notifications
|
|
893
|
+
? norbix.hub.notifications.getEmailTemplates(args) // example only
|
|
894
|
+
: Promise.resolve({}),
|
|
797
895
|
providesTags: [{ type: 'EmailCampaigns', id: 'LIST' }],
|
|
798
896
|
}),
|
|
799
897
|
// ...more app-specific endpoints
|