create-nextblock 0.10.9 → 0.11.2
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/templates/nextblock-template/app/actions/interactions.test.ts +301 -0
- package/templates/nextblock-template/app/actions/interactions.ts +372 -0
- package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +4 -4
- package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +2 -2
- package/templates/nextblock-template/app/api/ai/global-agent/route.ts +56 -57
- package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +1 -1
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +837 -0
- package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +6 -0
- package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +4 -0
- package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +122 -0
- package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +102 -0
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardOnboarding.tsx +18 -13
- package/templates/nextblock-template/app/cms/interactions/InteractionsModerationClient.tsx +408 -0
- package/templates/nextblock-template/app/cms/interactions/page.tsx +51 -0
- package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +4 -3
- package/templates/nextblock-template/app/cms/settings/cortex-ai/StoredCortexAiSettingsClient.tsx +1 -1
- package/templates/nextblock-template/app/cms/settings/cortex-ai/actions.ts +3 -5
- package/templates/nextblock-template/app/cms/settings/cortex-ai/page.tsx +1 -1
- package/templates/nextblock-template/app/page.tsx +2 -2
- package/templates/nextblock-template/app/product/[slug]/page.tsx +2 -0
- package/templates/nextblock-template/components/AppShell.tsx +1 -1
- package/templates/nextblock-template/components/PostCommentsSection.tsx +369 -0
- package/templates/nextblock-template/components/ProductReviewsSection.tsx +419 -0
- package/templates/nextblock-template/components/blocks/renderers/ProductDetailsBlockRenderer.tsx +2 -0
- package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +62 -19
- package/templates/nextblock-template/docs/08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md +19 -19
- package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +4 -4
- package/templates/nextblock-template/docs/12-VERCEL-DEPLOYMENT.md +9 -8
- package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +38 -9
- package/templates/nextblock-template/lib/blocks/ProductGridBlock.tsx +2 -0
- package/templates/nextblock-template/lib/onboarding/status.ts +13 -6
- package/templates/nextblock-template/lib/setup/actions.ts +3 -1
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +30 -0
- package/templates/nextblock-template/lib/updates/check-upstream.ts +44 -7
- package/templates/nextblock-template/lib/updates/github-device.ts +206 -0
- package/templates/nextblock-template/lib/updates/repo-identity.ts +11 -1
- package/templates/nextblock-template/package.json +2 -1
- package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +2 -4
- package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +1 -1
- package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +1 -1
- package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +1 -1
- package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
- package/templates/nextblock-template/lib/ai-block-generation.ts +0 -339
- package/templates/nextblock-template/lib/ai-client.ts +0 -247
- package/templates/nextblock-template/lib/ai-config.ts +0 -98
- package/templates/nextblock-template/lib/ai-cortex-widget-builder.ts +0 -125
- package/templates/nextblock-template/lib/ai-global-agent-custom-block-tools.ts +0 -363
- package/templates/nextblock-template/lib/ai-global-agent-db-tools.test.ts +0 -405
- package/templates/nextblock-template/lib/ai-global-agent-db-tools.ts +0 -1228
- package/templates/nextblock-template/lib/ai-global-agent-ecommerce.ts +0 -5
- package/templates/nextblock-template/lib/ai-global-agent-tools-stats.test.ts +0 -223
- package/templates/nextblock-template/lib/ai-global-agent-tools.test.ts +0 -2183
- package/templates/nextblock-template/lib/ai-global-agent-tools.ts +0 -4807
- package/templates/nextblock-template/lib/ai-key-crypto.test.ts +0 -70
- package/templates/nextblock-template/lib/ai-key-crypto.ts +0 -132
- package/templates/nextblock-template/lib/ai-model-catalog.test.ts +0 -49
- package/templates/nextblock-template/lib/ai-model-catalog.ts +0 -41
- package/templates/nextblock-template/lib/ai-model-registry.test.ts +0 -231
- package/templates/nextblock-template/lib/ai-model-registry.ts +0 -522
- package/templates/nextblock-template/lib/cortex-widget-registry.test.ts +0 -199
- package/templates/nextblock-template/lib/cortex-widget-registry.ts +0 -88
- package/templates/nextblock-template/lib/cortex-widget-schema.test.tsx +0 -237
- package/templates/nextblock-template/lib/cortex-widget-schema.ts +0 -393
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
customBlockDefinitionCreateSchema,
|
|
3
|
-
customBlockFieldKeySchema,
|
|
4
|
-
customBlockSlugSchema,
|
|
5
|
-
} from '@nextblock-cms/utils';
|
|
6
|
-
|
|
7
|
-
import { z } from './zod-config';
|
|
8
|
-
|
|
9
|
-
export const CORTEX_WIDGET_ALLOWED_RELATION_TABLES = [
|
|
10
|
-
'pages',
|
|
11
|
-
'posts',
|
|
12
|
-
'products',
|
|
13
|
-
'media',
|
|
14
|
-
'categories',
|
|
15
|
-
'profiles',
|
|
16
|
-
'languages',
|
|
17
|
-
] as const;
|
|
18
|
-
|
|
19
|
-
const htmlElementSchema = z
|
|
20
|
-
.enum([
|
|
21
|
-
'article',
|
|
22
|
-
'aside',
|
|
23
|
-
'blockquote',
|
|
24
|
-
'div',
|
|
25
|
-
'figure',
|
|
26
|
-
'figcaption',
|
|
27
|
-
'h2',
|
|
28
|
-
'h3',
|
|
29
|
-
'img',
|
|
30
|
-
'p',
|
|
31
|
-
'section',
|
|
32
|
-
'span',
|
|
33
|
-
])
|
|
34
|
-
.describe('A safe semantic element supported by the dynamic layout renderer.');
|
|
35
|
-
|
|
36
|
-
const tailwindClassSchema = z
|
|
37
|
-
.string()
|
|
38
|
-
.trim()
|
|
39
|
-
.max(4000)
|
|
40
|
-
.describe('Tailwind utility classes only. Do not include CSS, style tags, or JavaScript.');
|
|
41
|
-
|
|
42
|
-
const cortexWidgetFieldBaseSchema = z.strictObject({
|
|
43
|
-
description: z.string().trim().max(500).optional(),
|
|
44
|
-
key: customBlockFieldKeySchema.describe('Lowercase snake_case field key.'),
|
|
45
|
-
label: z.string().trim().min(1).max(120),
|
|
46
|
-
required: z.boolean().default(false),
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
export const cortexWidgetTextFieldSchema = cortexWidgetFieldBaseSchema.extend({
|
|
50
|
-
default_value: z.string().max(5000).optional(),
|
|
51
|
-
max_length: z.number().int().positive().max(10000).optional(),
|
|
52
|
-
min_length: z.number().int().min(0).max(10000).optional(),
|
|
53
|
-
placeholder: z.string().max(250).optional(),
|
|
54
|
-
type: z.literal('text'),
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
export const cortexWidgetRichTextFieldSchema = cortexWidgetFieldBaseSchema.extend({
|
|
58
|
-
default_value: z.string().max(50000).optional(),
|
|
59
|
-
placeholder: z.string().max(250).optional(),
|
|
60
|
-
type: z.literal('rich-text'),
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
export const cortexWidgetImageR2FieldSchema = cortexWidgetFieldBaseSchema.extend({
|
|
64
|
-
accept: z.array(z.string().trim().min(1).max(120)).max(20).optional(),
|
|
65
|
-
default_value: z
|
|
66
|
-
.strictObject({
|
|
67
|
-
alt: z.string().max(300).optional(),
|
|
68
|
-
file_name: z.string().trim().min(1).max(255).optional(),
|
|
69
|
-
file_type: z.string().trim().min(1).max(120).optional(),
|
|
70
|
-
height: z.number().int().positive().optional(),
|
|
71
|
-
object_key: z.string().trim().min(1).max(1024),
|
|
72
|
-
size_bytes: z.number().int().positive().optional(),
|
|
73
|
-
url: z.string().trim().min(1).max(2048),
|
|
74
|
-
width: z.number().int().positive().optional(),
|
|
75
|
-
})
|
|
76
|
-
.optional(),
|
|
77
|
-
max_bytes: z.number().int().positive().max(50 * 1024 * 1024).optional(),
|
|
78
|
-
type: z.literal('image_r2'),
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
export const cortexWidgetDbRelationFieldSchema = cortexWidgetFieldBaseSchema.extend({
|
|
82
|
-
default_value: z.union([z.string(), z.array(z.string()), z.null()]).optional(),
|
|
83
|
-
display_column: z.string().trim().min(1).max(80).default('title'),
|
|
84
|
-
filters: z.record(z.string(), z.unknown()).optional(),
|
|
85
|
-
multiple: z.boolean().default(false),
|
|
86
|
-
table: z.enum(CORTEX_WIDGET_ALLOWED_RELATION_TABLES),
|
|
87
|
-
type: z.literal('db_relation'),
|
|
88
|
-
value_column: z.string().trim().min(1).max(80).default('id'),
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
export const cortexWidgetFieldSchema = z
|
|
92
|
-
.discriminatedUnion('type', [
|
|
93
|
-
cortexWidgetTextFieldSchema,
|
|
94
|
-
cortexWidgetRichTextFieldSchema,
|
|
95
|
-
cortexWidgetImageR2FieldSchema,
|
|
96
|
-
cortexWidgetDbRelationFieldSchema,
|
|
97
|
-
])
|
|
98
|
-
.describe('A NextBlock custom block field. Allowed types: text, rich-text, image_r2, db_relation.');
|
|
99
|
-
|
|
100
|
-
export type CortexWidgetLayoutNode =
|
|
101
|
-
| {
|
|
102
|
-
as?: z.infer<typeof htmlElementSchema>;
|
|
103
|
-
children: CortexWidgetLayoutNode[];
|
|
104
|
-
className?: string;
|
|
105
|
-
type: 'container';
|
|
106
|
-
}
|
|
107
|
-
| {
|
|
108
|
-
as?: z.infer<typeof htmlElementSchema>;
|
|
109
|
-
className?: string;
|
|
110
|
-
column?: string;
|
|
111
|
-
emptyFallback?: string;
|
|
112
|
-
field_key: string;
|
|
113
|
-
type: 'field_render';
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
export const cortexWidgetLayoutNodeSchema: z.ZodType<CortexWidgetLayoutNode> = z.lazy(() =>
|
|
117
|
-
z.discriminatedUnion('type', [
|
|
118
|
-
z.strictObject({
|
|
119
|
-
as: htmlElementSchema.optional(),
|
|
120
|
-
children: z.array(cortexWidgetLayoutNodeSchema).max(200).default([]),
|
|
121
|
-
className: tailwindClassSchema.optional(),
|
|
122
|
-
type: z.literal('container'),
|
|
123
|
-
}),
|
|
124
|
-
z.strictObject({
|
|
125
|
-
as: htmlElementSchema.optional(),
|
|
126
|
-
className: tailwindClassSchema.optional(),
|
|
127
|
-
column: z
|
|
128
|
-
.string()
|
|
129
|
-
.trim()
|
|
130
|
-
.min(1)
|
|
131
|
-
.max(80)
|
|
132
|
-
.optional()
|
|
133
|
-
.describe('For a db_relation field, the related record column to display (e.g. title, price).'),
|
|
134
|
-
emptyFallback: z.string().max(300).optional(),
|
|
135
|
-
field_key: customBlockFieldKeySchema,
|
|
136
|
-
type: z.literal('field_render'),
|
|
137
|
-
}),
|
|
138
|
-
])
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
export const cortexWidgetBuildRequestSchema = z.strictObject({
|
|
142
|
-
context: z.string().trim().max(3000).optional(),
|
|
143
|
-
modelId: z.string().trim().min(1).max(200).optional(),
|
|
144
|
-
prompt: z.string().trim().min(3).max(4000),
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
export type CortexWidgetBuildRequest = z.infer<typeof cortexWidgetBuildRequestSchema>;
|
|
148
|
-
|
|
149
|
-
function collectLayoutFieldKeys(node: CortexWidgetLayoutNode): string[] {
|
|
150
|
-
if (node.type === 'field_render') {
|
|
151
|
-
return [node.field_key];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return node.children.flatMap((child) => collectLayoutFieldKeys(child));
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function assertCortexWidgetFieldKeys(
|
|
158
|
-
definition: {
|
|
159
|
-
fields: Array<{ key: string }>;
|
|
160
|
-
layout_schema: CortexWidgetLayoutNode;
|
|
161
|
-
},
|
|
162
|
-
context: z.RefinementCtx
|
|
163
|
-
) {
|
|
164
|
-
const seenFieldKeys = new Set<string>();
|
|
165
|
-
|
|
166
|
-
definition.fields.forEach((field, index) => {
|
|
167
|
-
if (seenFieldKeys.has(field.key)) {
|
|
168
|
-
context.addIssue({
|
|
169
|
-
code: 'custom',
|
|
170
|
-
message: `Duplicate field key "${field.key}".`,
|
|
171
|
-
path: ['fields', index, 'key'],
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
seenFieldKeys.add(field.key);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
for (const fieldKey of collectLayoutFieldKeys(definition.layout_schema)) {
|
|
179
|
-
if (!seenFieldKeys.has(fieldKey)) {
|
|
180
|
-
context.addIssue({
|
|
181
|
-
code: 'custom',
|
|
182
|
-
message: `Layout references unknown field "${fieldKey}".`,
|
|
183
|
-
path: ['layout_schema'],
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export const cortexWidgetDefinitionSchema = z
|
|
190
|
-
.strictObject({
|
|
191
|
-
description: z.string().trim().max(1000).default(''),
|
|
192
|
-
fields: z.array(cortexWidgetFieldSchema).min(1).max(80),
|
|
193
|
-
is_original: z.boolean().default(true),
|
|
194
|
-
layout_schema: cortexWidgetLayoutNodeSchema,
|
|
195
|
-
name: z.string().trim().min(1).max(160),
|
|
196
|
-
slug: customBlockSlugSchema.describe('Lowercase kebab-case slug.'),
|
|
197
|
-
})
|
|
198
|
-
.superRefine(assertCortexWidgetFieldKeys)
|
|
199
|
-
.describe('A complete NextBlock custom block definition stored as database JSONB.');
|
|
200
|
-
|
|
201
|
-
export type CortexWidgetDefinition = z.infer<typeof customBlockDefinitionCreateSchema>;
|
|
202
|
-
|
|
203
|
-
export function validateCortexWidgetDefinitionOutput(value: unknown): CortexWidgetDefinition {
|
|
204
|
-
const parsed = cortexWidgetDefinitionSchema.parse(value);
|
|
205
|
-
|
|
206
|
-
return customBlockDefinitionCreateSchema.parse({
|
|
207
|
-
...parsed,
|
|
208
|
-
is_original: true,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export function buildCortexWidgetBuilderSystemPrompt() {
|
|
213
|
-
return [
|
|
214
|
-
'You are NextBlock Cortex, an expert web platform engineer building database-rendered custom CMS widgets.',
|
|
215
|
-
'Return ONLY one clean raw JSON object with the exact structure described in the user message. Do not include markdown fences, comments, prose, or explanatory text.',
|
|
216
|
-
'Never emit TSX, JSX, React components, JavaScript, CSS blocks, style attributes, script tags, or runtime code.',
|
|
217
|
-
'Use only these field types: text, rich-text, image_r2, db_relation.',
|
|
218
|
-
`Use db_relation.table only from this allowlist: ${CORTEX_WIDGET_ALLOWED_RELATION_TABLES.join(', ')}.`,
|
|
219
|
-
'Use lowercase kebab-case for slug and lowercase snake_case for field keys.',
|
|
220
|
-
'Build layout_schema as a self-referential tree: container nodes may contain nested container or field_render nodes to any needed depth.',
|
|
221
|
-
'Use Tailwind utility classes in className strings. Use responsive utilities where helpful.',
|
|
222
|
-
'The "as" property of any node MUST be exactly one of: article, aside, blockquote, div, figure, figcaption, h2, h3, img, p, section, span. Never use a, button, ul, ol, li, table, or any other tag. For a call-to-action or "more info" button, use a span or p styled with button-like Tailwind classes (rounded, padded, colored background).',
|
|
223
|
-
'Every field_render.field_key must match one field key exactly.',
|
|
224
|
-
'For relation fields, set value_column to id and set display_column to a column that actually exists on the chosen table: use title for pages, posts, and products; sku for product_variants; full_name for profiles; name for categories and languages; file_name for media. Do not invent display columns.',
|
|
225
|
-
'Entity images: when a block displays an image that belongs to a related product, page, or post (for example a product card photo or a post thumbnail), do NOT add an image_r2 upload field for it. Instead add a single db_relation field to that table (products, product_variants, pages, or posts) and add a field_render node that references it with "as": "img". The renderer automatically resolves the related record\'s primary image — a product or variant main_image/object_key, or a page/post feature image — so keep the table\'s normal display_column (for example title for a products relation).',
|
|
226
|
-
'You may reference the same db_relation field from more than one field_render node: for example one node with "as": "img" for the image and another text node for its title, plus the relation value to drive a "more info" link. This builds a product/page/post card from a single relation field.',
|
|
227
|
-
'To display a SPECIFIC column of a related record (its title, price, sku, etc.), set the field_render node\'s "column" property to that column name. The "column" overrides the field display_column for that one node, so a single product relation can show its image (as "img"), title (column "title"), and price (column "price") from three field_render nodes.',
|
|
228
|
-
'Available record columns by table — only reference these in a node "column": products: title, sku, price, sale_price, stock, short_description, slug, status; product_variants: sku, price, sale_price, stock_quantity; pages: title, slug, status; posts: title, slug, excerpt, subtitle; profiles: full_name; categories: name, slug, description; media: file_name; languages: name, code. Use "as": "img" (no column) to show a record image.',
|
|
229
|
-
'Do NOT create a standalone text field for data that lives on a related record. For example, a product price must come from a products db_relation field rendered with column "price" — never a separate "text" field the editor types by hand.',
|
|
230
|
-
'Monetary columns (price, sale_price, price_adjustment) are stored in integer cents and are automatically formatted as currency on display, so reference them directly; never multiply, divide, or add currency symbols yourself.',
|
|
231
|
-
'Only use image_r2 for standalone images uploaded directly by the editor that are not tied to any database record (for example a decorative banner or icon). Only use text or rich-text fields for free-form copy the editor writes, not for values that exist on a related record.',
|
|
232
|
-
].join(' ');
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
export function buildCortexWidgetBuilderPrompt(params: CortexWidgetBuildRequest) {
|
|
236
|
-
return [
|
|
237
|
-
'Create a NextBlock custom block definition for this request:',
|
|
238
|
-
params.prompt,
|
|
239
|
-
params.context ? `Additional CMS context:\n${params.context}` : null,
|
|
240
|
-
[
|
|
241
|
-
'Return ONLY a JSON object with EXACTLY these top-level keys:',
|
|
242
|
-
'- "name": string (human-friendly block name).',
|
|
243
|
-
'- "slug": lowercase kebab-case string.',
|
|
244
|
-
'- "description": short string.',
|
|
245
|
-
'- "is_original": true.',
|
|
246
|
-
'- "fields": a non-empty array of field objects. Each field is { "key": lowercase snake_case string, "label": string, "required": boolean, "type": one of "text" | "rich-text" | "image_r2" | "db_relation" }.',
|
|
247
|
-
` For "db_relation" fields also include "table" (one of: ${CORTEX_WIDGET_ALLOWED_RELATION_TABLES.join(', ')}), "display_column" (e.g. title, name, full_name, file_name, code), "value_column": "id", and "multiple": boolean.`,
|
|
248
|
-
'- "layout_schema": a single root layout node (a tree). Every node is one of:',
|
|
249
|
-
' container: { "type": "container", "as": an HTML tag like div/section/article/figure, "className": Tailwind utility classes, "children": array of nodes }.',
|
|
250
|
-
' field render: { "type": "field_render", "field_key": one of the field keys above, "as": an HTML tag like p/span/img/h2/h3/div, "className": Tailwind utility classes, "emptyFallback": optional placeholder string }.',
|
|
251
|
-
' Containers may nest other containers to any depth. Every field_render.field_key MUST match one of the fields. Render image_r2 fields with "as": "img".',
|
|
252
|
-
].join('\n'),
|
|
253
|
-
]
|
|
254
|
-
.filter(Boolean)
|
|
255
|
-
.join('\n\n');
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export function buildCortexProfileCardVerificationDefinition(): CortexWidgetDefinition {
|
|
259
|
-
return validateCortexWidgetDefinitionOutput({
|
|
260
|
-
description:
|
|
261
|
-
'A multi-tier profile card with an R2 image asset slot and a live customer relation list.',
|
|
262
|
-
fields: [
|
|
263
|
-
{
|
|
264
|
-
accept: ['image/png', 'image/jpeg', 'image/webp'],
|
|
265
|
-
key: 'profile_photo',
|
|
266
|
-
label: 'Profile Photo',
|
|
267
|
-
max_bytes: 10485760,
|
|
268
|
-
required: false,
|
|
269
|
-
type: 'image_r2',
|
|
270
|
-
},
|
|
271
|
-
{
|
|
272
|
-
key: 'profile_name',
|
|
273
|
-
label: 'Profile Name',
|
|
274
|
-
placeholder: 'Ada Lovelace',
|
|
275
|
-
required: true,
|
|
276
|
-
type: 'text',
|
|
277
|
-
},
|
|
278
|
-
{
|
|
279
|
-
key: 'profile_role',
|
|
280
|
-
label: 'Profile Role',
|
|
281
|
-
placeholder: 'Principal Architect',
|
|
282
|
-
required: false,
|
|
283
|
-
type: 'text',
|
|
284
|
-
},
|
|
285
|
-
{
|
|
286
|
-
key: 'profile_summary',
|
|
287
|
-
label: 'Profile Summary',
|
|
288
|
-
placeholder: '<p>Short profile biography.</p>',
|
|
289
|
-
required: false,
|
|
290
|
-
type: 'rich-text',
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
display_column: 'full_name',
|
|
294
|
-
key: 'customer_list',
|
|
295
|
-
label: 'Customer List',
|
|
296
|
-
multiple: true,
|
|
297
|
-
required: false,
|
|
298
|
-
table: 'profiles',
|
|
299
|
-
type: 'db_relation',
|
|
300
|
-
value_column: 'id',
|
|
301
|
-
},
|
|
302
|
-
],
|
|
303
|
-
is_original: true,
|
|
304
|
-
layout_schema: {
|
|
305
|
-
as: 'article',
|
|
306
|
-
children: [
|
|
307
|
-
{
|
|
308
|
-
as: 'div',
|
|
309
|
-
children: [
|
|
310
|
-
{
|
|
311
|
-
as: 'div',
|
|
312
|
-
children: [
|
|
313
|
-
{
|
|
314
|
-
as: 'div',
|
|
315
|
-
children: [
|
|
316
|
-
{
|
|
317
|
-
as: 'img',
|
|
318
|
-
className:
|
|
319
|
-
'h-24 w-24 rounded-full border object-cover shadow-sm',
|
|
320
|
-
emptyFallback: 'Upload profile photo',
|
|
321
|
-
field_key: 'profile_photo',
|
|
322
|
-
type: 'field_render',
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
as: 'span',
|
|
326
|
-
className:
|
|
327
|
-
'rounded-full bg-muted px-3 py-1 text-center text-xs font-medium text-muted-foreground',
|
|
328
|
-
emptyFallback: 'No customers linked',
|
|
329
|
-
field_key: 'customer_list',
|
|
330
|
-
type: 'field_render',
|
|
331
|
-
},
|
|
332
|
-
],
|
|
333
|
-
className: 'flex flex-col items-center gap-4 md:w-48',
|
|
334
|
-
type: 'container',
|
|
335
|
-
},
|
|
336
|
-
{
|
|
337
|
-
as: 'div',
|
|
338
|
-
children: [
|
|
339
|
-
{
|
|
340
|
-
as: 'div',
|
|
341
|
-
children: [
|
|
342
|
-
{
|
|
343
|
-
as: 'h2',
|
|
344
|
-
className: 'text-2xl font-semibold leading-tight',
|
|
345
|
-
emptyFallback: 'Untitled profile',
|
|
346
|
-
field_key: 'profile_name',
|
|
347
|
-
type: 'field_render',
|
|
348
|
-
},
|
|
349
|
-
{
|
|
350
|
-
as: 'p',
|
|
351
|
-
className: 'text-sm font-medium text-muted-foreground',
|
|
352
|
-
emptyFallback: 'Role pending',
|
|
353
|
-
field_key: 'profile_role',
|
|
354
|
-
type: 'field_render',
|
|
355
|
-
},
|
|
356
|
-
],
|
|
357
|
-
className: 'flex flex-col gap-1',
|
|
358
|
-
type: 'container',
|
|
359
|
-
},
|
|
360
|
-
{
|
|
361
|
-
as: 'div',
|
|
362
|
-
children: [
|
|
363
|
-
{
|
|
364
|
-
as: 'div',
|
|
365
|
-
className: 'prose prose-sm max-w-none text-muted-foreground',
|
|
366
|
-
emptyFallback: '<p>Add a concise profile summary.</p>',
|
|
367
|
-
field_key: 'profile_summary',
|
|
368
|
-
type: 'field_render',
|
|
369
|
-
},
|
|
370
|
-
],
|
|
371
|
-
className: 'rounded-md border bg-muted/30 p-4',
|
|
372
|
-
type: 'container',
|
|
373
|
-
},
|
|
374
|
-
],
|
|
375
|
-
className: 'flex min-w-0 flex-1 flex-col gap-4',
|
|
376
|
-
type: 'container',
|
|
377
|
-
},
|
|
378
|
-
],
|
|
379
|
-
className: 'flex flex-col gap-6 md:flex-row',
|
|
380
|
-
type: 'container',
|
|
381
|
-
},
|
|
382
|
-
],
|
|
383
|
-
className: 'rounded-lg border bg-background p-6 shadow-sm',
|
|
384
|
-
type: 'container',
|
|
385
|
-
},
|
|
386
|
-
],
|
|
387
|
-
className: 'mx-auto max-w-3xl p-4',
|
|
388
|
-
type: 'container',
|
|
389
|
-
},
|
|
390
|
-
name: 'Cortex Profile Card',
|
|
391
|
-
slug: 'cortex-profile-card',
|
|
392
|
-
});
|
|
393
|
-
}
|