добавлены стили, мелкие правки

оценка почти но никак
This commit is contained in:
Татьяна Артамонова 2024-12-13 04:12:54 +04:00
parent 76c13d303a
commit fd31ade855
36 changed files with 860 additions and 390 deletions

View File

@ -67,40 +67,15 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
public List<AssessmentViewModel>? ReadList(AssessmentSearchModel? model)
{
var list = model == null ? _assessmentStorage.GetFullList() : _assessmentStorage.GetFilteredList(model);
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)
if (list == null)
{
_logger.LogWarning("ReadList return null list");
return null;
}
_logger.LogInformation("ReadList. Count: {Count}", result.Count);
return result;
_logger.LogInformation("ReadList. Count: {Count}", list.Count);
return list;
}
public bool Update(AssessmentBindingModel model)

View File

@ -64,6 +64,7 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
{
Id = a.Id,
ResumeId = a.ResumeId,
UserName = a.UserName,
UserId = a.UserId,
Comment = a.Comment,
AssessmentCriterions = a.AssessmentCriterions
@ -107,6 +108,7 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
Id = a.Id,
ResumeId = a.ResumeId,
UserId = a.UserId,
UserName = a.UserName,
Comment = a.Comment,
AssessmentCriterions = a.AssessmentCriterions
}).ToList() ?? new List<AssessmentViewModel>();

View File

@ -104,6 +104,12 @@ namespace CandidateReviewBusinessLogic.BusinessLogic
_logger.LogWarning("ReadList return null list");
return null;
}
foreach (var item in list)
{
var companyName = _companyStorage.GetElement(new CompanySearchModel { Id = item.CompanyId }).Name;
item.CompanyName = companyName;
}
_logger.LogInformation("ReadList. Count: {Count}", list.Count);
return list;
}

View File

