поиск вакансий, вывод сотрудников и добавление сотрудников, новая роль Администратор, мелкие правки

This commit is contained in:
2024-11-23 21:39:18 +04:00
parent 525668cb62
commit be517e47e4
20 changed files with 415 additions and 160 deletions

View File

@@ -3,19 +3,18 @@
@{
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 g-4">
<div class="col-md-4">
<div class="card">
<img src="@(Model.LogoFilePath ?? "https://static.thenounproject.com/png/2504969-200.png")"
class="card-img-top img-fluid rounded" alt="Логотип компании">
<img src="@(Model.LogoFilePath ?? "https://static.thenounproject.com/png/2504969-200.png")" style="max-width: 150px; max-height: 150px;" class="card-img-top img-fluid rounded-circle mx-auto d-block" alt="Логотип компании">
<div class="card-body">
<h5 class="card-title">@Model.Name</h5>
<p class="card-text">@(Model.Description == null ? "Описание отсутствует" : Model.Description) </p>
<a href="@(Model.Website ?? "#")" target="_blank" class="btn btn-primary mt-2">@(Model.Website ?? "Веб-сайт отсутствует")</a> </a>
<a href="@(Model.Website ?? "#")" target="_blank" class="btn btn-primary mt-2">@(Model.Website != null ? "Официальный сайт" : "Веб-сайт отсутствует")</a> </a>
</div>
</div>
</div>
@@ -48,30 +47,36 @@
}
</div>
<div class="card-body">
@if (@Model.Vacancies != null && @Model.Vacancies.Any())
@if (Model.Vacancies != null && Model.Vacancies.Any())
{
<table class="table table-striped">
<thead>
<tr>
<th>Название</th>
<th>Тип занятости</th>
<th>Зарплата</th>
<th>Статус</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
@foreach (var vacancy in @Model.Vacancies)
@foreach (var vacancy in Model.Vacancies)
{
<tr>
<td>@vacancy.JobTitle</td>
<td>@vacancy.JobType</td>
<td>@vacancy.Salary</td>
<td>@vacancy.Status</td>
<td>
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="btn btn-sm btn-info">Просмотр</a>
<a asp-action="VacancyDetails" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-info" title="Просмотр">
<i class="bi bi-eye"></i>
</a>
@if (userRole)
{
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="btn btn-sm btn-warning">Редактировать</a>
<a asp-action="DeleteVacancy" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="btn btn-sm btn-danger" onclick="return confirm('Вы уверены, что хотите удалить вакансию?');">Удалить</a>
<a asp-action="EditVacancy" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-warning" title="Редактировать">
<i class="bi bi-pencil"></i>
</a>
<a asp-action="Delete" asp-controller="Vacancy" asp-route-id="@vacancy.Id" class="text-danger" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить вакансию?');">
<i class="bi bi-trash"></i>
</a>
}
</td>
</tr>
@@ -85,6 +90,70 @@
}
</div>
</div>
</div>
<br />
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h2>Сотрудники компании</h2>
@if (userRole)
{
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-companyId="@Model.Id" class="btn btn-success">Добавить сотрудника</a>
}
</div>
<div class="card-body">
@if (Model.Employees != null && Model.Employees.Any())
{
<table class="table table-striped">
<thead>
<tr>
<th>Фамилия</th>
<th>Имя</th>
<th>Эл. почта</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
@foreach (var employee in Model.Employees)
{
<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="text-info" title="Просмотр">
<i class="bi bi-eye"></i>
</a>
@if (userRole)
{
<a asp-action="UserProfileEdit" asp-controller="User" asp-route-id="@employee.Id" class="text-warning" title="Редактировать">
<i class="bi bi-pencil"></i>
</a>
<a asp-action="DeleteEmployee" asp-controller="User" asp-route-id="@employee.Id" class="text-danger" title="Удалить" onclick="return confirm('Вы уверены, что хотите удалить сотрудника?');">
<i class="bi bi-trash"></i>
</a>
}
</td>
</tr>
}
</tbody>
</table>
@* <nav>
<ul class="pagination justify-content-center">
@for (int i = 1; i <= pageCount; i++)
{
<li class="page-item @(i == pageNumber ? "active" : "")">
<a class="page-link" asp-action="CompanyProfile" asp-controller="Company" asp-route-id="@Model.Id" asp-route-pageNumber="@i">
@i
</a>
</li>
}
</ul>
</nav> *@
}
else
{
<p>Сотрудников нет.</p>
}
</div>
</div>
</div>
</div>

View File

@@ -7,6 +7,7 @@
<title>@ViewData["Title"] - CandidateReviewClientApp</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" asp-append-version="true">
<link rel="stylesheet" href="~/CandidateReviewClientApp.styles.css" asp-append-version="true" />
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@@ -21,10 +22,10 @@
</button>
<div class="collapse navbar-collapse" id="mainNav">
<ul class="navbar-nav ml-auto">
@if (APIClient.User?.Role == RoleEnum.Сотрудник)
@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-action="@(APIClient.User?.CompanyId == null ? "EditCompanyProfile" : "CompanyProfile")">Профиль компании</a>
<a class="nav-link text-dark" asp-area="" asp-controller="Company" asp-route-id="@(APIClient.Company?.Id)" asp-action="@(APIClient.User?.CompanyId == null ? "EditCompanyProfile" : "CompanyProfile")">Профиль компании</a>
</li>
}
<li class="nav-item">

