мелкие правки, стили, оценка еще в работе
This commit is contained in:
parent
aa28aecfc1
commit
76c13d303a
@ -5,7 +5,7 @@ using CandidateReviewContracts.StoragesContracts;
|
|||||||
using CandidateReviewContracts.ViewModels;
|
using CandidateReviewContracts.ViewModels;
|
||||||
using CandidateReviewDataModels.Models;
|
using CandidateReviewDataModels.Models;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Text.RegularExpressions;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace CandidateReviewBusinessLogic.BusinessLogic
|
namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||||
{
|
{
|
||||||
@ -13,43 +13,15 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
|||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IAssessmentStorage _assessmentStorage;
|
private readonly IAssessmentStorage _assessmentStorage;
|
||||||
public AssessmentLogic(ILogger<AssessmentLogic> logger, IAssessmentStorage assessmentStorage)
|
private readonly IUserStorage _userStorage;
|
||||||
|
private readonly ICriterionStorage _criterionStorage;
|
||||||
|
|
||||||
|
public AssessmentLogic(ILogger<AssessmentLogic> logger, IAssessmentStorage assessmentStorage, IUserStorage userStorage, ICriterionStorage criterionStorage)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_assessmentStorage = assessmentStorage;
|
_assessmentStorage = assessmentStorage;
|
||||||
}
|
_userStorage = userStorage;
|
||||||
|
_criterionStorage = criterionStorage;
|
||||||
public bool AddCriterionToAssessment(AssessmentSearchModel model, ICriterionModel criterion, int value)
|
|
||||||
{
|
|
||||||
if (model == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(model));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (criterion == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(criterion));
|
|
||||||
}
|
|
||||||
|
|
||||||
var element = _assessmentStorage.GetElement(model);
|
|
||||||
|
|
||||||
if (element == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
element.AssessmentCriterions[element.Id] = (criterion, value);
|
|
||||||
|
|
||||||
_assessmentStorage.Update(new AssessmentBindingModel
|
|
||||||
{
|
|
||||||
Id = element.Id,
|
|
||||||
UserId = element.UserId,
|
|
||||||
ResumeId = element.ResumeId,
|
|
||||||
Comment = element.Comment,
|
|
||||||
AssessmentCriterions = element.AssessmentCriterions
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int? Create(AssessmentBindingModel model)
|
public int? Create(AssessmentBindingModel model)
|
||||||
@ -95,13 +67,40 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
|||||||
public List<AssessmentViewModel>? ReadList(AssessmentSearchModel? model)
|
public List<AssessmentViewModel>? ReadList(AssessmentSearchModel? model)
|
||||||
{
|
{
|
||||||
var list = model == null ? _assessmentStorage.GetFullList() : _assessmentStorage.GetFilteredList(model);
|
var list = model == null ? _assessmentStorage.GetFullList() : _assessmentStorage.GetFilteredList(model);
|
||||||
if (list == null)
|
List<AssessmentViewModel> result = new();
|
||||||
|
|
||||||
|
foreach (var elem in list)
|
||||||
|
{
|
||||||
|
var user = _userStorage.GetElement(new UserSearchModel { Id = elem.UserId });
|
||||||
|
var assessmentCriterions = _assessmentStorage.GetAssessmentCriterions(elem.Id);
|
||||||
|
|
||||||
|
var assessmentViewModel = new AssessmentViewModel
|
||||||
|
{
|
||||||
|
Id = elem.Id,
|
||||||
|
ResumeId = elem.ResumeId,
|
||||||
|
UserId = elem.UserId,
|
||||||
|
UserName = user.Name + " " + user.Name,
|
||||||
|
Comment = elem.Comment,
|
||||||
|
AssessmentCriterions = assessmentCriterions.Select(ac => new AssessmentCriterionViewModel
|
||||||
|
{
|
||||||
|
AssessmentId = ac.AssessmentId,
|
||||||
|
CriterionId = ac.CriterionId,
|
||||||
|
Value = ac.Value,
|
||||||
|
CriterionName = _criterionStorage.GetElement(new CriterionSearchModel { Id = ac.CriterionId })?.Name
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
result.Add(assessmentViewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("ReadList return null list");
|
_logger.LogWarning("ReadList return null list");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
_logger.LogInformation("ReadList. Count: {Count}", list.Count);
|
|
||||||
return list;
|
_logger.LogInformation("ReadList. Count: {Count}", result.Count);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Update(AssessmentBindingModel model)
|
public bool Update(AssessmentBindingModel model)
|
||||||
|
@ -4,6 +4,7 @@ using CandidateReviewContracts.SearchModels;
|
|||||||
using CandidateReviewContracts.StoragesContracts;
|
using CandidateReviewContracts.StoragesContracts;
|
||||||
using CandidateReviewContracts.ViewModels;
|
using CandidateReviewContracts.ViewModels;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace CandidateReviewBusinessLogic.BusinessLogic
|
namespace CandidateReviewBusinessLogic.BusinessLogic
|
||||||
{
|
{
|
||||||
@ -84,6 +85,7 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
|||||||
Status = element.Status,
|
Status = element.Status,
|
||||||
Assessments = assessmentViewModels
|
Assessments = assessmentViewModels
|
||||||
};
|
};
|
||||||
|
|
||||||
_logger.LogInformation("ReadElement find. Id: {Id}", element.Id);
|
_logger.LogInformation("ReadElement find. Id: {Id}", element.Id);
|
||||||
return resumeViewModel;
|
return resumeViewModel;
|
||||||
}
|
}
|
||||||
@ -96,8 +98,39 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
|||||||
_logger.LogWarning("ReadList return null list");
|
_logger.LogWarning("ReadList return null list");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
List<ResumeViewModel> result = new();
|
||||||
|
foreach (var element in list)
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
Assessments = assessmentViewModels
|
||||||
|
};
|
||||||
|
result.Add(resumeViewModel);
|
||||||
|
}
|
||||||
_logger.LogInformation("ReadList. Count: {Count}", list.Count);
|
_logger.LogInformation("ReadList. Count: {Count}", list.Count);
|
||||||
return list;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Update(ResumeBindingModel model)
|
public bool Update(ResumeBindingModel model)
|
||||||
|
@ -71,6 +71,7 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
|||||||
{
|
{
|
||||||
Id = r.Id,
|
Id = r.Id,
|
||||||
VacancyId = r.VacancyId,
|
VacancyId = r.VacancyId,
|
||||||
|
UserName = element.Name + " " + element.Surname,
|
||||||
VacancyName = _vacancyStorage.GetElement(new VacancySearchModel { Id = r.VacancyId }).JobTitle,
|
VacancyName = _vacancyStorage.GetElement(new VacancySearchModel { Id = r.VacancyId }).JobTitle,
|
||||||
UserId = r.UserId,
|
UserId = r.UserId,
|
||||||
Title = r.Title,
|
Title = r.Title,
|
||||||
|
@ -14,13 +14,15 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
|||||||
private readonly IVacancyStorage _vacancyStorage;
|
private readonly IVacancyStorage _vacancyStorage;
|
||||||
private readonly IResumeStorage _resumeStorage;
|
private readonly IResumeStorage _resumeStorage;
|
||||||
private readonly ICompanyStorage _companyStorage;
|
private readonly ICompanyStorage _companyStorage;
|
||||||
|
private readonly IUserStorage _userStorage;
|
||||||
|
|
||||||
public VacancyLogic(ILogger<VacancyLogic> logger, IVacancyStorage vacancyStorage, IResumeStorage resumeStorage, ICompanyStorage companyStorage)
|
public VacancyLogic(ILogger<VacancyLogic> logger, IVacancyStorage vacancyStorage, IResumeStorage resumeStorage, ICompanyStorage companyStorage, IUserStorage userStorage)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_vacancyStorage = vacancyStorage;
|
_vacancyStorage = vacancyStorage;
|
||||||
_resumeStorage = resumeStorage;
|
_resumeStorage = resumeStorage;
|
||||||
_companyStorage = companyStorage;
|
_companyStorage = companyStorage;
|
||||||
|
_userStorage = userStorage;
|
||||||
}
|
}
|
||||||
public bool Create(VacancyBindingModel model)
|
public bool Create(VacancyBindingModel model)
|
||||||
{
|
{
|
||||||
@ -63,7 +65,7 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
|
|||||||
Id = r.Id,
|
Id = r.Id,
|
||||||
VacancyId = r.VacancyId,
|
VacancyId = r.VacancyId,
|
||||||
VacancyName = _vacancyStorage.GetElement(new VacancySearchModel { Id = r.VacancyId }).JobTitle,
|
VacancyName = _vacancyStorage.GetElement(new VacancySearchModel { Id = r.VacancyId }).JobTitle,
|
||||||
UserName = r.UserName,
|
UserName = _userStorage.GetElement(new UserSearchModel { Id = r.UserId}).Name + " " + _userStorage.GetElement(new UserSearchModel { Id = r.UserId }).Surname,
|
||||||
UserId = r.UserId,
|
UserId = r.UserId,
|
||||||
Title = r.Title,
|
Title = r.Title,
|
||||||
Experience = r.Experience,
|
Experience = r.Experience,
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CandidateReviewContracts\CandidateReviewContracts.csproj" />
|
<ProjectReference Include="..\CandidateReviewContracts\CandidateReviewContracts.csproj" />
|
||||||
|
<ProjectReference Include="..\CandidateReviewDatabaseImplement\CandidateReviewDatabaseImplement.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -17,6 +17,8 @@ namespace CandidateReviewClientApp.Controllers
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> AddAssessmentCriterion(int resumeId, int[] criterion, int[] value, string comment)
|
public async Task<IActionResult> AddAssessmentCriterion(int resumeId, int[] criterion, int[] value, string comment)
|
||||||
{
|
{
|
||||||
@ -42,29 +44,12 @@ namespace CandidateReviewClientApp.Controllers
|
|||||||
ResumeId = resumeId,
|
ResumeId = resumeId,
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
Comment = comment,
|
Comment = comment,
|
||||||
AssessmentCriterions = new Dictionary<int, (ICriterionModel, int)>()
|
AssessmentCriterions = criterion.Select((t, i) => new KeyValuePair<int, (ICriterionModel, int)>
|
||||||
|
(t, (new CriterionViewModel { Id = t }, value[i]))).ToDictionary(kv => kv.Key, kv => kv.Value)
|
||||||
};
|
};
|
||||||
|
|
||||||
var assessmentId = await APIClient.PostRequestAsync("api/assessment/create", assessmentModel);
|
|
||||||
|
|
||||||
for (int i = 0; i < criterion.Length; i++)
|
int assessmentId = await APIClient.PostRequestAsync("api/assessment/create", assessmentModel);
|
||||||
{
|
|
||||||
if (value[i] < 1 || value[i] > 5)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Оценка для критерия {criterion[i]} должна быть от 1 до 5.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (assessmentData.ContainsKey(assessmentId))
|
|
||||||
{
|
|
||||||
assessmentData[assessmentId] = (new CriterionViewModel { Id = criterion[i] }, value[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assessmentData.Add(assessmentId, (new CriterionViewModel { Id = criterion[i] }, value[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
APIClient.PostRequest("api/assessment/AddCriterionToAssessment", assessmentData);
|
|
||||||
|
|
||||||
return Redirect($"~/Resume/ResumeDetails/{resumeId}");
|
return Redirect($"~/Resume/ResumeDetails/{resumeId}");
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,14 @@ namespace CandidateReviewClientApp.Controllers
|
|||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
var assessments = APIClient.GetRequest<List<AssessmentViewModel>>($"api/assessment/listByResumeId?id={id}");
|
|
||||||
|
var resumeAssessments = APIClient.GetRequest<List<AssessmentViewModel>>($"api/assessment/listByResumeId?id={id}");
|
||||||
|
var userAssessment = resumeAssessments?.FirstOrDefault(a => a.UserId == APIClient.User.Id);
|
||||||
var criterions = APIClient.GetRequest<List<CriterionViewModel>>($"api/criterion/list");
|
var criterions = APIClient.GetRequest<List<CriterionViewModel>>($"api/criterion/list");
|
||||||
resume.Assessments = assessments;
|
resume.Assessments = resumeAssessments ?? new List<AssessmentViewModel>();
|
||||||
|
ViewBag.UserAssessment = userAssessment;
|
||||||
ViewBag.Criterions = criterions;
|
ViewBag.Criterions = criterions;
|
||||||
|
|
||||||
return View(resume);
|
return View(resume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,37 +8,47 @@
|
|||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="card">
|
<div class="card shadow-sm">
|
||||||
<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="Логотип компании">
|
<img src="@(Model.LogoFilePath ?? "https://static.thenounproject.com/png/2504969-200.png")"
|
||||||
<div class="card-body">
|
class="card-img-top img-fluid rounded-circle mx-auto d-block" alt="Логотип компании"
|
||||||
|
style="max-width: 150px; max-height: 150px;">
|
||||||
|
<div class="card-body text-center">
|
||||||
<h5 class="card-title">@Model.Name</h5>
|
<h5 class="card-title">@Model.Name</h5>
|
||||||
<input type="hidden" name="id" value="@Model?.Id" />
|
<input type="hidden" name="id" value="@Model?.Id" />
|
||||||
<p class="card-text">@(Model.Description == null ? "Описание отсутствует" : Model.Description) </p>
|
<p class="card-text">@((Model.Description ?? "Описание отсутствует"))</p>
|
||||||
<a href="@(Model.Website ?? "#")" target="_blank" class="btn btn-primary mt-2">@(Model.Website != null ? "Официальный сайт" : "Веб-сайт отсутствует") @(Model.Website != null ? "" : "disabled")</a> </a>
|
<a href="@(Model.Website ?? "#")"
|
||||||
|
target="_blank"
|
||||||
|
class="btn btn-primary mt-2 @(Model.Website == null ? "disabled" : "")"
|
||||||
|
@(Model.Website == null ? "aria-disabled=\"true\"" : "")>
|
||||||
|
@(Model.Website != null ? "Официальный сайт" : "Веб-сайт отсутствует")
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4 shadow-sm">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<h2>Информация о компании</h2>
|
<h2>Информация о компании</h2>
|
||||||
<a asp-action="EditCompanyProfile" asp-controller="Company" asp-route-id="@Model.Id" class="btn btn-primary">Редактировать</a>
|
<a asp-action="EditCompanyProfile" asp-controller="Company" asp-route-id="@Model.Id"
|
||||||
|
class="btn btn-warning">Редактировать</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<dl class="row">
|
<dl class="row">
|
||||||
<dt class="col-sm-3">Адрес:</dt>
|
<dt class="col-sm-3">Адрес:</dt>
|
||||||
<dd class="col-sm-9">@(Model.Address?.ToString() ?? "Адрес не указан")</dd>
|
<dd class="col-sm-9">@((Model.Address?.ToString() ?? "Адрес не указан"))</dd>
|
||||||
|
|
||||||
<dt class="col-sm-3">Контакты:</dt>
|
<dt class="col-sm-3">Контакты:</dt>
|
||||||
<dd class="col-sm-9">@(Model.Contacts?.ToString() ?? "Контакты не указаны")</dd>
|
<dd class="col-sm-9">@((Model.Contacts?.ToString() ?? "Контакты не указаны"))</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card mb-4 shadow-sm">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<h2>Вакансии компании</h2>
|
<h2>Вакансии компании</h2>
|
||||||
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-companyId="@Model.Id" class="btn btn-success">Добавить вакансию</a>
|
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-companyId="@Model.Id"
|
||||||
|
class="btn btn-success">Добавить вакансию</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@if (Model.Vacancies != null && Model.Vacancies.Any())
|
@if (Model.Vacancies != null && Model.Vacancies.Any())
|
||||||
@ -59,11 +69,12 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
|
||||||
<div class="card">
|
<div class="card shadow-sm">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<h2>Сотрудники компании</h2>
|
<h2>Сотрудники компании</h2>
|
||||||
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-companyId="@Model.Id" class="btn btn-success">Добавить сотрудника</a>
|
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-companyId="@Model.Id"
|
||||||
|
class="btn btn-success">Добавить сотрудника</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@if (Model.Employees != null && Model.Employees.Any())
|
@if (Model.Employees != null && Model.Employees.Any())
|
||||||
@ -83,16 +94,17 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>@employee.Surname</td>
|
<td>@employee.Surname</td>
|
||||||
<td>@employee.Name</td>
|
<td>@employee.Name</td>
|
||||||
<td>@employee.Email</td>
|
|
||||||
<td>
|
<td>
|
||||||
<a asp-action="UserProfile" asp-controller="User" asp-route-id="@employee.Id" class="text-info" title="Просмотр">
|
<a asp-action="UserProfile" asp-controller="User" asp-route-id="@employee.Id" class="btn btn-info btn-sm rounded-pill" title="Просмотр">
|
||||||
<i class="bi bi-eye"></i>
|
<i class="bi bi-eye"></i> Просмотр
|
||||||
</a>
|
</a>
|
||||||
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-id="@employee.Id" class="text-warning" title="Редактировать">
|
|
||||||
<i class="bi bi-pencil"></i>
|
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-id="@employee.Id" class="btn btn-warning btn-sm rounded-pill" title="Редактировать">
|
||||||
|
<i class="bi bi-pencil"></i> Редактировать
|
||||||
</a>
|
</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 asp-action="DeleteEmployee" asp-controller="User" asp-route-id="@employee.Id" class="btn btn-danger btn-sm rounded-pill" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить сотрудника?');">
|
||||||
|
<i class="bi bi-trash"></i> Удалить
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -107,6 +119,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
|
@ -8,38 +8,33 @@
|
|||||||
|
|
||||||
<div class="d-flex justify-content-center mt-4">
|
<div class="d-flex justify-content-center mt-4">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h3>Создать профиль своей компании</h3>
|
<h3>Создать профиль компании</h3>
|
||||||
<p>Здесь вы можете создать и настроить профиль вашей компании, указать основные данные. Профиль вашей компании будет виден кандидатам, что поможет вам привлечь их внимание к вакансиям, которые вы предлагаете.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h3>Подобрать кандидатов на вакансию</h3>
|
<h3>Подобрать кандидатов на вакансию</h3>
|
||||||
<p>Используйте наш удобный инструмент для подбора кандидатов на вакансию. Вы можете просмотреть параметры возможных кандидатов. Это упростит процесс поиска подходящих кандидатов для ваших вакансий и даст вам больше шансов найти именно того, кто вам нужен.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h3>Найти подходящую вакансию</h3>
|
<h3>Найти подходящую вакансию</h3>
|
||||||
<p>Если вы ищете работу, здесь вы можете просмотреть все доступные вакансии. Мы стремимся помочь вам найти именно то рабочее место, которое соответствует вашим ожиданиям и интересам.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h3>Оценить кандидатов на вакансию</h3>
|
<h3>Оценить кандидатов на вакансию</h3>
|
||||||
<p>После просмотра резюме у вас есть возможность оценить кандидатов и оставить свои комментарии. Вы можете выставить оценки по различным критериям и добавить свои заметки. Это поможет в дальнейшем процессе выбора и предоставляет общую картину по каждому кандидату, что значительно упрощает принятие решения.</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.d-flex {
|
.d-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: space-between;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin: 10px;
|
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
width: 200px;
|
width: 45%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
transition: transform 0.2s;
|
transition: transform 0.2s;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
@using CandidateReviewDataModels.Models
|
@using CandidateReviewDataModels.Models
|
||||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
@using CandidateReviewContracts.ViewModels
|
@using CandidateReviewContracts.ViewModels
|
||||||
|
@using Newtonsoft.Json
|
||||||
@model ResumeViewModel
|
@model ResumeViewModel
|
||||||
|
|
||||||
@{
|
@{
|
||||||
@ -64,29 +65,91 @@
|
|||||||
<h2 class="mb-4">Оценка резюме</h2>
|
<h2 class="mb-4">Оценка резюме</h2>
|
||||||
|
|
||||||
<form method="post" asp-controller="Assessment" asp-action="AddAssessmentCriterion">
|
<form method="post" asp-controller="Assessment" asp-action="AddAssessmentCriterion">
|
||||||
<input type="hidden" name="ResumeId" value="@Model.Id" />
|
<input type="hidden" name="resumeId" value="@Model.Id" />
|
||||||
|
|
||||||
@if (ViewBag.Criterions != null)
|
<div id="criteria-container">
|
||||||
|
<div id="existing-criteria-container">
|
||||||
|
@if (Model.Assessments.Any())
|
||||||
{
|
{
|
||||||
foreach (var criterion in ViewBag.Criterions)
|
foreach (var assessment in Model.Assessments)
|
||||||
{
|
{
|
||||||
<div class="row mb-3">
|
// Если это пользовательская оценка, пропускаем её, так как она будет отображена отдельно.
|
||||||
<div class="col-md-6">
|
if (ViewBag.UserAssessment is AssessmentViewModel currentUserAssessment && assessment.UserId == currentUserAssessment.UserId)
|
||||||
<input type="text" class="form-control" value="@criterion.Name" readonly />
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
<h4>Оценка пользователя: @assessment.UserName</h4>
|
||||||
|
@foreach (var criterion in assessment.AssessmentCriterions)
|
||||||
|
{
|
||||||
|
<div class="row mb-3 criterion-row">
|
||||||
|
<div class="col-8">
|
||||||
|
<select name="criterion" class="form-control" disabled>
|
||||||
|
<option value="@criterion.CriterionId" selected>@criterion.CriterionName</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<input type="number" class="form-control" name="value[]" min="1" max="5" placeholder="Введите значение от 1 до 5" required />
|
<input type="number" name="value" class="form-control" value="@criterion.Value" readonly />
|
||||||
<input type="hidden" name="criterion[]" value="@criterion.Id" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
<p>Комментарий: @assessment.Comment</p>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<p>Нет доступных критериев.</p>
|
<p>Это резюме еще никто не оценивал. Станьте первыми!</p>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
@if (ViewBag.UserAssessment is AssessmentViewModel userAssessment)
|
||||||
|
{
|
||||||
|
<div id="user-assessment-container">
|
||||||
|
<h4>Ваша оценка</h4>
|
||||||
|
@foreach (var criterion in userAssessment.AssessmentCriterions)
|
||||||
|
{
|
||||||
|
<div class="row mb-3 criterion-row">
|
||||||
|
<div class="col-8">
|
||||||
|
<select name="criterion" class="form-control" disabled>
|
||||||
|
<option value="@criterion.CriterionId" selected>@criterion.CriterionName</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<input type="number" name="value" class="form-control" value="@criterion.Value" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<p>Комментарий: @userAssessment.Comment</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div id="new-criteria-container">
|
||||||
|
<div class="row mb-3 criterion-row">
|
||||||
|
<div class="col-8">
|
||||||
|
<select name="criterion" class="form-control">
|
||||||
|
<option value="">Выберите критерий</option>
|
||||||
|
@if (ViewBag.Criterions is List<CriterionViewModel> criterions)
|
||||||
|
{
|
||||||
|
foreach (var criterion in criterions)
|
||||||
|
{
|
||||||
|
<option value="@criterion.Id">@criterion.Name</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<input type="number" name="value" class="form-control" min="1" max="5" placeholder="Введите значение от 1 до 5" required />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1 d-flex align-items-center">
|
||||||
|
<button type="button" class="btn btn-danger btn-sm remove-criterion">Х</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-outline-secondary mt-2" id="add-criterion">Добавить критерий</button>
|
||||||
|
|
||||||
|
<div class="row mt-4">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<label for="comment" class="form-label">Комментарий</label>
|
<label for="comment" class="form-label">Комментарий</label>
|
||||||
<textarea id="comment" name="comment" class="form-control" rows="4" placeholder="Оставьте ваш комментарий..."></textarea>
|
<textarea id="comment" name="comment" class="form-control" rows="4" placeholder="Оставьте ваш комментарий..."></textarea>
|
||||||
@ -94,6 +157,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-success mt-3">Сохранить</button>
|
<button type="submit" class="btn btn-success mt-3">Сохранить</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
@ -103,7 +168,49 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
const criterions = @Html.Raw(JsonConvert.SerializeObject(ViewBag.Criterions));
|
||||||
var criterions = @Html.Raw(Json.Serialize(ViewBag.Criterions));
|
|
||||||
|
document.getElementById('add-criterion').addEventListener('click', function () {
|
||||||
|
const container = document.getElementById('new-criteria-container');
|
||||||
|
const existingCriteria = Array.from(container.querySelectorAll('select[name="criterion"]')).map(select => select.value);
|
||||||
|
|
||||||
|
const newRow = document.createElement('div');
|
||||||
|
newRow.className = 'row mb-3 criterion-row';
|
||||||
|
|
||||||
|
let selectOptions = '<option value="">Выберите критерий</option>';
|
||||||
|
criterions.forEach(criterion => {
|
||||||
|
if (!existingCriteria.includes(criterion.Id.toString())) {
|
||||||
|
selectOptions += `<option value="${criterion.Id}">${criterion.Name}</option>`;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
newRow.innerHTML = `
|
||||||
|
<div class="col-8">
|
||||||
|
<select name="criterion" class="form-control">${selectOptions}</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<input type="number" name="value" class="form-control" min="1" max="5" placeholder="Введите значение от 1 до 5" required />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1 d-flex align-items-center">
|
||||||
|
<button type="button" class="btn btn-danger btn-sm remove-criterion">✕</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
container.appendChild(newRow);
|
||||||
|
attachRemoveEvent(newRow.querySelector('.remove-criterion'));
|
||||||
|
});
|
||||||
|
|
||||||
|
function attachRemoveEvent(button) {
|
||||||
|
button.addEventListener('click', function () {
|
||||||
|
const container = document.getElementById('new-criteria-container');
|
||||||
|
const rows = container.querySelectorAll('.criterion-row');
|
||||||
|
if (rows.length > 1) {
|
||||||
|
button.closest('.criterion-row').remove();
|
||||||
|
} else {
|
||||||
|
alert('Нельзя удалить последний критерий. Должен быть хотя бы один критерий оценки.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.remove-criterion').forEach(button => attachRemoveEvent(button));
|
||||||
</script>
|
</script>
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
<td>@vacancy.JobType</td>
|
<td>@vacancy.JobType</td>
|
||||||
<td>@vacancy.Status</td>
|
<td>@vacancy.Status</td>
|
||||||
<td>
|
<td>
|
||||||
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-info" title="Просмотр">
|
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="btn btn-info btn-sm rounded-pill" title="Просмотр">
|
||||||
<i class="bi bi-eye"></i>
|
<i class="bi bi-eye"></i>
|
||||||
</a>
|
</a>
|
||||||
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-warning" title="Редактировать">
|
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="btn btn-warning btn-sm rounded-pill" title="Редактировать">
|
||||||
<i class="bi bi-pencil"></i>
|
<i class="bi bi-pencil"></i>
|
||||||
</a>
|
</a>
|
||||||
<a asp-action="Delete" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-danger" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить вакансию?');">
|
<a asp-action="Delete" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="btn btn-danger btn-sm rounded-pill" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить вакансию?');">
|
||||||
<i class="bi bi-trash"></i>
|
<i class="bi bi-trash"></i>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
@ -9,8 +9,9 @@
|
|||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4 mb-4">
|
<div class="col-md-4 mb-4">
|
||||||
<div class="card">
|
<div class="card shadow-sm">
|
||||||
<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="Аватар пользователя">
|
<img src="@(Model.AvatarFilePath ?? "https://cdn-icons-png.flaticon.com/512/18/18601.png")"
|
||||||
|
class="card-img-top img-fluid rounded-circle mx-auto d-block" style="max-width: 150px; max-height: 150px;" alt="Аватар пользователя">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<h5 class="card-title mb-0">
|
<h5 class="card-title mb-0">
|
||||||
@(string.IsNullOrEmpty(@Model?.Surname) ? "" : @Model?.Surname) @Model?.Name @(string.IsNullOrEmpty(@Model?.LastName) ? "" : @Model?.LastName)
|
@(string.IsNullOrEmpty(@Model?.Surname) ? "" : @Model?.Surname) @Model?.Name @(string.IsNullOrEmpty(@Model?.LastName) ? "" : @Model?.LastName)
|
||||||
@ -41,9 +42,10 @@
|
|||||||
@if (userRole)
|
@if (userRole)
|
||||||
{
|
{
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="card">
|
<div class="card shadow-sm mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<h2>Мои резюме</h2>
|
<h2>Мои резюме</h2>
|
||||||
|
<a asp-action="CreateResume" asp-controller="Resume" class="btn btn-success">Создать резюме</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@if (Model.Resumes != null && Model.Resumes.Any())
|
@if (Model.Resumes != null && Model.Resumes.Any())
|
||||||
@ -91,3 +93,77 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th, .table td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-info, .btn-success {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
border-color: #80bdff;
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-striped tbody tr:nth-of-type(odd) {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-light {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-muted {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger {
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll('form[onsubmit]').forEach(function (form) {
|
||||||
|
form.addEventListener('submit', function (event) {
|
||||||
|
if (!confirm('Вы уверены, что хотите удалить?')) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
@ -8,21 +8,23 @@
|
|||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<h1>Поиск вакансий</h1>
|
<h1 class="mb-4 text-center">Поиск вакансий</h1>
|
||||||
|
|
||||||
<form asp-action="SearchVacancies" asp-controller="Vacancy" asp-route-tags="tags" method="get">
|
<form asp-action="SearchVacancies" asp-controller="Vacancy" asp-route-tags="tags" method="get">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-4">
|
||||||
<input type="text" class="form-control" name="tags" id="tags" placeholder="Введите поисковый запрос">
|
<input type="text" class="form-control form-control-lg" name="tags" id="tags" placeholder="Введите поисковый запрос" required>
|
||||||
<button class="btn btn-primary" type="submit">Поиск</button>
|
<button class="btn btn-primary btn-lg" type="submit">Поиск</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@if (Model != null)
|
@if (Model != null)
|
||||||
{
|
{
|
||||||
<h2>Результаты поиска:</h2>
|
<h2 class="mb-3">Результаты поиска:</h2>
|
||||||
|
|
||||||
@if (Model.Any(v => v != null))
|
@if (Model.Any(v => v != null))
|
||||||
{
|
{
|
||||||
<table class="table table-striped">
|
<table class="table table-striped table-bordered">
|
||||||
<thead>
|
<thead class="table-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Название вакансии</th>
|
<th>Название вакансии</th>
|
||||||
<th>Тип работы</th>
|
<th>Тип работы</th>
|
||||||
@ -40,8 +42,8 @@
|
|||||||
<td>@vacancy.Salary</td>
|
<td>@vacancy.Salary</td>
|
||||||
<td>@vacancy.Tags</td>
|
<td>@vacancy.Tags</td>
|
||||||
<td>
|
<td>
|
||||||
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="btn btn-primary">Просмотр</a>
|
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="btn btn-info btn-sm btn-block mr-2">Просмотр</a>
|
||||||
<a asp-controller="Resume" asp-action="EditResume" asp-route-vacancyId="@vacancy.Id" class="btn btn-primary">Составить резюме</a>
|
<a asp-controller="Resume" asp-action="EditResume" asp-route-vacancyId="@vacancy.Id" class="btn btn-success btn-sm btn-block">Составить резюме</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
@ -50,17 +52,80 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<p>Вакансий не найдено.</p>
|
<p class="text-center text-muted">Вакансий не найдено.</p>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<p>Произошла ошибка при получении данных.</p>
|
<p class="text-center text-danger">Произошла ошибка при получении данных.</p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th, .table td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-info, .btn-success {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus {
|
||||||
|
border-color: #80bdff;
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-striped tbody tr:nth-of-type(odd) {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-light {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-muted {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger {
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.querySelector('form').addEventListener('submit', function (event) {
|
document.querySelector('form').addEventListener('submit', function (event) {
|
||||||
const tagsInput = document.getElementById('tags');
|
const tagsInput = document.getElementById('tags');
|
||||||
@ -70,4 +135,3 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using CandidateReviewContracts.BindingModels;
|
using CandidateReviewContracts.BindingModels;
|
||||||
using CandidateReviewContracts.SearchModels;
|
using CandidateReviewContracts.SearchModels;
|
||||||
using CandidateReviewContracts.ViewModels;
|
using CandidateReviewContracts.ViewModels;
|
||||||
using CandidateReviewDataModels.Models;
|
|
||||||
|
|
||||||
namespace CandidateReviewContracts.BusinessLogicsContracts
|
namespace CandidateReviewContracts.BusinessLogicsContracts
|
||||||
{
|
{
|
||||||
@ -10,7 +9,6 @@ namespace CandidateReviewContracts.BusinessLogicsContracts
|
|||||||
List<AssessmentViewModel>? ReadList(AssessmentSearchModel? model);
|
List<AssessmentViewModel>? ReadList(AssessmentSearchModel? model);
|
||||||
AssessmentViewModel? ReadElement(AssessmentSearchModel model);
|
AssessmentViewModel? ReadElement(AssessmentSearchModel model);
|
||||||
int? Create(AssessmentBindingModel model);
|
int? Create(AssessmentBindingModel model);
|
||||||
bool AddCriterionToAssessment(AssessmentSearchModel model, ICriterionModel criterion, int value);
|
|
||||||
bool Update(AssessmentBindingModel model);
|
bool Update(AssessmentBindingModel model);
|
||||||
bool Delete(AssessmentBindingModel model);
|
bool Delete(AssessmentBindingModel model);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace CandidateReviewContracts.SearchModels
|
using CandidateReviewDataModels.Models;
|
||||||
|
|
||||||
|
namespace CandidateReviewContracts.SearchModels
|
||||||
{
|
{
|
||||||
public class AssessmentSearchModel
|
public class AssessmentSearchModel
|
||||||
{
|
{
|
||||||
@ -8,5 +10,7 @@
|
|||||||
public int? ResumeId { get; set; }
|
public int? ResumeId { get; set; }
|
||||||
|
|
||||||
public int? Id { get; set; }
|
public int? Id { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<int, (ICriterionModel, int)> AssessmentCriterions = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ namespace CandidateReviewContracts.StoragesContracts
|
|||||||
{
|
{
|
||||||
List<AssessmentViewModel> GetFullList();
|
List<AssessmentViewModel> GetFullList();
|
||||||
List<AssessmentViewModel> GetFilteredList(AssessmentSearchModel model);
|
List<AssessmentViewModel> GetFilteredList(AssessmentSearchModel model);
|
||||||
|
List<AssessmentCriterionViewModel>? GetAssessmentCriterions(int? assessmentId);
|
||||||
AssessmentViewModel? GetElement(AssessmentSearchModel model);
|
AssessmentViewModel? GetElement(AssessmentSearchModel model);
|
||||||
int? Insert(AssessmentBindingModel model);
|
int? Insert(AssessmentBindingModel model);
|
||||||
AssessmentViewModel? Update(AssessmentBindingModel model);
|
AssessmentViewModel? Update(AssessmentBindingModel model);
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
using CandidateReviewDataModels.Models;
|
||||||
|
|
||||||
|
namespace CandidateReviewContracts.ViewModels
|
||||||
|
{
|
||||||
|
public class AssessmentCriterionViewModel : IAssessmentCriterionModel
|
||||||
|
{
|
||||||
|
public int AssessmentId { get; set; }
|
||||||
|
|
||||||
|
public int CriterionId { get; set; }
|
||||||
|
|
||||||
|
public string CriterionName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public int Value { get; set; }
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -6,12 +6,14 @@ namespace CandidateReviewContracts.ViewModels
|
|||||||
{
|
{
|
||||||
public int? UserId { get; set; }
|
public int? UserId { get; set; }
|
||||||
|
|
||||||
|
public string? UserName { get; set; }
|
||||||
|
|
||||||
public string? Comment { get; set; }
|
public string? Comment { get; set; }
|
||||||
|
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
public int? ResumeId { get; set; }
|
public int? ResumeId { get; set; }
|
||||||
|
|
||||||
public Dictionary<int, (ICriterionModel, int)> AssessmentCriterions { get; set; } = new();
|
public List<AssessmentCriterionViewModel> AssessmentCriterions { get; set; } = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
namespace CandidateReviewDataModels.Models
|
||||||
|
{
|
||||||
|
public interface IAssessmentCriterionModel : IId
|
||||||
|
{
|
||||||
|
int AssessmentId { get; }
|
||||||
|
int CriterionId { get; }
|
||||||
|
int Value { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ using CandidateReviewContracts.SearchModels;
|
|||||||
using CandidateReviewContracts.StoragesContracts;
|
using CandidateReviewContracts.StoragesContracts;
|
||||||
using CandidateReviewContracts.ViewModels;
|
using CandidateReviewContracts.ViewModels;
|
||||||
using CandidateReviewDatabaseImplement.Models;
|
using CandidateReviewDatabaseImplement.Models;
|
||||||
|
using CandidateReviewDataModels.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace CandidateReviewDatabaseImplement.Implements
|
namespace CandidateReviewDatabaseImplement.Implements
|
||||||
@ -38,25 +39,44 @@ namespace CandidateReviewDatabaseImplement.Implements
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
using var context = new CandidateReviewDatabase();
|
using var context = new CandidateReviewDatabase();
|
||||||
if (model.ResumeId.HasValue)
|
if (model.ResumeId.HasValue && model.UserId.HasValue)
|
||||||
|
{
|
||||||
|
return context.Assessments
|
||||||
|
.FirstOrDefault(x => x.ResumeId == model.ResumeId && x.UserId == model.UserId)
|
||||||
|
?.GetViewModel;
|
||||||
|
}
|
||||||
|
else if (model.ResumeId.HasValue)
|
||||||
{
|
{
|
||||||
return context.Assessments
|
return context.Assessments
|
||||||
.FirstOrDefault(x => x.ResumeId == model.ResumeId)
|
.FirstOrDefault(x => x.ResumeId == model.ResumeId)
|
||||||
?.GetViewModel;
|
?.GetViewModel;
|
||||||
}
|
}
|
||||||
|
else if (model.UserId.HasValue)
|
||||||
if (model.UserId.HasValue)
|
|
||||||
{
|
{
|
||||||
return context.Assessments
|
return context.Assessments
|
||||||
.FirstOrDefault(x => x.UserId == model.UserId)
|
.FirstOrDefault(x => x.UserId == model.UserId)
|
||||||
?.GetViewModel;
|
?.GetViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return context.Assessments
|
return context.Assessments
|
||||||
.FirstOrDefault(x => model.Id.HasValue && x.Id == model.Id)
|
.FirstOrDefault(x => model.Id.HasValue && x.Id == model.Id)
|
||||||
?.GetViewModel;
|
?.GetViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<AssessmentCriterionViewModel>? GetAssessmentCriterions(int? assessmentId)
|
||||||
|
{
|
||||||
|
if (!assessmentId.HasValue)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
using var context = new CandidateReviewDatabase();
|
||||||
|
|
||||||
|
return context.AssessmentCriterions
|
||||||
|
.Where(x => x.AssessmentId == assessmentId)
|
||||||
|
.ToList()
|
||||||
|
.Select(x => x.GetViewModel)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public List<AssessmentViewModel> GetFilteredList(AssessmentSearchModel model)
|
public List<AssessmentViewModel> GetFilteredList(AssessmentSearchModel model)
|
||||||
{
|
{
|
||||||
if (model is null)
|
if (model is null)
|
||||||
@ -102,16 +122,27 @@ namespace CandidateReviewDatabaseImplement.Implements
|
|||||||
public int? Insert(AssessmentBindingModel model)
|
public int? Insert(AssessmentBindingModel model)
|
||||||
{
|
{
|
||||||
using var context = new CandidateReviewDatabase();
|
using var context = new CandidateReviewDatabase();
|
||||||
var newAssessment = Assessment.Create(context, model);
|
|
||||||
|
|
||||||
if (newAssessment == null)
|
var newAssessment = new Assessment
|
||||||
{
|
{
|
||||||
return null;
|
ResumeId = model.ResumeId,
|
||||||
}
|
UserId = model.UserId,
|
||||||
|
Comment = model.Comment
|
||||||
|
};
|
||||||
|
|
||||||
context.Assessments.Add(newAssessment);
|
context.Assessments.Add(newAssessment);
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
|
|
||||||
|
/*var criterionsToAdd = model.AssessmentCriterions.Select(x => new AssessmentCriterion
|
||||||
|
{
|
||||||
|
AssessmentId = newAssessment.Id,
|
||||||
|
Criterion = context.Criterions.First(y => y.Id == x.Key),
|
||||||
|
Value = x.Value.Item2
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
context.AssessmentCriterions.AddRange(criterionsToAdd);
|
||||||
|
context.SaveChanges();*/
|
||||||
|
|
||||||
return newAssessment.Id;
|
return newAssessment.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,19 +43,18 @@ namespace CandidateReviewDatabaseImplement.Models
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new Assessment()
|
|
||||||
|
var assessment = new Assessment
|
||||||
{
|
{
|
||||||
Id = model.Id,
|
Id = model.Id,
|
||||||
ResumeId = model.ResumeId,
|
ResumeId = model.ResumeId,
|
||||||
UserId = model.UserId,
|
UserId = model.UserId,
|
||||||
Comment = model.Comment,
|
Comment = model.Comment,
|
||||||
Criterions = model.AssessmentCriterions.Select(x => new AssessmentCriterion()
|
|
||||||
{
|
|
||||||
Criterion = context.Criterions.First(y => y.Id == x.Key),
|
|
||||||
Value = x.Value.Item2
|
|
||||||
}).ToList()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return assessment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(AssessmentBindingModel model)
|
public void Update(AssessmentBindingModel model)
|
||||||
{
|
{
|
||||||
if (model == null)
|
if (model == null)
|
||||||
@ -66,12 +65,24 @@ namespace CandidateReviewDatabaseImplement.Models
|
|||||||
UserId = model.UserId;
|
UserId = model.UserId;
|
||||||
Comment = model.Comment;
|
Comment = model.Comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<AssessmentCriterionViewModel> GetAssessmentCriterionsAsViewModel()
|
||||||
|
{
|
||||||
|
return AssessmentCriterions.Select(ac => new AssessmentCriterionViewModel
|
||||||
|
{
|
||||||
|
CriterionId = ac.Key,
|
||||||
|
Value = ac.Value.Item2,
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public AssessmentViewModel GetViewModel => new()
|
public AssessmentViewModel GetViewModel => new()
|
||||||
{
|
{
|
||||||
Id = Id,
|
Id = Id,
|
||||||
ResumeId = ResumeId,
|
ResumeId = ResumeId,
|
||||||
UserId = UserId,
|
UserId = UserId,
|
||||||
Comment = Comment
|
Comment = Comment,
|
||||||
|
AssessmentCriterions = GetAssessmentCriterionsAsViewModel()
|
||||||
};
|
};
|
||||||
|
|
||||||
public void UpdateCriterions(CandidateReviewDatabase context, AssessmentBindingModel model)
|
public void UpdateCriterions(CandidateReviewDatabase context, AssessmentBindingModel model)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using CandidateReviewContracts.ViewModels;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace CandidateReviewDatabaseImplement.Models
|
namespace CandidateReviewDatabaseImplement.Models
|
||||||
{
|
{
|
||||||
@ -13,5 +14,13 @@ namespace CandidateReviewDatabaseImplement.Models
|
|||||||
public int Value { get; set; }
|
public int Value { get; set; }
|
||||||
public virtual Assessment Assessment { get; set; } = new();
|
public virtual Assessment Assessment { get; set; } = new();
|
||||||
public virtual Criterion Criterion { get; set; } = new();
|
public virtual Criterion Criterion { get; set; } = new();
|
||||||
|
|
||||||
|
public AssessmentCriterionViewModel GetViewModel => new()
|
||||||
|
{
|
||||||
|
Id = Id,
|
||||||
|
AssessmentId = AssessmentId,
|
||||||
|
CriterionId = CriterionId,
|
||||||
|
Value = Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,30 +25,13 @@ namespace CandidateReviewRestApi.Controllers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
return _logic.ReadElement(new AssessmentSearchModel
|
return _logic.ReadElement(new AssessmentSearchModel
|
||||||
{
|
|
||||||
Id = id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Ошибка получения оценки");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public List<AssessmentViewModel>? ListByUserId(int id)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _logic.ReadList(new AssessmentSearchModel
|
|
||||||
{
|
{
|
||||||
UserId = id
|
UserId = id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Ошибка получения оценок");
|
_logger.LogError(ex, "Ошибка получения оценки");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,12 +54,12 @@ namespace CandidateReviewRestApi.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public int? Create(AssessmentBindingModel model)
|
public IActionResult Create(AssessmentBindingModel model)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int? id = _logic.Create(model);
|
int? id = _logic.Create(model);
|
||||||
return id;
|
return Ok(new AssessmentBindingModel { Id = (int)id });
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -84,53 +67,6 @@ namespace CandidateReviewRestApi.Controllers
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[HttpPost]
|
|
||||||
public void AddCriterionToAssessment(Dictionary<int, (ICriterionModel, int)> model)
|
|
||||||
{
|
|
||||||
if (model == null || model.Count == 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Модель данных не должна быть пустой.", nameof(model));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (var kvp in model)
|
|
||||||
{
|
|
||||||
var id = kvp.Key;
|
|
||||||
var (criterion, value) = kvp.Value;
|
|
||||||
|
|
||||||
if (criterion == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!model.ContainsKey(id))
|
|
||||||
{
|
|
||||||
model.Add(id, (criterion, value));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
model[id] = (criterion, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var success = _logic.AddCriterionToAssessment(
|
|
||||||
new AssessmentSearchModel { Id = id },
|
|
||||||
criterion,
|
|
||||||
value
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
throw new Exception("Ошибка добавления критерия.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Ошибка добавления критериев.");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public void Update(AssessmentBindingModel model)
|
public void Update(AssessmentBindingModel model)
|
||||||
|
Loading…
Reference in New Issue
Block a user