@ -5,6 +5,8 @@ using CandidateReviewContracts.ViewModels;
using CandidateReviewDataModels.Models;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using System.Reflection;
using System.Security.Cryptography.Xml;
namespace CandidateReviewClientApp.Controllers
{
@ -22,7 +24,7 @@ namespace CandidateReviewClientApp.Controllers
[HttpPost]
public async Task<IActionResult> AddAssessmentCriterion(int resumeId, int[] criterion, int[] value, string comment)
{
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
string returnUrl = HttpContext.Request.Headers.Referer.ToString();
try
{
@ -35,22 +37,54 @@ namespace CandidateReviewClientApp.Controllers
{
throw new ArgumentException("Массивы критериев и оценок должны быть не null и одинаковой длины.");
}
Dictionary<int, (CriterionModel, int)> assCriterions = new();
var userId = APIClient.User?.Id;
var assessmentData = new Dictionary<int, (ICriterionModel, int)>();
var assessmentModel = new AssessmentBindingModel
{
ResumeId = resumeId,
UserId = userId,
Comment = comment,
AssessmentCriterions = criterion.Select((t, i) => new KeyValuePair<int, (ICriterionModel, int)>
(t, (new CriterionViewModel { Id = t }, value[i]))).ToDictionary(kv => kv.Key, kv => kv.Value)
AssessmentCriterions = assCriterions
};
int assessmentId = await APIClient.PostRequestAsync("api/assessment/create", assessmentModel);
for (int i = 0; i < criterion.Length; i++)
{
var crit = APIClient.GetRequest<CriterionViewModel>($"api/criterion/details?id={criterion[i]}");
assCriterions.Add(assessmentId, (new CriterionModel { Id = crit.Id, Name = crit.Name }, value[i]));
}
assessmentModel.AssessmentCriterions = assCriterions;
assessmentModel.Id = assessmentId;
APIClient.PostRequest("api/assessment/update", assessmentModel);
var user = APIClient.GetRequest<UserViewModel>($"api/user/profile?id={APIClient.User?.Id}");
APIClient.User?.Assessments.Add(new AssessmentViewModel
{
ResumeId = assessmentModel.ResumeId,
UserId = assessmentModel.UserId,
Comment = assessmentModel.Comment,
AssessmentCriterions = assessmentModel.AssessmentCriterions
});
var resume = APIClient.GetRequest<ResumeViewModel>($"api/resume/details?id={resumeId}");
if (resume != null)
{
resume?.Assessments.Add(new AssessmentViewModel
{
ResumeId = assessmentModel.ResumeId,
UserId = assessmentModel.UserId,
Comment = assessmentModel.Comment,
AssessmentCriterions = assessmentModel.AssessmentCriterions
});
}
else
{
throw new Exception("Пользователь не найден");
}
return Redirect($"~/Resume/ResumeDetails/{resumeId}");
}
catch (Exception ex)
@ -59,6 +93,22 @@ namespace CandidateReviewClientApp.Controllers
}
}
public IActionResult Delete(int id, int resumeId)
{
string returnUrl = HttpContext.Request.Headers["Referer"].ToString();
try
{
APIClient.PostRequest($"api/assessment/delete", new AssessmentBindingModel { Id = id });
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)
{

View File

@ -105,7 +105,15 @@ namespace CandidateReviewClientApp.Controllers
{
APIClient.User?.Resumes.Add(new ResumeViewModel
{
Id = model.Id
Id = model.Id,
Description = model.Description,
Education = model.Education,
Experience = model.Experience,
Skills = model.Skills,
Status = model.Status,
Title = model.Title,
UserId = model.UserId,
VacancyId = model.VacancyId
});
}
else
@ -116,7 +124,15 @@ namespace CandidateReviewClientApp.Controllers
{
vacancy.Resumes.Add(new ResumeViewModel
{
Id = model.Id
Id = model.Id,
Description = model.Description,
Education = model.Education,
Experience = model.Experience,
Skills = model.Skills,
Status = model.Status,
Title = model.Title,
UserId = model.UserId,
VacancyId = model.VacancyId
});
}
else

View File

@ -43,7 +43,11 @@ namespace CandidateReviewClientApp.Controllers
if (!id.HasValue)
{
return View(new UserViewModel());
var model = new UserViewModel
{
CompanyId = APIClient.Company.Id
};
return View(model);
}
else if (id.HasValue)
{
@ -68,7 +72,7 @@ namespace CandidateReviewClientApp.Controllers
APIClient.PostRequest("api/user/update", model);
}
else
{
{
APIClient.PostRequest("api/user/register", model);
if (APIClient.Company != null)
{

View File

@ -30,6 +30,17 @@ namespace CandidateReviewClientApp.Controllers
return View();
}
[HttpGet]
public IActionResult Vacancies(int? companyId)
{
if (APIClient.User == null)
{
return Redirect("~/Home/Enter");
}
var vacancies = APIClient.GetRequest<List<VacancyViewModel>?>($"api/vacancy/list?companyId={companyId}");
return View(vacancies);
}
[HttpGet]
public IActionResult EditVacancy(int? id)
{

View File

@ -3,6 +3,7 @@
@{
ViewData["Title"] = "Профиль компании";
var isAdmin = APIClient.User.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор ? true : false;
}
<div class="container mt-5">
@ -48,20 +49,54 @@
<div class="card-header d-flex justify-content-between align-items-center">
<h2>Вакансии компании</h2>
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-companyId="@Model.Id"
class="btn btn-success">Добавить вакансию</a>
class="btn btn-success"> <i class="bi bi-plus-circle me-2"></i> Добавить вакансию</a>
</div>
<div class="card-body">
@if (Model.Vacancies != null && Model.Vacancies.Any())
{
@await Html.PartialAsync("_VacanciesTable", Model.Vacancies.Take(5))
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead class="table-light">
<tr>
<th>Название вакансии</th>
<th>Статус</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
@foreach (var vacancy in Model.Vacancies.Take(5))
{
<tr>
<td>@vacancy.JobTitle</td>
<td>@vacancy.Status</td>
<td>
<div class="d-flex justify-content-center">
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id"
class="btn btn-info btn-sm me-2" title="Просмотр">
<i class="bi bi-eye"></i>
</a>
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-id="@vacancy.Id"
class="btn btn-warning btn-sm me-2" title="Редактировать">
<i class="bi bi-pencil-square"></i>
</a>
<a asp-action="Delete" asp-controller="Vacancy" asp-route-id="@vacancy.Id"
class="btn btn-danger btn-sm" title="Удалить"
onclick="return confirm('Вы уверены, что хотите удалить вакансию?');">
<i class="bi bi-trash"></i>
</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
@if (Model.Vacancies.Count() > 5)
{
<button id="showAllVacanciesBtn" class="btn btn-primary" onclick="toggleVacancies()">Показать все</button>
<a asp-action="Vacancies" asp-controller="Vacancy" asp-route-companyId="@Model.Id"
class="btn btn-primary mt-3">Посмотреть все</a>
}
<div id="allVacancies" style="display:none;">
@await Html.PartialAsync("_VacanciesTable", Model.Vacancies)
</div>
}
else
{
@ -73,44 +108,50 @@
<div class="card shadow-sm">
<div class="card-header d-flex justify-content-between align-items-center">
<h2>Сотрудники компании</h2>
@if (@isAdmin) {
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-companyId="@Model.Id"
class="btn btn-success">Добавить сотрудника</a>
class="btn btn-success"> <i class="bi bi-plus-circle me-2"></i> Добавить сотрудника</a>
}
</div>
<div class="card-body">
@if (Model.Employees != null && Model.Employees.Any())
{
<table class="table table-striped">
<thead>
<tr>
<th>Фамилия</th>
<th>Имя</th>
<th>Эл. почта</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
@foreach (var employee in Model.Employees)
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead class="table-light">
<tr>
<th>Фамилия</th>
<th>Имя</th>
<th>Эл. почта</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
@foreach (var employee in Model.Employees.Take(5))
{
<tr>
<td>@employee.Surname</td>
<td>@employee.Name</td>
<td>@employee.Email</td>
<td>
<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> Просмотр
<a asp-action="UserProfile" asp-controller="User" asp-route-id="@employee.Id" class="btn btn-info btn-sm me-2" title="Просмотр">
<i class="bi bi-eye"></i>
</a>
<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 asp-action="UserProfileEdit" asp-controller="User" asp-route-id="@employee.Id" class="btn btn-warning btn-sm me-2" title="Редактировать">
<i class="bi bi-pencil"></i>
</a>
<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 asp-action="DeleteEmployee" asp-controller="User" asp-route-id="@employee.Id" class="btn btn-danger btn-sm me-2" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить сотрудника?');">
<i class="bi bi-trash"></i>
</a>
</td>
</tr>
}
</tbody>
</table>
</tbody>
</table>
</div>
}
else
{
@ -121,20 +162,19 @@
</div>
</div>
</div>
<style>
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
@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 = 'Показать все';
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</script>
}
</style>

View File

@ -78,4 +78,20 @@
})
})()
</script>
<style>
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</style>

View File

@ -70,4 +70,20 @@
</form>
</div>
</div>
</div>
</div>
<style>
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</style>

View File

@ -3,40 +3,28 @@
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">
<div class="card shadow-sm">
<div class="card-header">
<h3 class="text-center">Вход в приложение</h3>
<div class="card shadow-lg rounded">
<div class="card-header bg-primary text-white text-center rounded-top">
<h3 class="mb-0">Вход в приложение</h3>
</div>
<div class="card-body">
<div class="card-body p-4">
<form method="post">
<div class="form-group">
<label for="login">Логин:</label>
<div class="form-group mb-3">
<label for="login" class="form-label">Логин:</label>
<input type="text" class="form-control" id="login" name="login" required>
</div>
<div class="form-group">
<label for="password">Пароль:</label>
<div class="form-group mb-3">
<label for="password" class="form-label">Пароль:</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="form-group text-center mt-3">
<button type="submit" class="btn btn-primary">Войти</button>
<div class="form-group text-center mt-4">
<button type="submit" class="btn btn-primary w-100 py-2">Войти</button>
</div>
<div class="form-group text-center mt-2">
<a asp-area="" asp-controller="Home" asp-action="Register" class="text-muted">Ещё не зарегистрированы? Регистрация</a>
<div class="form-group text-center mt-3">
<a asp-area="" asp-controller="Home" asp-action="Register" class="text-muted text-decoration-none">Ещё не зарегистрированы? Регистрация</a>
</div>
</form>
</div>
@ -44,4 +32,54 @@
</div>
</div>
</div>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f0f4f7;
}
.card {
border-radius: 15px;
}
.card-header {
background-color: #f0f4f7; /* Цвет фона формы */
border-top-left-radius: 15px;
border-top-right-radius: 15px;
border-bottom: 1px solid #ddd; /* Тонкая линия, чтобы отделить заголовок от содержимого */
}
.form-control {
border-radius: 10px;
box-shadow: none;
transition: all 0.3s ease;
}
.form-control:focus {
border-color: #0A1128;
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
}
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
.text-muted {
font-size: 14px;
}
.text-muted:hover {
text-decoration: underline;
}
</style>

View File

@ -7,18 +7,21 @@
<p>Здесь вы можете:</p>
<div class="d-flex justify-content-center mt-4">
<div class="box">
<h3>Создать профиль компании</h3>
</div>
<div class="box">
<h3>Подобрать кандидатов на вакансию</h3>
</div>
<div class="box">
<h3>Найти подходящую вакансию</h3>
</div>
<div class="box">
<h3>Оценить кандидатов на вакансию</h3>
</div>
<div class="box">
<i class="bi bi-building" style="font-size: 2rem; color: #007bff;"></i>
<h3 class="mt-3">Создать профиль компании</h3>
</div>
<div class="box">
<i class="bi bi-search" style="font-size: 2rem; color: #007bff;"></i>
<h3 class="mt-3">Подобрать кандидатов на вакансию</h3>
</div>
<div class="box">
<i class="bi bi-briefcase" style="font-size: 2rem; color: #007bff;"></i>
<h3 class="mt-3">Найти подходящую вакансию</h3>
</div>
<div class="box">
<i class="bi bi-people" style="font-size: 2rem; color: #007bff;"></i>
<h3 class="mt-3">Оценить кандидатов на вакансию</h3>
</div>
</div>
@ -30,6 +33,10 @@
gap: 20px;
}
.box i {
margin-bottom: 10px;
}
.box {
border: 1px solid #ccc;
border-radius: 8px;

View File

@ -6,8 +6,8 @@
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow-sm">
<div class="card-header">
<div class="card shadow-lg rounded">
<div class="card-header bg-primary text-white text-center rounded-top">
<h3 class="text-center">Регистрация</h3>
</div>
<div class="card-body">
@ -36,11 +36,62 @@
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</div>
<div class="form-group text-center mt-2">
<a asp-area="" asp-controller="Home" asp-action="Enter" class="text-muted">Уже зарегистрированы? Войти</a>
<a asp-area="" asp-controller="Home" asp-action="Enter" class="text-muted text-decoration-none">Уже зарегистрированы? Войти</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f0f4f7;
}
.card {
border-radius: 15px;
}
.card-header {
border-top-left-radius: 15px;
border-top-right-radius: 15px;
}
.form-control {
border-radius: 10px;
box-shadow: none;
transition: all 0.3s ease;
}
.form-control:focus {
border-color: #0A1128;
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
}
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
.text-muted {
font-size: 14px;
}
.text-muted:hover {
text-decoration: underline;
}
</style>

View File

@ -40,12 +40,6 @@
<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.Черновик)
{
@ -54,6 +48,10 @@
@if (Model.Id <= 0)
{
<button type="submit" formmethod="post" class="btn btn-secondary" name="isDraft" value="true">Сохранить черновик</button>
}
else
{
<button type="submit" class="btn btn-primary">Сохранить</button>
}
<button class="btn btn-secondary" onclick="window.history.back();">Назад</button>
</div>
@ -78,4 +76,20 @@
}, false)
})
})()
</script>
</script>
<style>
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</style>

