From be3f2959b2d6aca9fb3e866d14c0d17f7d34470d Mon Sep 17 00:00:00 2001 From: DarkPhoenix2704 Date: Tue, 20 Feb 2024 07:15:40 +0000 Subject: [PATCH 001/284] feat(nocodb): update swagger and create migration files for calendar view --- .../meta/migrations/XcMigrationSourcev2.ts | 4 + .../migrations/v2/nc_041_calander_view.ts | 64 +++++ packages/nocodb/src/models/CalanderView.ts | 0 packages/nocodb/src/schema/swagger.json | 250 ++++++++++++++++++ packages/nocodb/src/utils/globals.ts | 3 + 5 files changed, 321 insertions(+) create mode 100644 packages/nocodb/src/meta/migrations/v2/nc_041_calander_view.ts create mode 100644 packages/nocodb/src/models/CalanderView.ts diff --git a/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts b/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts index 6b950151c7..75f2ae3965 100644 --- a/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts +++ b/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts @@ -27,6 +27,7 @@ import * as nc_037_rename_project_and_base from '~/meta/migrations/v2/nc_037_ren import * as nc_038_formula_parsed_tree_column from '~/meta/migrations/v2/nc_038_formula_parsed_tree_column'; import * as nc_039_sqlite_alter_column_types from '~/meta/migrations/v2/nc_039_sqlite_alter_column_types'; import * as nc_040_form_view_alter_column_types from '~/meta/migrations/v2/nc_040_form_view_alter_column_types'; +import * as nc_041_calander_view from '~/meta/migrations/v2/nc_041_calander_view'; // Create a custom migration source class export default class XcMigrationSourcev2 { @@ -65,6 +66,7 @@ export default class XcMigrationSourcev2 { 'nc_038_formula_parsed_tree_column', 'nc_039_sqlite_alter_column_types', 'nc_040_form_view_alter_column_types', + 'nc_041_calander_view', ]); } @@ -132,6 +134,8 @@ export default class XcMigrationSourcev2 { return nc_039_sqlite_alter_column_types; case 'nc_040_form_view_alter_column_types': return nc_040_form_view_alter_column_types; + case 'nc_041_calander_view': + return nc_041_calander_view; } } } diff --git a/packages/nocodb/src/meta/migrations/v2/nc_041_calander_view.ts b/packages/nocodb/src/meta/migrations/v2/nc_041_calander_view.ts new file mode 100644 index 0000000000..679246f174 --- /dev/null +++ b/packages/nocodb/src/meta/migrations/v2/nc_041_calander_view.ts @@ -0,0 +1,64 @@ +import type { Knex } from 'knex'; +import { MetaTable } from '~/utils/globals'; + +const up = async (knex: Knex) => { + await knex.schema.createTable(MetaTable.CALANDER_VIEW, (table) => { + table.string('fk_view_id', 20).primary(); + + table.string('base_id', 20); + + table.string('source_id', 128); + + table.string('title'); + + table.string('fk_cover_image_col_id', 20); + + table.text('meta'); + + table.dateTime('created_at'); + table.dateTime('updated_at'); + }); + + await knex.schema.createTable(MetaTable.CALANDER_VIEW_COLUMNS, (table) => { + table.string('id', 20).primary().notNullable(); + + table.string('base_id', 20); + table.string('source_id', 128); + + table.string('fk_view_id', 20); + + table.string('fk_column_id', 20); + + table.boolean('show'); + + table.boolean('bold'); + + table.boolean('underline'); + + table.boolean('italic'); + + table.float('order'); + + table.timestamps(true, true); + }); + + await knex.schema.createTable(MetaTable.CALANDER_VIEW_RANGE, (table) => { + table.string('id', 20).primary().notNullable(); + + table.string('fk_view_id', 20); + + table.string('fk_to_column_id', 20); + + table.string('fk_from_column_id', 20); + + table.timestamps(true, true); + }); +}; + +const down = async (knex: Knex) => { + await knex.schema.dropTable(MetaTable.CALANDER_VIEW); + await knex.schema.dropTable(MetaTable.CALANDER_VIEW_COLUMNS); + await knex.schema.dropTable(MetaTable.CALANDER_VIEW_RANGE); +}; + +export { up, down }; diff --git a/packages/nocodb/src/models/CalanderView.ts b/packages/nocodb/src/models/CalanderView.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/nocodb/src/schema/swagger.json b/packages/nocodb/src/schema/swagger.json index ecccd9c63d..ed359ec8b2 100644 --- a/packages/nocodb/src/schema/swagger.json +++ b/packages/nocodb/src/schema/swagger.json @@ -20071,6 +20071,256 @@ "id": "9zirjgj9k1gqa" } }, + "Calendar": { + "description": "Model for Calendar", + "examples": [ + { + "id": "vw_wqs4zheuo5lgdy", + "fk_view_id": "vw_wqs4zheuo5lgdy", + "fk_cover_image_col_id": null, + "columns": [ + { + "id": "kvc_2skkg5mi1eb37f", + "fk_column_id": "cl_hzos4ghyncqi4k", + "fk_view_id": "vw_wqs4zheuo5lgdy", + "source_id": "ds_hd4ojj0xpquaam", + "base_id": "p_kzfl5lb0t3tcok", + "title": "string", + "show": 1, + "bold": 0, + "italic": 0, + "underline": 0, + "order": "1" + } + ], + "calendar_range_columns": [ + { + "id": "kvc_2skkg5mi1eb37f", + "fk_view_id": "vw_wqs4zheuo5lgdy", + "fk_from_column_id": "cl_hzos4ghyncqi4k", + "fk_to_column_id": "cl_hzos4ghyncqi4k" + } + ], + "meta": null, + "title": "My Calendar" + } + ], + "title": "Calendar Model", + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Id", + "description": "Unique ID" + }, + "fk_view_id": { + "$ref": "#/components/schemas/Id", + "x-stoplight": { + "id": "1kgw1w06b97nl" + }, + "description": "View ID" + }, + "fk_cover_image_col_id": { + "$ref": "#/components/schemas/StringOrNull", + "description": "Cover Image Column ID" + }, + "columns": { + "type": "array", + "description": "Calendar Columns", + "items": { + "$ref": "#/components/schemas/CalendarColumn" + } + }, + "meta": { + "$ref": "#/components/schemas/Meta", + "description": "Meta Info for Kanban" + }, + "title": { + "type": "string", + "description": "Kanban Title", + "example": "My Kanban" + } + }, + "x-stoplight": { + "id": "gu721t0zw7jqq" + } + }, + "CalendarColumn": { + "description": "Model for Calendar Column", + "examples": [ + { + "id": "kvc_2skkg5mi1eb37f", + "fk_column_id": "cl_hzos4ghyncqi4k", + "fk_view_id": "vw_wqs4zheuo5lgdy", + "source_id": "ds_hd4ojj0xpquaam", + "base_id": "p_kzfl5lb0t3tcok", + "title": "string", + "show": 0, + "bold": 0, + "italic": 0, + "underline": 0, + "order": "1" + } + ], + "title": "Calendar Column Model", + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Id", + "description": "Unique ID" + }, + "fk_column_id": { + "$ref": "#/components/schemas/Id", + "description": "Foreign Key to Column" + }, + "fk_view_id": { + "$ref": "#/components/schemas/Id", + "x-stoplight": { + "id": "t1fy4zy561ih8" + }, + "description": "Foreign Key to View" + }, + "source_id": { + "$ref": "#/components/schemas/Id", + "x-stoplight": { + "id": "uqq8xmyz97t1u" + }, + "description": "Baes ID\n" + }, + "base_id": { + "$ref": "#/components/schemas/Id", + "x-stoplight": { + "id": "uqq8xmyz97t1u" + }, + "description": "Base ID" + }, + "title": { + "x-stoplight": { + "id": "uqq8xmyz97t1u" + }, + "description": "Base ID", + "type": "string" + }, + "show": { + "$ref": "#/components/schemas/Bool", + "x-stoplight": { + "id": "uqq8xmyz97t1u" + }, + "description": "Is this column shown?" + }, + "bold": { + "$ref": "#/components/schemas/Bool", + "x-stoplight": { + "id": "uqq8xmyz97t1u" + }, + "description": "Is this column shown as bold?" + }, + "italic": { + "$ref": "#/components/schemas/Bool", + "x-stoplight": { + "id": "uqq8xmyz97t1u" + }, + "description": "Is this column shown as italic?" + }, + "underline": { + "$ref": "#/components/schemas/Bool", + "x-stoplight": { + "id": "uqq8xmyz97t1u" + }, + "description": "Is this column shown underlines?" + }, + "order": { + "type": "number", + "x-stoplight": { + "id": "pbnchzgci5dwa" + }, + "example": 1, + "description": "Column Order" + } + }, + "x-stoplight": { + "id": "psbv6c6y9qvbu" + } + }, + "CalenderDateRange": { + "description": "Model for Calendar Date Range", + "examples": [ + { + "id": "kvc_2skkg5mi1eb37f", + "fk_from_column_id": "cl_hzos4ghyncqi4k", + "fk_to_column_id": "cl_hzos4ghyncqi4k", + "fk_view_id": "vw_wqs4zheuo5lgdy", + "label": "string" + } + ], + "title": "Calendar Date Range Model", + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/Id", + "description": "Unique ID" + }, + "fk_from_column_id": { + "$ref": "#/components/schemas/Id", + "description": "Foreign Key to Column" + }, + "fk_to_column_id": { + "$ref": "#/components/schemas/Id", + "description": "Foreign Key to Column" + }, + "fk_view_id": { + "$ref": "#/components/schemas/Id", + "x-stoplight": { + "id": "t1fy4zy561ih8" + }, + "description": "Foreign Key to View" + }, + "label": { + "x-stoplight": { + "id": "uqq8xmyz97t1u" + }, + "description": "Base ID", + "type": "string" + } + }, + "x-stoplight": { + "id": "psbv6c6y9qvbu" + } + }, + "CalanderUpdateReq": { + "description": "Model for Calendar Update Request", + "examples": [ + { + "fk_cover_image_col_id": "cl_ib8l4j1kiu1efx", + "title": "Calander 2" + } + ], + "title": "Calander Update Request Model", + "type": "object", + "properties": { + "fk_cover_image_col_id": { + "$ref": "#/components/schemas/StringOrNull", + "x-stoplight": { + "id": "81wn4hzj76wod" + }, + "description": "Foreign Key to Cover Image Column" + }, + "title": { + "type": "string", + "description": "Calander Title", + "example": "Calander 01" + }, + "meta": { + "$ref": "#/components/schemas/Meta", + "x-stoplight": { + "id": "stsvdmkli1b0r" + }, + "description": "Meta Info" + } + }, + "x-stoplight": { + "id": "9zirjgj9k1gqa" + } + }, "LicenseReq": { "description": "Model for Kanban Request", "examples": [ diff --git a/packages/nocodb/src/utils/globals.ts b/packages/nocodb/src/utils/globals.ts index 0ecacc7efa..df25f4ef03 100644 --- a/packages/nocodb/src/utils/globals.ts +++ b/packages/nocodb/src/utils/globals.ts @@ -21,6 +21,9 @@ export enum MetaTable { FORM_VIEW_COLUMNS = 'nc_form_view_columns_v2', GALLERY_VIEW = 'nc_gallery_view_v2', GALLERY_VIEW_COLUMNS = 'nc_gallery_view_columns_v2', + CALANDER_VIEW = 'nc_calendar_view_v2', + CALANDER_VIEW_COLUMNS = 'nc_calendar_view_columns_v2', + CALANDER_VIEW_RANGE = 'nc_calendar_view_range_v2', GRID_VIEW = 'nc_grid_view_v2', GRID_VIEW_COLUMNS = 'nc_grid_view_columns_v2', KANBAN_VIEW = 'nc_kanban_view_v2', From 8930a92c631cc9dd06e77d86d93a146c9a261fb3 Mon Sep 17 00:00:00 2001 From: DarkPhoenix2704 Date: Tue, 20 Feb 2024 07:15:40 +0000 Subject: [PATCH 002/284] feat(nocodb): update calendarModel feat(nocodb): update swagger.json feat(nocodb): create calendarViewColumn model --- packages/nocodb-sdk/src/lib/globals.ts | 1 + .../migrations/v2/nc_041_calander_view.ts | 2 +- packages/nocodb/src/models/CalanderView.ts | 0 packages/nocodb/src/models/CalendarView.ts | 102 ++++++++++ .../nocodb/src/models/CalendarViewColumn.ts | 187 ++++++++++++++++++ packages/nocodb/src/schema/swagger.json | 34 +++- packages/nocodb/src/utils/globals.ts | 9 +- 7 files changed, 325 insertions(+), 10 deletions(-) delete mode 100644 packages/nocodb/src/models/CalanderView.ts create mode 100644 packages/nocodb/src/models/CalendarView.ts create mode 100644 packages/nocodb/src/models/CalendarViewColumn.ts diff --git a/packages/nocodb-sdk/src/lib/globals.ts b/packages/nocodb-sdk/src/lib/globals.ts index b5bd47fd12..f534f25749 100644 --- a/packages/nocodb-sdk/src/lib/globals.ts +++ b/packages/nocodb-sdk/src/lib/globals.ts @@ -6,6 +6,7 @@ export enum ViewTypes { GRID = 3, KANBAN = 4, MAP = 5, + CALENDAR = 6, } export enum ProjectTypes { diff --git a/packages/nocodb/src/meta/migrations/v2/nc_041_calander_view.ts b/packages/nocodb/src/meta/migrations/v2/nc_041_calander_view.ts index 679246f174..7d25f276b0 100644 --- a/packages/nocodb/src/meta/migrations/v2/nc_041_calander_view.ts +++ b/packages/nocodb/src/meta/migrations/v2/nc_041_calander_view.ts @@ -2,7 +2,7 @@ import type { Knex } from 'knex'; import { MetaTable } from '~/utils/globals'; const up = async (knex: Knex) => { - await knex.schema.createTable(MetaTable.CALANDER_VIEW, (table) => { + await knex.schema.createTable(MetaTable.CALANDER, (table) => { table.string('fk_view_id', 20).primary(); table.string('base_id', 20); diff --git a/packages/nocodb/src/models/CalanderView.ts b/packages/nocodb/src/models/CalanderView.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/nocodb/src/models/CalendarView.ts b/packages/nocodb/src/models/CalendarView.ts new file mode 100644 index 0000000000..c1bc255e1f --- /dev/null +++ b/packages/nocodb/src/models/CalendarView.ts @@ -0,0 +1,102 @@ +import type { MetaType } from 'nocodb-sdk'; +import type { CalendarType } from 'nocodb-sdk'; +import View from '~/models/View'; +import { extractProps } from '~/helpers/extractProps'; +import NocoCache from '~/cache/NocoCache'; +import Noco from '~/Noco'; +import { CacheGetType, CacheScope, MetaTable } from '~/utils/globals'; + +export default class CalendarView implements CalendarType { + fk_view_id: string; + title: string; + base_id?: string; + source_id?: string; + meta?: MetaType; + + // below fields are not in use at this moment + // keep them for time being + show?: boolean; + public?: boolean; + password?: string; + show_all_fields?: boolean; + + constructor(data: CalendarView) { + Object.assign(this, data); + } + + public static async get(viewId: string, ncMeta = Noco.ncMeta) { + let view = + viewId && + (await NocoCache.get( + `${CacheScope.CALENDAR_VIEW}:${viewId}`, + CacheGetType.TYPE_OBJECT, + )); + if (!view) { + view = await ncMeta.metaGet2(null, null, MetaTable.CALENDAR, { + fk_view_id: viewId, + }); + await NocoCache.set(`${CacheScope.CALENDAR_VIEW}:${viewId}`, view); + } + + return view && new CalendarView(view); + } + + static async insert(view: Partial, ncMeta = Noco.ncMeta) { + const insertObj = { + base_id: view.base_id, + source_id: view.source_id, + fk_view_id: view.fk_view_id, + meta: view.meta, + }; + + const viewRef = await View.get(view.fk_view_id); + + if (!(view.base_id && view.source_id)) { + insertObj.base_id = viewRef.base_id; + insertObj.source_id = viewRef.source_id; + } + + await ncMeta.metaInsert2(null, null, MetaTable.CALENDAR, insertObj, true); + + return this.get(view.fk_view_id, ncMeta); + } + + static async update( + calendarId: string, + body: Partial, + ncMeta = Noco.ncMeta, + ) { + // get existing cache + const key = `${CacheScope.CALENDAR_VIEW_COLUMN}:${calendarId}`; + let o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT); + + const updateObj = extractProps(body, ['fk_cover_image_col_id', 'meta']); + + if (updateObj.meta && typeof updateObj.meta === 'object') { + updateObj.meta = JSON.stringify(updateObj.meta ?? {}); + } + + if (o) { + o = { ...o, ...updateObj }; + // set cache + await NocoCache.set(key, o); + } + + // TODO: Update this when API is ready + /*if (body.fk_geo_data_col_id != null) { + const mapViewColumns = await MapViewColumn.list(mapId); + const mapViewMappedByColumn = mapViewColumns.find( + (mapViewColumn) => + mapViewColumn.fk_column_id === body.fk_geo_data_col_id, + ); + await View.updateColumn(body.fk_view_id, mapViewMappedByColumn.id, { + show: true, + }); + }*/ + + // update meta + return await ncMeta.metaUpdate(null, null, MetaTable.CALENDAR, updateObj, { + fk_view_id: calendarId, + }); + } +} diff --git a/packages/nocodb/src/models/CalendarViewColumn.ts b/packages/nocodb/src/models/CalendarViewColumn.ts new file mode 100644 index 0000000000..d5c5c28b2f --- /dev/null +++ b/packages/nocodb/src/models/CalendarViewColumn.ts @@ -0,0 +1,187 @@ +import type { + BoolType, + MetaType, +} from 'nocodb-sdk'; +import View from '~/models/View'; +import Noco from '~/Noco'; +import NocoCache from '~/cache/NocoCache'; +import { extractProps } from '~/helpers/extractProps'; +import { deserializeJSON, serializeJSON } from '~/utils/serialize'; +import { CacheGetType, CacheScope, MetaTable } from '~/utils/globals'; + +export default class CalendarViewColumn { + id?: string; + fk_view_id?: string; + fk_column_id?: string; + base_id?: string; + source_id?: string; + show?: BoolType; + underline?: BoolType; + bold?: BoolType; + italic?: BoolType; + order?: number; + meta?: MetaType; + + constructor(data: CalendarViewColumn) { + Object.assign(this, data); + } + + public static async get(calendarViewColumnId: string, ncMeta = Noco.ncMeta) { + let viewColumn = + calendarViewColumnId && + (await NocoCache.get( + `${CacheScope.CALENDAR_VIEW_COLUMN}:${calendarViewColumnId}`, + CacheGetType.TYPE_OBJECT, + )); + if (!viewColumn) { + viewColumn = await ncMeta.metaGet2( + null, + null, + MetaTable.CALENDAR_VIEW_COLUMNS, + calendarViewColumnId, + ); + viewColumn.meta = + viewColumn.meta && typeof viewColumn.meta === 'string' + ? JSON.parse(viewColumn.meta) + : viewColumn.meta; + } + await NocoCache.set( + `${CacheScope.CALENDAR_VIEW_COLUMN}:${calendarViewColumnId}`, + viewColumn, + ); + + return viewColumn && new CalendarViewColumn(viewColumn); + } + + static async insert(column: Partial, ncMeta = Noco.ncMeta) { + const insertObj = extractProps(column, [ + 'fk_view_id', + 'fk_column_id', + 'show', + 'base_id', + 'source_id', + 'underline', + 'bold', + 'italic', + 'meta', + ]); + + insertObj.order = await ncMeta.metaGetNextOrder( + MetaTable.CALENDAR_VIEW_COLUMNS, + { + fk_view_id: insertObj.fk_view_id, + }, + ); + + if (insertObj.meta) { + insertObj.meta = serializeJSON(insertObj.meta); + } + + if (!(insertObj.base_id && insertObj.source_id)) { + const viewRef = await View.get(insertObj.fk_view_id, ncMeta); + insertObj.base_id = viewRef.base_id; + insertObj.source_id = viewRef.source_id; + } + + const { id, fk_column_id } = await ncMeta.metaInsert2( + null, + null, + MetaTable.CALENDAR_VIEW_COLUMNS, + insertObj, + ); + + await NocoCache.set(`${CacheScope.CALENDAR_VIEW_COLUMN}:${fk_column_id}`, id); + + // if cache is not present skip pushing it into the list to avoid unexpected behaviour + const { list } = await NocoCache.getList(CacheScope.CALENDAR_VIEW_COLUMN, [ + column.fk_view_id, + ]); + + if (list?.length) + await NocoCache.appendToList( + CacheScope.CALENDAR_VIEW_COLUMN, + [column.fk_view_id], + `${CacheScope.CALENDAR_VIEW_COLUMN}:${id}`, + ); + return this.get(id, ncMeta); + } + + public static async list( + viewId: string, + ncMeta = Noco.ncMeta, + ): Promise { + const cachedList = await NocoCache.getList(CacheScope.CALENDAR_VIEW_COLUMN, [ + viewId, + ]); + let { list: viewColumns } = cachedList; + const { isNoneList } = cachedList; + if (!isNoneList && !viewColumns.length) { + viewColumns = await ncMeta.metaList2( + null, + null, + MetaTable.CALENDAR_VIEW_COLUMNS, + { + condition: { + fk_view_id: viewId, + }, + orderBy: { + order: 'asc', + }, + }, + ); + + for (const viewColumn of viewColumns) { + viewColumn.meta = deserializeJSON(viewColumn.meta); + } + + await NocoCache.setList( + CacheScope.CALENDAR_VIEW_COLUMN, + [viewId], + viewColumns, + ); + } + viewColumns.sort( + (a, b) => + (a.order != null ? a.order : Infinity) - + (b.order != null ? b.order : Infinity), + ); + return viewColumns?.map((v) => new CalendarViewColumn(v)); + } + + static async update( + columnId: string, + body: Partial, + ncMeta = Noco.ncMeta, + ) { + const updateObj = extractProps(body, [ + 'show', + 'order', + 'meta', + 'underline', + 'bold', + 'italic', + ]); + + // get existing cache + const key = `${CacheScope.CALENDAR_VIEW_COLUMN}:${columnId}`; + const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT); + if (o) { + Object.assign(o, updateObj); + // set cache + await NocoCache.set(key, o); + } + + if (updateObj.meta) { + updateObj.meta = serializeJSON(updateObj.meta); + } + + // update meta + return await ncMeta.metaUpdate( + null, + null, + MetaTable.CALENDAR_VIEW_COLUMNS, + updateObj, + columnId, + ); + } +} diff --git a/packages/nocodb/src/schema/swagger.json b/packages/nocodb/src/schema/swagger.json index ed359ec8b2..e304dc4925 100644 --- a/packages/nocodb/src/schema/swagger.json +++ b/packages/nocodb/src/schema/swagger.json @@ -14619,6 +14619,10 @@ "type": "integer", "description": "Kanban Count" }, + "calendarCount": { + "type": "integer", + "description": "Calendar Count" + }, "total": { "type": "integer", "description": "Total View Count" @@ -14639,6 +14643,10 @@ "type": "integer", "description": "Shared Kanban Count" }, + "sharedCalendarCount": { + "type": "integer", + "description": "Shared Calendar Count" + }, "sharedTotal": { "type": "integer", "description": "Shared Total View Count" @@ -14704,11 +14712,13 @@ "gridCount": 3, "galleryCount": 0, "kanbanCount": 0, + "calendarCount": 0, "total": 3, "sharedFormCount": 0, "sharedGridCount": 0, "sharedGalleryCount": 0, "sharedKanbanCount": 0, + "sharedCalendarCount": 0, "sharedTotal": 0, "sharedLockedCount": 0 }, @@ -14744,11 +14754,13 @@ "gridCount": 3, "galleryCount": 0, "kanbanCount": 0, + "calendarCount": 0, "total": 3, "sharedFormCount": 0, "sharedGridCount": 0, "sharedGalleryCount": 0, "sharedKanbanCount": 0, + "sharedCalendarCount": 0, "sharedTotal": 0, "sharedLockedCount": 0 }, @@ -20241,7 +20253,7 @@ "id": "psbv6c6y9qvbu" } }, - "CalenderDateRange": { + "CalendarDateRange": { "description": "Model for Calendar Date Range", "examples": [ { @@ -20286,15 +20298,15 @@ "id": "psbv6c6y9qvbu" } }, - "CalanderUpdateReq": { + "CalendarUpdateReq": { "description": "Model for Calendar Update Request", "examples": [ { "fk_cover_image_col_id": "cl_ib8l4j1kiu1efx", - "title": "Calander 2" + "title": "Calendar 2" } ], - "title": "Calander Update Request Model", + "title": "Calendar Update Request Model", "type": "object", "properties": { "fk_cover_image_col_id": { @@ -20306,8 +20318,8 @@ }, "title": { "type": "string", - "description": "Calander Title", - "example": "Calander 01" + "description": "Calendar Title", + "example": "Calendar 01" }, "meta": { "$ref": "#/components/schemas/Meta", @@ -23445,6 +23457,9 @@ }, { "$ref": "#/components/schemas/Map" + }, + { + "$ref": "#/components/schemas/Calendar" } ], "description": "Associated View Model" @@ -23627,6 +23642,13 @@ "fk_grp_col_id": "cl_g0a89q9xdry3lu", "fk_geo_data_col_id": null }, + { + "title": "My Calendar View", + "type": 4, + "copy_from_id": null, + "fk_grp_col_id": null, + "fk_geo_data_col_id": null + }, { "title": "My Map View", "type": 5, diff --git a/packages/nocodb/src/utils/globals.ts b/packages/nocodb/src/utils/globals.ts index df25f4ef03..39d7f4eb2a 100644 --- a/packages/nocodb/src/utils/globals.ts +++ b/packages/nocodb/src/utils/globals.ts @@ -21,9 +21,9 @@ export enum MetaTable { FORM_VIEW_COLUMNS = 'nc_form_view_columns_v2', GALLERY_VIEW = 'nc_gallery_view_v2', GALLERY_VIEW_COLUMNS = 'nc_gallery_view_columns_v2', - CALANDER_VIEW = 'nc_calendar_view_v2', - CALANDER_VIEW_COLUMNS = 'nc_calendar_view_columns_v2', - CALANDER_VIEW_RANGE = 'nc_calendar_view_range_v2', + CALENDAR = 'nc_calendar_view_v2', + CALENDAR_VIEW_COLUMNS = 'nc_calendar_view_columns_v2', + CALENDAR_VIEW_RANGE = 'nc_calendar_view_range_v2', GRID_VIEW = 'nc_grid_view_v2', GRID_VIEW_COLUMNS = 'nc_grid_view_columns_v2', KANBAN_VIEW = 'nc_kanban_view_v2', @@ -141,6 +141,9 @@ export enum CacheScope { GRID_VIEW = 'gridView', GRID_VIEW_COLUMN = 'gridViewColumn', KANBAN_VIEW = 'kanbanView', + CALENDAR_VIEW = 'calendarView', + CALENDAR_VIEW_COLUMN = 'calendarViewColumn', + CALENDAR_VIEW_RANGE = 'calendarViewRange', MAP_VIEW = 'mapView', MAP_VIEW_COLUMN = 'mapViewColumn', KANBAN_VIEW_COLUMN = 'kanbanViewColumn', From 55f9a0b82baf23b62817c82e5bc78ed41c8dd147 Mon Sep 17 00:00:00 2001 From: DarkPhoenix2704 Date: Tue, 20 Feb 2024 07:15:41 +0000 Subject: [PATCH 003/284] feat(nc-gui): create calendar view --- .../dashboard/TreeView/CreateViewBtn.vue | 11 ++ packages/nc-gui/components/dlg/ViewCreate.vue | 123 ++++++++++++++++-- packages/nc-gui/context/index.ts | 1 + packages/nc-gui/lang/en.json | 8 ++ packages/nc-gui/utils/viewUtils.ts | 3 +- 5 files changed, 136 insertions(+), 10 deletions(-) diff --git a/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue b/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue index 1e8906ff66..ca7d9befac 100644 --- a/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue +++ b/packages/nc-gui/components/dashboard/TreeView/CreateViewBtn.vue @@ -149,6 +149,17 @@ async function onOpenModal({ + +
+
+ +
Calendar
+
+ + + +
+
diff --git a/packages/nc-gui/components/dlg/ViewCreate.vue b/packages/nc-gui/components/dlg/ViewCreate.vue index 524c1d66f0..27c2bc28df 100644 --- a/packages/nc-gui/components/dlg/ViewCreate.vue +++ b/packages/nc-gui/components/dlg/ViewCreate.vue @@ -3,7 +3,7 @@ import type { ComponentPublicInstance } from '@vue/runtime-core' import type { Form as AntForm, SelectProps } from 'ant-design-vue' import { capitalize } from '@vue/runtime-core' import type { FormType, GalleryType, GridType, KanbanType, MapType, TableType } from 'nocodb-sdk' -import { UITypes, ViewTypes } from 'nocodb-sdk' +import { UITypes, ViewTypes, isSystemColumn } from 'nocodb-sdk' import { computed, message, nextTick, onBeforeMount, reactive, ref, useApi, useI18n, useVModel, watch } from '#imports' interface Props { @@ -28,6 +28,10 @@ interface Form { // for kanban view only fk_grp_col_id: string | null fk_geo_data_col_id: string | null + + // for calendar view only + fk_from_col_id: string | null + fk_to_col_id: string | null // For EE only } const props = withDefaults(defineProps(), { @@ -62,12 +66,22 @@ const isViewCreating = ref(false) const views = computed(() => viewsByTable.value.get(tableId.value) ?? []) +const isNecessaryColumnsPresent = ref(true) + +const errorMessages = { + [ViewTypes.KANBAN]: t('msg.warning.kanbanNoFields'), + [ViewTypes.MAP]: t('msg.warning.mapNoFields'), + [ViewTypes.CALENDAR]: t('msg.warning.calendarNoFields'), +} + const form = reactive
({ title: props.title || '', type: props.type, copy_from_id: null, fk_grp_col_id: null, fk_geo_data_col_id: null, + fk_from_col_id: null, + fk_to_col_id: null, }) const viewSelectFieldOptions = ref([]) @@ -97,6 +111,7 @@ const typeAlias = computed( [ViewTypes.FORM]: 'form', [ViewTypes.KANBAN]: 'kanban', [ViewTypes.MAP]: 'map', + [ViewTypes.CALENDAR]: 'calendar', }[props.type]), ) @@ -163,6 +178,15 @@ async function onSubmit() { break case ViewTypes.MAP: data = await api.dbView.mapCreate(tableId.value, form) + break + case ViewTypes.CALENDAR: + data = { + base_id: meta.value?.base_id, + source_id: meta.value?.source_id, + } + // TODO: implement api call + // data = await api.dbView.calendarCreate(tableId.value, form) + break } if (data) { @@ -188,7 +212,7 @@ async function onSubmit() { const isMetaLoading = ref(false) onMounted(async () => { - if (props.type === ViewTypes.KANBAN || props.type === ViewTypes.MAP) { + if (props.type === ViewTypes.KANBAN || props.type === ViewTypes.MAP || props.type === ViewTypes.CALENDAR) { isMetaLoading.value = true try { meta.value = (await getMeta(tableId.value))! @@ -206,9 +230,12 @@ onMounted(async () => { if (geoDataFieldColumnId.value) { // take from the one from copy view form.fk_geo_data_col_id = geoDataFieldColumnId.value - } else { - // take the first option + } else if (viewSelectFieldOptions.value?.length) { + // if there is geo data column take the first option form.fk_geo_data_col_id = viewSelectFieldOptions.value?.[0]?.value as string + } else { + // if there is no geo data column, disable the create button + isNecessaryColumnsPresent.value = false } } @@ -226,9 +253,32 @@ onMounted(async () => { if (groupingFieldColumnId.value) { // take from the one from copy view form.fk_grp_col_id = groupingFieldColumnId.value + } else if (viewSelectFieldOptions.value?.length) { + // take the first option + form.fk_grp_col_id = viewSelectFieldOptions.value[0].value as string } else { + // if there is no grouping field column, disable the create button + isNecessaryColumnsPresent.value = false + } + } + + if (props.type === ViewTypes.CALENDAR) { + console.log(meta) + viewSelectFieldOptions.value = meta + .value!.columns!.filter((el) => el.uidt === UITypes.Date || (el.uidt === UITypes.DateTime && !isSystemColumn(el))) + .map((field) => { + return { + value: field.id, + label: field.title, + } + }) + + if (viewSelectFieldOptions.value?.length) { // take the first option - form.fk_grp_col_id = viewSelectFieldOptions.value?.[0]?.value as string + form.fk_from_col_id = viewSelectFieldOptions.value[0].value as string + } else { + // if there is no grouping field column, disable the create button + isNecessaryColumnsPresent.value = false } } } catch (e) { @@ -241,7 +291,10 @@ onMounted(async () => { + + + diff --git a/packages/nc-gui/context/index.ts b/packages/nc-gui/context/index.ts index 788da6ec52..119fca55f6 100644 --- a/packages/nc-gui/context/index.ts +++ b/packages/nc-gui/context/index.ts @@ -10,6 +10,7 @@ export const ColumnInj: InjectionKey> = Symbol('column-injection export const MetaInj: InjectionKey | Ref> = Symbol('meta-injection') export const TabMetaInj: InjectionKey | Ref> = Symbol('tab-meta-injection') export const IsFormInj: InjectionKey> = Symbol('is-form-injection') +export const IsCalendarInj: InjectionKey> = Symbol('is-calendar-injection') export const IsSurveyFormInj: InjectionKey> = Symbol('is-survey-form-injection') export const IsGridInj: InjectionKey> = Symbol('is-grid-injection') export const IsGroupByInj: InjectionKey> = Symbol('is-group-by-injection') diff --git a/packages/nc-gui/lang/en.json b/packages/nc-gui/lang/en.json index e58ac4dc21..041374eb3c 100644 --- a/packages/nc-gui/lang/en.json +++ b/packages/nc-gui/lang/en.json @@ -473,6 +473,8 @@ "timeFormat": "Time Format", "singularLabel": "Singular Label", "pluralLabel": "Plural Label", + "selectDateField": "Select a date field", + "selectEndDateField": "Select end date field", "optional": "(Optional)", "clickToMake": "Click to make", "visibleForRole": "visible for role:", @@ -515,7 +517,9 @@ "duplicateFormView": "Duplicate Form View", "createFormView": "Create Form View", "duplicateKanbanView": "Duplicate Kanban View", + "duplicateCalendarView": "Duplicate Calendar View", "createKanbanView": "Create Kanban View", + "createCalendarView": "Create Calendar View", "viewName": "View name", "viewLink": "View Link", "columnName": "Field Name", @@ -931,6 +935,7 @@ "selectGroupField": "Select a Grouping Field", "selectGroupFieldNotFound": "No Single Select Field can be found. Please create one first.", "selectGeoField": "Select a GeoData Field", + "notSelected": "-not selected-", "selectGeoFieldNotFound": "No GeoData Field can be found. Please create one first.", "password": { "enter": "Enter the password", @@ -1074,6 +1079,9 @@ "tooLargeFieldEntity": "The field is too large to be converted to {entity}", "roleRequired": "Role required", "warning": { + "calendarNoFields": "Calendar view requires a date or date time field to be setup. Try setting up a calendar view after adding a date/ date time field!", + "kanbanNoFields": "Kanban view requires a single select field to be setup. Try setting up a kanban view after adding a single select field!", + "mapNoFields": "Map view requires a geo data field to be setup. Try setting up a map view after adding a geo data field!", "dbValid": "Please make sure database you are trying to connect is valid! This operation can cause schema loss!!", "barcode": { "renderError": "Barcode error - please check compatibility between input and barcode type" diff --git a/packages/nc-gui/utils/viewUtils.ts b/packages/nc-gui/utils/viewUtils.ts index 9afc42485b..ac293a2d19 100644 --- a/packages/nc-gui/utils/viewUtils.ts +++ b/packages/nc-gui/utils/viewUtils.ts @@ -5,7 +5,7 @@ import { iconMap } from './iconUtils' export const viewIcons: Record = { [ViewTypes.GRID]: { icon: iconMap.grid, color: '#36BFFF' }, [ViewTypes.FORM]: { icon: iconMap.form, color: '#7D26CD' }, - calendar: { icon: iconMap.calendar, color: 'purple' }, + [ViewTypes.CALENDAR]: { icon: iconMap.calendar, color: 'purple' }, [ViewTypes.GALLERY]: { icon: iconMap.gallery, color: '#FC3AC6' }, [ViewTypes.MAP]: { icon: iconMap.map, color: 'blue' }, [ViewTypes.KANBAN]: { icon: iconMap.kanban, color: '#FF9052' }, @@ -18,6 +18,7 @@ export const viewTypeAlias: Record = { [ViewTypes.GALLERY]: 'gallery', [ViewTypes.KANBAN]: 'kanban', [ViewTypes.MAP]: 'map', + [ViewTypes.CALENDAR]: 'calendar', } export const isRtlLang = (lang: keyof typeof Language) => ['fa', 'ar'].includes(lang) From cb78e2b9cdf1184b01dcb3e7443ec0cb077203d4 Mon Sep 17 00:00:00 2001 From: DarkPhoenix2704 Date: Tue, 20 Feb 2024 07:15:41 +0000 Subject: [PATCH 004/284] feat(nc-gui): calendarRange options --- .../nc-gui/components/smartsheet/Toolbar.vue | 9 +- .../smartsheet/toolbar/CalendarRange.vue | 177 ++++++++++++++++++ .../smartsheet/toolbar/FieldsMenu.vue | 6 +- .../nc-gui/composables/useSmartsheetStore.ts | 2 + packages/nc-gui/lang/en.json | 3 + 5 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 packages/nc-gui/components/smartsheet/toolbar/CalendarRange.vue diff --git a/packages/nc-gui/components/smartsheet/Toolbar.vue b/packages/nc-gui/components/smartsheet/Toolbar.vue index 72cf647e2c..3783a0b03e 100644 --- a/packages/nc-gui/components/smartsheet/Toolbar.vue +++ b/packages/nc-gui/components/smartsheet/Toolbar.vue @@ -10,7 +10,7 @@ import { useViewsStore, } from '#imports' -const { isGrid, isGallery, isKanban, isMap } = useSmartsheetStoreOrThrow() +const { isGrid, isGallery, isKanban, isMap, isCalendar } = useSmartsheetStoreOrThrow() const isPublic = inject(IsPublicInj, ref(false)) @@ -30,16 +30,17 @@ const { allowCSVDownload } = useSharedView()