model Entity { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt moduleId String? name String @unique slug String @unique order Int prefix String @unique type String @default("app") // app, admin, all title String titlePlural String isAutogenerated Boolean hasApi Boolean icon String active Boolean showInSidebar Boolean @default(true) hasTags Boolean @default(true) hasComments Boolean @default(true) hasTasks Boolean @default(true) hasActivity Boolean @default(true) hasBulkDelete Boolean @default(false) hasViews Boolean @default(true) defaultVisibility String @default("private") onCreated String? @default("redirectToOverview") // redirectToOverview, redirectToNew, redirectToList, redirectToEdit, addAnother onEdit String? @default("editRoute") // editRoute, overviewRoute, overviewAlwaysEditable createdPermissions Permission[] apiKeys ApiKeyEntity[] tags EntityTag[] permissions EntityTenantUserPermission[] webhooks EntityWebhook[] properties Property[] rows Row[] views EntityView[] parentEntities EntityRelationship[] @relation(name: "childEntities") childEntities EntityRelationship[] @relation(name: "parentEntities") // module Module? @relation(fields: [moduleId], references: [id]) groups EntityGroupEntity[] templates EntityTemplate[] @@index([name], name: "entity_name") @@index([slug], name: "entity_slug") } model Property { id String @id @default(cuid()) entityId String order Int name String title String type Int subtype String? isDefault Boolean @default(false) isRequired Boolean @default(false) isHidden Boolean @default(false) isDisplay Boolean @default(false) isUnique Boolean @default(false) // TODO: Validate uniqueness isReadOnly Boolean @default(false) showInCreate Boolean @default(true) canUpdate Boolean @default(true) formulaId String? formula Formula? @relation(fields: [formulaId], references: [id], onDelete: Cascade) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id]) attributes PropertyAttribute[] entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) options PropertyOption[] values RowValue[] inViewProperties EntityViewProperty[] inViewGroupBy EntityView[] @@unique([entityId, name, tenantId]) @@unique([entityId, title, tenantId]) @@index([entityId], name: "entity_property") @@index([entityId, name], name: "entity_property_name") } model EntityView { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdByUserId String? createdByUser User? @relation(name: "createdByUser", fields: [createdByUserId], references: [id]) entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) userId String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) layout String @default("table") // table, board, calendar, list, gallery... order Int name String title String pageSize Int isDefault Boolean isSystem Boolean @default(false) gridColumns Int? @default(5) gridColumnsSm Int? @default(2) gridColumnsMd Int? @default(3) gridColumnsLg Int? @default(4) gridColumnsXl Int? @default(5) gridColumns2xl Int? @default(6) gridGap String? @default("sm") properties EntityViewProperty[] filters EntityViewFilter[] sort EntityViewSort[] groupByPropertyId String? groupByProperty Property? @relation(fields: [groupByPropertyId], references: [id], onDelete: Cascade) inGroups EntityGroupEntity[] inChildRelationships EntityRelationship[] @relation(name: "childEntityView") inParentRelationships EntityRelationship[] @relation(name: "parentEntityView") @@index([entityId], name: "entity_view") @@index([entityId, name], name: "entity_view_name") } model EntityViewProperty { id String @id @default(cuid()) entityViewId String entityView EntityView @relation(fields: [entityViewId], references: [id], onDelete: Cascade) propertyId String? property Property? @relation(fields: [propertyId], references: [id], onDelete: Cascade) name String? // if not a property, e.g. "default.folio" order Int @@index([entityViewId], name: "entity_view_property") @@index([entityViewId, name], name: "entity_view_property_name") } model EntityViewFilter { id String @id @default(cuid()) entityViewId String entityView EntityView @relation(fields: [entityViewId], references: [id], onDelete: Cascade) match String @default("and") // and, or name String condition String // is, isNot, contains, doesNotContain... value String @@index([entityViewId], name: "entity_view_filter") @@index([entityViewId, name], name: "entity_view_filter_name") } model EntityViewSort { id String @id @default(cuid()) entityViewId String entityView EntityView @relation(fields: [entityViewId], references: [id], onDelete: Cascade) name String asc Boolean order Int @@index([entityViewId], name: "entity_view_sort") @@index([entityViewId, name], name: "entity_view_sort_name") } model PropertyAttribute { id String @id @default(cuid()) propertyId String property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade) name String // pattern, min, max, step, rows, defaultValue, maxSize, acceptFileTypes, uppercase... value String @@unique([propertyId, name]) @@index([propertyId], name: "property_attribute") @@index([propertyId, name], name: "property_attribute_name") } model PropertyOption { id String @id @default(cuid()) propertyId String order Int value String name String? color Int @default(0) property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade) // values RowValueSelection[] @@index([propertyId], name: "property_option") @@index([propertyId, name], name: "property_option_name") } model EntityTag { id String @id @default(cuid()) createdAt DateTime @default(now()) entityId String value String color Int entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) rowTags RowTag[] @@index([entityId], name: "entity_tag") @@index([entityId, value], name: "entity_tag_value") } model EntityTenantUserPermission { id String @id @default(cuid()) entityId String level Int entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) } model EntityWebhook { id String @id @default(cuid()) entityId String action String method String endpoint String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) logs EntityWebhookLog[] } model EntityWebhookLog { id String @id @default(cuid()) webhookId String logId String status Int error String? log Log @relation(fields: [logId], references: [id], onDelete: Cascade) webhook EntityWebhook @relation(fields: [webhookId], references: [id], onDelete: Cascade) } model EntityRelationship { id String @id @default(cuid()) parentId String parent Entity @relation(name: "parentEntities", fields: [parentId], references: [id], onDelete: Cascade) childId String child Entity @relation(name: "childEntities", fields: [childId], references: [id], onDelete: Cascade) order Int? title String? type String @default("one-to-many") required Boolean @default(false) cascade Boolean @default(false) readOnly Boolean @default(false) hiddenIfEmpty Boolean @default(false) childEntityViewId String? childEntityView EntityView? @relation(name: "childEntityView", fields: [childEntityViewId], references: [id], onDelete: Cascade) parentEntityViewId String? parentEntityView EntityView? @relation(name: "parentEntityView", fields: [parentEntityViewId], references: [id], onDelete: Cascade) rows RowRelationship[] @@unique([parentId, childId, title]) @@index([parentId], name: "parent_entity_relationship") @@index([childId], name: "child_entity_relationship") @@index([parentId, childId], name: "parent_child_entity_relationship") @@index([parentId, childId, order], name: "parent_child_entity_relationship_order") } model SampleCustomEntity { rowId String @unique row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) customText String customNumber Decimal customDate DateTime customBoolean Boolean customSelect String } model RowRelationship { id String @id @default(cuid()) createdAt DateTime @default(now()) relationshipId String relationship EntityRelationship @relation(fields: [relationshipId], references: [id], onDelete: Cascade) parentId String parent Row @relation(name: "parentRow", fields: [parentId], references: [id], onDelete: Cascade) childId String child Row @relation(name: "childRow", fields: [childId], references: [id], onDelete: Cascade) metadata String? @@unique([parentId, childId]) @@index([parentId], name: "parent_row_relationship") @@index([childId], name: "child_row_relationship") @@index([parentId, childId], name: "parent_child_row_relationship") } model Row { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? entityId String tenantId String? folio Int createdByUserId String? createdByApiKeyId String? order Int @default(0) createdByApiKey ApiKey? @relation(fields: [createdByApiKeyId], references: [id]) createdByUser User? @relation(fields: [createdByUserId], references: [id]) entity Entity @relation(fields: [entityId], references: [id]) tenant Tenant? @relation(fields: [tenantId], references: [id]) logs Log[] comments RowComment[] permissions RowPermission[] tags RowTag[] tasks RowTask[] values RowValue[] childRows RowRelationship[] @relation("parentRow") parentRows RowRelationship[] @relation("childRow") sampleCustomEntity SampleCustomEntity? outboundEmails OutboundEmail[] tenantSettingsRow TenantSettingsRow[] formulaCalculationLogs FormulaComponentLog[] inEntityGroupConfigurationRows EntityGroupConfigurationRow[] @@index([deletedAt], name: "row_deletedAt") @@index([entityId], name: "row_entity") @@index([entityId, tenantId], name: "row_entity_tenant") @@index([entityId, tenantId, createdAt], name: "row_entity_tenant_created_at") @@index([tenantId], name: "row_tenant") @@index([createdByUserId], name: "row_createdByUserId") } model RowValue { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime? @updatedAt rowId String propertyId String textValue String? numberValue Decimal? dateValue DateTime? booleanValue Boolean? // selection RowValueSelection[] // TODO: MULTI SELECT property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) media RowMedia[] multiple RowValueMultiple[] range RowValueRange? @@index([rowId], name: "row_value_row") @@index([rowId, propertyId], name: "row_value_row_property") } model RowValueMultiple { id String @id @default(cuid()) rowValueId String order Int value String rowValue RowValue @relation(fields: [rowValueId], references: [id], onDelete: Cascade) @@index([rowValueId], name: "row_value_multiple_row_value") } model RowValueRange { rowValueId String @unique numberMin Decimal? numberMax Decimal? dateMin DateTime? dateMax DateTime? rowValue RowValue @relation(fields: [rowValueId], references: [id], onDelete: Cascade) @@index([rowValueId], name: "row_value_range_row_value") } model RowPermission { id String @id @default(cuid()) rowId String tenantId String? roleId String? groupId String? userId String? public Boolean? access String @default("view") group Group? @relation(fields: [groupId], references: [id], onDelete: Cascade) role Role? @relation(fields: [roleId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([rowId], name: "row_permission_row") @@index([rowId, tenantId], name: "row_permission_row_tenant") @@index([rowId, roleId], name: "row_permission_row_role") @@index([rowId, groupId], name: "row_permission_row_group") @@index([rowId, userId], name: "row_permission_row_user") @@index([public], name: "row_permission_public") } model RowMedia { id String @id @default(cuid()) rowValueId String title String name String file String type String publicUrl String? storageBucket String? storageProvider String? rowValue RowValue @relation(fields: [rowValueId], references: [id], onDelete: Cascade) @@index([rowValueId], name: "row_media_row_value") @@index([rowValueId, name], name: "row_media_row_value_name") } model RowTag { id String @id @default(cuid()) createdAt DateTime @default(now()) rowId String tagId String row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) tag EntityTag @relation(fields: [tagId], references: [id], onDelete: Cascade) @@index([rowId], name: "row_tag_row") @@index([rowId, tagId], name: "row_tag_row_tag") } model RowComment { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String rowId String value String isDeleted Boolean? createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) logs Log[] reactions RowCommentReaction[] @@index([rowId], name: "row_comment_row") } model RowCommentReaction { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String rowCommentId String reaction String createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) rowComment RowComment @relation(fields: [rowCommentId], references: [id], onDelete: Cascade) } model RowTask { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String rowId String title String description String completed Boolean completedAt DateTime? completedByUserId String? assignedToUserId String? deadline DateTime? assignedToUser User? @relation("assignedToUser", fields: [assignedToUserId], references: [id], onDelete: Cascade) completedByUser User? @relation("completedByUser", fields: [completedByUserId], references: [id], onDelete: Cascade) createdByUser User @relation("createdByUser", fields: [createdByUserId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) } model EntityTemplate { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) title String config String @@unique([tenantId, entityId, title]) } model EntityGroup { id String @id @default(cuid()) createdAt DateTime @default(now()) order Int slug String title String icon String collapsible Boolean section String? entities EntityGroupEntity[] configurationRows EntityGroupConfiguration[] @@unique([slug]) } model EntityGroupEntity { id String @id @default(cuid()) entityGroupId String entityGroup EntityGroup @relation(fields: [entityGroupId], references: [id], onDelete: Cascade) entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) allViewId String? allView EntityView? @relation(fields: [allViewId], references: [id], onDelete: Cascade) selectMin Int? selectMax Int? } model Formula { id String @id @default(cuid()) createdAt DateTime @default(now()) name String description String? resultAs String // string, number, boolean, date calculationTrigger String // one or many: always, onList, onCreated, onOverview, onEdit, custom, manual withLogs Boolean @default(false) components FormulaComponent[] inProperties Property[] logs FormulaLog[] } model FormulaComponent { id String @id @default(cuid()) formulaId String formula Formula @relation(fields: [formulaId], references: [id], onDelete: Cascade) order Int type String //name, operator, parenthesis, value value String } model FormulaLog { id String @id @default(cuid()) createdAt DateTime @default(now()) formulaId String userId String? tenantId String? originalTrigger String? triggeredBy String expression String result String duration Int @default(0) error String? rowValueId String? formula Formula @relation(fields: [formulaId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: Cascade) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) components FormulaComponentLog[] } model FormulaComponentLog { id String @id @default(cuid()) order Int type String //name, operator, parenthesis, value value String rowId String? row Row? @relation(fields: [rowId], references: [id], onDelete: Cascade) formulaLogId String formulaLog FormulaLog @relation(fields: [formulaLogId], references: [id], onDelete: Cascade) } model EntityGroupConfiguration { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) entityGroupId String entityGroup EntityGroup @relation(fields: [entityGroupId], references: [id], onDelete: Cascade) title String rows EntityGroupConfigurationRow[] } model EntityGroupConfigurationRow { id String @id @default(cuid()) entityGroupConfigurationId String entityGroupConfiguration EntityGroupConfiguration @relation(fields: [entityGroupConfigurationId], references: [id], onDelete: Cascade) rowId String row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) @@unique([entityGroupConfigurationId, rowId]) } model ApiKeyEntity { id String @id @default(cuid()) apiKeyId String entityId String create Boolean read Boolean update Boolean delete Boolean apiKey ApiKey @relation(fields: [apiKeyId], references: [id], onDelete: Cascade) entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) } model TenantSettingsRow { tenantId String @id @unique rowId String tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) }