View File

@ -7,16 +7,14 @@
@{
ViewData["Title"] = "Резюме";
bool userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Сотрудник || APIClient.User.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор;
bool isAdmin = APIClient.User.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор ? true : false;
var assessmentCriterions = new Dictionary<int, (ICriterionModel, int)>();
}
<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>
@ -64,6 +62,14 @@
{
<h2 class="mb-4">Оценка резюме</h2>
@if (isAdmin)
{
<div class="mt-4">
<a asp-action="ManageCriterions" asp-controller="Criterion" class="btn btn-outline-secondary">Управление критериями</a>
</div>
<br></br>
}
<form method="post" asp-controller="Assessment" asp-action="AddAssessmentCriterion">
<input type="hidden" name="resumeId" value="@Model.Id" />
@ -85,11 +91,11 @@
<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>
<option value="@criterion.Value.Item1.Id" selected>@criterion.Value.Item1.Name</option>
</select>
</div>
<div class="col-md-4">
<input type="number" name="value" class="form-control" value="@criterion.Value" readonly />
<input type="number" name="value" class="form-control" value="@criterion.Value.Item2" readonly />
</div>
</div>
}
@ -111,15 +117,20 @@
<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>
<option value="@criterion.Value.Item1.Id" selected>@criterion.Value.Item1.Name</option>
</select>
</div>
<div class="col-md-4">
<input type="number" name="value" class="form-control" value="@criterion.Value" readonly />
<input type="number" name="value" class="form-control" value="@criterion.Value.Item2" readonly />
</div>
</div>
}
<p>Комментарий: @userAssessment.Comment</p>
<a asp-controller="Assessment" asp-action="Delete" asp-route-id="@userAssessment.Id" asp-route-resumeId="@Model.Id" onclick="return confirm('Вы уверены, что хотите удалить оценку?');"
class="btn btn-info btn-sm me-2" title="Просмотр">
Удалить оценку
</a>
</div>
}
else
@ -160,11 +171,8 @@
}
</div>
</form>
<div class="mt-4">
<a asp-action="ManageCriterions" asp-controller="Criterion" class="btn btn-outline-secondary">Управление критериями</a>
</div>
}
</div>
<script>
@ -214,3 +222,20 @@
document.querySelectorAll('.remove-criterion').forEach(button => attachRemoveEvent(button));
</script>
<style>
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</style>

