/**
* Templates for NocoBase MCP
*
* This file contains page templates for modern NocoBase.
*
* Design principles:
* 1. MINIMAL schema - only include what's absolutely necessary
* 2. NO fields/columns in initial creation - let user configure via UI
* 3. SIMPLE structure - avoid complex nesting that causes bugs
* 4. RETURN UIDs - so user can configure later
*/
import { uid } from '@formily/shared';
const APP_VERSION = '1.4.0-beta.1';
// =============================================================================
// HELPER: Enrich schema with mandatory NocoBase properties
// =============================================================================
export function enrichSchema(node: any): any {
if (!node || typeof node !== 'object') return node;
const result: any = { ...node };
// Add mandatory properties
if (!result._isJSONSchemaObject) result._isJSONSchemaObject = true;
if (!result.version) result.version = '2.0';
if (!result['x-app-version']) result['x-app-version'] = APP_VERSION;
// Ensure x-uid and name are set
if (!result['x-uid']) result['x-uid'] = uid();
if (!result.name) result.name = result['x-uid'];
// Recursively enrich properties
if (result.properties) {
const newProps: any = {};
for (const [key, value] of Object.entries(result.properties)) {
newProps[key] = enrichSchema(value);
}
result.properties = newProps;
}
return result;
}
// =============================================================================
// PAGE TEMPLATE - Minimal Empty Page
// =============================================================================
export function createEmptyPage() {
const pageUid = uid();
const gridUid = uid();
const schema = {
type: 'void',
'x-component': 'Page',
'x-uid': pageUid,
'x-async': true,
properties: {
[gridUid]: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'page:addBlock',
'x-uid': gridUid
}
}
};
return {
schema: enrichSchema(schema),
pageUid,
gridUid
};
}
// =============================================================================
// TABLE BLOCK - Minimal Table (NO columns, NO actions)
// User will configure via UI
// =============================================================================
export function createTableBlock(collectionName: string) {
const blockUid = uid();
const tableUid = uid();
const schema = {
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': `${collectionName}:list`,
'x-use-decorator-props': 'useTableBlockDecoratorProps',
'x-decorator-props': {
collection: collectionName,
dataSource: 'main',
action: 'list',
params: { pageSize: 20 },
rowKey: 'id',
showIndex: true,
dragSort: false,
},
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'blockSettings:table',
'x-component': 'CardItem',
'x-filter-targets': [],
'x-uid': blockUid,
properties: {
actions: {
type: 'void',
'x-initializer': 'table:configureActions',
'x-component': 'ActionBar',
'x-component-props': {
style: { marginBottom: 'var(--nb-spacing)' },
},
'x-uid': uid(),
properties: {} // Empty - user configures via UI
},
[tableUid]: {
type: 'array',
'x-initializer': 'table:configureColumns',
'x-component': 'TableV2',
'x-use-component-props': 'useTableBlockProps',
'x-component-props': {
rowKey: 'id',
rowSelection: { type: 'checkbox' },
},
'x-uid': tableUid,
properties: {} // Empty - user configures columns via UI
}
}
};
return {
schema: enrichSchema(schema),
blockUid,
tableUid
};
}
// =============================================================================
// WRAP BLOCK IN GRID ROW/COL
// =============================================================================
export function wrapInGridRowCol(blockSchema: any) {
const rowUid = uid();
const colUid = uid();
const wrapped = {
type: 'void',
'x-component': 'Grid.Row',
'x-uid': rowUid,
properties: {
[colUid]: {
type: 'void',
'x-component': 'Grid.Col',
'x-uid': colUid,
properties: {
[blockSchema['x-uid']]: blockSchema
}
}
}
};
return {
schema: enrichSchema(wrapped),
rowUid,
colUid
};
}
// =============================================================================
// CREATE FULL PAGE WITH TABLE
// Combines: Page + Grid + Row + Col + TableBlock
// =============================================================================
export function createPageWithTable(collectionName: string) {
const pageUid = uid();
const gridUid = uid();
const rowUid = uid();
const colUid = uid();
const tableBlock = createTableBlock(collectionName);
const schema = {
type: 'void',
'x-component': 'Page',
'x-uid': pageUid,
'x-async': true,
properties: {
[gridUid]: {
type: 'void',
'x-component': 'Grid',
'x-initializer': 'page:addBlock',
'x-uid': gridUid,
properties: {
[rowUid]: {
type: 'void',
'x-component': 'Grid.Row',
'x-uid': rowUid,
properties: {
[colUid]: {
type: 'void',
'x-component': 'Grid.Col',
'x-uid': colUid,
properties: {
[tableBlock.blockUid]: tableBlock.schema
}
}
}
}
}
}
}
};
return {
schema: enrichSchema(schema),
pageUid,
gridUid,
rowUid,
colUid,
blockUid: tableBlock.blockUid,
tableUid: tableBlock.tableUid
};
}
// =============================================================================
// ROUTE PAYLOAD HELPER
// =============================================================================
export function createRoutePayload(title: string, pageUid: string, options: {
icon?: string;
parentId?: string;
} = {}) {
const menuSchemaUid = uid();
const tabUid = uid();
const tabName = uid();
const payload: any = {
type: 'page',
title,
icon: options.icon || 'TableOutlined',
schemaUid: pageUid,
menuSchemaUid,
enableTabs: false,
children: [{
type: 'tabs',
schemaUid: tabUid,
tabSchemaName: tabName,
hidden: true
}]
};
if (options.parentId) {
payload.parentId = options.parentId;
}
return payload;
}