diff --git a/packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue b/packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue index 94969896ed..1f37ed82a3 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue @@ -132,7 +132,12 @@ - + @@ -644,9 +649,18 @@ export default { if (this.newColumn.uidt === 'Formula' && this.$refs.formula) { return await this.$refs.formula.save(); } - - this.newColumn.table_name = this.nodes.table_name; - this.newColumn.title = this.newColumn.column_name; + this.newColumn.table_name = this.nodes.table_name + this.newColumn.title = this.newColumn.column_name + + if (this.isSelect && this.$refs.customselect) { + if (this.column) { + await this.$refs.customselect.update() + } else { + await this.$refs.customselect.save() + } + await this.$emit('saved') + return this.$emit('close') + } if (this.editColumn) { await this.$api.dbTableColumn.update(this.column.id, this.newColumn); @@ -678,18 +692,17 @@ export default { if (this.newColumn.uidt !== UITypes.ID) { this.newColumn.primaryKey = false; } - this.newColumn.ai = false; - this.newColumn.cdf = null; - this.newColumn.un = false; - this.newColumn.dtxp = this.sqlUi.getDefaultLengthForDatatype(this.newColumn.dt); - this.newColumn.dtxs = this.sqlUi.getDefaultScaleForDatatype(this.newColumn.dt); - - this.newColumn.dtx = 'specificType'; - - const selectTypes = [UITypes.MultiSelect, UITypes.SingleSelect]; - if (this.column && selectTypes.includes(this.newColumn.uidt) && selectTypes.includes(this.column.uidt)) { - this.newColumn.dtxp = this.column.dtxp; - } + this.newColumn.ai = false + this.newColumn.cdf = null + this.newColumn.un = false + this.newColumn.dtxp = this.sqlUi.getDefaultLengthForDatatype( + this.newColumn.dt + ) + this.newColumn.dtxs = this.sqlUi.getDefaultScaleForDatatype( + this.newColumn.dt + ) + + this.newColumn.dtx = 'specificType' if (this.isCurrency) { if (this.column?.uidt === UITypes.Currency) { @@ -722,11 +735,6 @@ export default { this.newColumn.dtxp = this.sqlUi.getDefaultLengthForDatatype(this.newColumn.dt); this.newColumn.dtxs = this.sqlUi.getDefaultScaleForDatatype(this.newColumn.dt); - const selectTypes = [UITypes.MultiSelect, UITypes.SingleSelect]; - if (this.column && selectTypes.includes(this.newColumn.uidt) && selectTypes.includes(this.column.uidt)) { - this.newColumn.dtxp = this.column.dtxp; - } - if (columnToValidate.includes(this.newColumn.uidt)) { this.newColumn.meta = { validate: this.newColumn.meta && this.newColumn.meta.validate, diff --git a/packages/nc-gui/components/project/spreadsheet/components/editColumn/CustomSelectOptions.vue b/packages/nc-gui/components/project/spreadsheet/components/editColumn/CustomSelectOptions.vue index ed0c393484..a9af26d0d8 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/editColumn/CustomSelectOptions.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/editColumn/CustomSelectOptions.vue @@ -1,68 +1,173 @@ diff --git a/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts index 5c8bc82dba..480f5f2eae 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts @@ -254,10 +254,10 @@ export class MysqlUi { return ''; case 'enum': - return "'a','b'"; + return ''; case 'set': - return "'a','b'"; + return ''; case 'geometry': return ''; @@ -976,10 +976,10 @@ export class MysqlUi { colProp.dtxp = 1; break; case 'MultiSelect': - colProp.dt = 'text'; + colProp.dt = 'set'; break; case 'SingleSelect': - colProp.dt = 'text'; + colProp.dt = 'enum'; break; case 'Collaborator': colProp.dt = 'varchar'; diff --git a/packages/nocodb/src/lib/meta/api/columnApis.ts b/packages/nocodb/src/lib/meta/api/columnApis.ts index 1627e868b7..4c329a69ad 100644 --- a/packages/nocodb/src/lib/meta/api/columnApis.ts +++ b/packages/nocodb/src/lib/meta/api/columnApis.ts @@ -509,6 +509,23 @@ export async function columnAdd(req: Request, res: Response) { // Duration column needs more that that colBody.dtxs = '4'; } + + if (colBody.uidt === UITypes.SingleSelect) { + colBody.dtxp = (colBody?.options.length) + ? `${colBody.options.map(o => `'${o.title.replace(/'/gi, '\'\'')}'`).join(',')}` + : ''; + } else if (colBody.uidt === UITypes.MultiSelect){ + colBody.dtxp = (colBody?.options.length) + ? `${colBody.options.map((o) => { + if(o.title.includes(',')) { + NcError.badRequest('Illegal char(\',\') for MultiSelect'); + throw new Error(''); + } + return `'${o.title.replace(/'/gi, '\'\'')}'`; + }).join(',')}` + : ''; + } + const tableUpdateBody = { ...table, tn: table.table_name, @@ -540,13 +557,6 @@ export async function columnAdd(req: Request, res: Response) { const insertedColumnMeta = columns.find((c) => c.cn === colBody.column_name) || ({} as any); - if ( - colBody.uidt === UITypes.SingleSelect || - colBody.uidt === UITypes.MultiSelect - ) { - insertedColumnMeta.dtxp = colBody.dtxp; - } - await Column.insert({ ...colBody, ...insertedColumnMeta, @@ -655,6 +665,23 @@ export async function columnUpdate(req: Request, res: Response) { ); } else { colBody = getColumnPropsFromUIDT(colBody, base); + + if (colBody.uidt === UITypes.SingleSelect) { + colBody.dtxp = (colBody?.options.length) + ? `${colBody.options.map(o => `'${o.title.replace(/'/gi, '\'\'')}'`).join(',')}` + : ''; + } else if (colBody.uidt === UITypes.MultiSelect){ + colBody.dtxp = (colBody?.options.length) + ? `${colBody.options.map((o) => { + if(o.title.includes(',')) { + NcError.badRequest('Illegal char(\',\') for MultiSelect'); + throw new Error(''); + } + return `'${o.title.replace(/'/gi, '\'\'')}'`; + }).join(',')}` + : ''; + } + const tableUpdateBody = { ...table, tn: table.table_name, @@ -706,12 +733,12 @@ export async function columnUpdate(req: Request, res: Response) { ), }; - const sqlMgr = await ProjectMgrv2.getSqlMgr({ id: base.project_id }); - await sqlMgr.sqlOpPlus(base, 'tableUpdate', tableUpdateBody); - - await Column.update(req.params.columnId, { + const sqlMgr = await ProjectMgrv2.getSqlMgr({ id: base.project_id }); + await sqlMgr.sqlOpPlus(base, 'tableUpdate', tableUpdateBody); + + await Column.update(req.params.columnId, { ...colBody, - }); + }); } Audit.insert({ project_id: base.project_id, diff --git a/packages/nocodb/src/lib/meta/helpers/populateSamplePayload.ts b/packages/nocodb/src/lib/meta/helpers/populateSamplePayload.ts index c14006e222..0f0a068a22 100644 --- a/packages/nocodb/src/lib/meta/helpers/populateSamplePayload.ts +++ b/packages/nocodb/src/lib/meta/helpers/populateSamplePayload.ts @@ -4,8 +4,7 @@ import { RelationTypes, UITypes } from 'nocodb-sdk'; import Model from '../../models/Model'; import LinkToAnotherRecordColumn from '../../models/LinkToAnotherRecordColumn'; import LookupColumn from '../../models/LookupColumn'; -import MultiSelectColumn from '../../models/MultiSelectColumn'; -import SingleSelectColumn from '../../models/SingleSelectColumn'; +import SelectOption from '../../models/SelectOption'; export default async function populateSamplePayload( viewOrModel: View | Model, @@ -105,7 +104,7 @@ async function getSampleColumnValue(column: Column): Promise { break; case UITypes.MultiSelect: { - const colOpt = await column.getColOptions(); + const colOpt = await column.getColOptions(); return ( colOpt?.[0]?.title || column?.dtxp?.split(',')?.[0]?.replace(/^['"]|['"]$/g, '') @@ -114,7 +113,7 @@ async function getSampleColumnValue(column: Column): Promise { break; case UITypes.SingleSelect: { - const colOpt = await column.getColOptions(); + const colOpt = await column.getColOptions(); return ( colOpt?.[0]?.title || column?.dtxp?.split(',')?.[0]?.replace(/^['"]|['"]$/g, '') diff --git a/packages/nocodb/src/lib/models/Column.ts b/packages/nocodb/src/lib/models/Column.ts index 5589888ef3..2fbad1377c 100644 --- a/packages/nocodb/src/lib/models/Column.ts +++ b/packages/nocodb/src/lib/models/Column.ts @@ -2,7 +2,7 @@ import FormulaColumn from './FormulaColumn'; import LinkToAnotherRecordColumn from './LinkToAnotherRecordColumn'; import LookupColumn from './LookupColumn'; import RollupColumn from './RollupColumn'; -import SingleSelectColumn from './SingleSelectColumn'; +import SelectOption from './SelectOption'; import MultiSelectColumn from './MultiSelectColumn'; import Model from './Model'; import NocoCache from '../cache/NocoCache'; @@ -232,26 +232,54 @@ export default class Column implements ColumnType { break; } case UITypes.MultiSelect: { - for (const option of column.dtxp?.split(',') || []) { - await MultiSelectColumn.insert( - { - fk_column_id: colId, - title: option, - }, - ncMeta - ); + if (column.dt === 'set' && !column.altered) { + for (const [i, option] of column.dtxp?.split(',').entries() || [].entries()) { + await SelectOption.insert( + { + fk_column_id: colId, + title: option, + order: i + 1 + }, + ncMeta + ); + } + } else { + for (const [i, option] of column.options.entries() || [].entries()) { + await SelectOption.insert( + { + ...option, + fk_column_id: colId, + order: i + 1 + }, + ncMeta + ); + } } break; } case UITypes.SingleSelect: { - for (const option of column.dtxp?.split(',') || []) { - await SingleSelectColumn.insert( - { - fk_column_id: colId, - title: option, - }, - ncMeta - ); + if (column.dt === 'enum' && !column.altered) { + for (const [i, option] of column.dtxp?.split(',').entries() || [].entries()) { + await SelectOption.insert( + { + fk_column_id: colId, + title: option, + order: i + 1 + }, + ncMeta + ); + } + } else { + for (const [i, option] of column.options.entries() || [].entries()) { + await SelectOption.insert( + { + ...option, + fk_column_id: colId, + order: i + 1 + }, + ncMeta + ); + } } break; } @@ -322,10 +350,10 @@ export default class Column implements ColumnType { res = await LinkToAnotherRecordColumn.read(this.id, ncMeta); break; case UITypes.MultiSelect: - res = await MultiSelectColumn.get(this.id, ncMeta); + res = await SelectOption.read(this.id, ncMeta); break; case UITypes.SingleSelect: - res = await SingleSelectColumn.get(this.id, ncMeta); + res = await SelectOption.read(this.id, ncMeta); break; case UITypes.Formula: res = await FormulaColumn.read(this.id, ncMeta); @@ -772,10 +800,11 @@ export default class Column implements ColumnType { await ncMeta.metaDelete(null, null, MetaTable.COL_SELECT_OPTIONS, { fk_column_id: colId, }); + await NocoCache.deepDel( CacheScope.COL_SELECT_OPTION, - `${CacheScope.COL_SELECT_OPTION}:${colId}`, - CacheDelDirection.CHILD_TO_PARENT + `${CacheScope.COL_SELECT_OPTION}:${colId}:list`, + CacheDelDirection.PARENT_TO_CHILD ); break; } diff --git a/packages/nocodb/src/lib/models/MultiSelectColumn.ts b/packages/nocodb/src/lib/models/SelectOption.ts similarity index 64% rename from packages/nocodb/src/lib/models/MultiSelectColumn.ts rename to packages/nocodb/src/lib/models/SelectOption.ts index 1fa7c4451a..53a74800e5 100644 --- a/packages/nocodb/src/lib/models/MultiSelectColumn.ts +++ b/packages/nocodb/src/lib/models/SelectOption.ts @@ -2,26 +2,25 @@ import Noco from '../Noco'; import NocoCache from '../cache/NocoCache'; import { CacheGetType, CacheScope, MetaTable } from '../utils/globals'; -export default class MultiSelectColumn { +export default class SelectOption { title: string; fk_column_id: string; + color: string; + order: number; - constructor(data: Partial) { + constructor(data: Partial) { Object.assign(this, data); } public static async insert( - data: Partial, + data: Partial, ncMeta = Noco.ncMeta ) { const { id } = await ncMeta.metaInsert2( null, null, MetaTable.COL_SELECT_OPTIONS, - { - fk_column_id: data.fk_column_id, - title: data.title, - } + data ); await NocoCache.appendToList( @@ -36,7 +35,7 @@ export default class MultiSelectColumn { public static async get( selectOptionId: string, ncMeta = Noco.ncMeta - ): Promise { + ): Promise { let data = selectOptionId && (await NocoCache.get( @@ -55,33 +54,52 @@ export default class MultiSelectColumn { data ); } - return data && new MultiSelectColumn(data); + return data && new SelectOption(data); } - public static async read(columnId: string, ncMeta = Noco.ncMeta) { + public static async read(fk_column_id: string, ncMeta = Noco.ncMeta) { let options = await NocoCache.getList(CacheScope.COL_SELECT_OPTION, [ - columnId, + fk_column_id ]); if (!options.length) { options = await ncMeta.metaList2( null, //, null, //model.db_alias, MetaTable.COL_SELECT_OPTIONS, - { condition: { fk_column_id: columnId } } + { condition: { fk_column_id } } ); await NocoCache.setList( CacheScope.COL_SELECT_OPTION, - [columnId], - options + [fk_column_id], + options.map(({created_at, updated_at, ...others}) => others) ); } return options?.length ? { - options: options.map((c) => new MultiSelectColumn(c)), + options: options.map(({created_at, updated_at, ...c}) => new SelectOption(c)) } : null; } + public static async find( + fk_column_id: string, + title: string, + ncMeta = Noco.ncMeta + ): Promise { + + let data = await ncMeta.metaGet2( + null, + null, + MetaTable.COL_SELECT_OPTIONS, + { + fk_column_id, + title + } + ); + + return data && new SelectOption(data); + } + id: string; } diff --git a/packages/nocodb/src/lib/models/SingleSelectColumn.ts b/packages/nocodb/src/lib/models/SingleSelectColumn.ts deleted file mode 100644 index ec85d94942..0000000000 --- a/packages/nocodb/src/lib/models/SingleSelectColumn.ts +++ /dev/null @@ -1,87 +0,0 @@ -import Noco from '../Noco'; -import NocoCache from '../cache/NocoCache'; -import { CacheGetType, CacheScope, MetaTable } from '../utils/globals'; - -export default class SingleSelectColumn { - title: string; - fk_column_id: string; - - constructor(data: Partial) { - Object.assign(this, data); - } - - public static async insert( - data: Partial, - ncMeta = Noco.ncMeta - ) { - const { id } = await ncMeta.metaInsert2( - null, - null, - MetaTable.COL_SELECT_OPTIONS, - { - fk_column_id: data.fk_column_id, - title: data.title, - } - ); - - await NocoCache.appendToList( - CacheScope.COL_SELECT_OPTION, - [data.fk_column_id], - `${CacheScope.COL_SELECT_OPTION}:${id}` - ); - - return this.get(id, ncMeta); - } - - public static async get( - selectOptionId: string, - ncMeta = Noco.ncMeta - ): Promise { - let data = - selectOptionId && - (await NocoCache.get( - `${CacheScope.COL_SELECT_OPTION}:${selectOptionId}`, - CacheGetType.TYPE_OBJECT - )); - if (!data) { - data = await ncMeta.metaGet2( - null, - null, - MetaTable.COL_SELECT_OPTIONS, - selectOptionId - ); - await NocoCache.set( - `${CacheScope.COL_SELECT_OPTION}:${selectOptionId}`, - data - ); - } - return data && new SingleSelectColumn(data); - } - - public static async read(columnId: string, ncMeta = Noco.ncMeta) { - let options = await NocoCache.getList(CacheScope.COL_SELECT_OPTION, [ - columnId, - ]); - if (!options.length) { - options = await ncMeta.metaList2( - null, //, - null, //model.db_alias, - MetaTable.COL_SELECT_OPTIONS, - { condition: { fk_column_id: columnId } } - ); - await NocoCache.setList( - CacheScope.COL_SELECT_OPTION, - [columnId], - options - ); - } - - return options?.length - ? { - options: options.map((c) => new SingleSelectColumn(c)), - } - : null; - } - - id: string; -}