feat(editor): Improve n8n welcome experience (#3289)
* ✨ Injecting a welcome sticky note if a corresponding flag has been received from backend * 🔒 Allowing resources from `/static` route to be displayed in markown component. * ✨ Implemented image width control via markdown URLs * 💄Updating quickstart video thumbnail images. * 🔨 Updated new workflow action name and quickstart sticky name * ✨ Added quickstart menu item in the Help menu * 🔨 Moving quickstart video thumbnail to the translation file. * 🔒 Limiting http static resource requests in markdown img tags only to image files. * 🔒 Adding more file types to supported image list in markown component. * 👌 Extracting quickstart note name to constant. * 🐘 add DB migration sqlite * ⚡️ add logic for onboarding flow flag * 🐘 add postgres migration for user settings * 🐘 add mysql migration for user settings * ✨ Injecting a welcome sticky note if a corresponding flag has been received from backend * 🔒 Allowing resources from `/static` route to be displayed in markown component. * ✨ Implemented image width control via markdown URLs * 💄Updating quickstart video thumbnail images. * 🔨 Updated new workflow action name and quickstart sticky name * ✨ Added quickstart menu item in the Help menu * 🔨 Moving quickstart video thumbnail to the translation file. * 🔒 Limiting http static resource requests in markdown img tags only to image files. * 🔒 Adding more file types to supported image list in markown component. * 👌 Extracting quickstart note name to constant. * 📈 Added telemetry events to quickstart sticky note. * ⚡ Disable sticky node type from showing in expression editor * 🔨 Improving welcome video link detecton when triggering telemetry events * 👌Moved sticky links click handling logic outside of the design system, removed user and instance id from telemetry events. * 👌Improving sticky note link telemetry tracking. * 🔨 Refactoring markdown component click event logic. * 🔨 Moving bits of clicked link detection logic to Markdown component. * 💄Fixing code spacing. * remove transpileonly option * update package lock * 💄Changing the default route to `/workflow`, updating welcome sticky content. * remove hardcoded * 🐛 Fixing the onboarding threshold logic so sticky notes are skipped when counting nodes. * 👕 Fixing linting errors. Co-authored-by: Milorad Filipović <milorad.filipovic19@gmail.com> Co-authored-by: Milorad Filipović <miloradfilipovic19@gmail.com> Co-authored-by: Ben Hesseldieck <b.hesseldieck@gmail.com> Co-authored-by: Milorad Filipović <milorad@n8n.io>
This commit is contained in:
@@ -16,7 +16,7 @@ import {
|
||||
} from 'typeorm';
|
||||
import { IsEmail, IsString, Length } from 'class-validator';
|
||||
import * as config from '../../../config';
|
||||
import { DatabaseType, IPersonalizationSurveyAnswers } from '../..';
|
||||
import { DatabaseType, IPersonalizationSurveyAnswers, IUserSettings } from '../..';
|
||||
import { Role } from './Role';
|
||||
import { SharedWorkflow } from './SharedWorkflow';
|
||||
import { SharedCredentials } from './SharedCredentials';
|
||||
@@ -102,6 +102,12 @@ export class User {
|
||||
})
|
||||
personalizationAnswers: IPersonalizationSurveyAnswers | null;
|
||||
|
||||
@Column({
|
||||
type: resolveDataType('json') as ColumnOptions['type'],
|
||||
nullable: true,
|
||||
})
|
||||
settings: IUserSettings | null;
|
||||
|
||||
@ManyToOne(() => Role, (role) => role.globalForUsers, {
|
||||
cascade: true,
|
||||
nullable: false,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
import * as config from '../../../../config';
|
||||
|
||||
export class AddUserSettings1652367743993 implements MigrationInterface {
|
||||
name = 'AddUserSettings1652367743993';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const tablePrefix = config.getEnv('database.tablePrefix');
|
||||
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE `' + tablePrefix + 'user` ADD COLUMN `settings` json NULL DEFAULT NULL',
|
||||
);
|
||||
await queryRunner.query(
|
||||
'ALTER TABLE `' +
|
||||
tablePrefix +
|
||||
'user` CHANGE COLUMN `personalizationAnswers` `personalizationAnswers` json NULL DEFAULT NULL',
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
const tablePrefix = config.getEnv('database.tablePrefix');
|
||||
|
||||
await queryRunner.query('ALTER TABLE `' + tablePrefix + 'user` DROP COLUMN `settings`');
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import { UpdateWorkflowCredentials1630451444017 } from './1630451444017-UpdateWo
|
||||
import { AddExecutionEntityIndexes1644424784709 } from './1644424784709-AddExecutionEntityIndexes';
|
||||
import { CreateUserManagement1646992772331 } from './1646992772331-CreateUserManagement';
|
||||
import { LowerCaseUserEmail1648740597343 } from './1648740597343-LowerCaseUserEmail';
|
||||
import { AddUserSettings1652367743993 } from './1652367743993-AddUserSettings';
|
||||
|
||||
export const mysqlMigrations = [
|
||||
InitialMigration1588157391238,
|
||||
@@ -30,4 +31,5 @@ export const mysqlMigrations = [
|
||||
AddExecutionEntityIndexes1644424784709,
|
||||
CreateUserManagement1646992772331,
|
||||
LowerCaseUserEmail1648740597343,
|
||||
AddUserSettings1652367743993,
|
||||
];
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
import * as config from '../../../../config';
|
||||
|
||||
export class AddUserSettings1652367743993 implements MigrationInterface {
|
||||
name = 'AddUserSettings1652367743993';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
let tablePrefix = config.getEnv('database.tablePrefix');
|
||||
const schema = config.getEnv('database.postgresdb.schema');
|
||||
if (schema) {
|
||||
tablePrefix = schema + '.' + tablePrefix;
|
||||
}
|
||||
|
||||
await queryRunner.query(`ALTER TABLE ${tablePrefix}user ADD COLUMN settings json`);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE ${tablePrefix}user ALTER COLUMN "personalizationAnswers" TYPE json USING to_jsonb("personalizationAnswers")::json;`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
let tablePrefix = config.getEnv('database.tablePrefix');
|
||||
const schema = config.getEnv('database.postgresdb.schema');
|
||||
if (schema) {
|
||||
tablePrefix = schema + '.' + tablePrefix;
|
||||
}
|
||||
|
||||
await queryRunner.query(`ALTER TABLE ${tablePrefix}user DROP COLUMN settings`);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { AddExecutionEntityIndexes1644422880309 } from './1644422880309-AddExecu
|
||||
import { IncreaseTypeVarcharLimit1646834195327 } from './1646834195327-IncreaseTypeVarcharLimit';
|
||||
import { CreateUserManagement1646992772331 } from './1646992772331-CreateUserManagement';
|
||||
import { LowerCaseUserEmail1648740597343 } from './1648740597343-LowerCaseUserEmail';
|
||||
import { AddUserSettings1652367743993 } from './1652367743993-AddUserSettings';
|
||||
|
||||
export const postgresMigrations = [
|
||||
InitialMigration1587669153312,
|
||||
@@ -26,4 +27,5 @@ export const postgresMigrations = [
|
||||
IncreaseTypeVarcharLimit1646834195327,
|
||||
CreateUserManagement1646992772331,
|
||||
LowerCaseUserEmail1648740597343,
|
||||
AddUserSettings1652367743993,
|
||||
];
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
import * as config from '../../../../config';
|
||||
import { logMigrationEnd, logMigrationStart } from '../../utils/migrationHelpers';
|
||||
|
||||
export class AddUserSettings1652367743993 implements MigrationInterface {
|
||||
name = 'AddUserSettings1652367743993';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
logMigrationStart(this.name);
|
||||
|
||||
const tablePrefix = config.getEnv('database.tablePrefix');
|
||||
|
||||
await queryRunner.query('PRAGMA foreign_keys=OFF');
|
||||
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "temporary_user" ("id" varchar PRIMARY KEY NOT NULL, "email" varchar(255), "firstName" varchar(32), "lastName" varchar(32), "password" varchar, "resetPasswordToken" varchar, "resetPasswordTokenExpiration" integer DEFAULT NULL, "personalizationAnswers" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "globalRoleId" integer NOT NULL, "settings" text, CONSTRAINT "FK_${tablePrefix}f0609be844f9200ff4365b1bb3d" FOREIGN KEY ("globalRoleId") REFERENCES "${tablePrefix}role" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "temporary_user"("id", "email", "firstName", "lastName", "password", "resetPasswordToken", "resetPasswordTokenExpiration", "personalizationAnswers", "createdAt", "updatedAt", "globalRoleId") SELECT "id", "email", "firstName", "lastName", "password", "resetPasswordToken", "resetPasswordTokenExpiration", "personalizationAnswers", "createdAt", "updatedAt", "globalRoleId" FROM "${tablePrefix}user"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "${tablePrefix}user"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_user" RENAME TO "${tablePrefix}user"`);
|
||||
|
||||
await queryRunner.query(
|
||||
`CREATE UNIQUE INDEX "UQ_${tablePrefix}e12875dfb3b1d92d7d7c5377e2" ON "${tablePrefix}user" ("email")`,
|
||||
);
|
||||
|
||||
await queryRunner.query('PRAGMA foreign_keys=ON');
|
||||
|
||||
logMigrationEnd(this.name);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
const tablePrefix = config.getEnv('database.tablePrefix');
|
||||
|
||||
await queryRunner.query('PRAGMA foreign_keys=OFF');
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "${tablePrefix}user" RENAME TO "temporary_user"`);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "${tablePrefix}user" ("id" varchar PRIMARY KEY NOT NULL, "email" varchar(255), "firstName" varchar(32), "lastName" varchar(32), "password" varchar, "resetPasswordToken" varchar, "resetPasswordTokenExpiration" integer DEFAULT NULL, "personalizationAnswers" text, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "globalRoleId" integer NOT NULL, CONSTRAINT "FK_${tablePrefix}f0609be844f9200ff4365b1bb3d" FOREIGN KEY ("globalRoleId") REFERENCES "${tablePrefix}role" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "${tablePrefix}user"("id", "email", "firstName", "lastName", "password", "resetPasswordToken", "resetPasswordTokenExpiration", "personalizationAnswers", "createdAt", "updatedAt", "globalRoleId") SELECT "id", "email", "firstName", "lastName", "password", "resetPasswordToken", "resetPasswordTokenExpiration", "personalizationAnswers", "createdAt", "updatedAt", "globalRoleId" FROM "temporary_user"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "temporary_user"`);
|
||||
await queryRunner.query(
|
||||
`CREATE UNIQUE INDEX "UQ_${tablePrefix}e12875dfb3b1d92d7d7c5377e2" ON "${tablePrefix}user" ("email")`,
|
||||
);
|
||||
|
||||
await queryRunner.query('PRAGMA foreign_keys=ON');
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { UpdateWorkflowCredentials1630330987096 } from './1630330987096-UpdateWo
|
||||
import { AddExecutionEntityIndexes1644421939510 } from './1644421939510-AddExecutionEntityIndexes';
|
||||
import { CreateUserManagement1646992772331 } from './1646992772331-CreateUserManagement';
|
||||
import { LowerCaseUserEmail1648740597343 } from './1648740597343-LowerCaseUserEmail';
|
||||
import { AddUserSettings1652367743993 } from './1652367743993-AddUserSettings';
|
||||
|
||||
const sqliteMigrations = [
|
||||
InitialMigration1588102412422,
|
||||
@@ -26,6 +27,7 @@ const sqliteMigrations = [
|
||||
AddExecutionEntityIndexes1644421939510,
|
||||
CreateUserManagement1646992772331,
|
||||
LowerCaseUserEmail1648740597343,
|
||||
AddUserSettings1652367743993,
|
||||
];
|
||||
|
||||
export { sqliteMigrations };
|
||||
|
||||
Reference in New Issue
Block a user