View File

@@ -9,10 +9,7 @@
<div class="row">
<div class="col-md-4 mb-4">
<div class="card">
@if (!string.IsNullOrEmpty(@Model?.AvatarFilePath))
{
<img src="@Model?.AvatarFilePath" class="card-img-top img-fluid rounded-circle mx-auto d-block" style="max-width: 150px; max-height: 150px;" alt="Аватар пользователя">
}
<img src="@(Model.AvatarFilePath ?? "https://cdn-icons-png.flaticon.com/512/18/18601.png")" style="max-width: 150px; max-height: 150px;" class="card-img-top img-fluid rounded-circle mx-auto d-block" alt="Аватар пользователя">
<div class="card-body text-center">
<h5 class="card-title mb-0">
@Model?.Name @(string.IsNullOrEmpty(@Model?.Surname) ? "" : @Model?.Surname) @(string.IsNullOrEmpty(@Model?.LastName) ? "" : @Model?.LastName)

View File

@@ -3,6 +3,7 @@
@{
ViewData["Title"] = "Редактирование профиля";
var userRole = APIClient.User?.Role == CandidateReviewDataModels.Enums.RoleEnum.Администратор ? true : false;
}
<div class="container mt-5">
@@ -10,47 +11,69 @@
<div class="col-md-6">
<h2 class="mb-4">Редактирование профиля</h2>
<form method="post" class="needs-validation" novalidate>
<input type="hidden" name="id" value="@Model?.Id" />
<input type="hidden" name="id" value="@Model?.Id" />
@if (userRole)
{
<input type="hidden" name="companyId" value="@(APIClient.User?.CompanyId)" />
<div class="mb-3">
<label for="Name" class="form-label">Имя <span class="text-danger">*</span></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="Name" class="form-label">Имя <span class="text-danger">*</span></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="Surname" class="form-label">Фамилия <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="Surname" name="Surname" value="@Model?.Surname" required />
<div class="invalid-feedback">Пожалуйста, введите фамилию.</div>
</div>
<div class="mb-3">
<label for="Surname" class="form-label">Фамилия <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="Surname" name="Surname" value="@Model?.Surname" required />
<div class="invalid-feedback">Пожалуйста, введите фамилию.</div>
</div>
<div class="mb-3">
<label for="Email" class="form-label">Электронная почта <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="Email" name="Email" value="@Model?.Email" />
<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="Password" class="form-label">Пароль <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="Password" name="Password" value="@Model?.Password" />
<div class="invalid-feedback">Пожалуйста, введите пароль.</div>
</div>
<div class="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>
<div class="d-flex justify-content-between">
<button type="submit" class="btn btn-primary">Сохранить</button>
<a asp-controller="Company" asp-action="CompanyProfile" class="btn btn-secondary">Отмена</a>
</div>
}
else
{
<input type="hidden" name="Email" value="@Model?.Email" />
<input type="hidden" name="Password" value="@Model?.Password" />
<div class="mb-3">
<label for="LastName" class="form-label">Отчество</label>
<input type="text" class="form-control" id="LastName" name="LastName" value="@Model?.LastName" />
<div class="invalid-feedback">Пожалуйста, введите отчество.</div>
</div>
<input type="hidden" name="Email" value="@Model?.Email" />
<input type="hidden" name="Password" value="@Model?.Password" />
<input type="hidden" name="Role" value="@Model?.Role" />
<input type="hidden" name="CompanyId" value="@Model?.CompanyId" />
<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" value="@Model?.AvatarFilePath" accept=".jpg,.jpeg,.png" />
<div class="invalid-feedback">Выберите файл изображения.</div>
</div>
<div class="d-flex justify-content-between">
<button type="submit" class="btn btn-primary">Сохранить</button>
<a asp-controller="User" asp-action="UserProfile" class="btn btn-secondary">Отмена</a>
</div>
<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>
<div class="d-flex justify-content-between">
<button type="submit" class="btn btn-primary">Сохранить</button>
<a asp-controller="User" asp-action="UserProfile" class="btn btn-secondary">Отмена</a>
</div>
}
</form>
</div>
</div>
@@ -62,7 +85,7 @@
<script>
$(document).ready(function () {
$('#PhoneNumber').inputmask({
mask: '+7 (999) 999-99-99', // Adjust mask as needed
mask: '+7 (999) 999-99-99',
showMaskOnHover: false,
onincomplete: function () {
$(this).removeClass('is-valid').addClass('is-invalid');
@@ -91,6 +114,23 @@
})
})()
</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");
}

