@contractspec/bundle.library 3.9.8 → 3.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.turbo/turbo-build.log +222 -214
  2. package/CHANGELOG.md +52 -0
  3. package/dist/components/docs/DocsIndexPage.js +2 -2
  4. package/dist/components/docs/docsManifest.js +1 -1
  5. package/dist/components/docs/getting-started/DataViewTutorialPage.js +81 -6
  6. package/dist/components/docs/getting-started/index.js +94 -19
  7. package/dist/components/docs/guides/GuideDataExchangeImportTemplatesPage.content.d.ts +6 -0
  8. package/dist/components/docs/guides/GuideDataExchangeImportTemplatesPage.content.js +176 -0
  9. package/dist/components/docs/guides/GuideDataExchangeImportTemplatesPage.d.ts +1 -0
  10. package/dist/components/docs/guides/GuideDataExchangeImportTemplatesPage.js +176 -0
  11. package/dist/components/docs/guides/GuidesIndexPage.js +2 -2
  12. package/dist/components/docs/guides/index.d.ts +1 -0
  13. package/dist/components/docs/guides/index.js +220 -46
  14. package/dist/components/docs/index.js +1003 -309
  15. package/dist/components/docs/libraries/LibrariesApplicationShellPage.content.d.ts +22 -5
  16. package/dist/components/docs/libraries/LibrariesApplicationShellPage.content.js +125 -37
  17. package/dist/components/docs/libraries/LibrariesApplicationShellPage.js +125 -37
  18. package/dist/components/docs/libraries/LibrariesDataViewsPage.js +120 -3
  19. package/dist/components/docs/libraries/LibrariesDesignSystemPage.js +101 -2
  20. package/dist/components/docs/libraries/LibrariesOverviewPage.js +1 -1
  21. package/dist/components/docs/libraries/LibrariesPersonalizationPage.js +58 -4
  22. package/dist/components/docs/libraries/LibrariesTranslationRuntimePage.content.d.ts +10 -0
  23. package/dist/components/docs/libraries/LibrariesTranslationRuntimePage.content.js +43 -0
  24. package/dist/components/docs/libraries/LibrariesTranslationRuntimePage.d.ts +1 -0
  25. package/dist/components/docs/libraries/LibrariesTranslationRuntimePage.js +43 -0
  26. package/dist/components/docs/libraries/index.d.ts +1 -0
  27. package/dist/components/docs/libraries/index.js +496 -97
  28. package/dist/components/docs/specs/SpecsDataViewsPage.js +49 -3
  29. package/dist/components/docs/specs/index.js +60 -14
  30. package/dist/index.js +1014 -320
  31. package/dist/node/components/docs/DocsIndexPage.js +2 -2
  32. package/dist/node/components/docs/docsManifest.js +1 -1
  33. package/dist/node/components/docs/getting-started/DataViewTutorialPage.js +81 -6
  34. package/dist/node/components/docs/getting-started/index.js +94 -19
  35. package/dist/node/components/docs/guides/GuideDataExchangeImportTemplatesPage.content.js +175 -0
  36. package/dist/node/components/docs/guides/GuideDataExchangeImportTemplatesPage.js +175 -0
  37. package/dist/node/components/docs/guides/GuidesIndexPage.js +2 -2
  38. package/dist/node/components/docs/guides/index.js +220 -46
  39. package/dist/node/components/docs/index.js +1003 -309
  40. package/dist/node/components/docs/libraries/LibrariesApplicationShellPage.content.js +125 -37
  41. package/dist/node/components/docs/libraries/LibrariesApplicationShellPage.js +125 -37
  42. package/dist/node/components/docs/libraries/LibrariesDataViewsPage.js +120 -3
  43. package/dist/node/components/docs/libraries/LibrariesDesignSystemPage.js +101 -2
  44. package/dist/node/components/docs/libraries/LibrariesOverviewPage.js +1 -1
  45. package/dist/node/components/docs/libraries/LibrariesPersonalizationPage.js +58 -4
  46. package/dist/node/components/docs/libraries/LibrariesTranslationRuntimePage.content.js +42 -0
  47. package/dist/node/components/docs/libraries/LibrariesTranslationRuntimePage.js +42 -0
  48. package/dist/node/components/docs/libraries/index.js +496 -97
  49. package/dist/node/components/docs/specs/SpecsDataViewsPage.js +49 -3
  50. package/dist/node/components/docs/specs/index.js +60 -14
  51. package/dist/node/index.js +1014 -320
  52. package/package.json +74 -26
  53. package/src/components/docs/docsManifest.test.ts +87 -0
  54. package/src/components/docs/docsManifest.ts +90 -3
  55. package/src/components/docs/generated/docs-index.notifications.json +7 -7
  56. package/src/components/docs/getting-started/DataViewTutorialPage.tsx +181 -50
  57. package/src/components/docs/guides/GuideDataExchangeImportTemplatesPage.content.ts +185 -0
  58. package/src/components/docs/guides/GuideDataExchangeImportTemplatesPage.tsx +186 -0
  59. package/src/components/docs/guides/GuidesIndexPage.tsx +49 -42
  60. package/src/components/docs/guides/index.ts +1 -0
  61. package/src/components/docs/libraries/LibrariesApplicationShellPage.content.ts +148 -35
  62. package/src/components/docs/libraries/LibrariesApplicationShellPage.tsx +38 -5
  63. package/src/components/docs/libraries/LibrariesDataViewsPage.tsx +267 -64
  64. package/src/components/docs/libraries/LibrariesDesignSystemPage.tsx +235 -0
  65. package/src/components/docs/libraries/LibrariesOverviewPage.tsx +8 -2
  66. package/src/components/docs/libraries/LibrariesPersonalizationPage.tsx +141 -31
  67. package/src/components/docs/libraries/LibrariesTranslationRuntimePage.content.ts +78 -0
  68. package/src/components/docs/libraries/LibrariesTranslationRuntimePage.tsx +137 -0
  69. package/src/components/docs/libraries/index.ts +1 -0
  70. package/src/components/docs/specs/SpecsDataViewsPage.tsx +239 -113