View File

@ -14,25 +14,25 @@
</head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<nav class="navbar navbar-expand-lg navbar-dark shadow-md">
<div class="container">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Candidate Review</a>
<button class="navbar-toggler" type="button" onclick="toggleNavbar()" aria-controls="mainNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainNav">
<ul class="navbar-nav ml-auto">
<ul class="navbar-nav ms-auto">
@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.Company == null ? "EditCompanyProfile" : "CompanyProfile")">Профиль компании</a>
<a class="nav-link" asp-area="" asp-controller="Company" asp-route-id="@(APIClient.Company?.Id)" asp-action="@(APIClient.Company == null ? "EditCompanyProfile" : "CompanyProfile")">Профиль компании</a>
</li>
}
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="User" asp-action="UserProfile" asp-route-id="@(APIClient.User?.Id)">Профиль</a>
<a class="nav-link" asp-area="" asp-controller="User" asp-action="UserProfile" asp-route-id="@(APIClient.User?.Id)">Профиль</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Vacancy" asp-action="SearchVacancies">Поиск вакансий</a>
<a class="nav-link" asp-area="" asp-controller="Vacancy" asp-action="SearchVacancies">Поиск вакансий</a>
</li>
</ul>
</div>
@ -58,5 +58,25 @@
navbar.classList.toggle('show');
}
</script>
<style>
.navbar {
background-color: #0A1128;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.navbar .nav-link {
font-size: 1rem;
transition: color 0.2s ease-in-out;
}
.navbar .nav-link:hover {
color: #D9D9D9;
}
.navbar-brand {
font-size: 1.25rem;
}
</style>
</body>
</html>

View File

@ -1,33 +0,0 @@
@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="btn btn-info btn-sm rounded-pill" title="Просмотр">
<i class="bi bi-eye"></i>
</a>
<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>
</a>
<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>
</a>
</td>
</tr>
}
</tbody>
</table>

View File

