@odla-ai/chapter 0.20.0 → 0.20.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/README.md +122 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -154,10 +154,10 @@ export default {
|
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
That is the reusable application shell. The follower's public pages remain
|
|
157
|
-
site-owned: start from
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
site-owned: start from a reference design and `@odla-ai/ui` marketing
|
|
158
|
+
components, then change the wordmark, palette, copy, imagery, and investment
|
|
159
|
+
thesis. Do not fork auth, admin routing, CRM, payment, booking, or account logic
|
|
160
|
+
to achieve a different brand.
|
|
161
161
|
|
|
162
162
|
### Leader → follower delivery
|
|
163
163
|
|
|
@@ -190,9 +190,126 @@ config. The standard collection drawer discovers the target and calls the
|
|
|
190
190
|
admin-gated push route. A follower must declare the receiving CRM type/fields;
|
|
191
191
|
otherwise it rejects the record cleanly instead of dropping fields.
|
|
192
192
|
|
|
193
|
+
#### Existing follower CRMs must opt into the shared graph
|
|
194
|
+
|
|
195
|
+
When `crm` is omitted, chapter's default already contains compatible `person`
|
|
196
|
+
and `company` types plus a `works_at` relation. Passing a custom CRM replaces
|
|
197
|
+
that default; chapter does not merge missing types or fields into it. A follower
|
|
198
|
+
that wants both people and businesses must therefore declare compatible types
|
|
199
|
+
itself:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { defineCrm } from "@odla-ai/crm";
|
|
203
|
+
|
|
204
|
+
const crm = defineCrm({
|
|
205
|
+
types: {
|
|
206
|
+
person: {
|
|
207
|
+
label: "Person",
|
|
208
|
+
labelPlural: "People",
|
|
209
|
+
nameField: "name",
|
|
210
|
+
emailField: "email",
|
|
211
|
+
fields: {
|
|
212
|
+
name: { type: "string", label: "Name", required: true },
|
|
213
|
+
email: { type: "email", label: "Email" },
|
|
214
|
+
firstName: { type: "string", label: "First name" },
|
|
215
|
+
lastName: { type: "string", label: "Last name" },
|
|
216
|
+
phone: { type: "string", label: "Phone" },
|
|
217
|
+
linkedin: { type: "string", label: "LinkedIn" },
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
company: {
|
|
221
|
+
label: "Business",
|
|
222
|
+
labelPlural: "Businesses",
|
|
223
|
+
nameField: "name",
|
|
224
|
+
fields: {
|
|
225
|
+
name: { type: "string", label: "Name", required: true },
|
|
226
|
+
domain: { type: "string", label: "Domain / website", slot: "s1" },
|
|
227
|
+
industry: { type: "string", label: "Industry" },
|
|
228
|
+
location: { type: "string", label: "Location" },
|
|
229
|
+
linkedin: { type: "string", label: "LinkedIn" },
|
|
230
|
+
notes: { type: "string", label: "Notes" },
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
relations: {
|
|
235
|
+
works_at: {
|
|
236
|
+
from: "person",
|
|
237
|
+
to: "company",
|
|
238
|
+
label: "works at",
|
|
239
|
+
reverseLabel: "team",
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The follower must declare every field its leader may send. Unknown types or
|
|
246
|
+
fields fail the request before any CRM write. Record delivery currently moves
|
|
247
|
+
records, not `crm_link` relation rows; create or curate `works_at` links locally.
|
|
248
|
+
|
|
249
|
+
#### Custom leader consoles must mount the sharing UI
|
|
250
|
+
|
|
251
|
+
Automatic “Share with …” actions come from chapter's standard collection
|
|
252
|
+
sections. They are present when the console uses
|
|
253
|
+
`<ChapterAdmin chapter={chapter} />`. Passing an explicit `sections` array
|
|
254
|
+
replaces that catalog, so a custom console must deliberately compose it:
|
|
255
|
+
|
|
256
|
+
```tsx
|
|
257
|
+
import {
|
|
258
|
+
ChapterAdmin,
|
|
259
|
+
defaultAdminSections,
|
|
260
|
+
} from "@odla-ai/chapter/ui/admin";
|
|
261
|
+
|
|
262
|
+
const sections = [
|
|
263
|
+
customOverviewSection,
|
|
264
|
+
...defaultAdminSections(chapter),
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
render(
|
|
268
|
+
<ChapterAdmin chapter={chapter} sections={sections} />,
|
|
269
|
+
document.getElementById("admin-root"),
|
|
270
|
+
);
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
If the custom console renders its own record drawer instead, mount
|
|
274
|
+
`NetworkShareActions` inside that drawer:
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
<NetworkShareActions
|
|
278
|
+
recordId={record.id}
|
|
279
|
+
recordType={record.type}
|
|
280
|
+
getToken={sectionContext.getToken}
|
|
281
|
+
/>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The component discovers compatible targets through
|
|
285
|
+
`GET /api/admin/network/targets`; it never receives follower secrets in browser
|
|
286
|
+
data.
|
|
287
|
+
|
|
288
|
+
#### Verify delivery in development before production
|
|
289
|
+
|
|
290
|
+
Use distinct development tenants and follower origins for the first delivery:
|
|
291
|
+
|
|
292
|
+
1. Vault one random value as `network_share_secret` in the follower and under
|
|
293
|
+
the target's resolved `secretName` in the leader.
|
|
294
|
+
2. Confirm `GET /api/admin/network/targets` lists the development follower with
|
|
295
|
+
the expected compatible record types.
|
|
296
|
+
3. Share one test person and one test business from the leader's record drawer.
|
|
297
|
+
Confirm each appears in the follower with only allowlisted fields.
|
|
298
|
+
4. Share each record again. The second delivery must update the same follower
|
|
299
|
+
record, not create a duplicate.
|
|
300
|
+
5. Confirm the follower's pipeline, account, and billing state did not change;
|
|
301
|
+
those remain locally authoritative.
|
|
302
|
+
6. Change one allowlisted leader field and share again to prove later deliveries
|
|
303
|
+
update the existing record. A target with the wrong secret must return `401`
|
|
304
|
+
without writing CRM data.
|
|
305
|
+
|
|
306
|
+
Only after this contract passes against development origins should the leader
|
|
307
|
+
target be changed to a production follower origin and the matching production
|
|
308
|
+
vault values be installed.
|
|
309
|
+
|
|
193
310
|
Section navigation defaults to `/admin/?tab=people`, not
|
|
194
311
|
`/admin/people`. Query routing deliberately works in both leader-style SPAs and
|
|
195
|
-
|
|
312
|
+
ordinary static subdirectories. Legacy path links still open, and
|
|
196
313
|
`routing="path"` remains available only for hosts with an explicit SPA fallback.
|
|
197
314
|
|
|
198
315
|
## Adopting into an existing site
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@odla-ai/chapter",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"description": "A leader/follower foundation for branded membership sites: shared CRM, admin, auth, payments, booking, and explicit record delivery from one defineChapter config.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://odla.ai/docs/packages/chapter",
|