@@ -1,4 +1,13 @@
1
1
  import { CodeBlock } from '@contractspec/lib.design-system';
2
+ import { HStack, VStack } from '@contractspec/lib.design-system/layout';
3
+ import { List, ListItem } from '@contractspec/lib.design-system/list';
4
+ import {
5
+ H1,
6
+ H2,
7
+ H3,
8
+ P,
9
+ Text,
10
+ } from '@contractspec/lib.design-system/typography';
2
11
  import Link from '@contractspec/lib.ui-link';
3
12
  import { ChevronRight } from 'lucide-react';
4
13
 
@@ -64,8 +73,41 @@ export const TransactionHistory = defineDataView({
64
73
  dataPath: 'processingMinutes',
65
74
  format: { type: 'duration', unit: 'minute', display: 'digital' },
66
75
  },
67
- { key: 'notes', label: 'Notes', dataPath: 'notes' },
76
+ {
77
+ key: 'notes',
78
+ label: 'Notes',
79
+ dataPath: 'notes',
80
+ visibility: { minDataDepth: 'detailed' },
81
+ },
68
82
  ],
83
+ collection: {
84
+ viewModes: {
85
+ defaultMode: 'table',
86
+ allowedModes: ['list', 'grid', 'table'],
87
+ },
88
+ toolbar: {
89
+ search: true,
90
+ viewMode: true,
91
+ filters: true,
92
+ density: true,
93
+ dataDepth: true,
94
+ },
95
+ pagination: {
96
+ pageSize: 25,
97
+ pageSizeOptions: [10, 25, 50],
98
+ },
99
+ density: 'comfortable',
100
+ dataDepth: 'standard',
101
+ personalization: {
102
+ enabled: true,
103
+ persist: {
104
+ viewMode: true,
105
+ density: true,
106
+ dataDepth: true,
107
+ pageSize: true,
108
+ },
109
+ },
110
+ },
69
111
  filters: [
70
112
  { key: 'status', label: 'Status', field: 'status', type: 'enum' },
71
113
  {
@@ -88,20 +130,20 @@ export const TransactionHistory = defineDataView({
88
130
 
89
131
  export function DataViewTutorialPage() {
90
132
  return (
91
- <div className="space-y-8">
92
- <div className="space-y-4">
93
- <h1 className="font-bold text-4xl">Display Data with DataViews</h1>
94
- <p className="text-lg text-muted-foreground">
133
+ <VStack className="space-y-8">
134
+ <VStack className="space-y-4">
135
+ <H1 className="font-bold text-4xl">Display Data with DataViews</H1>
136
+ <P className="text-lg text-muted-foreground">
95
137
  Define a filterable, sortable transaction history view that works
96
138
  across web and mobile without duplicating UI code.
97
- </p>
98
- </div>
139
+ </P>
140
+ </VStack>
99
141
 
100
- <div className="space-y-4">
101
- <h2 className="font-bold text-2xl">1. Define the underlying query</h2>
102
- <p className="text-muted-foreground">
142
+ <VStack className="space-y-4">
143
+ <H2 className="font-bold text-2xl">1. Define the underlying query</H2>
144
+ <P className="text-muted-foreground">
103
145
  First, create a query operation to fetch the data:
104
- </p>
146
+ </P>
105
147
  <CodeBlock
106
148
  language="typescript"
107
149
  filename="lib/specs/billing/list-transactions.ts"
@@ -120,35 +162,35 @@ export const ListTransactions = defineQuery({
120
162
  policy: { auth: 'user' },
121
163
  });`}
122
164
  />
123
- </div>
165
+ </VStack>
124
166
 
125
- <div className="space-y-4">
126
- <h2 className="font-bold text-2xl">2. Define the DataView spec</h2>
127
- <p className="text-muted-foreground">
167
+ <VStack className="space-y-4">
168
+ <H2 className="font-bold text-2xl">2. Define the DataView spec</H2>
169
+ <P className="text-muted-foreground">
128
170
  Wrap your query with presentation metadata:
129
- </p>
171
+ </P>
130
172
  <CodeBlock
131
173
  language="typescript"
132
174
  filename="lib/specs/billing/transaction-history.data-view.ts"
133
175
  code={DATAVIEW_TUTORIAL_EXAMPLE}
134
176
  />
135
- <p className="text-muted-foreground text-sm">
177
+ <P className="text-muted-foreground text-sm">
136
178
  The live version of this pattern is available in the canonical{' '}
137
179
  <Link
138
180
  href="/docs/examples/data-grid-showcase"
139
181
  className="text-[color:var(--rust)] underline underline-offset-4"
140
182
  >
141
- Data Grid Showcase
183
+ <Text>Data Grid Showcase</Text>
142
184
  </Link>
143
185
  .
144
- </p>
145
- </div>
186
+ </P>
187
+ </VStack>
146
188
 
147
- <div className="space-y-4">
148
- <h2 className="font-bold text-2xl">3. Render on the frontend</h2>
149
- <p className="text-muted-foreground">
189
+ <VStack className="space-y-4">
190
+ <H2 className="font-bold text-2xl">3. Render on the frontend</H2>
191
+ <P className="text-muted-foreground">
150
192
  Use the runtime renderer in your React or React Native app:
151
- </p>
193
+ </P>
152
194
  <CodeBlock
153
195
  language="tsx"
154
196
  filename="app/dashboard/transactions/page.tsx"
@@ -169,8 +211,11 @@ export function TransactionsPage() {
169
211
  <h1 className="text-3xl font-bold mb-6">Payment History</h1>
170
212
  <DataViewRenderer
171
213
  spec={TransactionHistory}
172
- data={data?.items ?? []}
214
+ items={data?.items ?? []}
173
215
  loading={isLoading}
216
+ defaultViewMode="table"
217
+ defaultDensity="comfortable"
218
+ defaultDataDepth="standard"
174
219
  onFilterChange={(filters) => {
175
220
  // refetch with new filters
176
221
  }}
@@ -179,34 +224,120 @@ export function TransactionsPage() {
179
224
  );
180
225
  }`}
181
226
  />
182
- </div>
183
-
184
- <div className="card-subtle space-y-4 p-6">
185
- <h3 className="font-bold">Why DataViews?</h3>
186
- <ul className="space-y-2 text-muted-foreground text-sm">
187
- <li>Same spec renders on web (React) and mobile (React Native)</li>
188
- <li>Filters, sorting, and pagination handled automatically</li>
189
- <li>
190
- Column visibility, pinning, resizing, and row expansion stay
191
- contract-driven
192
- </li>
193
- <li>
194
- Typed format rules for numbers, percent values, currency, dates,
195
- times, datetimes, and durations applied consistently
196
- </li>
197
- <li>Export to CSV/PDF using the same spec</li>
198
- <li>A/B test different layouts without touching the backend</li>
199
- </ul>
200
- </div>
201
-
202
- <div className="flex items-center gap-4 pt-4">
227
+ </VStack>
228
+
229
+ <VStack className="space-y-4">
230
+ <H2 className="font-bold text-2xl">4. Add personalization</H2>
231
+ <P className="text-muted-foreground">
232
+ When the app has a user profile or behavior insights, resolve DataView
233
+ preferences before rendering. The renderer receives plain props;
234
+ personalization stays in the app/runtime boundary.
235
+ </P>
236
+ <CodeBlock
237
+ language="tsx"
238
+ filename="app/dashboard/transactions/PersonalizedTransactions.tsx"
239
+ code={`'use client';
240
+
241
+ import { DataViewRenderer } from '@contractspec/lib.design-system';
242
+ import { resolveDataViewPreferences } from '@contractspec/lib.personalization/data-view-preferences';
243
+ import { createBehaviorTracker } from '@contractspec/lib.personalization';
244
+
245
+ const tracker = createBehaviorTracker({
246
+ store,
247
+ context: { tenantId: tenant.id, userId: user.id },
248
+ });
249
+
250
+ const resolved = resolveDataViewPreferences({
251
+ spec: TransactionHistory,
252
+ preferences: profile.canonical,
253
+ insights,
254
+ record: savedTransactionViewPreference,
255
+ });
256
+
257
+ <DataViewRenderer
258
+ spec={TransactionHistory}
259
+ items={transactions}
260
+ defaultViewMode={resolved.viewMode}
261
+ defaultDensity={resolved.density}
262
+ defaultDataDepth={resolved.dataDepth}
263
+ pagination={{ page, pageSize: resolved.pageSize ?? 25, total }}
264
+ onViewModeChange={(viewMode) =>
265
+ tracker.trackDataViewInteraction({
266
+ dataViewKey: TransactionHistory.meta.key,
267
+ action: 'view_mode_changed',
268
+ viewMode,
269
+ })
270
+ }
271
+ onDataDepthChange={(dataDepth) =>
272
+ tracker.trackDataViewInteraction({
273
+ dataViewKey: TransactionHistory.meta.key,
274
+ action: 'data_depth_changed',
275
+ dataDepth,
276
+ })
277
+ }
278
+ />;`}
279
+ />
280
+ </VStack>
281
+
282
+ <VStack className="card-subtle space-y-4 p-6">
283
+ <H3 className="font-bold">Why DataViews?</H3>
284
+ <List className="space-y-2 text-muted-foreground text-sm">
285
+ <ListItem>
286
+ <Text>
287
+ Same spec renders on web (React) and mobile (React Native)
288
+ </Text>
289
+ </ListItem>
290
+ <ListItem>
291
+ <Text>Filters, sorting, and pagination handled automatically</Text>
292
+ </ListItem>
293
+ <ListItem>
294
+ <Text>
295
+ Column visibility, pinning, resizing, and row expansion stay
296
+ contract-driven
297
+ </Text>
298
+ </ListItem>
299
+ <ListItem>
300
+ <Text>
301
+ List, grid, and table modes can share one collection config with
302
+ toolbar and pagination defaults
303
+ </Text>
304
+ </ListItem>
305
+ <ListItem>
306
+ <Text>
307
+ Data depth lets summary screens hide detailed fields without
308
+ forking the spec
309
+ </Text>
310
+ </ListItem>
311
+ <ListItem>
312
+ <Text>
313
+ Typed format rules for numbers, percent values, currency, dates,
314
+ times, datetimes, and durations applied consistently
315
+ </Text>
316
+ </ListItem>
317
+ <ListItem>
318
+ <Text>
319
+ Personalization helpers can seed preferred view mode, density,
320
+ data depth, and page size from user preferences or behavior
321
+ insights
322
+ </Text>
323
+ </ListItem>
324
+ <ListItem>
325
+ <Text>Export to CSV/PDF using the same spec</Text>
326
+ </ListItem>
327
+ <ListItem>
328
+ <Text>A/B test different layouts without touching the backend</Text>
329
+ </ListItem>
330
+ </List>
331
+ </VStack>
332
+
333
+ <HStack className="items-center gap-4 pt-4">
203
334
  <Link href="/docs/libraries/data-views" className="btn-primary">
204
- DataView API Reference <ChevronRight size={16} />
335
+ <Text>DataView API Reference</Text> <ChevronRight size={16} />
205
336
  </Link>
206
337
  <Link href="/docs/specs/workflows" className="btn-ghost">
207
- Next: Workflows
338
+ <Text>Next: Workflows</Text>
208
339
  </Link>
209
- </div>
210
- </div>
340
+ </HStack>
341
+ </VStack>
211
342
  );
212
343
  }
@@ -0,0 +1,185 @@
1
+ export const accountImportTemplateCode = `import {
2
+ createImportPlan,
3
+ createRecordBatch,
4
+ defineDataExchangeTemplate,
5
+ previewImport,
6
+ } from "@contractspec/lib.data-exchange-core";
7
+ import { defineSchemaModel, ScalarTypeEnum } from "@contractspec/lib.schema";
8
+
9
+ const AccountImportSchema = defineSchemaModel({
10
+ name: "AccountImport",
11
+ fields: {
12
+ id: { type: ScalarTypeEnum.ID(), isOptional: false },
13
+ status: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
14
+ amount: { type: ScalarTypeEnum.Float_unsecure(), isOptional: false },
15
+ active: { type: ScalarTypeEnum.Boolean(), isOptional: false },
16
+ tags: { type: ScalarTypeEnum.JSON(), isOptional: true },
17
+ },
18
+ });
19
+
20
+ const accountImportTemplate = defineDataExchangeTemplate({
21
+ key: "accounts.import",
22
+ version: "1.0.0",
23
+ title: "Account import",
24
+ columns: [
25
+ {
26
+ key: "id",
27
+ label: "Account ID",
28
+ targetField: "id",
29
+ required: true,
30
+ sourceAliases: ["Account Identifier", "External ID", "No compte"],
31
+ },
32
+ {
33
+ key: "status",
34
+ label: "Status",
35
+ targetField: "status",
36
+ required: true,
37
+ sourceAliases: ["Statut", "State"],
38
+ format: { kind: "text", trim: true, case: "lowercase" },
39
+ },
40
+ {
41
+ key: "amount",
42
+ label: "Amount",
43
+ targetField: "amount",
44
+ required: true,
45
+ sourceAliases: ["Montant", "Balance"],
46
+ format: { kind: "number", decimalSeparator: ",", thousandsSeparator: "." },
47
+ },
48
+ {
49
+ key: "active",
50
+ label: "Active",
51
+ targetField: "active",
52
+ sourceAliases: ["Actif", "Enabled"],
53
+ format: { kind: "boolean", trueValues: ["yes", "oui"], falseValues: ["no", "non"] },
54
+ },
55
+ {
56
+ key: "tags",
57
+ label: "Tags",
58
+ targetField: "tags",
59
+ format: { kind: "split", delimiter: ";" },
60
+ },
61
+ ],
62
+ });
63
+
64
+ const sourceBatch = createRecordBatch([
65
+ {
66
+ "No compte": "acc-1",
67
+ Statut: " Active ",
68
+ Montant: "1.234,50",
69
+ Actif: "oui",
70
+ Tags: "vip; beta",
71
+ },
72
+ ]);
73
+
74
+ const preview = previewImport(
75
+ createImportPlan({
76
+ source: { kind: "memory", batch: sourceBatch, format: "csv" },
77
+ target: { kind: "memory", format: "json" },
78
+ schema: AccountImportSchema,
79
+ sourceBatch,
80
+ template: accountImportTemplate,
81
+ }),
82
+ );
83
+
84
+ console.log(preview.plan.mappingSource); // "template"
85
+ console.log(preview.normalizedRecords[0]);`;
86
+
87
+ export const serverDryRunCode = `import { defineDataExchangeTemplate } from "@contractspec/lib.data-exchange-core";
88
+ import { dryRunImport, executeImport } from "@contractspec/lib.data-exchange-server";
89
+
90
+ const template = defineDataExchangeTemplate({
91
+ key: "accounts.import",
92
+ version: "1.0.0",
93
+ columns: [
94
+ { key: "id", label: "Account ID", targetField: "id", required: true, sourceAliases: ["Account Identifier"] },
95
+ { key: "amount", label: "Amount", targetField: "amount", format: { kind: "currency", currencySymbol: "€", decimalSeparator: "," } },
96
+ ],
97
+ });
98
+
99
+ const partnerSource = {
100
+ kind: "file",
101
+ path: "partner-accounts.csv",
102
+ format: "csv",
103
+ codecOptions: { csv: { delimiter: ";", skipRows: 1 } },
104
+ } as const;
105
+
106
+ const formatProfile = {
107
+ columns: {
108
+ amount: { kind: "currency", currencySymbol: "€", decimalSeparator: "," },
109
+ },
110
+ } as const;
111
+
112
+ const preview = await dryRunImport({
113
+ source: partnerSource,
114
+ target: { kind: "memory", format: "json" },
115
+ schema: AccountImportSchema,
116
+ template,
117
+ formatProfile,
118
+ });
119
+
120
+ const blockingIssues = preview.issues.filter((issue) => issue.severity === "error");
121
+
122
+ if (blockingIssues.length === 0) {
123
+ await executeImport({
124
+ source: partnerSource,
125
+ target: { kind: "memory", format: "json" },
126
+ schema: AccountImportSchema,
127
+ template,
128
+ formatProfile,
129
+ });
130
+ }`;
131
+
132
+ export const clientReviewCode = `"use client";
133
+
134
+ import { useDataExchangeController } from "@contractspec/lib.data-exchange-client";
135
+
136
+ export function ImportMappingReview({ preview }) {
137
+ const controller = useDataExchangeController({ preview });
138
+ const replacementSourceColumn = controller.model.ignoredSourceColumns[0];
139
+
140
+ return (
141
+ <section>
142
+ {controller.model.templateRows.map((row) => (
143
+ <button
144
+ key={row.id}
145
+ type="button"
146
+ disabled={!replacementSourceColumn}
147
+ onClick={() => {
148
+ if (!replacementSourceColumn) return;
149
+ controller.selectAlias(row.targetField, replacementSourceColumn);
150
+ }}
151
+ >
152
+ {row.label}: {row.sourceField || "Unmatched"} -> {row.targetField}
153
+ {row.required ? " required" : ""}
154
+ {row.formatLabel ? \` (\${row.formatLabel})\` : ""}
155
+ </button>
156
+ ))}
157
+ {controller.model.unmatchedRequiredRows.length > 0 ? (
158
+ <p>Resolve required columns before import.</p>
159
+ ) : null}
160
+ </section>
161
+ );
162
+ }`;
163
+
164
+ export const developerPrompt = `You are adding an import flow to a ContractSpec app.
165
+
166
+ Define a reusable data-exchange template for this canonical schema:
167
+ - target fields, required flags, and display labels
168
+ - known partner column aliases
169
+ - value formats for numbers, dates, booleans, JSON, split/join lists, currency, and percentages
170
+
171
+ Wire the template into core preview planning and server dry-run execution. Keep explicit mappings higher precedence than template resolution. Return the template, preview call, server dry-run call, and tests for alias matching plus localized formatting.`;
172
+
173
+ export const partnerPrompt = `A partner sent a CSV/JSON/XML file that does not match our recommended import template.
174
+
175
+ Compare the incoming headers and value samples against this ContractSpec data-exchange template. Propose:
176
+ - source-to-target column matches with confidence
177
+ - missing required target fields
178
+ - ignored source columns
179
+ - format overrides for localized numbers, booleans, dates, JSON, split/join lists, currency, or percentages
180
+
181
+ Do not execute the import. Produce a dry-run plan and the user-facing review copy.`;
182
+
183
+ export const verificationCode = `cd packages/libs/data-exchange-core && bun test && bun run typecheck && bun run lint:check
184
+ cd packages/libs/data-exchange-client && bun test && bun run typecheck && bun run lint:check
185
+ cd packages/libs/data-exchange-server && bun test && bun run typecheck && bun run lint:check`;
@@ -0,0 +1,186 @@
1
+ import { CodeBlock } from '@contractspec/lib.design-system';
2
+ import { HStack, VStack } from '@contractspec/lib.design-system/layout';
3
+ import { List, ListItem } from '@contractspec/lib.design-system/list';
4
+ import {
5
+ Code,
6
+ H1,
7
+ H2,
8
+ H3,
9
+ P,
10
+ Text,
11
+ } from '@contractspec/lib.design-system/typography';
12
+ import Link from '@contractspec/lib.ui-link';
13
+ import { ChevronRight } from 'lucide-react';
14
+ import {
15
+ accountImportTemplateCode,
16
+ clientReviewCode,
17
+ developerPrompt,
18
+ partnerPrompt,
19
+ serverDryRunCode,
20
+ verificationCode,
21
+ } from './GuideDataExchangeImportTemplatesPage.content';
22
+
23
+ const mappingRules = [
24
+ 'Explicit mappings win first, so existing integrations can keep their current mapping arrays.',
25
+ 'Template resolution checks exact headers, aliases, normalized labels, and SchemaModel fallback inference.',
26
+ 'Format profiles can override formats by target field or template column key without changing the template.',
27
+ 'Required template columns become visible preview issues when no source column can be matched.',
28
+ ];
29
+
30
+ const formatKinds = [
31
+ 'text trim and case normalization',
32
+ 'localized numbers with decimal and thousands separators',
33
+ 'custom true/false boolean labels',
34
+ 'dates and datetimes with accepted input formats',
35
+ 'JSON parsing, empty-as-null, and default values',
36
+ 'split/join delimiters, currency symbols, and percentages',
37
+ ];
38
+
39
+ export function GuideDataExchangeImportTemplatesPage() {
40
+ return (
41
+ <VStack className="space-y-8">
42
+ <VStack className="space-y-3">
43
+ <Text className="editorial-kicker">Build</Text>
44
+ <H1 className="font-bold text-4xl">
45
+ Import flexible files with data-exchange templates
46
+ </H1>
47
+ <P className="max-w-3xl text-lg text-muted-foreground leading-8">
48
+ Publish one recommended import shape, then let users import
49
+ CSV/JSON/XML files with partner-specific headers, skipped rows,
50
+ localized values, and alternate metadata layouts.
51
+ </P>
52
+ </VStack>
53
+
54
+ <VStack className="card-subtle space-y-4 p-6">
55
+ <H2 className="font-bold text-2xl">What you&apos;ll build</H2>
56
+ <List className="space-y-2 text-muted-foreground text-sm">
57
+ <ListItem>
58
+ <Text>
59
+ A canonical template with target fields, aliases, and formats.
60
+ </Text>
61
+ </ListItem>
62
+ <ListItem>
63
+ <Text>
64
+ A dry-run import that reports confidence, gaps, and ignored
65
+ columns.
66
+ </Text>
67
+ </ListItem>
68
+ <ListItem>
69
+ <Text>
70
+ A client review state where users remap, update formats, or accept
71
+ inferred mappings.
72
+ </Text>
73
+ </ListItem>
74
+ </List>
75
+ </VStack>
76
+
77
+ <VStack className="space-y-4">
78
+ <H2 className="font-bold text-2xl">1) Define the template</H2>
79
+ <P className="text-muted-foreground text-sm leading-7">
80
+ Use <Code>defineDataExchangeTemplate</Code> for neutral import/export
81
+ naming. <Code>defineImportTemplate</Code> and{' '}
82
+ <Code>defineExportTemplate</Code> remain available aliases.
83
+ </P>
84
+ <CodeBlock
85
+ language="typescript"
86
+ filename="src/data-exchange/accounts-import.ts"
87
+ code={accountImportTemplateCode}
88
+ />
89
+ </VStack>
90
+
91
+ <VStack className="grid gap-4 md:grid-cols-2">
92
+ <VStack className="card-subtle space-y-3 p-6">
93
+ <H3 className="font-bold text-xl">Mapping precedence</H3>
94
+ <List className="space-y-2 text-muted-foreground text-sm">
95
+ {mappingRules.map((rule) => (
96
+ <ListItem key={rule}>
97
+ <Text>{rule}</Text>
98
+ </ListItem>
99
+ ))}
100
+ </List>
101
+ </VStack>
102
+ <VStack className="card-subtle space-y-3 p-6">
103
+ <H3 className="font-bold text-xl">Supported value formats</H3>
104
+ <List className="space-y-2 text-muted-foreground text-sm">
105
+ {formatKinds.map((format) => (
106
+ <ListItem key={format}>
107
+ <Text>{format}</Text>
108
+ </ListItem>
109
+ ))}
110
+ </List>
111
+ </VStack>
112
+ </VStack>
113
+
114
+ <VStack className="space-y-4">
115
+ <H2 className="font-bold text-2xl">
116
+ 2) Dry-run partner CSV, JSON, or XML files on the server
117
+ </H2>
118
+ <P className="text-muted-foreground text-sm leading-7">
119
+ File, HTTP, and storage adapters accept codec options. CSV can set
120
+ delimiters, quotes, skipped rows, header rows, or explicit columns.
121
+ JSON can read records and metadata from custom keys. XML can use
122
+ custom root, record, metadata, and attribute fields.
123
+ </P>
124
+ <CodeBlock
125
+ language="typescript"
126
+ filename="src/server/import-accounts.ts"
127
+ code={serverDryRunCode}
128
+ />
129
+ </VStack>
130
+
131
+ <VStack className="space-y-4">
132
+ <H2 className="font-bold text-2xl">3) Let users review the mapping</H2>
133
+ <P className="text-muted-foreground text-sm leading-7">
134
+ The shared controller exposes template rows, matched source columns,
135
+ confidence, required status, formatting summaries, unmatched required
136
+ rows, and ignored source columns. Actions let users remap columns,
137
+ select aliases, update field formats, reset to the template, or accept
138
+ inferred mappings.
139
+ </P>
140
+ <CodeBlock
141
+ language="tsx"
142
+ filename="src/components/ImportMappingReview.tsx"
143
+ code={clientReviewCode}
144
+ />
145
+ </VStack>
146
+
147
+ <VStack className="space-y-4">
148
+ <H2 className="font-bold text-2xl">4) Verify the package stack</H2>
149
+ <CodeBlock
150
+ language="bash"
151
+ filename="verification"
152
+ code={verificationCode}
153
+ />
154
+ </VStack>
155
+
156
+ <VStack className="grid gap-4 md:grid-cols-2">
157
+ <VStack className="card-subtle space-y-3 p-6">
158
+ <H3 className="font-bold text-xl">Prompt: build a template</H3>
159
+ <CodeBlock
160
+ language="markdown"
161
+ filename="template-authoring.prompt.md"
162
+ code={developerPrompt}
163
+ />
164
+ </VStack>
165
+ <VStack className="card-subtle space-y-3 p-6">
166
+ <H3 className="font-bold text-xl">Prompt: inspect a partner file</H3>
167
+ <CodeBlock
168
+ language="markdown"
169
+ filename="partner-import-review.prompt.md"
170
+ code={partnerPrompt}
171
+ />
172
+ </VStack>
173
+ </VStack>
174
+
175
+ <HStack className="flex flex-wrap items-center gap-4 pt-4">
176
+ <Link href="/llms/lib.data-exchange-core" className="btn-primary">
177
+ <Text>Core package guide</Text>
178
+ <ChevronRight size={16} />
179
+ </Link>
180
+ <Link href="/llms/lib.data-exchange-server" className="btn-ghost">
181
+ <Text>Server package guide</Text>
182
+ </Link>
183
+ </HStack>
184
+ </VStack>
185
+ );
186
+ }