View File

@@ -29,8 +29,8 @@
</div>
<div class="col-md-6">
<label for="JobType" class="form-label">Тип занятости</label>
<select id="JobType" name="JobType" class="form-control" asp-items="@(new SelectList(Enum.GetValues(typeof(JobTypeEnum)).Cast<JobTypeEnum>()))"></select>
<label asp-for="JobType" class="form-label">Тип занятости</label>
<select asp-for="JobType" class="form-control" asp-items="@GetJobTypeSelectList()"></select>
</div>
<div class="col-md-6">
@@ -44,30 +44,32 @@
</div>
<div class="col-md-6">
<label for="Status" class="form-label">Статус вакансии</label>
<select id="Status" name="Status" class="form-control" asp-items="@(new SelectList(Enum.GetValues(typeof(VacancyStatusEnum)).Cast<VacancyStatusEnum>()))"></select>
<label asp-for="Status" class="form-label">Статус вакансии</label>
<select asp-for="Status" class="form-control" asp-items="@GetStatusSelectList()"></select>
</div>
<input type="hidden" name="createdAt" value="@Model?.CreatedAt" />
@{
var tagsString = @Model?.Tags;
var displayedTags = string.IsNullOrEmpty(tagsString) ? "" : string.Join(", ", tagsString.Split(',').Select(t => t.Trim()));
}
<div class="col-md-6">
<label for="tags" class="form-label">Тэги</label>
<input type="text" class="form-control" id="tags" name="tags" value="@displayedTags" placeholder="Введите тэги через запятую">
<input type="text" class="form-control" id="tags" name="tags" value="@Model?.Tags" placeholder="Введите тэги через пробел">
</div>
@* <input type="hidden" name="createdAt" value="@(APIClient.Company?.Vacancies.Add(Model))" />
*@
<div class="col-12">
<button type="submit" class="btn btn-primary">Сохранить</button>
</div>
</form>
</div>
@functions {
public SelectList GetJobTypeSelectList()
{
return new SelectList(Enum.GetValues(typeof(JobTypeEnum)).Cast<JobTypeEnum>());
}
public SelectList GetStatusSelectList()
{
return new SelectList(Enum.GetValues(typeof(VacancyStatusEnum)).Cast<VacancyStatusEnum>());
}
}
<script>
(function () {
'use strict'

View File

@@ -1,5 +1,5 @@
@* @using CandidateReviewContracts.ViewModels
@model VacancyViewModel
@using CandidateReviewContracts.ViewModels
@model List<VacancyViewModel?>
@{
ViewData["Title"] = "Поиск вакансий";
@@ -7,34 +7,66 @@
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="col-md-8">
<h1>Поиск вакансий</h1>
<form asp-action="Search" asp-controller="Vacancy" method="get">
<form asp-action="SearchVacancies" asp-controller="Vacancy" asp-route-tags="tags" method="get">
<div class="input-group mb-3">
<input type="text" class="form-control" name="searchTerm" placeholder="Введите поисковый запрос" value="@Model?.SearchTerm">
<button class="btn btn-primary" type="submit">Поиск</button>
<input type="text" class="form-control" name="tags" id="tags" placeholder="Введите поисковый запрос">
<button class="btn btn-primary" type="submit">Поиск</button>
</div>
@if (ViewBag.Message != null)
{
<p class="alert alert-warning">@ViewBag.Message</p>
}
</form>
@if (@Model != null && @Model.Vacancies != null)
@if (Model != null)
{
<h2>Результаты поиска:</h2>
@if (Model.Vacancies.Count > 0)
@if (Model.Any(v => v != null))
{
<ul>
@foreach (var vacancy in Model.Vacancies)
{
<li>@vacancy.JobTitle</li>
}
</ul>
<table class="table table-striped">
<thead>
<tr>
<th>Название вакансии</th>
<th>Тип работы</th>
<th>Зарплата</th>
<th>Тэги</th>
</tr>
</thead>
<tbody>
@foreach (var vacancy in Model.Where(v => v != null))
{
<tr>
<td>@vacancy.JobTitle</td>
<td>@vacancy.JobType</td>
<td>@vacancy.Salary</td>
<td>@vacancy.Tags</td>
</tr>
}
</tbody>
</table>
}
else
{
<p>Вакансий не найдено.</p>
}
}
else
{
<p>Произошла ошибка при получении данных.</p>
}
</div>
</div>
</div>
*@
<script>
document.querySelector('form').addEventListener('submit', function (event) {
const tagsInput = document.getElementById('tags');
if (tagsInput.value.trim() === "") {
alert("Пожалуйста, введите поисковый запрос.");
event.preventDefault();
}
});
</script>

View File

@@ -4,20 +4,25 @@
@{
ViewData["Title"] = "Информация о вакансии";
var companyName = APIClient.Company?.Name ?? "Компания не определена";
}
<div class="container mt-5">
<div class="card">
<div class="d-flex justify-content-between mb-3">
<div class="card-header">
<h2>@Model.JobTitle</h2>
<input type="hidden" name="id" value="@Model?.Id" />
</div>
<a asp-action="CompanyProfile" asp-controller="Company" class="btn btn-secondary">Назад</a>
</div>
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-6">
<dl class="row">
<dt class="col-sm-4">Компания:</dt>
<dd class="col-sm-8">@Model.CompanyId</dd>
<dd class="col-sm-8">@companyName</dd>
<dt class="col-sm-4">Тип занятости:</dt>
<dd class="col-sm-8">@Model.JobType</dd>
@@ -38,10 +43,10 @@
<div class="col-md-6">
<dl class="row">
<dt class="col-sm-4">Требования:</dt>
<dd class="col-sm-8">@Html.Raw(Model.Requirements)</dd>
<dd class="col-sm-8">@Html.Raw(Model.Requirements)</dd>
<dt class="col-sm-4">Обязанности:</dt>
<dd class="col-sm-8">@Html.Raw(Model.Responsibilities)</dd>
<dd class="col-sm-8">@Html.Raw(Model.Responsibilities)</dd>
<dt class="col-sm-4">Описание:</dt>
<dd class="col-sm-8">@Html.Raw(Model.Description)</dd>
@@ -50,5 +55,4 @@
</div>
</div>
</div>
</div>
</div>