From 21b993e9b9ca5720b6da863522e1d05bf767ea02 Mon Sep 17 00:00:00 2001 From: nichenqin Date: Thu, 10 Oct 2024 14:46:27 +0800 Subject: [PATCH 1/2] feat: init chart --- .../drizzle/0007_steep_dragon_lord.sql | 1 + apps/backend/drizzle/meta/0007_snapshot.json | 1796 +++++++++++++++++ apps/backend/drizzle/meta/_journal.json | 7 + apps/frontend/schema.graphql | 7 + .../lib/components/blocks/chart/chart.svelte | 13 + .../blocks/chart/count-chart.svelte | 10 + .../blocks/grid-view/grid-view.svelte | 4 + .../blocks/table-tools/table-tools.svelte | 2 + .../view-widget/view-widget-button.svelte | 10 + .../view-widget/view-widget-sheet.svelte | 62 + .../blocks/widget/create-widget-form.svelte | 76 + .../components/blocks/widget/widget.svelte | 12 + apps/frontend/src/lib/store/modal.store.ts | 2 + .../(authed)/(space)/t/[tableId]/+layout.gql | 6 + .../create-view-widget.command-handler.ts | 22 + .../command-handlers/src/handlers/index.ts | 2 + .../src/create-view-widget.command.ts | 22 + packages/commands/src/index.ts | 1 + packages/graphql/src/index.ts | 7 + .../src/table/table.filter-visitor.ts | 4 + .../src/table/table.mutation-visitor.ts | 4 + .../src/table/table.reference-visitor.ts | 2 + .../persistence/src/table/table.repository.ts | 12 +- packages/persistence/src/tables.ts | 1 + .../underlying-table-spec.visitor.ts | 2 + packages/table/src/dto/create-table.dto.ts | 5 +- packages/table/src/modules/chart/chart.vo.ts | 41 + packages/table/src/modules/index.ts | 1 + .../src/modules/views/dto/create-view.dto.ts | 2 + .../table/src/modules/views/view/index.ts | 1 + .../views/view/variants/abstract-view.vo.ts | 21 +- .../modules/views/view/view-widget/index.ts | 1 + .../views/view/view-widget/view-widget.dto.ts | 10 + packages/table/src/modules/views/views.vo.ts | 12 + packages/table/src/modules/widgets/index.ts | 1 + .../table/src/modules/widgets/widget-id.vo.ts | 12 + .../src/modules/widgets/widget-name.vo.ts | 12 + .../table/src/modules/widgets/widget.vo.ts | 61 + .../table-view.specification.ts | 23 + .../specifications/table-visitor.interface.ts | 2 + packages/trpc/src/router.ts | 16 +- 41 files changed, 2296 insertions(+), 12 deletions(-) create mode 100644 apps/backend/drizzle/0007_steep_dragon_lord.sql create mode 100644 apps/backend/drizzle/meta/0007_snapshot.json create mode 100644 apps/frontend/src/lib/components/blocks/chart/chart.svelte create mode 100644 apps/frontend/src/lib/components/blocks/chart/count-chart.svelte create mode 100644 apps/frontend/src/lib/components/blocks/view-widget/view-widget-button.svelte create mode 100644 apps/frontend/src/lib/components/blocks/view-widget/view-widget-sheet.svelte create mode 100644 apps/frontend/src/lib/components/blocks/widget/create-widget-form.svelte create mode 100644 apps/frontend/src/lib/components/blocks/widget/widget.svelte create mode 100644 packages/command-handlers/src/handlers/create-view-widget.command-handler.ts create mode 100644 packages/commands/src/create-view-widget.command.ts create mode 100644 packages/table/src/modules/chart/chart.vo.ts create mode 100644 packages/table/src/modules/views/view/view-widget/index.ts create mode 100644 packages/table/src/modules/views/view/view-widget/view-widget.dto.ts create mode 100644 packages/table/src/modules/widgets/index.ts create mode 100644 packages/table/src/modules/widgets/widget-id.vo.ts create mode 100644 packages/table/src/modules/widgets/widget-name.vo.ts create mode 100644 packages/table/src/modules/widgets/widget.vo.ts diff --git a/apps/backend/drizzle/0007_steep_dragon_lord.sql b/apps/backend/drizzle/0007_steep_dragon_lord.sql new file mode 100644 index 000000000..49153cf17 --- /dev/null +++ b/apps/backend/drizzle/0007_steep_dragon_lord.sql @@ -0,0 +1 @@ +ALTER TABLE `undb_table` ADD `widgets` text; \ No newline at end of file diff --git a/apps/backend/drizzle/meta/0007_snapshot.json b/apps/backend/drizzle/meta/0007_snapshot.json new file mode 100644 index 000000000..cb0d54af8 --- /dev/null +++ b/apps/backend/drizzle/meta/0007_snapshot.json @@ -0,0 +1,1796 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "7f4f5874-e1a9-431c-a1e8-926866197d71", + "prevId": "f4f5f7dd-cbf3-45de-a072-b086f2e2c170", + "tables": { + "undb_api_token": { + "name": "undb_api_token", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "undb_api_token_token_unique": { + "name": "undb_api_token_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "api_token_space_id_idx": { + "name": "api_token_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "api_token_user_id_idx": { + "name": "api_token_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_api_token_user_id_undb_user_id_fk": { + "name": "undb_api_token_user_id_undb_user_id_fk", + "tableFrom": "undb_api_token", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_api_token_space_id_undb_space_id_fk": { + "name": "undb_api_token_space_id_undb_space_id_fk", + "tableFrom": "undb_api_token", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_attachment_mapping": { + "name": "undb_attachment_mapping", + "columns": { + "attachment_id": { + "name": "attachment_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "record_id": { + "name": "record_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "field_id": { + "name": "field_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_attachment_mapping_attachment_id_undb_attachment_id_fk": { + "name": "undb_attachment_mapping_attachment_id_undb_attachment_id_fk", + "tableFrom": "undb_attachment_mapping", + "tableTo": "undb_attachment", + "columnsFrom": [ + "attachment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_attachment_mapping_table_id_undb_table_id_fk": { + "name": "undb_attachment_mapping_table_id_undb_table_id_fk", + "tableFrom": "undb_attachment_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "undb_attachment_mapping_attachment_id_table_id_record_id_field_id_pk": { + "columns": [ + "attachment_id", + "table_id", + "record_id", + "field_id" + ], + "name": "undb_attachment_mapping_attachment_id_table_id_record_id_field_id_pk" + } + }, + "uniqueConstraints": {} + }, + "undb_attachment": { + "name": "undb_attachment", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "attachment_size_idx": { + "name": "attachment_size_idx", + "columns": [ + "size" + ], + "isUnique": false + }, + "attachment_space_id_idx": { + "name": "attachment_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_attachment_created_by_undb_user_id_fk": { + "name": "undb_attachment_created_by_undb_user_id_fk", + "tableFrom": "undb_attachment", + "tableTo": "undb_user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_attachment_space_id_undb_space_id_fk": { + "name": "undb_attachment_space_id_undb_space_id_fk", + "tableFrom": "undb_attachment", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_audit": { + "name": "undb_audit", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "detail": { + "name": "detail", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "meta": { + "name": "meta", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "op": { + "name": "op", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "record_id": { + "name": "record_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "operator_id": { + "name": "operator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "audit_table_id_idx": { + "name": "audit_table_id_idx", + "columns": [ + "table_id" + ], + "isUnique": false + }, + "audit_space_id_idx": { + "name": "audit_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "audit_record_id_idx": { + "name": "audit_record_id_idx", + "columns": [ + "record_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_audit_space_id_undb_space_id_fk": { + "name": "undb_audit_space_id_undb_space_id_fk", + "tableFrom": "undb_audit", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_base": { + "name": "undb_base", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(CURRENT_TIMESTAMP)" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "base_space_id_idx": { + "name": "base_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "base_name_unique_idx": { + "name": "base_name_unique_idx", + "columns": [ + "name", + "space_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_base_space_id_undb_space_id_fk": { + "name": "undb_base_space_id_undb_space_id_fk", + "tableFrom": "undb_base", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_base_created_by_undb_user_id_fk": { + "name": "undb_base_created_by_undb_user_id_fk", + "tableFrom": "undb_base", + "tableTo": "undb_user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_base_updated_by_undb_user_id_fk": { + "name": "undb_base_updated_by_undb_user_id_fk", + "tableFrom": "undb_base", + "tableTo": "undb_user", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_email_verification_code": { + "name": "undb_email_verification_code", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "undb_email_verification_code_user_id_unique": { + "name": "undb_email_verification_code_user_id_unique", + "columns": [ + "user_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_email_verification_code_user_id_undb_user_id_fk": { + "name": "undb_email_verification_code_user_id_undb_user_id_fk", + "tableFrom": "undb_email_verification_code", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_invitation": { + "name": "undb_invitation", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "invited_at": { + "name": "invited_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "invitation_space_id_idx": { + "name": "invitation_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "invitation_unique_idx": { + "name": "invitation_unique_idx", + "columns": [ + "email", + "space_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_invitation_space_id_undb_space_id_fk": { + "name": "undb_invitation_space_id_undb_space_id_fk", + "tableFrom": "undb_invitation", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_invitation_inviter_id_undb_user_id_fk": { + "name": "undb_invitation_inviter_id_undb_user_id_fk", + "tableFrom": "undb_invitation", + "tableTo": "undb_user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_oauth_account": { + "name": "undb_oauth_account", + "columns": { + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_oauth_account_user_id_undb_user_id_fk": { + "name": "undb_oauth_account_user_id_undb_user_id_fk", + "tableFrom": "undb_oauth_account", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "undb_oauth_account_provider_id_provider_user_id_pk": { + "columns": [ + "provider_id", + "provider_user_id" + ], + "name": "undb_oauth_account_provider_id_provider_user_id_pk" + } + }, + "uniqueConstraints": {} + }, + "undb_outbox": { + "name": "undb_outbox", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "meta": { + "name": "meta", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "timestamp": { + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "outbox_space_id_idx": { + "name": "outbox_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_outbox_space_id_undb_space_id_fk": { + "name": "undb_outbox_space_id_undb_space_id_fk", + "tableFrom": "undb_outbox", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_password_reset_token": { + "name": "undb_password_reset_token", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "undb_password_reset_token_token_unique": { + "name": "undb_password_reset_token_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "password_reset_token_user_id_idx": { + "name": "password_reset_token_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_password_reset_token_user_id_undb_user_id_fk": { + "name": "undb_password_reset_token_user_id_undb_user_id_fk", + "tableFrom": "undb_password_reset_token", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_reference_id_mapping": { + "name": "undb_reference_id_mapping", + "columns": { + "field_id": { + "name": "field_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "symmetric_field_id": { + "name": "symmetric_field_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "foreign_table_id": { + "name": "foreign_table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "reference_id_mapping_unique_idx": { + "name": "reference_id_mapping_unique_idx", + "columns": [ + "field_id", + "table_id", + "symmetric_field_id", + "foreign_table_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_reference_id_mapping_table_id_undb_table_id_fk": { + "name": "undb_reference_id_mapping_table_id_undb_table_id_fk", + "tableFrom": "undb_reference_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_reference_id_mapping_foreign_table_id_undb_table_id_fk": { + "name": "undb_reference_id_mapping_foreign_table_id_undb_table_id_fk", + "tableFrom": "undb_reference_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "foreign_table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_rollup_id_mapping": { + "name": "undb_rollup_id_mapping", + "columns": { + "field_id": { + "name": "field_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rollup_id": { + "name": "rollup_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rollup_table_id": { + "name": "rollup_table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_rollup_id_mapping_table_id_undb_table_id_fk": { + "name": "undb_rollup_id_mapping_table_id_undb_table_id_fk", + "tableFrom": "undb_rollup_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_rollup_id_mapping_rollup_table_id_undb_table_id_fk": { + "name": "undb_rollup_id_mapping_rollup_table_id_undb_table_id_fk", + "tableFrom": "undb_rollup_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "rollup_table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "undb_rollup_id_mapping_field_id_rollup_id_pk": { + "columns": [ + "field_id", + "rollup_id" + ], + "name": "undb_rollup_id_mapping_field_id_rollup_id_pk" + } + }, + "uniqueConstraints": {} + }, + "undb_session": { + "name": "undb_session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_session_user_id_undb_user_id_fk": { + "name": "undb_session_user_id_undb_user_id_fk", + "tableFrom": "undb_session", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_session_space_id_undb_space_id_fk": { + "name": "undb_session_space_id_undb_space_id_fk", + "tableFrom": "undb_session", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_share": { + "name": "undb_share", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "share_space_id_idx": { + "name": "share_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "share_unique_idx": { + "name": "share_unique_idx", + "columns": [ + "target_type", + "target_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_share_space_id_undb_space_id_fk": { + "name": "undb_share_space_id_undb_space_id_fk", + "tableFrom": "undb_share", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_space": { + "name": "undb_space", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_personal": { + "name": "is_personal", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(CURRENT_TIMESTAMP)" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_by": { + "name": "deleted_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "space_name_idx": { + "name": "space_name_idx", + "columns": [ + "name" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_space_created_by_undb_user_id_fk": { + "name": "undb_space_created_by_undb_user_id_fk", + "tableFrom": "undb_space", + "tableTo": "undb_user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_space_updated_by_undb_user_id_fk": { + "name": "undb_space_updated_by_undb_user_id_fk", + "tableFrom": "undb_space", + "tableTo": "undb_user", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_space_deleted_by_undb_user_id_fk": { + "name": "undb_space_deleted_by_undb_user_id_fk", + "tableFrom": "undb_space", + "tableTo": "undb_user", + "columnsFrom": [ + "deleted_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_space_member": { + "name": "undb_space_member", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "space_member_unique_idx": { + "name": "space_member_unique_idx", + "columns": [ + "user_id", + "space_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_space_member_user_id_undb_user_id_fk": { + "name": "undb_space_member_user_id_undb_user_id_fk", + "tableFrom": "undb_space_member", + "tableTo": "undb_user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_space_member_space_id_undb_space_id_fk": { + "name": "undb_space_member_space_id_undb_space_id_fk", + "tableFrom": "undb_space_member", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_table_id_mapping": { + "name": "undb_table_id_mapping", + "columns": { + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "subject_id": { + "name": "subject_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "undb_table_id_mapping_table_id_undb_table_id_fk": { + "name": "undb_table_id_mapping_table_id_undb_table_id_fk", + "tableFrom": "undb_table_id_mapping", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "undb_table_id_mapping_table_id_subject_id_pk": { + "columns": [ + "table_id", + "subject_id" + ], + "name": "undb_table_id_mapping_table_id_subject_id_pk" + } + }, + "uniqueConstraints": {} + }, + "undb_table": { + "name": "undb_table", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "base_id": { + "name": "base_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "schema": { + "name": "schema", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "views": { + "name": "views", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "forms": { + "name": "forms", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rls": { + "name": "rls", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "widgets": { + "name": "widgets", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(CURRENT_TIMESTAMP)" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "table_base_id_idx": { + "name": "table_base_id_idx", + "columns": [ + "base_id" + ], + "isUnique": false + }, + "table_space_id_idx": { + "name": "table_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "table_name_unique_idx": { + "name": "table_name_unique_idx", + "columns": [ + "name", + "base_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "undb_table_base_id_undb_base_id_fk": { + "name": "undb_table_base_id_undb_base_id_fk", + "tableFrom": "undb_table", + "tableTo": "undb_base", + "columnsFrom": [ + "base_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_table_space_id_undb_space_id_fk": { + "name": "undb_table_space_id_undb_space_id_fk", + "tableFrom": "undb_table", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_table_created_by_undb_user_id_fk": { + "name": "undb_table_created_by_undb_user_id_fk", + "tableFrom": "undb_table", + "tableTo": "undb_user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_table_updated_by_undb_user_id_fk": { + "name": "undb_table_updated_by_undb_user_id_fk", + "tableFrom": "undb_table", + "tableTo": "undb_user", + "columnsFrom": [ + "updated_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_user": { + "name": "undb_user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "undb_user_email_unique": { + "name": "undb_user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + }, + "user_username_idx": { + "name": "user_username_idx", + "columns": [ + "username" + ], + "isUnique": false + }, + "user_email_idx": { + "name": "user_email_idx", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "undb_webhook": { + "name": "undb_webhook", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "table_id": { + "name": "table_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "headers": { + "name": "headers", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "condition": { + "name": "condition", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "event": { + "name": "event", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "space_id": { + "name": "space_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "webhook_table_id_idx": { + "name": "webhook_table_id_idx", + "columns": [ + "table_id" + ], + "isUnique": false + }, + "webhook_space_id_idx": { + "name": "webhook_space_id_idx", + "columns": [ + "space_id" + ], + "isUnique": false + }, + "webhook_url_idx": { + "name": "webhook_url_idx", + "columns": [ + "url" + ], + "isUnique": false + } + }, + "foreignKeys": { + "undb_webhook_table_id_undb_table_id_fk": { + "name": "undb_webhook_table_id_undb_table_id_fk", + "tableFrom": "undb_webhook", + "tableTo": "undb_table", + "columnsFrom": [ + "table_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "undb_webhook_space_id_undb_space_id_fk": { + "name": "undb_webhook_space_id_undb_space_id_fk", + "tableFrom": "undb_webhook", + "tableTo": "undb_space", + "columnsFrom": [ + "space_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/apps/backend/drizzle/meta/_journal.json b/apps/backend/drizzle/meta/_journal.json index 8b955642d..f480c5c3b 100644 --- a/apps/backend/drizzle/meta/_journal.json +++ b/apps/backend/drizzle/meta/_journal.json @@ -50,6 +50,13 @@ "when": 1728358607342, "tag": "0006_mature_madame_web", "breakpoints": true + }, + { + "idx": 7, + "version": "6", + "when": 1728539365470, + "tag": "0007_steep_dragon_lord", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/frontend/schema.graphql b/apps/frontend/schema.graphql index f9d483a31..9a1a83079 100644 --- a/apps/frontend/schema.graphql +++ b/apps/frontend/schema.graphql @@ -254,6 +254,7 @@ type View { shareId: ID sort: JSON type: ViewType! + widgets: [Widget] } type ViewData { @@ -269,3 +270,9 @@ enum ViewType { grid kanban } + +type Widget { + id: ID! + item: JSON + name: String! +} diff --git a/apps/frontend/src/lib/components/blocks/chart/chart.svelte b/apps/frontend/src/lib/components/blocks/chart/chart.svelte new file mode 100644 index 000000000..26b199835 --- /dev/null +++ b/apps/frontend/src/lib/components/blocks/chart/chart.svelte @@ -0,0 +1,13 @@ + + + diff --git a/apps/frontend/src/lib/components/blocks/chart/count-chart.svelte b/apps/frontend/src/lib/components/blocks/chart/count-chart.svelte new file mode 100644 index 000000000..8f6ae83f6 --- /dev/null +++ b/apps/frontend/src/lib/components/blocks/chart/count-chart.svelte @@ -0,0 +1,10 @@ + + + +
+ 50 +
+
diff --git a/apps/frontend/src/lib/components/blocks/grid-view/grid-view.svelte b/apps/frontend/src/lib/components/blocks/grid-view/grid-view.svelte index 0facbe0f2..27493e145 100644 --- a/apps/frontend/src/lib/components/blocks/grid-view/grid-view.svelte +++ b/apps/frontend/src/lib/components/blocks/grid-view/grid-view.svelte @@ -80,3 +80,7 @@ {#await import("$lib/components/blocks/record-detail/table-record-detail-sheet.svelte") then { default: TableRecordDetailSheet }} {/await} + +{#await import("$lib/components/blocks/view-widget/view-widget-sheet.svelte") then { default: ViewWidgetSheet }} + +{/await} diff --git a/apps/frontend/src/lib/components/blocks/table-tools/table-tools.svelte b/apps/frontend/src/lib/components/blocks/table-tools/table-tools.svelte index 40ca27e79..8b7970d14 100644 --- a/apps/frontend/src/lib/components/blocks/table-tools/table-tools.svelte +++ b/apps/frontend/src/lib/components/blocks/table-tools/table-tools.svelte @@ -8,6 +8,7 @@ import ViewFields from "../view-fields/view-fields.svelte" import ShareViewButton from "../share/share-view-button.svelte" import BulkUpdateRecordsButton from "../bulk-update-records/bulk-update-records-button.svelte" + import ViewWidgetButton from "../view-widget/view-widget-button.svelte" import type { Writable } from "svelte/store" export let readonly = false @@ -33,5 +34,6 @@ {/if} + diff --git a/apps/frontend/src/lib/components/blocks/view-widget/view-widget-button.svelte b/apps/frontend/src/lib/components/blocks/view-widget/view-widget-button.svelte new file mode 100644 index 000000000..ea3509ff8 --- /dev/null +++ b/apps/frontend/src/lib/components/blocks/view-widget/view-widget-button.svelte @@ -0,0 +1,10 @@ + + + diff --git a/apps/frontend/src/lib/components/blocks/view-widget/view-widget-sheet.svelte b/apps/frontend/src/lib/components/blocks/view-widget/view-widget-sheet.svelte new file mode 100644 index 000000000..360ecb502 --- /dev/null +++ b/apps/frontend/src/lib/components/blocks/view-widget/view-widget-sheet.svelte @@ -0,0 +1,62 @@ + + + toggleModal(VIEW_WIDGET_MODAL)}> + + + Widgets + View Widgets + +
+ {#each $widgets as widget} + + {/each} + + + + + + {#if $viewId} + { + open = false + }} + /> + {/if} + + +
+ + + + + +
+
diff --git a/apps/frontend/src/lib/components/blocks/widget/create-widget-form.svelte b/apps/frontend/src/lib/components/blocks/widget/create-widget-form.svelte new file mode 100644 index 000000000..fa5bc5a27 --- /dev/null +++ b/apps/frontend/src/lib/components/blocks/widget/create-widget-form.svelte @@ -0,0 +1,76 @@ + + +
+ + + Name + + + + + + +
diff --git a/apps/frontend/src/lib/components/blocks/widget/widget.svelte b/apps/frontend/src/lib/components/blocks/widget/widget.svelte new file mode 100644 index 000000000..b7e86a3f0 --- /dev/null +++ b/apps/frontend/src/lib/components/blocks/widget/widget.svelte @@ -0,0 +1,12 @@ + + +
+ {#if widget.item.type === "chart"} + + {/if} +
diff --git a/apps/frontend/src/lib/store/modal.store.ts b/apps/frontend/src/lib/store/modal.store.ts index 1309ea49b..0f49c6c51 100644 --- a/apps/frontend/src/lib/store/modal.store.ts +++ b/apps/frontend/src/lib/store/modal.store.ts @@ -22,6 +22,7 @@ export const DUPLICATE_BASE_MODAL = "duplicateBase" as const export const UPDATE_BASE_MODAL = "updateBase" as const export const DELETE_TABLE_MODAL = "deleteTable" as const export const IMPORT_TEMPLATE_MODAL = "importTemplate" as const +export const VIEW_WIDGET_MODAL = "viewWidget" as const type ModalType = | typeof CREATE_TABLE_MODAL @@ -42,6 +43,7 @@ type ModalType = | typeof DELETE_TABLE_MODAL | typeof DUPLICATE_BASE_MODAL | typeof IMPORT_TEMPLATE_MODAL + | typeof VIEW_WIDGET_MODAL export const toggleModal = (type: ModalType) => { modal.update(($modal) => { diff --git a/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.gql b/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.gql index 9be009854..7ce7cc4dc 100644 --- a/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.gql +++ b/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.gql @@ -43,6 +43,12 @@ query GetTableQuery($tableId: ID!, $viewId: ID) { enabled id } + + widgets { + id + name + item + } } forms { diff --git a/packages/command-handlers/src/handlers/create-view-widget.command-handler.ts b/packages/command-handlers/src/handlers/create-view-widget.command-handler.ts new file mode 100644 index 000000000..ab90474c0 --- /dev/null +++ b/packages/command-handlers/src/handlers/create-view-widget.command-handler.ts @@ -0,0 +1,22 @@ +import { CreateViewWidgetCommand } from "@undb/commands" +import { commandHandler } from "@undb/cqrs" +import { singleton } from "@undb/di" +import type { ICommandHandler } from "@undb/domain" +import { injectTableRepository, TableIdVo, type ITableRepository } from "@undb/table" + +@commandHandler(CreateViewWidgetCommand) +@singleton() +export class CreateViewWidgetCommandHandler implements ICommandHandler { + constructor( + @injectTableRepository() + private readonly repo: ITableRepository, + ) {} + + async execute(command: CreateViewWidgetCommand): Promise { + const table = (await this.repo.findOneById(new TableIdVo(command.tableId))).expect("table not found") + + const spec = table.views.$createWidget(table, command) + + await this.repo.updateOneById(table, spec) + } +} diff --git a/packages/command-handlers/src/handlers/index.ts b/packages/command-handlers/src/handlers/index.ts index 26f456df3..47d63489d 100644 --- a/packages/command-handlers/src/handlers/index.ts +++ b/packages/command-handlers/src/handlers/index.ts @@ -13,6 +13,7 @@ import { CreateTableFieldCommandHandler } from "./create-table-field.command-han import { CreateTableFormCommandHandler } from "./create-table-form.command-handler" import { CreateTableViewCommandHandler } from "./create-table-view.command-handler" import { CreateTableCommandHandler } from "./create-table.command-handler" +import { CreateViewWidgetCommandHandler } from "./create-view-widget.command-handler" import { CreateWebhookCommandHandler } from "./create-webhook.command-handler" import { DeleteBaseCommandHandler } from "./delete-base.command-handler" import { DeleteInvitationCommandHandler } from "./delete-invitation.command-handler" @@ -110,4 +111,5 @@ export const commandHandlers = [ DuplicateTableFormCommandHandler, CreateFromTemplateCommandHandler, SetDefaultViewCommandHandler, + CreateViewWidgetCommandHandler, ] diff --git a/packages/commands/src/create-view-widget.command.ts b/packages/commands/src/create-view-widget.command.ts new file mode 100644 index 000000000..4c1c0e26d --- /dev/null +++ b/packages/commands/src/create-view-widget.command.ts @@ -0,0 +1,22 @@ +import { Command, type CommandProps } from "@undb/domain" +import { createViewWidgetDTO, tableId, type IWidgetDTO } from "@undb/table" +import { z } from "@undb/zod" + +export const createViewWidgetCommand = createViewWidgetDTO.extend({ + tableId: tableId, +}) + +export type ICreateViewWidgetCommand = z.infer + +export class CreateViewWidgetCommand extends Command implements ICreateViewWidgetCommand { + public readonly tableId: string + public readonly viewId: string + public readonly widget: IWidgetDTO + + constructor(props: CommandProps) { + super(props) + this.tableId = props.tableId + this.viewId = props.viewId + this.widget = props.widget + } +} diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 39232467d..42b1a3fc2 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -13,6 +13,7 @@ export * from "./create-table-field.command" export * from "./create-table-form.command" export * from "./create-table-view.command" export * from "./create-table.command" +export * from "./create-view-widget.command" export * from "./create-webhook.command" export * from "./delete-base.command" export * from "./delete-form.command" diff --git a/packages/graphql/src/index.ts b/packages/graphql/src/index.ts index d6e94d9d5..7a556d7a8 100644 --- a/packages/graphql/src/index.ts +++ b/packages/graphql/src/index.ts @@ -148,6 +148,12 @@ export class Graphql { field: String } + type Widget { + id: ID! + name: String! + item: JSON + } + type View { id: ID! name: String! @@ -163,6 +169,7 @@ export class Graphql { grid: GridOption kanban: KanbanOption gallery: GalleryOption + widgets: [Widget] shareId: ID share: Share diff --git a/packages/persistence/src/table/table.filter-visitor.ts b/packages/persistence/src/table/table.filter-visitor.ts index 238437a5f..743d92200 100644 --- a/packages/persistence/src/table/table.filter-visitor.ts +++ b/packages/persistence/src/table/table.filter-visitor.ts @@ -33,6 +33,7 @@ import type { WithViewIdSpecification, WithViewOption, WithViewSort, + WithViewWidgets, } from "@undb/table" import type { ExpressionBuilder } from "kysely" import { AbstractQBVisitor } from "../abstract-qb.visitor" @@ -133,6 +134,9 @@ export class TableFilterVisitor extends AbstractQBVisitor implements IT withViewFields(fields: WithViewFields): void { throw new Error("Method not implemented.") } + withViewWidgets(spec: WithViewWidgets): void { + throw new Error("Method not implemented.") + } withForms(views: TableFormsSpecification): void { throw new Error("Method not implemented.") } diff --git a/packages/persistence/src/table/table.mutation-visitor.ts b/packages/persistence/src/table/table.mutation-visitor.ts index acc42687b..cce57a4f7 100644 --- a/packages/persistence/src/table/table.mutation-visitor.ts +++ b/packages/persistence/src/table/table.mutation-visitor.ts @@ -30,6 +30,7 @@ import type { WithViewIdSpecification, WithViewOption, WithViewSort, + WithViewWidgets, WithoutFieldSpecification, WithoutFormSpecification, WithoutView, @@ -170,6 +171,9 @@ export class TableMutationVisitor extends AbstractQBMutationVisitor implements I this.addSql(sql) } + withViewWidgets(spec: WithViewWidgets): void { + this.setData(tables.views.name, json(this.table.views.toJSON())) + } withoutView(view: WithoutView): void { this.setData(tables.views.name, json(this.table.views.toJSON())) const deleteQuery = this.qb diff --git a/packages/persistence/src/table/table.reference-visitor.ts b/packages/persistence/src/table/table.reference-visitor.ts index e85789a27..0118fdb7b 100644 --- a/packages/persistence/src/table/table.reference-visitor.ts +++ b/packages/persistence/src/table/table.reference-visitor.ts @@ -34,6 +34,7 @@ import type { WithViewIdSpecification, WithViewOption, WithViewSort, + WithViewWidgets, } from "@undb/table" import type { SelectQueryBuilder } from "kysely" import type { Database, Table } from "../db" @@ -80,6 +81,7 @@ export class TableReferenceVisitor implements ITableSpecVisitor { withForm(views: WithFormSpecification): void {} withForeignRollupField(spec: WithForeignRollupFieldSpec): void {} withTableForeignTables(spec: WithTableForeignTablesSpec): void {} + withViewWidgets(spec: WithViewWidgets): void {} withTableUnqueName(spec: TableUniqueNameSpecification): void { this.sqb = this.sqb .innerJoin("undb_base", "undb_table.base_id", "undb_base.id") diff --git a/packages/persistence/src/table/table.repository.ts b/packages/persistence/src/table/table.repository.ts index 6d8a47518..d934a7814 100644 --- a/packages/persistence/src/table/table.repository.ts +++ b/packages/persistence/src/table/table.repository.ts @@ -1,7 +1,7 @@ -import { injectContext, type IContext } from "@undb/context" -import { executionContext, getCurrentSpaceId } from "@undb/context/server" -import { inject, singleton } from "@undb/di" -import { None, Option, Some } from "@undb/domain" +import { injectContext,type IContext } from "@undb/context" +import { executionContext,getCurrentSpaceId } from "@undb/context/server" +import { inject,singleton } from "@undb/di" +import { None,Option,Some } from "@undb/domain" import { TableComositeSpecification, TableIdSpecification, @@ -13,8 +13,8 @@ import { type TableId, } from "@undb/table" import { getCurrentTransaction } from "../ctx" -import type { InsertTable, InsertTableIdMapping } from "../db" -import { json, type IQueryBuilder } from "../qb" +import type { InsertTable,InsertTableIdMapping } from "../db" +import { json,type IQueryBuilder } from "../qb" import { injectQueryBuilder } from "../qb.provider" import { UnderlyingTableService } from "../underlying/underlying-table.service" import { TableDbQuerySpecHandler } from "./table-db.query-spec-handler" diff --git a/packages/persistence/src/tables.ts b/packages/persistence/src/tables.ts index 3c3cad5f3..867499eba 100644 --- a/packages/persistence/src/tables.ts +++ b/packages/persistence/src/tables.ts @@ -53,6 +53,7 @@ export const tables = sqliteTable( views: text("views", { mode: "json" }).notNull(), forms: text("forms", { mode: "json" }), rls: text("rls", { mode: "json" }), + widgets: text("widgets", { mode: "json" }), createdAt: text("created_at") .notNull() diff --git a/packages/persistence/src/underlying/underlying-table-spec.visitor.ts b/packages/persistence/src/underlying/underlying-table-spec.visitor.ts index 6e4f6a792..6a677af1b 100644 --- a/packages/persistence/src/underlying/underlying-table-spec.visitor.ts +++ b/packages/persistence/src/underlying/underlying-table-spec.visitor.ts @@ -7,6 +7,7 @@ import { UPDATED_AT_TYPE, UPDATED_BY_TYPE, WithViewFieldWidth, + WithViewWidgets, type DuplicatedTableSpecification, type ITableSpecVisitor, type SelectField, @@ -200,6 +201,7 @@ export class UnderlyingTableSpecVisitor implements ITableSpecVisitor { } withViewFields(fields: WithViewFields): void {} withViewFieldWidth(spec: WithViewFieldWidth): void {} + withViewWidgets(spec: WithViewWidgets): void {} withForm(views: WithFormSpecification): void {} withForms(views: TableFormsSpecification): void {} withNewForm(views: WithNewFormSpecification): void {} diff --git a/packages/table/src/dto/create-table.dto.ts b/packages/table/src/dto/create-table.dto.ts index b81f2fc8c..f9e5b97ba 100644 --- a/packages/table/src/dto/create-table.dto.ts +++ b/packages/table/src/dto/create-table.dto.ts @@ -1,4 +1,4 @@ -import { baseIdSchema } from "@undb/base" +import { baseIdSchema, baseNameSchema } from "@undb/base" import { spaceIdSchema } from "@undb/space" import { z } from "@undb/zod" import { createFormDTO } from "../modules/forms/dto/create-form.dto" @@ -13,7 +13,8 @@ import { tableName } from "../table-name.vo" export const createTableDTO = z.object({ id: tableId.optional(), name: tableName, - baseId: baseIdSchema, + baseId: baseIdSchema.optional(), + baseName: baseNameSchema.optional(), spaceId: spaceIdSchema, schema: createSchemaDTO, diff --git a/packages/table/src/modules/chart/chart.vo.ts b/packages/table/src/modules/chart/chart.vo.ts new file mode 100644 index 000000000..6a9b17067 --- /dev/null +++ b/packages/table/src/modules/chart/chart.vo.ts @@ -0,0 +1,41 @@ +import { ValueObject } from "@undb/domain" +import { z } from "@undb/zod" +import { fieldId } from "../schema" +import { createConditionGroup } from "../schema/fields/condition/condition.type" + +const chartFilterOption = z.undefined() + +export const chartFilterGroup = createConditionGroup(chartFilterOption, chartFilterOption) + +const countChart = z.object({ + type: z.literal("count"), + config: z.object({ + condition: chartFilterGroup.optional(), + }), +}) + +const pieChart = z.object({ + type: z.literal("pie"), + config: z.object({ + fieldId: fieldId, + aggregateFieldId: fieldId.optional(), + aggregateFunction: z.enum(["count", "sum"]).optional(), + }), +}) + +const barChart = z.object({ + type: z.literal("bar"), + config: z.object({ + xFieldId: fieldId, + yFieldId: fieldId, + aggregateFunction: z.enum(["count", "sum", "average"]).optional(), + groupByFieldId: fieldId.optional(), + stacked: z.boolean().optional(), + }), +}) + +export const chart = z.discriminatedUnion("type", [countChart, pieChart, barChart]) + +export type IChart = z.infer + +export class ChartVO extends ValueObject {} diff --git a/packages/table/src/modules/index.ts b/packages/table/src/modules/index.ts index 30a0aa414..e2d9aaf4c 100644 --- a/packages/table/src/modules/index.ts +++ b/packages/table/src/modules/index.ts @@ -6,3 +6,4 @@ export * from "./schema" export * from "./schema/fields/condition" export * from "./storage" export * from "./views" +export * from "./widgets" diff --git a/packages/table/src/modules/views/dto/create-view.dto.ts b/packages/table/src/modules/views/dto/create-view.dto.ts index 9c4a33682..bd36cc9b6 100644 --- a/packages/table/src/modules/views/dto/create-view.dto.ts +++ b/packages/table/src/modules/views/dto/create-view.dto.ts @@ -1,5 +1,6 @@ import { z } from "@undb/zod" import { tableId } from "../../../table-id.vo" +import { widgetDTO } from "../../widgets/widget.vo" import { viewAggregate, viewColorGroup, viewFields, viewFilterGroup, viewOption, viewSort } from "../view" import { galleryOption } from "../view/variants/gallery-view.vo" import { kanbanOption } from "../view/variants/kanban-view.vo" @@ -18,6 +19,7 @@ export const createBaseViewDTO = z.object({ sort: viewSort.optional(), fields: viewFields.optional(), aggregate: viewAggregate.optional(), + widgets: widgetDTO.array().optional(), }) export const createGridViewDTO = createBaseViewDTO.extend({ diff --git a/packages/table/src/modules/views/view/index.ts b/packages/table/src/modules/views/view/index.ts index 5e40a548d..f60d8831b 100644 --- a/packages/table/src/modules/views/view/index.ts +++ b/packages/table/src/modules/views/view/index.ts @@ -8,4 +8,5 @@ export * from "./view-id.vo" export * from "./view-name.vo" export * from "./view-option.vo" export * from "./view-sort" +export * from "./view-widget" export * from "./view.type" diff --git a/packages/table/src/modules/views/view/variants/abstract-view.vo.ts b/packages/table/src/modules/views/view/variants/abstract-view.vo.ts index a185a5b55..a54ea7eea 100644 --- a/packages/table/src/modules/views/view/variants/abstract-view.vo.ts +++ b/packages/table/src/modules/views/view/variants/abstract-view.vo.ts @@ -1,7 +1,7 @@ import { None, Option, Some, and } from "@undb/domain" import { z } from "@undb/zod" import type { IDuplicateViewDTO, IUpdateViewDTO } from "../../../../dto" -import type { TableComositeSpecification } from "../../../../specifications" +import { type TableComositeSpecification } from "../../../../specifications" import { WithNewView, WithView, @@ -11,11 +11,13 @@ import { WithViewFilter, WithViewOption, WithViewSort, + WithViewWidgets, WithoutView, } from "../../../../specifications/table-view.specification" import { tableId } from "../../../../table-id.vo" import type { TableDo } from "../../../../table.do" import type { Field } from "../../../schema" +import { WidgetVO, widgetDTO, type IWidgetDTO } from "../../../widgets/widget.vo" import type { IViewDTO } from "../dto" import { ViewAggregateVO, viewAggregate, type IViewAggregate } from "../view-aggregate/view-aggregate.vo" import { ViewColor, viewColorGroup, type IRootViewColor } from "../view-color" @@ -52,6 +54,7 @@ export const baseViewDTO = z.object({ sort: viewSort.optional(), aggregate: viewAggregate.optional(), fields: viewFields.optional(), + widgets: widgetDTO.array().optional(), }) export type IBaseViewDTO = z.infer @@ -67,6 +70,7 @@ export abstract class AbstractView { sort: Option = None aggregate: Option = None fields: Option = None + widgets: Option = None abstract type: ViewType @@ -92,6 +96,20 @@ export abstract class AbstractView { if (dto.option) { this.setOption(dto.option) } + if (dto.widgets) { + this.setWidgets(dto.widgets) + } + } + + setWidgets(widgets: IWidgetDTO[]) { + this.widgets = Some(widgets.map((widget) => new WidgetVO(widget))) + } + + $createWidgetSpec(dto: IWidgetDTO): Option { + const widget = new WidgetVO(dto) + const previous = this.widgets.into(null) + const widgets = this.widgets.unwrapOr([]).concat(widget) + return Some(new WithViewWidgets(this.id, Option(previous), widgets)) } get showSystemFields() { @@ -247,6 +265,7 @@ export abstract class AbstractView { sort: this.sort.into(null)?.toJSON(), aggregate: this.aggregate.into(null)?.toJSON(), fields: this.fields.into(null)?.toJSON(), + widgets: this.widgets.into(undefined), } } } diff --git a/packages/table/src/modules/views/view/view-widget/index.ts b/packages/table/src/modules/views/view/view-widget/index.ts new file mode 100644 index 000000000..a97cde9a3 --- /dev/null +++ b/packages/table/src/modules/views/view/view-widget/index.ts @@ -0,0 +1 @@ +export * from "./view-widget.dto" diff --git a/packages/table/src/modules/views/view/view-widget/view-widget.dto.ts b/packages/table/src/modules/views/view/view-widget/view-widget.dto.ts new file mode 100644 index 000000000..bac4368b2 --- /dev/null +++ b/packages/table/src/modules/views/view/view-widget/view-widget.dto.ts @@ -0,0 +1,10 @@ +import { z } from "@undb/zod" +import { widgetDTO } from "../../../widgets/widget.vo" +import { viewId } from "../view-id.vo" + +export const createViewWidgetDTO = z.object({ + viewId: viewId, + widget: widgetDTO, +}) + +export type ICreateViewWidgetDTO = z.infer diff --git a/packages/table/src/modules/views/views.vo.ts b/packages/table/src/modules/views/views.vo.ts index 585339c3e..14abca4bc 100644 --- a/packages/table/src/modules/views/views.vo.ts +++ b/packages/table/src/modules/views/views.vo.ts @@ -1,4 +1,5 @@ import { and, andOptions, None, Option, ValueObject } from "@undb/domain" +import type { ICreateViewWidgetDTO } from ".." import type { ISetDefaultViewDTO } from "../../dto/set-default-view.dto" import { WithView, type TableComositeSpecification } from "../../specifications" import type { TableDo } from "../../table.do" @@ -90,6 +91,17 @@ export class Views extends ValueObject { return andOptions(...specs) } + $createWidget(table: TableDo, dto: ICreateViewWidgetDTO): Option { + const view = this.getViewById(dto.viewId) + const spec = view.$createWidgetSpec(dto.widget) + + if (spec.isSome()) { + spec.unwrap().mutate(table) + } + + return spec + } + $deleteField(table: TableDo, field: Field) { const specs = this.views.map((view) => view.$deleteField(table, field)) diff --git a/packages/table/src/modules/widgets/index.ts b/packages/table/src/modules/widgets/index.ts new file mode 100644 index 000000000..55207e37c --- /dev/null +++ b/packages/table/src/modules/widgets/index.ts @@ -0,0 +1 @@ +export * from "./widget.vo" diff --git a/packages/table/src/modules/widgets/widget-id.vo.ts b/packages/table/src/modules/widgets/widget-id.vo.ts new file mode 100644 index 000000000..c5883ba0e --- /dev/null +++ b/packages/table/src/modules/widgets/widget-id.vo.ts @@ -0,0 +1,12 @@ +import { IdFactory } from "@undb/domain" +import { z } from "@undb/zod" + +const prefix = "wid" +const size = 6 + +export const widgetId = z.string().startsWith(prefix) +export type IWidgetId = z.infer + +export const WidgetIdVo = IdFactory(prefix, size, widgetId) + +export type WidgetId = InstanceType diff --git a/packages/table/src/modules/widgets/widget-name.vo.ts b/packages/table/src/modules/widgets/widget-name.vo.ts new file mode 100644 index 000000000..f06db157c --- /dev/null +++ b/packages/table/src/modules/widgets/widget-name.vo.ts @@ -0,0 +1,12 @@ +import { ValueObject } from "@undb/domain" +import { z } from "@undb/zod" + +export const widgetName = z.string().min(2, { message: "widget name contains at least 2 chars" }) + +export type IWidgetName = z.infer + +export class WidgetNameVo extends ValueObject { + constructor(value: string) { + super({ value }) + } +} diff --git a/packages/table/src/modules/widgets/widget.vo.ts b/packages/table/src/modules/widgets/widget.vo.ts new file mode 100644 index 000000000..1609bef3d --- /dev/null +++ b/packages/table/src/modules/widgets/widget.vo.ts @@ -0,0 +1,61 @@ +import { ValueObject } from "@undb/domain" +import { z } from "@undb/zod" +import { tableId } from "../../table-id.vo" +import { chart } from "../chart/chart.vo" +import { widgetId, WidgetIdVo } from "./widget-id.vo" +import { widgetName } from "./widget-name.vo" + +const widgetItemChart = z.object({ + type: z.literal("chart"), + chart: chart, +}) + +const widgetItemTable = z.object({ + type: z.literal("table"), + tableId: tableId, +}) + +const widgetItem = z.discriminatedUnion("type", [widgetItemChart, widgetItemTable]) + +export const widgetDTO = z.object({ + id: widgetId, + name: widgetName, + item: widgetItem, +}) + +export type IWidgetDTO = z.infer + +export class WidgetVO extends ValueObject { + static default(name = "Count") { + return new WidgetVO({ + id: WidgetIdVo.create().value, + name: widgetName.parse(name), + item: { + type: "chart", + chart: { + type: "count", + config: {}, + }, + }, + }) + } + public get id() { + return this.props.id + } + + public get name() { + return this.props.name + } + + public get item() { + return this.props.item + } + + public toJSON(): IWidgetDTO { + return { + id: this.props.id, + name: this.props.name, + item: this.props.item, + } + } +} diff --git a/packages/table/src/specifications/table-view.specification.ts b/packages/table/src/specifications/table-view.specification.ts index f37a015e5..8f87af1bb 100644 --- a/packages/table/src/specifications/table-view.specification.ts +++ b/packages/table/src/specifications/table-view.specification.ts @@ -5,6 +5,7 @@ import type { IRootViewColor } from "../modules/views/view/view-color" import type { IViewFields } from "../modules/views/view/view-fields" import type { IViewOption } from "../modules/views/view/view-option.vo" import type { IViewSort } from "../modules/views/view/view-sort" +import type { IWidgetDTO } from "../modules/widgets/widget.vo" import type { TableDo } from "../table.do" import type { ITableSpecVisitor } from "./table-visitor.interface" import { TableComositeSpecification } from "./table.composite-specification" @@ -31,6 +32,28 @@ export class WithViewOption extends TableComositeSpecification { } } +export class WithViewWidgets extends TableComositeSpecification { + constructor( + public readonly viewId: ViewId, + public readonly previous: Option, + public readonly widgets: IWidgetDTO[], + ) { + super() + } + isSatisfiedBy(t: TableDo): boolean { + throw new WontImplementException(WithViewWidgets.name + ".isSatisfiedBy") + } + mutate(t: TableDo): Result { + const view = t.views.getViewById(this.viewId.value) + view.setWidgets(this.widgets) + return Ok(t) + } + accept(v: ITableSpecVisitor): Result { + v.withViewWidgets(this) + return Ok(undefined) + } +} + export class WithView extends TableComositeSpecification { constructor( public readonly previous: View, diff --git a/packages/table/src/specifications/table-visitor.interface.ts b/packages/table/src/specifications/table-visitor.interface.ts index a1e64941a..8c6399ebe 100644 --- a/packages/table/src/specifications/table-visitor.interface.ts +++ b/packages/table/src/specifications/table-visitor.interface.ts @@ -32,6 +32,7 @@ import type { WithViewIdSpecification, WithViewOption, WithViewSort, + WithViewWidgets, } from "./table-view.specification" import type { TableViewsSpecification } from "./table-views.specification" import type { DuplicatedTableSpecification } from "./table.specification" @@ -69,4 +70,5 @@ export interface ITableSpecVisitor extends ISpecVisitor { withTableForeignTables(spec: WithTableForeignTablesSpec): void withTableUnqueName(spec: TableUniqueNameSpecification): void withViewFieldWidth(spec: WithViewFieldWidth): void + withViewWidgets(spec: WithViewWidgets): void } diff --git a/packages/trpc/src/router.ts b/packages/trpc/src/router.ts index 4bb7cd8bd..8e498cd54 100644 --- a/packages/trpc/src/router.ts +++ b/packages/trpc/src/router.ts @@ -13,6 +13,7 @@ import { CreateTableFieldCommand, CreateTableFormCommand, CreateTableViewCommand, + CreateViewWidgetCommand, CreateWebhookCommand, DeleteBaseCommand, DeleteFormCommand, @@ -70,6 +71,7 @@ import { createTableFormCommandOutput, createTableViewCommand, createTableViewCommandOutput, + createViewWidgetCommand, createWebhookCommand, deleteBaseCommand, deleteFormCommand, @@ -112,9 +114,9 @@ import { updateaccountCommand, } from "@undb/commands" import { getCurrentSpaceId } from "@undb/context/server" -import { CommandBus, QueryBus } from "@undb/cqrs" +import { CommandBus,QueryBus } from "@undb/cqrs" import { container } from "@undb/di" -import type { ICommandBus, IQueryBus } from "@undb/domain" +import type { ICommandBus,IQueryBus } from "@undb/domain" import { CountRecordsQuery, GetAggregatesQuery, @@ -148,7 +150,7 @@ import { import { tableDTO } from "@undb/table" import { z } from "@undb/zod" import { authz } from "./authz.middleware" -import { privateProcedure, publicProcedure, t } from "./trpc" +import { privateProcedure,publicProcedure,t } from "./trpc" const commandBus = container.resolve(CommandBus) const queryBus = container.resolve(QueryBus) @@ -177,6 +179,13 @@ const formRouter = t.router({ .mutation(({ input }) => commandBus.execute(new SubmitFormCommand(input))), }) +const viewWidgetRouter = t.router({ + create: privateProcedure + .use(authz("view:update")) + .input(createViewWidgetCommand) + .mutation(({ input }) => commandBus.execute(new CreateViewWidgetCommand(input))), +}) + const viewRouter = t.router({ create: privateProcedure .use(authz("view:create")) @@ -228,6 +237,7 @@ const viewRouter = t.router({ .use(authz("view:update")) .input(setDefaultViewCommand) .mutation(({ input }) => commandBus.execute(new SetDefaultViewCommand(input))), + widget: viewWidgetRouter, }) const rlsRouter = t.router({ From 3b818fc06458a7d034dee666cde1d28e57b3fdc5 Mon Sep 17 00:00:00 2001 From: nichenqin Date: Thu, 10 Oct 2024 17:01:34 +0800 Subject: [PATCH 2/2] feat: chart --- .../blocks/aggregate/aggregate.svelte | 13 ++++++ .../blocks/aggregate/count-aggregate.svelte | 20 +++++++++ .../lib/components/blocks/chart/chart.svelte | 2 - .../blocks/chart/count-chart.svelte | 10 ----- .../create-reference-field-optioin.svelte | 2 +- .../field-options/rollup-field-option.svelte | 2 +- .../update-reference-field-optioin.svelte | 2 +- .../grid-view/grid-view-data-table.svelte | 2 +- .../foreign-records-picker-dropdown.svelte | 11 ++--- .../blocks/relations/relations.svelte | 2 +- .../blocks/table-tools/table-tools.svelte | 2 +- .../view-widget/view-widget-sheet.svelte | 4 +- .../components/blocks/widget/widget.svelte | 6 +-- apps/frontend/src/lib/store/table.store.ts | 4 +- .../(space)/t/[tableId]/+layout.svelte | 21 +++++----- .../(authed)/(space)/t/[tableId]/+layout.ts | 1 + .../s/b/[shareId]/t/[tableId]/+layout.svelte | 2 +- .../(share)/s/f/[shareId]/+layout.svelte | 2 +- .../(share)/s/v/[shareId]/+layout.svelte | 2 +- .../src/record/record.query-repository.ts | 24 +++++++---- .../src/methods/duplicate-table.method.ts | 2 +- .../src/modules/aggregate/aggregate.vo.ts | 41 +++++++++++++++++++ packages/table/src/modules/chart/chart.vo.ts | 14 +------ .../records/record/record.repository.ts | 9 +++- .../services/methods/get-aggregates.method.ts | 6 +-- .../variants/id-field/id-field.aggregate.ts | 3 ++ .../fields/variants/id-field/id-field.vo.ts | 3 +- .../table/src/modules/widgets/widget.vo.ts | 12 ++++-- packages/table/src/services/table.service.ts | 2 +- packages/table/src/table.factory.ts | 8 ++-- packages/template/src/template.factory.ts | 2 +- packages/template/src/templates/hr.base.json | 2 +- 32 files changed, 156 insertions(+), 82 deletions(-) create mode 100644 apps/frontend/src/lib/components/blocks/aggregate/aggregate.svelte create mode 100644 apps/frontend/src/lib/components/blocks/aggregate/count-aggregate.svelte delete mode 100644 apps/frontend/src/lib/components/blocks/chart/count-chart.svelte create mode 100644 packages/table/src/modules/aggregate/aggregate.vo.ts create mode 100644 packages/table/src/modules/schema/fields/variants/id-field/id-field.aggregate.ts diff --git a/apps/frontend/src/lib/components/blocks/aggregate/aggregate.svelte b/apps/frontend/src/lib/components/blocks/aggregate/aggregate.svelte new file mode 100644 index 000000000..d189edfa8 --- /dev/null +++ b/apps/frontend/src/lib/components/blocks/aggregate/aggregate.svelte @@ -0,0 +1,13 @@ + + + diff --git a/apps/frontend/src/lib/components/blocks/aggregate/count-aggregate.svelte b/apps/frontend/src/lib/components/blocks/aggregate/count-aggregate.svelte new file mode 100644 index 000000000..33008f174 --- /dev/null +++ b/apps/frontend/src/lib/components/blocks/aggregate/count-aggregate.svelte @@ -0,0 +1,20 @@ + + + +
+ 50 +
+
diff --git a/apps/frontend/src/lib/components/blocks/chart/chart.svelte b/apps/frontend/src/lib/components/blocks/chart/chart.svelte index 26b199835..3cbb610a5 100644 --- a/apps/frontend/src/lib/components/blocks/chart/chart.svelte +++ b/apps/frontend/src/lib/components/blocks/chart/chart.svelte @@ -1,12 +1,10 @@ diff --git a/apps/frontend/src/lib/components/blocks/chart/count-chart.svelte b/apps/frontend/src/lib/components/blocks/chart/count-chart.svelte deleted file mode 100644 index 8f6ae83f6..000000000 --- a/apps/frontend/src/lib/components/blocks/chart/count-chart.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - -
- 50 -
-
diff --git a/apps/frontend/src/lib/components/blocks/field-options/create-reference-field-optioin.svelte b/apps/frontend/src/lib/components/blocks/field-options/create-reference-field-optioin.svelte index 59e312ee2..77cdd585c 100644 --- a/apps/frontend/src/lib/components/blocks/field-options/create-reference-field-optioin.svelte +++ b/apps/frontend/src/lib/components/blocks/field-options/create-reference-field-optioin.svelte @@ -43,7 +43,7 @@ $: ft = $getForeignTableStore.data?.table // @ts-ignore - $: foreignTable = ft ? TableFactory.fromJSON(ft) : undefined + $: foreignTable = ft ? new TableFactory().fromJSON(ft) : undefined const value = writable | undefined>() $: if ($value && foreignTable) { diff --git a/apps/frontend/src/lib/components/blocks/field-options/rollup-field-option.svelte b/apps/frontend/src/lib/components/blocks/field-options/rollup-field-option.svelte index b48206e40..c97f75a44 100644 --- a/apps/frontend/src/lib/components/blocks/field-options/rollup-field-option.svelte +++ b/apps/frontend/src/lib/components/blocks/field-options/rollup-field-option.svelte @@ -34,7 +34,7 @@ $: foreignTable = $store.data?.table const foreignTableDo = writable() - $: if (foreignTable) foreignTableDo.set(TableFactory.fromJSON(foreignTable)) + $: if (foreignTable) foreignTableDo.set(new TableFactory().fromJSON(foreignTable)) $: schema = foreignTable?.schema $: fields = schema?.filter((f) => getIsFieldCanBeRollup(f.type)) diff --git a/apps/frontend/src/lib/components/blocks/field-options/update-reference-field-optioin.svelte b/apps/frontend/src/lib/components/blocks/field-options/update-reference-field-optioin.svelte index 5e0f6b7f1..c35f6cf95 100644 --- a/apps/frontend/src/lib/components/blocks/field-options/update-reference-field-optioin.svelte +++ b/apps/frontend/src/lib/components/blocks/field-options/update-reference-field-optioin.svelte @@ -43,7 +43,7 @@ $: ft = $getForeignTableStore.data?.table // @ts-ignore - $: foreignTable = ft ? TableFactory.fromJSON(ft) : undefined + $: foreignTable = ft ? new TableFactory().fromJSON(ft) : undefined const value = writable | undefined>() onMount(() => { diff --git a/apps/frontend/src/lib/components/blocks/grid-view/grid-view-data-table.svelte b/apps/frontend/src/lib/components/blocks/grid-view/grid-view-data-table.svelte index e9767214f..feeeb0423 100644 --- a/apps/frontend/src/lib/components/blocks/grid-view/grid-view-data-table.svelte +++ b/apps/frontend/src/lib/components/blocks/grid-view/grid-view-data-table.svelte @@ -45,7 +45,7 @@ const t = getTable() - let fields = derived(t, ($t) => $t?.getOrderedVisibleFields($viewId) ?? ([] as Field[])) + let fields = derived([t, viewId], ([$t, $viewId]) => $t?.getOrderedVisibleFields($viewId) ?? ([] as Field[])) $: perPage = $preferences.gridViewPerPage ?? 50 diff --git a/apps/frontend/src/lib/components/blocks/reference/foreign-records-picker-dropdown.svelte b/apps/frontend/src/lib/components/blocks/reference/foreign-records-picker-dropdown.svelte index b944c3594..77fb8dab0 100644 --- a/apps/frontend/src/lib/components/blocks/reference/foreign-records-picker-dropdown.svelte +++ b/apps/frontend/src/lib/components/blocks/reference/foreign-records-picker-dropdown.svelte @@ -2,7 +2,7 @@ import { GetForeignTableStore } from "$houdini" import * as Popover from "$lib/components/ui/popover" import ForeignRecordsPicker from "./foreign-records-picker.svelte" - import { readable, writable } from "svelte/store" + import { derived, readable, writable } from "svelte/store" import { ReferenceField, TableFactory } from "@undb/table" import Button from "$lib/components/ui/button/button.svelte" import { LoaderCircleIcon } from "lucide-svelte" @@ -28,9 +28,10 @@ $: if (open) foreignTableStore.fetch({ variables: { tableId: foreignTableId } }) - $: table = $foreignTableStore.data?.table - - $: foreignTable = table ? readable(TableFactory.fromJSON(table)) : null + const foreignTable = derived(foreignTableStore, ($foreignTableStore) => { + const table = $foreignTableStore.data?.table + return table ? new TableFactory().fromJSON(table) : null + }) @@ -46,7 +47,7 @@ {/if} - {#if foreignTable} + {#if $foreignTable} TableFactory.fromJSON(table)) + $: foreignTables = foreignTablesDTO.map((table) => new TableFactory().fromJSON(table)) diff --git a/apps/frontend/src/lib/components/blocks/table-tools/table-tools.svelte b/apps/frontend/src/lib/components/blocks/table-tools/table-tools.svelte index 8b7970d14..aab332c2d 100644 --- a/apps/frontend/src/lib/components/blocks/table-tools/table-tools.svelte +++ b/apps/frontend/src/lib/components/blocks/table-tools/table-tools.svelte @@ -34,6 +34,6 @@ {/if} - + diff --git a/apps/frontend/src/lib/components/blocks/view-widget/view-widget-sheet.svelte b/apps/frontend/src/lib/components/blocks/view-widget/view-widget-sheet.svelte index 360ecb502..0708d4cc6 100644 --- a/apps/frontend/src/lib/components/blocks/view-widget/view-widget-sheet.svelte +++ b/apps/frontend/src/lib/components/blocks/view-widget/view-widget-sheet.svelte @@ -15,7 +15,7 @@ export let viewId: Readable const view = derived([viewId], ([$viewId]) => $table?.views.getViewById($viewId)) - const widgets = derived([view], ([$view]) => $view?.widgets ?? []) + const widgets = derived([view], ([$view]) => $view?.widgets.unwrapOr([]) ?? []) const createViewWidgetMutation = createMutation({ mutationFn: trpc.table.view.widget.create.mutate, @@ -36,7 +36,7 @@ {/each} - diff --git a/apps/frontend/src/lib/components/blocks/widget/widget.svelte b/apps/frontend/src/lib/components/blocks/widget/widget.svelte index b7e86a3f0..bf3f288fa 100644 --- a/apps/frontend/src/lib/components/blocks/widget/widget.svelte +++ b/apps/frontend/src/lib/components/blocks/widget/widget.svelte @@ -1,12 +1,12 @@
- {#if widget.item.type === "chart"} - + {#if widget.item.type === "aggregate"} + {/if}
diff --git a/apps/frontend/src/lib/store/table.store.ts b/apps/frontend/src/lib/store/table.store.ts index cbb3a64e2..c680e22b6 100644 --- a/apps/frontend/src/lib/store/table.store.ts +++ b/apps/frontend/src/lib/store/table.store.ts @@ -1,7 +1,7 @@ import { page } from "$app/stores" import type { TableDo } from "@undb/table" -import { getContext, setContext } from "svelte" -import { derived, type Writable } from "svelte/store" +import { getContext,setContext } from "svelte" +import { derived,type Writable } from "svelte/store" export function setTable(table: Writable) { setContext("table", table) diff --git a/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.svelte b/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.svelte index 9b89625bc..d19ca1059 100644 --- a/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.svelte +++ b/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.svelte @@ -3,38 +3,37 @@ import { TableFactory, TableDo } from "@undb/table" import { setTable } from "$lib/store/table.store" import type { LayoutData } from "./$types" - import { writable } from "svelte/store" + import { derived, writable } from "svelte/store" import { shareStore } from "$lib/store/share.store" import { aggregatesStore } from "$lib/store/aggregates.store" export let data: LayoutData - $: tableStore = data.tableStore + let tableStore = data.tableStore - $: fetching = $tableStore.fetching - $: tableDTO = $tableStore.data?.table + let tableDTO = derived(tableStore, ($tableStore) => $tableStore.data?.table) const table = writable() - $: { - if (!fetching && tableDTO && $page.params.tableId === tableDTO.id) { - table.set(TableFactory.fromJSON(tableDTO)) + $: if ($page.params.tableId === $tableDTO?.id) { + if ($tableDTO) { + table.set(new TableFactory().fromJSON($tableDTO)) setTable(table) } } - $: if (tableDTO) { - for (const view of tableDTO.views) { + $: if ($tableDTO) { + for (const view of $tableDTO.views) { if (view.share) { shareStore.set(view.id, { ...view.share, target: { type: "view", id: view.id } }) } } - for (const form of tableDTO.forms ?? []) { + for (const form of $tableDTO.forms ?? []) { if (form?.share) { shareStore.set(form.id, { ...form.share, target: { type: "form", id: form.id } }) } } - aggregatesStore.updateTableAggregates(tableDTO.id, tableDTO.viewData?.aggregate ?? {}) + aggregatesStore.updateTableAggregates($tableDTO.id, $tableDTO.viewData?.aggregate ?? {}) } diff --git a/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.ts b/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.ts index 2887d54ac..d4f5eab89 100644 --- a/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.ts +++ b/apps/frontend/src/routes/(authed)/(space)/t/[tableId]/+layout.ts @@ -18,5 +18,6 @@ export const load: LayoutLoad = async (event) => { return { tableStore: store, + table: data.data?.table, } } diff --git a/apps/frontend/src/routes/(share)/s/b/[shareId]/t/[tableId]/+layout.svelte b/apps/frontend/src/routes/(share)/s/b/[shareId]/t/[tableId]/+layout.svelte index aa4cc5ea6..887da36d6 100644 --- a/apps/frontend/src/routes/(share)/s/b/[shareId]/t/[tableId]/+layout.svelte +++ b/apps/frontend/src/routes/(share)/s/b/[shareId]/t/[tableId]/+layout.svelte @@ -18,7 +18,7 @@ $: { if (!fetching && tableDTO) { // @ts-ignore - table.set(TableFactory.fromJSON(tableDTO)) + table.set(new TableFactory().fromJSON(tableDTO)) setTable(table) } } diff --git a/apps/frontend/src/routes/(share)/s/f/[shareId]/+layout.svelte b/apps/frontend/src/routes/(share)/s/f/[shareId]/+layout.svelte index 6aff79cc0..d8d2801cd 100644 --- a/apps/frontend/src/routes/(share)/s/f/[shareId]/+layout.svelte +++ b/apps/frontend/src/routes/(share)/s/f/[shareId]/+layout.svelte @@ -16,7 +16,7 @@ const table = writable() $: { if (!fetching && tableDTO) { - table.set(TableFactory.fromJSON(tableDTO)) + table.set(new TableFactory().fromJSON(tableDTO)) setTable(table) } } diff --git a/apps/frontend/src/routes/(share)/s/v/[shareId]/+layout.svelte b/apps/frontend/src/routes/(share)/s/v/[shareId]/+layout.svelte index 580171790..93a35ee8b 100644 --- a/apps/frontend/src/routes/(share)/s/v/[shareId]/+layout.svelte +++ b/apps/frontend/src/routes/(share)/s/v/[shareId]/+layout.svelte @@ -18,7 +18,7 @@ const table = writable() $: { if (!fetching && tableDTO) { - table.set(TableFactory.fromJSON(tableDTO)) + table.set(new TableFactory().fromJSON(tableDTO)) setTable(table) } } diff --git a/packages/persistence/src/record/record.query-repository.ts b/packages/persistence/src/record/record.query-repository.ts index 512c360a5..ee8d959bd 100644 --- a/packages/persistence/src/record/record.query-repository.ts +++ b/packages/persistence/src/record/record.query-repository.ts @@ -12,6 +12,7 @@ import { type IRecordDTO, type IRecordQueryRepository, type ITableRepository, + type IViewAggregate, type IViewSort, type QueryArgs, type RecordId, @@ -129,6 +130,7 @@ export class RecordQueryRepository implements IRecordQueryRepository { async aggregate( table: TableDo, viewId: Option, + aggregate: Option, query: Option, ): Promise> { const context = executionContext.getStore() @@ -136,13 +138,19 @@ export class RecordQueryRepository implements IRecordQueryRepository { const t = new UnderlyingTable(table) const view = table.views.getViewById(viewId.into(undefined)?.value) - const aggregates = view.aggregate - if (aggregates.isNone()) { - return {} + if (aggregate.isNone()) { + const aggregates = view.aggregate + if (aggregates.isNone()) { + return {} + } + const aggs = aggregates.unwrap() + if (aggs.isEmpty()) { + return {} + } + + aggregate = Some(aggs.toJSON()) } - - const aggs = aggregates.unwrap() - if (aggs.isEmpty()) { + if (aggregate.isNone()) { return {} } @@ -168,7 +176,7 @@ export class RecordQueryRepository implements IRecordQueryRepository { .select((eb) => { const ebs: AliasedExpression[] = [] - for (const [fieldId, fieldAggregate] of aggs) { + for (const [fieldId, fieldAggregate] of Object.entries(aggregate.unwrap())) { if (!fieldAggregate) { continue } @@ -185,7 +193,7 @@ export class RecordQueryRepository implements IRecordQueryRepository { .where((eb) => { const ebs: Expression[] = [] - for (const [fieldId, fieldAggregate] of aggs) { + for (const [fieldId, fieldAggregate] of Object.entries(aggregate.unwrap())) { if (!fieldAggregate) { continue } diff --git a/packages/table/src/methods/duplicate-table.method.ts b/packages/table/src/methods/duplicate-table.method.ts index 5f714fce2..9d1e0c2a3 100644 --- a/packages/table/src/methods/duplicate-table.method.ts +++ b/packages/table/src/methods/duplicate-table.method.ts @@ -10,7 +10,7 @@ export function duplicateTableMethod( dto: IDuplicateTableDTO, tableNames: string[], ): DuplicatedTableSpecification { - const duplicated = TableFactory.fromJSON({ + const duplicated = new TableFactory().fromJSON({ ...this.toJSON(), id: dto.tableId ?? TableIdVo.create().value, baseId: dto.baseId ?? this.baseId, diff --git a/packages/table/src/modules/aggregate/aggregate.vo.ts b/packages/table/src/modules/aggregate/aggregate.vo.ts new file mode 100644 index 000000000..c17ed3053 --- /dev/null +++ b/packages/table/src/modules/aggregate/aggregate.vo.ts @@ -0,0 +1,41 @@ +import { ValueObject } from "@undb/domain" +import { z } from "@undb/zod" +import { createConditionGroup } from "../schema/fields/condition/condition.type" + +const aggregateCondition = z.undefined() +const aggregateConditionGroup = createConditionGroup(aggregateCondition, aggregateCondition) + +export const countAggregate = z.object({ + type: z.literal("count"), + config: z.object({ + condition: aggregateConditionGroup.optional(), + }), +}) + +export const sumAggregate = z.object({ + type: z.literal("sum"), +}) + +export const avgAggregate = z.object({ + type: z.literal("avg"), +}) + +export const maxAggregate = z.object({ + type: z.literal("max"), +}) + +export const minAggregate = z.object({ + type: z.literal("min"), +}) + +export const aggregate = z.discriminatedUnion("type", [ + countAggregate, + sumAggregate, + avgAggregate, + maxAggregate, + minAggregate, +]) + +export type IAggregate = z.infer + +export class AggregateVO extends ValueObject {} diff --git a/packages/table/src/modules/chart/chart.vo.ts b/packages/table/src/modules/chart/chart.vo.ts index 6a9b17067..9bd2facb4 100644 --- a/packages/table/src/modules/chart/chart.vo.ts +++ b/packages/table/src/modules/chart/chart.vo.ts @@ -1,18 +1,6 @@ import { ValueObject } from "@undb/domain" import { z } from "@undb/zod" import { fieldId } from "../schema" -import { createConditionGroup } from "../schema/fields/condition/condition.type" - -const chartFilterOption = z.undefined() - -export const chartFilterGroup = createConditionGroup(chartFilterOption, chartFilterOption) - -const countChart = z.object({ - type: z.literal("count"), - config: z.object({ - condition: chartFilterGroup.optional(), - }), -}) const pieChart = z.object({ type: z.literal("pie"), @@ -34,7 +22,7 @@ const barChart = z.object({ }), }) -export const chart = z.discriminatedUnion("type", [countChart, pieChart, barChart]) +export const chart = z.discriminatedUnion("type", [pieChart, barChart]) export type IChart = z.infer diff --git a/packages/table/src/modules/records/record/record.repository.ts b/packages/table/src/modules/records/record/record.repository.ts index 62d46021d..bdbb94768 100644 --- a/packages/table/src/modules/records/record/record.repository.ts +++ b/packages/table/src/modules/records/record/record.repository.ts @@ -2,7 +2,7 @@ import { None, Some, andOptions, type IPagination, type Option, type PaginatedDT import type { TableId } from "../../../table-id.vo" import type { TableDo } from "../../../table.do" import { getSpec } from "../../schema/fields/condition" -import type { View, ViewId } from "../../views" +import type { IViewAggregate, View, ViewId } from "../../views" import type { AggregateResult, ICountRecordsDTO, IGetRecordsDTO } from "../dto" import { withQ } from "../specification/with-q.specification" import type { IRecordDTO } from "./dto" @@ -51,7 +51,12 @@ export interface IRecordQueryRepository { count(tableId: TableId): Promise countWhere(table: TableDo, spec: Option): Promise - aggregate(table: TableDo, viewId: Option, query: Option): Promise> + aggregate( + table: TableDo, + viewId: Option, + aggregate: Option, + query: Option, + ): Promise> } export function buildQuery(table: TableDo, dto: IGetRecordsDTO): Option { diff --git a/packages/table/src/modules/records/services/methods/get-aggregates.method.ts b/packages/table/src/modules/records/services/methods/get-aggregates.method.ts index 16bd9e1f5..c8f8d121a 100644 --- a/packages/table/src/modules/records/services/methods/get-aggregates.method.ts +++ b/packages/table/src/modules/records/services/methods/get-aggregates.method.ts @@ -1,7 +1,7 @@ -import { None, Some } from "@undb/domain" +import { None,Some } from "@undb/domain" import { TableIdVo } from "../../../../table-id.vo" import { ViewIdVo } from "../../../views" -import type { AggregateResult, IGetAggregatesDTO } from "../../dto" +import type { AggregateResult,IGetAggregatesDTO } from "../../dto" import type { RecordsQueryService } from "../records.query-service" export async function getAggregates( @@ -12,5 +12,5 @@ export async function getAggregates( const table = (await this.tableRepository.findOneById(tableId)).expect("Table not found") const viewId = dto.viewId ? Some(new ViewIdVo(dto.viewId)) : None - return this.repo.aggregate(table, viewId, None) + return this.repo.aggregate(table, viewId, None, None) } diff --git a/packages/table/src/modules/schema/fields/variants/id-field/id-field.aggregate.ts b/packages/table/src/modules/schema/fields/variants/id-field/id-field.aggregate.ts new file mode 100644 index 000000000..99032973d --- /dev/null +++ b/packages/table/src/modules/schema/fields/variants/id-field/id-field.aggregate.ts @@ -0,0 +1,3 @@ +import { z } from "@undb/zod" + +export const idFieldAggregate = z.enum(["count"]) diff --git a/packages/table/src/modules/schema/fields/variants/id-field/id-field.vo.ts b/packages/table/src/modules/schema/fields/variants/id-field/id-field.vo.ts index 84344152c..36bcc8915 100644 --- a/packages/table/src/modules/schema/fields/variants/id-field/id-field.vo.ts +++ b/packages/table/src/modules/schema/fields/variants/id-field/id-field.vo.ts @@ -7,6 +7,7 @@ import type { IFieldVisitor } from "../../field.visitor" import { AbstractField, baseFieldDTO, createBaseFieldDTO } from "../abstract-field.vo" import { IdEqual, IdIn } from "./id-field-value.specification" import { IdFieldValue } from "./id-field-value.vo" +import { idFieldAggregate } from "./id-field.aggregate" import { createIdFieldCondition, type IIdFieldCondition, type IIdFieldConditionSchema } from "./id-field.condition" export const ID_TYPE = "id" as const @@ -69,6 +70,6 @@ export class IdField extends AbstractField { } override get aggregate() { - return z.undefined() + return idFieldAggregate } } diff --git a/packages/table/src/modules/widgets/widget.vo.ts b/packages/table/src/modules/widgets/widget.vo.ts index 1609bef3d..4e8d94542 100644 --- a/packages/table/src/modules/widgets/widget.vo.ts +++ b/packages/table/src/modules/widgets/widget.vo.ts @@ -1,10 +1,16 @@ import { ValueObject } from "@undb/domain" import { z } from "@undb/zod" import { tableId } from "../../table-id.vo" +import { aggregate } from "../aggregate/aggregate.vo" import { chart } from "../chart/chart.vo" import { widgetId, WidgetIdVo } from "./widget-id.vo" import { widgetName } from "./widget-name.vo" +const widgetItemAggregate = z.object({ + type: z.literal("aggregate"), + aggregate: aggregate, +}) + const widgetItemChart = z.object({ type: z.literal("chart"), chart: chart, @@ -15,7 +21,7 @@ const widgetItemTable = z.object({ tableId: tableId, }) -const widgetItem = z.discriminatedUnion("type", [widgetItemChart, widgetItemTable]) +const widgetItem = z.discriminatedUnion("type", [widgetItemAggregate, widgetItemChart, widgetItemTable]) export const widgetDTO = z.object({ id: widgetId, @@ -31,8 +37,8 @@ export class WidgetVO extends ValueObject { id: WidgetIdVo.create().value, name: widgetName.parse(name), item: { - type: "chart", - chart: { + type: "aggregate", + aggregate: { type: "count", config: {}, }, diff --git a/packages/table/src/services/table.service.ts b/packages/table/src/services/table.service.ts index f533b88e6..0f936453b 100644 --- a/packages/table/src/services/table.service.ts +++ b/packages/table/src/services/table.service.ts @@ -73,7 +73,7 @@ export class TableService implements ITableService { readonly logger = createLogger(TableService.name) public get creator() { - return TableFactory + return new TableFactory() } constructor( diff --git a/packages/table/src/table.factory.ts b/packages/table/src/table.factory.ts index bb2a88a2e..aa779bf86 100644 --- a/packages/table/src/table.factory.ts +++ b/packages/table/src/table.factory.ts @@ -49,11 +49,11 @@ class TablesIdsMap { } export class TableFactory { - private static get builder() { + private get builder() { return new TableBuilder() } - static create(dto: ICreateTableDTO): TableDo { + create(dto: ICreateTableDTO): TableDo { dto = createTableDTO.parse(dto) const table = this.builder .setId(dto.id) @@ -73,7 +73,7 @@ export class TableFactory { } // create many table inside a base - static createMany( + createMany( baseNames: string[], base: Base, dtos: ICreateTablesDTO[], @@ -174,7 +174,7 @@ export class TableFactory { }) } - static fromJSON(dto: ITableDTO): TableDo { + fromJSON(dto: ITableDTO): TableDo { return this.builder .setId(dto.id) .setBaseId(dto.baseId) diff --git a/packages/template/src/template.factory.ts b/packages/template/src/template.factory.ts index 50b2642b9..983597f1b 100644 --- a/packages/template/src/template.factory.ts +++ b/packages/template/src/template.factory.ts @@ -58,7 +58,7 @@ export class TemplateFactory { } }) as ICreateTablesDTO[] - const tables = TableFactory.createMany(baseNames, base, dtos) + const tables = new TableFactory().createMany(baseNames, base, dtos) result.push({ base, tables }) } diff --git a/packages/template/src/templates/hr.base.json b/packages/template/src/templates/hr.base.json index 4592bee12..e0629c60e 100644 --- a/packages/template/src/templates/hr.base.json +++ b/packages/template/src/templates/hr.base.json @@ -82,7 +82,7 @@ } }, "Department Name": { - "id": "department_name", + "id": "employee_department_name", "type": "rollup", "option": { "referenceFieldId": "department",