adding skills to experiences, profiles, and projects
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,2 +1,9 @@
|
||||
.vscode/*
|
||||
node_modules
|
||||
**/.env
|
||||
**/.env.*
|
||||
**/*.env
|
||||
.DS_Store
|
||||
*.auto.tfvars
|
||||
.dapr
|
||||
**/**/secrets.json
|
||||
.prod
|
||||
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM node:24
|
||||
|
||||
WORKDIR app
|
||||
COPY ./api/dist ./api/dist
|
||||
COPY ./api/package.json package-lock.json ./api
|
||||
COPY ./client/dist ./client/dist
|
||||
|
||||
WORKDIR api
|
||||
RUN npm ci
|
||||
|
||||
WORKDIR /
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "./app/api/dist/main.js"]
|
||||
# ENTRYPOINT ["tail", "-f", "/dev/null"]
|
||||
@@ -6,5 +6,11 @@ export default () => ({
|
||||
clientSecret: process.env.CLIENT_SECRET,
|
||||
issuer: process.env.ISSUER_URL,
|
||||
jwksUri: process.env.JWKS_URI,
|
||||
tenantId: process.env.TENANT_ID
|
||||
tenantId: process.env.TENANT_ID,
|
||||
db: {
|
||||
sync: process.env.DB_SYNC,
|
||||
migrationsRun: process.env.DB_MIGRATIONS_RUN,
|
||||
path: process.env.DB_PATH
|
||||
},
|
||||
dbPath: process.env.DB_PATH
|
||||
})
|
||||
@@ -12,7 +12,7 @@ export const dataSourceOptions: DataSourceOptions & SeederOptions = {
|
||||
database: configService.get<string>('DB_PATH'),
|
||||
entities: ['../**/*.entity.js'],
|
||||
migrations: ['dist/database/migrations/**/*.js'],
|
||||
migrationsRun: false,
|
||||
migrationsRun: configService.get<boolean>('DB_MIGRATIONS_RUN'),
|
||||
seeds: ['dist/database/seeds/**/*.ts'],
|
||||
synchronize: configService.get<boolean>('DB_SYNC')
|
||||
}
|
||||
|
||||
@@ -1,56 +1,122 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class InitialMigration1782738482901 implements MigrationInterface {
|
||||
name = 'InitialMigration1782738482901'
|
||||
export class InitialMigration1784577734670 implements MigrationInterface {
|
||||
name = 'InitialMigration1784577734670'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE "statuses" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL)`);
|
||||
await queryRunner.query(`CREATE TABLE "skill_categories" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`CREATE TABLE "skill_levels" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`CREATE TABLE "skill_ranks" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "weight" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`CREATE TABLE "skills" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "categoryId" varchar NOT NULL, "levelId" integer NOT NULL, "rankId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_168133e3158020b53392549d3e" UNIQUE ("rankId"))`);
|
||||
await queryRunner.query(`CREATE TABLE "projects" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "iconName" varchar NOT NULL, "order" integer NOT NULL, "summary" varchar NOT NULL, "repoUrl" varchar, "siteUrl" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_5a833dfa646b99b852c67dd859" UNIQUE ("statusId"))`);
|
||||
await queryRunner.query(`CREATE TABLE "experiences" ("id" varchar PRIMARY KEY NOT NULL, "profileId" varchar NOT NULL, "companyName" varchar NOT NULL, "companyGeneric" varchar NOT NULL, "title" varchar NOT NULL, "startDate" datetime NOT NULL, "endDate" datetime, "summary" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_23dfcd8f2d1b6585b848f840d5" UNIQUE ("statusId"))`);
|
||||
await queryRunner.query(`CREATE TABLE "profiles" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "headline" varchar NOT NULL, "summary" varchar NOT NULL, "statusId" integer NOT NULL, "userId" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_a0e37cd676d13d116f73d494ee" UNIQUE ("statusId"))`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_skills" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "categoryId" varchar NOT NULL, "levelId" integer NOT NULL, "rankId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_168133e3158020b53392549d3e" UNIQUE ("rankId"), CONSTRAINT "FK_06d267f85858229c10a01a08ad7" FOREIGN KEY ("categoryId") REFERENCES "skill_categories" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_168133e3158020b53392549d3ee" FOREIGN KEY ("rankId") REFERENCES "skill_ranks" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_cad5a64685c1be599c10bb7fc7b" FOREIGN KEY ("levelId") REFERENCES "skill_levels" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_skills"("id", "name", "categoryId", "levelId", "rankId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "name", "categoryId", "levelId", "rankId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "skills"`);
|
||||
await queryRunner.query(`DROP TABLE "skills"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_skills" RENAME TO "skills"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_projects" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "iconName" varchar NOT NULL, "order" integer NOT NULL, "summary" varchar NOT NULL, "repoUrl" varchar, "siteUrl" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_5a833dfa646b99b852c67dd859" UNIQUE ("statusId"), CONSTRAINT "FK_5a833dfa646b99b852c67dd8593" FOREIGN KEY ("statusId") REFERENCES "statuses" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_projects"("id", "name", "iconName", "order", "summary", "repoUrl", "siteUrl", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "name", "iconName", "order", "summary", "repoUrl", "siteUrl", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "projects"`);
|
||||
await queryRunner.query(`DROP TABLE "projects"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_projects" RENAME TO "projects"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_experiences" ("id" varchar PRIMARY KEY NOT NULL, "profileId" varchar NOT NULL, "companyName" varchar NOT NULL, "companyGeneric" varchar NOT NULL, "title" varchar NOT NULL, "startDate" datetime NOT NULL, "endDate" datetime, "summary" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_23dfcd8f2d1b6585b848f840d5" UNIQUE ("statusId"), CONSTRAINT "FK_be01c61f0c549f2187b5c05c349" FOREIGN KEY ("profileId") REFERENCES "profiles" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_23dfcd8f2d1b6585b848f840d5e" FOREIGN KEY ("statusId") REFERENCES "statuses" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_experiences"("id", "profileId", "companyName", "companyGeneric", "title", "startDate", "endDate", "summary", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "profileId", "companyName", "companyGeneric", "title", "startDate", "endDate", "summary", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "experiences"`);
|
||||
await queryRunner.query(`DROP TABLE "experiences"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_experiences" RENAME TO "experiences"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_profiles" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "headline" varchar NOT NULL, "summary" varchar NOT NULL, "statusId" integer NOT NULL, "userId" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_a0e37cd676d13d116f73d494ee" UNIQUE ("statusId"), CONSTRAINT "FK_a0e37cd676d13d116f73d494ee6" FOREIGN KEY ("statusId") REFERENCES "statuses" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`CREATE TABLE "profiles" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "headline" varchar NOT NULL, "summary" varchar NOT NULL, "statusId" integer NOT NULL, "userId" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`CREATE TABLE "experiences" ("id" varchar PRIMARY KEY NOT NULL, "profileId" varchar NOT NULL, "companyName" varchar NOT NULL, "companyGeneric" varchar NOT NULL, "title" varchar NOT NULL, "startDate" datetime NOT NULL, "endDate" datetime, "summary" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`CREATE TABLE "skills" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "categoryId" varchar NOT NULL, "levelId" integer NOT NULL, "rankId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`CREATE TABLE "projects" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "iconName" varchar NOT NULL, "order" integer NOT NULL, "summary" varchar NOT NULL, "repoUrl" varchar, "siteUrl" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`CREATE TABLE "profiles_skills_skills" ("profilesId" varchar NOT NULL, "skillsId" varchar NOT NULL, PRIMARY KEY ("profilesId", "skillsId"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_8401800ec9c15b8f383ac8d9c0" ON "profiles_skills_skills" ("profilesId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_36a9448df496235d81a7ceb089" ON "profiles_skills_skills" ("skillsId") `);
|
||||
await queryRunner.query(`CREATE TABLE "experiences_skills_skills" ("experiencesId" varchar NOT NULL, "skillsId" varchar NOT NULL, PRIMARY KEY ("experiencesId", "skillsId"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_54957c4ce34cebce0c6f152b87" ON "experiences_skills_skills" ("experiencesId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_b39ff48a29419ea48077b619c4" ON "experiences_skills_skills" ("skillsId") `);
|
||||
await queryRunner.query(`CREATE TABLE "projects_skills_skills" ("projectsId" varchar NOT NULL, "skillsId" varchar NOT NULL, PRIMARY KEY ("projectsId", "skillsId"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_d8128e9b444108a09d81529ea0" ON "projects_skills_skills" ("projectsId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_5291aa5a2295552d375007fefe" ON "projects_skills_skills" ("skillsId") `);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_profiles" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "headline" varchar NOT NULL, "summary" varchar NOT NULL, "statusId" integer NOT NULL, "userId" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "FK_a0e37cd676d13d116f73d494ee6" FOREIGN KEY ("statusId") REFERENCES "statuses" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_profiles"("id", "name", "headline", "summary", "statusId", "userId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "name", "headline", "summary", "statusId", "userId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "profiles"`);
|
||||
await queryRunner.query(`DROP TABLE "profiles"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_profiles" RENAME TO "profiles"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_experiences" ("id" varchar PRIMARY KEY NOT NULL, "profileId" varchar NOT NULL, "companyName" varchar NOT NULL, "companyGeneric" varchar NOT NULL, "title" varchar NOT NULL, "startDate" datetime NOT NULL, "endDate" datetime, "summary" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "FK_23dfcd8f2d1b6585b848f840d5e" FOREIGN KEY ("statusId") REFERENCES "statuses" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_be01c61f0c549f2187b5c05c349" FOREIGN KEY ("profileId") REFERENCES "profiles" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_experiences"("id", "profileId", "companyName", "companyGeneric", "title", "startDate", "endDate", "summary", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "profileId", "companyName", "companyGeneric", "title", "startDate", "endDate", "summary", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "experiences"`);
|
||||
await queryRunner.query(`DROP TABLE "experiences"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_experiences" RENAME TO "experiences"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_skills" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "categoryId" varchar NOT NULL, "levelId" integer NOT NULL, "rankId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "FK_06d267f85858229c10a01a08ad7" FOREIGN KEY ("categoryId") REFERENCES "skill_categories" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_168133e3158020b53392549d3ee" FOREIGN KEY ("rankId") REFERENCES "skill_ranks" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_cad5a64685c1be599c10bb7fc7b" FOREIGN KEY ("levelId") REFERENCES "skill_levels" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_skills"("id", "name", "categoryId", "levelId", "rankId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "name", "categoryId", "levelId", "rankId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "skills"`);
|
||||
await queryRunner.query(`DROP TABLE "skills"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_skills" RENAME TO "skills"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_projects" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "iconName" varchar NOT NULL, "order" integer NOT NULL, "summary" varchar NOT NULL, "repoUrl" varchar, "siteUrl" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "FK_5a833dfa646b99b852c67dd8593" FOREIGN KEY ("statusId") REFERENCES "statuses" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_projects"("id", "name", "iconName", "order", "summary", "repoUrl", "siteUrl", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "name", "iconName", "order", "summary", "repoUrl", "siteUrl", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "projects"`);
|
||||
await queryRunner.query(`DROP TABLE "projects"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_projects" RENAME TO "projects"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_8401800ec9c15b8f383ac8d9c0"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_36a9448df496235d81a7ceb089"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_profiles_skills_skills" ("profilesId" varchar NOT NULL, "skillsId" varchar NOT NULL, CONSTRAINT "FK_8401800ec9c15b8f383ac8d9c0d" FOREIGN KEY ("profilesId") REFERENCES "profiles" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_36a9448df496235d81a7ceb089d" FOREIGN KEY ("skillsId") REFERENCES "skills" ("id") ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY ("profilesId", "skillsId"))`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_profiles_skills_skills"("profilesId", "skillsId") SELECT "profilesId", "skillsId" FROM "profiles_skills_skills"`);
|
||||
await queryRunner.query(`DROP TABLE "profiles_skills_skills"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_profiles_skills_skills" RENAME TO "profiles_skills_skills"`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_8401800ec9c15b8f383ac8d9c0" ON "profiles_skills_skills" ("profilesId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_36a9448df496235d81a7ceb089" ON "profiles_skills_skills" ("skillsId") `);
|
||||
await queryRunner.query(`DROP INDEX "IDX_54957c4ce34cebce0c6f152b87"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_b39ff48a29419ea48077b619c4"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_experiences_skills_skills" ("experiencesId" varchar NOT NULL, "skillsId" varchar NOT NULL, CONSTRAINT "FK_54957c4ce34cebce0c6f152b875" FOREIGN KEY ("experiencesId") REFERENCES "experiences" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_b39ff48a29419ea48077b619c46" FOREIGN KEY ("skillsId") REFERENCES "skills" ("id") ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY ("experiencesId", "skillsId"))`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_experiences_skills_skills"("experiencesId", "skillsId") SELECT "experiencesId", "skillsId" FROM "experiences_skills_skills"`);
|
||||
await queryRunner.query(`DROP TABLE "experiences_skills_skills"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_experiences_skills_skills" RENAME TO "experiences_skills_skills"`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_54957c4ce34cebce0c6f152b87" ON "experiences_skills_skills" ("experiencesId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_b39ff48a29419ea48077b619c4" ON "experiences_skills_skills" ("skillsId") `);
|
||||
await queryRunner.query(`DROP INDEX "IDX_d8128e9b444108a09d81529ea0"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_5291aa5a2295552d375007fefe"`);
|
||||
await queryRunner.query(`CREATE TABLE "temporary_projects_skills_skills" ("projectsId" varchar NOT NULL, "skillsId" varchar NOT NULL, CONSTRAINT "FK_d8128e9b444108a09d81529ea06" FOREIGN KEY ("projectsId") REFERENCES "projects" ("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "FK_5291aa5a2295552d375007fefee" FOREIGN KEY ("skillsId") REFERENCES "skills" ("id") ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY ("projectsId", "skillsId"))`);
|
||||
await queryRunner.query(`INSERT INTO "temporary_projects_skills_skills"("projectsId", "skillsId") SELECT "projectsId", "skillsId" FROM "projects_skills_skills"`);
|
||||
await queryRunner.query(`DROP TABLE "projects_skills_skills"`);
|
||||
await queryRunner.query(`ALTER TABLE "temporary_projects_skills_skills" RENAME TO "projects_skills_skills"`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_d8128e9b444108a09d81529ea0" ON "projects_skills_skills" ("projectsId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_5291aa5a2295552d375007fefe" ON "projects_skills_skills" ("skillsId") `);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "profiles" RENAME TO "temporary_profiles"`);
|
||||
await queryRunner.query(`CREATE TABLE "profiles" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "headline" varchar NOT NULL, "summary" varchar NOT NULL, "statusId" integer NOT NULL, "userId" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_a0e37cd676d13d116f73d494ee" UNIQUE ("statusId"))`);
|
||||
await queryRunner.query(`INSERT INTO "profiles"("id", "name", "headline", "summary", "statusId", "userId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "name", "headline", "summary", "statusId", "userId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "temporary_profiles"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_profiles"`);
|
||||
await queryRunner.query(`ALTER TABLE "experiences" RENAME TO "temporary_experiences"`);
|
||||
await queryRunner.query(`CREATE TABLE "experiences" ("id" varchar PRIMARY KEY NOT NULL, "profileId" varchar NOT NULL, "companyName" varchar NOT NULL, "companyGeneric" varchar NOT NULL, "title" varchar NOT NULL, "startDate" datetime NOT NULL, "endDate" datetime, "summary" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_23dfcd8f2d1b6585b848f840d5" UNIQUE ("statusId"))`);
|
||||
await queryRunner.query(`INSERT INTO "experiences"("id", "profileId", "companyName", "companyGeneric", "title", "startDate", "endDate", "summary", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "profileId", "companyName", "companyGeneric", "title", "startDate", "endDate", "summary", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "temporary_experiences"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_experiences"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_5291aa5a2295552d375007fefe"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_d8128e9b444108a09d81529ea0"`);
|
||||
await queryRunner.query(`ALTER TABLE "projects_skills_skills" RENAME TO "temporary_projects_skills_skills"`);
|
||||
await queryRunner.query(`CREATE TABLE "projects_skills_skills" ("projectsId" varchar NOT NULL, "skillsId" varchar NOT NULL, PRIMARY KEY ("projectsId", "skillsId"))`);
|
||||
await queryRunner.query(`INSERT INTO "projects_skills_skills"("projectsId", "skillsId") SELECT "projectsId", "skillsId" FROM "temporary_projects_skills_skills"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_projects_skills_skills"`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_5291aa5a2295552d375007fefe" ON "projects_skills_skills" ("skillsId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_d8128e9b444108a09d81529ea0" ON "projects_skills_skills" ("projectsId") `);
|
||||
await queryRunner.query(`DROP INDEX "IDX_b39ff48a29419ea48077b619c4"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_54957c4ce34cebce0c6f152b87"`);
|
||||
await queryRunner.query(`ALTER TABLE "experiences_skills_skills" RENAME TO "temporary_experiences_skills_skills"`);
|
||||
await queryRunner.query(`CREATE TABLE "experiences_skills_skills" ("experiencesId" varchar NOT NULL, "skillsId" varchar NOT NULL, PRIMARY KEY ("experiencesId", "skillsId"))`);
|
||||
await queryRunner.query(`INSERT INTO "experiences_skills_skills"("experiencesId", "skillsId") SELECT "experiencesId", "skillsId" FROM "temporary_experiences_skills_skills"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_experiences_skills_skills"`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_b39ff48a29419ea48077b619c4" ON "experiences_skills_skills" ("skillsId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_54957c4ce34cebce0c6f152b87" ON "experiences_skills_skills" ("experiencesId") `);
|
||||
await queryRunner.query(`DROP INDEX "IDX_36a9448df496235d81a7ceb089"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_8401800ec9c15b8f383ac8d9c0"`);
|
||||
await queryRunner.query(`ALTER TABLE "profiles_skills_skills" RENAME TO "temporary_profiles_skills_skills"`);
|
||||
await queryRunner.query(`CREATE TABLE "profiles_skills_skills" ("profilesId" varchar NOT NULL, "skillsId" varchar NOT NULL, PRIMARY KEY ("profilesId", "skillsId"))`);
|
||||
await queryRunner.query(`INSERT INTO "profiles_skills_skills"("profilesId", "skillsId") SELECT "profilesId", "skillsId" FROM "temporary_profiles_skills_skills"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_profiles_skills_skills"`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_36a9448df496235d81a7ceb089" ON "profiles_skills_skills" ("skillsId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_8401800ec9c15b8f383ac8d9c0" ON "profiles_skills_skills" ("profilesId") `);
|
||||
await queryRunner.query(`ALTER TABLE "projects" RENAME TO "temporary_projects"`);
|
||||
await queryRunner.query(`CREATE TABLE "projects" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "iconName" varchar NOT NULL, "order" integer NOT NULL, "summary" varchar NOT NULL, "repoUrl" varchar, "siteUrl" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_5a833dfa646b99b852c67dd859" UNIQUE ("statusId"))`);
|
||||
await queryRunner.query(`CREATE TABLE "projects" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "iconName" varchar NOT NULL, "order" integer NOT NULL, "summary" varchar NOT NULL, "repoUrl" varchar, "siteUrl" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`INSERT INTO "projects"("id", "name", "iconName", "order", "summary", "repoUrl", "siteUrl", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "name", "iconName", "order", "summary", "repoUrl", "siteUrl", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "temporary_projects"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_projects"`);
|
||||
await queryRunner.query(`ALTER TABLE "skills" RENAME TO "temporary_skills"`);
|
||||
await queryRunner.query(`CREATE TABLE "skills" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "categoryId" varchar NOT NULL, "levelId" integer NOT NULL, "rankId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar, CONSTRAINT "REL_168133e3158020b53392549d3e" UNIQUE ("rankId"))`);
|
||||
await queryRunner.query(`CREATE TABLE "skills" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "categoryId" varchar NOT NULL, "levelId" integer NOT NULL, "rankId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`INSERT INTO "skills"("id", "name", "categoryId", "levelId", "rankId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "name", "categoryId", "levelId", "rankId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "temporary_skills"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_skills"`);
|
||||
await queryRunner.query(`DROP TABLE "profiles"`);
|
||||
await queryRunner.query(`DROP TABLE "experiences"`);
|
||||
await queryRunner.query(`ALTER TABLE "experiences" RENAME TO "temporary_experiences"`);
|
||||
await queryRunner.query(`CREATE TABLE "experiences" ("id" varchar PRIMARY KEY NOT NULL, "profileId" varchar NOT NULL, "companyName" varchar NOT NULL, "companyGeneric" varchar NOT NULL, "title" varchar NOT NULL, "startDate" datetime NOT NULL, "endDate" datetime, "summary" varchar, "statusId" integer NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`INSERT INTO "experiences"("id", "profileId", "companyName", "companyGeneric", "title", "startDate", "endDate", "summary", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "profileId", "companyName", "companyGeneric", "title", "startDate", "endDate", "summary", "statusId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "temporary_experiences"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_experiences"`);
|
||||
await queryRunner.query(`ALTER TABLE "profiles" RENAME TO "temporary_profiles"`);
|
||||
await queryRunner.query(`CREATE TABLE "profiles" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "headline" varchar NOT NULL, "summary" varchar NOT NULL, "statusId" integer NOT NULL, "userId" varchar NOT NULL, "created" datetime NOT NULL DEFAULT (datetime('now')), "createdBy" varchar, "updated" datetime NOT NULL DEFAULT (datetime('now')), "updatedBy" varchar, "deleted" datetime, "deletedBy" varchar)`);
|
||||
await queryRunner.query(`INSERT INTO "profiles"("id", "name", "headline", "summary", "statusId", "userId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy") SELECT "id", "name", "headline", "summary", "statusId", "userId", "created", "createdBy", "updated", "updatedBy", "deleted", "deletedBy" FROM "temporary_profiles"`);
|
||||
await queryRunner.query(`DROP TABLE "temporary_profiles"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_5291aa5a2295552d375007fefe"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_d8128e9b444108a09d81529ea0"`);
|
||||
await queryRunner.query(`DROP TABLE "projects_skills_skills"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_b39ff48a29419ea48077b619c4"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_54957c4ce34cebce0c6f152b87"`);
|
||||
await queryRunner.query(`DROP TABLE "experiences_skills_skills"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_36a9448df496235d81a7ceb089"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_8401800ec9c15b8f383ac8d9c0"`);
|
||||
await queryRunner.query(`DROP TABLE "profiles_skills_skills"`);
|
||||
await queryRunner.query(`DROP TABLE "projects"`);
|
||||
await queryRunner.query(`DROP TABLE "skills"`);
|
||||
await queryRunner.query(`DROP TABLE "experiences"`);
|
||||
await queryRunner.query(`DROP TABLE "profiles"`);
|
||||
await queryRunner.query(`DROP TABLE "skill_ranks"`);
|
||||
await queryRunner.query(`DROP TABLE "skill_levels"`);
|
||||
await queryRunner.query(`DROP TABLE "skill_categories"`);
|
||||
@@ -31,6 +31,7 @@ export class ExperienceController {
|
||||
|
||||
@Post()
|
||||
async create(@Body() experienceDto: ExperienceDto): Promise<ExperienceDto> {
|
||||
console.log(experienceDto)
|
||||
try {
|
||||
return await this.experienceService.create(experienceDto);
|
||||
} catch (error) {
|
||||
@@ -45,6 +46,7 @@ export class ExperienceController {
|
||||
@Param('id') id: string,
|
||||
@Body() ExperienceDto: ExperienceDto
|
||||
) {
|
||||
console.log(ExperienceDto)
|
||||
try {
|
||||
return await this.experienceService.update(id, ExperienceDto);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { SkillEntity } from "src/skill/skill.entity";
|
||||
|
||||
export class ExperienceDto {
|
||||
companyName: string;
|
||||
companyNameGeneric: string;
|
||||
@@ -6,7 +8,7 @@ export class ExperienceDto {
|
||||
startDate: Date;
|
||||
endDate?: Date;
|
||||
summary: string;
|
||||
skills?: [];
|
||||
skills?: SkillEntity[];
|
||||
createdBy?: string;
|
||||
updatedBy?: string;
|
||||
deletedBy?: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ProfileEntity } from '../profile/profile.entity'
|
||||
import { SkillEntity } from 'src/skill/skill.entity';
|
||||
import { StatusEntity } from 'src/status/status.entity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, JoinColumn, ManyToOne, OneToMany, OneToOne, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, JoinColumn, ManyToOne, OneToMany, OneToOne, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, ManyToMany, JoinTable } from 'typeorm';
|
||||
|
||||
@Entity({ name: 'experiences' })
|
||||
export class ExperienceEntity {
|
||||
@@ -50,14 +50,15 @@ export class ExperienceEntity {
|
||||
@Column({ nullable: true })
|
||||
deletedBy: string;
|
||||
|
||||
@OneToMany(() => SkillEntity, (skill: SkillEntity) => skill.name )
|
||||
skills: SkillEntity[];
|
||||
@ManyToOne(() => StatusEntity, (status: StatusEntity) => status.name )
|
||||
@JoinColumn({ name: 'statusId' })
|
||||
status: StatusEntity;
|
||||
|
||||
@ManyToOne(() => ProfileEntity, (profile: ProfileEntity) => profile.experiences)
|
||||
@JoinColumn({ name: 'profileId' })
|
||||
profile: ProfileEntity;
|
||||
profile: ProfileEntity;
|
||||
|
||||
@OneToOne(() => StatusEntity, (status: StatusEntity) => status.name )
|
||||
@JoinColumn({ name: 'statusId' })
|
||||
status: StatusEntity;
|
||||
@ManyToMany(() => SkillEntity, (skill: SkillEntity) => skill.name )
|
||||
@JoinTable()
|
||||
skills: SkillEntity[];
|
||||
}
|
||||
@@ -13,13 +13,13 @@ export class ExperienceService {
|
||||
async find(id: string): Promise<ExperienceEntity> {
|
||||
return await this.experienceRepository.findOne({
|
||||
where: { id: id },
|
||||
relations: ['profile', 'status']
|
||||
relations: ['profile', 'skills', 'status']
|
||||
})
|
||||
}
|
||||
|
||||
async findAll(): Promise<ExperienceEntity[]> {
|
||||
return await this.experienceRepository.find({
|
||||
relations: ['profile', 'status']
|
||||
relations: ['profile', 'skills', 'status']
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
body {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export class ProfileController {
|
||||
return await this.profileService.findAll();
|
||||
} catch (error) {
|
||||
const customError = error as CustomError;
|
||||
|
||||
console.log(error)
|
||||
throw new HttpException(customError.message, customError.statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { SkillEntity } from "src/skill/skill.entity";
|
||||
|
||||
export class ProfileDto {
|
||||
name: string;
|
||||
headline: string;
|
||||
skills: SkillEntity[];
|
||||
statusId: number;
|
||||
summary: string;
|
||||
userId: string;
|
||||
statusId: number;
|
||||
createdBy?: string;
|
||||
updatedBy?: string;
|
||||
deletedBy?: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ExperienceEntity } from 'src/experience/experience.entity';
|
||||
import { SkillEntity } from 'src/skill/skill.entity';
|
||||
import { StatusEntity } from 'src/status/status.entity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, OneToOne, JoinColumn } from 'typeorm';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, OneToOne, JoinColumn, ManyToOne, ManyToMany, JoinTable } from 'typeorm';
|
||||
|
||||
@Entity({ name: 'profiles' })
|
||||
export class ProfileEntity {
|
||||
@@ -44,10 +44,11 @@ export class ProfileEntity {
|
||||
@OneToMany(() => ExperienceEntity, (experience: ExperienceEntity) => experience.profile)
|
||||
experiences: ExperienceEntity[]
|
||||
|
||||
@OneToMany(() => SkillEntity, (skill: SkillEntity) => skill.name )
|
||||
skills: SkillEntity[];
|
||||
|
||||
@OneToOne(() => StatusEntity, (status: StatusEntity) => status.name )
|
||||
@ManyToOne(() => StatusEntity, (status: StatusEntity) => status.name )
|
||||
@JoinColumn({ name: 'statusId' })
|
||||
status: StatusEntity;
|
||||
|
||||
@ManyToMany(() => SkillEntity, (skill: SkillEntity) => skill.name )
|
||||
@JoinTable()
|
||||
skills: SkillEntity[];
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export class ProfileService {
|
||||
async find(id: string): Promise<ProfileEntity> {
|
||||
const profile = await this.profileRepository.findOne({
|
||||
where: { id: id },
|
||||
relations: ['status']
|
||||
relations: ['skills', 'status']
|
||||
});
|
||||
|
||||
console.log(profile);
|
||||
@@ -22,7 +22,7 @@ export class ProfileService {
|
||||
|
||||
async findAll(): Promise<ProfileEntity[]> {
|
||||
return await this.profileRepository.find({
|
||||
relations: ['status']
|
||||
relations: ['skills', 'status']
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { SkillEntity } from "src/skill/skill.entity";
|
||||
|
||||
export class ProjectDto {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -6,7 +8,7 @@ export class ProjectDto {
|
||||
summary: string;
|
||||
repoUrl?: string;
|
||||
siteUrl?: string;
|
||||
skills?: [];
|
||||
skills?: SkillEntity[];
|
||||
createdBy?: string;
|
||||
updatedBy?: string;
|
||||
deletedBy?: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SkillEntity } from 'src/skill/skill.entity';
|
||||
import { StatusEntity } from 'src/status/status.entity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, OneToOne, JoinColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, OneToOne, JoinColumn, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, ManyToOne, ManyToMany, JoinTable } from 'typeorm';
|
||||
|
||||
@Entity({ name: 'projects' })
|
||||
export class ProjectEntity {
|
||||
@@ -46,10 +46,11 @@ export class ProjectEntity {
|
||||
@Column({ nullable: true })
|
||||
deletedBy: string
|
||||
|
||||
@OneToMany(() => SkillEntity, (skill: SkillEntity) => skill.name )
|
||||
skills: SkillEntity[];
|
||||
|
||||
@OneToOne(() => StatusEntity, (status: StatusEntity) => status.name )
|
||||
@ManyToOne(() => StatusEntity, (status: StatusEntity) => status.name )
|
||||
@JoinColumn({ name: 'statusId' })
|
||||
status: StatusEntity;
|
||||
status: StatusEntity;
|
||||
|
||||
@ManyToMany(() => SkillEntity, (skill: SkillEntity) => skill.name )
|
||||
@JoinTable()
|
||||
skills: SkillEntity[];
|
||||
}
|
||||
|
||||
@@ -13,16 +13,15 @@ export class ProjectService {
|
||||
async find(id: string): Promise<ProjectEntity> {
|
||||
const project = await this.projectRepository.findOne({
|
||||
where: { id: id },
|
||||
relations: ['status']
|
||||
relations: ['skills', 'status']
|
||||
});
|
||||
|
||||
console.log(project);
|
||||
return project;
|
||||
}
|
||||
|
||||
async findAll(): Promise<ProjectEntity[]> {
|
||||
return await this.projectRepository.find({
|
||||
relations: ['status']
|
||||
relations: ['skills', 'status']
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,5 @@ export class SkillLevelEntity {
|
||||
|
||||
@Column({ nullable: true })
|
||||
deletedBy: string;
|
||||
|
||||
@OneToMany(() => SkillEntity, (skill: SkillEntity) => skill.name)
|
||||
skills: SkillEntity[];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { SkillCategoryEntity } from 'src/skill/skill-category.entity';
|
||||
import { Entity, PrimaryGeneratedColumn, Column, JoinColumn, ManyToOne, ManyToMany, OneToOne, CreateDateColumn, UpdateDateColumn, DeleteDateColumn } from 'typeorm';
|
||||
import { SkillLevelEntity } from './skill-level.entity';
|
||||
import { SkillRankEntity } from './skill-rank.entity';
|
||||
import { ExperienceEntity } from 'src/experience/experience.entity';
|
||||
|
||||
@Entity({ name: 'skills' })
|
||||
export class SkillEntity {
|
||||
@@ -42,11 +43,14 @@ export class SkillEntity {
|
||||
@JoinColumn({ name: 'categoryId' })
|
||||
category: SkillCategoryEntity;
|
||||
|
||||
@OneToOne(() => SkillRankEntity, (rank: SkillRankEntity) => rank.name)
|
||||
@ManyToOne(() => SkillRankEntity, (rank: SkillRankEntity) => rank.name)
|
||||
@JoinColumn({ name: 'rankId' })
|
||||
rank: SkillRankEntity;
|
||||
|
||||
@ManyToOne(() => SkillLevelEntity, (level: SkillLevelEntity) => level.name)
|
||||
@JoinColumn({ name: 'levelId' })
|
||||
level: SkillLevelEntity;
|
||||
|
||||
@ManyToMany(() => ExperienceEntity, (experience: ExperienceEntity) => experience.skills)
|
||||
experiences: ExperienceEntity[]
|
||||
}
|
||||
@@ -9,11 +9,15 @@ import { AxiosError, AxiosResponse } from "axios";
|
||||
import { ScreenSize } from "../../enums/screenSize";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faSave, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useAppContext } from "../../hooks/appContext/UseAppContext";
|
||||
|
||||
const SkillCategoryForm = ({ isDrawerOpen, mode, onOpenClose, categoryId }: SkillCategoryFormProps) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const appContext = useAppContext();
|
||||
const defaultValues = {
|
||||
name: ''
|
||||
name: '',
|
||||
createdBy: '',
|
||||
updatedBy: ''
|
||||
}
|
||||
const methods = useForm({
|
||||
defaultValues: defaultValues
|
||||
@@ -26,14 +30,25 @@ const SkillCategoryForm = ({ isDrawerOpen, mode, onOpenClose, categoryId }: Skil
|
||||
onOpenClose(FormMode.CANCEL)
|
||||
}
|
||||
|
||||
const onSubmit = async (data: unknown) => {
|
||||
const onSubmit = async (data: any) => {
|
||||
try {
|
||||
dispatch({ type: 'SET_IS_LOADING', payload: true })
|
||||
|
||||
if (!categoryId) {
|
||||
await httpClient.post(`api/skill-categories`, data);
|
||||
const newData = {
|
||||
...data,
|
||||
createdBy: appContext.state.userProfile.userPrincipalName,
|
||||
updatedBy: appContext.state.userProfile.userPrincipalName
|
||||
}
|
||||
|
||||
await httpClient.post(`api/skill-categories`, newData);
|
||||
} else {
|
||||
await httpClient.put(`api/skill-categories`)
|
||||
const newData = {
|
||||
...data,
|
||||
updatedBy: appContext.state.userProfile.userPrincipalName
|
||||
}
|
||||
|
||||
await httpClient.put(`api/skill-categories`, newData)
|
||||
}
|
||||
|
||||
methods.reset(defaultValues);
|
||||
|
||||
@@ -9,14 +9,18 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faSave, faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||
import { ScreenSize } from '../../enums/screenSize';
|
||||
import { useBreakpoints } from '../../hooks/breakpoints/UseBreakpoints';
|
||||
import { useAppContext } from '../../hooks/appContext/UseAppContext';
|
||||
|
||||
const SkillForm = ({ isDrawerOpen, mode, onOpenClose, skillId }: SkillFormProps) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const appContext = useAppContext();
|
||||
const defaultValues = {
|
||||
name: '',
|
||||
categoryId: '',
|
||||
levelId: '',
|
||||
rankId: ''
|
||||
rankId: '',
|
||||
createdBy: '',
|
||||
updatedBy: ''
|
||||
}
|
||||
const methods = useForm({
|
||||
defaultValues: defaultValues
|
||||
@@ -29,14 +33,25 @@ const SkillForm = ({ isDrawerOpen, mode, onOpenClose, skillId }: SkillFormProps)
|
||||
onOpenClose(FormMode.CANCEL);
|
||||
};
|
||||
|
||||
const onSubmit = async (data: unknown) => {
|
||||
const onSubmit = async (data: any) => {
|
||||
try {
|
||||
dispatch({ type: 'SET_IS_LOADING', payload: true });
|
||||
console.log(data)
|
||||
if (!skillId) {
|
||||
await httpClient.post(`api/skills`, data);
|
||||
const newData = {
|
||||
...data,
|
||||
createdBy: appContext.state.userProfile.userPrincipalName,
|
||||
updatedBy: appContext.state.userProfile.userPrincipalName
|
||||
}
|
||||
|
||||
await httpClient.post(`api/skills`, newData);
|
||||
} else {
|
||||
await httpClient.put(`api/skills/skill/${skillId}`, data);
|
||||
const newData = {
|
||||
...data,
|
||||
updatedBy: appContext.state.userProfile.userPrincipalName
|
||||
}
|
||||
|
||||
await httpClient.put(`api/skills/skill/${skillId}`, newData);
|
||||
}
|
||||
|
||||
methods.reset(defaultValues);
|
||||
|
||||
@@ -12,14 +12,17 @@ import { useBreakpoints } from '../../hooks/breakpoints/UseBreakpoints';
|
||||
import { Profile } from '../profiles/Profile.interface';
|
||||
import { useProfiles } from '../../hooks/profiles/UseProfiles';
|
||||
import { useAppContext } from '../../hooks/appContext/UseAppContext';
|
||||
import { Listbox, ListboxButton, ListboxOption, ListboxOptions } from '@headlessui/react';
|
||||
import { useSkills } from '../../hooks/skills/UseSkills';
|
||||
import MultiSelectDropdown from '../multiSelectDropdown/MultiSelectDropdown';
|
||||
import { Skill } from '../skills/Skill.interface';
|
||||
import { Experience } from '../experiences/Experience.interface';
|
||||
|
||||
const ExperienceForm = ({ isDrawerOpen, mode, onOpenClose, experienceId }: ExperienceFormProps) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const appContext = useAppContext();
|
||||
const { profiles } = useProfiles();
|
||||
const { skills } = useSkills();
|
||||
const experienceSkills: Skill[] = []
|
||||
const defaultValues = {
|
||||
profileId: '',
|
||||
companyName: '',
|
||||
@@ -27,7 +30,7 @@ const ExperienceForm = ({ isDrawerOpen, mode, onOpenClose, experienceId }: Exper
|
||||
title: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
skills: [],
|
||||
skills: experienceSkills,
|
||||
summary: '',
|
||||
statusId: 1,
|
||||
createdBy: '',
|
||||
@@ -36,9 +39,23 @@ const ExperienceForm = ({ isDrawerOpen, mode, onOpenClose, experienceId }: Exper
|
||||
const methods = useForm({
|
||||
defaultValues: defaultValues
|
||||
});
|
||||
// const watchRepoName = methods.watch(['repoName'])
|
||||
|
||||
const { screenSize } = useBreakpoints();
|
||||
|
||||
const onSetSelectedSkills = (selectedSkill: Skill) => {
|
||||
const selectedSkillExists = state.selectedSkills.find((skill) => skill.id === selectedSkill.id)
|
||||
let newSelectedSkills: Skill[];
|
||||
|
||||
if (selectedSkillExists) {
|
||||
newSelectedSkills = state.selectedSkills.filter((skill) => skill.id !== selectedSkillExists.id)
|
||||
} else {
|
||||
newSelectedSkills = [...state.selectedSkills, selectedSkill]
|
||||
}
|
||||
|
||||
dispatch({ type: 'SET_SELECTED_SKILLS', payload: newSelectedSkills })
|
||||
methods.setValue('skills', newSelectedSkills)
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
methods.reset(defaultValues);
|
||||
dispatch({ type: 'SET_IS_DISABLED', payload: false });
|
||||
@@ -91,6 +108,10 @@ const ExperienceForm = ({ isDrawerOpen, mode, onOpenClose, experienceId }: Exper
|
||||
`api/experiences/${experienceId}`
|
||||
);
|
||||
const experience = response.data;
|
||||
|
||||
if (experience.skills) {
|
||||
dispatch({ type: 'SET_SELECTED_SKILLS', payload: experience.skills })
|
||||
}
|
||||
|
||||
methods.reset(experience);
|
||||
} catch (error) {
|
||||
@@ -127,7 +148,9 @@ const ExperienceForm = ({ isDrawerOpen, mode, onOpenClose, experienceId }: Exper
|
||||
}, [profiles]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'SET_SKILL_OPTIONS', payload: skills })
|
||||
if (skills) {
|
||||
dispatch({ type: 'SET_SKILL_OPTIONS', payload: skills })
|
||||
}
|
||||
}, [skills])
|
||||
|
||||
return (
|
||||
@@ -296,23 +319,12 @@ const ExperienceForm = ({ isDrawerOpen, mode, onOpenClose, experienceId }: Exper
|
||||
<Controller
|
||||
name="skills"
|
||||
control={methods.control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
// <input
|
||||
// type='text'
|
||||
// className='input w-full'
|
||||
// disabled={state.isDisabled}
|
||||
// onChange={onChange}
|
||||
// value={value}
|
||||
// />
|
||||
<Listbox value={value} onChange={onChange}>
|
||||
<ListboxOptions anchor='bottom'>
|
||||
{state.skillOptions?.map((skill) => (
|
||||
<ListboxOption key={skill.id} value={skill.id}>
|
||||
{skill.name}
|
||||
</ListboxOption>
|
||||
))}
|
||||
</ListboxOptions>
|
||||
</Listbox>
|
||||
render={() => (
|
||||
<MultiSelectDropdown
|
||||
skills={state.skillOptions}
|
||||
onChange={onSetSelectedSkills}
|
||||
selectedSkills={state.selectedSkills}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -6,5 +6,6 @@ export interface ExperienceFormState {
|
||||
isDisabled: boolean;
|
||||
isLoading: boolean;
|
||||
profileOptions: Profile[];
|
||||
skillOptions: Skill[] | undefined;
|
||||
selectedSkills: Skill[];
|
||||
skillOptions: Skill[];
|
||||
}
|
||||
@@ -7,13 +7,15 @@ type Action =
|
||||
| { type: 'SET_IS_DISABLED'; payload: boolean }
|
||||
| { type: 'SET_IS_LOADING'; payload: boolean }
|
||||
| { type: 'SET_PROFILE_OPTIONS'; payload: Profile[] }
|
||||
| { type: 'SET_SKILL_OPTIONS'; payload: Skill[] | undefined };
|
||||
| { type: 'SET_SELECTED_SKILLS'; payload: Skill[] }
|
||||
| { type: 'SET_SKILL_OPTIONS'; payload: Skill[] };
|
||||
|
||||
export const initialState: ExperienceFormState = {
|
||||
error: undefined,
|
||||
isDisabled: false,
|
||||
isLoading: true,
|
||||
profileOptions: [],
|
||||
selectedSkills: [],
|
||||
skillOptions: []
|
||||
};
|
||||
|
||||
@@ -46,6 +48,12 @@ export const reducer = (
|
||||
profileOptions: action.payload
|
||||
}
|
||||
}
|
||||
case 'SET_SELECTED_SKILLS': {
|
||||
return {
|
||||
...state,
|
||||
selectedSkills: action.payload
|
||||
}
|
||||
}
|
||||
case 'SET_SKILL_OPTIONS': {
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -12,7 +12,7 @@ export interface Experience {
|
||||
startDate: Date;
|
||||
endDate?: Date;
|
||||
summary?: string;
|
||||
skills: Skill[];
|
||||
skills?: Skill[];
|
||||
profile: Profile;
|
||||
status: Status;
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { useUserRole } from '../../hooks/userRole/UseUserRole';
|
||||
import Alert from '../../alert/Alert';
|
||||
import { CellContext, ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, useReactTable } from '@tanstack/react-table';
|
||||
import { Profile } from '../profiles/Profile.interface';
|
||||
import { Skill } from '../skills/Skill.interface';
|
||||
|
||||
interface ActionsProps {
|
||||
id: string;
|
||||
@@ -166,6 +167,17 @@ const Experiences = () => {
|
||||
accessorKey: 'summary',
|
||||
header: 'Summary'
|
||||
},
|
||||
{
|
||||
id: 'skills',
|
||||
accessorKey: 'skills',
|
||||
header: 'Skills',
|
||||
cell: (info: CellContext<Experience, unknown>) => {
|
||||
const skills = info.getValue() as Skill[];
|
||||
const skillsString: string = skills.map((skill) => skill.name).join(', ')
|
||||
|
||||
return skillsString
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
accessorKey: 'status',
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { MultiSelectDropdownProps } from "./MultiSelectDropdownProps";
|
||||
import { Skill } from "../skills/Skill.interface";
|
||||
|
||||
const MultiSelectDropdown = ({ options }: MultiSelectDropdownProps) => {
|
||||
const MultiSelectDropdown = ({ onChange, skills, selectedSkills }: MultiSelectDropdownProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedValues, setSelectedValues] = useState([])
|
||||
// const [selectedValues, setSelectedValues] = useState([])
|
||||
const dropdownRef = useRef(null)
|
||||
|
||||
const handleToggleOption = (value: any) => {
|
||||
let updated;
|
||||
// let updated;
|
||||
|
||||
if (selectedValues.includes(value)) {
|
||||
updated = selectedValues.filter((item) => item !== value);
|
||||
} else {
|
||||
updated = [...selectedValues, value];
|
||||
}
|
||||
// if (selectedValues.includes(value)) {
|
||||
// updated = selectedValues.filter((item) => item !== value);
|
||||
// } else {
|
||||
// updated = [...selectedValues, value];
|
||||
// }
|
||||
|
||||
setSelectedValues(updated);
|
||||
if (onChange) onChange(updated)
|
||||
// setSelectedValues(updated);
|
||||
// if (onChange) onChange(updated)
|
||||
}
|
||||
|
||||
const handleRemoveBadge = (event, value) => {
|
||||
event.stopPropagation();
|
||||
// event.stopPropagation();
|
||||
|
||||
const updated = selectedValues.filter((item) => item !== value);
|
||||
// const updated = selectedValues.filter((item) => item !== value);
|
||||
|
||||
setSelectedValues(updated)
|
||||
// setSelectedValues(updated)
|
||||
|
||||
if (onChange) onchange(updated)
|
||||
// if (onChange) onchange(updated)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -50,21 +51,15 @@ const MultiSelectDropdown = ({ options }: MultiSelectDropdownProps) => {
|
||||
className="select select-bordered w-full h-auto min-h-12 flex flex-wrap items-center gap-1 p-2 bg-base-100 text-left cursor-pointer"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
{selectedValues.length === 0 ? (
|
||||
{selectedSkills.length === 0 ? (
|
||||
<span className="text-base-content/50"></span>
|
||||
) : (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{selectedValues.map((val) => {
|
||||
const option = options.find((o: any) => o.value === val);
|
||||
{selectedSkills.map((selectedSkill: Skill) => {
|
||||
const option = skills.find((skill: Skill) => skill.id === selectedSkill.id);
|
||||
return (
|
||||
<div key={val} className="badge badge-primary gap-1 py-3 px-2">
|
||||
{option?.label || val}
|
||||
<button
|
||||
onClick={(event) => handleRemoveBadge(event, val)}
|
||||
className="btn btn-ghost btn-xs p-0 min-h-0 h-auto text-primary-content hover:bg-transparent"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
<div key={selectedSkill.id} className="badge badge-primary gap-1 py-3 px-2">
|
||||
{option?.name || selectedSkill.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
@@ -75,16 +70,16 @@ const MultiSelectDropdown = ({ options }: MultiSelectDropdownProps) => {
|
||||
tabIndex={0}
|
||||
className="dropdown-content menu p-2 shadow-lg bg-base-100 rounded-box w-full max-h-60 overflow-y-auto z-[1] border border-base-200"
|
||||
>
|
||||
{options.map((option) => (
|
||||
<li key={option.value} className="p-0">
|
||||
{skills.map((skill) => (
|
||||
<li key={skill.id} className="p-0">
|
||||
<label className="label cursor-pointer justify-start gap-3 px-4 py-2 hover:bg-base-200 rounded-lg w-full">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="checkbox checkbox-primary checkbox-sm"
|
||||
checked={selectedValues.includes(option.value)}
|
||||
onChange={() => handleToggleOption(option.value)}
|
||||
checked={selectedSkills.includes(skill)}
|
||||
onChange={() => onChange(skill)}
|
||||
/>
|
||||
<span className="label-text text-base-content">{option.label}</span>
|
||||
<span className="label-text text-base-content">{skill.name}</span>
|
||||
</label>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { Skill } from "../skills/Skill.interface";
|
||||
|
||||
export interface MultiSelectDropdownProps {
|
||||
options: { label: string; value: string }[];
|
||||
onChange: (selectedSkill: Skill) => void;
|
||||
skills: Skill[];
|
||||
selectedSkills: Skill[];
|
||||
}
|
||||
@@ -10,15 +10,21 @@ import { faSave, faXmark } from '@fortawesome/free-solid-svg-icons';
|
||||
import { ScreenSize } from '../../enums/screenSize';
|
||||
import { useBreakpoints } from '../../hooks/breakpoints/UseBreakpoints';
|
||||
import { useAppContext } from '../../hooks/appContext/UseAppContext';
|
||||
import { Skill } from '../skills/Skill.interface';
|
||||
import { useSkills } from '../../hooks/skills/UseSkills';
|
||||
import MultiSelectDropdown from '../multiSelectDropdown/MultiSelectDropdown';
|
||||
|
||||
const ProfileForm = ({ isDrawerOpen, mode, onOpenClose, profileId }: ProfileFormProps) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const appContext = useAppContext();
|
||||
const { skills } = useSkills();
|
||||
const projectSkills: Skill[] = []
|
||||
const defaultValues = {
|
||||
name: '',
|
||||
headline: '',
|
||||
summary: '',
|
||||
userId: '',
|
||||
skills: projectSkills,
|
||||
statusId: 1
|
||||
}
|
||||
const methods = useForm({
|
||||
@@ -27,6 +33,20 @@ const ProfileForm = ({ isDrawerOpen, mode, onOpenClose, profileId }: ProfileForm
|
||||
// const watchRepoName = methods.watch(['repoName'])
|
||||
const { screenSize } = useBreakpoints();
|
||||
|
||||
const onSetSelectedSkills = (selectedSkill: Skill) => {
|
||||
const selectedSkillExists = state.selectedSkills.find((skill) => skill.id === selectedSkill.id)
|
||||
let newSelectedSkills: Skill[];
|
||||
|
||||
if (selectedSkillExists) {
|
||||
newSelectedSkills = state.selectedSkills.filter((skill) => skill.id !== selectedSkillExists.id)
|
||||
} else {
|
||||
newSelectedSkills = [...state.selectedSkills, selectedSkill]
|
||||
}
|
||||
|
||||
dispatch({ type: 'SET_SELECTED_SKILLS', payload: newSelectedSkills })
|
||||
methods.setValue('skills', newSelectedSkills)
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
methods.reset(defaultValues);
|
||||
dispatch({ type: 'SET_IS_DISABLED', payload: false });
|
||||
@@ -80,9 +100,13 @@ const ProfileForm = ({ isDrawerOpen, mode, onOpenClose, profileId }: ProfileForm
|
||||
const response: AxiosResponse = await httpClient.get(
|
||||
`api/profiles/${profileId}`
|
||||
);
|
||||
const entry = response.data;
|
||||
const profile = response.data;
|
||||
|
||||
methods.reset(entry);
|
||||
if (profile.skills) {
|
||||
dispatch({ type: 'SET_SELECTED_SKILLS', payload: profile.skills })
|
||||
}
|
||||
|
||||
methods.reset(profile);
|
||||
} catch (error) {
|
||||
const axiosError = error as AxiosError;
|
||||
console.log(axiosError)
|
||||
@@ -97,6 +121,12 @@ const ProfileForm = ({ isDrawerOpen, mode, onOpenClose, profileId }: ProfileForm
|
||||
}
|
||||
}, [profileId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (skills) {
|
||||
dispatch({ type: 'SET_SKILL_OPTIONS', payload: skills })
|
||||
}
|
||||
}, [skills])
|
||||
|
||||
return (
|
||||
<div className='drawer drawer-end'>
|
||||
<input type='checkbox' className='drawer-toggle' onChange={() => {}} checked={isDrawerOpen} />
|
||||
@@ -175,6 +205,22 @@ const ProfileForm = ({ isDrawerOpen, mode, onOpenClose, profileId }: ProfileForm
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className={`col-span-3 self-center after:content-['*'] after:ms-0.5`}>
|
||||
<span>Skills</span>
|
||||
</div>
|
||||
<div className='col-span-9'>
|
||||
<Controller
|
||||
name="skills"
|
||||
control={methods.control}
|
||||
render={() => (
|
||||
<MultiSelectDropdown
|
||||
skills={state.skillOptions}
|
||||
onChange={onSetSelectedSkills}
|
||||
selectedSkills={state.selectedSkills}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className='col-span-12 justify-self-end self-center'>
|
||||
<button
|
||||
className='btn'
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Skill } from "../skills/Skill.interface";
|
||||
|
||||
export interface ProfileFormState {
|
||||
error: string | undefined;
|
||||
isDisabled: boolean;
|
||||
isLoading: boolean;
|
||||
selectedSkills: Skill[];
|
||||
skillOptions: Skill[];
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
import { Profile } from "../profiles/Profile.interface";
|
||||
import { Skill } from "../skills/Skill.interface";
|
||||
import { ProfileFormState } from "./ProfileFormState.interface"
|
||||
|
||||
type Action =
|
||||
| { type: 'SET_ERROR'; payload: string | undefined }
|
||||
| { type: 'SET_IS_DISABLED'; payload: boolean }
|
||||
| { type: 'SET_IS_LOADING'; payload: boolean };
|
||||
| { type: 'SET_IS_LOADING'; payload: boolean }
|
||||
| { type: 'SET_SELECTED_SKILLS'; payload: Skill[] }
|
||||
| { type: 'SET_SKILL_OPTIONS'; payload: Skill[] };
|
||||
|
||||
export const initialState: ProfileFormState = {
|
||||
error: undefined,
|
||||
isDisabled: false,
|
||||
isLoading: true
|
||||
isLoading: true,
|
||||
selectedSkills: [],
|
||||
skillOptions: []
|
||||
};
|
||||
|
||||
export const reducer = (
|
||||
@@ -34,6 +39,18 @@ export const reducer = (
|
||||
...state,
|
||||
isLoading: action.payload
|
||||
};
|
||||
}
|
||||
case 'SET_SELECTED_SKILLS': {
|
||||
return {
|
||||
...state,
|
||||
selectedSkills: action.payload
|
||||
}
|
||||
}
|
||||
case 'SET_SKILL_OPTIONS': {
|
||||
return {
|
||||
...state,
|
||||
skillOptions: action.payload
|
||||
}
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
|
||||
@@ -16,6 +16,7 @@ import { UserRole } from '../../enums/userRole';
|
||||
import { useUserRole } from '../../hooks/userRole/UseUserRole';
|
||||
import Alert from '../../alert/Alert';
|
||||
import { CellContext, ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, useReactTable } from '@tanstack/react-table';
|
||||
import { Skill } from '../skills/Skill.interface';
|
||||
|
||||
interface ActionsProps {
|
||||
id: string;
|
||||
@@ -144,6 +145,17 @@ const Profiles = () => {
|
||||
accessorKey: 'summary',
|
||||
header: 'Summary'
|
||||
},
|
||||
{
|
||||
id: 'skills',
|
||||
accessorKey: 'skills',
|
||||
header: 'Skills',
|
||||
cell: (info: CellContext<Profile, unknown>) => {
|
||||
const skills = info.getValue() as Skill[];
|
||||
const skillsString: string = skills.map((skill) => skill.name).join(', ')
|
||||
|
||||
return skillsString
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
accessorKey: 'status',
|
||||
|
||||
@@ -11,10 +11,15 @@ import { ScreenSize } from '../../enums/screenSize';
|
||||
import { useBreakpoints } from '../../hooks/breakpoints/UseBreakpoints';
|
||||
import { useProfiles } from '../../hooks/profiles/UseProfiles';
|
||||
import { Profile } from '../profiles/Profile.interface';
|
||||
import { Skill } from '../skills/Skill.interface';
|
||||
import { useSkills } from '../../hooks/skills/UseSkills';
|
||||
import MultiSelectDropdown from '../multiSelectDropdown/MultiSelectDropdown';
|
||||
|
||||
const ProjectForm = ({ isDrawerOpen, mode, onOpenClose, projectId }: ProjectFormProps) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const { profiles } = useProfiles()
|
||||
const { profiles } = useProfiles();
|
||||
const { skills } = useSkills();
|
||||
const projectSkills: Skill[] = []
|
||||
const defaultValues = {
|
||||
profileId: '',
|
||||
name: '',
|
||||
@@ -23,6 +28,7 @@ const ProjectForm = ({ isDrawerOpen, mode, onOpenClose, projectId }: ProjectForm
|
||||
repoUrl: '',
|
||||
siteUrl: '',
|
||||
order: '',
|
||||
skills: projectSkills,
|
||||
statusId: 1
|
||||
}
|
||||
const methods = useForm({
|
||||
@@ -31,6 +37,20 @@ const ProjectForm = ({ isDrawerOpen, mode, onOpenClose, projectId }: ProjectForm
|
||||
// const watchRepoName = methods.watch(['repoName'])
|
||||
const { screenSize } = useBreakpoints();
|
||||
|
||||
const onSetSelectedSkills = (selectedSkill: Skill) => {
|
||||
const selectedSkillExists = state.selectedSkills.find((skill) => skill.id === selectedSkill.id)
|
||||
let newSelectedSkills: Skill[];
|
||||
|
||||
if (selectedSkillExists) {
|
||||
newSelectedSkills = state.selectedSkills.filter((skill) => skill.id !== selectedSkillExists.id)
|
||||
} else {
|
||||
newSelectedSkills = [...state.selectedSkills, selectedSkill]
|
||||
}
|
||||
|
||||
dispatch({ type: 'SET_SELECTED_SKILLS', payload: newSelectedSkills })
|
||||
methods.setValue('skills', newSelectedSkills)
|
||||
}
|
||||
|
||||
const onCancel = () => {
|
||||
methods.reset(defaultValues);
|
||||
dispatch({ type: 'SET_IS_DISABLED', payload: false });
|
||||
@@ -44,7 +64,7 @@ const ProjectForm = ({ isDrawerOpen, mode, onOpenClose, projectId }: ProjectForm
|
||||
if (!projectId) {
|
||||
await httpClient.post(`api/projects`, data);
|
||||
} else {
|
||||
await httpClient.put(`api/projects/project/${projectId}`, data);
|
||||
await httpClient.put(`api/projects/${projectId}`, data);
|
||||
}
|
||||
|
||||
methods.reset(defaultValues);
|
||||
@@ -77,11 +97,15 @@ const ProjectForm = ({ isDrawerOpen, mode, onOpenClose, projectId }: ProjectForm
|
||||
dispatch({ type: 'SET_IS_LOADING', payload: true });
|
||||
|
||||
const response: AxiosResponse = await httpClient.get(
|
||||
`api/projects/project/${projectId}`
|
||||
`api/projects/${projectId}`
|
||||
);
|
||||
const entry = response.data;
|
||||
const project = response.data;
|
||||
|
||||
methods.reset(entry);
|
||||
if (project.skills) {
|
||||
dispatch({ type: 'SET_SELECTED_SKILLS', payload: project.skills })
|
||||
}
|
||||
|
||||
methods.reset(project);
|
||||
} catch (error) {
|
||||
const axiosError = error as AxiosError;
|
||||
console.log(axiosError)
|
||||
@@ -107,13 +131,20 @@ const ProjectForm = ({ isDrawerOpen, mode, onOpenClose, projectId }: ProjectForm
|
||||
summary: '',
|
||||
userId: '',
|
||||
experiences: [],
|
||||
skills: []
|
||||
skills: [],
|
||||
status: null
|
||||
})
|
||||
|
||||
dispatch({ type: 'SET_PROFILE_OPTIONS', payload: newProfileOptions });
|
||||
}
|
||||
}, [profiles]);
|
||||
|
||||
useEffect(() => {
|
||||
if (skills) {
|
||||
dispatch({ type: 'SET_SKILL_OPTIONS', payload: skills })
|
||||
}
|
||||
}, [skills])
|
||||
|
||||
return (
|
||||
<div className='drawer drawer-end'>
|
||||
<input type='checkbox' className='drawer-toggle' onChange={() => {}} checked={isDrawerOpen} />
|
||||
@@ -272,6 +303,22 @@ const ProjectForm = ({ isDrawerOpen, mode, onOpenClose, projectId }: ProjectForm
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className={`col-span-3 self-center after:content-['*'] after:ms-0.5`}>
|
||||
<span>Skills</span>
|
||||
</div>
|
||||
<div className='col-span-9'>
|
||||
<Controller
|
||||
name="skills"
|
||||
control={methods.control}
|
||||
render={() => (
|
||||
<MultiSelectDropdown
|
||||
skills={state.skillOptions}
|
||||
onChange={onSetSelectedSkills}
|
||||
selectedSkills={state.selectedSkills}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className='col-span-12 justify-self-end self-center'>
|
||||
<button
|
||||
className='btn'
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Profile } from "../profiles/Profile.interface";
|
||||
import { Skill } from "../skills/Skill.interface";
|
||||
|
||||
export interface ProjectFormState {
|
||||
error: string | undefined;
|
||||
isDisabled: boolean;
|
||||
isLoading: boolean;
|
||||
profileOptions: Profile[];
|
||||
selectedSkills: Skill[];
|
||||
skillOptions: Skill[];
|
||||
}
|
||||
@@ -1,17 +1,22 @@
|
||||
import { Profile } from "../profiles/Profile.interface";
|
||||
import { Skill } from "../skills/Skill.interface";
|
||||
import { ProjectFormState } from "./ProjectFormState.interface"
|
||||
|
||||
type Action =
|
||||
| { type: 'SET_ERROR'; payload: string | undefined }
|
||||
| { type: 'SET_IS_DISABLED'; payload: boolean }
|
||||
| { type: 'SET_IS_LOADING'; payload: boolean }
|
||||
| { type: 'SET_PROFILE_OPTIONS'; payload: Profile[] };
|
||||
| { type: 'SET_PROFILE_OPTIONS'; payload: Profile[] }
|
||||
| { type: 'SET_SELECTED_SKILLS'; payload: Skill[] }
|
||||
| { type: 'SET_SKILL_OPTIONS'; payload: Skill[] };
|
||||
|
||||
export const initialState: ProjectFormState = {
|
||||
error: undefined,
|
||||
isDisabled: false,
|
||||
isLoading: true,
|
||||
profileOptions: []
|
||||
profileOptions: [],
|
||||
selectedSkills: [],
|
||||
skillOptions: []
|
||||
};
|
||||
|
||||
export const reducer = (
|
||||
@@ -43,6 +48,18 @@ export const reducer = (
|
||||
profileOptions: action.payload
|
||||
}
|
||||
}
|
||||
case 'SET_SELECTED_SKILLS': {
|
||||
return {
|
||||
...state,
|
||||
selectedSkills: action.payload
|
||||
}
|
||||
}
|
||||
case 'SET_SKILL_OPTIONS': {
|
||||
return {
|
||||
...state,
|
||||
skillOptions: action.payload
|
||||
}
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,4 @@
|
||||
import { Project } from './Project.interface';
|
||||
// import {
|
||||
// Alert,
|
||||
// Box,
|
||||
// Button,
|
||||
// Card,
|
||||
// CardActions,
|
||||
// CardContent,
|
||||
// CardHeader,
|
||||
// Container,
|
||||
// Grid,
|
||||
// Icon,
|
||||
// IconName,
|
||||
// Skeleton,
|
||||
// Stack,
|
||||
// Typography
|
||||
// } from '@noahspan/noahspan-components';
|
||||
import { FormMode } from '../../enums/formMode';
|
||||
import { useEffect, useReducer } from 'react';
|
||||
import { initialState, reducer } from './reducer';
|
||||
@@ -32,6 +16,7 @@ import { UserRole } from '../../enums/userRole';
|
||||
import { useUserRole } from '../../hooks/userRole/UseUserRole';
|
||||
import Alert from '../../alert/Alert';
|
||||
import { CellContext, ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, useReactTable } from '@tanstack/react-table';
|
||||
import { Skill } from '../skills/Skill.interface';
|
||||
|
||||
interface ActionsProps {
|
||||
id: string;
|
||||
@@ -169,6 +154,17 @@ const Projects = () => {
|
||||
accessorKey: 'summary',
|
||||
header: 'Summary'
|
||||
},
|
||||
{
|
||||
id: 'skills',
|
||||
accessorKey: 'skills',
|
||||
header: 'Skills',
|
||||
cell: (info: CellContext<Project, unknown>) => {
|
||||
const skills = info.getValue() as Skill[];
|
||||
const skillsString: string = skills.map((skill) => skill.name).join(', ')
|
||||
|
||||
return skillsString
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
accessorKey: 'status',
|
||||
|
||||
BIN
database/portfolio.db
Normal file
BIN
database/portfolio.db
Normal file
Binary file not shown.
70
docker-compose.yaml
Normal file
70
docker-compose.yaml
Normal file
@@ -0,0 +1,70 @@
|
||||
services:
|
||||
azurite:
|
||||
container_name: azurite-portfolio
|
||||
image: mcr.microsoft.com/azure-storage/azurite
|
||||
ports:
|
||||
- '10000:10000'
|
||||
- '10001:10001'
|
||||
- '10002:10002'
|
||||
command: 'azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --loose --skipApiVersionCheck'
|
||||
healthcheck:
|
||||
test: ["CMD", "nc", "-z", "127.0.0.1", "10000"]
|
||||
volumes:
|
||||
- ./azurite-data:/data
|
||||
|
||||
# seed:
|
||||
# image: mcr.microsoft.com/azure-cli
|
||||
# depends_on:
|
||||
# azurite:
|
||||
# condition: service_healthy
|
||||
# volumes:
|
||||
# - ./seed-data:/data
|
||||
# entrypoint: >
|
||||
# sh -c "
|
||||
# az storage container create --name mycontainer --connection-string 'DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;' &&
|
||||
# az storage blob upload-batch --destination mycontainer --source /data --connection-string 'DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;'
|
||||
# "
|
||||
|
||||
restore:
|
||||
container_name: restore
|
||||
image: litestream/litestream:0.5.2
|
||||
volumes:
|
||||
- ./database/portfolio.db:/mnt/data/portfolio.db
|
||||
- ./database/litestream/litestream.yml:/mnt/litestream/litestream.yml
|
||||
- backup:/mnt/data/backup
|
||||
- data:/mnt/data
|
||||
command: restore -config /mnt/litestream/litestream.yml -if-db-not-exists -if-replica-exists /mnt/data/portfolio.db
|
||||
|
||||
app:
|
||||
container_name: portfolio
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
- '3000:3000'
|
||||
env_file:
|
||||
- ./api/.env
|
||||
environment:
|
||||
- DB_PATH=../../mnt/data/portfolio.db
|
||||
volumes:
|
||||
- data:/mnt/data
|
||||
depends_on:
|
||||
restore:
|
||||
condition: service_completed_successfully
|
||||
|
||||
replicate:
|
||||
container_name: replicate
|
||||
image: litestream/litestream:0.5.2
|
||||
volumes:
|
||||
- ./database/litestream/litestream.yml:/mnt/litestream/litestream.yml
|
||||
- backup:/mnt
|
||||
- data:/mnt/data
|
||||
command: replicate -config /mnt/litestream/litestream.yml
|
||||
depends_on:
|
||||
app:
|
||||
condition: service_started
|
||||
|
||||
volumes:
|
||||
azurite-data:
|
||||
backup:
|
||||
data:
|
||||
# seed-data:
|
||||
14535
package-lock.json
generated
Normal file
14535
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"workspaces": [
|
||||
"api",
|
||||
"cms",
|
||||
"client",
|
||||
"static-wfe"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user