@ -0,0 +1,101 @@
@using CandidateReviewContracts.ViewModels
@model List<UserViewModel>
@{
ViewData["Title"] = "Сотрудники компании";
}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-12">
<h1 class="mb-4 text-center">Сотрудники</h1>
<div class="mb-4 text-end">
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-companyId="@APIClient.Company.Id" class="btn btn-success">
<i class="bi bi-plus-circle me-2"></i> Добавить сотрудника
</a>
</div>
@if (Model != null)
{
@if (Model.Any())
{
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead class="table-light">
<tr>
<th>Фамилия</th>
<th>Имя</th>
<th>Эл. почта</th>
<th>Номер телефона</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
@foreach (var employee in Model)
{
<tr>
<td>@employee.Surname</td>
<td>@employee.Name</td>
<td>@employee.Email</td>
<td>@employee.PhoneNumber</td>
<td>
<a asp-action="UserProfile" asp-controller="User" asp-route-id="@employee.Id" class="btn btn-info btn-sm me-2" title="Просмотр">
<i class="bi bi-eye"></i> Просмотр
</a>
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-id="@employee.Id" class="btn btn-warning btn-sm me-2" title="Редактировать">
<i class="bi bi-pencil"></i> Редактировать
</a>
<a asp-action="Delete" asp-controller="User" asp-route-id="@employee.Id" class="btn btn-danger btn-sm me-2" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить сотрудника?');">
<i class="bi bi-trash"></i> Удалить
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<p class="text-center text-muted">Сотрудников нет.</p>
}
}
else
{
<p class="text-center text-danger">Произошла ошибка при получении данных.</p>
}
</div>
</div>
</div>
<style>
.table-hover tbody tr:hover {
background-color: #f1f1f1;
}
.btn-info, .btn-warning, .btn-danger {
display: flex;
align-items: center;
justify-content: center;
}
.btn-info i, .btn-warning i, .btn-danger i {
margin-right: 5px;
}
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</style>

View File

@ -3,54 +3,68 @@
@{
ViewData["Title"] = "Профиль пользователя";
var userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Пользователь ? true : false;
var userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Пользователь || APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор ? true : false;
}
<div class="container mt-5">
<div class="row">
<div class="col-md-4 mb-4">
<div class="card shadow-sm">
<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="row justify-content-center">
<div class="col-md-6">
<div class="card shadow-lg p-4 rounded">
<div class="text-center">
<img src="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>
<div class="card-body text-center">
<h5 class="card-title mb-0">
@(string.IsNullOrEmpty(@Model?.Surname) ? "" : @Model?.Surname) @Model?.Name @(string.IsNullOrEmpty(@Model?.LastName) ? "" : @Model?.LastName)
</h5>
<h3 class="card-title mb-2">
@(string.IsNullOrEmpty(@Model?.Surname) ? "" : @Model?.Surname)
@Model?.Name
@(string.IsNullOrEmpty(@Model?.LastName) ? "" : @Model?.LastName)
</h3>
<dl class="row mt-3">
<dt class="col-sm-4">Email:</dt>
<dt class="col-sm-4 text-muted">Email:</dt>
<dd class="col-sm-8">@Model?.Email</dd>
@if (!string.IsNullOrEmpty(@Model?.PhoneNumber))
{
<dt class="col-sm-4">Телефон:</dt>
<dt class="col-sm-4 text-muted">Телефон:</dt>
<dd class="col-sm-8">@Model?.PhoneNumber</dd>
}
<dt class="col-sm-4">Роль:</dt>
<dt class="col-sm-4 text-muted">Роль:</dt>
<dd class="col-sm-8">@Model?.Role</dd>
</dl>
<div class="btn-group mt-3" role="group" aria-label="Действия">
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-id="@Model?.Id" class="btn btn-primary">Редактировать профиль</a>
<form asp-action="Delete" asp-controller="User" method="post" onsubmit="return confirm('Вы уверены, что хотите удалить профиль?');">
<div class="btn-group mt-4" role="group" aria-label="Действия">
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-id="@Model?.Id"
class="btn btn-outline-primary px-4 py-2 me-2">
<i class="bi bi-pencil me-2"></i>Редактировать профиль
</a>
<form asp-action="Delete" asp-controller="User" method="post"
onsubmit="return confirm('Вы уверены, что хотите удалить профиль?');" class="d-inline me-2">
<input type="hidden" name="id" value="@Model?.Id" />
<button type="submit" class="btn btn-danger">Удалить профиль</button>
<button type="submit" class="btn btn-outline-danger px-4 py-2">
<i class="bi bi-trash me-2"></i>Удалить профиль
</button>
</form>
<a asp-action="Logout" asp-controller="User" class="btn btn-secondary">Выйти</a>
<a asp-action="Logout" asp-controller="User" class="btn btn-outline-secondary px-4 py-2">
<i class="bi bi-box-arrow-right me-2"></i>Выйти
</a>
</div>
</div>
</div>
</div>
@if (userRole)
{
<div class="col-md-8">
<div class="col-md-8 mt-4">
<div class="card shadow-sm mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h2>Мои резюме</h2>
<a asp-action="CreateResume" asp-controller="Resume" class="btn btn-success">Создать резюме</a>
</div>
<div class="card-body">
@if (Model.Resumes != null && Model.Resumes.Any())
{
<table class="table table-striped">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Название</th>
@ -67,13 +81,13 @@
<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="Просмотр">
<a asp-action="ResumeDetails" asp-controller="Resume" asp-route-id="@resume.Id" class="btn btn-info btn-sm me-2" title="Просмотр">
<i class="bi bi-eye"></i>
</a>
<a asp-action="EditResume" asp-controller="Resume" asp-route-id="@resume.Id" class="text-warning" title="Редактировать">
<a asp-action="EditResume" asp-controller="Resume" asp-route-id="@resume.Id" class="btn btn-warning btn-sm me-2" 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('Вы уверены, что хотите удалить резюме?');">
<a asp-action="Delete" asp-controller="Resume" asp-route-id="@resume.Id" class="btn btn-danger btn-sm me-2" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить резюме?');">
<i class="bi bi-trash"></i>
</a>
</td>
@ -84,7 +98,7 @@
}
else
{
<p>Нет резюме.</p>
<p class="text-muted">У вас нет резюме.</p>
}
</div>
</div>
@ -106,53 +120,36 @@
margin-top: 20px;
}
.table th, .table td {
vertical-align: middle;
.table th, .table td {
vertical-align: middle;
}
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
.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;
.btn-group .btn {
border-radius: 15px; /* скругление кнопок внутри группы */
}
.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;
.btn-group button, .btn-group a {
border-radius: 15px; /* добавлено для кнопок внутри .btn-group */
}
</style>

View File

@ -2,68 +2,60 @@
@model UserViewModel
@{
var title = @Model.Id <= 0 ? "Добавить сотрудника" : "Редактировать профиль";
var title = @Model?.Id <= 0 ? "Добавить сотрудника" : "Редактировать профиль";
var userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор ? true : false;
}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<h2 class="mb-4">Редактирование профиля</h2>
<h2 class="mb-4">@title</h2>
<form method="post" class="needs-validation" novalidate>
<input type="hidden" name="id" value="@Model?.Id" />
<input type="hidden" name="password" value="@Model?.Password" />
<input type="hidden" name="CompanyId" value="@Model?.CompanyId" />
<div class="mb-3">
<label for="Surname" class="form-label">Фамилия <span class="text-danger">*</span></label>
<label for="Surname" class="form-label">Фамилия *</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>
<label for="Name" class="form-label">Имя *</label>
<input type="text" class="form-control" id="Name" name="Name" value="@Model?.Name" required />
<div class="invalid-feedback">Пожалуйста, введите имя.</div>
</div>
<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="Email" class="form-label">Электронная почта <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="Email" name="Email" value="@Model?.Email" readonly/>
<div class="invalid-feedback">Пожалуйста, введите электронную почту.</div>
<label for="Email" class="form-label">Электронная почта *</label>
<input type="email" class="form-control" id="Email" name="Email" value="@Model?.Email" @(Model?.Id < 0 ? "readonly" : "") required />
</div>
@if (Model.Id <= 0)
@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>
<label for="Password" class="form-label">Пароль *</label>
<input type="password" class="form-control" id="Password" name="Password" required />
</div>
}
else
{
<input type="hidden" name="password" value="@Model?.Password" />
}
<div class="mb-3">
<label for="PhoneNumber" class="form-label">Телефон</label>
<input type="tel" class="form-control" id="PhoneNumber" name="PhoneNumber" value="@Model?.PhoneNumber" />
<div class="invalid-feedback">Пожалуйста, введите телефон.</div>
</div>
<input type="hidden" name="Role" value="@Model?.Role" />
<input type="hidden" name="CompanyId" value="@Model?.CompanyId" />
<div class="mb-3">
<label for="AvatarFilePath" class="form-label">Аватар</label>
<input type="file" class="form-control" id="AvatarFilePath" name="AvatarFilePath" accept=".jpg,.jpeg,.png" />
<img id="avatarPreview" src="@Model?.AvatarFilePath" alt="Предварительный просмотр аватара" style="max-width: 100px; max-height: 100px;" />
<div class="invalid-feedback">Выберите файл изображения.</div>
</div>
<input type="hidden" name="Role" value="@Model?.Role" />
<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>
<button type="button" class="btn btn-secondary" onclick="window.history.back();">Назад</button>
</div>
</form>
</div>
@ -105,26 +97,26 @@
})
})()
</script>
<script>
const avatarInput = document.getElementById('AvatarFilePath');
const avatarPreview = document.getElementById('avatarPreview');
avatarInput.addEventListener('change', function (event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function (e) {
avatarPreview.src = e.target.result;
}
reader.readAsDataURL(file);
} else {
avatarPreview.src = "@Model?.AvatarFilePath";
}
});
</script>
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
<style>
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</style>

