продолжается работа над оценкой резюме
This commit is contained in:
parent
be517e47e4
commit
e18de8db1d
@ -91,16 +91,6 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.ResumeId <= 0)
|
||||
{
|
||||
throw new ArgumentException("Нет идентификатора резюме", nameof(model.ResumeId));
|
||||
}
|
||||
|
||||
if (model.UserId <= 0)
|
||||
{
|
||||
throw new ArgumentException("Нет идентификатора пользователя", nameof(model.UserId));
|
||||
}
|
||||
|
||||
var element = _assessmentStorage.GetElement(new AssessmentSearchModel
|
||||
{
|
||||
ResumeId = model.ResumeId,
|
||||
|
@ -21,15 +21,16 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
_vacancyStorage = vacancyStorage;
|
||||
_userStorage = userStorage;
|
||||
}
|
||||
public int Create(CompanyBindingModel model)
|
||||
public int? Create(CompanyBindingModel model)
|
||||
{
|
||||
CheckModel(model);
|
||||
if (_сompanyStorage.Insert(model) == null)
|
||||
var companyId = _сompanyStorage.Insert(model);
|
||||
if (companyId == null)
|
||||
{
|
||||
_logger.LogWarning("Insert operation failed");
|
||||
return 0;
|
||||
}
|
||||
return model.Id;
|
||||
return companyId;
|
||||
}
|
||||
|
||||
public bool Delete(CompanyBindingModel model)
|
||||
|
@ -95,16 +95,6 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
{
|
||||
throw new ArgumentNullException("Нет названия критерия оценивания", nameof(model.Name));
|
||||
}
|
||||
|
||||
if (model.Weight <= 0)
|
||||
{
|
||||
throw new ArgumentException("Некорректный вес критерия оценивания", nameof(model.Weight));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(model.Type.ToString()))
|
||||
{
|
||||
throw new ArgumentNullException("Нет типа критерия оценивания", nameof(model.Type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,16 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IResumeStorage _resumeStorage;
|
||||
public ResumeLogic(ILogger<ResumeLogic> logger, IResumeStorage resumeStorage)
|
||||
private readonly IVacancyStorage _vacancyStorage;
|
||||
private readonly IUserStorage _userStorage;
|
||||
private readonly IAssessmentStorage _assessmentStorage;
|
||||
public ResumeLogic(ILogger<ResumeLogic> logger, IResumeStorage resumeStorage, IUserStorage userStorage, IVacancyStorage vacancyStorage, IAssessmentStorage assessmentStorage)
|
||||
{
|
||||
_logger = logger;
|
||||
_resumeStorage = resumeStorage;
|
||||
_userStorage = userStorage;
|
||||
_vacancyStorage = vacancyStorage;
|
||||
_assessmentStorage = assessmentStorage;
|
||||
}
|
||||
public bool Create(ResumeBindingModel model)
|
||||
{
|
||||
@ -51,8 +57,35 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
_logger.LogWarning("ReadElement element not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
var assessments = _assessmentStorage.GetFilteredList(new AssessmentSearchModel { UserId = element.Id });
|
||||
var assessmentViewModels = assessments?.Select(a => new AssessmentViewModel
|
||||
{
|
||||
Id = a.Id,
|
||||
ResumeId = a.ResumeId,
|
||||
UserId = a.UserId,
|
||||
Comment = a.Comment,
|
||||
AssessmentCriterions = a.AssessmentCriterions
|
||||
}).ToList() ?? new List<AssessmentViewModel>();
|
||||
|
||||
var resumeViewModel = new ResumeViewModel
|
||||
{
|
||||
Id = element.Id,
|
||||
VacancyId = element.VacancyId,
|
||||
VacancyName = _vacancyStorage.GetElement(new VacancySearchModel { Id = element.VacancyId }).JobTitle,
|
||||
UserId = element.UserId,
|
||||
UserName = _userStorage.GetElement(new UserSearchModel { Id = element.UserId }).Name + " " + _userStorage.GetElement(new UserSearchModel { Id = element.UserId }).Surname,
|
||||
Title = element.Title,
|
||||
Experience = element.Experience,
|
||||
Education = element.Education,
|
||||
PhotoFilePath = element.PhotoFilePath,
|
||||
Description = element.Description,
|
||||
Skills = element.Skills,
|
||||
Status = element.Status,
|
||||
PreviousAssessments = assessmentViewModels
|
||||
};
|
||||
_logger.LogInformation("ReadElement find. Id: {Id}", element.Id);
|
||||
return element;
|
||||
return resumeViewModel;
|
||||
}
|
||||
|
||||
public List<ResumeViewModel>? ReadList(ResumeSearchModel? model)
|
||||
|
@ -14,10 +14,14 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUserStorage _userStorage;
|
||||
public UserLogic(ILogger<UserLogic> logger, IUserStorage userStorage)
|
||||
private readonly IResumeStorage _resumeStorage;
|
||||
private readonly IVacancyStorage _vacancyStorage;
|
||||
public UserLogic(ILogger<UserLogic> logger, IUserStorage userStorage, IResumeStorage resumeStorage, IVacancyStorage vacancyStorage)
|
||||
{
|
||||
_logger = logger;
|
||||
_userStorage = userStorage;
|
||||
_resumeStorage = resumeStorage;
|
||||
_vacancyStorage = vacancyStorage;
|
||||
}
|
||||
|
||||
private string EncryptPassword(string password)
|
||||
@ -57,28 +61,50 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
throw new ArgumentNullException(nameof(model));
|
||||
}
|
||||
var element = _userStorage.GetElement(model);
|
||||
if (element != null)
|
||||
if (element == null)
|
||||
{
|
||||
_logger.LogWarning("ReadElement: User not found for Id: {Id}", model.Id);
|
||||
return null;
|
||||
}
|
||||
var resumes = _resumeStorage.GetFilteredList(new ResumeSearchModel { UserId = element.Id });
|
||||
var resumeViewModels = resumes?.Select(r => new ResumeViewModel
|
||||
{
|
||||
Id = r.Id,
|
||||
VacancyId = r.VacancyId,
|
||||
VacancyName = _vacancyStorage.GetElement(new VacancySearchModel { Id = r.VacancyId }).JobTitle,
|
||||
UserId = r.UserId,
|
||||
Title = r.Title,
|
||||
Experience = r.Experience,
|
||||
Education = r.Education,
|
||||
PhotoFilePath = r.PhotoFilePath,
|
||||
Description = r.Description,
|
||||
Skills = r.Skills,
|
||||
Status = r.Status
|
||||
}).ToList() ?? new List<ResumeViewModel>();
|
||||
|
||||
string hashedPassword = element.Password;
|
||||
if (element != null && model.Password != element.Password && model.Password != null)
|
||||
if (model.Password != element.Password && model.Password != null)
|
||||
{
|
||||
hashedPassword = EncryptPassword(model.Password);
|
||||
}
|
||||
if (element == null)
|
||||
|
||||
var userViewModel = new UserViewModel
|
||||
{
|
||||
_logger.LogWarning("ReadElement element not found");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element.Password == hashedPassword)
|
||||
{
|
||||
_logger.LogInformation("ReadElement find. Id: {Id}", element.Id);
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
Id = element.Id,
|
||||
Surname = element.Surname,
|
||||
Name = element.Name,
|
||||
LastName = element.LastName,
|
||||
Email = element.Email,
|
||||
Password = hashedPassword,
|
||||
EmailConfirmed = element.EmailConfirmed,
|
||||
AvatarFilePath = element.AvatarFilePath,
|
||||
CompanyId = element.CompanyId,
|
||||
PhoneNumber = element.PhoneNumber,
|
||||
Role = element.Role,
|
||||
Resumes = resumeViewModels
|
||||
};
|
||||
_logger.LogInformation("ReadElement: User found. Id: {Id}", element.Id);
|
||||
return userViewModel;
|
||||
}
|
||||
|
||||
public List<UserViewModel>? ReadList(UserSearchModel? model)
|
||||
@ -104,7 +130,6 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
{
|
||||
if (!Regex.IsMatch(model.Password, @"^^((\w+\d+\W+)|(\w+\W+\d+)|(\d+\w+\W+)|(\d+\W+\w+)|(\W+\w+\d+)|(\W+\d+\w+))[\w\d\W]*$", RegexOptions.IgnoreCase))
|
||||
{
|
||||
return false;
|
||||
throw new ArgumentException("Неправильно введенный пароль", nameof(model.Password));
|
||||
}
|
||||
model.Password = EncryptPassword(model.Password);
|
||||
|
@ -12,10 +12,15 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IVacancyStorage _vacancyStorage;
|
||||
public VacancyLogic(ILogger<VacancyLogic> logger, IVacancyStorage vacancyStorage)
|
||||
private readonly IResumeStorage _resumeStorage;
|
||||
private readonly ICompanyStorage _companyStorage;
|
||||
|
||||
public VacancyLogic(ILogger<VacancyLogic> logger, IVacancyStorage vacancyStorage, IResumeStorage resumeStorage, ICompanyStorage companyStorage)
|
||||
{
|
||||
_logger = logger;
|
||||
_vacancyStorage = vacancyStorage;
|
||||
_resumeStorage = resumeStorage;
|
||||
_companyStorage = companyStorage;
|
||||
}
|
||||
public bool Create(VacancyBindingModel model)
|
||||
{
|
||||
@ -52,8 +57,41 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||
_logger.LogWarning("ReadElement element not found");
|
||||
return null;
|
||||
}
|
||||
var resumes = _resumeStorage.GetFilteredList(new ResumeSearchModel { VacancyId = element.Id });
|
||||
var resumeViewModels = resumes?.Select(r => new ResumeViewModel
|
||||
{
|
||||
Id = r.Id,
|
||||
VacancyId = r.VacancyId,
|
||||
VacancyName = _vacancyStorage.GetElement(new VacancySearchModel { Id = r.VacancyId }).JobTitle,
|
||||
UserName = r.UserName,
|
||||
UserId = r.UserId,
|
||||
Title = r.Title,
|
||||
Experience = r.Experience,
|
||||
Education = r.Education,
|
||||
PhotoFilePath = r.PhotoFilePath,
|
||||
Description = r.Description,
|
||||
Skills = r.Skills,
|
||||
Status = r.Status
|
||||
}).ToList() ?? new List<ResumeViewModel>();
|
||||
|
||||
var vacancyViewModel = new VacancyViewModel
|
||||
{
|
||||
Id = element.Id,
|
||||
CompanyId = element.CompanyId,
|
||||
CompanyName = _companyStorage.GetElement(new CompanySearchModel { Id = element.CompanyId}).Name,
|
||||
CreatedAt = element.CreatedAt,
|
||||
Description = element.Description,
|
||||
JobTitle = element.JobTitle,
|
||||
JobType = element.JobType,
|
||||
Requirements = element.Requirements,
|
||||
Responsibilities = element.Responsibilities,
|
||||
Salary = element.Salary,
|
||||
Status = element.Status,
|
||||
Tags = element.Tags,
|
||||
Resumes = resumeViewModels
|
||||
};
|
||||
_logger.LogInformation("ReadElement find. Id: {Id}", element.Id);
|
||||
return element;
|
||||
return vacancyViewModel;
|
||||
}
|
||||
|
||||
public List<VacancyViewModel>? ReadList(VacancySearchModel? model)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
@ -10,7 +11,6 @@ namespace CandidateReviewClientApp
|
||||
private static readonly HttpClient _user = new();
|
||||
public static UserViewModel? User { get; set; } = null;
|
||||
public static CompanyViewModel? Company { get; set; } = null;
|
||||
public static VacancyViewModel? Vacancy { get; set; } = null;
|
||||
public static void Connect(IConfiguration configuration)
|
||||
{
|
||||
_user.BaseAddress = new Uri(configuration["IPAddress"]);
|
||||
@ -30,6 +30,36 @@ namespace CandidateReviewClientApp
|
||||
throw new Exception(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<T?> GetRequestAsync<T>(string requestUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Асинхронный запрос
|
||||
var response = await _user.GetAsync(requestUrl);
|
||||
|
||||
// Чтение содержимого ответа
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Проверка статуса ответа
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
// Десериализация результата
|
||||
return JsonConvert.DeserializeObject<T>(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(result);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Логирование или дополнительная обработка исключений, если требуется
|
||||
throw new Exception($"Ошибка при выполнении запроса к {requestUrl}: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void PostRequest<T>(string requestUrl, T model)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(model);
|
||||
@ -41,5 +71,48 @@ namespace CandidateReviewClientApp
|
||||
throw new Exception(result);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<int> PostRequestAsync(string requestUrl, object model)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(model);
|
||||
var data = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _user.PostAsync(requestUrl, data);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"HTTP Error {response.StatusCode}: {errorContent}");
|
||||
}
|
||||
|
||||
var responseJson = await response.Content.ReadAsStringAsync();
|
||||
dynamic responseObject = JsonConvert.DeserializeObject(responseJson);
|
||||
|
||||
try
|
||||
{
|
||||
return (int)responseObject.id;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Could not parse ID from response: {responseJson}, Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task PostRequestAsynchron(string requestUrl, object model)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(model);
|
||||
var data = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _user.PostAsync(requestUrl, data);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"HTTP Error {response.StatusCode}: {errorContent}");
|
||||
}
|
||||
|
||||
var responseJson = await response.Content.ReadAsStringAsync();
|
||||
dynamic responseObject = JsonConvert.DeserializeObject(responseJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
79
CandidateReviewClientApp/Controllers/AssessmentController.cs
Normal file
79
CandidateReviewClientApp/Controllers/AssessmentController.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using CandidateReviewClientApp.Models;
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using CandidateReviewDataModels.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CandidateReviewClientApp.Controllers
|
||||
{
|
||||
public class AssessmentController : Controller
|
||||
{
|
||||
private readonly ILogger<AssessmentController> _logger;
|
||||
|
||||
public AssessmentController(ILogger<AssessmentController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult AddAssessment(int resumeId, string comment, Dictionary<int, int> criteriaValues)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
var userId = APIClient.User?.Id;
|
||||
var allCriterions = APIClient.GetRequest<List<CriterionViewModel>>("api/criterion/list");
|
||||
if (allCriterions == null || !allCriterions.Any())
|
||||
{
|
||||
throw new Exception("Критерии не найдены");
|
||||
}
|
||||
|
||||
criteriaValues = criteriaValues.Where(kvp => kvp.Key != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
|
||||
var assessmentCriterions = criteriaValues
|
||||
.ToDictionary(
|
||||
kvp => kvp.Key,
|
||||
kvp =>
|
||||
{
|
||||
var criterionViewModel = allCriterions.FirstOrDefault(c => c.Id == kvp.Key);
|
||||
if (criterionViewModel == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Критерий с ID {kvp.Key} не найден.");
|
||||
}
|
||||
|
||||
var criterionModel = new CriterionViewModel
|
||||
{
|
||||
Id = criterionViewModel.Id,
|
||||
Name = criterionViewModel.Name
|
||||
};
|
||||
|
||||
return ((ICriterionModel)criterionModel, kvp.Value);
|
||||
}
|
||||
);
|
||||
|
||||
APIClient.PostRequest("api/assessment/create", new AssessmentBindingModel
|
||||
{
|
||||
ResumeId = resumeId,
|
||||
UserId = userId,
|
||||
Comment = comment,
|
||||
AssessmentCriterions = assessmentCriterions
|
||||
});
|
||||
|
||||
return Redirect($"~/Resume/ResumeDetails/{resumeId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error(string errorMessage, string returnUrl)
|
||||
{
|
||||
ViewBag.ErrorMessage = errorMessage ?? "Произошла непредвиденная ошибка.";
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewClientApp.Models;
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace CandidateReviewClientApp.Controllers
|
||||
{
|
||||
@ -39,27 +42,14 @@ namespace CandidateReviewClientApp.Controllers
|
||||
return View(new CompanyViewModel());
|
||||
}
|
||||
var model = APIClient.GetRequest<CompanyViewModel?>($"api/company/profile?id={id}");
|
||||
if (model != null)
|
||||
{
|
||||
APIClient.PostRequest($"api/user/update", new UserBindingModel {
|
||||
Id = APIClient.User.Id,
|
||||
CompanyId = model.Id,
|
||||
Surname = APIClient.User.Surname,
|
||||
Name = APIClient.User.Name,
|
||||
LastName = APIClient.User.LastName,
|
||||
Email = APIClient.User.Email,
|
||||
Password = APIClient.User.Password,
|
||||
EmailConfirmed = APIClient.User.EmailConfirmed,
|
||||
Role = APIClient.User.Role,
|
||||
AvatarFilePath = APIClient.User.AvatarFilePath,
|
||||
PhoneNumber = APIClient.User.PhoneNumber
|
||||
});
|
||||
}
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void EditCompanyProfile(CompanyBindingModel model)
|
||||
public async Task<IActionResult> EditCompanyProfile(CompanyBindingModel model)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
if (APIClient.User == null)
|
||||
{
|
||||
@ -71,9 +61,33 @@ namespace CandidateReviewClientApp.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
APIClient.PostRequest("api/company/create", model);
|
||||
var companyId = await APIClient.PostRequestAsync("api/company/create", model);
|
||||
APIClient.PostRequest("api/user/update", new UserBindingModel
|
||||
{
|
||||
Id = APIClient.User.Id,
|
||||
Surname = APIClient.User.Surname,
|
||||
Name = APIClient.User.Name,
|
||||
LastName = APIClient.User.LastName,
|
||||
CompanyId = companyId,
|
||||
Email = APIClient.User.Email,
|
||||
Password = APIClient.User.Password,
|
||||
EmailConfirmed = APIClient.User.EmailConfirmed,
|
||||
Role = APIClient.User.Role,
|
||||
AvatarFilePath = APIClient.User.AvatarFilePath,
|
||||
PhoneNumber = APIClient.User.PhoneNumber
|
||||
});
|
||||
APIClient.Company = APIClient.GetRequest<CompanyViewModel?>($"api/company/profile?id={companyId}");
|
||||
}
|
||||
if (APIClient.Company == null)
|
||||
{
|
||||
throw new Exception("Компания не определена");
|
||||
}
|
||||
return Redirect($"~/Company/CompanyProfile/{APIClient.Company.Id}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
Response.Redirect($"/Company/CompanyProfile/{model.Id}");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -87,5 +101,13 @@ namespace CandidateReviewClientApp.Controllers
|
||||
APIClient.PostRequest($"api/company/delete", new CompanyBindingModel { Id = id });
|
||||
Response.Redirect("/Home/Index");
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error(string errorMessage, string returnUrl)
|
||||
{
|
||||
ViewBag.ErrorMessage = errorMessage ?? "Произошла непредвиденная ошибка.";
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
79
CandidateReviewClientApp/Controllers/CriterionController.cs
Normal file
79
CandidateReviewClientApp/Controllers/CriterionController.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using CandidateReviewClientApp.Models;
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CandidateReviewClientApp.Controllers
|
||||
{
|
||||
public class CriterionController : Controller
|
||||
{
|
||||
private readonly ILogger<CriterionController> _logger;
|
||||
|
||||
public CriterionController(ILogger<CriterionController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddCriterion(string name)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new Exception("Название критерия не может быть пустым");
|
||||
}
|
||||
await APIClient.PostRequestAsynchron("api/criterion/create", new CriterionBindingModel
|
||||
{
|
||||
Name = name
|
||||
});
|
||||
|
||||
return Redirect("/Criterion/ManageCriterions");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> ManageCriterions()
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
var criterions = await APIClient.GetRequestAsync<List<CriterionViewModel>>("api/criterion/list");
|
||||
return View(criterions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
public IActionResult Delete(int id)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
APIClient.PostRequest($"api/criterion/delete", new CriterionBindingModel { Id = id });
|
||||
return Redirect("~/Criterion/ManageCriterions");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error(string errorMessage, string returnUrl)
|
||||
{
|
||||
ViewBag.ErrorMessage = errorMessage ?? "Произошла непредвиденная ошибка.";
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
@ -29,23 +29,31 @@ namespace CandidateReviewUserApp.Controllers
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Enter(string login, string password)
|
||||
public async Task<IActionResult> Enter(string login, string password)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(password))
|
||||
{
|
||||
throw new Exception("Ââåäèòå ëîãèí è ïàðîëü");
|
||||
}
|
||||
APIClient.User = APIClient.GetRequest<UserViewModel>($"api/user/login?login={login}&password={password}");
|
||||
APIClient.User = await APIClient.GetRequestAsync<UserViewModel>($"api/user/login?login={login}&password={password}");
|
||||
if (APIClient.User == null)
|
||||
{
|
||||
throw new Exception("Íåâåðíûé ëîãèí/ïàðîëü");
|
||||
}
|
||||
if (APIClient.User?.CompanyId != null)
|
||||
{
|
||||
APIClient.Company = APIClient.GetRequest<CompanyViewModel>($"api/company/profile?id={APIClient.User?.CompanyId}");
|
||||
APIClient.Company = await APIClient.GetRequestAsync<CompanyViewModel>($"api/company/profile?id={APIClient.User?.CompanyId}");
|
||||
}
|
||||
|
||||
Response.Redirect("/Home/Index");
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@ -55,7 +63,10 @@ namespace CandidateReviewUserApp.Controllers
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Register(string login, string password, string surname, string name, string lastname)
|
||||
public IActionResult Register(string login, string password, string surname, string name, string lastname)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(surname) || string.IsNullOrEmpty(name))
|
||||
{
|
||||
@ -68,11 +79,10 @@ namespace CandidateReviewUserApp.Controllers
|
||||
{
|
||||
role = RoleEnum.Àäìèíèñòðàòîð;
|
||||
}
|
||||
else if (login.Equals("t.artamonova73@icloud.com", StringComparison.OrdinalIgnoreCase))
|
||||
else
|
||||
{
|
||||
role = RoleEnum.Ïîëüçîâàòåëü;
|
||||
}
|
||||
|
||||
APIClient.PostRequest("api/user/register", new UserBindingModel
|
||||
{
|
||||
Surname = surname,
|
||||
@ -84,13 +94,19 @@ namespace CandidateReviewUserApp.Controllers
|
||||
Role = role
|
||||
});
|
||||
|
||||
Response.Redirect("/Home/Enter");
|
||||
return;
|
||||
return RedirectToAction("Enter");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
public IActionResult Error(string errorMessage, string returnUrl)
|
||||
{
|
||||
ViewBag.ErrorMessage = errorMessage ?? "Ïðîèçîøëà íåïðåäâèäåííàÿ îøèáêà.";
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
|
159
CandidateReviewClientApp/Controllers/ResumeController.cs
Normal file
159
CandidateReviewClientApp/Controllers/ResumeController.cs
Normal file
@ -0,0 +1,159 @@
|
||||
using CandidateReviewClientApp.Models;
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CandidateReviewClientApp.Controllers
|
||||
{
|
||||
public class ResumeController : Controller
|
||||
{
|
||||
private readonly ILogger<ResumeController> _logger;
|
||||
|
||||
public ResumeController(ILogger<ResumeController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> ResumeDetails(int? id)
|
||||
{
|
||||
if (APIClient.User == null)
|
||||
{
|
||||
return Redirect("~/Home/Enter");
|
||||
}
|
||||
|
||||
var resume = await APIClient.GetRequestAsync<ResumeViewModel?>($"api/resume/details?id={id}");
|
||||
if (resume == null || !id.HasValue)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
var criterions = APIClient.GetRequest<List<CriterionViewModel>>($"api/criterion/list");
|
||||
|
||||
ViewBag.Criterions = criterions;
|
||||
return View(resume);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> EditResume(int? id, int? vacancyId)
|
||||
{
|
||||
if (APIClient.User == null)
|
||||
{
|
||||
return RedirectToAction("Enter", "Home");
|
||||
}
|
||||
|
||||
ResumeViewModel model;
|
||||
if (id.HasValue)
|
||||
{
|
||||
model = await APIClient.GetRequestAsync<ResumeViewModel>($"api/resume/details?id={id}");
|
||||
if (model == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
model = new ResumeViewModel { UserId = APIClient.User.Id, VacancyId = vacancyId ?? 0 };
|
||||
}
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult EditResume(ResumeBindingModel model, bool isDraft)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
if (APIClient.User == null)
|
||||
{
|
||||
throw new Exception("Доступно только авторизованным пользователям");
|
||||
}
|
||||
if (model.Id != 0)
|
||||
{
|
||||
if (isDraft)
|
||||
{
|
||||
model.Status = CandidateReviewDataModels.Enums.ResumeStatusEnum.Черновик;
|
||||
}
|
||||
else
|
||||
{
|
||||
model.Status = CandidateReviewDataModels.Enums.ResumeStatusEnum.Обрабатывается;
|
||||
}
|
||||
APIClient.PostRequest("api/resume/update", model);
|
||||
}
|
||||
else
|
||||
{
|
||||
model.UserId = APIClient.User.Id;
|
||||
if (isDraft)
|
||||
{
|
||||
model.Status = CandidateReviewDataModels.Enums.ResumeStatusEnum.Черновик;
|
||||
}
|
||||
else
|
||||
{
|
||||
model.Status = CandidateReviewDataModels.Enums.ResumeStatusEnum.Обрабатывается;
|
||||
}
|
||||
var vacancy = APIClient.GetRequest<VacancyViewModel>($"api/vacancy/details?id={model.VacancyId}");
|
||||
var resume = APIClient.GetRequest<ResumeViewModel>($"api/resume/check?userId={model.UserId}&vacancyId={model.VacancyId}");
|
||||
if (resume == null)
|
||||
{
|
||||
APIClient.PostRequest("api/resume/create", model);
|
||||
if (APIClient.User != null)
|
||||
{
|
||||
APIClient.User?.Resumes.Add(new ResumeViewModel
|
||||
{
|
||||
Id = model.Id
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Пользователь не найден");
|
||||
}
|
||||
if (vacancy != null)
|
||||
{
|
||||
vacancy.Resumes.Add(new ResumeViewModel
|
||||
{
|
||||
Id = model.Id
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Вакансия не найдена");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Вы уже создавали резюме на эту вакансию!");
|
||||
}
|
||||
}
|
||||
return Redirect($"~/User/UserProfile/{model.UserId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
public IActionResult Delete(int id)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
APIClient.PostRequest($"api/resume/delete", new ResumeBindingModel { Id = id });
|
||||
return Redirect("~/User/UserProfile");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error(string errorMessage, string returnUrl)
|
||||
{
|
||||
ViewBag.ErrorMessage = errorMessage ?? "Произошла непредвиденная ошибка.";
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
@ -18,13 +18,11 @@ namespace CandidateReviewClientApp.Controllers
|
||||
[HttpGet]
|
||||
public IActionResult UserProfile(int? id)
|
||||
{
|
||||
var userId = id ?? APIClient.User?.Id;
|
||||
|
||||
var model = APIClient.GetRequest<UserViewModel>($"api/user/profile?id={userId}");
|
||||
var model = APIClient.GetRequest<UserViewModel>($"api/user/profile?id={id}");
|
||||
|
||||
if (model == null)
|
||||
{
|
||||
return RedirectToAction("/Home/Index");
|
||||
return Redirect("/Home/Index");
|
||||
}
|
||||
|
||||
return View(model);
|
||||
@ -60,7 +58,10 @@ namespace CandidateReviewClientApp.Controllers
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void UserProfileEdit(UserBindingModel model)
|
||||
public IActionResult UserProfileEdit(UserBindingModel model)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
if (model.Id != 0)
|
||||
{
|
||||
@ -77,7 +78,7 @@ namespace CandidateReviewClientApp.Controllers
|
||||
Surname = model.Surname,
|
||||
Name = model.Name,
|
||||
LastName = model.LastName,
|
||||
CompanyId = model.CompanyId,
|
||||
CompanyId = APIClient.Company.Id,
|
||||
Email = model.Email,
|
||||
Password = model.Password,
|
||||
EmailConfirmed = model.EmailConfirmed,
|
||||
@ -86,14 +87,20 @@ namespace CandidateReviewClientApp.Controllers
|
||||
PhoneNumber = model.PhoneNumber
|
||||
});
|
||||
}
|
||||
Response.Redirect($"/Company/CompanyProfile/{model.CompanyId}");
|
||||
return;
|
||||
return Redirect($"~/Company/CompanyProfile/{model.CompanyId}");
|
||||
}
|
||||
return Redirect($"/User/UserProfile/{model.Id}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
|
||||
Response.Redirect($"/User/UserProfile/{model.Id}");
|
||||
}
|
||||
|
||||
public IActionResult DeleteEmployee(int id)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
APIClient.PostRequest("api/user/delete", new UserBindingModel
|
||||
{
|
||||
@ -103,11 +110,17 @@ namespace CandidateReviewClientApp.Controllers
|
||||
|
||||
return Redirect($"~/Company/CompanyProfile");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public void Logout()
|
||||
{
|
||||
APIClient.User = null;
|
||||
APIClient.Company = null;
|
||||
Response.Redirect("/Home/Enter");
|
||||
}
|
||||
|
||||
@ -119,26 +132,15 @@ namespace CandidateReviewClientApp.Controllers
|
||||
throw new Exception("Доступно только авторизованным пользователям");
|
||||
}
|
||||
|
||||
APIClient.PostRequest($"api/user/delete", new UserBindingModel {
|
||||
Id = model.Id,
|
||||
Surname = model.Surname,
|
||||
Name = model.Name,
|
||||
LastName = model.LastName,
|
||||
CompanyId = model.CompanyId,
|
||||
Email = model.Email,
|
||||
Password = model.Password,
|
||||
EmailConfirmed = model.EmailConfirmed,
|
||||
Role = model.Role,
|
||||
AvatarFilePath = model.AvatarFilePath,
|
||||
PhoneNumber = model.PhoneNumber
|
||||
});
|
||||
|
||||
APIClient.PostRequest($"api/user/delete", model);
|
||||
Response.Redirect("/Home/Enter");
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
public IActionResult Error(string errorMessage, string returnUrl)
|
||||
{
|
||||
ViewBag.ErrorMessage = errorMessage ?? "Произошла непредвиденная ошибка.";
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewClientApp.Models;
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CandidateReviewClientApp.Controllers
|
||||
{
|
||||
@ -20,12 +21,13 @@ namespace CandidateReviewClientApp.Controllers
|
||||
{
|
||||
return Redirect("~/Home/Enter");
|
||||
}
|
||||
VacancyViewModel vacancy;
|
||||
if (id.HasValue)
|
||||
{
|
||||
APIClient.Vacancy = APIClient.GetRequest<VacancyViewModel?>($"api/vacancy/details?id={id}");
|
||||
vacancy = APIClient.GetRequest<VacancyViewModel?>($"api/vacancy/details?id={id}");
|
||||
return View(vacancy);
|
||||
}
|
||||
var model = APIClient.Vacancy;
|
||||
return View(model);
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@ -44,8 +46,10 @@ namespace CandidateReviewClientApp.Controllers
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost]
|
||||
public void EditVacancy(VacancyBindingModel model)
|
||||
public IActionResult EditVacancy(VacancyBindingModel model)
|
||||
{
|
||||
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
|
||||
try
|
||||
{
|
||||
if (APIClient.User == null)
|
||||
{
|
||||
@ -56,13 +60,13 @@ namespace CandidateReviewClientApp.Controllers
|
||||
{
|
||||
model.Tags = model.Tags.ToLowerInvariant();
|
||||
}
|
||||
|
||||
if (model.Id != 0)
|
||||
{
|
||||
APIClient.PostRequest("api/vacancy/update", model);
|
||||
}
|
||||
else
|
||||
{
|
||||
model.CompanyId = APIClient.Company.Id;
|
||||
APIClient.PostRequest("api/vacancy/create", model);
|
||||
if (APIClient.Company != null)
|
||||
{
|
||||
@ -87,7 +91,12 @@ namespace CandidateReviewClientApp.Controllers
|
||||
});
|
||||
}
|
||||
}
|
||||
Response.Redirect($"/Company/CompanyProfile/{model.CompanyId}");
|
||||
return Redirect($"~/Company/CompanyProfile/{model.CompanyId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return RedirectToAction("Error", new { errorMessage = $"{ex.Message}", returnUrl });
|
||||
}
|
||||
}
|
||||
|
||||
public IActionResult Delete(int id)
|
||||
@ -119,5 +128,13 @@ namespace CandidateReviewClientApp.Controllers
|
||||
var results = APIClient.GetRequest<List<VacancyViewModel?>>($"api/vacancy/search?tags={tags}");
|
||||
return View(results);
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error(string errorMessage, string returnUrl)
|
||||
{
|
||||
ViewBag.ErrorMessage = errorMessage ?? "Произошла непредвиденная ошибка.";
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ namespace CandidateReviewClientApp.Models
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string? RequestId { get; set; }
|
||||
|
||||
public string? ErrorMessage { get; set; }
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Профиль компании";
|
||||
var userRole = (APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Сотрудник || APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор) ? true : false;
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
@ -13,8 +12,9 @@
|
||||
<img src="@(Model.LogoFilePath ?? "https://static.thenounproject.com/png/2504969-200.png")" style="max-width: 150px; max-height: 150px;" class="card-img-top img-fluid rounded-circle mx-auto d-block" alt="Логотип компании">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">@Model.Name</h5>
|
||||
<input type="hidden" name="id" value="@Model?.Id" />
|
||||
<p class="card-text">@(Model.Description == null ? "Описание отсутствует" : Model.Description) </p>
|
||||
<a href="@(Model.Website ?? "#")" target="_blank" class="btn btn-primary mt-2">@(Model.Website != null ? "Официальный сайт" : "Веб-сайт отсутствует")</a> </a>
|
||||
<a href="@(Model.Website ?? "#")" target="_blank" class="btn btn-primary mt-2">@(Model.Website != null ? "Официальный сайт" : "Веб-сайт отсутствует") @(Model.Website != null ? "" : "disabled")</a> </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -22,10 +22,7 @@
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2>Информация о компании</h2>
|
||||
@if (userRole)
|
||||
{
|
||||
<a asp-action="EditCompanyProfile" asp-controller="Company" asp-route-id="@Model.Id" class="btn btn-primary">Редактировать</a>
|
||||
}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
@ -41,48 +38,20 @@
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2>Вакансии компании</h2>
|
||||
@if (userRole)
|
||||
{
|
||||
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-companyId="@Model.Id" class="btn btn-success">Добавить вакансию</a>
|
||||
}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (Model.Vacancies != null && Model.Vacancies.Any())
|
||||
{
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Название</th>
|
||||
<th>Тип занятости</th>
|
||||
<th>Статус</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var vacancy in Model.Vacancies)
|
||||
@await Html.PartialAsync("_VacanciesTable", Model.Vacancies.Take(5))
|
||||
@if (Model.Vacancies.Count() > 5)
|
||||
{
|
||||
<tr>
|
||||
<td>@vacancy.JobTitle</td>
|
||||
<td>@vacancy.JobType</td>
|
||||
<td>@vacancy.Status</td>
|
||||
<td>
|
||||
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-info" title="Просмотр">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
@if (userRole)
|
||||
{
|
||||
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-warning" title="Редактировать">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<a asp-action="Delete" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-danger" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить вакансию?');">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
<button id="showAllVacanciesBtn" class="btn btn-primary" onclick="toggleVacancies()">Показать все</button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="allVacancies" style="display:none;">
|
||||
@await Html.PartialAsync("_VacanciesTable", Model.Vacancies)
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -94,10 +63,7 @@
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2>Сотрудники компании</h2>
|
||||
@if (userRole)
|
||||
{
|
||||
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-companyId="@Model.Id" class="btn btn-success">Добавить сотрудника</a>
|
||||
}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (Model.Employees != null && Model.Employees.Any())
|
||||
@ -122,32 +88,17 @@
|
||||
<a asp-action="UserProfile" asp-controller="User" asp-route-id="@employee.Id" class="text-info" title="Просмотр">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
@if (userRole)
|
||||
{
|
||||
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-id="@employee.Id" class="text-warning" title="Редактировать">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<a asp-action="DeleteEmployee" asp-controller="User" asp-route-id="@employee.Id" class="text-danger" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить сотрудника?');">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
@* <nav>
|
||||
<ul class="pagination justify-content-center">
|
||||
@for (int i = 1; i <= pageCount; i++)
|
||||
{
|
||||
<li class="page-item @(i == pageNumber ? "active" : "")">
|
||||
<a class="page-link" asp-action="CompanyProfile" asp-controller="Company" asp-route-id="@Model.Id" asp-route-pageNumber="@i">
|
||||
@i
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav> *@
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -157,3 +108,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
function toggleVacancies() {
|
||||
var allVacancies = document.getElementById('allVacancies');
|
||||
var button = document.getElementById('showAllVacanciesBtn');
|
||||
|
||||
if (allVacancies.style.display === 'none') {
|
||||
allVacancies.style.display = 'block';
|
||||
button.innerText = 'Скрыть';
|
||||
} else {
|
||||
allVacancies.style.display = 'none';
|
||||
button.innerText = 'Показать все';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
@ -5,6 +5,18 @@
|
||||
var title = @Model.Id <= 0 ? "Создать профиль компании" : "Редактировать профиль компании";
|
||||
}
|
||||
|
||||
@if (!ViewData.ModelState.IsValid)
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<ul>
|
||||
@foreach (var error in ViewData.ModelState.Values.SelectMany(v => v.Errors))
|
||||
{
|
||||
<li>@error.ErrorMessage</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="container">
|
||||
<h1>@title</h1>
|
||||
<form method="post" class="row g-3 needs-validation" novalidate>
|
||||
@ -37,8 +49,12 @@
|
||||
<input type="text" class="form-control" id="contacts" name="contacts" value="@Model?.Contacts" placeholder="Введите контактную информацию">
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||||
@if (Model.Id > 0)
|
||||
{
|
||||
<a asp-controller="Company" asp-action="CompanyProfile" class="btn btn-secondary">Отмена</a>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -0,0 +1,73 @@
|
||||
@using CandidateReviewContracts.ViewModels
|
||||
@model List<CriterionViewModel>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Управление критериями";
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<h2>Управление критериями</h2>
|
||||
<button class="btn btn-secondary" onclick="window.history.back();">Назад</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Название критерия</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (Model != null && Model.Count > 0)
|
||||
{
|
||||
int index = 1;
|
||||
foreach (var criterion in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>@index</td>
|
||||
<td>@criterion.Name</td>
|
||||
<td>
|
||||
<a asp-action="Delete" asp-controller="Criterion" asp-route-id="@criterion.Id" class="btn btn-danger btn-sm" onclick="return confirm('Вы уверены, что хотите удалить критерий?');">Удалить</a>
|
||||
</td>
|
||||
</tr>
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">Нет доступных критериев.</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addCriterionModal">
|
||||
Добавить критерий
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="addCriterionModal" tabindex="-1" aria-labelledby="addCriterionModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="post" asp-controller="Criterion" asp-action="AddCriterion">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addCriterionModalLabel">Добавить критерий</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="criterionName" class="form-label">Название критерия</label>
|
||||
<input type="text" class="form-control" id="criterionName" name="name" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -3,6 +3,18 @@
|
||||
Layout = "_LoginLayout";
|
||||
}
|
||||
|
||||
@if (!ViewData.ModelState.IsValid)
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<ul>
|
||||
@foreach (var error in ViewData.ModelState.Values.SelectMany(v => v.Errors))
|
||||
{
|
||||
<li>@error.ErrorMessage</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
|
@ -3,6 +3,49 @@
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome</h1>
|
||||
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
<h1 class="display-4">Добро пожаловать в CandidateReview</h1>
|
||||
<p>Здесь вы можете:</p>
|
||||
|
||||
<div class="d-flex justify-content-center mt-4">
|
||||
<div class="box">
|
||||
<h3>Создать профиль своей компании</h3>
|
||||
<p>Здесь вы можете создать и настроить профиль вашей компании, указать основные данные. Профиль вашей компании будет виден кандидатам, что поможет вам привлечь их внимание к вакансиям, которые вы предлагаете.</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h3>Подобрать кандидатов на вакансию</h3>
|
||||
<p>Используйте наш удобный инструмент для подбора кандидатов на вакансию. Вы можете просмотреть параметры возможных кандидатов. Это упростит процесс поиска подходящих кандидатов для ваших вакансий и даст вам больше шансов найти именно того, кто вам нужен.</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h3>Найти подходящую вакансию</h3>
|
||||
<p>Если вы ищете работу, здесь вы можете просмотреть все доступные вакансии. Мы стремимся помочь вам найти именно то рабочее место, которое соответствует вашим ожиданиям и интересам.</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h3>Оценить кандидатов на вакансию</h3>
|
||||
<p>После просмотра резюме у вас есть возможность оценить кандидатов и оставить свои комментарии. Вы можете выставить оценки по различным критериям и добавить свои заметки. Это поможет в дальнейшем процессе выбора и предоставляет общую картину по каждому кандидату, что значительно упрощает принятие решения.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.d-flex {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.box {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
margin: 10px;
|
||||
padding: 20px;
|
||||
width: 200px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.box:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
</style>
|
||||
|
81
CandidateReviewClientApp/Views/Resume/EditResume.cshtml
Normal file
81
CandidateReviewClientApp/Views/Resume/EditResume.cshtml
Normal file
@ -0,0 +1,81 @@
|
||||
@using CandidateReviewContracts.ViewModels
|
||||
@using CandidateReviewDataModels.Enums
|
||||
@model ResumeViewModel
|
||||
|
||||
@{
|
||||
var title = Model.Id <= 0 ? "Создать резюме" : "Редактировать резюме";
|
||||
}
|
||||
|
||||
|
||||
<div class="container">
|
||||
<h1>@title</h1>
|
||||
<form method="post" class="row g-3 needs-validation" novalidate>
|
||||
<input type="hidden" name="id" value="@Model?.Id" />
|
||||
<input type="hidden" name="vacancyId" value="@Model?.VacancyId" />
|
||||
<input type="hidden" name="userId" value="@Model?.UserId" />
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="Title" class="form-label">Название резюме <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="JobTitle" name="Title" value="@Model?.Title" required placeholder="Введите название резюме">
|
||||
<div class="invalid-feedback">Пожалуйста, введите название резюме.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="experience" class="form-label">Опыт работы <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" id="experience" name="experience" rows="3" placeholder="Внесите ваш опыт работы">@Model?.Experience</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="Education" class="form-label">Образование <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" id="Education" name="Education" rows="3" placeholder="Внесите ваше образование">@Model?.Education</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="Skills" class="form-label">Навыки <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" id="skills" name="Skills" rows="3" placeholder="Перечислите ваши навыки">@Model?.Skills</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<input type="text" class="form-control" id="description" name="description" value="@Model?.Description" placeholder="Введите описание резюме">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="PhotoFilePath" class="form-label">Ваше фото для текущего резюме</label>
|
||||
<input type="file" class="form-control" id="PhotoFilePath" name="PhotoFilePath" accept=".jpg,.jpeg,.png" />
|
||||
<img id="avatarPreview" src="@Model?.PhotoFilePath" alt="Предварительный просмотр фото" style="max-width: 100px; max-height: 100px;" />
|
||||
<div class="invalid-feedback">Выберите файл изображения.</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
@if (Model.Status == ResumeStatusEnum.Черновик)
|
||||
{
|
||||
<button type="submit" class="btn btn-primary" name="isDraft" value="false">Сохранить и отправить на оценку</button>
|
||||
}
|
||||
@if (Model.Id <= 0)
|
||||
{
|
||||
<button type="submit" formmethod="post" class="btn btn-secondary" name="isDraft" value="true">Сохранить черновик</button>
|
||||
}
|
||||
<button class="btn btn-secondary" onclick="window.history.back();">Назад</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
'use strict'
|
||||
|
||||
var forms = document.querySelectorAll('.needs-validation')
|
||||
|
||||
Array.prototype.slice.call(forms)
|
||||
.forEach(function (form) {
|
||||
form.addEventListener('submit', function (event) {
|
||||
if (!form.checkValidity()) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
form.classList.add('was-validated')
|
||||
}, false)
|
||||
})
|
||||
})()
|
||||
</script>
|
166
CandidateReviewClientApp/Views/Resume/ResumeDetails.cshtml
Normal file
166
CandidateReviewClientApp/Views/Resume/ResumeDetails.cshtml
Normal file
@ -0,0 +1,166 @@
|
||||
@using CandidateReviewDataModels.Models
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using CandidateReviewContracts.ViewModels
|
||||
@model ResumeViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Резюме";
|
||||
bool userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Сотрудник || APIClient.User.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор;
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="d-flex justify-content-between mb-4">
|
||||
<div class="card-header">
|
||||
<h2>@Model.Title</h2>
|
||||
<img src="@(Model.PhotoFilePath ?? "https://cdn-icons-png.flaticon.com/512/18/18601.png")"
|
||||
style="max-width: 150px; max-height: 150px; object-fit: cover; "
|
||||
class="card-img-top img-fluid rounded-circle mx-auto d-block"
|
||||
alt="Аватар пользователя" />
|
||||
<input type="hidden" name="id" value="@Model?.Id" />
|
||||
</div>
|
||||
<button class="btn btn-secondary" onclick="window.history.back();">Назад</button>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-4">Вакансия:</dt>
|
||||
<dd class="col-sm-8 text-muted">@Model.VacancyName</dd>
|
||||
|
||||
<dt class="col-sm-4">Навыки:</dt>
|
||||
<dd class="col-sm-8 text-muted">@Model.Skills</dd>
|
||||
|
||||
<dt class="col-sm-4">Статус:</dt>
|
||||
<dd class="col-sm-8 text-muted">@Model.Status</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-4">Опыт работы:</dt>
|
||||
<dd class="col-sm-8 text-muted">@Model.Experience</dd>
|
||||
|
||||
<dt class="col-sm-4">Образование:</dt>
|
||||
<dd class="col-sm-8 text-muted">@Model.Education</dd>
|
||||
|
||||
<dt class="col-sm-4">Описание:</dt>
|
||||
<dd class="col-sm-8 text-muted">@Html.Raw(Model.Description)</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.Status == CandidateReviewDataModels.Enums.ResumeStatusEnum.Черновик)
|
||||
{
|
||||
<div class="mt-4 text-end">
|
||||
<a asp-action="EditResume" asp-controller="Resume" class="btn btn-primary">Отправить на оценку</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (userRole)
|
||||
{
|
||||
<h2 class="mb-4">Оценка резюме</h2>
|
||||
|
||||
<form method="post" asp-controller="Assessment" asp-action="AddAssessment">
|
||||
<div class="row" id="criteriaList">
|
||||
@if (ViewBag.Criterions != null)
|
||||
{
|
||||
<input type="hidden" name="ResumeId" value="@Model?.Id" />
|
||||
@foreach (var criterion in ViewBag.Criterions)
|
||||
{
|
||||
var previousAssessment = Model.PreviousAssessments?.FirstOrDefault(a => a.Id == criterion.Id);
|
||||
var isSelected = previousAssessment?.Id == criterion.Id ? "selected" : string.Empty;
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<select name="criterionIds[]" class="form-control" required>
|
||||
<option value="" disabled selected>Выберите критерий</option>
|
||||
<option value="@criterion.Id" isSelected>@criterion.Name</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<input type="number" class="form-control" name="criteriaValues[]" min="1" max="5"
|
||||
value="@previousAssessment?.AssessmentCriterions?.FirstOrDefault().Value" required />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>Нет доступных критериев.</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<label for="comment" class="form-label">Комментарий</label>
|
||||
<textarea id="comment" name="comment" class="form-control" rows="4" placeholder="Оставьте ваш комментарий..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary mt-3" id="addCriterionBtn">Оценить по другому критерию</button>
|
||||
|
||||
<button type="submit" class="btn btn-success mt-3">Сохранить</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-4">
|
||||
<a asp-action="ManageCriterions" asp-controller="Criterion" class="btn btn-outline-secondary">Управление критериями</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var criterions = @Html.Raw(Json.Serialize(ViewBag.Criterions));
|
||||
|
||||
document.getElementById('addCriterionBtn').addEventListener('click', function () {
|
||||
var criteriaList = document.getElementById('criteriaList');
|
||||
var newRow = document.createElement('div');
|
||||
newRow.classList.add('row', 'mb-3');
|
||||
|
||||
// Создаем выпадающий список для критерия
|
||||
var criterionSelect = document.createElement('div');
|
||||
criterionSelect.classList.add('col-md-6');
|
||||
var select = document.createElement('select');
|
||||
select.classList.add('form-control');
|
||||
select.name = 'criterionIds[]';
|
||||
select.required = true;
|
||||
|
||||
var defaultOption = document.createElement('option');
|
||||
defaultOption.value = "";
|
||||
defaultOption.textContent = "Выберите критерий";
|
||||
select.appendChild(defaultOption);
|
||||
|
||||
criterions.forEach(function (criterion) {
|
||||
var option = document.createElement('option');
|
||||
option.value = criterion.id;
|
||||
option.textContent = criterion.name;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
criterionSelect.appendChild(select);
|
||||
|
||||
// Создаем поле ввода для оценки
|
||||
var valueInput = document.createElement('div');
|
||||
valueInput.classList.add('col-md-6');
|
||||
var input = document.createElement('input');
|
||||
input.type = 'number';
|
||||
input.classList.add('form-control');
|
||||
input.name = 'criteriaValues[]';
|
||||
input.min = 1;
|
||||
input.max = 5;
|
||||
input.required = true; // Обязательное поле
|
||||
|
||||
valueInput.appendChild(input);
|
||||
|
||||
// Добавляем созданные элементы в новый ряд
|
||||
newRow.appendChild(criterionSelect);
|
||||
newRow.appendChild(valueInput);
|
||||
|
||||
// Добавляем новый ряд в список критериев
|
||||
criteriaList.appendChild(newRow);
|
||||
});
|
||||
});
|
||||
</script>
|
@ -1,25 +1,39 @@
|
||||
@model ErrorViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="errorModal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Ошибка</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>@ViewBag.ErrorMessage</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="window.location.href='@ViewBag.ReturnUrl'">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
var errorMessage = '@ViewBag.ErrorMessage';
|
||||
if (errorMessage) {
|
||||
$('#errorModal').modal('show');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -25,7 +25,7 @@
|
||||
@if (APIClient.User?.Role == RoleEnum.Сотрудник || APIClient.User?.Role == RoleEnum.Администратор)
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Company" asp-route-id="@(APIClient.Company?.Id)" asp-action="@(APIClient.User?.CompanyId == null ? "EditCompanyProfile" : "CompanyProfile")">Профиль компании</a>
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Company" asp-route-id="@(APIClient.Company?.Id)" asp-action="@(APIClient.Company == null ? "EditCompanyProfile" : "CompanyProfile")">Профиль компании</a>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item">
|
||||
@ -45,6 +45,7 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
|
||||
<footer class="border-top footer text-muted mt-5">
|
||||
<div class="container">
|
||||
© 2024 - Ревью кандидатов на вакансию
|
||||
|
33
CandidateReviewClientApp/Views/Shared/_VacanciesTable.cshtml
Normal file
33
CandidateReviewClientApp/Views/Shared/_VacanciesTable.cshtml
Normal file
@ -0,0 +1,33 @@
|
||||
@model IEnumerable<CandidateReviewContracts.ViewModels.VacancyViewModel>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Название</th>
|
||||
<th>Тип занятости</th>
|
||||
<th>Статус</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var vacancy in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>@vacancy.JobTitle</td>
|
||||
<td>@vacancy.JobType</td>
|
||||
<td>@vacancy.Status</td>
|
||||
<td>
|
||||
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-info" title="Просмотр">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-warning" title="Редактировать">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<a asp-action="Delete" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-danger" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить вакансию?');">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
@ -3,6 +3,7 @@
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Профиль пользователя";
|
||||
var userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Пользователь ? true : false;
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
@ -12,7 +13,7 @@
|
||||
<img src="@(Model.AvatarFilePath ?? "https://cdn-icons-png.flaticon.com/512/18/18601.png")" style="max-width: 150px; max-height: 150px;" class="card-img-top img-fluid rounded-circle mx-auto d-block" alt="Аватар пользователя">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title mb-0">
|
||||
@Model?.Name @(string.IsNullOrEmpty(@Model?.Surname) ? "" : @Model?.Surname) @(string.IsNullOrEmpty(@Model?.LastName) ? "" : @Model?.LastName)
|
||||
@(string.IsNullOrEmpty(@Model?.Surname) ? "" : @Model?.Surname) @Model?.Name @(string.IsNullOrEmpty(@Model?.LastName) ? "" : @Model?.LastName)
|
||||
</h5>
|
||||
<dl class="row mt-3">
|
||||
<dt class="col-sm-4">Email:</dt>
|
||||
@ -37,15 +38,56 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (userRole)
|
||||
{
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>Мои резюме</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (Model.Resumes != null && Model.Resumes.Any())
|
||||
{
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Название</th>
|
||||
<th>Вакансия</th>
|
||||
<th>Статус</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var resume in Model.Resumes)
|
||||
{
|
||||
<tr>
|
||||
<td>@resume.Title</td>
|
||||
<td>@resume.VacancyName</td>
|
||||
<td>@resume.Status</td>
|
||||
<td>
|
||||
<a asp-action="ResumeDetails" asp-controller="Resume" asp-route-id="@resume.Id" class="text-info" title="Просмотр">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
<a asp-action="EditResume" asp-controller="Resume" asp-route-id="@resume.Id" class="text-warning" title="Редактировать">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<a asp-action="Delete" asp-controller="Resume" asp-route-id="@resume.Id" class="text-danger" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить резюме?');">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>Нет резюме.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
@model UserViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Редактирование профиля";
|
||||
var title = @Model.Id <= 0 ? "Добавить сотрудника" : "Редактировать профиль";
|
||||
var userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор ? true : false;
|
||||
}
|
||||
|
||||
@ -12,10 +12,13 @@
|
||||
<h2 class="mb-4">Редактирование профиля</h2>
|
||||
<form method="post" class="needs-validation" novalidate>
|
||||
<input type="hidden" name="id" value="@Model?.Id" />
|
||||
<input type="hidden" name="password" value="@Model?.Password" />
|
||||
|
||||
@if (userRole)
|
||||
{
|
||||
<input type="hidden" name="companyId" value="@(APIClient.User?.CompanyId)" />
|
||||
<div class="mb-3">
|
||||
<label for="Surname" class="form-label">Фамилия <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="Surname" name="Surname" value="@Model?.Surname" required />
|
||||
<div class="invalid-feedback">Пожалуйста, введите фамилию.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="Name" class="form-label">Имя <span class="text-danger">*</span></label>
|
||||
@ -24,36 +27,25 @@
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="Surname" class="form-label">Фамилия <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="Surname" name="Surname" value="@Model?.Surname" required />
|
||||
<div class="invalid-feedback">Пожалуйста, введите фамилию.</div>
|
||||
<label for="LastName" class="form-label">Отчество</label>
|
||||
<input type="text" class="form-control" id="LastName" name="LastName" value="@Model?.LastName" />
|
||||
<div class="invalid-feedback">Пожалуйста, введите отчество.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="Email" class="form-label">Электронная почта <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="Email" name="Email" value="@Model?.Email" />
|
||||
<input type="text" class="form-control" id="Email" name="Email" value="@Model?.Email" readonly/>
|
||||
<div class="invalid-feedback">Пожалуйста, введите электронную почту.</div>
|
||||
</div>
|
||||
|
||||
@if (Model.Id <= 0)
|
||||
{
|
||||
<div class="mb-3">
|
||||
<label for="Password" class="form-label">Пароль <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="Password" name="Password" value="@Model?.Password" />
|
||||
<div class="invalid-feedback">Пожалуйста, введите пароль.</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||||
<a asp-controller="Company" asp-action="CompanyProfile" class="btn btn-secondary">Отмена</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="hidden" name="Email" value="@Model?.Email" />
|
||||
<input type="hidden" name="Password" value="@Model?.Password" />
|
||||
<div class="mb-3">
|
||||
<label for="LastName" class="form-label">Отчество</label>
|
||||
<input type="text" class="form-control" id="LastName" name="LastName" value="@Model?.LastName" />
|
||||
<div class="invalid-feedback">Пожалуйста, введите отчество.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="PhoneNumber" class="form-label">Телефон</label>
|
||||
@ -71,9 +63,8 @@
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||||
<a asp-controller="User" asp-action="UserProfile" class="btn btn-secondary">Отмена</a>
|
||||
<button class="btn btn-secondary" onclick="window.history.back();">Назад</button>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@
|
||||
<h1>@title</h1>
|
||||
<form method="post" class="row g-3 needs-validation" novalidate>
|
||||
<input type="hidden" name="id" value="@Model?.Id" />
|
||||
<input type="hidden" name="companyId" value="@(APIClient.User?.CompanyId)" />
|
||||
<input type="hidden" name="CompanyId" value="@Model?.CompanyId" />
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="JobTitle" class="form-label">Название должности <span class="text-danger">*</span></label>
|
||||
@ -28,6 +28,11 @@
|
||||
<textarea class="form-control" id="Responsibilities" name="Responsibilities" rows="3" placeholder="Введите обязанности">@Model?.Responsibilities</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="3" placeholder="Введите описание вакансии">@Model?.Description</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label asp-for="JobType" class="form-label">Тип занятости</label>
|
||||
<select asp-for="JobType" class="form-control" asp-items="@GetJobTypeSelectList()"></select>
|
||||
@ -38,11 +43,6 @@
|
||||
<input type="text" class="form-control" id="salary" name="salary" value="@Model?.Salary" placeholder="Введите заработную плату или поставьте '-'">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<input type="text" class="form-control" id="description" name="description" value="@Model?.Description" placeholder="Введите описание вакансии">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label asp-for="Status" class="form-label">Статус вакансии</label>
|
||||
<select asp-for="Status" class="form-control" asp-items="@GetStatusSelectList()"></select>
|
||||
@ -52,8 +52,9 @@
|
||||
<label for="tags" class="form-label">Тэги</label>
|
||||
<input type="text" class="form-control" id="tags" name="tags" value="@Model?.Tags" placeholder="Введите тэги через пробел">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-primary">Сохранить</button>
|
||||
<button class="btn btn-secondary" onclick="window.history.back();">Назад</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -14,10 +14,6 @@
|
||||
<input type="text" class="form-control" name="tags" id="tags" placeholder="Введите поисковый запрос">
|
||||
<button class="btn btn-primary" type="submit">Поиск</button>
|
||||
</div>
|
||||
@if (ViewBag.Message != null)
|
||||
{
|
||||
<p class="alert alert-warning">@ViewBag.Message</p>
|
||||
}
|
||||
</form>
|
||||
|
||||
@if (Model != null)
|
||||
@ -32,6 +28,7 @@
|
||||
<th>Тип работы</th>
|
||||
<th>Зарплата</th>
|
||||
<th>Тэги</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -42,6 +39,10 @@
|
||||
<td>@vacancy.JobType</td>
|
||||
<td>@vacancy.Salary</td>
|
||||
<td>@vacancy.Tags</td>
|
||||
<td>
|
||||
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="btn btn-primary">Просмотр</a>
|
||||
<a asp-controller="Resume" asp-action="EditResume" asp-route-vacancyId="@vacancy.Id" class="btn btn-primary">Составить резюме</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
|
@ -4,25 +4,26 @@
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Информация о вакансии";
|
||||
var companyName = APIClient.Company?.Name ?? "Компания не определена";
|
||||
bool userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Сотрудник || APIClient.User.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор;
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<div class="d-flex justify-content-between mb-4">
|
||||
<div class="card-header">
|
||||
<h2>@Model.JobTitle</h2>
|
||||
<input type="hidden" name="id" value="@Model?.Id" />
|
||||
</div>
|
||||
<a asp-action="CompanyProfile" asp-controller="Company" class="btn btn-secondary">Назад</a>
|
||||
<button class="btn btn-secondary" onclick="window.history.back();">
|
||||
<i class="bi bi-arrow-left"></i> Назад
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-4">Компания:</dt>
|
||||
<dd class="col-sm-8">@companyName</dd>
|
||||
<dd class="col-sm-8">@Model.CompanyName</dd>
|
||||
|
||||
<dt class="col-sm-4">Тип занятости:</dt>
|
||||
<dd class="col-sm-8">@Model.JobType</dd>
|
||||
@ -55,4 +56,51 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (userRole)
|
||||
{
|
||||
<div class="col-md-12">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header">
|
||||
<h3>Резюме на вакансию</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (Model.Resumes != null && Model.Resumes.Any())
|
||||
{
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Название</th>
|
||||
<th>Кандидат</th>
|
||||
<th>Статус</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var resume in Model.Resumes)
|
||||
{
|
||||
<tr>
|
||||
<td>@resume.Title</td>
|
||||
<td>@resume.UserName</td>
|
||||
<td>@resume.Status</td>
|
||||
<td>
|
||||
<a asp-action="ResumeDetails" asp-controller="Resume" asp-route-id="@resume.Id" class="btn btn-info btn-sm">
|
||||
<i class="bi bi-eye"></i> Посмотреть и оценить
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>Нет резюме для данной вакансии.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.min.js"></script>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
@ -4,17 +4,14 @@ namespace CandidateReviewContracts.BindingModels
|
||||
{
|
||||
public class AssessmentBindingModel : IAssessmentModel
|
||||
{
|
||||
public int ResumeId { get; set; }
|
||||
|
||||
public int UserId { get; set; }
|
||||
|
||||
public int? Rating { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.Now;
|
||||
public int? UserId { get; set; }
|
||||
|
||||
public string? Comment { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
public Dictionary<int, (ICriterionModel, int)> AssessmentCriterions { get; set; } = new();
|
||||
|
||||
public int? ResumeId { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using CandidateReviewDataModels.Enums;
|
||||
using CandidateReviewDataModels.Models;
|
||||
using CandidateReviewDataModels.Models;
|
||||
|
||||
namespace CandidateReviewContracts.BindingModels
|
||||
{
|
||||
@ -7,12 +6,6 @@ namespace CandidateReviewContracts.BindingModels
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public CriterionTypeEnum Type { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public int Weight { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace CandidateReviewContracts.BusinessLogicsContracts
|
||||
{
|
||||
List<CompanyViewModel>? ReadList(CompanySearchModel? model);
|
||||
CompanyViewModel? ReadElement(CompanySearchModel model);
|
||||
int Create(CompanyBindingModel model);
|
||||
int? Create(CompanyBindingModel model);
|
||||
bool Update(CompanyBindingModel model);
|
||||
bool Delete(CompanyBindingModel model);
|
||||
}
|
||||
|
@ -2,10 +2,11 @@
|
||||
{
|
||||
public class AssessmentSearchModel
|
||||
{
|
||||
public int? ResumeId { get; set; }
|
||||
|
||||
public int? UserId { get; set; }
|
||||
|
||||
public int? ResumeId { get; set; }
|
||||
|
||||
public int? Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
namespace CandidateReviewContracts.SearchModels
|
||||
using CandidateReviewDataModels.Enums;
|
||||
|
||||
namespace CandidateReviewContracts.SearchModels
|
||||
{
|
||||
public class VacancySearchModel
|
||||
{
|
||||
@ -11,5 +13,7 @@
|
||||
public string? Tags { get; set; }
|
||||
|
||||
public int? Id { get; set; }
|
||||
|
||||
public VacancyStatusEnum? Status { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace CandidateReviewContracts.StoragesContracts
|
||||
List<CompanyViewModel> GetFullList();
|
||||
List<CompanyViewModel> GetFilteredList(CompanySearchModel model);
|
||||
CompanyViewModel? GetElement(CompanySearchModel model);
|
||||
CompanyViewModel? Insert(CompanyBindingModel model);
|
||||
int? Insert(CompanyBindingModel model);
|
||||
CompanyViewModel? Update(CompanyBindingModel model);
|
||||
CompanyViewModel? Delete(CompanyBindingModel model);
|
||||
}
|
||||
|
@ -4,16 +4,14 @@ namespace CandidateReviewContracts.ViewModels
|
||||
{
|
||||
public class AssessmentViewModel : IAssessmentModel
|
||||
{
|
||||
public int ResumeId { get; set; }
|
||||
|
||||
public int UserId { get; set; }
|
||||
|
||||
public int? Rating { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public int? UserId { get; set; }
|
||||
|
||||
public string? Comment { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
public int? ResumeId { get; set; }
|
||||
|
||||
public Dictionary<int, (ICriterionModel, int)> AssessmentCriterions { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using CandidateReviewDataModels.Enums;
|
||||
using CandidateReviewDataModels.Models;
|
||||
using CandidateReviewDataModels.Models;
|
||||
|
||||
namespace CandidateReviewContracts.ViewModels
|
||||
{
|
||||
@ -7,12 +6,6 @@ namespace CandidateReviewContracts.ViewModels
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public CriterionTypeEnum Type { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public int Weight { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,12 @@ namespace CandidateReviewContracts.ViewModels
|
||||
{
|
||||
public int VacancyId { get; set; }
|
||||
|
||||
public string VacancyName { get; set; } = string.Empty;
|
||||
|
||||
public int UserId { get; set; }
|
||||
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
public string Experience { get; set; } = string.Empty;
|
||||
@ -24,5 +28,6 @@ namespace CandidateReviewContracts.ViewModels
|
||||
public ResumeStatusEnum Status { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
public List<AssessmentViewModel> PreviousAssessments { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
@ -26,5 +26,7 @@ namespace CandidateReviewContracts.ViewModels
|
||||
public RoleEnum Role { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
public List<ResumeViewModel> Resumes { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ namespace CandidateReviewContracts.ViewModels
|
||||
{
|
||||
public int CompanyId { get; set; }
|
||||
|
||||
public string CompanyName { get; set; } = string.Empty;
|
||||
|
||||
public string JobTitle { get; set; } = string.Empty;
|
||||
|
||||
public string Requirements { get; set; } = string.Empty;
|
||||
@ -26,5 +28,7 @@ namespace CandidateReviewContracts.ViewModels
|
||||
public string? Tags { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
public List<ResumeViewModel> Resumes { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
namespace CandidateReviewDataModels.Enums
|
||||
{
|
||||
public enum CriterionTypeEnum
|
||||
{
|
||||
Опыт = 0,
|
||||
Навыки = 1,
|
||||
Образование = 2,
|
||||
Впечатление = 3
|
||||
}
|
||||
}
|
@ -2,17 +2,15 @@
|
||||
{
|
||||
public enum JobTypeEnum
|
||||
{
|
||||
РаботаВОфисе = 0,
|
||||
УдаленнаяРабота = 1,
|
||||
Офис = 0,
|
||||
Удаленно = 1,
|
||||
Гибрид = 2,
|
||||
Фриланс = 3,
|
||||
Подработка = 4,
|
||||
Сменная = 5,
|
||||
Контракт = 6,
|
||||
ПолныйРабочийДень = 7,
|
||||
НеполныйРабочийДень = 8,
|
||||
Проектная = 9,
|
||||
Сезонная = 10,
|
||||
Волонтерская = 11
|
||||
Проектная = 7,
|
||||
Сезонная = 8,
|
||||
Волонтерская = 9
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,10 @@
|
||||
{
|
||||
public enum ResumeStatusEnum
|
||||
{
|
||||
Создано = 0,
|
||||
Отправлено = 1,
|
||||
Обрабатывается = 2,
|
||||
Принято = 3,
|
||||
Отклонено = 4,
|
||||
Архив = 5
|
||||
Черновик = 0,
|
||||
Обрабатывается = 1,
|
||||
Принято = 2,
|
||||
Отклонено = 3,
|
||||
Архив = 4
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,8 @@
|
||||
{
|
||||
public interface IAssessmentModel : IId
|
||||
{
|
||||
int ResumeId { get; }
|
||||
int UserId { get; }
|
||||
int? Rating { get; }
|
||||
DateTime CreatedAt { get; }
|
||||
int? UserId { get; }
|
||||
int? ResumeId { get; }
|
||||
string? Comment { get; }
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,7 @@
|
||||
using CandidateReviewDataModels.Enums;
|
||||
|
||||
namespace CandidateReviewDataModels.Models
|
||||
namespace CandidateReviewDataModels.Models
|
||||
{
|
||||
public interface ICriterionModel : IId
|
||||
{
|
||||
string Name { get; }
|
||||
CriterionTypeEnum Type { get; }
|
||||
string? Description { get; }
|
||||
int Weight { get; }
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
public CompanyViewModel? Delete(CompanyBindingModel model)
|
||||
{
|
||||
using var context = new CandidateReviewDatabase();
|
||||
var element = context.Companies.Include(x => x.Users).Include(x => x.Vacancies).FirstOrDefault(rec => rec.Id == model.Id);
|
||||
var element = context.Companies.FirstOrDefault(rec => rec.Id == model.Id);
|
||||
if (element != null)
|
||||
{
|
||||
context.Companies.Remove(element);
|
||||
@ -30,8 +30,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
}
|
||||
using var context = new CandidateReviewDatabase();
|
||||
return context.Companies
|
||||
.Include(x => x.Users)
|
||||
.Include(x => x.Vacancies)
|
||||
.FirstOrDefault(x => (!string.IsNullOrEmpty(model.Name) && x.Name == model.Name) || (model.Id.HasValue && x.Id == model.Id))?
|
||||
.GetViewModel;
|
||||
}
|
||||
@ -44,8 +42,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
}
|
||||
using var context = new CandidateReviewDatabase();
|
||||
return context.Companies
|
||||
.Include(x => x.Users)
|
||||
.Include(x => x.Vacancies)
|
||||
.Where(x => x.Name.Equals(model.Name))
|
||||
.Select(x => x.GetViewModel)
|
||||
.ToList();
|
||||
@ -55,13 +51,11 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
{
|
||||
using var context = new CandidateReviewDatabase();
|
||||
return context.Companies
|
||||
.Include(x => x.Users)
|
||||
.Include(x => x.Vacancies)
|
||||
.Select(x => x.GetViewModel)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public CompanyViewModel? Insert(CompanyBindingModel model)
|
||||
public int? Insert(CompanyBindingModel model)
|
||||
{
|
||||
var newCompany = Company.Create(model);
|
||||
if (newCompany == null)
|
||||
@ -71,19 +65,13 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
using var context = new CandidateReviewDatabase();
|
||||
context.Companies.Add(newCompany);
|
||||
context.SaveChanges();
|
||||
return context.Companies
|
||||
.Include(x => x.Users)
|
||||
.Include(x => x.Vacancies)
|
||||
.FirstOrDefault(x => x.Id == model.Id)?
|
||||
.GetViewModel;
|
||||
return newCompany.Id;
|
||||
}
|
||||
|
||||
public CompanyViewModel? Update(CompanyBindingModel model)
|
||||
{
|
||||
using var context = new CandidateReviewDatabase();
|
||||
var company = context.Companies
|
||||
.Include(x => x.Users)
|
||||
.Include(x => x.Vacancies)
|
||||
.FirstOrDefault(x => x.Id == model.Id);
|
||||
if (company == null)
|
||||
{
|
||||
|
@ -16,7 +16,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
var element = context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.FirstOrDefault(rec => rec.Id == model.Id);
|
||||
|
||||
if (element != null)
|
||||
@ -32,17 +31,21 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
|
||||
public ResumeViewModel? GetElement(ResumeSearchModel model)
|
||||
{
|
||||
if (!model.Id.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
using var context = new CandidateReviewDatabase();
|
||||
if (model.VacancyId.HasValue && model.UserId.HasValue)
|
||||
{
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.FirstOrDefault(x => x.VacancyId == model.VacancyId && x.UserId == model.UserId)
|
||||
?.GetViewModel;
|
||||
}
|
||||
|
||||
if (model.VacancyId.HasValue)
|
||||
{
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.FirstOrDefault(x => x.VacancyId == model.VacancyId)
|
||||
?.GetViewModel;
|
||||
}
|
||||
@ -52,17 +55,19 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.FirstOrDefault(x => x.UserId == model.UserId)
|
||||
?.GetViewModel;
|
||||
}
|
||||
if (!model.Id.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(model.Title))
|
||||
{
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.FirstOrDefault(x => x.Title == model.Title)
|
||||
?.GetViewModel;
|
||||
}
|
||||
@ -70,7 +75,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.FirstOrDefault(x => model.Id.HasValue && x.Id == model.Id)
|
||||
?.GetViewModel;
|
||||
}
|
||||
@ -87,7 +91,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.Where(x => x.VacancyId == model.VacancyId)
|
||||
.ToList()
|
||||
.Select(x => x.GetViewModel)
|
||||
@ -99,7 +102,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.Where(x => x.UserId == model.UserId)
|
||||
.ToList()
|
||||
.Select(x => x.GetViewModel)
|
||||
@ -111,7 +113,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.Where(x => x.Title == model.Title)
|
||||
.ToList()
|
||||
.Select(x => x.GetViewModel)
|
||||
@ -120,7 +121,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.ToList()
|
||||
.Select(x => x.GetViewModel)
|
||||
.ToList();
|
||||
@ -133,7 +133,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.Select(x => x.GetViewModel).ToList();
|
||||
}
|
||||
|
||||
@ -154,7 +153,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
return context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.FirstOrDefault(x => x.Id == newResume.Id)
|
||||
?.GetViewModel;
|
||||
}
|
||||
@ -166,7 +164,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
var resume = context.Resumes
|
||||
.Include(x => x.Vacancy)
|
||||
.Include(x => x.User)
|
||||
.Include(x => x.Assessment)
|
||||
.FirstOrDefault(x => x.Id == model.Id);
|
||||
|
||||
if (resume == null)
|
||||
|
@ -13,7 +13,7 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
public UserViewModel? Delete(UserBindingModel model)
|
||||
{
|
||||
using var context = new CandidateReviewDatabase();
|
||||
var element = context.Users.Include(x => x.Company).Include(x => x.Resumes).Include(x => x.Assessments).FirstOrDefault(rec => rec.Id == model.Id);
|
||||
var element = context.Users.Include(x => x.Company).FirstOrDefault(rec => rec.Id == model.Id);
|
||||
if (element != null)
|
||||
{
|
||||
context.Users.Remove(element);
|
||||
@ -32,8 +32,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
using var context = new CandidateReviewDatabase();
|
||||
return context.Users
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.Include(x => x.Assessments)
|
||||
.FirstOrDefault(x => (!string.IsNullOrEmpty(model.Email) && x.Email == model.Email) || (model.Id.HasValue && x.Id == model.Id))?
|
||||
.GetViewModel;
|
||||
}
|
||||
@ -45,9 +43,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
if (model.CompanyId.HasValue && model.Role != null)
|
||||
{
|
||||
return context.Users
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.Include(x => x.Assessments)
|
||||
.Where(x => x.CompanyId.Equals(model.CompanyId) && x.Role.Equals(model.Role))
|
||||
.Select(x => x.GetViewModel)
|
||||
.ToList();
|
||||
@ -59,8 +54,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
|
||||
return context.Users
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.Include(x => x.Assessments)
|
||||
.Where(x => x.Email.Equals(model.Email) && x.Password.Equals(model.Password))
|
||||
.Select(x => x.GetViewModel)
|
||||
.ToList();
|
||||
@ -71,8 +64,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
using var context = new CandidateReviewDatabase();
|
||||
return context.Users
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.Include(x => x.Assessments)
|
||||
.ToList()
|
||||
.Select(x => x.GetViewModel)
|
||||
.ToList();
|
||||
@ -90,8 +81,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
context.SaveChanges();
|
||||
return context.Users
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.Include(x => x.Assessments)
|
||||
.FirstOrDefault(x => x.Id == newUser.Id)?
|
||||
.GetViewModel;
|
||||
}
|
||||
@ -101,8 +90,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
using var context = new CandidateReviewDatabase();
|
||||
var user = context.Users
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.Include(x => x.Assessments)
|
||||
.FirstOrDefault(x => x.Id == model.Id);
|
||||
|
||||
if (user == null)
|
||||
|
@ -15,7 +15,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
|
||||
var element = context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.FirstOrDefault(rec => rec.Id == model.Id);
|
||||
|
||||
if (element != null)
|
||||
@ -40,7 +39,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
{
|
||||
return context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.FirstOrDefault(x => x.CompanyId == model.CompanyId && x.JobTitle.Equals(model.JobTitle))
|
||||
?.GetViewModel;
|
||||
}
|
||||
@ -48,13 +46,11 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
{
|
||||
return context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.FirstOrDefault(x => x.Tags.Contains(model.Tags))
|
||||
?.GetViewModel;
|
||||
}
|
||||
return context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.FirstOrDefault(x => model.Id.HasValue && x.Id == model.Id)
|
||||
?.GetViewModel;
|
||||
}
|
||||
@ -70,26 +66,23 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
{
|
||||
return context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.Where(x => x.CompanyId == model.CompanyId && x.JobTitle.Equals(model.JobTitle))
|
||||
.ToList()
|
||||
.Select(x => x.GetViewModel)
|
||||
.ToList();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(model.Tags))
|
||||
if (!string.IsNullOrEmpty(model.Tags) && model.Status.HasValue)
|
||||
{
|
||||
var tags = model.Tags.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(t => t.ToLowerInvariant()).ToArray();
|
||||
return context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.Where(x => tags.Any(tag => x.Tags.Contains(tag)))
|
||||
.Where(x => tags.Any(tag => x.Tags.Contains(tag)) && x.Status == model.Status)
|
||||
.ToList()
|
||||
.Select(x => x.GetViewModel)
|
||||
.ToList();
|
||||
}
|
||||
return context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.ToList()
|
||||
.Select(x => x.GetViewModel)
|
||||
.ToList();
|
||||
@ -101,7 +94,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
|
||||
return context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.Select(x => x.GetViewModel).ToList();
|
||||
}
|
||||
|
||||
@ -121,7 +113,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
|
||||
return context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.FirstOrDefault(x => x.Id == newVacancy.Id)
|
||||
?.GetViewModel;
|
||||
}
|
||||
@ -132,7 +123,6 @@ namespace CandidateReviewDatabaseImplement.Implements
|
||||
|
||||
var vacancy = context.Vacancies
|
||||
.Include(x => x.Company)
|
||||
.Include(x => x.Resumes)
|
||||
.FirstOrDefault(x => x.Id == model.Id);
|
||||
|
||||
if (vacancy == null)
|
||||
|
@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace CandidateReviewDatabaseImplement.Migrations
|
||||
{
|
||||
[DbContext(typeof(CandidateReviewDatabase))]
|
||||
[Migration("20241105152221_InitialCreate")]
|
||||
[Migration("20241203082827_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@ -36,20 +36,16 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int?>("Rating")
|
||||
b.Property<int?>("ResumeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ResumeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
b.Property<int?>("UserId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ResumeId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Assessments");
|
||||
@ -121,19 +117,10 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Weight")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Criterions");
|
||||
@ -161,9 +148,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
b.Property<string>("PhotoFilePath")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("ResumeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Skills")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
@ -183,9 +167,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ResumeId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("VacancyId");
|
||||
@ -292,11 +273,15 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Assessment", b =>
|
||||
{
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Resume", "Resume")
|
||||
.WithMany()
|
||||
.HasForeignKey("ResumeId");
|
||||
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.User", "User")
|
||||
.WithMany("Assessments")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("Resume");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
@ -322,26 +307,18 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Resume", b =>
|
||||
{
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Assessment", "Assessment")
|
||||
.WithOne("Resume")
|
||||
.HasForeignKey("CandidateReviewDatabaseImplement.Models.Resume", "ResumeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.User", "User")
|
||||
.WithMany("Resumes")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Vacancy", "Vacancy")
|
||||
.WithMany("Resumes")
|
||||
.WithMany()
|
||||
.HasForeignKey("VacancyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Assessment");
|
||||
|
||||
b.Navigation("User");
|
||||
|
||||
b.Navigation("Vacancy");
|
||||
@ -350,7 +327,7 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.User", b =>
|
||||
{
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Company", "Company")
|
||||
.WithMany("Users")
|
||||
.WithMany()
|
||||
.HasForeignKey("CompanyId");
|
||||
|
||||
b.Navigation("Company");
|
||||
@ -359,7 +336,7 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Vacancy", b =>
|
||||
{
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Company", "Company")
|
||||
.WithMany("Vacancies")
|
||||
.WithMany()
|
||||
.HasForeignKey("CompanyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
@ -370,34 +347,12 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Assessment", b =>
|
||||
{
|
||||
b.Navigation("Criterions");
|
||||
|
||||
b.Navigation("Resume")
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Company", b =>
|
||||
{
|
||||
b.Navigation("Users");
|
||||
|
||||
b.Navigation("Vacancies");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Criterion", b =>
|
||||
{
|
||||
b.Navigation("AssessmentCriterions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Assessments");
|
||||
|
||||
b.Navigation("Resumes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Vacancy", b =>
|
||||
{
|
||||
b.Navigation("Resumes");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
@ -36,10 +36,7 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Type = table.Column<int>(type: "integer", nullable: false),
|
||||
Description = table.Column<string>(type: "text", nullable: true),
|
||||
Weight = table.Column<int>(type: "integer", nullable: false)
|
||||
Name = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
@ -101,27 +98,62 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Resumes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
VacancyId = table.Column<int>(type: "integer", nullable: false),
|
||||
UserId = table.Column<int>(type: "integer", nullable: false),
|
||||
Title = table.Column<string>(type: "text", nullable: false),
|
||||
Experience = table.Column<string>(type: "text", nullable: false),
|
||||
Education = table.Column<string>(type: "text", nullable: false),
|
||||
PhotoFilePath = table.Column<string>(type: "text", nullable: true),
|
||||
Description = table.Column<string>(type: "text", nullable: true),
|
||||
Skills = table.Column<string>(type: "text", nullable: false),
|
||||
Status = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Resumes", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Resumes_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Resumes_Vacancies_VacancyId",
|
||||
column: x => x.VacancyId,
|
||||
principalTable: "Vacancies",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Assessments",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
ResumeId = table.Column<int>(type: "integer", nullable: false),
|
||||
UserId = table.Column<int>(type: "integer", nullable: false),
|
||||
Rating = table.Column<int>(type: "integer", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UserId = table.Column<int>(type: "integer", nullable: true),
|
||||
ResumeId = table.Column<int>(type: "integer", nullable: true),
|
||||
Comment = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Assessments", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Assessments_Resumes_ResumeId",
|
||||
column: x => x.ResumeId,
|
||||
principalTable: "Resumes",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_Assessments_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
@ -151,46 +183,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Resumes",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
VacancyId = table.Column<int>(type: "integer", nullable: false),
|
||||
UserId = table.Column<int>(type: "integer", nullable: false),
|
||||
Title = table.Column<string>(type: "text", nullable: false),
|
||||
Experience = table.Column<string>(type: "text", nullable: false),
|
||||
Education = table.Column<string>(type: "text", nullable: false),
|
||||
PhotoFilePath = table.Column<string>(type: "text", nullable: true),
|
||||
Description = table.Column<string>(type: "text", nullable: true),
|
||||
Skills = table.Column<string>(type: "text", nullable: false),
|
||||
Status = table.Column<int>(type: "integer", nullable: false),
|
||||
ResumeId = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Resumes", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Resumes_Assessments_ResumeId",
|
||||
column: x => x.ResumeId,
|
||||
principalTable: "Assessments",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Resumes_Users_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Resumes_Vacancies_VacancyId",
|
||||
column: x => x.VacancyId,
|
||||
principalTable: "Vacancies",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AssessmentCriterions_AssessmentId",
|
||||
table: "AssessmentCriterions",
|
||||
@ -201,17 +193,16 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
table: "AssessmentCriterions",
|
||||
column: "CriterionId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Assessments_ResumeId",
|
||||
table: "Assessments",
|
||||
column: "ResumeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Assessments_UserId",
|
||||
table: "Assessments",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Resumes_ResumeId",
|
||||
table: "Resumes",
|
||||
column: "ResumeId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Resumes_UserId",
|
||||
table: "Resumes",
|
||||
@ -240,20 +231,20 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
name: "AssessmentCriterions");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Resumes");
|
||||
name: "Assessments");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Criterions");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Assessments");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Vacancies");
|
||||
name: "Resumes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Vacancies");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Companies");
|
||||
}
|
@ -33,20 +33,16 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int?>("Rating")
|
||||
b.Property<int?>("ResumeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("ResumeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
b.Property<int?>("UserId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ResumeId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Assessments");
|
||||
@ -118,19 +114,10 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Weight")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Criterions");
|
||||
@ -158,9 +145,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
b.Property<string>("PhotoFilePath")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("ResumeId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Skills")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
@ -180,9 +164,6 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ResumeId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("VacancyId");
|
||||
@ -289,11 +270,15 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Assessment", b =>
|
||||
{
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Resume", "Resume")
|
||||
.WithMany()
|
||||
.HasForeignKey("ResumeId");
|
||||
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.User", "User")
|
||||
.WithMany("Assessments")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("Resume");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
@ -319,26 +304,18 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Resume", b =>
|
||||
{
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Assessment", "Assessment")
|
||||
.WithOne("Resume")
|
||||
.HasForeignKey("CandidateReviewDatabaseImplement.Models.Resume", "ResumeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.User", "User")
|
||||
.WithMany("Resumes")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Vacancy", "Vacancy")
|
||||
.WithMany("Resumes")
|
||||
.WithMany()
|
||||
.HasForeignKey("VacancyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Assessment");
|
||||
|
||||
b.Navigation("User");
|
||||
|
||||
b.Navigation("Vacancy");
|
||||
@ -347,7 +324,7 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.User", b =>
|
||||
{
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Company", "Company")
|
||||
.WithMany("Users")
|
||||
.WithMany()
|
||||
.HasForeignKey("CompanyId");
|
||||
|
||||
b.Navigation("Company");
|
||||
@ -356,7 +333,7 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Vacancy", b =>
|
||||
{
|
||||
b.HasOne("CandidateReviewDatabaseImplement.Models.Company", "Company")
|
||||
.WithMany("Vacancies")
|
||||
.WithMany()
|
||||
.HasForeignKey("CompanyId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
@ -367,34 +344,12 @@ namespace CandidateReviewDatabaseImplement.Migrations
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Assessment", b =>
|
||||
{
|
||||
b.Navigation("Criterions");
|
||||
|
||||
b.Navigation("Resume")
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Company", b =>
|
||||
{
|
||||
b.Navigation("Users");
|
||||
|
||||
b.Navigation("Vacancies");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Criterion", b =>
|
||||
{
|
||||
b.Navigation("AssessmentCriterions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Assessments");
|
||||
|
||||
b.Navigation("Resumes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CandidateReviewDatabaseImplement.Models.Vacancy", b =>
|
||||
{
|
||||
b.Navigation("Resumes");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
@ -8,14 +8,9 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
{
|
||||
public class Assessment : IAssessmentModel
|
||||
{
|
||||
[Required]
|
||||
public int ResumeId { get; set; }
|
||||
[Required]
|
||||
public int UserId { get; set; }
|
||||
public int? UserId { get; set; }
|
||||
|
||||
public int? Rating { get; set; }
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public int? ResumeId { get; set; }
|
||||
|
||||
public string? Comment { get; set; }
|
||||
|
||||
@ -53,8 +48,6 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
Id = model.Id,
|
||||
ResumeId = model.ResumeId,
|
||||
UserId = model.UserId,
|
||||
Rating = model.Rating,
|
||||
CreatedAt = model.CreatedAt,
|
||||
Comment = model.Comment,
|
||||
Criterions = model.AssessmentCriterions.Select(x => new AssessmentCriterion()
|
||||
{
|
||||
@ -71,8 +64,6 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
}
|
||||
ResumeId = model.ResumeId;
|
||||
UserId = model.UserId;
|
||||
Rating = model.Rating;
|
||||
CreatedAt = model.CreatedAt;
|
||||
Comment = model.Comment;
|
||||
}
|
||||
public AssessmentViewModel GetViewModel => new()
|
||||
@ -80,8 +71,6 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
Id = Id,
|
||||
ResumeId = ResumeId,
|
||||
UserId = UserId,
|
||||
Rating = Rating,
|
||||
CreatedAt = CreatedAt,
|
||||
Comment = Comment
|
||||
};
|
||||
|
||||
|
@ -22,12 +22,6 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
[ForeignKey("CompanyId")]
|
||||
public virtual List<User> Users { get; set; } = new();
|
||||
|
||||
[ForeignKey("CompanyId")]
|
||||
public virtual List<Vacancy> Vacancies { get; set; } = new();
|
||||
|
||||
public static Company? Create(CompanyBindingModel model)
|
||||
{
|
||||
if (model == null)
|
||||
|
@ -1,6 +1,5 @@
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using CandidateReviewDataModels.Enums;
|
||||
using CandidateReviewDataModels.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
@ -11,12 +10,6 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
{
|
||||
[Required]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
[Required]
|
||||
public CriterionTypeEnum Type { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
[Required]
|
||||
public int Weight { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
[ForeignKey("CriterionId")]
|
||||
@ -31,10 +24,7 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
return new Criterion()
|
||||
{
|
||||
Id = model.Id,
|
||||
Name = model.Name,
|
||||
Type = model.Type,
|
||||
Description = model.Description,
|
||||
Weight = model.Weight
|
||||
Name = model.Name
|
||||
};
|
||||
}
|
||||
public static Criterion Create(CriterionViewModel model)
|
||||
@ -42,10 +32,7 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
return new Criterion
|
||||
{
|
||||
Id = model.Id,
|
||||
Name = model.Name,
|
||||
Type = model.Type,
|
||||
Description = model.Description,
|
||||
Weight = model.Weight
|
||||
Name = model.Name
|
||||
};
|
||||
}
|
||||
public void Update(CriterionBindingModel model)
|
||||
@ -55,17 +42,11 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
return;
|
||||
}
|
||||
Name = model.Name;
|
||||
Type = model.Type;
|
||||
Description = model.Description;
|
||||
Weight = model.Weight;
|
||||
}
|
||||
public CriterionViewModel GetViewModel => new()
|
||||
{
|
||||
Id = Id,
|
||||
Name = Name,
|
||||
Type = Type,
|
||||
Description = Description,
|
||||
Weight = Weight
|
||||
Name = Name
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,6 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
[ForeignKey("ResumeId")]
|
||||
public virtual Assessment Assessment { get; set; } = new();
|
||||
public virtual Vacancy Vacancy { get; set; }
|
||||
public virtual User User { get; set; }
|
||||
|
||||
|
@ -31,12 +31,6 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
public int Id { get; set; }
|
||||
public virtual Company Company { get; set; }
|
||||
|
||||
[ForeignKey("UserId")]
|
||||
public virtual List<Resume> Resumes { get; set; } = new();
|
||||
|
||||
[ForeignKey("UserId")]
|
||||
public virtual List<Assessment> Assessments { get; set; } = new();
|
||||
|
||||
public static User? Create(UserBindingModel model)
|
||||
{
|
||||
if (model == null)
|
||||
|
@ -33,9 +33,6 @@ namespace CandidateReviewDatabaseImplement.Models
|
||||
public int Id { get; set; }
|
||||
public virtual Company Company { get; set; }
|
||||
|
||||
[ForeignKey("VacancyId")]
|
||||
public virtual List<Resume> Resumes { get; set; } = new();
|
||||
|
||||
public static Vacancy? Create(VacancyBindingModel model)
|
||||
{
|
||||
if (model == null)
|
||||
|
80
CandidateReviewRestApi/Controllers/AssessmentController.cs
Normal file
80
CandidateReviewRestApi/Controllers/AssessmentController.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.BusinessLogicsContracts;
|
||||
using CandidateReviewContracts.SearchModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CandidateReviewRestApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]/[action]")]
|
||||
[ApiController]
|
||||
public class AssessmentController : Controller
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IAssessmentLogic _logic;
|
||||
public AssessmentController(IAssessmentLogic logic, ILogger<AssessmentController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_logic = logic;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public AssessmentViewModel? Details(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _logic.ReadElement(new AssessmentSearchModel
|
||||
{
|
||||
Id = id
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка получения оценки");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Create(AssessmentBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logic.Create(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка создания оценки");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Update(AssessmentBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logic.Update(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка обновления оценки");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Delete(AssessmentBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logic.Delete(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка удаления оценки");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
using CandidateReviewContracts.BusinessLogicsContracts;
|
||||
using CandidateReviewContracts.SearchModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using CandidateReviewDataModels.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Linq;
|
||||
|
||||
@ -39,17 +38,34 @@ namespace CandidateReviewRestApi.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public int Create(CompanyBindingModel model)
|
||||
[HttpGet]
|
||||
public CompanyViewModel? DefineByName(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
int newCompanyId = _logic.Create(model);
|
||||
return newCompanyId;
|
||||
return _logic.ReadElement(new CompanySearchModel
|
||||
{
|
||||
Name = name
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка создания профиля компании");
|
||||
_logger.LogError(ex, "Ошибка определения компании");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult Create(CompanyBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
int? id = _logic.Create(model);
|
||||
return Ok(new CompanyBindingModel { Id = (int)id });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка создания компании");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
95
CandidateReviewRestApi/Controllers/CriterionController.cs
Normal file
95
CandidateReviewRestApi/Controllers/CriterionController.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.BusinessLogicsContracts;
|
||||
using CandidateReviewContracts.SearchModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using CandidateReviewDataModels.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CandidateReviewRestApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]/[action]")]
|
||||
[ApiController]
|
||||
public class CriterionController : Controller
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly ICriterionLogic _logic;
|
||||
public CriterionController(ICriterionLogic logic, ILogger<CriterionController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_logic = logic;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public CriterionViewModel? Details(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _logic.ReadElement(new CriterionSearchModel
|
||||
{
|
||||
Id = id
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка получения критерия");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public List<CriterionViewModel>? List()
|
||||
{
|
||||
try
|
||||
{
|
||||
return _logic.ReadList(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка получения списка критериев");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Create(CriterionBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logic.Create(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка создания критерия");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Update(CriterionBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logic.Update(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка обновления критерия");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Delete(CriterionBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logic.Delete(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка удаления критерия");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
98
CandidateReviewRestApi/Controllers/ResumeController.cs
Normal file
98
CandidateReviewRestApi/Controllers/ResumeController.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using CandidateReviewContracts.BindingModels;
|
||||
using CandidateReviewContracts.BusinessLogicsContracts;
|
||||
using CandidateReviewContracts.SearchModels;
|
||||
using CandidateReviewContracts.ViewModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CandidateReviewRestApi.Controllers
|
||||
{
|
||||
[Route("api/[controller]/[action]")]
|
||||
[ApiController]
|
||||
public class ResumeController : Controller
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IResumeLogic _logic;
|
||||
public ResumeController(IResumeLogic logic, ILogger<ResumeController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_logic = logic;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ResumeViewModel? Details(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _logic.ReadElement(new ResumeSearchModel
|
||||
{
|
||||
Id = id
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка получения резюме");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ResumeViewModel? Check(int userId, int vacancyId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _logic.ReadElement(new ResumeSearchModel
|
||||
{
|
||||
UserId = userId,
|
||||
VacancyId = vacancyId
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка получения резюме");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Create(ResumeBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logic.Create(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка создания резюме");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Update(ResumeBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logic.Update(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка обновления резюме");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public void Delete(ResumeBindingModel model)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logic.Delete(model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ошибка удаления резюме");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -44,7 +44,8 @@ namespace CandidateReviewRestApi.Controllers
|
||||
{
|
||||
return _logic.ReadList(new VacancySearchModel
|
||||
{
|
||||
Tags = tags
|
||||
Tags = tags,
|
||||
Status = CandidateReviewDataModels.Enums.VacancyStatusEnum.Открыта
|
||||
});
|
||||
}
|
||||
return new List<VacancyViewModel>();
|
||||
|
Loading…
Reference in New Issue
Block a user