diff --git a/frontends/nextjs/src/lib/schema/default-schema.ts b/frontends/nextjs/src/lib/schema/default-schema.ts index 398071c44..a01c0a590 100644 --- a/frontends/nextjs/src/lib/schema/default-schema.ts +++ b/frontends/nextjs/src/lib/schema/default-schema.ts @@ -1,308 +1,6 @@ import type { SchemaConfig } from '../types/schema-types' +import { defaultApps } from './default/components' export const defaultSchema: SchemaConfig = { - apps: [ - { - name: 'blog', - label: 'Blog', - models: [ - { - name: 'post', - label: 'Post', - labelPlural: 'Posts', - icon: 'Article', - listDisplay: ['title', 'author', 'status', 'publishedAt'], - listFilter: ['status', 'author'], - searchFields: ['title', 'content'], - ordering: ['-publishedAt'], - fields: [ - { - name: 'id', - type: 'string', - label: 'ID', - required: true, - unique: true, - editable: false, - listDisplay: false, - }, - { - name: 'title', - type: 'string', - label: 'Title', - required: true, - validation: { - minLength: 3, - maxLength: 200, - }, - listDisplay: true, - searchable: true, - sortable: true, - }, - { - name: 'slug', - type: 'string', - label: 'Slug', - required: true, - unique: true, - helpText: 'URL-friendly version of the title', - validation: { - pattern: '^[a-z0-9-]+$', - }, - listDisplay: false, - sortable: true, - }, - { - name: 'content', - type: 'text', - label: 'Content', - required: true, - helpText: 'Main post content', - listDisplay: false, - searchable: true, - }, - { - name: 'excerpt', - type: 'text', - label: 'Excerpt', - required: false, - helpText: ['Short summary of the post', 'Used in list views and previews'], - validation: { - maxLength: 500, - }, - listDisplay: false, - }, - { - name: 'author', - type: 'relation', - label: 'Author', - required: true, - relatedModel: 'author', - listDisplay: true, - sortable: true, - }, - { - name: 'status', - type: 'select', - label: 'Status', - required: true, - default: 'draft', - choices: [ - { value: 'draft', label: 'Draft' }, - { value: 'published', label: 'Published' }, - { value: 'archived', label: 'Archived' }, - ], - listDisplay: true, - sortable: true, - }, - { - name: 'featured', - type: 'boolean', - label: 'Featured', - default: false, - helpText: 'Display on homepage', - listDisplay: true, - }, - { - name: 'publishedAt', - type: 'datetime', - label: 'Published At', - required: false, - listDisplay: true, - sortable: true, - }, - { - name: 'tags', - type: 'json', - label: 'Tags', - required: false, - helpText: 'JSON array of tag strings', - listDisplay: false, - }, - { - name: 'views', - type: 'number', - label: 'Views', - default: 0, - validation: { - min: 0, - }, - listDisplay: false, - }, - ], - }, - { - name: 'author', - label: 'Author', - labelPlural: 'Authors', - icon: 'User', - listDisplay: ['name', 'email', 'active', 'createdAt'], - listFilter: ['active'], - searchFields: ['name', 'email'], - ordering: ['name'], - fields: [ - { - name: 'id', - type: 'string', - label: 'ID', - required: true, - unique: true, - editable: false, - listDisplay: false, - }, - { - name: 'name', - type: 'string', - label: 'Name', - required: true, - validation: { - minLength: 2, - maxLength: 100, - }, - listDisplay: true, - searchable: true, - sortable: true, - }, - { - name: 'email', - type: 'email', - label: 'Email', - required: true, - unique: true, - listDisplay: true, - searchable: true, - sortable: true, - }, - { - name: 'bio', - type: 'text', - label: 'Bio', - required: false, - helpText: 'Author biography', - validation: { - maxLength: 1000, - }, - listDisplay: false, - }, - { - name: 'website', - type: 'url', - label: 'Website', - required: false, - listDisplay: false, - }, - { - name: 'active', - type: 'boolean', - label: 'Active', - default: true, - listDisplay: true, - }, - { - name: 'createdAt', - type: 'datetime', - label: 'Created At', - required: true, - editable: false, - listDisplay: true, - sortable: true, - }, - ], - }, - ], - }, - { - name: 'ecommerce', - label: 'E-Commerce', - models: [ - { - name: 'product', - label: 'Product', - labelPlural: 'Products', - icon: 'ShoppingCart', - listDisplay: ['name', 'price', 'stock', 'available'], - listFilter: ['available', 'category'], - searchFields: ['name', 'description'], - ordering: ['name'], - fields: [ - { - name: 'id', - type: 'string', - label: 'ID', - required: true, - unique: true, - editable: false, - listDisplay: false, - }, - { - name: 'name', - type: 'string', - label: 'Product Name', - required: true, - validation: { - minLength: 3, - maxLength: 200, - }, - listDisplay: true, - searchable: true, - sortable: true, - }, - { - name: 'description', - type: 'text', - label: 'Description', - required: false, - helpText: 'Product description', - listDisplay: false, - searchable: true, - }, - { - name: 'price', - type: 'number', - label: 'Price', - required: true, - validation: { - min: 0, - }, - listDisplay: true, - sortable: true, - }, - { - name: 'stock', - type: 'number', - label: 'Stock', - required: true, - default: 0, - validation: { - min: 0, - }, - listDisplay: true, - sortable: true, - }, - { - name: 'category', - type: 'select', - label: 'Category', - required: true, - choices: [ - { value: 'electronics', label: 'Electronics' }, - { value: 'clothing', label: 'Clothing' }, - { value: 'books', label: 'Books' }, - { value: 'home', label: 'Home & Garden' }, - { value: 'toys', label: 'Toys' }, - ], - listDisplay: false, - sortable: true, - }, - { - name: 'available', - type: 'boolean', - label: 'Available', - default: true, - listDisplay: true, - }, - ], - }, - ], - }, - ], + apps: defaultApps, } diff --git a/frontends/nextjs/src/lib/schema/default/components.ts b/frontends/nextjs/src/lib/schema/default/components.ts new file mode 100644 index 000000000..9fe3f02bd --- /dev/null +++ b/frontends/nextjs/src/lib/schema/default/components.ts @@ -0,0 +1,54 @@ +import type { AppSchema, ModelSchema } from '../../types/schema-types' +import { authorFields, postFields, productFields } from './forms' + +export const blogModels: ModelSchema[] = [ + { + name: 'post', + label: 'Post', + labelPlural: 'Posts', + icon: 'Article', + listDisplay: ['title', 'author', 'status', 'publishedAt'], + listFilter: ['status', 'author'], + searchFields: ['title', 'content'], + ordering: ['-publishedAt'], + fields: postFields, + }, + { + name: 'author', + label: 'Author', + labelPlural: 'Authors', + icon: 'User', + listDisplay: ['name', 'email', 'active', 'createdAt'], + listFilter: ['active'], + searchFields: ['name', 'email'], + ordering: ['name'], + fields: authorFields, + }, +] + +export const ecommerceModels: ModelSchema[] = [ + { + name: 'product', + label: 'Product', + labelPlural: 'Products', + icon: 'ShoppingCart', + listDisplay: ['name', 'price', 'stock', 'available'], + listFilter: ['available', 'category'], + searchFields: ['name', 'description'], + ordering: ['name'], + fields: productFields, + }, +] + +export const defaultApps: AppSchema[] = [ + { + name: 'blog', + label: 'Blog', + models: blogModels, + }, + { + name: 'ecommerce', + label: 'E-Commerce', + models: ecommerceModels, + }, +] diff --git a/frontends/nextjs/src/lib/schema/default/forms.ts b/frontends/nextjs/src/lib/schema/default/forms.ts new file mode 100644 index 000000000..81ae491a5 --- /dev/null +++ b/frontends/nextjs/src/lib/schema/default/forms.ts @@ -0,0 +1,244 @@ +import type { FieldSchema } from '../../types/schema-types' +import { authorValidations, postValidations, productValidations } from './validation' + +export const postFields: FieldSchema[] = [ + { + name: 'id', + type: 'string', + label: 'ID', + required: true, + unique: true, + editable: false, + listDisplay: false, + }, + { + name: 'title', + type: 'string', + label: 'Title', + required: true, + validation: postValidations.title, + listDisplay: true, + searchable: true, + sortable: true, + }, + { + name: 'slug', + type: 'string', + label: 'Slug', + required: true, + unique: true, + helpText: 'URL-friendly version of the title', + validation: postValidations.slug, + listDisplay: false, + sortable: true, + }, + { + name: 'content', + type: 'text', + label: 'Content', + required: true, + helpText: 'Main post content', + listDisplay: false, + searchable: true, + }, + { + name: 'excerpt', + type: 'text', + label: 'Excerpt', + required: false, + helpText: ['Short summary of the post', 'Used in list views and previews'], + validation: postValidations.excerpt, + listDisplay: false, + }, + { + name: 'author', + type: 'relation', + label: 'Author', + required: true, + relatedModel: 'author', + listDisplay: true, + sortable: true, + }, + { + name: 'status', + type: 'select', + label: 'Status', + required: true, + default: 'draft', + choices: [ + { value: 'draft', label: 'Draft' }, + { value: 'published', label: 'Published' }, + { value: 'archived', label: 'Archived' }, + ], + listDisplay: true, + sortable: true, + }, + { + name: 'featured', + type: 'boolean', + label: 'Featured', + default: false, + helpText: 'Display on homepage', + listDisplay: true, + }, + { + name: 'publishedAt', + type: 'datetime', + label: 'Published At', + required: false, + listDisplay: true, + sortable: true, + }, + { + name: 'tags', + type: 'json', + label: 'Tags', + required: false, + helpText: 'JSON array of tag strings', + listDisplay: false, + }, + { + name: 'views', + type: 'number', + label: 'Views', + default: 0, + validation: postValidations.views, + listDisplay: false, + }, +] + +export const authorFields: FieldSchema[] = [ + { + name: 'id', + type: 'string', + label: 'ID', + required: true, + unique: true, + editable: false, + listDisplay: false, + }, + { + name: 'name', + type: 'string', + label: 'Name', + required: true, + validation: authorValidations.name, + listDisplay: true, + searchable: true, + sortable: true, + }, + { + name: 'email', + type: 'email', + label: 'Email', + required: true, + unique: true, + listDisplay: true, + searchable: true, + sortable: true, + }, + { + name: 'bio', + type: 'text', + label: 'Bio', + required: false, + helpText: 'Author biography', + validation: authorValidations.bio, + listDisplay: false, + }, + { + name: 'website', + type: 'url', + label: 'Website', + required: false, + listDisplay: false, + }, + { + name: 'active', + type: 'boolean', + label: 'Active', + default: true, + listDisplay: true, + }, + { + name: 'createdAt', + type: 'datetime', + label: 'Created At', + required: true, + editable: false, + listDisplay: true, + sortable: true, + }, +] + +export const productFields: FieldSchema[] = [ + { + name: 'id', + type: 'string', + label: 'ID', + required: true, + unique: true, + editable: false, + listDisplay: false, + }, + { + name: 'name', + type: 'string', + label: 'Product Name', + required: true, + validation: productValidations.name, + listDisplay: true, + searchable: true, + sortable: true, + }, + { + name: 'description', + type: 'text', + label: 'Description', + required: false, + helpText: 'Product description', + listDisplay: false, + searchable: true, + }, + { + name: 'price', + type: 'number', + label: 'Price', + required: true, + validation: productValidations.price, + listDisplay: true, + sortable: true, + }, + { + name: 'stock', + type: 'number', + label: 'Stock', + required: true, + default: 0, + validation: productValidations.stock, + listDisplay: true, + sortable: true, + }, + { + name: 'category', + type: 'select', + label: 'Category', + required: true, + choices: [ + { value: 'electronics', label: 'Electronics' }, + { value: 'clothing', label: 'Clothing' }, + { value: 'books', label: 'Books' }, + { value: 'home', label: 'Home & Garden' }, + { value: 'toys', label: 'Toys' }, + ], + listDisplay: false, + sortable: true, + }, + { + name: 'available', + type: 'boolean', + label: 'Available', + default: true, + listDisplay: true, + }, +] diff --git a/frontends/nextjs/src/lib/schema/default/validation.ts b/frontends/nextjs/src/lib/schema/default/validation.ts new file mode 100644 index 000000000..0573520f4 --- /dev/null +++ b/frontends/nextjs/src/lib/schema/default/validation.ts @@ -0,0 +1,19 @@ +import type { FieldSchema } from '../../types/schema-types' + +export const postValidations: Record = { + title: { minLength: 3, maxLength: 200 }, + slug: { pattern: '^[a-z0-9-]+$' }, + excerpt: { maxLength: 500 }, + views: { min: 0 }, +} + +export const authorValidations: Record = { + name: { minLength: 2, maxLength: 100 }, + bio: { maxLength: 1000 }, +} + +export const productValidations: Record = { + name: { minLength: 3, maxLength: 200 }, + price: { min: 0 }, + stock: { min: 0 }, +} diff --git a/frontends/nextjs/src/lib/schema/index.ts b/frontends/nextjs/src/lib/schema/index.ts index 1e68e8671..97056cc3f 100644 --- a/frontends/nextjs/src/lib/schema/index.ts +++ b/frontends/nextjs/src/lib/schema/index.ts @@ -1,3 +1,6 @@ // Schema utilities exports export * from './schema-utils' export { defaultSchema } from './default-schema' +export * from './default/components' +export * from './default/forms' +export * from './default/validation'