View File

@ -91,3 +91,20 @@
})()
</script>
<style>
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</style>

View File

@ -26,6 +26,7 @@
<table class="table table-striped table-bordered">
<thead class="table-light">
<tr>
<th>Компания</th>
<th>Название вакансии</th>
<th>Тип работы</th>
<th>Зарплата</th>
@ -37,6 +38,7 @@
@foreach (var vacancy in Model.Where(v => v != null))
{
<tr>
<td>@vacancy.CompanyName</td>
<td>@vacancy.JobTitle</td>
<td>@vacancy.JobType</td>
<td>@vacancy.Salary</td>
@ -87,10 +89,6 @@
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);
@ -109,17 +107,20 @@
background-color: #f9f9f9;
}
.table-light {
background-color: #f8f9fa;
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.text-muted {
font-style: italic;
}
.text-danger {
color: #dc3545;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
.text-center {
text-align: center;

View File

@ -0,0 +1,107 @@
@using CandidateReviewContracts.ViewModels
@model List<VacancyViewModel>
@{
ViewData["Title"] = "Вакансии";
}
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-12">
<h1 class="mb-4 text-center">Вакансии</h1>
<div class="mb-4 text-end">
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-companyId="@APIClient.Company?.Id" class="btn btn-success">
<i class="bi bi-plus-circle me-2"></i> Добавить вакансию
</a>
</div>
@if (Model != null)
{
@if (Model.Any())
{
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead class="table-light">
<tr>
<th>Название вакансии</th>
<th>Тип занятости</th>
<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.Salary</td>
<td>@vacancy.Tags</td>
<td>@vacancy.Status</td>
<td>
<div class="d-flex justify-content-center">
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id"
class="btn btn-info btn-sm me-2" title="Просмотр">
<i class="bi bi-eye"></i> Просмотр
</a>
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-id="@vacancy.Id"
class="btn btn-warning btn-sm me-2" title="Редактировать">
<i class="bi bi-pencil-square"></i> Редактировать
</a>
<a asp-action="Delete" asp-controller="Vacancy" asp-route-id="@vacancy.Id"
class="btn btn-danger btn-sm" title="Удалить"
onclick="return confirm('Вы уверены, что хотите удалить вакансию?');">
<i class="bi bi-trash"></i> Удалить
</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<p class="text-center text-muted">Вакансий нет.</p>
}
}
else
{
<p class="text-center text-danger">Произошла ошибка при получении данных.</p>
}
</div>
</div>
</div>
<style>
.table-hover tbody tr:hover {
background-color: #f1f1f1;
}
.btn-info, .btn-warning, .btn-danger {
display: flex;
align-items: center;
justify-content: center;
}
.btn-info i, .btn-warning i, .btn-danger i {
margin-right: 5px;
}
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</style>

View File

@ -104,3 +104,20 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.min.js"></script>
<style>
.btn {
background-color: #0A1128;
color: white;
border: none;
border-radius: 10px;
padding: 12px;
font-size: 16px;
transition: background-color 0.3s ease, transform 0.2s ease;
}
.btn:hover {
background-color: #1C3273;
transform: scale(1.05);
}
</style>

View File

@ -10,7 +10,7 @@ namespace CandidateReviewContracts.BindingModels
public string? Comment { get; set; }
public int Id { get; set; }
public Dictionary<int, (ICriterionModel, int)> AssessmentCriterions { get; set; } = new();
public Dictionary<int, (CriterionModel, int)> AssessmentCriterions { get; set; } = new();
public int? ResumeId { get; set; }
}

View File

@ -8,7 +8,6 @@ namespace CandidateReviewContracts.StoragesContracts
{
List<AssessmentViewModel> GetFullList();
List<AssessmentViewModel> GetFilteredList(AssessmentSearchModel model);
List<AssessmentCriterionViewModel>? GetAssessmentCriterions(int? assessmentId);
AssessmentViewModel? GetElement(AssessmentSearchModel model);
int? Insert(AssessmentBindingModel model);
AssessmentViewModel? Update(AssessmentBindingModel model);

View File

@ -1,17 +0,0 @@
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; }
}
}

