Repository: ever-co/ever-gauzy
Pull request #8933: [Feat] Alter "TimeSlot" Entity Migration
Author: login: rahul-rocket
Created: 2025-05-02T08:38:55Z | Merged: 2025-05-02T08:47:48Z

# PR

- [x] Have you followed the [contributing guidelines](https://github.com/ever-co/ever-gauzy/blob/master/.github/CONTRIBUTING.md)?
- [x] Have you explained what your changes do, and why they add value?

**Please note: we will close your PR without comment if you do not check the boxes above and provide ALL requested information.**

---



Diff:
diff --git a/packages/core/src/lib/database/migration-executor.ts b/packages/core/src/lib/database/migration-executor.ts
index 92c1131e915..f1cec25dc28 100644
--- a/packages/core/src/lib/database/migration-executor.ts
+++ b/packages/core/src/lib/database/migration-executor.ts
@@ -14,29 +14,28 @@ import { isDatabaseType, isSqliteDB } from './../core/utils';
  * @description
  * Run pending database migrations. See [TypeORM migration docs](https://typeorm.io/#/migrations)
  *
- * @param pluginConfig
+ * @param pluginConfig - Partial application plugin config
  */
-export async function runDatabaseMigrations(pluginConfig: Partial<ApplicationPluginConfig>) {
+export async function runDatabaseMigrations(pluginConfig: Partial<ApplicationPluginConfig>): Promise<void> {
 	const config = await registerPluginConfig(pluginConfig);
-	const dataSource: DataSource = await establishDatabaseConnection(config);
+	const dataSource = await initializeDatabaseConnection(config);
 
 	try {
 		const migrations = await dataSource.runMigrations({ transaction: 'each' });
+
 		if (isNotEmpty(migrations)) {
-			for (const migration of migrations) {
+			migrations.forEach((migration) => {
 				console.log(chalk.green(`Migration ${migration.name} has been run successfully!`));
-			}
+			});
 		} else {
 			console.log(chalk.yellow(`There are no pending migrations to run.`));
 		}
 	} catch (error) {
-		if (dataSource) await closeConnection(dataSource);
-
-		console.log(chalk.black.bgRed('Error during migration run:'));
+		console.error(chalk.black.bgRed('Error during migration run:'));
 		console.error(error);
 		process.exit(1);
 	} finally {
-		await closeConnection(dataSource);
+		await shutdownDatabaseConnection(dataSource);
 	}
 }
 
@@ -44,54 +43,45 @@ export async function runDatabaseMigrations(pluginConfig: Partial<ApplicationPlu
  * @description
  * Reverts last applied database migration. See [TypeORM migration docs](https://typeorm.io/#/migrations)
  *
- * @param pluginConfig
+ * @param pluginConfig - Partial application plugin config
  */
-export async function revertLastDatabaseMigration(pluginConfig: Partial<ApplicationPluginConfig>) {
+export async function revertLastDatabaseMigration(pluginConfig: Partial<ApplicationPluginConfig>): Promise<void> {
 	const config = await registerPluginConfig(pluginConfig);
-	const connection = await establishDatabaseConnection(config);
+	const dataSource = await initializeDatabaseConnection(config);
 
 	try {
-		await connection.undoLastMigration({ transaction: 'each' });
-		console.log(chalk.green(`Migration has been reverted successfully!`));
+		await dataSource.undoLastMigration({ transaction: 'each' });
+		console.log(chalk.green('Last migration has been reverted successfully!'));
 	} catch (error) {
-		if (connection) await closeConnection(connection);
-
-		console.log(chalk.black.bgRed('Error during migration revert:'));
+		console.error(chalk.black.bgRed('Error during migration revert:'));
 		console.error(error);
 		process.exit(1);
 	} finally {
-		await closeConnection(connection);
+		await shutdownDatabaseConnection(dataSource);
 	}
 }
 
 /**
  * @description
- * Generates a new migration file with sql needs to be executed to update schema.
+ * Generates a new migration file with SQL required to update the schema.
  *
- * @param pluginConfig
+ * @param pluginConfig - Partial application plugin configuration
+ * @param options - Migration generation options including name and output directory
  */
-export async function generateMigration(pluginConfig: Partial<ApplicationPluginConfig>, options: IMigrationOptions) {
+export async function generateMigration(
+	pluginConfig: Partial<ApplicationPluginConfig>,
+	options: IMigrationOptions
+): Promise<void> {
 	if (!options.name) {
 		console.log(chalk.yellow('Migration name must be required.Please specify migration name!'));
 		return;
 	}
 	const config = await registerPluginConfig(pluginConfig);
+	const dataSource = await initializeDatabaseConnection(config);
+	const directory = resolveMigrationDirectory(options, config);
 
-	let directory = options.dir;
-	// if directory is not set then try to open plugin config and find default path there
-	if (!directory) {
-		try {
-			directory = config.dbConnectionOptions['cli']
-				? config.dbConnectionOptions['cli']['migrationsDir']
-				: undefined;
-		} catch (err) {
-			console.log('Error while finding migration directory', err);
-		}
-	}
-
-	const connection = await establishDatabaseConnection(config);
 	try {
-		const sqlInMemory = await connection.driver.createSchemaBuilder().log();
+		const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
 		const upSqls: string[] = [];
 		const downSqls: string[] = [];
 
@@ -119,7 +109,7 @@ export async function generateMigration(pluginConfig: Partial<ApplicationPluginC
 			/**
 			 *  Gets contents of the migration file.
 			 */
-			const fileContent = getTemplate(connection, options.name as any, timestamp, upSqls, downSqls.reverse());
+			const fileContent = getTemplate(dataSource, options.name as any, timestamp, upSqls, downSqls.reverse());
 
 			const filename = timestamp + '-' + options.name + '.ts';
 			const outputPath = directory ? path.join(directory, filename) : path.join(process.cwd(), filename);
@@ -139,122 +129,131 @@ export async function generateMigration(pluginConfig: Partial<ApplicationPluginC
 			);
 		}
 	} catch (error) {
-		if (connection) await closeConnection(connection);
-
-		console.log(chalk.black.bgRed('Error during migration generation:'));
+		console.error(chalk.red('❌ Error during migration generation:'));
 		console.error(error);
 		process.exit(1);
 	} finally {
-		await closeConnection(connection);
+		await shutdownDatabaseConnection(dataSource);
+	}
+}
+
+/**
+ * Resolves the directory where migration files should be generated.
+ *
+ * @param options - Migration options that may include a `dir` path
+ * @param config - Plugin configuration containing possible CLI migration settings
+ * @returns The resolved directory path or `undefined` if not found
+ */
+export function resolveMigrationDirectory(
+	options: IMigrationOptions,
+	config: Partial<ApplicationPluginConfig>
+): string | undefined {
+	if (options.dir) {
+		return options.dir;
+	}
+
+	try {
+		return config.dbConnectionOptions && config.dbConnectionOptions['cli']
+			? config.dbConnectionOptions['cli']['migrationsDir']
+			: undefined;
+	} catch (err) {
+		console.error(chalk.red('❌ Error while resolving migration directory:'), err);
+		return undefined;
 	}
 }
 
 /**
  * @description
- * Create a new blank migration file to be executed to create/update schema.
+ * Creates a new blank migration file to be used for schema changes.
  *
- * @param pluginConfig
+ * @param pluginConfig - Partial application plugin configuration
+ * @param options - Migration creation options including name and optional directory
  */
-export async function createMigration(pluginConfig: Partial<ApplicationPluginConfig>, options: IMigrationOptions) {
+export async function createMigration(
+	pluginConfig: Partial<ApplicationPluginConfig>,
+	options: IMigrationOptions
+): Promise<void> {
 	if (!options.name) {
-		console.log(chalk.yellow('Migration name must be required.Please specify migration name!'));
+		console.log(chalk.yellow('Migration name is required. Please specify a name using "--name".'));
 		return;
 	}
-	const config = await registerPluginConfig(pluginConfig);
 
-	let directory = options.dir;
-	// if directory is not set then try to open plugin config and find default path there
-	if (!directory) {
-		try {
-			directory = config.dbConnectionOptions['cli']
-				? config.dbConnectionOptions['cli']['migrationsDir']
-				: undefined;
-		} catch (err) {
-			console.log('Error while finding migration directory', err);
-		}
-	}
+	const config = await registerPluginConfig(pluginConfig);
+	const dataSource = await initializeDatabaseConnection(config);
+	const directory = resolveMigrationDirectory(options, config);
 
-	const connection = await establishDatabaseConnection(config);
 	try {
-		const timestamp = new Date().getTime();
-		/**
-		 *  Gets contents of the migration file.
-		 */
-		const fileContent = getTemplate(connection, options.name as any, timestamp, [], []);
-
-		const filename = timestamp + '-' + options.name + '.ts';
+		const timestamp = Date.now();
+		const fileContent = getTemplate(dataSource, options.name, timestamp, [], []);
+		const filename = `${timestamp}-${options.name}.ts`;
 		const outputPath = directory ? path.join(directory, filename) : path.join(process.cwd(), filename);
 
-		try {
-			await MigrationUtils.createFile(outputPath, fileContent);
-			console.log(chalk.green(`Migration ${chalk.blue(outputPath)} has been created successfully.`));
-		} catch (error) {
-			console.log(chalk.black.bgRed('Error during migration creating files:'));
-			console.error(error);
-		}
+		await MigrationUtils.createFile(outputPath, fileContent);
+		console.log(chalk.green(`Migration ${chalk.blue(outputPath)} has been created successfully.`));
 	} catch (error) {
-		if (connection) await closeConnection(connection);
-
-		console.log(chalk.black.bgRed('Error during migration create:'));
+		console.error(chalk.red('❌ Error while creating migration file:'));
 		console.error(error);
 		process.exit(1);
 	} finally {
-		await closeConnection(connection);
+		await shutdownDatabaseConnection(dataSource);
 	}
 }
 
 /**
  * @description
- * Establish new database connection, if not found any connection. See [TypeORM migration docs](https://typeorm.io/#/connection)
+ * Initializes a new database connection. See [TypeORM migration docs](https://typeorm.io/#/connection)
  *
- * @param config
+ * @param config - Partial application plugin configuration
+ * @returns An initialized TypeORM DataSource instance
  */
-export async function establishDatabaseConnection(config: Partial<ApplicationPluginConfig>): Promise<DataSource> {
+export async function initializeDatabaseConnection(config: Partial<ApplicationPluginConfig>): Promise<DataSource> {
 	const { dbConnectionOptions } = config;
-	const overrideDbConfig = {
+
+	if (!dbConnectionOptions) {
+		throw new Error('❌ Missing database connection options in plugin config.');
+	}
+
+	const dataSource = new DataSource({
+		...dbConnectionOptions,
 		subscribers: [],
 		synchronize: false,
 		migrationsRun: false,
-		dropSchema: false,
-		logging: ['all']
-	};
+		dropSchema: false
+	} as DataSourceOptions);
+
+	console.log(chalk.yellow('NOTE: No existing database connection found. Creating a new one...'));
 
-	let dataSource: DataSource;
 	try {
-		console.log(chalk.yellow('NOTE: DATABASE CONNECTION DOES NOT EXIST YET. NEW ONE WILL BE CREATED!'));
-		try {
-			console.log(chalk.green(`CONNECTING TO DATABASE...`));
-			dataSource = new DataSource({
-				...dbConnectionOptions,
-				...overrideDbConfig
-			} as DataSourceOptions);
-			if (!dataSource.isInitialized) {
-				await dataSource.initialize();
-				console.log(chalk.green(`✅ CONNECTED TO DATABASE!`));
-			}
-		} catch (error) {
-			console.log('Unable to connect to database', error);
+		if (!dataSource.isInitialized) {
+			console.log(chalk.blue('Connecting to the database...'));
+			await dataSource.initialize();
+			console.log(chalk.green('✅ Successfully connected to the database.'));
 		}
 	} catch (error) {
-		console.log('Error while connecting to the database', error);
+		console.error(chalk.red('❌ Failed to connect to the database:'), error);
+		process.exit(1);
 	}
+
 	return dataSource;
 }
 
 /**
  * @description
- * Close database connection, after complete or failed process
+ * Gracefully shuts down the database connection after use.
  *
- * @param connection
+ * @param dataSource - An initialized TypeORM DataSource
  */
-async function closeConnection(dataSource: DataSource) {
+export async function shutdownDatabaseConnection(dataSource: DataSource): Promise<void> {
+	if (!dataSource || !dataSource.isInitialized) {
+		console.log(chalk.yellow('⚠️ Database connection is not initialized or already closed.'));
+		return;
+	}
+
 	try {
-		if (dataSource && dataSource.isInitialized) {
-			await dataSource.destroy();
-			console.log(chalk.green(`✅ DISCONNECTED TO DATABASE!`));
-		}
+		await dataSource.destroy();
+		console.log(chalk.green('✅ Database connection closed successfully.'));
 	} catch (error) {
-		console.log('Error while disconnecting to the database', error);
+		console.error(chalk.red('❌ Failed to close the database connection:'), error);
 	}
 }
 
@@ -279,13 +278,11 @@ function getTemplate(
 	upSqls: string[],
 	downSqls: string[]
 ): string {
-	return `
-import { MigrationInterface, QueryRunner } from "typeorm";
+	return `import { MigrationInterface, QueryRunner } from 'typeorm';
 import * as chalk from 'chalk';
-import { DatabaseTypeEnum } from "@gauzy/config";
+import { DatabaseTypeEnum } from '@gauzy/config';
 
 export class ${camelCase(name, true)}${timestamp} implements MigrationInterface {
-
     name = '${camelCase(name, true)}${timestamp}';
 
     /**
@@ -318,6 +315,8 @@ export class ${camelCase(name, true)}${timestamp} implements MigrationInterface
      * @param queryRunner
      */
     public async down(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' reverting changes!'));
+
         switch (queryRunner.connection.options.type) {
             case DatabaseTypeEnum.sqlite:
             case DatabaseTypeEnum.betterSqlite3:
diff --git a/packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts b/packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts
new file mode 100644
index 00000000000..1307afc1e40
--- /dev/null
+++ b/packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts
@@ -0,0 +1,179 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+import * as chalk from 'chalk';
+import { DatabaseTypeEnum } from '@gauzy/config';
+
+export class AlterTimeSlotEntityTable1746169515410 implements MigrationInterface {
+	name = 'AlterTimeSlotEntityTable1746169515410';
+
+	/**
+	 * Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async up(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' start running!'));
+
+		switch (queryRunner.connection.options.type) {
+			case DatabaseTypeEnum.sqlite:
+			case DatabaseTypeEnum.betterSqlite3:
+				await this.sqliteUpQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.postgres:
+				await this.postgresUpQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.mysql:
+				await this.mysqlUpQueryRunner(queryRunner);
+				break;
+			default:
+				throw Error(`Unsupported database: ${queryRunner.connection.options.type}`);
+		}
+	}
+
+	/**
+	 * Down Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async down(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' reverting changes!'));
+
+		switch (queryRunner.connection.options.type) {
+			case DatabaseTypeEnum.sqlite:
+			case DatabaseTypeEnum.betterSqlite3:
+				await this.sqliteDownQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.postgres:
+				await this.postgresDownQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.mysql:
+				await this.mysqlDownQueryRunner(queryRunner);
+				break;
+			default:
+				throw Error(`Unsupported database: ${queryRunner.connection.options.type}`);
+		}
+	}
+
+	/**
+	 * PostgresDB Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "location" integer NOT NULL DEFAULT 0`);
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "kbMouseActivity" jsonb`);
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "locationActivity" jsonb`);
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "customActivity" jsonb`);
+	}
+
+	/**
+	 * PostgresDB Down Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "customActivity"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "locationActivity"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "kbMouseActivity"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "location"`);
+	}
+
+	/**
+	 * SqliteDB and BetterSQlite3DB Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`DROP INDEX "IDX_9445b0316f60bd53c29661247a"`);
+		await queryRunner.query(`DROP INDEX "IDX_d40a78a0d0a6e64e8d15f32db6"`);
+		await queryRunner.query(`DROP INDEX "IDX_7913305b850c7afc89b6ed96a3"`);
+		await queryRunner.query(`DROP INDEX "IDX_c6e7d1075bfd97eea6643b1479"`);
+		await queryRunner.query(`DROP INDEX "IDX_f44e721669d5c6bed32cd6a3bf"`);
+		await queryRunner.query(`DROP INDEX "IDX_0c707825a7c2ecc4e186b07ebf"`);
+		await queryRunner.query(`DROP INDEX "IDX_b8284109257b5137256b5b3e84"`);
+		await queryRunner.query(`DROP INDEX "IDX_b407841271245501dd1a8c7551"`);
+		await queryRunner.query(`DROP INDEX "IDX_645a6bc3f1141d4a111a3166d8"`);
+		await queryRunner.query(`DROP INDEX "IDX_81060c5dbe69efa1f3b6e1a2e5"`);
+		await queryRunner.query(`DROP INDEX "IDX_596e0e849822aa09ec0d4a1052"`);
+		await queryRunner.query(
+			`CREATE TABLE "temporary_time_slot" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "duration" integer NOT NULL DEFAULT (0), "keyboard" integer NOT NULL DEFAULT (0), "mouse" integer NOT NULL DEFAULT (0), "overall" integer NOT NULL DEFAULT (0), "startedAt" datetime NOT NULL, "employeeId" varchar NOT NULL, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "deletedAt" datetime, "archivedAt" datetime, "createdByUserId" varchar, "updatedByUserId" varchar, "deletedByUserId" varchar, "location" integer NOT NULL DEFAULT (0), "kbMouseActivity" text, "locationActivity" text, "customActivity" text, CONSTRAINT "FK_9445b0316f60bd53c29661247a1" FOREIGN KEY ("deletedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_d40a78a0d0a6e64e8d15f32db63" FOREIGN KEY ("createdByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_7913305b850c7afc89b6ed96a30" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b8284109257b5137256b5b3e848" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b407841271245501dd1a8c75513" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_596e0e849822aa09ec0d4a10521" FOREIGN KEY ("updatedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
+		);
+		await queryRunner.query(
+			`INSERT INTO "temporary_time_slot"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId" FROM "time_slot"`
+		);
+		await queryRunner.query(`DROP TABLE "time_slot"`);
+		await queryRunner.query(`ALTER TABLE "temporary_time_slot" RENAME TO "time_slot"`);
+		await queryRunner.query(`CREATE INDEX "IDX_9445b0316f60bd53c29661247a" ON "time_slot" ("deletedByUserId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_d40a78a0d0a6e64e8d15f32db6" ON "time_slot" ("createdByUserId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_7913305b850c7afc89b6ed96a3" ON "time_slot" ("employeeId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_c6e7d1075bfd97eea6643b1479" ON "time_slot" ("startedAt") `);
+		await queryRunner.query(`CREATE INDEX "IDX_f44e721669d5c6bed32cd6a3bf" ON "time_slot" ("overall") `);
+		await queryRunner.query(`CREATE INDEX "IDX_0c707825a7c2ecc4e186b07ebf" ON "time_slot" ("duration") `);
+		await queryRunner.query(`CREATE INDEX "IDX_b8284109257b5137256b5b3e84" ON "time_slot" ("tenantId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_b407841271245501dd1a8c7551" ON "time_slot" ("organizationId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_645a6bc3f1141d4a111a3166d8" ON "time_slot" ("isActive") `);
+		await queryRunner.query(`CREATE INDEX "IDX_81060c5dbe69efa1f3b6e1a2e5" ON "time_slot" ("isArchived") `);
+		await queryRunner.query(`CREATE INDEX "IDX_596e0e849822aa09ec0d4a1052" ON "time_slot" ("updatedByUserId") `);
+	}
+
+	/**
+	 * SqliteDB and BetterSQlite3DB Down Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`DROP INDEX "IDX_596e0e849822aa09ec0d4a1052"`);
+		await queryRunner.query(`DROP INDEX "IDX_81060c5dbe69efa1f3b6e1a2e5"`);
+		await queryRunner.query(`DROP INDEX "IDX_645a6bc3f1141d4a111a3166d8"`);
+		await queryRunner.query(`DROP INDEX "IDX_b407841271245501dd1a8c7551"`);
+		await queryRunner.query(`DROP INDEX "IDX_b8284109257b5137256b5b3e84"`);
+		await queryRunner.query(`DROP INDEX "IDX_0c707825a7c2ecc4e186b07ebf"`);
+		await queryRunner.query(`DROP INDEX "IDX_f44e721669d5c6bed32cd6a3bf"`);
+		await queryRunner.query(`DROP INDEX "IDX_c6e7d1075bfd97eea6643b1479"`);
+		await queryRunner.query(`DROP INDEX "IDX_7913305b850c7afc89b6ed96a3"`);
+		await queryRunner.query(`DROP INDEX "IDX_d40a78a0d0a6e64e8d15f32db6"`);
+		await queryRunner.query(`DROP INDEX "IDX_9445b0316f60bd53c29661247a"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" RENAME TO "temporary_time_slot"`);
+		await queryRunner.query(
+			`CREATE TABLE "time_slot" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "duration" integer NOT NULL DEFAULT (0), "keyboard" integer NOT NULL DEFAULT (0), "mouse" integer NOT NULL DEFAULT (0), "overall" integer NOT NULL DEFAULT (0), "startedAt" datetime NOT NULL, "employeeId" varchar NOT NULL, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "deletedAt" datetime, "archivedAt" datetime, "createdByUserId" varchar, "updatedByUserId" varchar, "deletedByUserId" varchar, CONSTRAINT "FK_9445b0316f60bd53c29661247a1" FOREIGN KEY ("deletedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_d40a78a0d0a6e64e8d15f32db63" FOREIGN KEY ("createdByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_7913305b850c7afc89b6ed96a30" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b8284109257b5137256b5b3e848" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b407841271245501dd1a8c75513" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_596e0e849822aa09ec0d4a10521" FOREIGN KEY ("updatedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
+		);
+		await queryRunner.query(
+			`INSERT INTO "time_slot"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId" FROM "temporary_time_slot"`
+		);
+		await queryRunner.query(`DROP TABLE "temporary_time_slot"`);
+		await queryRunner.query(`CREATE INDEX "IDX_596e0e849822aa09ec0d4a1052" ON "time_slot" ("updatedByUserId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_81060c5dbe69efa1f3b6e1a2e5" ON "time_slot" ("isArchived") `);
+		await queryRunner.query(`CREATE INDEX "IDX_645a6bc3f1141d4a111a3166d8" ON "time_slot" ("isActive") `);
+		await queryRunner.query(`CREATE INDEX "IDX_b407841271245501dd1a8c7551" ON "time_slot" ("organizationId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_b8284109257b5137256b5b3e84" ON "time_slot" ("tenantId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_0c707825a7c2ecc4e186b07ebf" ON "time_slot" ("duration") `);
+		await queryRunner.query(`CREATE INDEX "IDX_f44e721669d5c6bed32cd6a3bf" ON "time_slot" ("overall") `);
+		await queryRunner.query(`CREATE INDEX "IDX_c6e7d1075bfd97eea6643b1479" ON "time_slot" ("startedAt") `);
+		await queryRunner.query(`CREATE INDEX "IDX_7913305b850c7afc89b6ed96a3" ON "time_slot" ("employeeId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_d40a78a0d0a6e64e8d15f32db6" ON "time_slot" ("createdByUserId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_9445b0316f60bd53c29661247a" ON "time_slot" ("deletedByUserId") `);
+	}
+
+	/**
+	 * MySQL Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async mysqlUpQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`ALTER TABLE \`time_slot\` ADD \`location\` int NOT NULL DEFAULT '0'`);
+		await queryRunner.query(`ALTER TABLE \`time_slot\` ADD \`kbMouseActivity\` json NULL`);
+		await queryRunner.query(`ALTER TABLE \`time_slot\` ADD \`locationActivity\` json NULL`);
+		await queryRunner.query(`ALTER TABLE \`time_slot\` ADD \`customActivity\` json NULL`);
+	}
+
+	/**
+	 * MySQL Down Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async mysqlDownQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`ALTER TABLE \`time_slot\` DROP COLUMN \`customActivity\``);
+		await queryRunner.query(`ALTER TABLE \`time_slot\` DROP COLUMN \`locationActivity\``);
+		await queryRunner.query(`ALTER TABLE \`time_slot\` DROP COLUMN \`kbMouseActivity\``);
+		await queryRunner.query(`ALTER TABLE \`time_slot\` DROP COLUMN \`location\``);
+	}
+}
diff --git a/packages/core/src/lib/time-tracking/time-slot/time-slot-minute/time-slot-minute.entity.ts b/packages/core/src/lib/time-tracking/time-slot/time-slot-minute/time-slot-minute.entity.ts
index 8d060e4a0db..0af15058f68 100644
--- a/packages/core/src/lib/time-tracking/time-slot/time-slot-minute/time-slot-minute.entity.ts
+++ b/packages/core/src/lib/time-tracking/time-slot/time-slot-minute/time-slot-minute.entity.ts
@@ -15,7 +15,7 @@ export class TimeSlotMinute extends TenantOrganizationBaseEntity implements ITim
 	 * The number of keyboard interactions in the given time slot minute.
 	 * Defaults to 0 if not provided.
 	 */
-	@ApiProperty({
+	@ApiPropertyOptional({
 		type: () => Number,
 		description: 'Number of keyboard interactions in the given time slot minute.',
 		example: 42,
@@ -30,7 +30,7 @@ export class TimeSlotMinute extends TenantOrganizationBaseEntity implements ITim
 	 * The number of mouse interactions in the given time slot minute.
 	 * Defaults to 0 if not provided.
 	 */
-	@ApiProperty({
+	@ApiPropertyOptional({
 		type: () => Number,
 		description: 'Number of mouse interactions in the given time slot minute.',
 		example: 42,
@@ -45,7 +45,7 @@ export class TimeSlotMinute extends TenantOrganizationBaseEntity implements ITim
 	 * Number of movements (e.g., mouse or device movements) detected within one minute.
 	 * Used to track activity levels during time tracking sessions.
 	 */
-	@ApiProperty({
+	@ApiPropertyOptional({
 		type: Number,
 		description: 'Number of movements detected in 1 minute',
 		example: 42,
diff --git a/packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts b/packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts
index 04179b6af45..4f6c070cc73 100644
--- a/packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts
+++ b/packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts
@@ -1,7 +1,17 @@
 import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
 import { RelationId, JoinTable } from 'typeorm';
-import { IsNumber, IsDateString, IsUUID, IsNotEmpty, IsOptional } from 'class-validator';
-import { ITimeSlot, ITimeSlotMinute, IActivity, IScreenshot, IEmployee, ITimeLog, ID } from '@gauzy/contracts';
+import { IsNumber, IsDateString, IsUUID, IsNotEmpty, IsOptional, IsObject } from 'class-validator';
+import {
+	ITimeSlot,
+	ITimeSlotMinute,
+	IActivity,
+	IScreenshot,
+	IEmployee,
+	ITimeLog,
+	ID,
+	JsonData
+} from '@gauzy/contracts';
+import { isMySQL, isPostgres } from '@gauzy/config';
 import { Activity, Employee, Screenshot, TenantOrganizationBaseEntity, TimeLog } from './../../core/entities/internal';
 import {
 	ColumnIndex,
@@ -17,25 +27,69 @@ import { MikroOrmTimeSlotRepository } from './repository/mikro-orm-time-slot.rep
 
 @MultiORMEntity('time_slot', { mikroOrmRepository: () => MikroOrmTimeSlotRepository })
 export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot {
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of seconds employee spent in the given time slot.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'The number of seconds employee spent in the given time slot',
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@ColumnIndex()
 	@MultiORMColumn({ default: 0 })
 	duration?: number;
 
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of keyboard interactions in the given time slot minute.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'Number of keyboard interactions in the given time slot.',
+		example: 42,
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@MultiORMColumn({ default: 0 })
 	keyboard?: number;
 
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of mouse interactions in the given time slot minute.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'Number of mouse interactions in the given time slot.',
+		example: 42,
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@MultiORMColumn({ default: 0 })
 	mouse?: number;
 
+	/**
+	 * Number of movements (e.g., mouse or device movements) detected within 10 minutes.
+	 * Used to track activity levels during time tracking sessions.
+	 */
+	@ApiPropertyOptional({
+		type: Number,
+		description: 'Number of movements detected in 10 minutes',
+		example: 42,
+		default: 0
+	})
+	@IsOptional()
+	@IsNumber()
+	@MultiORMColumn({ default: 0 })
+	location?: number;
+
+	/**
+	 * The overall activity time of the time slot.
+	 */
 	@ApiPropertyOptional({ type: () => Number, default: 0 })
 	@IsOptional()
 	@IsNumber()
@@ -43,6 +97,9 @@ export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot
 	@MultiORMColumn({ default: 0 })
 	overall?: number;
 
+	/**
+	 * The start time of the time slot.
+	 */
 	@ApiProperty({ type: () => 'timestamptz' })
 	@IsNotEmpty()
 	@IsDateString()
@@ -51,17 +108,54 @@ export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot
 	startedAt: Date;
 
 	/**
-	 * Additional virtual columns
+	 * Raw keyboard and mouse activity data (e.g., event logs or durations).
+	 */
+	@ApiPropertyOptional({ type: () => Object })
+	@IsOptional()
+	@IsObject()
+	@MultiORMColumn({
+		type: isPostgres() ? 'jsonb' : isMySQL() ? 'json' : 'text',
+		nullable: true
+	})
+	kbMouseActivity?: JsonData;
+
+	/**
+	 * Raw location activity data (e.g., coordinates or movement patterns).
+	 */
+	@ApiPropertyOptional({ type: () => Object })
+	@IsOptional()
+	@IsObject()
+	@MultiORMColumn({
+		type: isPostgres() ? 'jsonb' : isMySQL() ? 'json' : 'text',
+		nullable: true
+	})
+	locationActivity?: JsonData;
+
+	/**
+	 * Custom-defined activity data (e.g., domain-specific or extension usage).
 	 */
+	@ApiPropertyOptional({ type: () => Object })
+	@IsOptional()
+	@IsObject()
+	@MultiORMColumn({
+		type: isPostgres() ? 'jsonb' : isMySQL() ? 'json' : 'text',
+		nullable: true
+	})
+	customActivity?: JsonData;
+
+	// The stopped time of the time slot.
 	@VirtualMultiOrmColumn()
 	stoppedAt?: Date;
 
+	// The percentage of interactions in the given time slot.
 	@VirtualMultiOrmColumn()
 	percentage?: number;
 
+	// The percentage of keyboard interactions in the given time slot.
 	@VirtualMultiOrmColumn()
 	keyboardPercentage?: number;
 
+	// The percentage of mouse interactions in the given time slot.
 	@VirtualMultiOrmColumn()
 	mousePercentage?: number;
 
@@ -70,8 +164,9 @@ export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot
 	| @ManyToOne
 	|--------------------------------------------------------------------------
 	*/
+
 	/**
-	 * Employee
+	 * The reference to the `Employee` entity to which this time slot belongs.
 	 */
 	@MultiORMManyToOne(() => Employee, (it) => it.timeSlots, {
 		/** Database cascade action on delete. */
@@ -79,6 +174,9 @@ export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot
 	})
 	employee?: IEmployee;
 
+	/**
+	 * The ID of the related `Employee` entity, stored as a UUID.
+	 */
 	@ApiProperty({ type: () => String })
 	@IsNotEmpty()
 	@IsUUID()
@@ -94,27 +192,21 @@ export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot
 	*/
 
 	/**
-	 * Screenshot
+	 * The reference to the `Screenshot` entity to which this time slot belongs.
 	 */
-	@MultiORMOneToMany(() => Screenshot, (it) => it.timeSlot, {
-		cascade: true
-	})
+	@MultiORMOneToMany(() => Screenshot, (it) => it.timeSlot, { cascade: true })
 	screenshots?: IScreenshot[];
 
 	/**
-	 * Activity
+	 * The reference to the `Activity` entity to which this time slot belongs.
 	 */
-	@MultiORMOneToMany(() => Activity, (it) => it.timeSlot, {
-		cascade: true
-	})
+	@MultiORMOneToMany(() => Activity, (it) => it.timeSlot, { cascade: true })
 	activities?: IActivity[];
 
 	/**
-	 * TimeSlotMinute
+	 * The reference to the `TimeSlotMinute` entity to which this time slot belongs.
 	 */
-	@MultiORMOneToMany(() => TimeSlotMinute, (it) => it.timeSlot, {
-		cascade: true
-	})
+	@MultiORMOneToMany(() => TimeSlotMinute, (it) => it.timeSlot, { cascade: true })
 	timeSlotMinutes?: ITimeSlotMinute[];
 
 	/*
@@ -122,8 +214,9 @@ export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot
 	| @ManyToMany
 	|--------------------------------------------------------------------------
 	*/
+
 	/**
-	 * TimeLog
+	 * The reference to the `TimeLog` entity to which this time slot belongs.
 	 */
 	@MultiORMManyToMany(() => TimeLog, (it) => it.timeSlots, {
 		/**  Database cascade action on update. */


Reviews:
user: login: greptile-apps[bot]
body: 
## PR Summary

Added new tracking capabilities to TimeSlot entity with location and activity monitoring, along with corresponding database migrations and improved documentation.

- Added `location` field to track movement activity with default value 0
- Added JSON/JSONB columns for detailed activity tracking: `kbMouseActivity`, `locationActivity`, and `customActivity`
- Improved migration executor with better error handling and connection management
- Added comprehensive database-specific migrations for PostgreSQL, SQLite, and MySQL
- Enhanced API documentation with `@ApiPropertyOptional` decorators for optional fields

<sub>💡 (5/5) You can turn off certain types of comments like style [here](https://app.greptile.com/review/github)!</sub>

<sub>4 file(s) reviewed, 3 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=ever-gauzy_8933)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T08:40:21Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 5**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>packages/core/src/lib/time-tracking/time-slot/time-slot-minute/time-slot-minute.entity.ts (1)</summary><blockquote>

`48-57`: _🧹 Nitpick (assertive)_

**Rename numeric `location` for clarity and future-proofing**

Within the same entity we now have both  
• `location?: number` – count of movements  
• `locationActivity?: JsonData` – raw data object  

Using the same root word for two different concepts can easily confuse maintainers and API consumers (e.g. `dto.location` vs `dto.locationActivity`).  Consider a more explicit name such as `movementCount` or `locationMovementCount` for the numeric field.

```diff
- location?: number;
+ movementCount?: number;   // or locationMovementCount
```

Renaming now (while the feature is new) avoids painful breaking changes later.

</blockquote></details>

</blockquote></details>

<details>
<summary>♻️ Duplicate comments (3)</summary><blockquote>

<details>
<summary>packages/core/src/lib/database/migration-executor.ts (3)</summary><blockquote>

`55-61`: **Same premature exit pattern as above**  

`process.exit(1)` appears again, causing the identical resource-leak risk. Please apply the same remedy here.

---

`131-137`: **Repeat of the premature‐exit problem in `generateMigration`**  

For consistency and safety, replace `process.exit(1)` with a throw or explicit shutdown as suggested earlier.

---

`193-199`: **And again in `createMigration`**  

Please fix the same cleanup/exit issue here as well.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between b2fa6e41830e76ce29a3ba8c3b71aec2c339c419 and 5b8d4754fd37dc8f68c742b6c97ccbe51c31e8c6.

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `packages/core/src/lib/database/migration-executor.ts` (5 hunks)
* `packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts` (1 hunks)
* `packages/core/src/lib/time-tracking/time-slot/time-slot-minute/time-slot-minute.entity.ts` (3 hunks)
* `packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts` (5 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts</summary>

[error] 14-14: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

---

[error] 38-38: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

</details>
<details>
<summary>packages/core/src/lib/database/migration-executor.ts</summary>

[error] 156-157: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: Codacy Static Code Analysis
* GitHub Check: Analyze (javascript-typescript)
* GitHub Check: test
* GitHub Check: build

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts (1)</summary>

`161-166`: **Verify MySQL version supports JSON column type**

The migration adds `json` columns (`kbMouseActivity`, `locationActivity`, `customActivity`). Native JSON is only available from MySQL 5.7+.  Gauzy typically targets modern versions, but if any deployments still use < 5.7 the migration will fail.

Please confirm the minimum supported MySQL version in deployment docs.


Would you like a quick shell script to scan `docker-compose`/helm charts for the MySQL image tag and alert if it is < 5.7?

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T08:47:56Z
user: login: rahul-rocket
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T08:50:47Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T08:51:14Z

Review comments:
diff_hunk: @@ -17,32 +27,79 @@ import { MikroOrmTimeSlotRepository } from './repository/mikro-orm-time-slot.rep
 
 @MultiORMEntity('time_slot', { mikroOrmRepository: () => MikroOrmTimeSlotRepository })
 export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot {
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of seconds employee spent in the given time slot.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'The number of seconds employee spent in the given time slot',
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@ColumnIndex()
 	@MultiORMColumn({ default: 0 })
 	duration?: number;
 
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of keyboard interactions in the given time slot minute.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'Number of keyboard interactions in the given time slot.',
+		example: 42,
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@MultiORMColumn({ default: 0 })
 	keyboard?: number;
 
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of mouse interactions in the given time slot minute.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'Number of mouse interactions in the given time slot.',
+		example: 42,
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@MultiORMColumn({ default: 0 })
 	mouse?: number;
 
+	/**
+	 * Number of movements (e.g., mouse or device movements) detected within one minute.
+	 * Used to track activity levels during time tracking sessions.
+	 */
+	@ApiPropertyOptional({
+		type: Number,
+		description: 'Number of movements detected in 10 minutes',
+		example: 42,
path: packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts
user: login: greptile-apps[bot]
body: logic: Description mentions '10 minutes' but comment above says 'one minute'. This inconsistency needs to be clarified.
created_at: 2025-05-02T08:39:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,179 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+import * as chalk from 'chalk';
+import { DatabaseTypeEnum } from '@gauzy/config';
+
+export class AlterTimeSlotEntityTable1746169515410 implements MigrationInterface {
+	name = 'AlterTimeSlotEntityTable1746169515410';
+
+	/**
+	 * Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async up(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' start running!'));
+
+		switch (queryRunner.connection.options.type) {
+			case DatabaseTypeEnum.sqlite:
+			case DatabaseTypeEnum.betterSqlite3:
+				await this.sqliteUpQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.postgres:
+				await this.postgresUpQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.mysql:
+				await this.mysqlUpQueryRunner(queryRunner);
+				break;
+			default:
+				throw Error(`Unsupported database: ${queryRunner.connection.options.type}`);
+		}
+	}
+
+	/**
+	 * Down Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async down(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' reverting changes!'));
+
+		switch (queryRunner.connection.options.type) {
+			case DatabaseTypeEnum.sqlite:
+			case DatabaseTypeEnum.betterSqlite3:
+				await this.sqliteDownQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.postgres:
+				await this.postgresDownQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.mysql:
+				await this.mysqlDownQueryRunner(queryRunner);
+				break;
+			default:
+				throw Error(`Unsupported database: ${queryRunner.connection.options.type}`);
+		}
+	}
+
+	/**
+	 * PostgresDB Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "location" integer NOT NULL DEFAULT 0`);
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "kbMouseActivity" jsonb`);
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "locationActivity" jsonb`);
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "customActivity" jsonb`);
+	}
+
+	/**
+	 * PostgresDB Down Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "customActivity"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "locationActivity"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "kbMouseActivity"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "location"`);
+	}
+
+	/**
+	 * SqliteDB and BetterSQlite3DB Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`DROP INDEX "IDX_9445b0316f60bd53c29661247a"`);
+		await queryRunner.query(`DROP INDEX "IDX_d40a78a0d0a6e64e8d15f32db6"`);
+		await queryRunner.query(`DROP INDEX "IDX_7913305b850c7afc89b6ed96a3"`);
+		await queryRunner.query(`DROP INDEX "IDX_c6e7d1075bfd97eea6643b1479"`);
+		await queryRunner.query(`DROP INDEX "IDX_f44e721669d5c6bed32cd6a3bf"`);
+		await queryRunner.query(`DROP INDEX "IDX_0c707825a7c2ecc4e186b07ebf"`);
+		await queryRunner.query(`DROP INDEX "IDX_b8284109257b5137256b5b3e84"`);
+		await queryRunner.query(`DROP INDEX "IDX_b407841271245501dd1a8c7551"`);
+		await queryRunner.query(`DROP INDEX "IDX_645a6bc3f1141d4a111a3166d8"`);
+		await queryRunner.query(`DROP INDEX "IDX_81060c5dbe69efa1f3b6e1a2e5"`);
+		await queryRunner.query(`DROP INDEX "IDX_596e0e849822aa09ec0d4a1052"`);
+		await queryRunner.query(
+			`CREATE TABLE "temporary_time_slot" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "duration" integer NOT NULL DEFAULT (0), "keyboard" integer NOT NULL DEFAULT (0), "mouse" integer NOT NULL DEFAULT (0), "overall" integer NOT NULL DEFAULT (0), "startedAt" datetime NOT NULL, "employeeId" varchar NOT NULL, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "deletedAt" datetime, "archivedAt" datetime, "createdByUserId" varchar, "updatedByUserId" varchar, "deletedByUserId" varchar, "location" integer NOT NULL DEFAULT (0), "kbMouseActivity" text, "locationActivity" text, "customActivity" text, CONSTRAINT "FK_9445b0316f60bd53c29661247a1" FOREIGN KEY ("deletedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_d40a78a0d0a6e64e8d15f32db63" FOREIGN KEY ("createdByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_7913305b850c7afc89b6ed96a30" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b8284109257b5137256b5b3e848" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b407841271245501dd1a8c75513" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_596e0e849822aa09ec0d4a10521" FOREIGN KEY ("updatedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
+		);
+		await queryRunner.query(
+			`INSERT INTO "temporary_time_slot"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId" FROM "time_slot"`
+		);
path: packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts
user: login: greptile-apps[bot]
body: logic: Data migration missing the new columns (location, kbMouseActivity, locationActivity, customActivity) in the INSERT statement, which could lead to data loss if the table already contains data in these columns

```suggestion
		await queryRunner.query(
			`INSERT INTO "temporary_time_slot"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId", "location", "kbMouseActivity", "locationActivity", "customActivity") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId", "location", "kbMouseActivity", "locationActivity", "customActivity" FROM "time_slot"`
		);
```
created_at: 2025-05-02T08:39:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,179 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+import * as chalk from 'chalk';
+import { DatabaseTypeEnum } from '@gauzy/config';
+
+export class AlterTimeSlotEntityTable1746169515410 implements MigrationInterface {
+	name = 'AlterTimeSlotEntityTable1746169515410';
+
+	/**
+	 * Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async up(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' start running!'));
+
+		switch (queryRunner.connection.options.type) {
+			case DatabaseTypeEnum.sqlite:
+			case DatabaseTypeEnum.betterSqlite3:
+				await this.sqliteUpQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.postgres:
+				await this.postgresUpQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.mysql:
+				await this.mysqlUpQueryRunner(queryRunner);
+				break;
+			default:
+				throw Error(`Unsupported database: ${queryRunner.connection.options.type}`);
+		}
+	}
+
+	/**
+	 * Down Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async down(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' reverting changes!'));
+
+		switch (queryRunner.connection.options.type) {
+			case DatabaseTypeEnum.sqlite:
+			case DatabaseTypeEnum.betterSqlite3:
+				await this.sqliteDownQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.postgres:
+				await this.postgresDownQueryRunner(queryRunner);
+				break;
+			case DatabaseTypeEnum.mysql:
+				await this.mysqlDownQueryRunner(queryRunner);
+				break;
+			default:
+				throw Error(`Unsupported database: ${queryRunner.connection.options.type}`);
+		}
+	}
+
+	/**
+	 * PostgresDB Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "location" integer NOT NULL DEFAULT 0`);
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "kbMouseActivity" jsonb`);
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "locationActivity" jsonb`);
+		await queryRunner.query(`ALTER TABLE "time_slot" ADD "customActivity" jsonb`);
+	}
+
+	/**
+	 * PostgresDB Down Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "customActivity"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "locationActivity"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "kbMouseActivity"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" DROP COLUMN "location"`);
+	}
+
+	/**
+	 * SqliteDB and BetterSQlite3DB Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`DROP INDEX "IDX_9445b0316f60bd53c29661247a"`);
+		await queryRunner.query(`DROP INDEX "IDX_d40a78a0d0a6e64e8d15f32db6"`);
+		await queryRunner.query(`DROP INDEX "IDX_7913305b850c7afc89b6ed96a3"`);
+		await queryRunner.query(`DROP INDEX "IDX_c6e7d1075bfd97eea6643b1479"`);
+		await queryRunner.query(`DROP INDEX "IDX_f44e721669d5c6bed32cd6a3bf"`);
+		await queryRunner.query(`DROP INDEX "IDX_0c707825a7c2ecc4e186b07ebf"`);
+		await queryRunner.query(`DROP INDEX "IDX_b8284109257b5137256b5b3e84"`);
+		await queryRunner.query(`DROP INDEX "IDX_b407841271245501dd1a8c7551"`);
+		await queryRunner.query(`DROP INDEX "IDX_645a6bc3f1141d4a111a3166d8"`);
+		await queryRunner.query(`DROP INDEX "IDX_81060c5dbe69efa1f3b6e1a2e5"`);
+		await queryRunner.query(`DROP INDEX "IDX_596e0e849822aa09ec0d4a1052"`);
+		await queryRunner.query(
+			`CREATE TABLE "temporary_time_slot" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "duration" integer NOT NULL DEFAULT (0), "keyboard" integer NOT NULL DEFAULT (0), "mouse" integer NOT NULL DEFAULT (0), "overall" integer NOT NULL DEFAULT (0), "startedAt" datetime NOT NULL, "employeeId" varchar NOT NULL, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "deletedAt" datetime, "archivedAt" datetime, "createdByUserId" varchar, "updatedByUserId" varchar, "deletedByUserId" varchar, "location" integer NOT NULL DEFAULT (0), "kbMouseActivity" text, "locationActivity" text, "customActivity" text, CONSTRAINT "FK_9445b0316f60bd53c29661247a1" FOREIGN KEY ("deletedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_d40a78a0d0a6e64e8d15f32db63" FOREIGN KEY ("createdByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_7913305b850c7afc89b6ed96a30" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b8284109257b5137256b5b3e848" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b407841271245501dd1a8c75513" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_596e0e849822aa09ec0d4a10521" FOREIGN KEY ("updatedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
+		);
+		await queryRunner.query(
+			`INSERT INTO "temporary_time_slot"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId" FROM "time_slot"`
+		);
+		await queryRunner.query(`DROP TABLE "time_slot"`);
+		await queryRunner.query(`ALTER TABLE "temporary_time_slot" RENAME TO "time_slot"`);
+		await queryRunner.query(`CREATE INDEX "IDX_9445b0316f60bd53c29661247a" ON "time_slot" ("deletedByUserId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_d40a78a0d0a6e64e8d15f32db6" ON "time_slot" ("createdByUserId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_7913305b850c7afc89b6ed96a3" ON "time_slot" ("employeeId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_c6e7d1075bfd97eea6643b1479" ON "time_slot" ("startedAt") `);
+		await queryRunner.query(`CREATE INDEX "IDX_f44e721669d5c6bed32cd6a3bf" ON "time_slot" ("overall") `);
+		await queryRunner.query(`CREATE INDEX "IDX_0c707825a7c2ecc4e186b07ebf" ON "time_slot" ("duration") `);
+		await queryRunner.query(`CREATE INDEX "IDX_b8284109257b5137256b5b3e84" ON "time_slot" ("tenantId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_b407841271245501dd1a8c7551" ON "time_slot" ("organizationId") `);
+		await queryRunner.query(`CREATE INDEX "IDX_645a6bc3f1141d4a111a3166d8" ON "time_slot" ("isActive") `);
+		await queryRunner.query(`CREATE INDEX "IDX_81060c5dbe69efa1f3b6e1a2e5" ON "time_slot" ("isArchived") `);
+		await queryRunner.query(`CREATE INDEX "IDX_596e0e849822aa09ec0d4a1052" ON "time_slot" ("updatedByUserId") `);
+	}
+
+	/**
+	 * SqliteDB and BetterSQlite3DB Down Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise<any> {
+		await queryRunner.query(`DROP INDEX "IDX_596e0e849822aa09ec0d4a1052"`);
+		await queryRunner.query(`DROP INDEX "IDX_81060c5dbe69efa1f3b6e1a2e5"`);
+		await queryRunner.query(`DROP INDEX "IDX_645a6bc3f1141d4a111a3166d8"`);
+		await queryRunner.query(`DROP INDEX "IDX_b407841271245501dd1a8c7551"`);
+		await queryRunner.query(`DROP INDEX "IDX_b8284109257b5137256b5b3e84"`);
+		await queryRunner.query(`DROP INDEX "IDX_0c707825a7c2ecc4e186b07ebf"`);
+		await queryRunner.query(`DROP INDEX "IDX_f44e721669d5c6bed32cd6a3bf"`);
+		await queryRunner.query(`DROP INDEX "IDX_c6e7d1075bfd97eea6643b1479"`);
+		await queryRunner.query(`DROP INDEX "IDX_7913305b850c7afc89b6ed96a3"`);
+		await queryRunner.query(`DROP INDEX "IDX_d40a78a0d0a6e64e8d15f32db6"`);
+		await queryRunner.query(`DROP INDEX "IDX_9445b0316f60bd53c29661247a"`);
+		await queryRunner.query(`ALTER TABLE "time_slot" RENAME TO "temporary_time_slot"`);
+		await queryRunner.query(
+			`CREATE TABLE "time_slot" ("id" varchar PRIMARY KEY NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "tenantId" varchar, "organizationId" varchar, "duration" integer NOT NULL DEFAULT (0), "keyboard" integer NOT NULL DEFAULT (0), "mouse" integer NOT NULL DEFAULT (0), "overall" integer NOT NULL DEFAULT (0), "startedAt" datetime NOT NULL, "employeeId" varchar NOT NULL, "isActive" boolean DEFAULT (1), "isArchived" boolean DEFAULT (0), "deletedAt" datetime, "archivedAt" datetime, "createdByUserId" varchar, "updatedByUserId" varchar, "deletedByUserId" varchar, CONSTRAINT "FK_9445b0316f60bd53c29661247a1" FOREIGN KEY ("deletedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_d40a78a0d0a6e64e8d15f32db63" FOREIGN KEY ("createdByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_7913305b850c7afc89b6ed96a30" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b8284109257b5137256b5b3e848" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_b407841271245501dd1a8c75513" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_596e0e849822aa09ec0d4a10521" FOREIGN KEY ("updatedByUserId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
+		);
+		await queryRunner.query(
+			`INSERT INTO "time_slot"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId" FROM "temporary_time_slot"`
+		);
path: packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts
user: login: greptile-apps[bot]
body: logic: Down migration INSERT statement should include all columns to preserve any existing data before dropping

```suggestion
		await queryRunner.query(
			`INSERT INTO "time_slot"("id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId", "location", "kbMouseActivity", "locationActivity", "customActivity") SELECT "id", "createdAt", "updatedAt", "tenantId", "organizationId", "duration", "keyboard", "mouse", "overall", "startedAt", "employeeId", "isActive", "isArchived", "deletedAt", "archivedAt", "createdByUserId", "updatedByUserId", "deletedByUserId", "location", "kbMouseActivity", "locationActivity", "customActivity" FROM "temporary_time_slot"`
		);
```
created_at: 2025-05-02T08:39:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,32 +27,79 @@ import { MikroOrmTimeSlotRepository } from './repository/mikro-orm-time-slot.rep
 
 @MultiORMEntity('time_slot', { mikroOrmRepository: () => MikroOrmTimeSlotRepository })
 export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot {
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of seconds employee spent in the given time slot.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'The number of seconds employee spent in the given time slot',
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@ColumnIndex()
 	@MultiORMColumn({ default: 0 })
 	duration?: number;
 
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of keyboard interactions in the given time slot minute.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'Number of keyboard interactions in the given time slot.',
+		example: 42,
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@MultiORMColumn({ default: 0 })
 	keyboard?: number;
 
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of mouse interactions in the given time slot minute.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'Number of mouse interactions in the given time slot.',
+		example: 42,
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@MultiORMColumn({ default: 0 })
 	mouse?: number;
 
+	/**
+	 * Number of movements (e.g., mouse or device movements) detected within one minute.
+	 * Used to track activity levels during time tracking sessions.
+	 */
+	@ApiPropertyOptional({
+		type: Number,
+		description: 'Number of movements detected in 10 minutes',
+		example: 42,
+		default: 0
+	})
+	@IsOptional()
+	@IsNumber()
+	@MultiORMColumn({ default: 0 })
+	location?: number;
path: packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Ambiguous `location` naming and mismatched comment**

1. Comment says “detected in 10 minutes” but a `TimeSlot` can have a configurable duration; better phrase it generically.  
2. Field name `location` does not indicate it is a *movement count*.  As with the minute-level entity, a clearer name (e.g. `movementCount`) avoids confusion with the JSON `locationActivity`.

If you adopt the rename suggested for the minute entity, please keep both entities consistent.

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts lines 75 to 88, rename the field `location` to `movementCount` to clearly indicate it tracks the number of movements, not a location. Update the comment to generically describe it as the number of movements detected during the time slot, avoiding a fixed duration reference. Ensure the ApiPropertyOptional description and example reflect this rename and updated meaning. Maintain consistency with the similarly named field in the minute-level entity.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T08:47:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,32 +27,79 @@
 
 @MultiORMEntity('time_slot', { mikroOrmRepository: () => MikroOrmTimeSlotRepository })
 export class TimeSlot extends TenantOrganizationBaseEntity implements ITimeSlot {
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of seconds employee spent in the given time slot.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'The number of seconds employee spent in the given time slot',
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@ColumnIndex()
 	@MultiORMColumn({ default: 0 })
 	duration?: number;
 
-	@ApiPropertyOptional({ type: () => Number, default: 0 })
+	/**
+	 * The number of keyboard interactions in the given time slot minute.
+	 * Defaults to 0 if not provided.
+	 */
+	@ApiPropertyOptional({
+		type: () => Number,
+		description: 'Number of keyboard interactions in the given time slot.',
+		example: 42,
+		default: 0
+	})
 	@IsOptional()
 	@IsNumber()
 	@MultiORMColumn({ default: 0 })
 	keyboard?: number;
path: packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Docstring refers to “time slot minute” but the field is on TimeSlot**

The JSDoc & Swagger description still says  
`Number of keyboard interactions in the given time slot minute.`

However this property lives on `TimeSlot`, which (in Gauzy) represents a 10-minute bucket, not a single minute.  The wording should drop “minute” to avoid misleading integrators.

```diff
- description: 'Number of keyboard interactions in the given time slot minute.',
+ description: 'Number of keyboard interactions in the given time slot.',
```

Same applies to the `mouse` property a few lines below.



> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts around lines 45 to 58, update the JSDoc and Swagger description for the keyboard property to remove the word "minute" since TimeSlot represents a 10-minute bucket, not a single minute. Change the description to say "Number of keyboard interactions in the given time slot." Similarly, locate the mouse property a few lines below and make the same wording adjustment in its JSDoc and Swagger description.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T08:47:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,179 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+import * as chalk from 'chalk';
+import { DatabaseTypeEnum } from '@gauzy/config';
+
+export class AlterTimeSlotEntityTable1746169515410 implements MigrationInterface {
+	name = 'AlterTimeSlotEntityTable1746169515410';
+
+	/**
+	 * Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async up(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' start running!'));
+
path: packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Use template literals instead of `+` concatenation (Biome warning)**

The static-analysis linter flags these two lines:

```diff
-console.log(chalk.yellow(this.name + ' start running!'));
+console.log(chalk.yellow(`${this.name} start running!`));

-console.log(chalk.yellow(this.name + ' reverting changes!'));
+console.log(chalk.yellow(`${this.name} reverting changes!`));
```

This is purely stylistic but removes the linter error and keeps consistency with the rest of the codebase.



Also applies to: 38-39

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 14-14: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts at lines 14-15 and 38-39, replace string concatenation using '+' with template literals for consistency and to resolve the linter warning. Use backticks and embed expressions with ${} instead of concatenating strings with '+'. This will align with the codebase style and remove the Biome linter error.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-02T08:47:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -139,122 +129,131 @@ export async function generateMigration(pluginConfig: Partial<ApplicationPluginC
 			);
 		}
 	} catch (error) {
-		if (connection) await closeConnection(connection);
-
-		console.log(chalk.black.bgRed('Error during migration generation:'));
+		console.error(chalk.red('❌ Error during migration generation:'));
 		console.error(error);
 		process.exit(1);
 	} finally {
-		await closeConnection(connection);
+		await shutdownDatabaseConnection(dataSource);
+	}
+}
+
+/**
+ * Resolves the directory where migration files should be generated.
+ *
+ * @param options - Migration options that may include a `dir` path
+ * @param config - Plugin configuration containing possible CLI migration settings
+ * @returns The resolved directory path or `undefined` if not found
+ */
+export function resolveMigrationDirectory(
+	options: IMigrationOptions,
+	config: Partial<ApplicationPluginConfig>
+): string | undefined {
+	if (options.dir) {
+		return options.dir;
+	}
+
+	try {
+		return config.dbConnectionOptions && config.dbConnectionOptions['cli']
+			? config.dbConnectionOptions['cli']['migrationsDir']
+			: undefined;
path: packages/core/src/lib/database/migration-executor.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Use optional chaining for brevity & clarity**  

The nested property access can be simplified using optional chaining, making the intent clearer and silencing the linter warning.

```diff
-return config.dbConnectionOptions && config.dbConnectionOptions['cli']
-  ? config.dbConnectionOptions['cli']['migrationsDir']
-  : undefined;
+return config.dbConnectionOptions?.cli?.migrationsDir;
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
		return config.dbConnectionOptions?.cli?.migrationsDir;
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 156-157: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In packages/core/src/lib/database/migration-executor.ts around lines 156 to 158, replace the nested property access of config.dbConnectionOptions and its cli and migrationsDir properties with optional chaining syntax. This means using config.dbConnectionOptions?.cli?.migrationsDir instead of the current conditional checks, which simplifies the code and resolves the linter warning.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T08:47:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,84 +14,74 @@
  * @description
  * Run pending database migrations. See [TypeORM migration docs](https://typeorm.io/#/migrations)
  *
- * @param pluginConfig
+ * @param pluginConfig - Partial application plugin config
  */
-export async function runDatabaseMigrations(pluginConfig: Partial<ApplicationPluginConfig>) {
+export async function runDatabaseMigrations(pluginConfig: Partial<ApplicationPluginConfig>): Promise<void> {
 	const config = await registerPluginConfig(pluginConfig);
-	const dataSource: DataSource = await establishDatabaseConnection(config);
+	const dataSource = await initializeDatabaseConnection(config);
 
 	try {
 		const migrations = await dataSource.runMigrations({ transaction: 'each' });
+
 		if (isNotEmpty(migrations)) {
-			for (const migration of migrations) {
+			migrations.forEach((migration) => {
 				console.log(chalk.green(`Migration ${migration.name} has been run successfully!`));
-			}
+			});
 		} else {
 			console.log(chalk.yellow(`There are no pending migrations to run.`));
 		}
 	} catch (error) {
-		if (dataSource) await closeConnection(dataSource);
-
-		console.log(chalk.black.bgRed('Error during migration run:'));
+		console.error(chalk.black.bgRed('Error during migration run:'));
 		console.error(error);
 		process.exit(1);
 	} finally {
-		await closeConnection(dataSource);
+		await shutdownDatabaseConnection(dataSource);
 	}
path: packages/core/src/lib/database/migration-executor.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**`process.exit()` short-circuits the `finally` block, leaking the connection**  

`process.exit(1)` inside the `catch` prevents the `finally` block from executing, so `shutdownDatabaseConnection` never runs when an error occurs. This leaves pooled connections open and may corrupt the next CI run or local dev DB.

A safer pattern is to either:
1. call `await shutdownDatabaseConnection(dataSource)` right in the `catch`, _then_ exit, or  
2. simply re-throw the error and let the `finally` block clean up.

Example fix (option #2):

```diff
} catch (error) {
   console.error(chalk.black.bgRed('Error during migration run:'));
   console.error(error);
-  process.exit(1);
+  throw error;          // let the finally–block handle cleanup
} finally {
   await shutdownDatabaseConnection(dataSource);
}
```

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In packages/core/src/lib/database/migration-executor.ts around lines 34 to 39, the use of process.exit(1) inside the catch block prevents the finally block from running, causing the database connection to remain open. To fix this, remove process.exit(1) from the catch block and instead re-throw the error so that the finally block can execute and properly shut down the database connection.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-02T08:47:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,179 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+import * as chalk from 'chalk';
+import { DatabaseTypeEnum } from '@gauzy/config';
+
+export class AlterTimeSlotEntityTable1746169515410 implements MigrationInterface {
+	name = 'AlterTimeSlotEntityTable1746169515410';
+
+	/**
+	 * Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async up(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' start running!'));
+
path: packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts
user: login: rahul-rocket
body: @coderabbit We don't need this type of changes, because we generating migration dynamically. 
created_at: 2025-05-02T08:50:46Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,179 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+import * as chalk from 'chalk';
+import { DatabaseTypeEnum } from '@gauzy/config';
+
+export class AlterTimeSlotEntityTable1746169515410 implements MigrationInterface {
+	name = 'AlterTimeSlotEntityTable1746169515410';
+
+	/**
+	 * Up Migration
+	 *
+	 * @param queryRunner
+	 */
+	public async up(queryRunner: QueryRunner): Promise<void> {
+		console.log(chalk.yellow(this.name + ' start running!'));
+
path: packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts
user: login: coderabbitai[bot]
body: @rahul-rocket I understand. Since these migrations are generated dynamically, making manual style changes to the code wouldn't be maintained in future generations. Thanks for clarifying your workflow - I'll keep this in mind for future reviews.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: rahul-rocket
PR: ever-co/ever-gauzy#8933
File: packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts:14-15
Timestamp: 2025-05-02T08:51:11.220Z
Learning: In the Ever Gauzy project, migration files are generated dynamically, so style changes (like replacing string concatenation with template literals) are not needed or desired in migration files.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>




<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@rahul-rocket I understand. Since these migrations are generated dynamically, making manual style changes to the code wouldn't be maintained in future generations. Thanks for clarifying your workflow - I'll keep this in mind for future reviews.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: rahul-rocket
PR: ever-co/ever-gauzy#8933
File: packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts:14-15
Timestamp: 2025-05-02T08:51:11.220Z
Learning: In the Ever Gauzy project, migration files are generated dynamically, so style changes (like replacing string concatenation with template literals) are not needed or desired in migration files.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-02T08:51:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-02T08:39:03Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: failure by coderabbit.ai -->

> [!CAUTION]
> ## Review failed
> 
> The pull request is closed.

<!-- end of auto-generated comment: failure by coderabbit.ai -->
<!-- walkthrough_start -->

## Summary by CodeRabbit

- **New Features**
  - Added new tracking fields to time slots, including location and detailed activity data.
  - Introduced support for storing keyboard/mouse, location, and custom activity information in time slots.
- **Improvements**
  - Enhanced API documentation for time slot properties, marking certain fields as optional and providing clearer descriptions.
  - Improved error handling and consistency in database migration processes.
- **Bug Fixes**
  - Standardized logging and error messages during migration and database connection operations.
## Summary by CodeRabbit

- **New Features**
  - Added new activity tracking fields to time slots, including movement counts and detailed activity data (keyboard/mouse, location, and custom activity).
- **Improvements**
  - Enhanced API documentation for time slot properties and relationships, providing clearer descriptions and marking certain fields as optional.
  - Improved error handling and logging for database migration processes.
- **Bug Fixes**
  - Standardized migration logic across supported databases to ensure consistent schema updates and rollbacks.

## Walkthrough

The changes introduce enhancements and refactoring to the migration executor logic, time slot entities, and database migration scripts. The migration executor was refactored for improved error handling, consistent naming, modular directory resolution, and standardized logging. The `TimeSlot` entity received new columns for location and activity data, with enriched API documentation and comments. A new migration script was added to alter the `time_slot` table, supporting multiple database types and handling schema changes with up and down methods. Additionally, the `TimeSlotMinute` entity's API documentation was updated to mark certain properties as optional.

## Changes

| File(s)                                                                                           | Change Summary |
|-------------------------------------------------------------------------------------------------|----------------|
| `packages/core/src/lib/database/migration-executor.ts`                                          | Refactored migration executor: renamed connection functions, improved error handling, modularized migration directory resolution, standardized logging, updated function signatures to return promises, and removed redundant code. |
| `packages/core/src/lib/database/migrations/1746169515410-AlterTimeSlotEntityTable.ts`           | Added migration class to modify `time_slot` table: adds four columns (`location`, `kbMouseActivity`, `locationActivity`, `customActivity`), with up/down logic tailored for SQLite, PostgreSQL, and MySQL databases. |
| `packages/core/src/lib/time-tracking/time-slot/time-slot-minute/time-slot-minute.entity.ts`     | Updated API documentation decorators for `keyboard`, `mouse`, and `location` properties to be optional using `@ApiPropertyOptional`. |
| `packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts`                             | Enhanced `TimeSlot` entity: added new columns for location and activity data, improved API documentation with detailed JSDoc and Swagger metadata, added clarifying comments, updated imports, and refined entity relationships. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant CLI/Service
    participant MigrationExecutor
    participant Database

    CLI/Service->>MigrationExecutor: runDatabaseMigrations(config)
    activate MigrationExecutor
    MigrationExecutor->>Database: initializeDatabaseConnection(config)
    Database-->>MigrationExecutor: DataSource
    MigrationExecutor->>MigrationExecutor: resolveMigrationDirectory(options, config)
    MigrationExecutor->>Database: Run migration scripts (up/down)
    Database-->>MigrationExecutor: Migration result
    MigrationExecutor->>Database: shutdownDatabaseConnection(DataSource)
    deactivate MigrationExecutor
    Database-->>CLI/Service: Migration process complete
```

## Possibly related PRs

- #8933: Modifies the same migration executor functions and introduces the identical migration class `AlterTimeSlotEntityTable1746169515410` with matching database-specific logic, indicating a direct relation.

## Poem

> 🐇✨  
> Refactored code and tables anew,  
> With columns for things we track and do.  
> Migrations now run safe and bright,  
> Logging errors, naming right.  
> Time slots richer, docs in tow—  
> This rabbit hops where schemas grow!  
> 🗃️🍃

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 5b8d4754fd37dc8f68c742b6c97ccbe51c31e8c6 and 0f830e2f24bea9b5f1a4f1b00d9d4144268c4b3c.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `packages/core/src/lib/time-tracking/time-slot/time-slot.entity.ts` (5 hunks)

</details>

</details>
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=ever-co/ever-gauzy&utm_content=8933):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-05-02T08:42:37Z
body: > [!NOTE]
> Generated docstrings for this pull request at https://github.com/ever-co/ever-gauzy/pull/8934
author_association: CONTRIBUTOR
user: login: nx-cloud[bot]
created_at: 2025-05-02T09:31:43Z
body: 
View your [CI Pipeline Execution ↗](https://cloud.nx.app/cipes/681490fdcf4ed57e36026772?utm_source=pull-request&utm_medium=comment) for commit 0f830e2f24bea9b5f1a4f1b00d9d4144268c4b3c.

| Command | Status | Duration | Result |
|---------|--------|----------:|--------|
| `nx build api -c=production --prod` | ✅ Succeeded | 1m 10s | [View ↗](https://cloud.nx.app/runs/u7j6BvKg84?utm_source=pull-request&utm_medium=comment) |
| `nx build desktop --base-href ./` | ✅ Succeeded | 1m 54s | [View ↗](https://cloud.nx.app/runs/mk1Vw502Ad?utm_source=pull-request&utm_medium=comment) |
| `nx build desktop-ui-lib --configuration=develop...` | ✅ Succeeded | 33s | [View ↗](https://cloud.nx.app/runs/YlIVre7Eca?utm_source=pull-request&utm_medium=comment) |
| `nx build plugin-integration-wakatime` | ✅ Succeeded | <1s | [View ↗](https://cloud.nx.app/runs/IeMqh64ltY?utm_source=pull-request&utm_medium=comment) |
| `nx build desktop-lib` | ✅ Succeeded | <1s | [View ↗](https://cloud.nx.app/runs/h39xrVNLa8?utm_source=pull-request&utm_medium=comment) |
| `nx build desktop-window` | ✅ Succeeded | <1s | [View ↗](https://cloud.nx.app/runs/UOjPDULYCy?utm_source=pull-request&utm_medium=comment) |
| `nx build desktop-core` | ✅ Succeeded | <1s | [View ↗](https://cloud.nx.app/runs/97pFkYLGJW?utm_source=pull-request&utm_medium=comment) |
| `nx build desktop-activity` | ✅ Succeeded | <1s | [View ↗](https://cloud.nx.app/runs/B6Ihzuu4sa?utm_source=pull-request&utm_medium=comment) |
| `Additional runs (61)` | ✅ Succeeded | ... | [View ↗](https://cloud.nx.app/cipes/681490fdcf4ed57e36026772?utm_source=pull-request&utm_medium=comment) |

---

☁️ [Nx Cloud](https://cloud.nx.app?utm_source=pull-request&utm_medium=comment) last updated this comment at `2025-05-02 09:48:56` UTC

<!-- NX_CLOUD_APP_COMMENT_END -->
author_association: NONE