From 4bf6fd8c5b845000d7847ef271967e79fece4257 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 16 Jun 2022 12:51:26 +0800 Subject: [PATCH 1/2] feat: duration --- .../project/spreadsheet/components/Cell.vue | 4 +- .../spreadsheet/components/EditColumn.vue | 51 ++-- .../spreadsheet/components/EditableCell.vue | 17 +- .../components/cell/DurationCell.vue | 69 +++++ .../components/editColumn/DurationOptions.vue | 126 ++++++++ .../components/editableCell/DurationCell.vue | 126 ++++++++ .../project/spreadsheet/mixins/cell.js | 5 +- packages/nc-gui/helpers/durationHelper.js | 192 ++++++++++++ packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts | 2 +- packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts | 2 +- packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts | 2 +- packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts | 2 +- .../nocodb/src/lib/meta/api/columnApis.ts | 6 + .../integration/common/3e_duration_column.js | 275 ++++++++++++++++++ .../integration/test/pg-restTableOps.js | 3 + .../cypress/integration/test/restTableOps.js | 3 + .../integration/test/xcdb-restTableOps.js | 3 + 17 files changed, 859 insertions(+), 29 deletions(-) create mode 100644 packages/nc-gui/components/project/spreadsheet/components/cell/DurationCell.vue create mode 100644 packages/nc-gui/components/project/spreadsheet/components/editColumn/DurationOptions.vue create mode 100644 packages/nc-gui/components/project/spreadsheet/components/editableCell/DurationCell.vue create mode 100644 packages/nc-gui/helpers/durationHelper.js create mode 100644 scripts/cypress/integration/common/3e_duration_column.js diff --git a/packages/nc-gui/components/project/spreadsheet/components/Cell.vue b/packages/nc-gui/components/project/spreadsheet/components/Cell.vue index 5dd090cedc..76966531a8 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/Cell.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/Cell.vue @@ -17,6 +17,7 @@ + @@ -37,10 +38,11 @@ import BooleanCell from '~/components/project/spreadsheet/components/cell/Boolea import EmailCell from '~/components/project/spreadsheet/components/cell/EmailCell' import RatingCell from '~/components/project/spreadsheet/components/editableCell/RatingCell' import CurrencyCell from '@/components/project/spreadsheet/components/cell/CurrencyCell' +import DurationCell from '@/components/project/spreadsheet/components/cell/DurationCell' export default { name: 'TableCell', - components: { RatingCell, EmailCell, TimeCell, DateTimeCell, DateCell, JsonCell, UrlCell, EditableAttachmentCell, EnumCell, SetListCell, BooleanCell, CurrencyCell }, + components: { RatingCell, EmailCell, TimeCell, DateTimeCell, DateCell, JsonCell, UrlCell, EditableAttachmentCell, EnumCell, SetListCell, BooleanCell, CurrencyCell, DurationCell }, mixins: [cell], props: ['value', 'dbAlias', 'isLocked', 'selected', 'column'], computed: { diff --git a/packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue b/packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue index 15d7c07ac7..03d75d602c 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/EditColumn.vue @@ -172,7 +172,6 @@ :column="newColumn" :meta="meta" /> - - - @@ -572,6 +580,7 @@ import { validateColumnName } from '~/helpers' import RatingOptions from '~/components/project/spreadsheet/components/editColumn/RatingOptions' import CheckboxOptions from '~/components/project/spreadsheet/components/editColumn/CheckboxOptions' import CurrencyOptions from '@/components/project/spreadsheet/components/editColumn/CurrencyOptions' +import DurationOptions from '@/components/project/spreadsheet/components/editColumn/DurationOptions' const columnToValidate = [UITypes.Email, UITypes.URL, UITypes.PhoneNumber] @@ -587,7 +596,8 @@ export default { DlgLabelSubmitCancel, RelationOptions, CustomSelectOptions, - CurrencyOptions + CurrencyOptions, + DurationOptions }, props: { nodes: Object, @@ -617,7 +627,8 @@ export default { UITypes.Lookup, UITypes.Rollup, UITypes.SpecificDBType, - UITypes.Formula + UITypes.Formula, + UITypes.Duration ].includes(this.newColumn && this.newColumn.uidt) }, uiTypes() { @@ -631,7 +642,7 @@ export default { ] }, isEditDisabled() { - return this.editColumn && this.sqlUi === SqliteUi + return this.editColumn && this.sqlUi === SqliteUi && this.column.uidt !== UITypes.Duration }, isSQLite() { return this.sqlUi === SqliteUi diff --git a/packages/nc-gui/components/project/spreadsheet/components/EditableCell.vue b/packages/nc-gui/components/project/spreadsheet/components/EditableCell.vue index 5ce6c7e428..321388584f 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/EditableCell.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/EditableCell.vue @@ -35,6 +35,15 @@ v-on="$listeners" /> + + + + + + + + + + diff --git a/packages/nc-gui/components/project/spreadsheet/components/editColumn/DurationOptions.vue b/packages/nc-gui/components/project/spreadsheet/components/editColumn/DurationOptions.vue new file mode 100644 index 0000000000..d39a198c42 --- /dev/null +++ b/packages/nc-gui/components/project/spreadsheet/components/editColumn/DurationOptions.vue @@ -0,0 +1,126 @@ + + + + + + + diff --git a/packages/nc-gui/components/project/spreadsheet/components/editableCell/DurationCell.vue b/packages/nc-gui/components/project/spreadsheet/components/editableCell/DurationCell.vue new file mode 100644 index 0000000000..d39a198c42 --- /dev/null +++ b/packages/nc-gui/components/project/spreadsheet/components/editableCell/DurationCell.vue @@ -0,0 +1,126 @@ + + + + + + + diff --git a/packages/nc-gui/components/project/spreadsheet/mixins/cell.js b/packages/nc-gui/components/project/spreadsheet/mixins/cell.js index 85fb8c5238..dd900e99e4 100644 --- a/packages/nc-gui/components/project/spreadsheet/mixins/cell.js +++ b/packages/nc-gui/components/project/spreadsheet/mixins/cell.js @@ -67,8 +67,10 @@ export default { }, isCurrency() { return this.uiDatatype === 'Currency' + }, + isDuration() { + return this.uiDatatype === UITypes.Duration } - } } /** @@ -76,6 +78,7 @@ export default { * * @author Naveen MR * @author Pranav C Balan + * @author Wing-Kam Wong * * @license GNU AGPL version 3 or any later version * diff --git a/packages/nc-gui/helpers/durationHelper.js b/packages/nc-gui/helpers/durationHelper.js new file mode 100644 index 0000000000..d958014858 --- /dev/null +++ b/packages/nc-gui/helpers/durationHelper.js @@ -0,0 +1,192 @@ +export const durationOptions = [ + { + id: 0, + title: 'h:mm', + example: '(e.g. 1:23)', + regex: /(\d+)(?::(\d+))?/ + }, { + id: 1, + title: 'h:mm:ss', + example: '(e.g. 3:45, 1:23:40)', + regex: /(\d+)?(?::(\d+))?(?::(\d+))?/ + }, { + id: 2, + title: 'h:mm:ss.s', + example: '(e.g. 3:34.6, 1:23:40.0)', + regex: /(\d+)?(?::(\d+))?(?::(\d+))?(?:.(\d{0,4})?)?/ + }, { + id: 3, + title: 'h:mm:ss.ss', + example: '(e.g. 3.45.67, 1:23:40.00)', + regex: /(\d+)?(?::(\d+))?(?::(\d+))?(?:.(\d{0,4})?)?/ + }, { + id: 4, + title: 'h:mm:ss.sss', + example: '(e.g. 3.45.678, 1:23:40.000)', + regex: /(\d+)?(?::(\d+))?(?::(\d+))?(?:.(\d{0,4})?)?/ + } +] + +// pad zero +// mm && ss +// e.g. 3 -> 03 +// e.g. 12 -> 12 +// sss +// e.g. 1 -> 001 +// e.g. 10 -> 010 +const padZero = (val, isSSS = false) => { + return (val + '').padStart(isSSS ? 3 : 2, '0') +} + +export const convertMS2Duration = (val, durationType) => { + if (val === null || val === undefined) { return val } + // 600.000 s --> 10:00 (10 mins) + const milliseconds = Math.round((val % 1) * 1000) + const centiseconds = Math.round(milliseconds / 10) + const deciseconds = Math.round(centiseconds / 10) + const hours = Math.floor(parseInt(val, 10) / (60 * 60)) + const minutes = Math.floor((parseInt(val, 10) - (hours * 60 * 60)) / 60) + const seconds = parseInt(val, 10) - (hours * 60 * 60) - (minutes * 60) + + if (durationType === 0) { + // h:mm + return `${padZero(hours)}:${padZero(minutes + (seconds >= 30))}` + } else if (durationType === 1) { + // h:mm:ss + return `${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}` + } else if (durationType === 2) { + // h:mm:ss.s + return `${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}.${deciseconds}` + } else if (durationType === 3) { + // h:mm:ss.ss + return `${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}.${padZero(centiseconds)}` + } else if (durationType === 4) { + // h:mm:ss.sss + return `${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}.${padZero(milliseconds, true)}` + } + return val +} + +export const convertDurationToSeconds = (val, durationType) => { + // 10:00 (10 mins) -> 600.000 s + const res = { + _ms: null, + _isValid: true + } + const durationRegex = durationOptions[durationType].regex + if (durationRegex.test(val)) { + let h, mm, ss + const groups = val.match(durationRegex) + if (groups[0] && groups[1] && !groups[2] && !groups[3] && !groups[4]) { + const val = parseInt(groups[1], 10) + if (groups.input.slice(-1) === ':') { + // e.g. 30: + h = groups[1] + mm = 0 + ss = 0 + } else if (durationType === 0) { + // consider it as minutes + // e.g. 360 -> 06:00 + h = Math.floor(val / 60) + mm = Math.floor((val - ((h * 3600)) / 60)) + ss = 0 + } else { + // consider it as seconds + // e.g. 3600 -> 01:00:00 + h = Math.floor(groups[1] / 3600) + mm = Math.floor(groups[1] / 60) % 60 + ss = val % 60 + } + } else if (durationType !== 0 && groups[1] && groups[2] && !groups[3]) { + // 10:10 means mm:ss instead of h:mm + // 10:10:10 means h:mm:ss + h = 0 + mm = groups[1] + ss = groups[2] + } else { + h = groups[1] || 0 + mm = groups[2] || 0 + ss = groups[3] || 0 + } + + if (durationType === 0) { + // h:mm + res._sec = h * 3600 + mm * 60 + } else if (durationType === 1) { + // h:mm:ss + res._sec = h * 3600 + mm * 60 + ss * 1 + } else if (durationType === 2) { + // h:mm:ss.s (deciseconds) + const ds = groups[4] || 0 + const len = Math.log(ds) * Math.LOG10E + 1 | 0 + const ms = ( + // e.g. len = 4: 1234 -> 1, 1456 -> 1 + // e.g. len = 3: 123 -> 1, 191 -> 2 + // e.g. len = 2: 12 -> 1 , 16 -> 2 + len === 4 + ? Math.round(ds / 1000) + : len === 3 + ? Math.round(ds / 100) + : len === 2 + ? Math.round(ds / 10) + // take whatever it is + : ds + ) * 100 + res._sec = h * 3600 + mm * 60 + ss * 1 + ms / 1000 + } else if (durationType === 3) { + // h:mm:ss.ss (centiseconds) + const cs = groups[4] || 0 + const len = Math.log(cs) * Math.LOG10E + 1 | 0 + const ms = ( + // e.g. len = 4: 1234 -> 12, 1285 -> 13 + // e.g. len = 3: 123 -> 12, 128 -> 13 + // check the third digit + len === 4 + ? Math.round(cs / 100) + : len === 3 + ? Math.round(cs / 10) + // take whatever it is + : cs + ) * 10 + res._sec = h * 3600 + mm * 60 + ss * 1 + ms / 1000 + } else if (durationType === 4) { + // h:mm:ss.sss (milliseconds) + let ms = groups[4] || 0 + const len = Math.log(ms) * Math.LOG10E + 1 | 0 + ms = ( + // e.g. 1235 -> 124 + // e.g. 1234 -> 123 + len === 4 + ? Math.round(ms / 10) + // take whatever it is + : ms + ) * 1 + res._sec = h * 3600 + mm * 60 + ss * 1 + ms / 1000 + } + } else { + res._isValid = false + } + return res +} + +/** + * @copyright Copyright (c) 2021, Xgene Cloud Ltd + * + * @author Wing-Kam Wong + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ diff --git a/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts index 8b44d13078..40461614d3 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/MssqlUi.ts @@ -1144,7 +1144,7 @@ export class MssqlUi { colProp.dt = 'double'; break; case 'Duration': - colProp.dt = 'int'; + colProp.dt = 'decimal'; break; case 'Rating': colProp.dt = 'int'; diff --git a/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts index ab1e268818..5c8bc82dba 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/MysqlUi.ts @@ -1036,7 +1036,7 @@ export class MysqlUi { colProp.dt = 'double'; break; case 'Duration': - colProp.dt = 'int'; + colProp.dt = 'decimal'; break; case 'Rating': colProp.dt = 'int'; diff --git a/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts index d49690518f..096ec77ad1 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/PgUi.ts @@ -1660,7 +1660,7 @@ export class PgUi { colProp.dt = 'double precision'; break; case 'Duration': - colProp.dt = 'int8'; + colProp.dt = 'decimal'; break; case 'Rating': colProp.dt = 'smallint'; diff --git a/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts b/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts index 41a2f816f9..5a780c65be 100644 --- a/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts +++ b/packages/nocodb-sdk/src/lib/sqlUi/SqliteUi.ts @@ -853,7 +853,7 @@ export class SqliteUi { colProp.dt = 'double'; break; case 'Duration': - colProp.dt = 'integer'; + colProp.dt = 'decimal'; break; case 'Rating': colProp.dt = 'integer'; diff --git a/packages/nocodb/src/lib/meta/api/columnApis.ts b/packages/nocodb/src/lib/meta/api/columnApis.ts index 2e942107f8..e68aff846a 100644 --- a/packages/nocodb/src/lib/meta/api/columnApis.ts +++ b/packages/nocodb/src/lib/meta/api/columnApis.ts @@ -503,6 +503,12 @@ export async function columnAdd(req: Request, res: Response) { default: { colBody = getColumnPropsFromUIDT(colBody, base); + if (colBody.uidt === UITypes.Duration) { + colBody.dtxp = '20'; + // by default, colBody.dtxs is 2 + // Duration column needs more that that + colBody.dtxs = '4'; + } const tableUpdateBody = { ...table, tn: table.table_name, diff --git a/scripts/cypress/integration/common/3e_duration_column.js b/scripts/cypress/integration/common/3e_duration_column.js new file mode 100644 index 0000000000..331f6d97fc --- /dev/null +++ b/scripts/cypress/integration/common/3e_duration_column.js @@ -0,0 +1,275 @@ +import { mainPage } from "../../support/page_objects/mainPage"; +import { + isTestSuiteActive, +} from "../../support/page_objects/projectConstants"; + +export const genTest = (apiType, dbType) => { + if (!isTestSuiteActive(apiType, dbType)) return; + + describe(`${apiType.toUpperCase()} api - DURATION`, () => { + const tableName = "DurationTable"; + + // to retrieve few v-input nodes from their label + // + const fetchParentFromLabel = (label) => { + cy.get("label").contains(label).parents(".v-input").click(); + }; + + // Run once before test- create table + // + before(() => { + mainPage.tabReset(); + cy.createTable(tableName); + }); + + after(() => { + cy.deleteTable(tableName); + }); + + // Routine to create a new look up column + // + const addDurationColumn = (columnName, durationFormat) => { + // (+) icon at end of column header (to add a new column) + // opens up a pop up window + // + cy.get(".new-column-header").click(); + + // Column name + cy.get(".nc-column-name-input input").clear().type(`${columnName}`); + + // Column data type + cy.get(".nc-ui-dt-dropdown").click(); + cy.getActiveMenu().contains("Duration").click(); + + // Configure Child table & column names + fetchParentFromLabel("Duration Format"); + cy.getActiveMenu().contains(durationFormat).click(); + + // click on Save + cy.get(".nc-col-create-or-edit-card").contains("Save").click(); + + // Verify if column exists. + // + cy.get(`th:contains(${columnName})`).should("exist"); + }; + + // routine to delete column + // + const deleteColumnByName = (columnName) => { + // verify if column exists before delete + cy.get(`th:contains(${columnName})`).should("exist"); + + // delete opiton visible on mouse-over + cy.get(`th:contains(${columnName}) .mdi-menu-down`) + .trigger("mouseover") + .click(); + + // delete/ confirm on pop-up + cy.get(".nc-column-delete").click(); + cy.getActiveModal().find("button:contains(Confirm)").click(); + + // validate if deleted (column shouldnt exist) + cy.get(`th:contains(${columnName})`).should("not.exist"); + }; + + // routine to edit column + // + const editColumnByName = (oldName, newName, newDurationFormat) => { + // verify if column exists before delete + cy.get(`th:contains(${oldName})`).should("exist"); + + // delete opiton visible on mouse-over + cy.get(`th:contains(${oldName}) .mdi-menu-down`) + .trigger("mouseover") + .click(); + + // edit/ save on pop-up + cy.get(".nc-column-edit").click(); + cy.get(".nc-column-name-input input").clear().type(newName); + + // Configure Child table & column names + fetchParentFromLabel("Duration Format"); + cy.getActiveMenu().contains(newDurationFormat).click(); + + cy.get(".nc-col-create-or-edit-card") + .contains("Save") + .click({ force: true }); + + cy.toastWait("Duration column updated successfully"); + + // validate if deleted (column shouldnt exist) + cy.get(`th:contains(${oldName})`).should("not.exist"); + cy.get(`th:contains(${newName})`).should("exist"); + }; + + const addDurationData = (colName, index, cellValue, expectedValue, isNewRow = false) => { + if (isNewRow) { + cy.get(".nc-add-new-row-btn:visible").should("exist"); + cy.wait(500) + cy.get(".nc-add-new-row-btn").click({ force: true }); + } else { + mainPage.getRow(index).find(".nc-row-expand-icon").click({ force: true }); + } + cy.get(".duration-cell-wrapper > input").first().should('exist').type(cellValue); + cy.getActiveModal().find("button").contains("Save row").click({ force: true }); + cy.toastWait("Row updated successfully"); + mainPage.getCell(colName, index).find('input').then(($e) => { + expect($e[0].value).to.equal(expectedValue) + }) + } + + /////////////////////////////////////////////////// + // Test case + { + // Duration: h:mm + it("Duration: h:mm", () => { + addDurationColumn("NC_DURATION_0", "h:mm (e.g. 1:23)"); + addDurationData("NC_DURATION_0", 1, "1:30", "01:30", true); + addDurationData("NC_DURATION_0", 2, "30", "00:30", true); + addDurationData("NC_DURATION_0", 3, "60", "01:00", true); + addDurationData("NC_DURATION_0", 4, "80", "01:20", true); + addDurationData("NC_DURATION_0", 5, "12:34", "12:34", true); + addDurationData("NC_DURATION_0", 6, "15:130", "17:10", true); + addDurationData("NC_DURATION_0", 7, "123123", "2052:03", true); + }); + + it("Duration: Edit Column NC_DURATION_0", () => { + editColumnByName( + "NC_DURATION_0", + "NC_DURATION_EDITED_0", + "h:mm:ss (e.g. 3:45, 1:23:40)" + ); + }); + + it("Duration: Delete column", () => { + deleteColumnByName("NC_DURATION_EDITED_0"); + }); + } + + { + // Duration: h:mm:ss + it("Duration: h:mm:ss", () => { + addDurationColumn("NC_DURATION_1", "h:mm:ss (e.g. 3:45, 1:23:40)"); + addDurationData("NC_DURATION_1", 1, "11:22:33", "11:22:33"); + addDurationData("NC_DURATION_1", 2, "1234", "00:20:34"); + addDurationData("NC_DURATION_1", 3, "50", "00:00:50"); + addDurationData("NC_DURATION_1", 4, "1:1111", "00:19:31"); + addDurationData("NC_DURATION_1", 5, "1:11:1111", "01:29:31"); + addDurationData("NC_DURATION_1", 6, "15:130", "00:17:10"); + addDurationData("NC_DURATION_1", 7, "123123", "34:12:03"); + }); + + it("Duration: Edit Column NC_DURATION_1", () => { + editColumnByName( + "NC_DURATION_1", + "NC_DURATION_EDITED_1", + "h:mm:ss.s (e.g. 3:34.6, 1:23:40.0)" + ); + }); + + it("Duration: Delete column", () => { + deleteColumnByName("NC_DURATION_EDITED_1"); + }); + } + + { + // h:mm:ss.s + it("Duration: h:mm:ss.s", () => { + addDurationColumn("NC_DURATION_2", "h:mm:ss.s (e.g. 3:34.6, 1:23:40.0)"); + addDurationData("NC_DURATION_2", 1, "1234", "00:20:34.0"); + addDurationData("NC_DURATION_2", 2, "12:34", "00:12:34.0"); + addDurationData("NC_DURATION_2", 3, "12:34:56", "12:34:56.0"); + addDurationData("NC_DURATION_2", 4, "12:34:999", "12:50:39.0"); + addDurationData("NC_DURATION_2", 5, "12:999:56", "28:39:56.0"); + addDurationData("NC_DURATION_2", 6, "12:34:56.12", "12:34:56.1"); + addDurationData("NC_DURATION_2", 7, "12:34:56.199", "12:34:56.2"); + }); + + it("Duration: Edit Column NC_DURATION_2", () => { + editColumnByName( + "NC_DURATION_2", + "NC_DURATION_EDITED_2", + "h:mm:ss (e.g. 3:45, 1:23:40)" + ); + }); + + it("Duration: Delete column", () => { + deleteColumnByName("NC_DURATION_EDITED_2"); + }); + } + + { + // h:mm:ss.ss + it("Duration: h:mm:ss.ss", () => { + addDurationColumn("NC_DURATION_3", "h:mm:ss.ss (e.g. 3.45.67, 1:23:40.00)"); + addDurationData("NC_DURATION_3", 1, "1234", "00:20:34.00"); + addDurationData("NC_DURATION_3", 2, "12:34", "00:12:34.00"); + addDurationData("NC_DURATION_3", 3, "12:34:56", "12:34:56.00"); + addDurationData("NC_DURATION_3", 4, "12:34:999", "12:50:39.00"); + addDurationData("NC_DURATION_3", 5, "12:999:56", "28:39:56.00"); + addDurationData("NC_DURATION_3", 6, "12:34:56.12", "12:34:56.12"); + addDurationData("NC_DURATION_3", 7, "12:34:56.199", "12:34:56.20"); + }); + + it("Duration: Edit Column NC_DURATION_3", () => { + editColumnByName( + "NC_DURATION_3", + "NC_DURATION_EDITED_3", + "h:mm:ss.ss (e.g. 3.45.67, 1:23:40.00)" + ); + }); + + it("Duration: Delete column", () => { + deleteColumnByName("NC_DURATION_EDITED_3"); + }); + } + + { + // h:mm:ss.sss + it("Duration: h:mm:ss.sss", () => { + addDurationColumn("NC_DURATION_4", "h:mm:ss.sss (e.g. 3.45.678, 1:23:40.000)"); + addDurationData("NC_DURATION_4", 1, "1234", "00:20:34.000"); + addDurationData("NC_DURATION_4", 2, "12:34", "00:12:34.000"); + addDurationData("NC_DURATION_4", 3, "12:34:56", "12:34:56.000"); + addDurationData("NC_DURATION_4", 4, "12:34:999", "12:50:39.000"); + addDurationData("NC_DURATION_4", 5, "12:999:56", "28:39:56.000"); + addDurationData("NC_DURATION_4", 6, "12:34:56.12", "12:34:56.012"); + addDurationData("NC_DURATION_4", 7, "12:34:56.199", "12:34:56.199"); + }); + + it("Duration: Edit Column NC_DURATION_4", () => { + editColumnByName( + "NC_DURATION_4", + "NC_DURATION_EDITED_4", + "h:mm (e.g. 1:23)" + ); + }); + + it("Duration: Delete column", () => { + deleteColumnByName("NC_DURATION_EDITED_4"); + }); + } + }); +}; + +/** + * @copyright Copyright (c) 2021, Xgene Cloud Ltd + * + * @author Wing-Kam Wong + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ \ No newline at end of file diff --git a/scripts/cypress/integration/test/pg-restTableOps.js b/scripts/cypress/integration/test/pg-restTableOps.js index 3c009b940a..a19ce2a4ca 100644 --- a/scripts/cypress/integration/test/pg-restTableOps.js +++ b/scripts/cypress/integration/test/pg-restTableOps.js @@ -11,6 +11,7 @@ let t3a = require("../common/3a_filter_sort_fields_operations"); let t3b = require("../common/3b_formula_column"); let t3c = require("../common/3c_lookup_column"); let t3d = require("../common/3d_rollup_column"); +let t3e = require("../common/3e_duration_column"); const { setCurrentMode, } = require("../../support/page_objects/projectConstants"); @@ -38,6 +39,7 @@ const nocoTestSuite = (apiType, dbType) => { // t3b.genTest(apiType, dbType); t3c.genTest(apiType, dbType); t3d.genTest(apiType, dbType); + t3e.genTest(apiType, dbType); }; nocoTestSuite("rest", "postgres"); @@ -46,6 +48,7 @@ nocoTestSuite("rest", "postgres"); * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Raju Udava + * @author Wing-Kam Wong * * @license GNU AGPL version 3 or any later version * diff --git a/scripts/cypress/integration/test/restTableOps.js b/scripts/cypress/integration/test/restTableOps.js index 750cf629f1..c0db808f3b 100644 --- a/scripts/cypress/integration/test/restTableOps.js +++ b/scripts/cypress/integration/test/restTableOps.js @@ -11,6 +11,7 @@ let t3a = require("../common/3a_filter_sort_fields_operations"); let t3b = require("../common/3b_formula_column"); let t3c = require("../common/3c_lookup_column"); let t3d = require("../common/3d_rollup_column"); +let t3e = require("../common/3e_duration_column"); const { setCurrentMode, } = require("../../support/page_objects/projectConstants"); @@ -38,6 +39,7 @@ const nocoTestSuite = (apiType, dbType) => { t3b.genTest(apiType, dbType); t3c.genTest(apiType, dbType); t3d.genTest(apiType, dbType); + t3e.genTest(apiType, dbType); }; nocoTestSuite("rest", "mysql"); @@ -46,6 +48,7 @@ nocoTestSuite("rest", "mysql"); * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Raju Udava + * @author Wing-Kam Wong * * @license GNU AGPL version 3 or any later version * diff --git a/scripts/cypress/integration/test/xcdb-restTableOps.js b/scripts/cypress/integration/test/xcdb-restTableOps.js index cc05e6f79a..ac1428f946 100644 --- a/scripts/cypress/integration/test/xcdb-restTableOps.js +++ b/scripts/cypress/integration/test/xcdb-restTableOps.js @@ -11,6 +11,7 @@ let t3a = require("../common/3a_filter_sort_fields_operations"); let t3b = require("../common/3b_formula_column"); let t3c = require("../common/3c_lookup_column"); let t3d = require("../common/3d_rollup_column"); +let t3e = require("../common/3e_duration_column"); const { setCurrentMode, } = require("../../support/page_objects/projectConstants"); @@ -38,6 +39,7 @@ const nocoTestSuite = (apiType, dbType) => { t3b.genTest(apiType, dbType); t3c.genTest(apiType, dbType); t3d.genTest(apiType, dbType); + t3e.genTest(apiType, dbType); }; nocoTestSuite("rest", "xcdb"); @@ -46,6 +48,7 @@ nocoTestSuite("rest", "xcdb"); * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Raju Udava + * @author Wing-Kam Wong * * @license GNU AGPL version 3 or any later version * From e069491d39565714f42b4a46b89f4ec2db549441 Mon Sep 17 00:00:00 2001 From: Wing-Kam Wong Date: Thu, 16 Jun 2022 15:37:38 +0800 Subject: [PATCH 2/2] fix: wrong DurationOptions --- .../components/editColumn/DurationOptions.vue | 156 ++++++------------ 1 file changed, 51 insertions(+), 105 deletions(-) diff --git a/packages/nc-gui/components/project/spreadsheet/components/editColumn/DurationOptions.vue b/packages/nc-gui/components/project/spreadsheet/components/editColumn/DurationOptions.vue index d39a198c42..4386131fd4 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/editColumn/DurationOptions.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/editColumn/DurationOptions.vue @@ -1,126 +1,72 @@ - -