View File

@ -14,6 +14,6 @@ namespace CandidateReviewContracts.ViewModels
public int? ResumeId { get; set; }
public List<AssessmentCriterionViewModel> AssessmentCriterions { get; set; } = new();
public Dictionary<int, (CriterionModel, int)> AssessmentCriterions { get; set; } = new();
}
}

View File

@ -28,5 +28,6 @@ namespace CandidateReviewContracts.ViewModels
public int Id { get; set; }
public List<ResumeViewModel> Resumes { get; set; } = new();
public List<AssessmentViewModel> Assessments { get; set; } = new();
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CandidateReviewDataModels.Models
{
public class CriterionModel : ICriterionModel
{
public string Name { get; set; } = string.Empty;
public int Id { get; set; }
}
}

View File

@ -1,9 +0,0 @@
namespace CandidateReviewDataModels.Models
{
public interface IAssessmentCriterionModel : IId
{
int AssessmentId { get; }
int CriterionId { get; }
int Value { get; }
}
}

View File

@ -15,10 +15,10 @@ namespace CandidateReviewDatabaseImplement.Implements
using var context = new CandidateReviewDatabase();
var element = context.Assessments
.Include(x => x.User)
.Include(x => x.Resume)
.Include(x => x.Criterions)
.ThenInclude(x => x.Criterion)
.Include(x => x.Criterions)
.ThenInclude(x => x.Criterion)
.Include(x => x.User)
.Include(x => x.Resume)
.FirstOrDefault(rec => rec.Id == model.Id);
if (element != null)
@ -62,21 +62,6 @@ namespace CandidateReviewDatabaseImplement.Implements
?.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)
{
if (model is null)
@ -123,26 +108,15 @@ namespace CandidateReviewDatabaseImplement.Implements
{
using var context = new CandidateReviewDatabase();
var newAssessment = new Assessment
var newAssessment = Assessment.Create(context, model);
if (newAssessment == null)
{
ResumeId = model.ResumeId,
UserId = model.UserId,
Comment = model.Comment
};
return null;
}
context.Assessments.Add(newAssessment);
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;
}

