From 0957cd9b6f69e7973cbeff95748f697f9817d618 Mon Sep 17 00:00:00 2001 From: Adam Revesz Date: Thu, 23 Apr 2026 20:25:36 +0200 Subject: [PATCH 01/58] AI evaluation, company dashboard and routerlink service to navigate to the proper dashboard based on the user role --- .../Controllers/GeminiController.cs | 45 + .../Controllers/JobsController.cs | 6 + .../Controllers/UserController.cs | 7 + .../20260423084452_JobsNewField.Designer.cs | 976 ++++++++++++++++++ .../Migrations/20260423084452_JobsNewField.cs | 29 + .../SkillProofDbContextModelSnapshot.cs | 4 + .../Dtos/Gemini/GradeAnswerDto.cs | 14 + .../Dtos/Jobs/JobCreateDto.cs | 1 + .../Dtos/Jobs/JobViewDto.cs | 2 + .../Dtos/Tests/UserTestsDto.cs | 15 + .../Models/Gemini/GradingRequest.cs | 14 + .../Models/Gemini/GradingResult.cs | 13 + .../SkillProof.Entities/Models/Job.cs | 3 + .../SkillProof.Logic/Gemini/GeminiService.cs | 84 ++ .../SkillProof.Logic/Gemini/IGeminiService.cs | 14 + .../SkillProof.Logic/Jobs/IJobLogic.cs | 1 + .../SkillProof.Logic/Jobs/JobLogic.cs | 33 + .../SkillProof.Logic/SkillProof.Logic.csproj | 1 + .../SkillProof.Logic/User/IUserLogic.cs | 2 + .../SkillProof.Logic/User/UserLogic.cs | 30 +- web/public/Assets/Junior.svg | 20 + web/public/Assets/Medior.svg | 20 + web/public/Assets/Senior.svg | 20 + web/public/Assets/Unknown.svg | 12 + web/src/app/Models/Dtos/Job/JobCreate-dto.ts | 1 + web/src/app/Models/Dtos/Job/JobView-dto.ts | 1 + web/src/app/Models/Dtos/Job/job.ts | 1 + web/src/app/Models/Dtos/User/userTests-dto.ts | 6 + web/src/app/app-module.ts | 8 + web/src/app/app-routing-module.ts | 5 + .../components/company-home/company-home.html | 74 ++ .../components/company-home/company-home.scss | 30 + .../company-home/company-home.spec.ts | 22 + .../components/company-home/company-home.ts | 77 ++ web/src/app/components/job-edit/job-edit.html | 8 +- web/src/app/components/job-edit/job-edit.scss | 4 + web/src/app/components/job-edit/job-edit.ts | 9 + .../app/components/jobupload/jobupload.html | 70 +- .../app/components/jobupload/jobupload.scss | 8 +- web/src/app/components/jobupload/jobupload.ts | 10 +- web/src/app/components/login/login.ts | 6 +- .../components/profile-view/profile-view.ts | 4 + web/src/app/header/header.html | 20 +- web/src/app/header/header.scss | 8 +- web/src/app/header/header.ts | 6 + web/src/app/services/dashboardRouting.ts | 53 + web/src/app/services/job-service.ts | 18 + web/src/app/services/profile-service.ts | 22 + web/src/styles.scss | 41 +- web/tsconfig.app.json | 1 + 50 files changed, 1814 insertions(+), 65 deletions(-) create mode 100644 backend/SkillProof/SkillProof.Api/Controllers/GeminiController.cs create mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260423084452_JobsNewField.Designer.cs create mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260423084452_JobsNewField.cs create mode 100644 backend/SkillProof/SkillProof.Entities/Dtos/Gemini/GradeAnswerDto.cs create mode 100644 backend/SkillProof/SkillProof.Entities/Dtos/Tests/UserTestsDto.cs create mode 100644 backend/SkillProof/SkillProof.Entities/Models/Gemini/GradingRequest.cs create mode 100644 backend/SkillProof/SkillProof.Entities/Models/Gemini/GradingResult.cs create mode 100644 backend/SkillProof/SkillProof.Logic/Gemini/GeminiService.cs create mode 100644 backend/SkillProof/SkillProof.Logic/Gemini/IGeminiService.cs create mode 100644 web/public/Assets/Junior.svg create mode 100644 web/public/Assets/Medior.svg create mode 100644 web/public/Assets/Senior.svg create mode 100644 web/public/Assets/Unknown.svg create mode 100644 web/src/app/Models/Dtos/User/userTests-dto.ts create mode 100644 web/src/app/components/company-home/company-home.html create mode 100644 web/src/app/components/company-home/company-home.scss create mode 100644 web/src/app/components/company-home/company-home.spec.ts create mode 100644 web/src/app/components/company-home/company-home.ts create mode 100644 web/src/app/services/dashboardRouting.ts diff --git a/backend/SkillProof/SkillProof.Api/Controllers/GeminiController.cs b/backend/SkillProof/SkillProof.Api/Controllers/GeminiController.cs new file mode 100644 index 0000000..ceab954 --- /dev/null +++ b/backend/SkillProof/SkillProof.Api/Controllers/GeminiController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Mvc; +using SkillProof.Entities.Dtos.Gemini; +using SkillProof.Entities.Models.Gemini; +using SkillProof.Logic.Gemini; + +namespace SkillProof.Api.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class GradingController : ControllerBase + { + private readonly IGeminiService _gradingService; + + public GradingController(IGeminiService gradingService) + { + _gradingService = gradingService; + } + + [HttpPost("grade")] + public async Task Grade([FromBody] GradingRequest request) + { + if (string.IsNullOrWhiteSpace(request.StudentAnswer)) + return BadRequest("Student answer cannot be empty."); + + var result = await _gradingService.EvaluateAnswerAsync(request); + return Ok(result); + } + + [HttpPost("grade-multiple")] + public async Task GradeMultiple([FromBody] List requests) + { + if (requests == null || !requests.Any()) + return BadRequest("Request list cannot be empty."); + var results = new List(); + foreach (var request in requests) + { + if (string.IsNullOrWhiteSpace(request.StudentAnswer)) + return BadRequest("Student answer cannot be empty."); + var score = await _gradingService.EvaluateAnswerAsync(request); + results.Add(score); + } + return Ok(results); + } + } +} diff --git a/backend/SkillProof/SkillProof.Api/Controllers/JobsController.cs b/backend/SkillProof/SkillProof.Api/Controllers/JobsController.cs index 3feba62..692fb73 100644 --- a/backend/SkillProof/SkillProof.Api/Controllers/JobsController.cs +++ b/backend/SkillProof/SkillProof.Api/Controllers/JobsController.cs @@ -102,4 +102,10 @@ public async Task GetCandidateTest(string id) return Ok(candidateTest); } + [HttpGet("jobsOfCompany/{id}")] + public async Task GetJobsByCompanyId(string id) + { + var jobs = await _jobLogic.GetJobsOfCompanyAsync(id); + return Ok(jobs); + } } \ No newline at end of file diff --git a/backend/SkillProof/SkillProof.Api/Controllers/UserController.cs b/backend/SkillProof/SkillProof.Api/Controllers/UserController.cs index ff50901..63844c8 100644 --- a/backend/SkillProof/SkillProof.Api/Controllers/UserController.cs +++ b/backend/SkillProof/SkillProof.Api/Controllers/UserController.cs @@ -91,5 +91,12 @@ public async Task RevokeRole(string userId) await _userLogic.RevokeRoleAsync(userId); return Ok(new { message = "Roles revoked successfully." }); } + + [HttpGet("UserTests/{userId}")] + public async Task GetUserTests(string userId) + { + var tests = await _userLogic.GetUserTestsAsync(userId); + return Ok(tests); + } } } diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260423084452_JobsNewField.Designer.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260423084452_JobsNewField.Designer.cs new file mode 100644 index 0000000..9632aba --- /dev/null +++ b/backend/SkillProof/SkillProof.Data/Migrations/20260423084452_JobsNewField.Designer.cs @@ -0,0 +1,976 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SkillProof.Data; + +#nullable disable + +namespace SkillProof.Data.Migrations +{ + [DbContext(typeof(SkillProofDbContext))] + [Migration("20260423084452_JobsNewField")] + partial class JobsNewField + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("AssessmentsJob", b => + { + b.Property("AssessmentsId") + .HasColumnType("nvarchar(450)"); + + b.Property("JobsId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("AssessmentsId", "JobsId"); + + b.HasIndex("JobsId"); + + b.ToTable("AssessmentsJob"); + }); + + modelBuilder.Entity("AssessmentsQuestions", b => + { + b.Property("AssessmentsId") + .HasColumnType("nvarchar(450)"); + + b.Property("QuestionsId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("AssessmentsId", "QuestionsId"); + + b.HasIndex("QuestionsId"); + + b.ToTable("AssessmentsQuestions"); + }); + + modelBuilder.Entity("AssessmentsTests", b => + { + b.Property("AssessmentsId") + .HasColumnType("nvarchar(450)"); + + b.Property("TestAttemptsId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("AssessmentsId", "TestAttemptsId"); + + b.HasIndex("TestAttemptsId"); + + b.ToTable("AssessmentsTests"); + }); + + modelBuilder.Entity("JobQuestions", b => + { + b.Property("JobsId") + .HasColumnType("nvarchar(450)"); + + b.Property("QuestionsId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("JobsId", "QuestionsId"); + + b.HasIndex("QuestionsId"); + + b.ToTable("JobQuestions"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(13) + .HasColumnType("nvarchar(13)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + + b.HasDiscriminator().HasValue("IdentityUser"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Assessments", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DifficultyLevel") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.ToTable("Assessments"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => + { + b.Property("QuestionId") + .HasColumnType("nvarchar(450)"); + + b.Property("AcceptedAnswers") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CodeSnippet") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("QuestionId"); + + b.ToTable("CodeCompletionQuestions"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Website") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Companies"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => + { + b.Property("QuestionId") + .HasColumnType("nvarchar(450)"); + + b.Property("Answer") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("manualFeedback") + .HasColumnType("nvarchar(max)"); + + b.HasKey("QuestionId"); + + b.ToTable("FillInTheBlankQuestions"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Job", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CompanyId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("EmploymentType") + .HasColumnType("int"); + + b.Property("Location") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ShortDescription") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId"); + + b.ToTable("Jobs"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AppliedAt") + .HasColumnType("datetime2"); + + b.Property("JobId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TestId") + .HasColumnType("nvarchar(450)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("JobId"); + + b.HasIndex("TestId") + .IsUnique() + .HasFilter("[TestId] IS NOT NULL"); + + b.HasIndex("UserId"); + + b.ToTable("JobApplications"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => + { + b.Property("QuestionId") + .HasColumnType("nvarchar(450)"); + + b.Property("AllowMultipleSelection") + .HasColumnType("bit"); + + b.Property("CorrectAnswerIds") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Options") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("QuestionId"); + + b.ToTable("MultipleChoiceQuestion"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Difficulty") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("QuestionText") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Questions"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AiFeedback") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FreeTextResponse") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsCorrect") + .HasColumnType("bit"); + + b.Property("QuestionId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("TestId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("QuestionId"); + + b.HasIndex("TestId"); + + b.ToTable("TestAnswers"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CompletedAt") + .HasColumnType("datetime2"); + + b.Property("DifficultyLevel") + .HasColumnType("int"); + + b.Property("Passed") + .HasColumnType("bit"); + + b.Property("Score") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tests"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => + { + b.Property("QuestionId") + .HasColumnType("nvarchar(450)"); + + b.Property("CorrectAnswer") + .HasColumnType("bit"); + + b.Property("Explanation") + .HasColumnType("nvarchar(max)"); + + b.HasKey("QuestionId"); + + b.ToTable("TrueFalseQuestions"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("CompanyName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("JobTitle") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("isVerified") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserExperiences"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Users", b => + { + b.HasBaseType("Microsoft.AspNetCore.Identity.IdentityUser"); + + b.Property("Bio") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("CompanyId") + .HasColumnType("nvarchar(450)"); + + b.Property("CompanyRole") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Headline") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ProfilePicture") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.HasIndex("CompanyId"); + + b.HasDiscriminator().HasValue("Users"); + }); + + modelBuilder.Entity("AssessmentsJob", b => + { + b.HasOne("SkillProof.Entities.Models.Assessments", null) + .WithMany() + .HasForeignKey("AssessmentsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SkillProof.Entities.Models.Job", null) + .WithMany() + .HasForeignKey("JobsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AssessmentsQuestions", b => + { + b.HasOne("SkillProof.Entities.Models.Assessments", null) + .WithMany() + .HasForeignKey("AssessmentsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SkillProof.Entities.Models.Questions", null) + .WithMany() + .HasForeignKey("QuestionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("AssessmentsTests", b => + { + b.HasOne("SkillProof.Entities.Models.Assessments", null) + .WithMany() + .HasForeignKey("AssessmentsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SkillProof.Entities.Models.Tests", null) + .WithMany() + .HasForeignKey("TestAttemptsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("JobQuestions", b => + { + b.HasOne("SkillProof.Entities.Models.Job", null) + .WithMany() + .HasForeignKey("JobsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SkillProof.Entities.Models.Questions", null) + .WithMany() + .HasForeignKey("QuestionsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => + { + b.HasOne("SkillProof.Entities.Models.Questions", "Question") + .WithOne("CodeCompletionQuestion") + .HasForeignKey("SkillProof.Entities.Models.CodeCompletionQuestions", "QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => + { + b.HasOne("SkillProof.Entities.Models.Questions", "Question") + .WithOne("FillInTheBlankQuestions") + .HasForeignKey("SkillProof.Entities.Models.FillInTheBlankQuestions", "QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Job", b => + { + b.HasOne("SkillProof.Entities.Models.Companies", "Company") + .WithMany("Jobs") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Company"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => + { + b.HasOne("SkillProof.Entities.Models.Job", "Job") + .WithMany("JobApplications") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SkillProof.Entities.Models.Tests", "Test") + .WithOne("JobApplication") + .HasForeignKey("SkillProof.Entities.Models.JobApplication", "TestId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("SkillProof.Entities.Models.Users", "User") + .WithMany("JobApplications") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + + b.Navigation("Test"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => + { + b.HasOne("SkillProof.Entities.Models.Questions", "Question") + .WithOne("MultipleChoiceQuestion") + .HasForeignKey("SkillProof.Entities.Models.MultipleChoiceQuestions", "QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => + { + b.HasOne("SkillProof.Entities.Models.Questions", "Question") + .WithMany("TestAnswers") + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SkillProof.Entities.Models.Tests", "Test") + .WithMany("TestAnswers") + .HasForeignKey("TestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Question"); + + b.Navigation("Test"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => + { + b.HasOne("SkillProof.Entities.Models.Users", "User") + .WithMany("Tests") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("User"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => + { + b.HasOne("SkillProof.Entities.Models.Questions", "Question") + .WithOne("TrueFalseQuestion") + .HasForeignKey("SkillProof.Entities.Models.TrueFalseQuestions", "QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => + { + b.HasOne("SkillProof.Entities.Models.Users", "User") + .WithMany("UserExperiences") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Users", b => + { + b.HasOne("SkillProof.Entities.Models.Companies", "Companies") + .WithMany("Users") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Companies"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => + { + b.Navigation("Jobs"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Job", b => + { + b.Navigation("JobApplications"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => + { + b.Navigation("CodeCompletionQuestion"); + + b.Navigation("FillInTheBlankQuestions"); + + b.Navigation("MultipleChoiceQuestion"); + + b.Navigation("TestAnswers"); + + b.Navigation("TrueFalseQuestion"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => + { + b.Navigation("JobApplication"); + + b.Navigation("TestAnswers"); + }); + + modelBuilder.Entity("SkillProof.Entities.Models.Users", b => + { + b.Navigation("JobApplications"); + + b.Navigation("Tests"); + + b.Navigation("UserExperiences"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260423084452_JobsNewField.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260423084452_JobsNewField.cs new file mode 100644 index 0000000..1ada0d7 --- /dev/null +++ b/backend/SkillProof/SkillProof.Data/Migrations/20260423084452_JobsNewField.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SkillProof.Data.Migrations +{ + /// + public partial class JobsNewField : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ShortDescription", + table: "Jobs", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ShortDescription", + table: "Jobs"); + } + } +} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/SkillProofDbContextModelSnapshot.cs b/backend/SkillProof/SkillProof.Data/Migrations/SkillProofDbContextModelSnapshot.cs index afd4d27..f493959 100644 --- a/backend/SkillProof/SkillProof.Data/Migrations/SkillProofDbContextModelSnapshot.cs +++ b/backend/SkillProof/SkillProof.Data/Migrations/SkillProofDbContextModelSnapshot.cs @@ -409,6 +409,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasMaxLength(100) .HasColumnType("nvarchar(100)"); + b.Property("ShortDescription") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("Tags") .IsRequired() .HasMaxLength(500) diff --git a/backend/SkillProof/SkillProof.Entities/Dtos/Gemini/GradeAnswerDto.cs b/backend/SkillProof/SkillProof.Entities/Dtos/Gemini/GradeAnswerDto.cs new file mode 100644 index 0000000..49c3902 --- /dev/null +++ b/backend/SkillProof/SkillProof.Entities/Dtos/Gemini/GradeAnswerDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SkillProof.Entities.Dtos.Gemini +{ + public class GradeAnswerRequest + { + public string Question { get; set; } = string.Empty; + public string UserAnswer { get; set; } = string.Empty; + } +} diff --git a/backend/SkillProof/SkillProof.Entities/Dtos/Jobs/JobCreateDto.cs b/backend/SkillProof/SkillProof.Entities/Dtos/Jobs/JobCreateDto.cs index a7c6635..bcb9f23 100644 --- a/backend/SkillProof/SkillProof.Entities/Dtos/Jobs/JobCreateDto.cs +++ b/backend/SkillProof/SkillProof.Entities/Dtos/Jobs/JobCreateDto.cs @@ -11,6 +11,7 @@ public class JobCreateDto public EmploymentType EmploymentType { get; set; } public string? Salary { get; set; } public string Description { get; set; } + public string ShortDescription { get; set; } public string Tags { get; set; } public List? AssessmentIds { get; set; } } \ No newline at end of file diff --git a/backend/SkillProof/SkillProof.Entities/Dtos/Jobs/JobViewDto.cs b/backend/SkillProof/SkillProof.Entities/Dtos/Jobs/JobViewDto.cs index f55875c..b4af710 100644 --- a/backend/SkillProof/SkillProof.Entities/Dtos/Jobs/JobViewDto.cs +++ b/backend/SkillProof/SkillProof.Entities/Dtos/Jobs/JobViewDto.cs @@ -17,6 +17,8 @@ public class JobViewDto public string Title { get; set; } public string Description { get; set; } + + public string ShortDescription { get; set; } public string Location { get; set; } diff --git a/backend/SkillProof/SkillProof.Entities/Dtos/Tests/UserTestsDto.cs b/backend/SkillProof/SkillProof.Entities/Dtos/Tests/UserTestsDto.cs new file mode 100644 index 0000000..36c87f0 --- /dev/null +++ b/backend/SkillProof/SkillProof.Entities/Dtos/Tests/UserTestsDto.cs @@ -0,0 +1,15 @@ +using SkillProof.Entities.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SkillProof.Entities.Dtos.Tests +{ + public class UserTestsDto + { + public DifficultyLevel DifficultyLevel { get; set; } + public bool Passed { get; set; } + } +} diff --git a/backend/SkillProof/SkillProof.Entities/Models/Gemini/GradingRequest.cs b/backend/SkillProof/SkillProof.Entities/Models/Gemini/GradingRequest.cs new file mode 100644 index 0000000..457689d --- /dev/null +++ b/backend/SkillProof/SkillProof.Entities/Models/Gemini/GradingRequest.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SkillProof.Entities.Models.Gemini +{ + public class GradingRequest + { + public string Question { get; set; } + public string StudentAnswer { get; set; } + } +} diff --git a/backend/SkillProof/SkillProof.Entities/Models/Gemini/GradingResult.cs b/backend/SkillProof/SkillProof.Entities/Models/Gemini/GradingResult.cs new file mode 100644 index 0000000..bc78b23 --- /dev/null +++ b/backend/SkillProof/SkillProof.Entities/Models/Gemini/GradingResult.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SkillProof.Entities.Models.Gemini +{ + public class GradingResult + { + public double Score { get; set; } + } +} diff --git a/backend/SkillProof/SkillProof.Entities/Models/Job.cs b/backend/SkillProof/SkillProof.Entities/Models/Job.cs index 5325abc..c2cef78 100644 --- a/backend/SkillProof/SkillProof.Entities/Models/Job.cs +++ b/backend/SkillProof/SkillProof.Entities/Models/Job.cs @@ -19,6 +19,9 @@ public class Job: IIdentity [Required] public string Description { get; set; } + [Required] + public string ShortDescription { get; set; } + [Required] public string Location { get; set; } diff --git a/backend/SkillProof/SkillProof.Logic/Gemini/GeminiService.cs b/backend/SkillProof/SkillProof.Logic/Gemini/GeminiService.cs new file mode 100644 index 0000000..2dd8b62 --- /dev/null +++ b/backend/SkillProof/SkillProof.Logic/Gemini/GeminiService.cs @@ -0,0 +1,84 @@ +using Google.GenAI; +using Google.GenAI.Types; +using Microsoft.Extensions.Configuration; +using SkillProof.Entities.Models.Gemini; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Http.Json; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace SkillProof.Logic.Gemini +{ + public class GeminiService : IGeminiService + { + private readonly Client _client; + private readonly IConfiguration _config; + private static readonly SemaphoreSlim _rateLimiter = new SemaphoreSlim(1, 1); + + public GeminiService(IConfiguration config) + { + string apiKey = config["Gemini:ApiKey"]; + _client = new Client(apiKey: apiKey); + } + + public async Task EvaluateAnswerAsync(GradingRequest request) + { + await _rateLimiter.WaitAsync(); + + try + { + var prompt = $@" + You are an expert strict exam evaluator. + Evaluate the user's answer to the following question. + Score the answer as 0 (completely wrong), 0.5 (partially correct), or 1 (perfect). + Respond ONLY with the number (0, 0.5, or 1). No explanation, no extra text. + + Question: {request.Question} + Student Answer: {request.StudentAnswer}"; + + var config = new GenerateContentConfig + { + ResponseMimeType = "application/json" + }; + + var response = await _client.Models.GenerateContentAsync( + model: "gemini-2.5-flash", + contents: prompt, + config: config + ); + + await Task.Delay(TimeSpan.FromSeconds(4)); + + if (double.TryParse(response.Text, out double parsedScore)) + { + return parsedScore; + } + + return 0.0; + } + finally + { + _rateLimiter.Release(); + } + } + + public async Task> EvalueateAllAsync(List requests) + { + var answers = new List(); + foreach (var request in requests) + { + double score = await EvaluateAnswerAsync(request); + answers.Add(score); + } + return answers; + } + } +} + + diff --git a/backend/SkillProof/SkillProof.Logic/Gemini/IGeminiService.cs b/backend/SkillProof/SkillProof.Logic/Gemini/IGeminiService.cs new file mode 100644 index 0000000..8b86f79 --- /dev/null +++ b/backend/SkillProof/SkillProof.Logic/Gemini/IGeminiService.cs @@ -0,0 +1,14 @@ +using SkillProof.Entities.Models.Gemini; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SkillProof.Logic.Gemini +{ + public interface IGeminiService + { + Task EvaluateAnswerAsync(GradingRequest request); + } +} diff --git a/backend/SkillProof/SkillProof.Logic/Jobs/IJobLogic.cs b/backend/SkillProof/SkillProof.Logic/Jobs/IJobLogic.cs index 603c075..8440389 100644 --- a/backend/SkillProof/SkillProof.Logic/Jobs/IJobLogic.cs +++ b/backend/SkillProof/SkillProof.Logic/Jobs/IJobLogic.cs @@ -20,4 +20,5 @@ public interface IJobLogic Task GetCandidateTestForJob(string jobId); + Task> GetJobsOfCompanyAsync(string currentUserId); } \ No newline at end of file diff --git a/backend/SkillProof/SkillProof.Logic/Jobs/JobLogic.cs b/backend/SkillProof/SkillProof.Logic/Jobs/JobLogic.cs index c88a5a8..4fa34d0 100644 --- a/backend/SkillProof/SkillProof.Logic/Jobs/JobLogic.cs +++ b/backend/SkillProof/SkillProof.Logic/Jobs/JobLogic.cs @@ -50,6 +50,7 @@ public async Task CreateJobAsync(JobCreateDto model, string companyI CompanyId = companyId, Title = model.Title, Description = _markdownService.ToHtml(model.Description), + ShortDescription = model.ShortDescription, Location = model.Location, Tags = model.Tags, EmploymentType = model.EmploymentType, @@ -75,6 +76,7 @@ public async Task CreateJobAsync(JobCreateDto model, string companyI CompanyId = newJob.CompanyId, Title = newJob.Title, Description = newJob.Description, + ShortDescription = newJob.ShortDescription, Location = newJob.Location, Tags = newJob.Tags, EmploymentType = newJob.EmploymentType, @@ -94,6 +96,7 @@ public async Task> GetAllJobsAsync() CompanyId = j.CompanyId, Title = j.Title, Description = j.Description, + ShortDescription = j.ShortDescription, Location = j.Location, Tags = j.Tags, EmploymentType = j.EmploymentType, @@ -139,6 +142,7 @@ public async Task> GetAllJobsAsync() CompanyId = job.CompanyId, Title = job.Title, Description = job.Description, + ShortDescription = job.ShortDescription, Location = job.Location, Tags = job.Tags, EmploymentType = job.EmploymentType, @@ -183,6 +187,7 @@ public async Task> GetJobsByCompanyIdAsync(string compan CompanyId = j.CompanyId, Title = j.Title, Description = j.Description, + ShortDescription = j.ShortDescription, Location = j.Location, Tags = j.Tags, EmploymentType = j.EmploymentType, @@ -229,6 +234,7 @@ public async Task UpdateJobAsync(string id, JobViewDto model, string job.Title = model.Title; job.Description = model.Description; + job.ShortDescription = model.ShortDescription; job.Location = model.Location; job.Tags = model.Tags; job.EmploymentType = model.EmploymentType; @@ -254,6 +260,7 @@ public async Task UpdateJobAsync(string id, JobViewDto model, string CompanyId = job.CompanyId, Title = job.Title, Description = job.Description, + ShortDescription = job.ShortDescription, Location = job.Location, Tags = job.Tags, EmploymentType = job.EmploymentType, @@ -440,4 +447,30 @@ public async Task> GetTestToJob(string id) } + public async Task> GetJobsOfCompanyAsync(string currentUserId) + { + var user = await _userManager.FindByIdAsync(currentUserId); + if (user == null || user.CompanyId == null) + { + throw new UnauthorizedAccessException("User profile not found or not associated with a company."); + } + + return await _jobRepository.GetAll() + .Where(j => j.CompanyId == user.CompanyId) + .Select(j => new JobViewDto + { + CompanyId = j.CompanyId, + Title = j.Title, + Description = j.Description, + ShortDescription = j.ShortDescription, + Location = j.Location, + Tags = j.Tags, + EmploymentType = j.EmploymentType, + CreatedAt = j.CreatedAt, + Id = j.Id + }) + .ToListAsync(); + } + + } \ No newline at end of file diff --git a/backend/SkillProof/SkillProof.Logic/SkillProof.Logic.csproj b/backend/SkillProof/SkillProof.Logic/SkillProof.Logic.csproj index 4165f6f..04cc734 100644 --- a/backend/SkillProof/SkillProof.Logic/SkillProof.Logic.csproj +++ b/backend/SkillProof/SkillProof.Logic/SkillProof.Logic.csproj @@ -8,6 +8,7 @@ + diff --git a/backend/SkillProof/SkillProof.Logic/User/IUserLogic.cs b/backend/SkillProof/SkillProof.Logic/User/IUserLogic.cs index 9b0b615..f6351fa 100644 --- a/backend/SkillProof/SkillProof.Logic/User/IUserLogic.cs +++ b/backend/SkillProof/SkillProof.Logic/User/IUserLogic.cs @@ -1,3 +1,4 @@ +using SkillProof.Entities.Dtos.Tests; using SkillProof.Entities.Dtos.Users; namespace SkillProof.Logic.User; @@ -13,4 +14,5 @@ public interface IUserLogic Task LoginAsync(LoginUser dto); Task GrantAdminRoleAsync(string userId); Task RevokeRoleAsync(string userId); + Task> GetUserTestsAsync(string userId); } \ No newline at end of file diff --git a/backend/SkillProof/SkillProof.Logic/User/UserLogic.cs b/backend/SkillProof/SkillProof.Logic/User/UserLogic.cs index c58cf89..834656d 100644 --- a/backend/SkillProof/SkillProof.Logic/User/UserLogic.cs +++ b/backend/SkillProof/SkillProof.Logic/User/UserLogic.cs @@ -1,16 +1,17 @@ -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; -using System.Text; -using System.Text.RegularExpressions; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using SkillProof.Data.Repositorys; +using SkillProof.Entities.Dtos.Tests; using SkillProof.Entities.Dtos.Users; using SkillProof.Entities.Helper; using SkillProof.Entities.Models; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; +using System.Text.RegularExpressions; namespace SkillProof.Logic.User { @@ -297,5 +298,26 @@ private JwtSecurityToken GenerateAccessToken(IEnumerable? claims, int exp signingCredentials: new SigningCredentials(signinKey, SecurityAlgorithms.HmacSha256) ); } + + public async Task> GetUserTestsAsync(string userId) + { + var user = await _userManager.FindByIdAsync(userId); + + if (user == null) + { + throw new KeyNotFoundException("User not found."); + } + var result = new List(); + + foreach (var test in user.Tests) + { + result.Add(new UserTestsDto + { + DifficultyLevel = test.DifficultyLevel, + Passed = test.Passed + }); + } + return result; + } } } \ No newline at end of file diff --git a/web/public/Assets/Junior.svg b/web/public/Assets/Junior.svg new file mode 100644 index 0000000..8d0b87c --- /dev/null +++ b/web/public/Assets/Junior.svg @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/web/public/Assets/Medior.svg b/web/public/Assets/Medior.svg new file mode 100644 index 0000000..3f19aa2 --- /dev/null +++ b/web/public/Assets/Medior.svg @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/web/public/Assets/Senior.svg b/web/public/Assets/Senior.svg new file mode 100644 index 0000000..a4e3961 --- /dev/null +++ b/web/public/Assets/Senior.svg @@ -0,0 +1,20 @@ + + + + + + + + \ No newline at end of file diff --git a/web/public/Assets/Unknown.svg b/web/public/Assets/Unknown.svg new file mode 100644 index 0000000..7e47817 --- /dev/null +++ b/web/public/Assets/Unknown.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/web/src/app/Models/Dtos/Job/JobCreate-dto.ts b/web/src/app/Models/Dtos/Job/JobCreate-dto.ts index 569dc83..36aab3d 100644 --- a/web/src/app/Models/Dtos/Job/JobCreate-dto.ts +++ b/web/src/app/Models/Dtos/Job/JobCreate-dto.ts @@ -4,6 +4,7 @@ export class JobCreateDto{ companyId: string = "" title: string = "" description: string = "" + shortDescription = "" EmploymentType: EmploymentType | null = null location: string = "" tags: string[] = [] diff --git a/web/src/app/Models/Dtos/Job/JobView-dto.ts b/web/src/app/Models/Dtos/Job/JobView-dto.ts index bba3e49..f403bec 100644 --- a/web/src/app/Models/Dtos/Job/JobView-dto.ts +++ b/web/src/app/Models/Dtos/Job/JobView-dto.ts @@ -6,6 +6,7 @@ export class JobViewDto { companyId: string = ''; title: string = ''; description: string = ''; + shortDescription = ""; EmploymentType: EmploymentType | null = null; location: string = ''; tags: string[] = []; diff --git a/web/src/app/Models/Dtos/Job/job.ts b/web/src/app/Models/Dtos/Job/job.ts index 2ce6b08..438a640 100644 --- a/web/src/app/Models/Dtos/Job/job.ts +++ b/web/src/app/Models/Dtos/Job/job.ts @@ -5,6 +5,7 @@ export class Job{ companyId: string = "" title: string = "" description: string = "" + shortDescription: string = "" EmploymentType: EmploymentType | null = null location: string = "" tags: string = "" diff --git a/web/src/app/Models/Dtos/User/userTests-dto.ts b/web/src/app/Models/Dtos/User/userTests-dto.ts new file mode 100644 index 0000000..8d5eb5f --- /dev/null +++ b/web/src/app/Models/Dtos/User/userTests-dto.ts @@ -0,0 +1,6 @@ +import { DifficultyLevel } from "../../Enums/DifficultyLevel" + +export interface UserTestsDto { + difficultyLevel: DifficultyLevel; + passed: boolean; +} \ No newline at end of file diff --git a/web/src/app/app-module.ts b/web/src/app/app-module.ts index a8e564e..7978a28 100644 --- a/web/src/app/app-module.ts +++ b/web/src/app/app-module.ts @@ -18,12 +18,16 @@ import { ProfileView } from './components/profile-view/profile-view'; import { Jobupload } from './components/jobupload/jobupload'; import { JobEdit } from './components/job-edit/job-edit'; import { AssessmentCreate } from './components/assessment-create/assessment-create'; +<<<<<<< Updated upstream import { JobDetail } from './components/job-detail/job-detail'; import { TestTake } from './components/test-take/test-take'; import { QuestionTrueFalse } from './components/question-true-false/question-true-false'; import { QuestionMultipleChoice } from './components/question-multiple-choice/question-multiple-choice'; import { QuestionCodeCompletion } from './components/question-code-completion/question-code-completion'; import { QuestionFillInTheBlank } from './components/question-fill-in-the-blank/question-fill-in-the-blank'; +======= +import { CompanyHome } from './components/company-home/company-home'; +>>>>>>> Stashed changes @NgModule({ declarations: [ @@ -41,12 +45,16 @@ import { QuestionFillInTheBlank } from './components/question-fill-in-the-blank/ Jobupload, JobEdit, AssessmentCreate, +<<<<<<< Updated upstream JobDetail, TestTake, QuestionTrueFalse, QuestionMultipleChoice, QuestionCodeCompletion, QuestionFillInTheBlank, +======= + CompanyHome, +>>>>>>> Stashed changes ], imports: [BrowserModule, AppRoutingModule, ReactiveFormsModule, FormsModule], providers: [ diff --git a/web/src/app/app-routing-module.ts b/web/src/app/app-routing-module.ts index c85e21d..4f367a9 100644 --- a/web/src/app/app-routing-module.ts +++ b/web/src/app/app-routing-module.ts @@ -11,8 +11,12 @@ import { ProfileView } from './components/profile-view/profile-view'; import { Jobupload } from './components/jobupload/jobupload'; import { JobEdit } from './components/job-edit/job-edit'; import { AssessmentCreate } from './components/assessment-create/assessment-create'; +<<<<<<< Updated upstream import { JobDetail } from './components/job-detail/job-detail'; import { TestTake } from './components/test-take/test-take'; +======= +import { CompanyHome } from './components/company-home/company-home'; +>>>>>>> Stashed changes const routes: Routes = [ { path: '', redirectTo: 'home', pathMatch: 'full' }, @@ -30,6 +34,7 @@ const routes: Routes = [ { path: 'job/:id', component: JobDetail }, { path: 'job/:id/test', component: TestTake }, { path: 'assessments/create', component: AssessmentCreate}, + { path: 'company', component: CompanyHome}, { path: '**', redirectTo: 'home', pathMatch: 'full' }, ]; diff --git a/web/src/app/components/company-home/company-home.html b/web/src/app/components/company-home/company-home.html new file mode 100644 index 0000000..42835c4 --- /dev/null +++ b/web/src/app/components/company-home/company-home.html @@ -0,0 +1,74 @@ +
+
+ + +
+
+ @if (companyJobs$ | async; as jobs) { + @if (jobs.length > 0) { + @for (job of jobs; track job.id) { +
+
+ +

{{job.title}}

+

{{job.shortDescription}}

+ +
+ {{job.location}} + {{ getTimeAgo(job.createdAt) }} +
+
+
+ } + } + } + +
+ +
+ @if (selectedJob) { +
+
+ +
+

{{selectedJob.title}}

+ + + +
+ +
+

{{selectedJob.shortDescription}}

+
+ +
+
Job characteristics
+
+ + Full-time + {{selectedJob.EmploymentType}} +
+ +
+
Office location
+

{{selectedJob.location}}

+ +
+
+ } @else { +
+
+
+
+ } +
+ +
+ + +
+
\ No newline at end of file diff --git a/web/src/app/components/company-home/company-home.scss b/web/src/app/components/company-home/company-home.scss new file mode 100644 index 0000000..4b504c3 --- /dev/null +++ b/web/src/app/components/company-home/company-home.scss @@ -0,0 +1,30 @@ +.button-medium { + width: 50%; + justify-content: center; + border: 0px; +} + +.bi { + -webkit-text-stroke: 0.5px; +} + +.color { + color: #003049; +} + +.radius { + border-radius: 18px; + cursor: pointer; +} + +.border-highlight { + border: 1px solid #003049 !important; +} + +.weight { + font-weight: 500 !important; +} + +.sample { + height: 150px; +} \ No newline at end of file diff --git a/web/src/app/components/company-home/company-home.spec.ts b/web/src/app/components/company-home/company-home.spec.ts new file mode 100644 index 0000000..62dc309 --- /dev/null +++ b/web/src/app/components/company-home/company-home.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CompanyHome } from './company-home'; + +describe('CompanyHome', () => { + let component: CompanyHome; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CompanyHome], + }).compileComponents(); + + fixture = TestBed.createComponent(CompanyHome); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/components/company-home/company-home.ts b/web/src/app/components/company-home/company-home.ts new file mode 100644 index 0000000..4528c73 --- /dev/null +++ b/web/src/app/components/company-home/company-home.ts @@ -0,0 +1,77 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable, filter, switchMap, of } from 'rxjs'; +import { JobViewDto } from '../../Models/Dtos/Job/JobView-dto'; +import { JobService } from '../../services/job-service'; +import { ProfileViewDto } from '../../Models/User/profile-view-dto'; +import { ProfileService } from '../../services/profile-service'; + +@Component({ + selector: 'app-company-home', + standalone: false, + templateUrl: './company-home.html', + styleUrl: './company-home.scss', +}) +export class CompanyHome implements OnInit { + profile$!: Observable; + companyJobs$!: Observable; + selectedJob: JobViewDto | null = null; + + constructor( + private jobService: JobService, + private profileService: ProfileService + ) { } + + ngOnInit(): void { + this.profile$ = this.profileService.currentProfile$; + + this.companyJobs$ = this.profile$.pipe( + filter((profile): profile is ProfileViewDto => profile !== null), + switchMap((profile) => { + if (!profile.companyId) { + return of([]); + } + return this.jobService.getJobsByCompanyId(profile.companyId); + }), + ); + } + + getTimeAgo(dateInput: string | Date): string { + if (!dateInput) return ''; + + const pastDate = new Date(dateInput); + const now = new Date(); + const seconds = Math.floor((now.getTime() - pastDate.getTime()) / 1000); + + if (seconds < 60) { + return 'just now'; + } + + const minutes = Math.floor(seconds / 60); + if (minutes < 60) { + return ` ${minutes} minute${minutes > 1 ? 's' : ''} ago`; + } + + const hours = Math.floor(minutes / 60); + if (hours < 24) { + return ` ${hours} hour${hours > 1 ? 's' : ''} ago`; + } + + const days = Math.floor(hours / 24); + if (days < 30) { + return ` ${days} day${days > 1 ? 's' : ''} ago`; + } + + const months = Math.floor(days / 30); + if (months < 12) { + return ` ${months} month${months > 1 ? 's' : ''} ago`; + } + + const years = Math.floor(days / 365); + return ` ${years} year${years > 1 ? 's' : ''} ago`; + } + + selectJob(job: JobViewDto): void { + this.selectedJob = job; + } + +} diff --git a/web/src/app/components/job-edit/job-edit.html b/web/src/app/components/job-edit/job-edit.html index ce47744..22e801f 100644 --- a/web/src/app/components/job-edit/job-edit.html +++ b/web/src/app/components/job-edit/job-edit.html @@ -1,6 +1,6 @@
@@ -41,6 +41,12 @@

Update job advertisement

+
+ + +
+ @if(isPreView$ | async){
diff --git a/web/src/app/components/job-edit/job-edit.scss b/web/src/app/components/job-edit/job-edit.scss index 652ea31..58334b2 100644 --- a/web/src/app/components/job-edit/job-edit.scss +++ b/web/src/app/components/job-edit/job-edit.scss @@ -213,3 +213,7 @@ $red-accent: #b91c22; .preview-content :first-child { margin-top: 0; } + +.click { + cursor: pointer; +} diff --git a/web/src/app/components/job-edit/job-edit.ts b/web/src/app/components/job-edit/job-edit.ts index d90099a..0a54e95 100644 --- a/web/src/app/components/job-edit/job-edit.ts +++ b/web/src/app/components/job-edit/job-edit.ts @@ -3,6 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; import { JobService } from '../../services/job-service'; import { AssessmentService } from '../../services/assesmentservice'; +import { DashboardRoutingService } from '../../services/dashboardRouting'; @Component({ @@ -20,6 +21,7 @@ export class JobEdit implements OnInit { salary = ''; tags = ''; description = ''; + shortDescription =''; error = ''; loading = false; @@ -39,6 +41,7 @@ export class JobEdit implements OnInit { private jobService: JobService, private assessmentService: AssessmentService, private cdr: ChangeDetectorRef, + private dashRoute: DashboardRoutingService ) {} ngOnInit(): void { @@ -53,6 +56,7 @@ export class JobEdit implements OnInit { this.employmentType = job.EmploymentType ?? 0; this.tags = job.tags ? job.tags.join(', ') : ''; this.description = job.description; + this.shortDescription = job.shortDescription; this.selectedAssessments = job.assessments || []; this.cdr.detectChanges(); }, @@ -110,6 +114,7 @@ export class JobEdit implements OnInit { location: this.location, employmentType: Number(this.employmentType), description: this.description, + shortDescription: this.shortDescription, tags: tagsArray, assessmentIds: this.selectedAssessments.map((a) => a.id), }; @@ -124,4 +129,8 @@ export class JobEdit implements OnInit { this.loading = false; } } + + goHome() { + this.dashRoute.routeToDashboard(); + } } diff --git a/web/src/app/components/jobupload/jobupload.html b/web/src/app/components/jobupload/jobupload.html index 8be9540..e461b08 100644 --- a/web/src/app/components/jobupload/jobupload.html +++ b/web/src/app/components/jobupload/jobupload.html @@ -1,6 +1,6 @@
@@ -17,19 +17,20 @@

Post a new job advertisement

- +
- -
+
+
@@ -41,33 +42,42 @@

Post a new job advertisement

+
+ + +
+ @if(isPreView$ | async){ -
- - -
+
+ + +
-
- -
+
+
+
}@else{ -
- - -
- -
+
+ + +
+
+
} + @if (error) { -
-

{{ error }}

-
+
+

{{ error }}

+
}
-
+
\ No newline at end of file diff --git a/web/src/app/components/jobupload/jobupload.scss b/web/src/app/components/jobupload/jobupload.scss index 33328cc..68c4bbf 100644 --- a/web/src/app/components/jobupload/jobupload.scss +++ b/web/src/app/components/jobupload/jobupload.scss @@ -1,7 +1,7 @@ /* Változók - a Merit arculati színei */ -$bg-color: #f7eedf; +$bg-color: #FDF0D5; $card-bg: #ffffff; -$text-main: #1a2b3c; +$text-main: #003049; $text-muted: #5e6c79; $btn-blue: #002b49; $btn-blue-hover: #001f36; @@ -212,4 +212,8 @@ $red-accent: #b91c22; /* Ha azt akarod, hogy a p, h1, ul elemek szépen nézzenek ki benne */ .preview-content :first-child { margin-top: 0; +} + +.click { + cursor: pointer; } \ No newline at end of file diff --git a/web/src/app/components/jobupload/jobupload.ts b/web/src/app/components/jobupload/jobupload.ts index d22b532..62c7207 100644 --- a/web/src/app/components/jobupload/jobupload.ts +++ b/web/src/app/components/jobupload/jobupload.ts @@ -3,6 +3,7 @@ import { Router } from '@angular/router'; import { JobService } from '../../services/job-service'; import { MarkdownService } from '../../services/markdown-service'; import { BehaviorSubject, Observable } from 'rxjs'; +import { DashboardRoutingService } from '../../services/dashboardRouting'; @Component({ selector: 'app-jobupload', @@ -17,6 +18,7 @@ export class Jobupload { salary: string = ''; tags: string = ''; description: string = ''; + shortDescription: string = ""; preViewmd: BehaviorSubject = new BehaviorSubject('') isPreView: BehaviorSubject = new BehaviorSubject(false) @@ -29,7 +31,8 @@ export class Jobupload { constructor( private jobService: JobService, private router: Router, - private markdownService: MarkdownService + private markdownService: MarkdownService, + private dashboardRoutingService: DashboardRoutingService ) {} onSubmit() { @@ -53,6 +56,7 @@ export class Jobupload { employmentType: Number(this.employmentType), salary: this.salary, description: this.description, + shortDescription: this.shortDescription, tags: tagsArray, }; @@ -79,4 +83,8 @@ export class Jobupload { } ) } + + goHome(){ + this.dashboardRoutingService.routeToDashboard(); + } } diff --git a/web/src/app/components/login/login.ts b/web/src/app/components/login/login.ts index 1637480..d489dd3 100644 --- a/web/src/app/components/login/login.ts +++ b/web/src/app/components/login/login.ts @@ -4,6 +4,7 @@ import { HttpClient } from '@angular/common/http'; import { LoginDto } from '../../Models/Dtos/User/login-dto'; import { AuthService } from '../../services/auth-service'; import { Router } from '@angular/router'; +import { DashboardRoutingService } from '../../services/dashboardRouting'; @Component({ selector: 'app-login', @@ -19,7 +20,8 @@ export class Login { error = ''; loading = false; - constructor(private fb: FormBuilder, private http: HttpClient, private authService:AuthService, private router:Router){ + constructor(private fb: FormBuilder, private http: HttpClient, + private dashRoute:DashboardRoutingService, private authService:AuthService, private router:Router){ this.loginForm = this.fb.group({ email: [''], password: [''] @@ -45,7 +47,7 @@ export class Login { return; } this.authService.saveToken(token); - this.router.navigate(['/homePage']); + this.dashRoute.routeToDashboard(); }, error: (err) => { this.loading = false; diff --git a/web/src/app/components/profile-view/profile-view.ts b/web/src/app/components/profile-view/profile-view.ts index 6d179ad..bc74031 100644 --- a/web/src/app/components/profile-view/profile-view.ts +++ b/web/src/app/components/profile-view/profile-view.ts @@ -4,6 +4,7 @@ import { JobService } from '../../services/job-service'; import { Observable, filter, switchMap, of } from 'rxjs'; import { ProfileViewDto } from '../../Models/User/profile-view-dto'; import { JobViewDto } from '../../Models/Dtos/Job/JobView-dto'; +import { UserTestsDto } from '../../Models/Dtos/User/userTests-dto'; @Component({ selector: 'app-profile-view', @@ -14,6 +15,7 @@ import { JobViewDto } from '../../Models/Dtos/Job/JobView-dto'; export class ProfileView implements OnInit { profile$!: Observable; companyJobs$!: Observable; + profileTests$!: Observable; constructor( private profileService: ProfileService, @@ -22,6 +24,7 @@ export class ProfileView implements OnInit { ngOnInit(): void { this.profile$ = this.profileService.currentProfile$; + this.profileTests$ = this.profileService.currentProfileTests$; this.companyJobs$ = this.profile$.pipe( filter((profile): profile is ProfileViewDto => profile !== null), @@ -41,4 +44,5 @@ export class ProfileView implements OnInit { console.error('Failed to delete job.', error); } } + } diff --git a/web/src/app/header/header.html b/web/src/app/header/header.html index 99cfd7e..737bab2 100644 --- a/web/src/app/header/header.html +++ b/web/src/app/header/header.html @@ -1,13 +1,27 @@
- - + +
-
-
-

Overview

-
- -
-
- Difficulty - {{ getDifficultyLabel(question.difficulty) }} -
-
- Language - {{ question.language || '-' }} -
-
- Created by - {{ question.createdBy || '-' }} -
-
- Created at - {{ question.createdAt | date: 'medium' }} +
+
+
+

Overview

-
- Updated at - {{ question.updatedAt | date: 'medium' }} -
-
- Question ID - {{ question.id }} + +
+
+ Difficulty + {{ getDifficultyLabel(question.difficulty) }} +
+
+ Created by + {{ createdByDisplay }} +
+
+ Created at + {{ question.createdAt | date: 'medium' }} +
+
+ Updated at + {{ question.updatedAt | date: 'medium' }} +
-
-
+
-
-
-

Tags

- {{ question.tags.length }} tag{{ question.tags.length === 1 ? '' : 's' }} -
+
+
+

Tags

+ {{ question.tags.length }} tag{{ question.tags.length === 1 ? '' : 's' }} +
-
- {{ tag }} -
-
+
+ {{ tag }} +
+
+
@@ -101,10 +95,6 @@

Answer data

Answer {{ getOpenEndedAnswer() }}
-
- Manual feedback - {{ getOpenEndedManualFeedback() }} -
diff --git a/web/src/app/components/question-bank-details/question-bank-details.scss b/web/src/app/components/question-bank-details/question-bank-details.scss index eb39d77..c3650ae 100644 --- a/web/src/app/components/question-bank-details/question-bank-details.scss +++ b/web/src/app/components/question-bank-details/question-bank-details.scss @@ -21,7 +21,6 @@ .qbd-header { display: flex; justify-content: space-between; - gap: 1rem; padding: 1.25rem 1.45rem; border-bottom: 1px solid rgba(0, 48, 73, 0.18); background: #ffffff; @@ -39,14 +38,14 @@ .qbd-title-block h2 { margin: 0.55rem 0 0; - font-size: clamp(1.45rem, 1.12rem + 1.15vw, 2.25rem); + font-size: 1.05rem; font-weight: 800; } -.qbd-title-block p { +.qbd-question-text { margin: 0.45rem 0 0; - color: rgba(0, 48, 73, 0.82); - font-size: 1rem; + font-size: clamp(1.35rem, 1.12rem + 0.9vw, 2rem); + font-weight: 800; line-height: 1.5; white-space: pre-wrap; } @@ -63,7 +62,6 @@ background: transparent; color: #003049; font-size: 2rem; - line-height: 1; } .qbd-body { @@ -76,10 +74,10 @@ margin-top: 1rem; } -.qbd-section:first-child { - border-top: 0; - padding-top: 0; - margin-top: 0; +.qbd-info-grid { + display: grid; + grid-template-columns: minmax(0, 1.45fr) minmax(260px, 0.75fr); + gap: 0.85rem; } .qbd-section-heading { @@ -98,17 +96,9 @@ .qbd-section-heading span { color: rgba(0, 48, 73, 0.68); - font-size: 0.88rem; font-weight: 700; } -.qbd-meta-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 0.7rem; -} - -.qbd-meta-item, .qbd-answer-box, .qbd-answer-summary, .qbd-muted-box { @@ -118,6 +108,13 @@ padding: 0.72rem 0.85rem; } +.qbd-meta-item { + display: grid; + grid-template-columns: 126px minmax(0, 1fr); + gap: 0.7rem; + padding: 0.62rem 0; +} + .qbd-meta-item span, .qbd-answer-box span, .qbd-answer-summary span, @@ -134,7 +131,6 @@ .qbd-answer-summary strong { display: block; margin-top: 0.22rem; - font-size: 0.98rem; font-weight: 800; overflow-wrap: anywhere; } @@ -152,7 +148,6 @@ border-radius: 999px; background: rgba(102, 155, 188, 0.2); color: #003049; - font-size: 0.86rem; font-weight: 800; padding: 0.26rem 0.62rem; } @@ -165,6 +160,8 @@ margin-bottom: 0.75rem; } +.qbd-answer-box+.qbd-answer-box{margin-top:.75rem} + .qbd-option-list { display: flex; flex-direction: column; @@ -216,7 +213,6 @@ border-radius: 14px; background: #f9f9f9; padding: 0.85rem; - max-height: 280px; overflow: auto; white-space: pre-wrap; } @@ -276,7 +272,11 @@ align-items: flex-start; } - .qbd-meta-grid { + .qbd-info-grid { + grid-template-columns: 1fr; + } + + .qbd-meta-item { grid-template-columns: 1fr; } diff --git a/web/src/app/components/question-bank-details/question-bank-details.ts b/web/src/app/components/question-bank-details/question-bank-details.ts index 550b7d2..1da27f3 100644 --- a/web/src/app/components/question-bank-details/question-bank-details.ts +++ b/web/src/app/components/question-bank-details/question-bank-details.ts @@ -4,6 +4,7 @@ import { finalize } from 'rxjs'; import { QuestionResponseDto } from '../../Models/Dtos/Question/question-response-dto'; import { DifficultyLevel } from '../../Models/Enums/DifficultyLevel'; import { QuestionType } from '../../Models/Enums/QuestionType'; +import { ProfileService } from '../../services/profile-service'; import { QuestionBankService } from '../../services/question-bank-service'; @Component({ @@ -19,6 +20,7 @@ export class QuestionBankDetails implements OnChanges, OnInit { readonly QuestionType = QuestionType; question: QuestionResponseDto | null = null; + createdByDisplay = '-'; loading = false; readonly questionTypeOptions = [ @@ -37,6 +39,7 @@ export class QuestionBankDetails implements OnChanges, OnInit { constructor( private route: ActivatedRoute, private router: Router, + private profileService: ProfileService, private questionBankService: QuestionBankService, private ngZone: NgZone, private cdr: ChangeDetectorRef @@ -99,6 +102,7 @@ export class QuestionBankDetails implements OnChanges, OnInit { } this.question = null; + this.createdByDisplay = '-'; this.loading = true; this.questionBankService .getById(id) @@ -114,8 +118,10 @@ export class QuestionBankDetails implements OnChanges, OnInit { next: (question) => { this.ngZone.run(() => { this.question = question; + this.createdByDisplay = question.createdBy || '-'; this.cdr.detectChanges(); }); + this.loadCreatorName(question.createdBy); }, error: () => { this.ngZone.run(() => { @@ -125,4 +131,19 @@ export class QuestionBankDetails implements OnChanges, OnInit { }, }); } + + private loadCreatorName(userId: string): void { + if (!userId) { + return; + } + + this.profileService.getProfile(userId).subscribe({ + next: (profile) => { + this.ngZone.run(() => { + this.createdByDisplay = profile.fullName || profile.email || userId; + this.cdr.detectChanges(); + }); + }, + }); + } } diff --git a/web/src/app/services/profile-service.ts b/web/src/app/services/profile-service.ts index 0b3af84..d9351a9 100644 --- a/web/src/app/services/profile-service.ts +++ b/web/src/app/services/profile-service.ts @@ -17,9 +17,12 @@ export class ProfileService { private http: HttpClient, ) {} + getProfile(userId: string): Observable { + return this.http.get(`${environment.apiUrls.getProfile}/${userId}`); + } + loadProfile(userId: string): void { - this.http - .get(`${environment.apiUrls.getProfile}/${userId}`) + this.getProfile(userId) .subscribe({ next: (profile) => { console.log("PROFILE LOADED:", profile); From f3ab004a3038026deb0a86259925c87dce8b620e Mon Sep 17 00:00:00 2001 From: oli-tolnai Date: Fri, 24 Apr 2026 12:56:54 +0200 Subject: [PATCH 10/58] feat(question-bank): enhance question editing functionality with modal support --- .../question-bank-details.ts | 6 +++++ .../question-bank-form/question-bank-form.ts | 4 ++-- .../question-bank-list.html | 12 ++++++++++ .../question-bank-list/question-bank-list.ts | 23 ++++++++++++++++--- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/web/src/app/components/question-bank-details/question-bank-details.ts b/web/src/app/components/question-bank-details/question-bank-details.ts index 1da27f3..74e6c8a 100644 --- a/web/src/app/components/question-bank-details/question-bank-details.ts +++ b/web/src/app/components/question-bank-details/question-bank-details.ts @@ -17,6 +17,7 @@ export class QuestionBankDetails implements OnChanges, OnInit { @Input() isModalMode = false; @Input() questionId: string | null = null; @Output() closed = new EventEmitter(); + @Output() editRequested = new EventEmitter(); readonly QuestionType = QuestionType; question: QuestionResponseDto | null = null; @@ -69,6 +70,11 @@ export class QuestionBankDetails implements OnChanges, OnInit { return; } + if (this.isModalMode) { + this.editRequested.emit(this.question.id); + return; + } + this.router.navigate(['/question-bank', this.question.id, 'edit']); } diff --git a/web/src/app/components/question-bank-form/question-bank-form.ts b/web/src/app/components/question-bank-form/question-bank-form.ts index d910cec..1c80662 100644 --- a/web/src/app/components/question-bank-form/question-bank-form.ts +++ b/web/src/app/components/question-bank-form/question-bank-form.ts @@ -24,6 +24,7 @@ import { QuestionBankService } from '../../services/question-bank-service'; }) export class QuestionBankForm implements OnInit { @Input() isModalMode = false; + @Input() questionId: string | null = null; @Output() closed = new EventEmitter(); @Output() saved = new EventEmitter(); @@ -33,7 +34,6 @@ export class QuestionBankForm implements OnInit { loading = false; saving = false; isEditMode = false; - questionId: string | null = null; tagInputText = ''; tags: string[] = []; @@ -87,7 +87,7 @@ export class QuestionBankForm implements OnInit { } ngOnInit(): void { - const id = this.route.snapshot.paramMap.get('id'); + const id = this.questionId ?? this.route.snapshot.paramMap.get('id'); this.isEditMode = !!id; this.questionId = id; diff --git a/web/src/app/components/question-bank-list/question-bank-list.html b/web/src/app/components/question-bank-list/question-bank-list.html index 298646c..20e57d5 100644 --- a/web/src/app/components/question-bank-list/question-bank-list.html +++ b/web/src/app/components/question-bank-list/question-bank-list.html @@ -175,7 +175,19 @@

{{ group.tag }}

[isModalMode]="true" [questionId]="viewedQuestionId" (closed)="closeViewModal()" + (editRequested)="openEditFromView($event)" >
+ +
+
+ +
+
diff --git a/web/src/app/components/question-bank-list/question-bank-list.ts b/web/src/app/components/question-bank-list/question-bank-list.ts index e06081a..102362a 100644 --- a/web/src/app/components/question-bank-list/question-bank-list.ts +++ b/web/src/app/components/question-bank-list/question-bank-list.ts @@ -1,5 +1,4 @@ import { ChangeDetectorRef, Component, HostListener, NgZone, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; import { finalize } from 'rxjs'; import { QuestionResponseDto } from '../../Models/Dtos/Question/question-response-dto'; import { DifficultyLevel } from '../../Models/Enums/DifficultyLevel'; @@ -30,6 +29,7 @@ export class QuestionBankList implements OnInit { collapsedGroups: Record = {}; showCreateModal = false; viewedQuestionId: string | null = null; + editedQuestionId: string | null = null; readonly questionTypeOptions = [ { label: 'Multiple Choice', value: QuestionType.MultipleChoice }, @@ -47,7 +47,6 @@ export class QuestionBankList implements OnInit { constructor( private questionBankService: QuestionBankService, private modalService: ModalService, - private router: Router, private ngZone: NgZone, private cdr: ChangeDetectorRef ) {} @@ -72,6 +71,10 @@ export class QuestionBankList implements OnInit { this.closeViewModal(); } + if (this.editedQuestionId) { + this.closeEditModal(); + } + if (this.showCreateModal) { this.closeCreateModal(); } @@ -241,7 +244,21 @@ export class QuestionBankList implements OnInit { } editQuestion(id: string): void { - this.router.navigate(['/question-bank', id, 'edit']); + this.editedQuestionId = id; + } + + closeEditModal(): void { + this.editedQuestionId = null; + } + + onEditModalSaved(): void { + this.closeEditModal(); + this.loadQuestions(); + } + + openEditFromView(id: string): void { + this.closeViewModal(); + this.editQuestion(id); } deleteQuestion(id: string): void { From e9d8e95667df268e10de83c6f44b60430034c00f Mon Sep 17 00:00:00 2001 From: KelemenOzseb Date: Fri, 24 Apr 2026 17:40:39 +0200 Subject: [PATCH 11/58] add filters to search and refact --- web/package-lock.json | 53 ++++---- web/src/app/home-page/home-page.html | 40 ++++++ web/src/app/home-page/home-page.ts | 176 +++++++++++++++++++++++---- web/src/app/services/job-service.ts | 20 ++- 4 files changed, 239 insertions(+), 50 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index ad68d74..4a6222b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -468,6 +468,7 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.9.tgz", "integrity": "sha512-7spQcF3hPN/fjTx6Pwa32KRRdO0NcixnRuPV4lo50ejtXesjiLVR+fkaX38sawAyGoq89IuuYvUDrbLwCMypmQ==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -484,6 +485,7 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.9.tgz", "integrity": "sha512-clsK1EsSPtAuqlRl4CciA/gsvsW7xe0eWcvHxtrMW6DYaUJ6X4AAuDxEEJ5cf/3Mpw4s8KssjIUPPtbrUIGLSQ==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -497,6 +499,7 @@ "integrity": "sha512-hTTW/OiqTXrwTneS18CMp47OX0XSbLYl2rIomLS3nXVJniSETH6S/k+LqQtGWWgLbzsd3PzUOOckHnvzpTBTsA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "7.29.0", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -529,6 +532,7 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.9.tgz", "integrity": "sha512-uZLq2aedJ+0uEZxyf6a1Nc7y1aZ7akAW7K1Kon8JUDZOvI2IDbk0i00MzkELt8q9uSmSSqg9zNKuhjspFf0Pyw==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -573,6 +577,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.9.tgz", "integrity": "sha512-MjEtFvoFtsjsAeu2yzauqGgwwEHV4ml25c9vGFmw4OmSoNme4yp41f2DegwOkn1TTHL3OF3GE65ng2U2feJU4Q==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -697,6 +702,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1060,6 +1066,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -1108,35 +1115,11 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1144,7 +1127,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -1876,6 +1858,7 @@ "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@inquirer/checkbox": "^4.3.2", "@inquirer/confirm": "^5.1.21", @@ -4381,6 +4364,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -4532,6 +4516,7 @@ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "readdirp": "^5.0.0" }, @@ -5220,6 +5205,7 @@ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -5566,6 +5552,7 @@ "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -5907,6 +5894,7 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -6015,6 +6003,7 @@ "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", @@ -7112,6 +7101,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7425,6 +7415,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -8049,7 +8040,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tuf-js": { "version": "4.1.0", @@ -8087,6 +8079,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8172,6 +8165,7 @@ "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -8247,6 +8241,7 @@ "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.1.5", "@vitest/mocker": "4.1.5", @@ -8639,6 +8634,7 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -8657,7 +8653,8 @@ "version": "0.15.1", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==", - "license": "MIT" + "license": "MIT", + "peer": true } } } diff --git a/web/src/app/home-page/home-page.html b/web/src/app/home-page/home-page.html index 987d237..56f29a4 100644 --- a/web/src/app/home-page/home-page.html +++ b/web/src/app/home-page/home-page.html @@ -35,7 +35,46 @@

Search +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+ +
+ + +
+
@if (!isLoggedIn) {
@@ -113,6 +152,7 @@

{{ job.title }}

}
+ {{ getTimeAgo(job.createdAt) }}
-
@@ -29,7 +26,7 @@

Create Test Template

{{ profile.headline }}

-
- Email: +
+
Email:
{{ profile.email }}
+
-
- About: +
+
About:
{{ profile.bio }}
+
+ + @if(!profile.companyId) { +
+
+

Skills & Badges

+
- @if(profile.skills.length > 0) { -
- Skills & Badges: -
+ @if(profile.skills.length > 0) { +
@for (skill of profile.skills; track skill.id) { -
- -
- - {{ skill.name }} - +
+
+
{{ skill.name }}
@if (skill.assessments && skill.assessments.length > 0) { -
-
Available Assessments:
- - @for (assessment of skill.assessments; track assessment.id) { -
-
- {{ assessment.title || 'Standard Test' }} -
- Difficulty: {{ assessment.difficultyLevel }} -
- - Start - -
- } +
+ + + Take Test +
} @else { -
No assessments currently available.
+
No tests available
} -
}
+ } @else { +
+

No skills added yet.

+
+ }
+
+ +
Education:
+ @if (educations$ | async; as educationsList) { + @if (educationsList.length > 0) { +
+ @for (education of educationsList; track $index) { +
+
+

{{ education.school }}

+
{{ education.fieldOfStudy }}
+

{{ education.degree }}

+

+ {{ education.startDate | date:'yyyy-MM-dd' }} - + {{ education.endDate ? (education.endDate | date:'yyyy-MM-dd') : 'Ongoing' }} +

+
+
+ } +
+ } @else { +

No education entries yet.

+ } } - - -
-
-
- @if (profile.companyId) { -
-

- Company Dashboard -

-
- - - -
-
+ @if (profile.companyId) { +

My Posted Jobs

@if (companyJobs$ | async; as jobs) { @@ -111,7 +143,7 @@
-
diff --git a/web/src/app/components/profile-view/profile-view.scss b/web/src/app/components/profile-view/profile-view.scss index 7702112..8705d29 100644 --- a/web/src/app/components/profile-view/profile-view.scss +++ b/web/src/app/components/profile-view/profile-view.scss @@ -1,4 +1,264 @@ +$bg-color: #FDF0D5; +$card-bg: #ffffff; +$text-main: #003049; +$text-muted: #5e6c79; +$btn-blue: #003049; +$btn-blue-hover: #0a5680; +$border-color: #003049; +$input-bg: #ffffff; +$red-accent: #C1121F; + +:host { + display: flex; + flex-direction: column; + align-items: center; + min-height: 100vh; + background-color: $bg-color; + color: $text-main; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + width: 100%; + + * { + box-sizing: border-box; + } +} + +.container { + width: 100%; + max-width: 800px; + padding: 20px; + margin-top: 20px; +} + +.form-card { + background-color: $card-bg; + border-radius: 12px; + padding: 40px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); +} + +.form-title { + font-size: 28px; + color: $text-main; + margin-bottom: 8px; +} + +.form-subtitle { + color: $text-muted; + font-size: 14px; + margin-bottom: 30px; +} + +.section-card { + margin-top: 1.5rem; +} + +.section-card h3 { + margin: 0 0 0.75rem 0; + font-size: 1.15rem; + font-weight: 700; + color: $text-main; +} + +.card-list { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.75rem; + padding: 0.35rem; + border: none; + border-radius: 10px; +} + +.card-list>.education-card, +.card-list>.experience-card, +.card-list>.skill-chip { + width: 100%; + max-width: 740px; + box-sizing: border-box; + margin: 0; +} + +.skill-chip, +.education-card, +.experience-card { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem; + border-radius: 10px; + border: 2px solid $border-color; + background: #ffffff; + margin-bottom: 0.75rem; +} + +.education-card .card-left, +.experience-card .card-left { + flex: 1 1 auto; + min-width: 0; + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.education-card .card-actions, +.experience-card .card-actions { + width: 96px; + display: flex; + align-items: center; + justify-content: center; +} + +.education-card h4, +.experience-card h4, +.education-card h5, +.experience-card h5 { + margin: 0; + line-height: 1.15; +} + +.education-card h4, +.experience-card h4 { + font-size: 1rem; + color: $text-main; + font-weight: 700; +} + +.education-card h5, +.experience-card h5 { + font-size: 0.95rem; + color: #4b5963; + font-weight: 600; +} + +.skill-chip { + display: flex; + justify-content: space-between; + align-items: center; + gap: 0.5rem; +} + +.edit-link, +.click { + cursor: pointer; + text-decoration: none; + color: $text-main; + font-weight: 600; +} + +.search-btn, +.submit-btn { + background-color: $btn-blue; + color: #ffffff; + border: none; + border-radius: 8px; + padding: 14px 24px; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: background-color 0.2s; +} + +.search-btn:hover, +.submit-btn:hover { + background-color: $btn-blue-hover; +} + +.form-control, +input, +select, +textarea { + width: 100%; + padding: 12px 16px; + border: 2px solid $border-color; + border-radius: 8px; + background-color: $input-bg; + font-size: 16px; + color: $text-main; + outline: none; + transition: border-color 0.2s; + appearance: none; +} + +.form-control:focus, +input:focus, +select:focus, +textarea:focus { + border-color: $red-accent; +} + +.modal-backdrop { + background-color: rgba(0, 0, 0, 0.5); +} + +.modal-content { + border-radius: 12px; + border: 2px solid $border-color; + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.12); +} + +.modal-header, +.modal-footer { + border-color: rgba(0, 48, 73, 0.12); +} + +.modal-title { + color: $text-main; + font-weight: 700; +} + +.file-row { + width: 100%; +} + +.file-input-inline { + flex: 1 1 auto; + min-width: 0; +} + +.search-btn.btn-sm { + padding: 8px 12px; + height: 40px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.education-card, +.experience-card { + padding: 0.75rem 1rem; +} + +@media (max-width: 576px) { + + .education-card, + .experience-card { + flex-direction: column; + align-items: flex-start; + } + + .education-card .card-actions, + .experience-card .card-actions { + width: 100%; + justify-content: flex-end; + margin-top: 0.5rem; + } + + .search-btn.btn-sm { + height: 38px; + } + + +} + .size { width: 18px; height: 18px; +} + +.form-skill { + width: 150px; + height: 38px; + font-size: 15px; + padding: 6px 8px; } \ No newline at end of file diff --git a/web/src/app/components/profile-view/profile-view.ts b/web/src/app/components/profile-view/profile-view.ts index 820bbff..1e656df 100644 --- a/web/src/app/components/profile-view/profile-view.ts +++ b/web/src/app/components/profile-view/profile-view.ts @@ -6,6 +6,11 @@ import { JobViewDto } from '../../Models/Dtos/Job/JobView-dto'; import { UserTestsDto } from '../../Models/Dtos/User/userTests-dto'; import { ProfileViewDto } from '../../Models/Dtos/User/profile-view-dto'; import { BadgeService } from '../../services/badgeservice'; +import { EducationService } from '../../services/education-service'; +import { ExperienceService } from '../../services/experience-service'; +import { EducationViewDto } from '../../Models/Dtos/Education/EducationViewDto'; +import { ExperienceViewDto } from '../../Models/Dtos/Experience/ExperienceViewDto'; +import { AuthService } from '../../services/auth-service'; @Component({ selector: 'app-profile-view', @@ -17,27 +22,47 @@ export class ProfileView implements OnInit { profile$!: Observable; companyJobs$!: Observable; profileTests$!: Observable; - savedJobs$!: Observable; + educations$!: Observable; + experiences$!: Observable; + constructor( private profileService: ProfileService, private jobService: JobService, - public badgeService: BadgeService + public badgeService: BadgeService, + private educationService: EducationService, + private experienceService: ExperienceService, + private authService: AuthService ) { } ngOnInit(): void { this.profile$ = this.profileService.currentProfile$; this.profileTests$ = this.profileService.currentProfileTests$; - this.companyJobs$ = this.profile$.pipe( + this.educations$ = this.profile$.pipe( filter((profile): profile is ProfileViewDto => profile !== null), - switchMap((profile) => { - if (!profile.companyId) { - return of([]); - } - return this.jobService.getJobsByCompanyId(profile.companyId); - }), + switchMap(() => { + const userId = this.authService.getUserId(); + if (!userId) return of([]); + return this.educationService.getEducationsByUserId(userId); + }) + ); + + this.experiences$ = this.profile$.pipe( + filter((profile): profile is ProfileViewDto => profile !== null), + switchMap(() => { + const userId = this.authService.getUserId(); + if (!userId) return of([]); + return this.experienceService.getExperiencesByUserId(userId); + }) + ); + + this.companyJobs$ = combineLatest([this.profile$, this.jobService.jobs$]).pipe( + map(([profile, allJobs]) => { + if (!profile?.companyId) return []; + return allJobs.filter((job) => job.companyId === profile.companyId); + }) ); this.savedJobs$ = combineLatest([this.jobService.jobs$, this.profile$]).pipe( @@ -51,11 +76,7 @@ export class ProfileView implements OnInit { } deleteJob(jobId: string, companyId: string): void { - try { - this.jobService.deleteJob(jobId, companyId); - } catch (error) { - console.error('Failed to delete job.', error); - } + this.jobService.deleteJob(jobId, companyId); } removeSavedJob(jobId: string, event: MouseEvent): void { diff --git a/web/src/app/components/review-user/review-user.html b/web/src/app/components/review-user/review-user.html index 93ed4a3..e9c3ea2 100644 --- a/web/src/app/components/review-user/review-user.html +++ b/web/src/app/components/review-user/review-user.html @@ -69,7 +69,7 @@
Skills
@for (skill of profileData?.skills; track skill.id) {
- + {{ skill.name }} diff --git a/web/src/app/components/review-user/review-user.scss b/web/src/app/components/review-user/review-user.scss index ffe2593..1bd86e8 100644 --- a/web/src/app/components/review-user/review-user.scss +++ b/web/src/app/components/review-user/review-user.scss @@ -30,21 +30,7 @@ background-color: rgba(#beadad, 0.4); } -.review { - width: auto; - height: auto; - background-color: #003049; - border-radius: 10px; - border: 1px solid #003049; - color: #FFFF; - padding-left: 6px; - padding-right: 6px; - font-size: 15px; - &:hover { - background-color: #0a5680; - } -} .btn-color-red { background-color: #C1121F; @@ -67,3 +53,8 @@ border-color: #072636; } } + +.size { + width: 18px; + height: 18px; +} diff --git a/web/src/app/components/review-user/review-user.ts b/web/src/app/components/review-user/review-user.ts index d504f0f..251d791 100644 --- a/web/src/app/components/review-user/review-user.ts +++ b/web/src/app/components/review-user/review-user.ts @@ -36,7 +36,7 @@ export class ReviewUser implements OnInit { private questionBankService: QuestionBankService, private feedbackSharedService: FeedbackShared, private router: Router, - public badgeService: BadgeService + public badgeService: BadgeService, private jobService: JobService ) { } diff --git a/web/src/app/header/header.html b/web/src/app/header/header.html index e3b2631..3cb91d4 100644 --- a/web/src/app/header/header.html +++ b/web/src/app/header/header.html @@ -20,9 +20,6 @@ Skills } - - } - - } @else { + }@else { For employers - Login } + + +
\ No newline at end of file diff --git a/web/src/app/home-page/home-page.html b/web/src/app/home-page/home-page.html index 56f29a4..b470dad 100644 --- a/web/src/app/home-page/home-page.html +++ b/web/src/app/home-page/home-page.html @@ -10,71 +10,25 @@

} -
-
- - -
- -
- - -
- - -
-
-
-
- - -
- -
- - -
- -
- - -
+
+
+ + +
-
- - -
-
+
+ + +
-
- - +
-
@if (!isLoggedIn) {
@@ -130,43 +84,4 @@

Quick submission to job advertisements

-
-
-

Available Positions

- @if (filteredJobs$ | async; as jobs) { - @if (jobs.length > 0) { -
- @for (job of jobs; track job.id) { -
-
-
-

{{ job.title }}

-

- {{ job.location }} -

-
- @for (tag of job.tags; track tag) { - - {{ tag }} - - } -
- {{ getTimeAgo(job.createdAt) }} -
- -
-
- } -
- } @else { -
- -

No jobs found matching your criteria

-
- } - } -
-
diff --git a/web/src/app/home-page/home-page.ts b/web/src/app/home-page/home-page.ts index 06819a6..7329045 100644 --- a/web/src/app/home-page/home-page.ts +++ b/web/src/app/home-page/home-page.ts @@ -1,11 +1,6 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { JobService } from '../services/job-service'; -import { JobViewDto } from '../Models/Dtos/Job/JobView-dto'; -import levenshtein from 'fast-levenshtein'; -import { BehaviorSubject, debounceTime, Observable, Subject, take, takeUntil } from 'rxjs'; +import { Component, OnInit } from '@angular/core'; import { FormControl } from '@angular/forms'; -import { EmploymentType } from '../Models/Enums/EmploymentType'; -import { DifficultyLevel } from '../Models/Enums/DifficultyLevel'; +import { Router } from '@angular/router'; import { AuthService } from '../services/auth-service'; @Component({ @@ -14,281 +9,23 @@ import { AuthService } from '../services/auth-service'; templateUrl: './home-page.html', styleUrl: './home-page.scss', }) -export class HomePage implements OnInit, OnDestroy { +export class HomePage implements OnInit { freeTextControl = new FormControl(''); locationControl = new FormControl(''); - isLoggedIn = false; - filteredJobs = new BehaviorSubject([]); - filteredJobs$: Observable = this.filteredJobs.asObservable(); - private destroy$ = new Subject(); - filterType = ''; - filterExperience = ''; - filterTags = ''; - sort = ''; - - readonly jobTypeOptions = [ - { label: 'FullTime', value: 0 }, - { label: 'Internship', value: 1 }, - { label: 'PartTime', value: 2 }, - { label: 'Temporary', value: 3 }, - ]; - - readonly jobExperience = [ - { label: 'Senior', value: DifficultyLevel.Senior }, - { label: 'Medior', value: DifficultyLevel.Medior }, - { label: 'Junior', value: DifficultyLevel.Junior }, - ]; - - constructor(public jobService: JobService, private authService: AuthService) {} + constructor(private authService: AuthService, private router: Router) {} ngOnInit() { - this.isLoggedIn = this.authService.isLoggedIn() - - this.jobService.jobs$.subscribe((allJobs) => { - const parsedJobs = this.parseJobsTags(allJobs); - - if ( - !this.freeTextControl.value && - !this.locationControl.value && - !this.filterExperience && - !this.filterTags && - !this.filterType && - !this.sort - ) { - this.filteredJobs.next(parsedJobs); - } - }); - - this.freeTextControl.valueChanges - .pipe(takeUntil(this.destroy$), debounceTime(200)) - .subscribe(() => { - this.search(); - }); - - this.locationControl.valueChanges - .pipe(takeUntil(this.destroy$), debounceTime(200)) - .subscribe(() => { - this.search(); - }); - } - - private parseJobsTags(jobs: JobViewDto[]): JobViewDto[] { - return jobs.map((job) => { - const parsedJob = { ...job }; - if (typeof parsedJob.tags === 'string') { - const tagString = parsedJob.tags as unknown as string; - parsedJob.tags = tagString - .split(',') - .map((t) => t - .trim() - .replace(/[\[\]"']/g, '') - ) - .filter((t) => t !== '') - } - if (Array.isArray(parsedJob.tags)) { - parsedJob.tags = parsedJob.tags - .map((t) => - typeof t === 'string' ? - t.replace(/[\[\]"']/g, '').trim() - : t - ) - } - if (!Array.isArray(parsedJob.tags)) { - parsedJob.tags = []; - } - - return parsedJob - }) + this.isLoggedIn = this.authService.isLoggedIn(); } search() { - this.jobService.jobs$.pipe(take(1)).subscribe((allJobs) => { - const parsedJobs = this.parseJobsTags(allJobs); - - const fText = this.freeTextControl.value?.toLowerCase().trim() || ''; - const lText = this.locationControl.value?.toLowerCase().trim() || ''; - - if ( - !fText && - !lText && - !this.filterExperience && - !this.filterTags && - !this.filterType && - !this.sort - ) { - this.filteredJobs.next(parsedJobs); - return; + this.router.navigate(['/search'], { + queryParams: { + q: this.freeTextControl.value, + loc: this.locationControl.value } - - let threshold = 0; - const fLength = fText.length; - - if (fLength > 3 && fLength < 10) threshold = 1; - else if (fLength >= 10 && fLength < 16) threshold = 2; - else if (fLength >= 16) threshold = 3; - - let results = parsedJobs - .map((job) => { - let score = 0; - let tagScore = 0; - let titleScore = 0; - let employmentTypeScore = 0; - let titleMatched = false; - let locationMatched = false; - - const titles = job.title.split(' '); - - if (lText && job.location) { - const dist = levenshtein.get(lText, job.location.toLowerCase()); - if (dist <= 2) { - titleScore += 2; - locationMatched = true; - } - } - - if (fText) { - titles.forEach((title) => { - const dist = levenshtein.get(fText, title.toLowerCase()); - if (dist <= 2) { - titleScore += 3; - titleMatched = true; - } - }); - - job.tags?.forEach((tag) => { - const dist = levenshtein.get(fText, tag.toLowerCase()); - if (dist === 0) { - tagScore += 1; - } - }); - } - - if (this.filterTags.length > 0) { - job.tags?.forEach((tag) => { - this.filterTags - .trim() - .split(',') - .forEach((fTag) => { - if (tag.toLowerCase() === fTag.toLowerCase()) { - tagScore += 3; - } - }); - }); - } - - if (this.filterType !== '') { - const selectedType = Number(this.filterType); - const convertedType = EmploymentType[selectedType]; - if (job.employmentType?.toString() == convertedType) { - employmentTypeScore += 3; - } - } - score = employmentTypeScore + titleScore + tagScore; - return { - job, - score, - titleMatched, - locationMatched, - employmentTypeScore, - titleScore, - tagScore, - }; - }) - .filter((item) => { - if (!fText && !lText) { - return item.score > 0; - } - if (fText && lText) { - return item.score >= 2 && (item.titleMatched || item.locationMatched); - } - if (fText) { - return item.score >= 2 && item.titleMatched; - } - if (lText) { - return item.score >= 1 && item.locationMatched; - } - return true; - }) - .sort((a, b) => { - switch (this.sort) { - case 'title': - return b.titleScore - a.titleScore; - - case 'tags': - return b.tagScore - a.tagScore; - - case 'type': - return b.employmentTypeScore - a.employmentTypeScore; - - case 'date': - return new Date(b.job.createdAt).getTime() - new Date(a.job.createdAt).getTime(); - - default: - return b.score - a.score; - } - }) - .map((item) => item.job); - - if (results.length === 0) { - results = parsedJobs.sort( - (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), - ); - } - - this.filteredJobs.next(results); }); } - - clearFilters(): void { - this.filterType = ''; - this.filterExperience = ''; - this.filterTags = ''; - this.sort = ''; - this.freeTextControl.setValue(''); - this.locationControl.setValue(''); - this.search(); - } - - applyFilters(): void { - this.search(); - } - - getTimeAgo(dateStr: string): string { - const formattedDateStr = - dateStr.includes('Z') || dateStr.includes('+') ? dateStr : `${dateStr.replace(' ', 'T')}Z`; - const date = new Date(formattedDateStr); - const time = date.getTime(); - - if (!dateStr || Number.isNaN(time)) { - return 'Unknown'; - } - - const diffMs = Date.now() - time; - if (diffMs < 0) { - return 'Just now'; - } - - const minutes = Math.floor(diffMs / 60000); - if (minutes < 1) return 'Just now'; - if (minutes < 60) return `${minutes} minute${minutes === 1 ? '' : 's'} ago`; - - const hours = Math.floor(minutes / 60); - if (hours < 24) return `${hours} hour${hours === 1 ? '' : 's'} ago`; - - const days = Math.floor(hours / 24); - if (days < 30) return `${days} day${days === 1 ? '' : 's'} ago`; - - const months = Math.floor(days / 30); - if (months < 12) return `${months} month${months === 1 ? '' : 's'} ago`; - - const years = Math.floor(days / 365); - return `${years} year${years === 1 ? '' : 's'} ago`; - } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } -} +} \ No newline at end of file diff --git a/web/src/app/services/auth-service.ts b/web/src/app/services/auth-service.ts index ead4704..ded1243 100644 --- a/web/src/app/services/auth-service.ts +++ b/web/src/app/services/auth-service.ts @@ -46,7 +46,7 @@ export class AuthService { logout() { localStorage.removeItem(this.storageKey); this.profileService.clearProfile(); - window.location.href = '/login'; + window.location.href = '/home'; } isLoggedIn(): boolean { diff --git a/web/src/app/services/dashboardRouting.ts b/web/src/app/services/dashboardRouting.ts index dd27778..d68f0ee 100644 --- a/web/src/app/services/dashboardRouting.ts +++ b/web/src/app/services/dashboardRouting.ts @@ -14,14 +14,14 @@ export class DashboardRoutingService { private router: Router ) {} - routeToDashboard(): void { + routeToDashboard(): void { if (!this.authService.isLoggedIn()) { this.router.navigate(['/home']); return; } const userId = this.authService.getUserId(); - if (userId && !this.getValueFromBehaviorSubject()) { + if (userId) { this.profileService.loadProfile(userId); } @@ -33,12 +33,12 @@ export class DashboardRoutingService { if (profile?.companyId) { this.router.navigate(['/company']); } else { - this.router.navigate(['/home']); + this.router.navigate(['/search']); } }, error: (err) => { console.error('Failed to get profile for routing:', err); - this.router.navigate(['/home']); + this.router.navigate(['/search']); } }); } diff --git a/web/src/app/services/education-service.spec.ts b/web/src/app/services/education-service.spec.ts new file mode 100644 index 0000000..f0104f0 --- /dev/null +++ b/web/src/app/services/education-service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { EducationService } from './education-service'; + +describe('EducationService', () => { + let service: EducationService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(EducationService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/web/src/app/services/education-service.ts b/web/src/app/services/education-service.ts new file mode 100644 index 0000000..c7eab98 --- /dev/null +++ b/web/src/app/services/education-service.ts @@ -0,0 +1,27 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { environment } from '../../environments/environment.development'; +import { EducationViewDto } from '../Models/Dtos/Education/EducationViewDto'; +import { EducationCreateDto } from '../Models/Dtos/Education/EducationCreateDto'; + +@Injectable({ + providedIn: 'root', +}) +export class EducationService { + private readonly apiUrl = `${environment.apiUrl}/Education`; + + constructor(private http: HttpClient) {} + + getEducationsByUserId(userId: string): Observable { + return this.http.get(`${this.apiUrl}/${userId}`); + } + + createEducation(userId: string, entity: EducationCreateDto): Observable { + return this.http.post(`${this.apiUrl}/${userId}`, entity); + } + + deleteEducation(userId: string, id: string): Observable { + return this.http.delete(`${this.apiUrl}/${userId}/${id}`); + } +} \ No newline at end of file diff --git a/web/src/app/services/experience-service.spec.ts b/web/src/app/services/experience-service.spec.ts new file mode 100644 index 0000000..15b91f3 --- /dev/null +++ b/web/src/app/services/experience-service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ExperienceService } from './experience-service'; + +describe('ExperienceService', () => { + let service: ExperienceService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ExperienceService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/web/src/app/services/experience-service.ts b/web/src/app/services/experience-service.ts new file mode 100644 index 0000000..bcca935 --- /dev/null +++ b/web/src/app/services/experience-service.ts @@ -0,0 +1,26 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { environment } from '../../environments/environment.development'; +import { ExperienceCreateDto } from '../Models/Dtos/Experience/ExperienceCreateDto'; +import { ExperienceViewDto } from '../Models/Dtos/Experience/ExperienceViewDto'; +@Injectable({ + providedIn: 'root', +}) +export class ExperienceService { + private readonly apiUrl = `${environment.apiUrl}/Experience`; + + constructor(private http: HttpClient) {} + + getExperiencesByUserId(userId: string): Observable { + return this.http.get(`${this.apiUrl}/${userId}`); + } + + createExperience(userId: string, entity: ExperienceCreateDto): Observable { + return this.http.post(`${this.apiUrl}/${userId}`, entity); + } + + deleteExperience(userId: string, id: string): Observable { + return this.http.delete(`${this.apiUrl}/${userId}/${id}`); + } +} \ No newline at end of file diff --git a/web/src/styles.scss b/web/src/styles.scss index bf9790c..3c16d3d 100644 --- a/web/src/styles.scss +++ b/web/src/styles.scss @@ -21,7 +21,11 @@ main { } -h1 { +h1, +h2, +h3, +h4, +h5 { font-weight: $font-weight-semibold; font-family: $primary; color: $primary-color !important; @@ -29,6 +33,7 @@ h1 { p { color: $primary-color; + font-family: $primary; } .background { @@ -143,6 +148,19 @@ header { color: #fff; } +.search-btn-secondary { + background: #fff; + border: 2px solid #003049; + color: #003049; + font-weight: 700; + min-width: 140px; +} + +.search-btn-secondary:hover { + background: #003049; + color: #fff; +} + .promo-card { background: #f1dfba; border-radius: 12px; @@ -187,6 +205,21 @@ header { color: #fff; } +.apply-btn { + background: #c1121f; + color: #fff; + border: 0; + font-weight: 500; + border-radius: 7px; + font-family: $primary; + padding: 10px; +} + +.apply-btn:hover { + background: #a00f1a; + color: #fff; +} + .phone-preview { max-height: 450px; object-fit: contain; @@ -307,4 +340,44 @@ button { background: #fdf0d5; min-height: calc(100vh - 92px); padding-bottom: 25px; +} + +.review { + width: auto; + height: auto; + background-color: #003049; + border-radius: 10px; + border: 1px solid #003049; + color: #FFFF; + padding-left: 6px; + padding-right: 6px; + font-size: 15px; + + &:hover { + background-color: #0a5680; + } +} + +.badge-primary { + border-radius: 17px; + background-color: #EDEDED ; + padding: 8px; + padding-left: 12px; + padding-right: 12px; +} + +.button-design { + border: 2px solid #003049; + background-color: #fff; + color: #003049; + font-weight: 700; + border-top-right-radius: 0px; + border-bottom-right-radius: 8px; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; + + &:hover { + background-color: #003049; + color: #fff; + } } \ No newline at end of file From 6a08c600d22662510f45c5bad6e3d556e8b3119e Mon Sep 17 00:00:00 2001 From: Adam Revesz Date: Wed, 20 May 2026 23:51:33 +0200 Subject: [PATCH 54/58] Migration problem fix --- .../20260507090237_UpdateUserConfiguration.cs | 76 -- .../20260512151547_IsReadProperty.Designer.cs | 1028 ---------------- .../20260512151547_IsReadProperty.cs | 29 - .../20260516164322_InitialCreate.Designer.cs | 1089 ----------------- .../20260516172828_SkillUpdate.Designer.cs | 1086 ---------------- .../Migrations/20260516172828_SkillUpdate.cs | 59 - .../20260519140320_SkillChanges.Designer.cs | 1086 ---------------- .../Migrations/20260519140320_SkillChanges.cs | 82 -- .../20260520161302_EducationAdded.cs | 51 - ...ner.cs => 20260520213503_Init.Designer.cs} | 4 +- ...nitialCreate.cs => 20260520213503_Init.cs} | 54 +- .../SkillProof.Data/SkillProof.Data.csproj | 4 + .../components/edit-profile/edit-profile.html | 5 + 13 files changed, 54 insertions(+), 4599 deletions(-) delete mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260507090237_UpdateUserConfiguration.cs delete mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260512151547_IsReadProperty.Designer.cs delete mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260512151547_IsReadProperty.cs delete mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260516164322_InitialCreate.Designer.cs delete mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260516172828_SkillUpdate.Designer.cs delete mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260516172828_SkillUpdate.cs delete mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260519140320_SkillChanges.Designer.cs delete mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260519140320_SkillChanges.cs delete mode 100644 backend/SkillProof/SkillProof.Data/Migrations/20260520161302_EducationAdded.cs rename backend/SkillProof/SkillProof.Data/Migrations/{20260520161302_EducationAdded.Designer.cs => 20260520213503_Init.Designer.cs} (99%) rename backend/SkillProof/SkillProof.Data/Migrations/{20260516164322_InitialCreate.cs => 20260520213503_Init.cs} (94%) diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260507090237_UpdateUserConfiguration.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260507090237_UpdateUserConfiguration.cs deleted file mode 100644 index 095999d..0000000 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260507090237_UpdateUserConfiguration.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace SkillProof.Data.Migrations -{ - /// - public partial class UpdateUserConfiguration : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_JobApplications_AspNetUsers_UserId", - table: "JobApplications"); - - migrationBuilder.AddColumn( - name: "UserId1", - table: "JobApplications", - type: "nvarchar(450)", - nullable: false, - defaultValue: ""); - - migrationBuilder.CreateIndex( - name: "IX_JobApplications_UserId1", - table: "JobApplications", - column: "UserId1"); - - migrationBuilder.Sql( - "UPDATE JobApplications SET UserId1 = UserId WHERE UserId1 = ''"); - - migrationBuilder.AddForeignKey( - name: "FK_JobApplications_AspNetUsers_UserId", - table: "JobApplications", - column: "UserId", - principalTable: "AspNetUsers", - principalColumn: "Id"); - - migrationBuilder.AddForeignKey( - name: "FK_JobApplications_AspNetUsers_UserId1", - table: "JobApplications", - column: "UserId1", - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_JobApplications_AspNetUsers_UserId", - table: "JobApplications"); - - migrationBuilder.DropForeignKey( - name: "FK_JobApplications_AspNetUsers_UserId1", - table: "JobApplications"); - - migrationBuilder.DropIndex( - name: "IX_JobApplications_UserId1", - table: "JobApplications"); - - migrationBuilder.DropColumn( - name: "UserId1", - table: "JobApplications"); - - migrationBuilder.AddForeignKey( - name: "FK_JobApplications_AspNetUsers_UserId", - table: "JobApplications", - column: "UserId", - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - } -} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260512151547_IsReadProperty.Designer.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260512151547_IsReadProperty.Designer.cs deleted file mode 100644 index 1164a0c..0000000 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260512151547_IsReadProperty.Designer.cs +++ /dev/null @@ -1,1028 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SkillProof.Data; - -#nullable disable - -namespace SkillProof.Data.Migrations -{ - [DbContext(typeof(SkillProofDbContext))] - [Migration("20260512151547_IsReadProperty")] - partial class IsReadProperty - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("AssessmentsJob", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("JobsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "JobsId"); - - b.HasIndex("JobsId"); - - b.ToTable("AssessmentsJob"); - }); - - modelBuilder.Entity("AssessmentsQuestions", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("QuestionsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "QuestionsId"); - - b.HasIndex("QuestionsId"); - - b.ToTable("AssessmentsQuestions"); - }); - - modelBuilder.Entity("AssessmentsTests", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("TestAttemptsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "TestAttemptsId"); - - b.HasIndex("TestAttemptsId"); - - b.ToTable("AssessmentsTests"); - }); - - modelBuilder.Entity("JobQuestions", b => - { - b.Property("JobsId") - .HasColumnType("nvarchar(450)"); - - b.Property("QuestionsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("JobsId", "QuestionsId"); - - b.HasIndex("QuestionsId"); - - b.ToTable("JobQuestions"); - }); - - modelBuilder.Entity("JobUsers", b => - { - b.Property("SavedJobsId") - .HasColumnType("nvarchar(450)"); - - b.Property("UsersId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("SavedJobsId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("UserSavedJobs", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Discriminator") - .IsRequired() - .HasMaxLength(13) - .HasColumnType("nvarchar(13)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers", (string)null); - - b.HasDiscriminator().HasValue("IdentityUser"); - - b.UseTphMappingStrategy(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Assessments", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("DifficultyLevel") - .HasColumnType("int"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.HasKey("Id"); - - b.ToTable("Assessments"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("AcceptedAnswers") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CodeSnippet") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("CodeCompletionQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Website") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Companies"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("Answer") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("manualFeedback") - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("FillInTheBlankQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("EmploymentType") - .HasColumnType("int"); - - b.Property("Location") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Salary") - .HasColumnType("int"); - - b.Property("ShortDescription") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Tags") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.HasKey("Id"); - - b.HasIndex("CompanyId"); - - b.ToTable("Jobs"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AppliedAt") - .HasColumnType("datetime2"); - - b.Property("IsRead") - .HasColumnType("bit"); - - b.Property("JobId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Status") - .HasColumnType("int"); - - b.Property("TestId") - .HasColumnType("nvarchar(450)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("JobId"); - - b.HasIndex("TestId") - .IsUnique() - .HasFilter("[TestId] IS NOT NULL"); - - b.HasIndex("UserId"); - - b.ToTable("JobApplications"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("AllowMultipleSelection") - .HasColumnType("bit"); - - b.Property("CorrectAnswerIds") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Options") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("MultipleChoiceQuestion"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Difficulty") - .HasColumnType("int"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("Language") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.Property("QuestionText") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.PrimitiveCollection("Tags") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.Property("Type") - .HasColumnType("int"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.ToTable("Questions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AiFeedback") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("FreeTextResponse") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Inspected") - .HasColumnType("bit"); - - b.Property("IsCorrect") - .HasColumnType("bit"); - - b.Property("ManualFeedback") - .HasColumnType("nvarchar(max)"); - - b.Property("QuestionId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Score") - .HasColumnType("float"); - - b.Property("TestId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("QuestionId"); - - b.HasIndex("TestId"); - - b.ToTable("TestAnswers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompletedAt") - .HasColumnType("datetime2"); - - b.Property("DifficultyLevel") - .HasColumnType("int"); - - b.Property("Passed") - .HasColumnType("bit"); - - b.Property("Score") - .HasColumnType("float"); - - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Tests"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("CorrectAnswer") - .HasColumnType("bit"); - - b.Property("Explanation") - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("TrueFalseQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("EndDate") - .HasColumnType("datetime2"); - - b.Property("JobTitle") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("StartDate") - .HasColumnType("datetime2"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("isVerified") - .HasColumnType("bit"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserExperiences"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.HasBaseType("Microsoft.AspNetCore.Identity.IdentityUser"); - - b.Property("Bio") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("CompanyId") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyRole") - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("FirstName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("Headline") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("LastName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ProfilePicture") - .IsRequired() - .HasColumnType("varbinary(max)"); - - b.Property("Skills") - .HasColumnType("nvarchar(max)"); - - b.HasIndex("CompanyId"); - - b.HasDiscriminator().HasValue("Users"); - }); - - modelBuilder.Entity("AssessmentsJob", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("JobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("AssessmentsQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Questions", null) - .WithMany() - .HasForeignKey("QuestionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("AssessmentsTests", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", null) - .WithMany() - .HasForeignKey("TestAttemptsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("JobQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("JobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Questions", null) - .WithMany() - .HasForeignKey("QuestionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("JobUsers", b => - { - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("SavedJobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Users", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("CodeCompletionQuestion") - .HasForeignKey("SkillProof.Entities.Models.CodeCompletionQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("FillInTheBlankQuestions") - .HasForeignKey("SkillProof.Entities.Models.FillInTheBlankQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.HasOne("SkillProof.Entities.Models.Companies", "Company") - .WithMany("Jobs") - .HasForeignKey("CompanyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Company"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => - { - b.HasOne("SkillProof.Entities.Models.Job", "Job") - .WithMany("JobApplications") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", "Test") - .WithOne("JobApplication") - .HasForeignKey("SkillProof.Entities.Models.JobApplication", "TestId") - .OnDelete(DeleteBehavior.Restrict); - - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("JobApplications") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.NoAction) - .IsRequired(); - - b.Navigation("Job"); - - b.Navigation("Test"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("MultipleChoiceQuestion") - .HasForeignKey("SkillProof.Entities.Models.MultipleChoiceQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithMany("TestAnswers") - .HasForeignKey("QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", "Test") - .WithMany("TestAnswers") - .HasForeignKey("TestId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - - b.Navigation("Test"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("Tests") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("TrueFalseQuestion") - .HasForeignKey("SkillProof.Entities.Models.TrueFalseQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => - { - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("UserExperiences") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.HasOne("SkillProof.Entities.Models.Companies", "Companies") - .WithMany("Users") - .HasForeignKey("CompanyId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Companies"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => - { - b.Navigation("Jobs"); - - b.Navigation("Users"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.Navigation("JobApplications"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => - { - b.Navigation("CodeCompletionQuestion"); - - b.Navigation("FillInTheBlankQuestions"); - - b.Navigation("MultipleChoiceQuestion"); - - b.Navigation("TestAnswers"); - - b.Navigation("TrueFalseQuestion"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.Navigation("JobApplication"); - - b.Navigation("TestAnswers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.Navigation("JobApplications"); - - b.Navigation("Tests"); - - b.Navigation("UserExperiences"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260512151547_IsReadProperty.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260512151547_IsReadProperty.cs deleted file mode 100644 index 9f6cb26..0000000 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260512151547_IsReadProperty.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace SkillProof.Data.Migrations -{ - /// - public partial class IsReadProperty : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "IsRead", - table: "JobApplications", - type: "bit", - nullable: false, - defaultValue: false); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "IsRead", - table: "JobApplications"); - } - } -} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260516164322_InitialCreate.Designer.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260516164322_InitialCreate.Designer.cs deleted file mode 100644 index d03bfaf..0000000 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260516164322_InitialCreate.Designer.cs +++ /dev/null @@ -1,1089 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SkillProof.Data; - -#nullable disable - -namespace SkillProof.Data.Migrations -{ - [DbContext(typeof(SkillProofDbContext))] - [Migration("20260516164322_InitialCreate")] - partial class InitialCreate - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("AssessmentsJob", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("JobsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "JobsId"); - - b.HasIndex("JobsId"); - - b.ToTable("AssessmentsJob"); - }); - - modelBuilder.Entity("AssessmentsQuestions", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("QuestionsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "QuestionsId"); - - b.HasIndex("QuestionsId"); - - b.ToTable("AssessmentsQuestions"); - }); - - modelBuilder.Entity("AssessmentsTests", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("TestAttemptsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "TestAttemptsId"); - - b.HasIndex("TestAttemptsId"); - - b.ToTable("AssessmentsTests"); - }); - - modelBuilder.Entity("JobQuestions", b => - { - b.Property("JobsId") - .HasColumnType("nvarchar(450)"); - - b.Property("QuestionsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("JobsId", "QuestionsId"); - - b.HasIndex("QuestionsId"); - - b.ToTable("JobQuestions"); - }); - - modelBuilder.Entity("JobUsers", b => - { - b.Property("SavedJobsId") - .HasColumnType("nvarchar(450)"); - - b.Property("UsersId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("SavedJobsId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("UserSavedJobs", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Discriminator") - .IsRequired() - .HasMaxLength(13) - .HasColumnType("nvarchar(13)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers", (string)null); - - b.HasDiscriminator().HasValue("IdentityUser"); - - b.UseTphMappingStrategy(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Assessments", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("DifficultyLevel") - .HasColumnType("int"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("SkillId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("SkillId"); - - b.ToTable("Assessments"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("AcceptedAnswers") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CodeSnippet") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("CodeCompletionQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Website") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Companies"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("Answer") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("manualFeedback") - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("FillInTheBlankQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("EmploymentType") - .HasColumnType("int"); - - b.Property("Location") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Salary") - .HasColumnType("int"); - - b.Property("ShortDescription") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Tags") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.HasKey("Id"); - - b.HasIndex("CompanyId"); - - b.ToTable("Jobs"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AppliedAt") - .HasColumnType("datetime2"); - - b.Property("JobId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Status") - .HasColumnType("int"); - - b.Property("TestId") - .HasColumnType("nvarchar(450)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("JobId"); - - b.HasIndex("TestId") - .IsUnique() - .HasFilter("[TestId] IS NOT NULL"); - - b.HasIndex("UserId"); - - b.ToTable("JobApplications"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("AllowMultipleSelection") - .HasColumnType("bit"); - - b.Property("CorrectAnswerIds") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Options") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("MultipleChoiceQuestion"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Difficulty") - .HasColumnType("int"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("Language") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.Property("QuestionText") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.PrimitiveCollection("Tags") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.Property("Type") - .HasColumnType("int"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.ToTable("Questions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Skill", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.HasKey("Id"); - - b.ToTable("Skills"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AiFeedback") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("FreeTextResponse") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Inspected") - .HasColumnType("bit"); - - b.Property("IsCorrect") - .HasColumnType("bit"); - - b.Property("ManualFeedback") - .HasColumnType("nvarchar(max)"); - - b.Property("QuestionId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Score") - .HasColumnType("float"); - - b.Property("TestId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("QuestionId"); - - b.HasIndex("TestId"); - - b.ToTable("TestAnswers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompletedAt") - .HasColumnType("datetime2"); - - b.Property("DifficultyLevel") - .HasColumnType("int"); - - b.Property("Passed") - .HasColumnType("bit"); - - b.Property("Score") - .HasColumnType("float"); - - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Tests"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("CorrectAnswer") - .HasColumnType("bit"); - - b.Property("Explanation") - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("TrueFalseQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("EndDate") - .HasColumnType("datetime2"); - - b.Property("JobTitle") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("StartDate") - .HasColumnType("datetime2"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("isVerified") - .HasColumnType("bit"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserExperiences"); - }); - - modelBuilder.Entity("SkillUsers", b => - { - b.Property("SkillsId") - .HasColumnType("nvarchar(450)"); - - b.Property("UsersId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("SkillsId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("SkillUsers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.HasBaseType("Microsoft.AspNetCore.Identity.IdentityUser"); - - b.Property("Bio") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("CompanyId") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyRole") - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("FirstName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("Headline") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("LastName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ProfilePicture") - .IsRequired() - .HasColumnType("varbinary(max)"); - - b.HasIndex("CompanyId"); - - b.HasDiscriminator().HasValue("Users"); - }); - - modelBuilder.Entity("AssessmentsJob", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("JobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("AssessmentsQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Questions", null) - .WithMany() - .HasForeignKey("QuestionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("AssessmentsTests", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", null) - .WithMany() - .HasForeignKey("TestAttemptsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("JobQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("JobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Questions", null) - .WithMany() - .HasForeignKey("QuestionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("JobUsers", b => - { - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("SavedJobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Users", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Assessments", b => - { - b.HasOne("SkillProof.Entities.Models.Skill", "Skill") - .WithMany("Assessments") - .HasForeignKey("SkillId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Skill"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("CodeCompletionQuestion") - .HasForeignKey("SkillProof.Entities.Models.CodeCompletionQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("FillInTheBlankQuestions") - .HasForeignKey("SkillProof.Entities.Models.FillInTheBlankQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.HasOne("SkillProof.Entities.Models.Companies", "Company") - .WithMany("Jobs") - .HasForeignKey("CompanyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Company"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => - { - b.HasOne("SkillProof.Entities.Models.Job", "Job") - .WithMany("JobApplications") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", "Test") - .WithOne("JobApplication") - .HasForeignKey("SkillProof.Entities.Models.JobApplication", "TestId") - .OnDelete(DeleteBehavior.Restrict); - - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("JobApplications") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.NoAction) - .IsRequired(); - - b.Navigation("Job"); - - b.Navigation("Test"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("MultipleChoiceQuestion") - .HasForeignKey("SkillProof.Entities.Models.MultipleChoiceQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithMany("TestAnswers") - .HasForeignKey("QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", "Test") - .WithMany("TestAnswers") - .HasForeignKey("TestId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - - b.Navigation("Test"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("Tests") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("TrueFalseQuestion") - .HasForeignKey("SkillProof.Entities.Models.TrueFalseQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => - { - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("UserExperiences") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillUsers", b => - { - b.HasOne("SkillProof.Entities.Models.Skill", null) - .WithMany() - .HasForeignKey("SkillsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Users", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.HasOne("SkillProof.Entities.Models.Companies", "Companies") - .WithMany("Users") - .HasForeignKey("CompanyId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Companies"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => - { - b.Navigation("Jobs"); - - b.Navigation("Users"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.Navigation("JobApplications"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => - { - b.Navigation("CodeCompletionQuestion"); - - b.Navigation("FillInTheBlankQuestions"); - - b.Navigation("MultipleChoiceQuestion"); - - b.Navigation("TestAnswers"); - - b.Navigation("TrueFalseQuestion"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Skill", b => - { - b.Navigation("Assessments"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.Navigation("JobApplication"); - - b.Navigation("TestAnswers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.Navigation("JobApplications"); - - b.Navigation("Tests"); - - b.Navigation("UserExperiences"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260516172828_SkillUpdate.Designer.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260516172828_SkillUpdate.Designer.cs deleted file mode 100644 index 06b9498..0000000 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260516172828_SkillUpdate.Designer.cs +++ /dev/null @@ -1,1086 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SkillProof.Data; - -#nullable disable - -namespace SkillProof.Data.Migrations -{ - [DbContext(typeof(SkillProofDbContext))] - [Migration("20260516172828_SkillUpdate")] - partial class SkillUpdate - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("AssessmentsJob", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("JobsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "JobsId"); - - b.HasIndex("JobsId"); - - b.ToTable("AssessmentsJob"); - }); - - modelBuilder.Entity("AssessmentsQuestions", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("QuestionsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "QuestionsId"); - - b.HasIndex("QuestionsId"); - - b.ToTable("AssessmentsQuestions"); - }); - - modelBuilder.Entity("AssessmentsTests", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("TestAttemptsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "TestAttemptsId"); - - b.HasIndex("TestAttemptsId"); - - b.ToTable("AssessmentsTests"); - }); - - modelBuilder.Entity("JobQuestions", b => - { - b.Property("JobsId") - .HasColumnType("nvarchar(450)"); - - b.Property("QuestionsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("JobsId", "QuestionsId"); - - b.HasIndex("QuestionsId"); - - b.ToTable("JobQuestions"); - }); - - modelBuilder.Entity("JobUsers", b => - { - b.Property("SavedJobsId") - .HasColumnType("nvarchar(450)"); - - b.Property("UsersId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("SavedJobsId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("UserSavedJobs", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Discriminator") - .IsRequired() - .HasMaxLength(13) - .HasColumnType("nvarchar(13)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers", (string)null); - - b.HasDiscriminator().HasValue("IdentityUser"); - - b.UseTphMappingStrategy(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Assessments", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("DifficultyLevel") - .HasColumnType("int"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("SkillId") - .HasColumnType("nvarchar(450)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("SkillId"); - - b.ToTable("Assessments"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("AcceptedAnswers") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CodeSnippet") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("CodeCompletionQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Website") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Companies"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("Answer") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("manualFeedback") - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("FillInTheBlankQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("EmploymentType") - .HasColumnType("int"); - - b.Property("Location") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Salary") - .HasColumnType("int"); - - b.Property("ShortDescription") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Tags") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.HasKey("Id"); - - b.HasIndex("CompanyId"); - - b.ToTable("Jobs"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AppliedAt") - .HasColumnType("datetime2"); - - b.Property("JobId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Status") - .HasColumnType("int"); - - b.Property("TestId") - .HasColumnType("nvarchar(450)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("JobId"); - - b.HasIndex("TestId") - .IsUnique() - .HasFilter("[TestId] IS NOT NULL"); - - b.HasIndex("UserId"); - - b.ToTable("JobApplications"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("AllowMultipleSelection") - .HasColumnType("bit"); - - b.Property("CorrectAnswerIds") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Options") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("MultipleChoiceQuestion"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Difficulty") - .HasColumnType("int"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("Language") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.Property("QuestionText") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.PrimitiveCollection("Tags") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.Property("Type") - .HasColumnType("int"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.ToTable("Questions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Skill", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.HasKey("Id"); - - b.ToTable("Skills"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AiFeedback") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("FreeTextResponse") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Inspected") - .HasColumnType("bit"); - - b.Property("IsCorrect") - .HasColumnType("bit"); - - b.Property("ManualFeedback") - .HasColumnType("nvarchar(max)"); - - b.Property("QuestionId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Score") - .HasColumnType("float"); - - b.Property("TestId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("QuestionId"); - - b.HasIndex("TestId"); - - b.ToTable("TestAnswers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompletedAt") - .HasColumnType("datetime2"); - - b.Property("DifficultyLevel") - .HasColumnType("int"); - - b.Property("Passed") - .HasColumnType("bit"); - - b.Property("Score") - .HasColumnType("float"); - - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Tests"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("CorrectAnswer") - .HasColumnType("bit"); - - b.Property("Explanation") - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("TrueFalseQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("EndDate") - .HasColumnType("datetime2"); - - b.Property("JobTitle") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("StartDate") - .HasColumnType("datetime2"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("isVerified") - .HasColumnType("bit"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserExperiences"); - }); - - modelBuilder.Entity("SkillUsers", b => - { - b.Property("SkillsId") - .HasColumnType("nvarchar(450)"); - - b.Property("UsersId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("SkillsId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("SkillUsers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.HasBaseType("Microsoft.AspNetCore.Identity.IdentityUser"); - - b.Property("Bio") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("CompanyId") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyRole") - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("FirstName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("Headline") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("LastName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ProfilePicture") - .IsRequired() - .HasColumnType("varbinary(max)"); - - b.HasIndex("CompanyId"); - - b.HasDiscriminator().HasValue("Users"); - }); - - modelBuilder.Entity("AssessmentsJob", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("JobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("AssessmentsQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Questions", null) - .WithMany() - .HasForeignKey("QuestionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("AssessmentsTests", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", null) - .WithMany() - .HasForeignKey("TestAttemptsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("JobQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("JobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Questions", null) - .WithMany() - .HasForeignKey("QuestionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("JobUsers", b => - { - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("SavedJobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Users", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Assessments", b => - { - b.HasOne("SkillProof.Entities.Models.Skill", "Skill") - .WithMany("Assessments") - .HasForeignKey("SkillId"); - - b.Navigation("Skill"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("CodeCompletionQuestion") - .HasForeignKey("SkillProof.Entities.Models.CodeCompletionQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("FillInTheBlankQuestions") - .HasForeignKey("SkillProof.Entities.Models.FillInTheBlankQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.HasOne("SkillProof.Entities.Models.Companies", "Company") - .WithMany("Jobs") - .HasForeignKey("CompanyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Company"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => - { - b.HasOne("SkillProof.Entities.Models.Job", "Job") - .WithMany("JobApplications") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", "Test") - .WithOne("JobApplication") - .HasForeignKey("SkillProof.Entities.Models.JobApplication", "TestId") - .OnDelete(DeleteBehavior.Restrict); - - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("JobApplications") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.NoAction) - .IsRequired(); - - b.Navigation("Job"); - - b.Navigation("Test"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("MultipleChoiceQuestion") - .HasForeignKey("SkillProof.Entities.Models.MultipleChoiceQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithMany("TestAnswers") - .HasForeignKey("QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", "Test") - .WithMany("TestAnswers") - .HasForeignKey("TestId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - - b.Navigation("Test"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("Tests") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("TrueFalseQuestion") - .HasForeignKey("SkillProof.Entities.Models.TrueFalseQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => - { - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("UserExperiences") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillUsers", b => - { - b.HasOne("SkillProof.Entities.Models.Skill", null) - .WithMany() - .HasForeignKey("SkillsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Users", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.HasOne("SkillProof.Entities.Models.Companies", "Companies") - .WithMany("Users") - .HasForeignKey("CompanyId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Companies"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => - { - b.Navigation("Jobs"); - - b.Navigation("Users"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.Navigation("JobApplications"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => - { - b.Navigation("CodeCompletionQuestion"); - - b.Navigation("FillInTheBlankQuestions"); - - b.Navigation("MultipleChoiceQuestion"); - - b.Navigation("TestAnswers"); - - b.Navigation("TrueFalseQuestion"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Skill", b => - { - b.Navigation("Assessments"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.Navigation("JobApplication"); - - b.Navigation("TestAnswers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.Navigation("JobApplications"); - - b.Navigation("Tests"); - - b.Navigation("UserExperiences"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260516172828_SkillUpdate.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260516172828_SkillUpdate.cs deleted file mode 100644 index 032c1ba..0000000 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260516172828_SkillUpdate.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace SkillProof.Data.Migrations -{ - /// - public partial class SkillUpdate : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Assessments_Skills_SkillId", - table: "Assessments"); - - migrationBuilder.AlterColumn( - name: "SkillId", - table: "Assessments", - type: "nvarchar(450)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(450)"); - - migrationBuilder.AddForeignKey( - name: "FK_Assessments_Skills_SkillId", - table: "Assessments", - column: "SkillId", - principalTable: "Skills", - principalColumn: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Assessments_Skills_SkillId", - table: "Assessments"); - - migrationBuilder.AlterColumn( - name: "SkillId", - table: "Assessments", - type: "nvarchar(450)", - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "nvarchar(450)", - oldNullable: true); - - migrationBuilder.AddForeignKey( - name: "FK_Assessments_Skills_SkillId", - table: "Assessments", - column: "SkillId", - principalTable: "Skills", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - } -} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260519140320_SkillChanges.Designer.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260519140320_SkillChanges.Designer.cs deleted file mode 100644 index 73dfa89..0000000 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260519140320_SkillChanges.Designer.cs +++ /dev/null @@ -1,1086 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SkillProof.Data; - -#nullable disable - -namespace SkillProof.Data.Migrations -{ - [DbContext(typeof(SkillProofDbContext))] - [Migration("20260519140320_SkillChanges")] - partial class SkillChanges - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("AssessmentsJob", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("JobsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "JobsId"); - - b.HasIndex("JobsId"); - - b.ToTable("AssessmentsJob"); - }); - - modelBuilder.Entity("AssessmentsQuestions", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("QuestionsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "QuestionsId"); - - b.HasIndex("QuestionsId"); - - b.ToTable("AssessmentsQuestions"); - }); - - modelBuilder.Entity("AssessmentsTests", b => - { - b.Property("AssessmentsId") - .HasColumnType("nvarchar(450)"); - - b.Property("TestAttemptsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("AssessmentsId", "TestAttemptsId"); - - b.HasIndex("TestAttemptsId"); - - b.ToTable("AssessmentsTests"); - }); - - modelBuilder.Entity("JobQuestions", b => - { - b.Property("JobsId") - .HasColumnType("nvarchar(450)"); - - b.Property("QuestionsId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("JobsId", "QuestionsId"); - - b.HasIndex("QuestionsId"); - - b.ToTable("JobQuestions"); - }); - - modelBuilder.Entity("JobUsers", b => - { - b.Property("SavedJobsId") - .HasColumnType("nvarchar(450)"); - - b.Property("UsersId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("SavedJobsId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("UserSavedJobs", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Discriminator") - .IsRequired() - .HasMaxLength(13) - .HasColumnType("nvarchar(13)"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.ToTable("AspNetUsers", (string)null); - - b.HasDiscriminator().HasValue("IdentityUser"); - - b.UseTphMappingStrategy(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("RoleId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("SkillModelUsers", b => - { - b.Property("SkillsId") - .HasColumnType("nvarchar(450)"); - - b.Property("UsersId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("SkillsId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("SkillModelUsers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Assessments", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("DifficultyLevel") - .HasColumnType("int"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("SkillId") - .HasColumnType("nvarchar(450)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("SkillId"); - - b.ToTable("Assessments"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("AcceptedAnswers") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CodeSnippet") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("CodeCompletionQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Website") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Companies"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("Answer") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("manualFeedback") - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("FillInTheBlankQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(1000) - .HasColumnType("nvarchar(1000)"); - - b.Property("EmploymentType") - .HasColumnType("int"); - - b.Property("Location") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Salary") - .HasColumnType("int"); - - b.Property("ShortDescription") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Tags") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.HasKey("Id"); - - b.HasIndex("CompanyId"); - - b.ToTable("Jobs"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AppliedAt") - .HasColumnType("datetime2"); - - b.Property("JobId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Status") - .HasColumnType("int"); - - b.Property("TestId") - .HasColumnType("nvarchar(450)"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("JobId"); - - b.HasIndex("TestId") - .IsUnique() - .HasFilter("[TestId] IS NOT NULL"); - - b.HasIndex("UserId"); - - b.ToTable("JobApplications"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("AllowMultipleSelection") - .HasColumnType("bit"); - - b.Property("CorrectAnswerIds") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Options") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("MultipleChoiceQuestion"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Difficulty") - .HasColumnType("int"); - - b.Property("IsActive") - .HasColumnType("bit"); - - b.Property("Language") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.Property("QuestionText") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.PrimitiveCollection("Tags") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.Property("Type") - .HasColumnType("int"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.ToTable("Questions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.SkillModel", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.HasKey("Id"); - - b.ToTable("Skills"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("AiFeedback") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("FreeTextResponse") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Inspected") - .HasColumnType("bit"); - - b.Property("IsCorrect") - .HasColumnType("bit"); - - b.Property("ManualFeedback") - .HasColumnType("nvarchar(max)"); - - b.Property("QuestionId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Score") - .HasColumnType("float"); - - b.Property("TestId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("QuestionId"); - - b.HasIndex("TestId"); - - b.ToTable("TestAnswers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompletedAt") - .HasColumnType("datetime2"); - - b.Property("DifficultyLevel") - .HasColumnType("int"); - - b.Property("Passed") - .HasColumnType("bit"); - - b.Property("Score") - .HasColumnType("float"); - - b.Property("UserId") - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("Tests"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => - { - b.Property("QuestionId") - .HasColumnType("nvarchar(450)"); - - b.Property("CorrectAnswer") - .HasColumnType("bit"); - - b.Property("Explanation") - .HasColumnType("nvarchar(max)"); - - b.HasKey("QuestionId"); - - b.ToTable("TrueFalseQuestions"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyName") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("EndDate") - .HasColumnType("datetime2"); - - b.Property("JobTitle") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("StartDate") - .HasColumnType("datetime2"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("isVerified") - .HasColumnType("bit"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserExperiences"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.HasBaseType("Microsoft.AspNetCore.Identity.IdentityUser"); - - b.Property("Bio") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("CompanyId") - .HasColumnType("nvarchar(450)"); - - b.Property("CompanyRole") - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("FirstName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("Headline") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("LastName") - .IsRequired() - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ProfilePicture") - .IsRequired() - .HasColumnType("varbinary(max)"); - - b.HasIndex("CompanyId"); - - b.HasDiscriminator().HasValue("Users"); - }); - - modelBuilder.Entity("AssessmentsJob", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("JobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("AssessmentsQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Questions", null) - .WithMany() - .HasForeignKey("QuestionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("AssessmentsTests", b => - { - b.HasOne("SkillProof.Entities.Models.Assessments", null) - .WithMany() - .HasForeignKey("AssessmentsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", null) - .WithMany() - .HasForeignKey("TestAttemptsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("JobQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("JobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Questions", null) - .WithMany() - .HasForeignKey("QuestionsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("JobUsers", b => - { - b.HasOne("SkillProof.Entities.Models.Job", null) - .WithMany() - .HasForeignKey("SavedJobsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Users", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("SkillModelUsers", b => - { - b.HasOne("SkillProof.Entities.Models.SkillModel", null) - .WithMany() - .HasForeignKey("SkillsId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Users", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Assessments", b => - { - b.HasOne("SkillProof.Entities.Models.SkillModel", "Skill") - .WithMany("Assessments") - .HasForeignKey("SkillId"); - - b.Navigation("Skill"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.CodeCompletionQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("CodeCompletionQuestion") - .HasForeignKey("SkillProof.Entities.Models.CodeCompletionQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.FillInTheBlankQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("FillInTheBlankQuestions") - .HasForeignKey("SkillProof.Entities.Models.FillInTheBlankQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.HasOne("SkillProof.Entities.Models.Companies", "Company") - .WithMany("Jobs") - .HasForeignKey("CompanyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Company"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.JobApplication", b => - { - b.HasOne("SkillProof.Entities.Models.Job", "Job") - .WithMany("JobApplications") - .HasForeignKey("JobId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", "Test") - .WithOne("JobApplication") - .HasForeignKey("SkillProof.Entities.Models.JobApplication", "TestId") - .OnDelete(DeleteBehavior.Restrict); - - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("JobApplications") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.NoAction) - .IsRequired(); - - b.Navigation("Job"); - - b.Navigation("Test"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.MultipleChoiceQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("MultipleChoiceQuestion") - .HasForeignKey("SkillProof.Entities.Models.MultipleChoiceQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TestAnswers", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithMany("TestAnswers") - .HasForeignKey("QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("SkillProof.Entities.Models.Tests", "Test") - .WithMany("TestAnswers") - .HasForeignKey("TestId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - - b.Navigation("Test"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("Tests") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.TrueFalseQuestions", b => - { - b.HasOne("SkillProof.Entities.Models.Questions", "Question") - .WithOne("TrueFalseQuestion") - .HasForeignKey("SkillProof.Entities.Models.TrueFalseQuestions", "QuestionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Question"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.UserExperiences", b => - { - b.HasOne("SkillProof.Entities.Models.Users", "User") - .WithMany("UserExperiences") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.HasOne("SkillProof.Entities.Models.Companies", "Companies") - .WithMany("Users") - .HasForeignKey("CompanyId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Companies"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Companies", b => - { - b.Navigation("Jobs"); - - b.Navigation("Users"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Job", b => - { - b.Navigation("JobApplications"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Questions", b => - { - b.Navigation("CodeCompletionQuestion"); - - b.Navigation("FillInTheBlankQuestions"); - - b.Navigation("MultipleChoiceQuestion"); - - b.Navigation("TestAnswers"); - - b.Navigation("TrueFalseQuestion"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.SkillModel", b => - { - b.Navigation("Assessments"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Tests", b => - { - b.Navigation("JobApplication"); - - b.Navigation("TestAnswers"); - }); - - modelBuilder.Entity("SkillProof.Entities.Models.Users", b => - { - b.Navigation("JobApplications"); - - b.Navigation("Tests"); - - b.Navigation("UserExperiences"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260519140320_SkillChanges.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260519140320_SkillChanges.cs deleted file mode 100644 index a17f9d4..0000000 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260519140320_SkillChanges.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace SkillProof.Data.Migrations -{ - /// - public partial class SkillChanges : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "SkillUsers"); - - migrationBuilder.CreateTable( - name: "SkillModelUsers", - columns: table => new - { - SkillsId = table.Column(type: "nvarchar(450)", nullable: false), - UsersId = table.Column(type: "nvarchar(450)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_SkillModelUsers", x => new { x.SkillsId, x.UsersId }); - table.ForeignKey( - name: "FK_SkillModelUsers_AspNetUsers_UsersId", - column: x => x.UsersId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_SkillModelUsers_Skills_SkillsId", - column: x => x.SkillsId, - principalTable: "Skills", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_SkillModelUsers_UsersId", - table: "SkillModelUsers", - column: "UsersId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "SkillModelUsers"); - - migrationBuilder.CreateTable( - name: "SkillUsers", - columns: table => new - { - SkillsId = table.Column(type: "nvarchar(450)", nullable: false), - UsersId = table.Column(type: "nvarchar(450)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_SkillUsers", x => new { x.SkillsId, x.UsersId }); - table.ForeignKey( - name: "FK_SkillUsers_AspNetUsers_UsersId", - column: x => x.UsersId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_SkillUsers_Skills_SkillsId", - column: x => x.SkillsId, - principalTable: "Skills", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_SkillUsers_UsersId", - table: "SkillUsers", - column: "UsersId"); - } - } -} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260520161302_EducationAdded.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260520161302_EducationAdded.cs deleted file mode 100644 index 2c0958c..0000000 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260520161302_EducationAdded.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace SkillProof.Data.Migrations -{ - /// - public partial class EducationAdded : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Educations", - columns: table => new - { - Id = table.Column(type: "nvarchar(450)", nullable: false), - UserId = table.Column(type: "nvarchar(450)", nullable: false), - School = table.Column(type: "nvarchar(max)", nullable: false), - Degree = table.Column(type: "nvarchar(max)", nullable: false), - FieldOfStudy = table.Column(type: "nvarchar(max)", nullable: false), - StartDate = table.Column(type: "datetime2", nullable: false), - EndDate = table.Column(type: "datetime2", nullable: true), - Description = table.Column(type: "nvarchar(max)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Educations", x => x.Id); - table.ForeignKey( - name: "FK_Educations_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_Educations_UserId", - table: "Educations", - column: "UserId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Educations"); - } - } -} diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260520161302_EducationAdded.Designer.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260520213503_Init.Designer.cs similarity index 99% rename from backend/SkillProof/SkillProof.Data/Migrations/20260520161302_EducationAdded.Designer.cs rename to backend/SkillProof/SkillProof.Data/Migrations/20260520213503_Init.Designer.cs index 0dbc007..ed2d662 100644 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260520161302_EducationAdded.Designer.cs +++ b/backend/SkillProof/SkillProof.Data/Migrations/20260520213503_Init.Designer.cs @@ -12,8 +12,8 @@ namespace SkillProof.Data.Migrations { [DbContext(typeof(SkillProofDbContext))] - [Migration("20260520161302_EducationAdded")] - partial class EducationAdded + [Migration("20260520213503_Init")] + partial class Init { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/backend/SkillProof/SkillProof.Data/Migrations/20260516164322_InitialCreate.cs b/backend/SkillProof/SkillProof.Data/Migrations/20260520213503_Init.cs similarity index 94% rename from backend/SkillProof/SkillProof.Data/Migrations/20260516164322_InitialCreate.cs rename to backend/SkillProof/SkillProof.Data/Migrations/20260520213503_Init.cs index 7e3ddec..0484dc7 100644 --- a/backend/SkillProof/SkillProof.Data/Migrations/20260516164322_InitialCreate.cs +++ b/backend/SkillProof/SkillProof.Data/Migrations/20260520213503_Init.cs @@ -6,7 +6,7 @@ namespace SkillProof.Data.Migrations { /// - public partial class InitialCreate : Migration + public partial class Init : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -248,7 +248,7 @@ protected override void Up(MigrationBuilder migrationBuilder) CreatedBy = table.Column(type: "nvarchar(max)", nullable: false), CreatedAt = table.Column(type: "datetime2", nullable: false), IsActive = table.Column(type: "bit", nullable: false), - SkillId = table.Column(type: "nvarchar(450)", nullable: false) + SkillId = table.Column(type: "nvarchar(450)", nullable: true) }, constraints: table => { @@ -257,8 +257,7 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "FK_Assessments_Skills_SkillId", column: x => x.SkillId, principalTable: "Skills", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + principalColumn: "Id"); }); migrationBuilder.CreateTable( @@ -347,7 +346,31 @@ protected override void Up(MigrationBuilder migrationBuilder) }); migrationBuilder.CreateTable( - name: "SkillUsers", + name: "Educations", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + School = table.Column(type: "nvarchar(max)", nullable: false), + Degree = table.Column(type: "nvarchar(max)", nullable: false), + FieldOfStudy = table.Column(type: "nvarchar(max)", nullable: false), + StartDate = table.Column(type: "datetime2", nullable: false), + EndDate = table.Column(type: "datetime2", nullable: true), + Description = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Educations", x => x.Id); + table.ForeignKey( + name: "FK_Educations_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "SkillModelUsers", columns: table => new { SkillsId = table.Column(type: "nvarchar(450)", nullable: false), @@ -355,15 +378,15 @@ protected override void Up(MigrationBuilder migrationBuilder) }, constraints: table => { - table.PrimaryKey("PK_SkillUsers", x => new { x.SkillsId, x.UsersId }); + table.PrimaryKey("PK_SkillModelUsers", x => new { x.SkillsId, x.UsersId }); table.ForeignKey( - name: "FK_SkillUsers_AspNetUsers_UsersId", + name: "FK_SkillModelUsers_AspNetUsers_UsersId", column: x => x.UsersId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( - name: "FK_SkillUsers_Skills_SkillsId", + name: "FK_SkillModelUsers_Skills_SkillsId", column: x => x.SkillsId, principalTable: "Skills", principalColumn: "Id", @@ -544,6 +567,7 @@ protected override void Up(MigrationBuilder migrationBuilder) UserId = table.Column(type: "nvarchar(450)", nullable: false), TestId = table.Column(type: "nvarchar(450)", nullable: true), Status = table.Column(type: "int", nullable: false), + IsRead = table.Column(type: "bit", nullable: false), AppliedAt = table.Column(type: "datetime2", nullable: false) }, constraints: table => @@ -663,6 +687,11 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "AssessmentsTests", column: "TestAttemptsId"); + migrationBuilder.CreateIndex( + name: "IX_Educations_UserId", + table: "Educations", + column: "UserId"); + migrationBuilder.CreateIndex( name: "IX_JobApplications_JobId", table: "JobApplications", @@ -691,8 +720,8 @@ protected override void Up(MigrationBuilder migrationBuilder) column: "CompanyId"); migrationBuilder.CreateIndex( - name: "IX_SkillUsers_UsersId", - table: "SkillUsers", + name: "IX_SkillModelUsers_UsersId", + table: "SkillModelUsers", column: "UsersId"); migrationBuilder.CreateIndex( @@ -751,6 +780,9 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "CodeCompletionQuestions"); + migrationBuilder.DropTable( + name: "Educations"); + migrationBuilder.DropTable( name: "FillInTheBlankQuestions"); @@ -764,7 +796,7 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "MultipleChoiceQuestion"); migrationBuilder.DropTable( - name: "SkillUsers"); + name: "SkillModelUsers"); migrationBuilder.DropTable( name: "TestAnswers"); diff --git a/backend/SkillProof/SkillProof.Data/SkillProof.Data.csproj b/backend/SkillProof/SkillProof.Data/SkillProof.Data.csproj index b1c5b57..dbcae77 100644 --- a/backend/SkillProof/SkillProof.Data/SkillProof.Data.csproj +++ b/backend/SkillProof/SkillProof.Data/SkillProof.Data.csproj @@ -21,4 +21,8 @@ + + + + diff --git a/web/src/app/components/edit-profile/edit-profile.html b/web/src/app/components/edit-profile/edit-profile.html index ed177ba..6450ab5 100644 --- a/web/src/app/components/edit-profile/edit-profile.html +++ b/web/src/app/components/edit-profile/edit-profile.html @@ -51,6 +51,7 @@

Your Skills

+ @if(educations.length > 0) { @for (skill of userSkills; track skill.id) {
{{ skill.name }} @@ -59,6 +60,10 @@

Your Skills

} + } @else { +

Add a skill

+ } +
From 9fd9641a5de9fb46f88a645c4583f91e27139e85 Mon Sep 17 00:00:00 2001 From: KelemenOzseb Date: Thu, 21 May 2026 00:06:53 +0200 Subject: [PATCH 55/58] bug fixes --- web/src/app/app-routing-module.ts | 4 ++- .../components/company-home/company-home.ts | 32 ++++++++++++++++++- .../app/components/job-detail/job-detail.html | 4 +-- .../app/components/job-detail/job-detail.ts | 1 + 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/web/src/app/app-routing-module.ts b/web/src/app/app-routing-module.ts index 96cec18..94e08a6 100644 --- a/web/src/app/app-routing-module.ts +++ b/web/src/app/app-routing-module.ts @@ -96,7 +96,9 @@ const routes: Routes = [ { path: 'skills', component: AdminSkill}, - { path: 'skill/:skillId/test/:assessmentId', component: TestTake }, + { path: 'skill/:skillId/test/:assessmentId', component: TestTake, + canActivate: [authGuard] + }, {path: 'search', component: JobSearch}, diff --git a/web/src/app/components/company-home/company-home.ts b/web/src/app/components/company-home/company-home.ts index 56854c9..f626c1f 100644 --- a/web/src/app/components/company-home/company-home.ts +++ b/web/src/app/components/company-home/company-home.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { Observable, filter, switchMap, of } from 'rxjs'; +import { Observable, filter, switchMap, of, map } from 'rxjs'; import { JobViewDto } from '../../Models/Dtos/Job/JobView-dto'; import { JobService } from '../../services/job-service'; import { ProfileViewDto } from '../../Models/Dtos/User/profile-view-dto'; @@ -32,6 +32,7 @@ export class CompanyHome implements OnInit { } return this.jobService.getJobsByCompanyId(profile.companyId); }), + map((jobs) => this.parseJobsTags(jobs)) ); if(!this.selectedJob) { this.companyJobs$.subscribe(jobs => { this.selectedJob = jobs[0] ?? null}) @@ -73,4 +74,33 @@ export class CompanyHome implements OnInit { selectJob(job: JobViewDto): void { this.selectedJob = job; } + + private parseJobsTags(jobs: JobViewDto[]): JobViewDto[] { + return jobs.map((job) => { + const parsedJob = { ...job }; + if (typeof parsedJob.tags === 'string') { + const tagString = parsedJob.tags as unknown as string; + parsedJob.tags = tagString + .split(',') + .map((t) => t + .trim() + .replace(/[\[\]"']/g, '') + ) + .filter((t) => t !== '') + } + if (Array.isArray(parsedJob.tags)) { + parsedJob.tags = parsedJob.tags + .map((t) => + typeof t === 'string' ? + t.replace(/[\[\]"']/g, '').trim() + : t + ) + } + if (!Array.isArray(parsedJob.tags)) { + parsedJob.tags = []; + } + + return parsedJob + }) + } } diff --git a/web/src/app/components/job-detail/job-detail.html b/web/src/app/components/job-detail/job-detail.html index 3baada4..25502b5 100644 --- a/web/src/app/components/job-detail/job-detail.html +++ b/web/src/app/components/job-detail/job-detail.html @@ -1,6 +1,6 @@
- ← Back to jobs + ← Back to jobs
@@ -9,7 +9,7 @@

{{ errorMessage }}

- Back to Home + Back to Home
diff --git a/web/src/app/components/job-detail/job-detail.ts b/web/src/app/components/job-detail/job-detail.ts index 12a2649..c00eabc 100644 --- a/web/src/app/components/job-detail/job-detail.ts +++ b/web/src/app/components/job-detail/job-detail.ts @@ -48,6 +48,7 @@ export class JobDetail implements OnInit, OnDestroy { this.profileSub = this.profileService.currentProfile$.subscribe((profile) => { this.updateUIStatus(profile); + this.profileService.loadProfile(profile!.id!) }); this.checkUserJobStatus() } From 65dca076d9d75751b0b0d12c1cc7d5ec38ab8b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A1nya=20Marcell?= Date: Wed, 20 May 2026 23:04:46 +0200 Subject: [PATCH 56/58] update readme --- README.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0c31a71..89ccdaf 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,79 @@ -# skillproof -## Adatbázis beállítása helyi fejlesztéshez (Docker) +# Skillproof – Kompetencia-alapú toborzási platform -Mivel a projekt Linux/Mac környezetben is futtatható, az adatbázist (MSSQL) egy Docker konténerben futtatjuk. Nincs szükség bonyolult lokális telepítésekre! +A Skillproof egy modern, professzionális hálózatépítő és álláskereső webalkalmazás, amely a munkáltatókat és a munkavállalókat köti össze. A platform fókuszában a tudásalapú kiválasztás áll: a cégek az álláshirdetésekhez saját kompetenciateszteket rendelhetnek, amelyeket a jelölteknek a jelentkezés során ki kell tölteniük. -### Előfeltételek -* **Docker** és **Docker Compose** telepítve legyen a gépeden. -* **.NET EF Core Tools** telepítve legyen globálisan vagy lokálisan (`dotnet tool install dotnet-ef`). +## Célkitűzések + +- Objektív, valós tudáson alapuló szűrés biztosítása a munkáltatók számára. +- A jelentkezési és kiválasztási folyamat hatékonyságának növelése. +- Átlátható felület biztosítása a pályázóknak a képességeik bizonyítására és az ideális pozíciók megtalálására. + +## Technológiai Stack és Fejlesztési Környezet + +- **Backend:** C# .NET 9 +- **Frontend:** Angular 21 +- **Adatbázis:** MSSQL + +### Adatbázis indítása és futtatás + +A backend elindításához és az adatbázis migrációk futtatásához nyiss egy terminált a szerver oldali gyökérmappában: -### 1. Adatbázis szerver indítása -Nyiss egy terminált a projekt gyökerében (ahol a `docker-compose.yml` van), és futtasd ezt: ```bash -docker compose up -d +dotnet tool install --global dotnet-ef +dotnet ef database update +dotnet run +``` + +A frontend indítása a kliens mappában: + +```bash +npm install +ng serve +``` + +## Szerepkörök és Felhasználók + +- **Munkáltató (B2B):** Álláshirdetések és az azokhoz tartozó tesztek létrehozása, jelentkezők kezelése és eredményeik kiértékelése. +- **Pályázó (B2C):** Profil menedzselése, állások böngészése, mentése, tesztek kitöltése és jelentkezés a kiválasztott pozíciókra. +- **Rendszer adminisztrátor (Admin):** A platform globális felügyelete. + +## Csapat és Felelősségek + +- **[@BenjaminKovacs09](https://github.com/BenjaminKovacs09)** – Project Manager +- **[@Marci260](https://github.com/Marci260)** – Architect +- **[@zadoriaron](https://github.com/zadoriaron)** – Fullstack fejlesztő +- **[@KelemenOzseb](https://github.com/KelemenOzseb)** – Fullstack fejlesztő +- **[@oli-tolnai](https://github.com/oli-tolnai)** – Fullstack fejlesztő +- **[@AdamRevesz](https://github.com/AdamRevesz)** – Fullstack fejlesztő + +## Tervezett funkciók + +### 1. Publikus felületek + +- Értékajánlat kommunikálása (gyorsabb, tesztalapú kiválasztás). +- Regisztráció és bejelentkezés különválasztott munkáltatói és pályázói folyamattal. + +### 2. Munkáltatói funkciók + +- **Munkáltatói Dashboard:** Az aktív és lezárt hirdetések kártyás áttekintése. +- **Álláshirdetések kezelése:** + - Új pozíciók létrehozása (cím, leírás, elvárások). + - Meglévő hirdetések módosítása vagy törlése. +- **Teszt- és kérdésbank:** + - Kérdések és komplett kompetenciatesztek összeállítása. + - Tesztek hozzárendelése konkrét álláshirdetésekhez. +- **Jelentkezések adminisztrációja:** + - Pályázók listázása egy adott pozícióra. + - A kitöltött teszteredmények megtekintése a jelentkezők profilja mellett. + - AI asszisztált kiértékelés: A kifejtős kérdésekre adott válaszokat a rendszer mesterséges intelligencia segítségével előzetesen kiértékeli, amelyet a munkáltató felülvizsgálhat és szükség esetén manuálisan felülírhat. + +### 3. Pályázói funkciók + +- **Profilkezelés:** Szakmai tapasztalatok, készségek és személyes adatok szerkesztése. +- **Álláskeresés:** + - Elérhető pozíciók listázása és részleteik megtekintése. + - Állások kedvencekhez adása (könyvjelzőzés későbbi megtekintésre). +- **Jelentkezési folyamat:** + - Pályázás indítása a felületen keresztül. + - **Kompetenciateszt kitöltése:** Amennyiben a munkáltató tesztet rendelt az álláshoz, annak integrált kitöltése a jelentkezési folyamat részeként. + - Sikeres jelentkezés véglegesítése és visszajelzés a felhasználónak. From 33c9526eb035516068d6fec472b3359c2cfb8c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A1nya=20Marcell?= Date: Thu, 21 May 2026 02:19:23 +0200 Subject: [PATCH 57/58] fixes --- .../SkillProof.Logic/Helper/DbInitializer.cs | 31 ++++++- .../SkillProof.Logic/User/UserLogic.cs | 2 +- .../components/edit-profile/edit-profile.html | 16 ++-- .../components/edit-profile/edit-profile.ts | 88 +++++++++---------- 4 files changed, 82 insertions(+), 55 deletions(-) diff --git a/backend/SkillProof/SkillProof.Logic/Helper/DbInitializer.cs b/backend/SkillProof/SkillProof.Logic/Helper/DbInitializer.cs index 5583677..f78f063 100644 --- a/backend/SkillProof/SkillProof.Logic/Helper/DbInitializer.cs +++ b/backend/SkillProof/SkillProof.Logic/Helper/DbInitializer.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using SkillProof.Entities.Enums; using SkillProof.Entities.Models; @@ -68,7 +69,20 @@ public static void Seed(DbContext context) .ThenInclude(q => q.TrueFalseQuestion) .FirstOrDefault(a => a.Title == SeedAssessmentTitle); - var existingUser = context.Set().FirstOrDefault(); + var adminRole = context.Set().FirstOrDefault(r => r.NormalizedName == "ADMIN"); + + if (adminRole == null) + { + adminRole = new IdentityRole + { + Id = Guid.NewGuid().ToString(), + Name = "Admin", + NormalizedName = "ADMIN" + }; + context.Set().Add(adminRole); + } + + var existingUser = context.Set().FirstOrDefault(u => u.NormalizedUserName == "SYSTEMADMIN"); string adminUserId; if (existingUser != null) @@ -88,10 +102,25 @@ public static void Seed(DbContext context) SecurityStamp = Guid.NewGuid().ToString() }; + var passwordHasher = new PasswordHasher(); + newUser.PasswordHash = passwordHasher.HashPassword(newUser, "AdminPassword123!"); + context.Set().Add(newUser); adminUserId = newUser.Id; } + var userRoleExists = context.Set>() + .Any(ur => ur.UserId == adminUserId && ur.RoleId == adminRole.Id); + + if (!userRoleExists) + { + context.Set>().Add(new IdentityUserRole + { + UserId = adminUserId, + RoleId = adminRole.Id + }); + } + var seedFilePath = Path.Combine(AppContext.BaseDirectory, "seed-questions.json"); if (!File.Exists(seedFilePath)) diff --git a/backend/SkillProof/SkillProof.Logic/User/UserLogic.cs b/backend/SkillProof/SkillProof.Logic/User/UserLogic.cs index b156808..3f60eca 100644 --- a/backend/SkillProof/SkillProof.Logic/User/UserLogic.cs +++ b/backend/SkillProof/SkillProof.Logic/User/UserLogic.cs @@ -232,7 +232,7 @@ public async Task GetUserByIdAsync(string id) Id = user.Id, FullName = $"{user.FirstName} {user.LastName}", Email = user.Email, - Image = Convert.ToBase64String(user.ProfilePicture), + Image = user.ProfilePicture != null ? Convert.ToBase64String(user.ProfilePicture) : null, Bio = user.Bio, Headline = user.Headline, CompanyId = user.CompanyId, diff --git a/web/src/app/components/edit-profile/edit-profile.html b/web/src/app/components/edit-profile/edit-profile.html index 6450ab5..709db89 100644 --- a/web/src/app/components/edit-profile/edit-profile.html +++ b/web/src/app/components/edit-profile/edit-profile.html @@ -52,14 +52,14 @@

Your Skills

@if(educations.length > 0) { - @for (skill of userSkills; track skill.id) { -
- {{ skill.name }} - -
- } + @for (skill of userSkills; track skill.id) { +
+ {{ skill.name }} + +
+ } } @else {

Add a skill

} diff --git a/web/src/app/components/edit-profile/edit-profile.ts b/web/src/app/components/edit-profile/edit-profile.ts index 0fe9ec7..46a8ec3 100644 --- a/web/src/app/components/edit-profile/edit-profile.ts +++ b/web/src/app/components/edit-profile/edit-profile.ts @@ -20,7 +20,6 @@ import { ExperienceViewDto } from '../../Models/Dtos/Experience/ExperienceViewDt styleUrl: './edit-profile.scss', }) export class EditProfile implements OnInit { - constructor( private fb: FormBuilder, private profileService: ProfileService, @@ -29,8 +28,8 @@ export class EditProfile implements OnInit { private router: Router, private skillService: SkillService, private educationService: EducationService, - private experienceService: ExperienceService - ) { } + private experienceService: ExperienceService, + ) {} form!: FormGroup; selectedImageBase64: string | null = null; @@ -49,21 +48,20 @@ export class EditProfile implements OnInit { experienceForm!: FormGroup; educationForm!: FormGroup; - ngOnInit(): void { this.form = this.fb.group({ email: ['', Validators.required], firstName: ['', Validators.required], lastName: ['', Validators.required], headline: [''], - bio: [''] + bio: [''], }); this.experienceForm = this.fb.group({ jobTitle: ['', Validators.required], companyName: ['', Validators.required], startDate: ['', Validators.required], - endDate: [''] + endDate: [''], }); this.educationForm = this.fb.group({ @@ -72,7 +70,7 @@ export class EditProfile implements OnInit { fieldOfStudy: ['', Validators.required], startDate: ['', Validators.required], endDate: [''], - description: [''] + description: [''], }); this.getAllSkills(); @@ -82,7 +80,7 @@ export class EditProfile implements OnInit { this.profileService.loadProfile(userId); } - this.profileService.currentProfile$.subscribe(profile => { + this.profileService.currentProfile$.subscribe((profile) => { if (!profile) return; this.form.patchValue({ @@ -90,13 +88,14 @@ export class EditProfile implements OnInit { firstName: profile.fullName?.split(' ')[0] || '', lastName: profile.fullName?.split(' ')[1] || '', headline: profile.headline, - bio: profile.bio + bio: profile.bio, }); if (this.userSkills.length === 0 && profile.skills) { - this.userSkills = typeof profile.skills[0] === 'string' - ? (profile.skills as any as string[]).map(s => ({ id: s, name: s })) - : [...profile.skills] as any; + this.userSkills = + typeof profile.skills[0] === 'string' + ? (profile.skills as any as string[]).map((s) => ({ id: s, name: s })) + : ([...profile.skills] as any); } const uid = this.authService.getUserId(); @@ -153,32 +152,31 @@ export class EditProfile implements OnInit { dto.profilePicture = this.selectedImageBase64; } - this.http.put(`${environment.apiUrls.updateUser}/${userId}`, dto) - .subscribe({ - next: () => { - this.profileService.loadProfile(userId); - this.router.navigate(['/viewProfile']); - }, - error: (err) => { - console.error(err); - } - }); + this.http.put(`${environment.apiUrls.updateUser}/${userId}`, dto).subscribe({ + next: () => { + this.profileService.loadProfile(userId); + this.router.navigate(['/viewProfile']); + }, + error: (err) => { + console.error(err); + }, + }); } onSkillSelected(event: Event): void { const selectElement = event.target as HTMLSelectElement; const selectedId = selectElement.value; - if (!selectedId) return; + if (!selectedId) { + return; + } - const skillObj = this.availableSkills.find(s => s.id === selectedId); + const skillObj = this.availableSkills.find((s) => s.id === selectedId); - if (skillObj && !this.userSkills.some(s => s.id === skillObj.id)) { + if (skillObj && !this.userSkills.some((s) => s.id === skillObj.id)) { this.userSkills.push({ id: skillObj.id, name: skillObj.name }); this.pendingSkillAdditions.add(skillObj.id); } - - selectElement.value = ""; } removeSkill(index: number, skillId: string): void { @@ -194,17 +192,19 @@ export class EditProfile implements OnInit { this.userSkills.splice(index, 1); console.log('Skill removed successfully'); }, - error: (err) => console.error(`Failed to remove skill ${skillId}`, err) + error: (err) => console.error(`Failed to remove skill ${skillId}`, err), }); } } saveSkills(): void { const userId = this.authService.getUserId(); + console.log(this.pendingSkillAdditions); if (!userId || this.pendingSkillAdditions.size === 0) return; const newSkillsArray = Array.from(this.pendingSkillAdditions); - console.log("SENDING NEW SKILLS:", newSkillsArray); + + console.log('SENDING NEW SKILLS:', newSkillsArray); this.profileService.addSkillsToUser(userId, newSkillsArray).subscribe({ next: () => { @@ -212,20 +212,21 @@ export class EditProfile implements OnInit { this.pendingSkillAdditions.clear(); this.profileService.loadProfile(userId); }, - error: (err) => console.error(`Failed to assign skills`, err) + error: (err) => console.error(`Failed to assign skills`, err), }); } + getAllSkills(): void { this.skillService.skills$.subscribe({ next: (skills) => { this.availableSkills = skills; - console.log('skills', skills) - } + console.log('skills', skills); + }, }); if (this.availableSkills.length === 0) { this.skillService.getAllSkills().subscribe({ - error: (err) => console.error('Failed to load skills list', err) + error: (err) => console.error('Failed to load skills list', err), }); } } @@ -259,7 +260,7 @@ export class EditProfile implements OnInit { } else { alert('Something went wrong on the server. Check the IDE terminal.'); } - } + }, }); } @@ -280,7 +281,7 @@ export class EditProfile implements OnInit { this.loadExperiences(); this.experienceForm.reset(); }, - error: (err) => console.error('Failed to create experience', err) + error: (err) => console.error('Failed to create experience', err), }); } @@ -293,9 +294,9 @@ export class EditProfile implements OnInit { this.educations = items; }, error: (err) => { - console.error('Error while loading in skills', err) - } - }) + console.error('Error while loading in skills', err); + }, + }); } loadExperiences(): void { @@ -306,7 +307,7 @@ export class EditProfile implements OnInit { next: (items) => { this.experiences = items; }, - error: (err) => console.error('Failed to load experiences', err) + error: (err) => console.error('Failed to load experiences', err), }); } @@ -320,7 +321,7 @@ export class EditProfile implements OnInit { this.loadEducations(); this.profileService.loadProfile(userId); }, - error: (err) => console.error('Failed to delete education', err) + error: (err) => console.error('Failed to delete education', err), }); } @@ -334,10 +335,7 @@ export class EditProfile implements OnInit { this.loadExperiences(); this.profileService.loadProfile(userId); }, - error: (err) => console.error('Failed to delete experience', err) + error: (err) => console.error('Failed to delete experience', err), }); } - - - -} \ No newline at end of file +} From 611b1fcf5fa652056ec88cf60867544049b0fc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A1nya=20Marcell?= Date: Thu, 21 May 2026 02:47:22 +0200 Subject: [PATCH 58/58] small fixes --- README.md | 6 ++- .../components/edit-profile/edit-profile.html | 21 ++++------ .../components/edit-profile/edit-profile.ts | 42 +++++++++++-------- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 89ccdaf..b246dfc 100644 --- a/README.md +++ b/README.md @@ -69,11 +69,13 @@ ng serve ### 3. Pályázói funkciók -- **Profilkezelés:** Szakmai tapasztalatok, készségek és személyes adatok szerkesztése. +- **Profilkezelés:** + - Szakmai tapasztalatok, készségek és személyes adatok szerkesztése. + - Skillek hozzárendelése a profilhoz, vannak olyan skillek amelyeket teszt kitöltésével érhet el a felhasználó - **Álláskeresés:** - Elérhető pozíciók listázása és részleteik megtekintése. - Állások kedvencekhez adása (könyvjelzőzés későbbi megtekintésre). - **Jelentkezési folyamat:** - Pályázás indítása a felületen keresztül. - - **Kompetenciateszt kitöltése:** Amennyiben a munkáltató tesztet rendelt az álláshoz, annak integrált kitöltése a jelentkezési folyamat részeként. + - Kompetenciateszt kitöltése: Amennyiben a munkáltató tesztet rendelt az álláshoz, annak integrált kitöltése a jelentkezési folyamat részeként. - Sikeres jelentkezés véglegesítése és visszajelzés a felhasználónak. diff --git a/web/src/app/components/edit-profile/edit-profile.html b/web/src/app/components/edit-profile/edit-profile.html index 709db89..c925a39 100644 --- a/web/src/app/components/edit-profile/edit-profile.html +++ b/web/src/app/components/edit-profile/edit-profile.html @@ -51,21 +51,16 @@

Your Skills

- @if(educations.length > 0) { - @for (skill of userSkills; track skill.id) { -
- {{ skill.name }} - -
- } - } @else { -

Add a skill

+ @for (skill of userSkills; track skill.id) { +
+ {{ skill.name }} + +
} -
- +

Add a skill