Browse Source

feat(nocodb): calendar view apis

pull/7611/head
DarkPhoenix2704 7 months ago
parent
commit
7f272abf4d
  1. 145
      packages/nocodb/src/models/CalendarRange.ts
  2. 15
      packages/nocodb/src/models/CalendarView.ts
  3. 22
      packages/nocodb/src/models/CalendarViewColumn.ts
  4. 99
      packages/nocodb/src/models/View.ts
  5. 2
      packages/nocodb/src/models/index.ts
  6. 3
      packages/nocodb/src/modules/metas/metas.module.ts
  7. 36
      packages/nocodb/src/schema/swagger.json
  8. 75
      packages/nocodb/src/services/calendars.service.ts

145
packages/nocodb/src/models/CalendarRange.ts

@ -0,0 +1,145 @@
import type {CalendarRangeType} from 'nocodb-sdk';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
import {extractProps} from '~/helpers/extractProps';
import {CacheGetType, CacheScope, MetaTable} from '~/utils/globals';
export default class CalendarRange implements CalendarRangeType {
id: string;
fk_from_column_id: string;
fk_to_column_id: string | null;
fk_view_id: string;
constructor(data: Partial<CalendarRange>) {
Object.assign(this, data);
}
public static async insert(
data: Partial<CalendarRange>,
ncMeta = Noco.ncMeta,
) {
const insertObj = extractProps(data, [
'fk_from_column_id',
'fk_to_column_id',
'fk_view_id',
]);
const { id } = await ncMeta.metaInsert2(
null,
null,
MetaTable.CALENDAR_VIEW_RANGE,
insertObj,
);
await NocoCache.appendToList(
CacheScope.CALENDAR_VIEW_RANGE,
[data.fk_view_id],
`${CacheScope.CALENDAR_VIEW_RANGE}:${id}`,
);
return this.get(id, ncMeta);
}
public static async bulkInsert(
data: Partial<CalendarRange>[],
ncMeta = Noco.ncMeta,
) {
const insertObj = [];
for (const d of data) {
const tempObj = extractProps(d, [
'fk_from_column_id',
'fk_to_column_id',
'fk_view_id',
]);
insertObj.push(tempObj);
}
const bulkData = await ncMeta.bulkMetaInsert(
null,
null,
MetaTable.CALENDAR_VIEW_RANGE,
insertObj,
);
for (const d of bulkData) {
await NocoCache.appendToList(
CacheScope.CALENDAR_VIEW_RANGE,
[d.fk_view_id],
`${CacheScope.CALENDAR_VIEW_RANGE}:${d.id}`,
);
await NocoCache.set(`${CacheScope.CALENDAR_VIEW_RANGE}:${d.id}`, d);
}
return true;
}
public static async get(
calendarRangeId: string,
ncMeta = Noco.ncMeta,
): Promise<CalendarRange> {
let data =
calendarRangeId &&
(await NocoCache.get(
`${CacheScope.CALENDAR_VIEW_RANGE}:${calendarRangeId}`,
CacheGetType.TYPE_OBJECT,
));
if (!data) {
data = await ncMeta.metaGet2(
null,
null,
MetaTable.CALENDAR_VIEW_RANGE,
calendarRangeId,
);
await NocoCache.set(
`${CacheScope.CALENDAR_VIEW_RANGE}:${calendarRangeId}`,
data,
);
}
return data && new CalendarRange(data);
}
public static async read(fk_view_id: string, ncMeta = Noco.ncMeta) {
const cachedList = await NocoCache.getList(CacheScope.CALENDAR_VIEW, [
fk_view_id,
]);
let { list: ranges } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && !ranges.length) {
ranges = await ncMeta.metaList2(
null, //,
null, //model.db_alias,
MetaTable.CALENDAR_VIEW_RANGE,
{ condition: { fk_view_id } },
);
await NocoCache.setList(
CacheScope.CALENDAR_VIEW_RANGE,
[fk_view_id],
ranges.map(({ created_at, updated_at, ...others }) => others),
);
}
return ranges?.length
? {
ranges: ranges
.map(({ created_at, updated_at, ...c }) => new CalendarRange(c))
}
: null;
}
public static async find(
fk_view_id: string,
ncMeta = Noco.ncMeta,
): Promise<CalendarRange> {
const data = await ncMeta.metaGet2(
null,
null,
MetaTable.CALENDAR_VIEW_RANGE,
{
fk_view_id,
},
);
return data && new CalendarRange(data);
}
}