View File

@ -71,6 +71,15 @@ namespace CandidateReviewDatabaseImplement.Implements
.Select(x => x.GetViewModel)
.ToList();
}
if (model.CompanyId.HasValue)
{
return context.Vacancies
.Include(x => x.Company)
.Where(x => x.CompanyId == model.CompanyId)
.ToList()
.Select(x => x.GetViewModel)
.ToList();
}
if (!string.IsNullOrEmpty(model.Tags) && model.Status.HasValue)
{
var tags = model.Tags.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(t => t.ToLowerInvariant()).ToArray();

View File

@ -18,9 +18,9 @@ namespace CandidateReviewDatabaseImplement.Models
public virtual User User { get; set; }
public virtual Resume Resume { get; set; }
private Dictionary<int, (ICriterionModel, int)>? _assessmentCriterions = null;
private Dictionary<int, (CriterionModel, int)>? _assessmentCriterions = null;
public Dictionary<int, (ICriterionModel, int)> AssessmentCriterions
public Dictionary<int, (CriterionModel, int)> AssessmentCriterions
{
get
{
@ -28,7 +28,7 @@ namespace CandidateReviewDatabaseImplement.Models
{
_assessmentCriterions = Criterions
.ToDictionary(recAC => recAC.CriterionId, recAC =>
(recAC.Criterion as ICriterionModel, recAC.Value));
(new CriterionModel { Id = recAC.Criterion.Id, Name = recAC.Criterion.Name }, recAC.Value));
}
return _assessmentCriterions;
}
@ -37,22 +37,20 @@ namespace CandidateReviewDatabaseImplement.Models
[ForeignKey("AssessmentId")]
public virtual List<AssessmentCriterion> Criterions { get; set; } = new();
public static Assessment? Create(CandidateReviewDatabase context, AssessmentBindingModel model)
public static Assessment Create(CandidateReviewDatabase context, AssessmentBindingModel model)
{
if (model == null)
{
return null;
}
var assessment = new Assessment
return new Assessment()
{
Id = model.Id,
ResumeId = model.ResumeId,
UserId = model.UserId,
Comment = model.Comment,
Criterions = model.AssessmentCriterions.Select(x => new AssessmentCriterion
{
Assessment = context.Assessments.First(y => y.Id == x.Key),
Value = x.Value.Item2
}).ToList()
};
return assessment;
}
public void Update(AssessmentBindingModel model)
@ -66,15 +64,6 @@ namespace CandidateReviewDatabaseImplement.Models
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()
{
@ -82,7 +71,7 @@ namespace CandidateReviewDatabaseImplement.Models
ResumeId = ResumeId,
UserId = UserId,
Comment = Comment,
AssessmentCriterions = GetAssessmentCriterionsAsViewModel()
AssessmentCriterions = AssessmentCriterions
};
public void UpdateCriterions(CandidateReviewDatabase context, AssessmentBindingModel model)
@ -102,14 +91,20 @@ namespace CandidateReviewDatabaseImplement.Models
var assessment = context.Assessments.First(x => x.Id == Id);
foreach (var ac in model.AssessmentCriterions)
{
var criterion = context.Criterions.First(x => x.Id == ac.Value.Item1.Id);
if (criterion == null)
{
throw new Exception($"Критерий с ID {ac.Key} не найден.");
}
context.AssessmentCriterions.Add(new AssessmentCriterion
{
Assessment = assessment,
Criterion = context.Criterions.First(x => x.Id == ac.Key),
Criterion = criterion,
Value = ac.Value.Item2
});
context.SaveChanges();
}
context.SaveChanges();
_assessmentCriterions = null;
}
}

View File

@ -14,13 +14,5 @@ namespace CandidateReviewDatabaseImplement.Models
public int Value { get; set; }
public virtual Assessment Assessment { get; set; } = new();
public virtual Criterion Criterion { get; set; } = new();
public AssessmentCriterionViewModel GetViewModel => new()
{
Id = Id,
AssessmentId = AssessmentId,
CriterionId = CriterionId,
Value = Value
};
}
}

View File

@ -57,6 +57,27 @@ namespace CandidateReviewRestApi.Controllers
}
}
[HttpGet]
public List<VacancyViewModel>? List(int? companyId)
{
try
{
if (companyId.HasValue)
{
return _logic.ReadList(new VacancySearchModel
{
CompanyId = companyId
});
}
else return new List<VacancyViewModel>();
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения вакансий");
throw;
}
}
[HttpPost]
public void Create(VacancyBindingModel model)
{