Add example in create-twenty-app (#18043)

- add interactive mode to create-twenty-app
- by default create an example for each entities

<img width="1181" height="168" alt="image"
src="https://github.com/user-attachments/assets/a2490d8f-66a1-4cd5-bf41-57166cc20a1e"
/>
This commit is contained in:
martmull
2026-02-18 19:04:19 +01:00
committed by GitHub
parent 9733ff1b8e
commit 2cc3c75c7e
15 changed files with 934 additions and 38 deletions
@@ -1,10 +1,12 @@
import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/utilities/config/current-execution-directory';
import { getFrontComponentBaseFile } from '@/cli/utilities/entity/entity-front-component-template';
import { getLogicFunctionBaseFile } from '@/cli/utilities/entity/entity-logic-function-template';
import { getNavigationMenuItemBaseFile } from '@/cli/utilities/entity/entity-navigation-menu-item-template';
import { convertToLabel } from '@/cli/utilities/entity/entity-label';
import { getObjectBaseFile } from '@/cli/utilities/entity/entity-object-template';
import { getPageLayoutBaseFile } from '@/cli/utilities/entity/entity-page-layout-template';
import { getRoleBaseFile } from '@/cli/utilities/entity/entity-role-template';
import { getViewBaseFile } from '@/cli/utilities/entity/entity-view-template';
import chalk from 'chalk';
import * as fs from 'fs-extra';
import inquirer from 'inquirer';
@@ -114,13 +116,34 @@ export class EntityAddCommand {
return { name, file };
}
case SyncableEntity.View: {
const entityData = await this.getViewData();
const name = entityData.name;
const file = getViewBaseFile({
name,
});
return { name, file };
}
case SyncableEntity.NavigationMenuItem: {
const name = await this.getEntityName(entity);
const file = getNavigationMenuItemBaseFile({
name,
});
return { name, file };
}
case SyncableEntity.PageLayout: {
const name = await this.getEntityName(entity);
const file = getPageLayoutBaseFile({
name,
});
return { name, file };
}
@@ -302,6 +325,39 @@ export class EntityAddCommand {
]);
}
private async getViewData() {
return inquirer.prompt<{
name: string;
objectUniversalIdentifier: string;
}>([
{
type: 'input',
name: 'name',
message: 'Enter a name for your view:',
default: '',
validate: (input: string) => {
if (!input || input.trim().length === 0) {
return 'Please enter a non empty string';
}
return true;
},
},
{
type: 'input',
name: 'objectUniversalIdentifier',
message:
'Enter the universalIdentifier of the object this view belongs to:',
default: 'fill-later',
validate: (input: string) => {
if (!input || input.trim().length === 0) {
return 'Please enter a non empty string';
}
return true;
},
},
]);
}
getFolderName(entity: SyncableEntity) {
return `${kebabcase(entity)}s`;
}