15
packages/nocodb/src/models/CalendarView.ts

@ -1,10 +1,10 @@
import type { MetaType } from 'nocodb-sdk';
import type { CalendarType } from 'nocodb-sdk';
import type {CalendarType} from 'nocodb-sdk';
import {BoolType, MetaType} from 'nocodb-sdk';
import View from '~/models/View';
import { extractProps } from '~/helpers/extractProps';
import {extractProps} from '~/helpers/extractProps';
import NocoCache from '~/cache/NocoCache';
import Noco from '~/Noco';
import { CacheGetType, CacheScope, MetaTable } from '~/utils/globals';
import {CacheGetType, CacheScope, MetaTable} from '~/utils/globals';
export default class CalendarView implements CalendarType {
fk_view_id: string;
@ -13,12 +13,13 @@ export default class CalendarView implements CalendarType {
source_id?: string;
meta?: MetaType;
fk_cover_image_col_id?: string;
// below fields are not in use at this moment
// keep them for time being
show?: boolean;
public?: boolean;
show?: BoolType;
public?: BoolType;
password?: string;
show_all_fields?: boolean;
show_all_fields?: BoolType;
constructor(data: CalendarView) {
Object.assign(this, data);

22
packages/nocodb/src/models/CalendarViewColumn.ts

@ -1,13 +1,10 @@
import type {
BoolType,
MetaType,
} from 'nocodb-sdk';
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';
import {extractProps} from '~/helpers/extractProps';
import {deserializeJSON} from '~/utils/serialize';
import {CacheGetType, CacheScope, MetaTable} from '~/utils/globals';
export default class CalendarViewColumn {
id?: string;
@ -63,7 +60,6 @@ export default class CalendarViewColumn {
'underline',
'bold',
'italic',
'meta',
]);
insertObj.order = await ncMeta.metaGetNextOrder(
@ -73,10 +69,6 @@ export default class CalendarViewColumn {
},
);
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;
@ -156,7 +148,6 @@ export default class CalendarViewColumn {
const updateObj = extractProps(body, [
'show',
'order',
'meta',
'underline',
'bold',
'italic',
@ -170,11 +161,6 @@ export default class CalendarViewColumn {
// set cache
await NocoCache.set(key, o);
}
if (updateObj.meta) {
updateObj.meta = serializeJSON(updateObj.meta);
}
// update meta
return await ncMeta.metaUpdate(
null,

99
packages/nocodb/src/models/View.ts

@ -1,11 +1,14 @@
import { isSystemColumn, UITypes, ViewTypes } from 'nocodb-sdk';
import type { BoolType, ColumnReqType, ViewType } from 'nocodb-sdk';
import type {BoolType, ColumnReqType, ViewType} from 'nocodb-sdk';
import {isSystemColumn, UITypes, ViewTypes} from 'nocodb-sdk';
import Model from '~/models/Model';
import FormView from '~/models/FormView';
import GridView from '~/models/GridView';
import KanbanView from '~/models/KanbanView';
import GalleryView from '~/models/GalleryView';
import CalendarView from "~/models/CalendarView";
import GridViewColumn from '~/models/GridViewColumn';
import CalendarViewColumn from '~/models/CalendarViewColumn';
import CalendarRange from "~/models/CalendarRange";
import Sort from '~/models/Sort';
import Filter from '~/models/Filter';
import GalleryViewColumn from '~/models/GalleryViewColumn';
@ -14,16 +17,11 @@ import KanbanViewColumn from '~/models/KanbanViewColumn';
import Column from '~/models/Column';
import MapView from '~/models/MapView';
import MapViewColumn from '~/models/MapViewColumn';
import { extractProps } from '~/helpers/extractProps';
import {extractProps} from '~/helpers/extractProps';
import NocoCache from '~/cache/NocoCache';
import {
CacheDelDirection,
CacheGetType,
CacheScope,
MetaTable,
} from '~/utils/globals';
import {CacheDelDirection, CacheGetType, CacheScope, MetaTable,} from '~/utils/globals';
import Noco from '~/Noco';
import { parseMetaProp, stringifyMetaProp } from '~/utils/modelUtils';
import {parseMetaProp, stringifyMetaProp} from '~/utils/modelUtils';
const { v4: uuidv4 } = require('uuid');
@ -33,6 +31,7 @@ type ViewColumn =
| FormViewColumn
| GalleryViewColumn
| KanbanViewColumn
| CalendarViewColumn
| MapViewColumn;
type ViewColumnEnrichedWithTitleAndName = ViewColumn & {
@ -55,13 +54,14 @@ export default class View implements ViewType {
fk_model_id: string;
model?: Model;
view?: FormView | GridView | KanbanView | GalleryView | MapView;
view?: FormView | GridView | KanbanView | GalleryView | MapView | CalendarView;
columns?: Array<
| FormViewColumn
| GridViewColumn
| GalleryViewColumn
| KanbanViewColumn
| MapViewColumn
| CalendarViewColumn
>;
sorts: Sort[];
@ -106,6 +106,9 @@ export default class View implements ViewType {
case ViewTypes.FORM:
this.view = await FormView.get(this.id);
break;
case ViewTypes.CALENDAR:
this.view = await CalendarView.get(this.id);
break;
}
return <T>this.view;
}
@ -129,6 +132,9 @@ export default class View implements ViewType {
case ViewTypes.FORM:
this.view = await FormView.get(this.id, ncMeta);
break;
case ViewTypes.CALENDAR:
this.view = await CalendarView.get(this.id, ncMeta);
break;
}
return this.view;
}
@ -271,9 +277,10 @@ export default class View implements ViewType {
static async insert(
view: Partial<View> &
Partial<FormView | GridView | GalleryView | KanbanView | MapView> & {
Partial<FormView | GridView | GalleryView | KanbanView | MapView | CalendarView> & {
copy_from_id?: string;
fk_grp_col_id?: string;
calendar_range?: Partial<CalendarRange>[];
},
ncMeta = Noco.ncMeta,
) {
@ -378,6 +385,21 @@ export default class View implements ViewType {
ncMeta,
);
break;
case ViewTypes.CALENDAR:
const obj = extractProps(view, ["calendar_range"])
if (!obj.calendar_range) break;
const calendarRange = obj.calendar_range as Partial<CalendarRange>[];
calendarRange.forEach((range) => {
range.fk_view_id = view_id;
})
await CalendarView.insert({
...(copyFromView?.view || {}),
...view,
fk_view_id: view_id,
}, ncMeta,)
await CalendarRange.bulkInsert(calendarRange, ncMeta);
}
if (copyFromView) {
@ -429,6 +451,20 @@ export default class View implements ViewType {
let order = 1;
let galleryShowLimit = 0;
let kanbanShowLimit = 0;
let calendarRanges: Array<string> | null = null;
if (view.type === ViewTypes.CALENDAR) {
const calRange = await CalendarRange.read(view_id, ncMeta);
if (calRange) {
const calIds: Set<string> = new Set()
calRange.ranges.forEach((range) => {
calIds.add(range.fk_from_column_id);
if (!range.fk_to_column_id) return;
calIds.add(range.fk_to_column_id);
})
calendarRanges = Array.from(calIds) as Array<string>;
}
}
if (view.type === ViewTypes.KANBAN && !copyFromView) {
// sort by display value & attachment first, then by singleLineText & Number
@ -455,6 +491,9 @@ export default class View implements ViewType {
for (const vCol of columns) {
let show = 'show' in vCol ? vCol.show : true;
let underline = false;
let bold = false;
let italic = false;
if (view.type === ViewTypes.GALLERY) {
const galleryView = await GalleryView.get(view_id, ncMeta);
@ -485,6 +524,12 @@ export default class View implements ViewType {
// other columns will be hidden
show = false;
}
} else if (view.type === ViewTypes.CALENDAR && !copyFromView) {
const calendarView = await CalendarView.get(view_id, ncMeta);
if (calendarRanges && calendarRanges.includes(vCol.id)) {
show = true;
} else show = vCol.id === calendarView?.fk_cover_image_col_id;
// Show all Fields in Ranges
} else if (view.type === ViewTypes.MAP && !copyFromView) {
const mapView = await MapView.get(view_id, ncMeta);
if (vCol.id === mapView?.fk_geo_data_col_id) {
@ -506,6 +551,9 @@ export default class View implements ViewType {
view_id,
fk_column_id: vCol.fk_column_id || vCol.id,
show,
underline,
bold,
italic,
id: null,
},
ncMeta,
@ -591,9 +639,12 @@ export default class View implements ViewType {
view_id: any;
order;
show;
underline?;
bold?;
italic?;
fk_column_id;
id?: string;
} & Partial<FormViewColumn>,
} & Partial<FormViewColumn> & Partial<CalendarViewColumn>,
ncMeta = Noco.ncMeta,
) {
const view = await this.get(param.view_id, ncMeta);
@ -655,6 +706,16 @@ export default class View implements ViewType {
);
}
break;
case ViewTypes.CALENDAR: {
col = await CalendarViewColumn.insert(
{
...param,
fk_view_id: view.id,
},
ncMeta,
);
}
break;
}
return col;
@ -678,6 +739,7 @@ export default class View implements ViewType {
| GalleryViewColumn
| KanbanViewColumn
| MapViewColumn
| CalendarViewColumn
>
> {
let columns: Array<GridViewColumn | any> = [];
@ -700,6 +762,9 @@ export default class View implements ViewType {
case ViewTypes.KANBAN:
columns = await KanbanViewColumn.list(viewId, ncMeta);
break;
case ViewTypes.CALENDAR:
columns = await CalendarViewColumn.list(viewId, ncMeta);
break;
}
return columns;
@ -749,6 +814,11 @@ export default class View implements ViewType {
tableName = MetaTable.KANBAN_VIEW_COLUMNS;
cacheScope = CacheScope.KANBAN_VIEW_COLUMN;
break;
case ViewTypes.CALENDAR:
tableName = MetaTable.CALENDAR_VIEW_COLUMNS;
cacheScope = CacheScope.CALENDAR_VIEW_COLUMN;
break;
}
@ -800,6 +870,9 @@ export default class View implements ViewType {
table = MetaTable.FORM_VIEW_COLUMNS;
cacheScope = CacheScope.FORM_VIEW_COLUMN;
break;
case ViewTypes.CALENDAR:
table = MetaTable.CALENDAR_VIEW_COLUMNS;
cacheScope = CacheScope.CALENDAR_VIEW_COLUMN;
}
const updateObj = extractProps(colData, ['order', 'show']);

2
packages/nocodb/src/models/index.ts

@ -3,6 +3,8 @@ export { default as Audit } from './Audit';
export { default as BarcodeColumn } from './BarcodeColumn';
export { default as Source } from './Source';
export { default as Column } from './Column';
export { default as CalendarView } from './CalendarView';
export { default as CalendarViewColumn } from './CalendarViewColumn';
export { default as Filter } from './Filter';
export { default as FormulaColumn } from './FormulaColumn';
export { default as FormView } from './FormView';

3
packages/nocodb/src/modules/metas/metas.module.ts

@ -10,6 +10,7 @@ import { AttachmentsSecureController } from '~/controllers/attachments-secure.co
import { AuditsController } from '~/controllers/audits.controller';
import { SourcesController } from '~/controllers/sources.controller';
import { CachesController } from '~/controllers/caches.controller';
import { CalendarsController } from '~/controllers/calendars.controller';
import { ColumnsController } from '~/controllers/columns.controller';
import { FiltersController } from '~/controllers/filters.controller';
import { FormColumnsController } from '~/controllers/form-columns.controller';
@ -41,6 +42,7 @@ import { AuditsService } from '~/services/audits.service';
import { SourcesService } from '~/services/sources.service';
import { BulkDataAliasService } from '~/services/bulk-data-alias.service';
import { CachesService } from '~/services/caches.service';
import { CalendarsService } from '~/services/calendars.service';
import { ColumnsService } from '~/services/columns.service';
import { FiltersService } from '~/services/filters.service';
import { FormColumnsService } from '~/services/form-columns.service';
@ -137,6 +139,7 @@ export const metaModuleMetadata = {
AuditsService,
SourcesService,
CachesService,
CalendarsService,
ColumnsService,
FiltersService,
FormColumnsService,

36
packages/nocodb/src/schema/swagger.json

@ -20443,7 +20443,7 @@
"id": "psbv6c6y9qvbu"
}
},
"CalendarDateRange": {
"CalendarRange": {
"description": "Model for Calendar Date Range",
"examples": [
{
@ -20458,7 +20458,7 @@
"type": "object",
"properties": {
"fk_from_column_id": {
"$ref": "#/components/schemas/StringOrNull",
"$ref": "#/components/schemas/Id",
"description": "Foreign Key to Column"
},
"fk_to_column_id": {
@ -22586,6 +22586,31 @@
],
"title": "TextOrNull Model"
},
"CalendarRangeOrNull": {
"description": "Model for CalendarRangeOrNull",
"example": [{
"id": "kvc_2skkg5mi1eb37f",
"fk_from_column_id": "cl_hzos4ghyncqi4k",
"fk_to_column_id": "cl_hzos4ghyncqi4k",
"fk_view_id": "vw_wqs4zheuo5lgdy",
"label": "string"
}],
"oneOf": [
{
"type": "null"
},
{
"type": "array",
"items": {
"$ref": "#/components/schemas/CalendarRange"
}
}
],
"title": "CalendarRangeOrNull Model",
"x-stoplight": {
"id": "p1g7xrgdsn540"
}
},
"StringOrNull": {
"description": "Model for StringOrNull",
"examples": [
@ -23887,11 +23912,8 @@
"description": "Foreign Key to Geo Data Column. Used in creating Map View."
},
"calendar_range": {
"type": "array",
"description": "Calendar Range",
"items": {
"$ref": "#/components/schemas/CalendarDateRange"
}
"description": "Calendar Range or Null",
"$ref": "#/components/schemas/CalendarRangeOrNull"
}
},
"required": [

75
packages/nocodb/src/services/calendars.service.ts

@ -0,0 +1,75 @@
import {Injectable} from '@nestjs/common';
import type {CalendarUpdateReqType, UserType, ViewCreateReqType,} from 'nocodb-sdk';
import {AppEvents, ViewTypes} from 'nocodb-sdk';
import type {NcRequest} from '~/interface/config';
import {AppHooksService} from '~/services/app-hooks/app-hooks.service';
import {validatePayload} from '~/helpers';
import {NcError} from '~/helpers/catchError';
import {CalendarView, View} from '~/models';
@Injectable()
export class CalendarsService {
constructor(private readonly appHooksService: AppHooksService) {
}
async calendarViewGet(param: { calendarViewId: string }) {
return await CalendarView.get(param.calendarViewId);
}
async calendarViewCreate(param: {
tableId: string;
calendar: ViewCreateReqType;
user: UserType;
req: NcRequest;
}) {
-
validatePayload(
'swagger.json#/components/schemas/ViewCreateReq',
param.calendar,
);
const view = await View.insert({
...param.calendar,
// todo: sanitize
fk_model_id: param.tableId,
type: ViewTypes.CALENDAR,
});
this.appHooksService.emit(AppEvents.VIEW_CREATE, {
view,
showAs: 'calendar',
user: param.user,
req: param.req,
});
return view;
}
async calendarViewUpdate(param: {
calendarViewId: string;
calendar: CalendarUpdateReqType;
req: NcRequest;
}) {
validatePayload(
'swagger.json#/components/schemas/CalendarUpdateReq',
param.calendar,
);
const view = await View.get(param.calendarViewId);
if (!view) {
NcError.badRequest('View not found');
}
const res = await CalendarView.update(param.calendarViewId, param.calendar);
this.appHooksService.emit(AppEvents.VIEW_UPDATE, {
view,
showAs: 'calendar',
req: param.req,
});
return res;
}
}
Loading…
Cancel
Save