From 338e24f3b7c6cc11c336a89c2dff9561c8c96d5c Mon Sep 17 00:00:00 2001 From: Artem Niehrieiev Date: Fri, 10 Oct 2025 13:04:50 +0000 Subject: [PATCH] added color default widgets --- .../shared-jobs/shared-jobs.service.ts | 81 +++++++++++++++- .../helpers/validators/validation-helper.ts | 12 +++ .../saas-tests/table-widgets-e2e.test.ts | 93 ++++++++++--------- backend/test/utils/create-test-table.ts | 17 +++- 4 files changed, 153 insertions(+), 50 deletions(-) diff --git a/backend/src/entities/shared-jobs/shared-jobs.service.ts b/backend/src/entities/shared-jobs/shared-jobs.service.ts index 4e3abc417..360a493ec 100644 --- a/backend/src/entities/shared-jobs/shared-jobs.service.ts +++ b/backend/src/entities/shared-jobs/shared-jobs.service.ts @@ -66,6 +66,9 @@ export class SharedJobsService { countryCode: new Set(), email: new Set(), url: new Set(), + rgbColor: new Set(), + hexColor: new Set(), + hslColor: new Set(), }; for (const column of columnsArray) { @@ -95,7 +98,15 @@ export class SharedJobsService { case sampleValues.every((value) => this.isValueURL(value)): widgetColumnNames.url.add(column); break; - + case sampleValues.every((value) => this.isValueRgbColor(value)): + widgetColumnNames.rgbColor.add(column); + break; + case sampleValues.every((value) => this.isValueHexColor(value)): + widgetColumnNames.hexColor.add(column); + break; + case sampleValues.every((value) => this.isHslColor(value)): + widgetColumnNames.hslColor.add(column); + break; default: break; } @@ -106,8 +117,20 @@ export class SharedJobsService { const countryCodeColumns = Array.from(widgetColumnNames.countryCode); const emailColumns = Array.from(widgetColumnNames.email); const urlColumns = Array.from(widgetColumnNames.url); + const rgbColorColumns = Array.from(widgetColumnNames.rgbColor); + const hexColorColumns = Array.from(widgetColumnNames.hexColor); + const hslColorColumns = Array.from(widgetColumnNames.hslColor); - const allColumns = [telephoneColumns, uuidColumns, countryCodeColumns, emailColumns, urlColumns]; + const allColumns = [ + telephoneColumns, + uuidColumns, + countryCodeColumns, + emailColumns, + urlColumns, + rgbColorColumns, + hexColorColumns, + hslColorColumns, + ]; if (allColumns.every((columns) => columns.length === 0)) { return; } @@ -172,6 +195,39 @@ export class SharedJobsService { await this._dbContext.tableWidgetsRepository.save(newWidget); } } + if (rgbColorColumns.length) { + for (const column of rgbColorColumns) { + const newWidget = new TableWidgetEntity(); + newWidget.field_name = column; + newWidget.widget_type = WidgetTypeEnum.Color; + newWidget.widget_options = + '// Optional: Specify output format for color values\n// Supported formats: "hex", "hex_hash" (default), "rgb", "hsl"\n// Example configuration:\n\n{\n "format": "rgb" // Will display colors as "#FF5733"\n}\n\n// Format options:\n// - "hex": Display as "FF5733" (no hash)\n// - "hex_hash": Display as "#FF5733" (default)\n// - "rgb": Display as "rgb(255, 87, 51)"\n// - "hsl": Display as "hsl(9, 100%, 60%)"'; + newWidget.settings = savedTableSettings; + await this._dbContext.tableWidgetsRepository.save(newWidget); + } + } + if (hexColorColumns.length) { + for (const column of hexColorColumns) { + const newWidget = new TableWidgetEntity(); + newWidget.field_name = column; + newWidget.widget_type = WidgetTypeEnum.Color; + newWidget.widget_options = + '// Optional: Specify output format for color values\n// Supported formats: "hex", "hex_hash" (default), "rgb", "hsl"\n// Example configuration:\n\n{\n "format": "hex_hash" // Will display colors as "#FF5733"\n}\n\n// Format options:\n// - "hex": Display as "FF5733" (no hash)\n// - "hex_hash": Display as "#FF5733" (default)\n// - "rgb": Display as "rgb(255, 87, 51)"\n// - "hsl": Display as "hsl(9, 100%, 60%)"'; + newWidget.settings = savedTableSettings; + await this._dbContext.tableWidgetsRepository.save(newWidget); + } + } + if (hslColorColumns.length) { + for (const column of hslColorColumns) { + const newWidget = new TableWidgetEntity(); + newWidget.field_name = column; + newWidget.widget_type = WidgetTypeEnum.Color; + newWidget.widget_options = + '// Optional: Specify output format for color values\n// Supported formats: "hex", "hex_hash" (default), "rgb", "hsl"\n// Example configuration:\n\n{\n "format": "hsl" // Will display colors as "#FF5733"\n}\n\n// Format options:\n// - "hex": Display as "FF5733" (no hash)\n// - "hex_hash": Display as "#FF5733" (default)\n// - "rgb": Display as "rgb(255, 87, 51)"\n// - "hsl": Display as "hsl(9, 100%, 60%)"'; + newWidget.settings = savedTableSettings; + await this._dbContext.tableWidgetsRepository.save(newWidget); + } + } } } @@ -179,6 +235,27 @@ export class SharedJobsService { return ValidationHelper.isValidPhoneNumber(String(value)); } + private isValueRgbColor(value: unknown): boolean { + if (typeof value !== 'string') { + return false; + } + return ValidationHelper.isValidRgbColor(value); + } + + private isValueHexColor(value: unknown): boolean { + if (typeof value !== 'string') { + return false; + } + return ValidationHelper.isValidHexColor(value); + } + + private isHslColor(value: unknown): boolean { + if (typeof value !== 'string') { + return false; + } + return ValidationHelper.isValidHslColor(value); + } + private isValueUUID(value: unknown): boolean { if (typeof value !== 'string') { return false; diff --git a/backend/src/helpers/validators/validation-helper.ts b/backend/src/helpers/validators/validation-helper.ts index cc89a4ed3..e0bd79764 100644 --- a/backend/src/helpers/validators/validation-helper.ts +++ b/backend/src/helpers/validators/validation-helper.ts @@ -25,6 +25,18 @@ export class ValidationHelper { return validator.isMobilePhone(phoneNumber, 'any', { strictMode: false }); } + public static isValidRgbColor(rgbColor: string): boolean { + return validator.isRgbColor(rgbColor); + } + + public static isValidHexColor(hexColor: string): boolean { + return validator.isHexColor(hexColor); + } + + public static isValidHslColor(hslColor: string): boolean { + return validator.isHSL(hslColor); + } + public static isValidJSON(jsonString: string): boolean { if (typeof jsonString !== 'string') { return false; diff --git a/backend/test/ava-tests/saas-tests/table-widgets-e2e.test.ts b/backend/test/ava-tests/saas-tests/table-widgets-e2e.test.ts index c6fe8e718..e96bb041d 100644 --- a/backend/test/ava-tests/saas-tests/table-widgets-e2e.test.ts +++ b/backend/test/ava-tests/saas-tests/table-widgets-e2e.test.ts @@ -97,57 +97,60 @@ test.serial(`${currentTest} should return empty array, table widgets not created t.is(getTableWidgetsRO.length, 2); }); -test.serial(`${currentTest} should return automatically created widgets array, if table contains specific data`, async (t) => { - const connectionToTestDB = getTestData(mockFactory).connectionToPostgres; - const firstUserToken = (await registerUserAndReturnUserInfo(app)).token; - const { testTableName } = await createTestTable(connectionToTestDB, undefined, undefined, false, true); - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(connectionToTestDB) - .set('Cookie', firstUserToken) - .set('masterpwd', 'ahalaimahalai') - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); +test.serial( + `${currentTest} should return automatically created widgets array, if table contains specific data`, + async (t) => { + const connectionToTestDB = getTestData(mockFactory).connectionToPostgres; + const firstUserToken = (await registerUserAndReturnUserInfo(app)).token; + const { testTableName } = await createTestTable(connectionToTestDB, undefined, undefined, false, true); + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(connectionToTestDB) + .set('Cookie', firstUserToken) + .set('masterpwd', 'ahalaimahalai') + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); - t.is(createdConnection.status, 201); - const connectionId = JSON.parse(createdConnection.text).id; + t.is(createdConnection.status, 201); + const connectionId = JSON.parse(createdConnection.text).id; - // wait 3 seconds to let the system create widgets automatically in the background + // wait 3 seconds to let the system create widgets automatically in the background - await new Promise((resolve) => setTimeout(resolve, 3000)); + await new Promise((resolve) => setTimeout(resolve, 3000)); - const getTableWidgets = await request(app.getHttpServer()) - .get(`/widgets/${connectionId}?tableName=${testTableName}`) - .set('Content-Type', 'application/json') - .set('Cookie', firstUserToken) - .set('Accept', 'application/json'); + const getTableWidgets = await request(app.getHttpServer()) + .get(`/widgets/${connectionId}?tableName=${testTableName}`) + .set('Content-Type', 'application/json') + .set('Cookie', firstUserToken) + .set('Accept', 'application/json'); - const foundRows = await request(app.getHttpServer()) - .get(`/table/rows/${connectionId}?tableName=${testTableName}`) - .set('Cookie', firstUserToken) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(foundRows.status, 200); - const foundRowsRO = JSON.parse(foundRows.text); - console.log('🚀 ~ foundRowsRO:', foundRowsRO); + const foundRows = await request(app.getHttpServer()) + .get(`/table/rows/${connectionId}?tableName=${testTableName}`) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(foundRows.status, 200); + const foundRowsRO = JSON.parse(foundRows.text); - const getTableWidgetsRO = JSON.parse(getTableWidgets.text); - t.is(typeof getTableWidgetsRO, 'object'); - t.is(getTableWidgets.status, 200); - console.log('🚀 ~ getTableWidgetsRO:', getTableWidgetsRO); - t.is(getTableWidgetsRO.length, 5); - const phoneWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.Phone); - t.truthy(phoneWidget); - const uuidWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.UUID); - t.truthy(uuidWidget); - const countryCodeWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.Country); - t.truthy(countryCodeWidget); - const emailWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.String); - t.truthy(emailWidget); - t.is(emailWidget.widget_params.includes('"validate": "isEmail"'), true); - const urlWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.URL); - t.truthy(urlWidget); -}); + const getTableWidgetsRO = JSON.parse(getTableWidgets.text); + t.is(typeof getTableWidgetsRO, 'object'); + t.is(getTableWidgets.status, 200); + t.is(getTableWidgetsRO.length, 8); + const phoneWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.Phone); + t.truthy(phoneWidget); + const uuidWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.UUID); + t.truthy(uuidWidget); + const countryCodeWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.Country); + t.truthy(countryCodeWidget); + const emailWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.String); + t.truthy(emailWidget); + t.is(emailWidget.widget_params.includes('"validate": "isEmail"'), true); + const urlWidget = getTableWidgetsRO.find((w) => w.widget_type === WidgetTypeEnum.URL); + t.truthy(urlWidget); + const foundColorWIdgets = getTableWidgetsRO.filter((w) => w.widget_type === WidgetTypeEnum.Color); + t.is(foundColorWIdgets.length, 3); + }, +); test.serial(`${currentTest} should return array of table widgets for table`, async (t) => { const { token } = await registerUserAndReturnUserInfo(app); diff --git a/backend/test/utils/create-test-table.ts b/backend/test/utils/create-test-table.ts index 817976f94..6e3873fd3 100644 --- a/backend/test/utils/create-test-table.ts +++ b/backend/test/utils/create-test-table.ts @@ -68,15 +68,23 @@ export async function createTestTable( }); } - // telephone - // uuid - // countryCode + // telephoneColumns, + // uuidColumns, + // countryCodeColumns, + // urlColumns, + // rgbColorColumns, + // hexColorColumns, + // hslColorColumns, + // email columns already exists as some of the test columns if (withWidgetsData) { await Knex.schema.table(testTableName, function (table) { table.string('telephone'); table.string('uuid'); table.string('countryCode'); table.string('url'); + table.string('rgbColor'); + table.string('hexColor'); + table.string('hslColor'); }); } @@ -87,6 +95,9 @@ export async function createTestTable( widgetsDataObject.uuid = faker.string.uuid(); widgetsDataObject.countryCode = faker.location.countryCode(); widgetsDataObject.url = faker.internet.url(); + widgetsDataObject.rgbColor = faker.color.rgb(); + widgetsDataObject.hexColor = faker.color.rgb({ format: 'hex', casing: 'lower' }); + widgetsDataObject.hslColor = faker.color.hsl({ format: 'css' }); } if (i === 0 || i === testEntitiesSeedsCount - 21 || i === testEntitiesSeedsCount - 5) { await Knex(testTableName).insert({