AdminLTE3
This commit is contained in:
362
.history/views/admin/portfolio/list_20251026221344.ejs
Normal file
362
.history/views/admin/portfolio/list_20251026221344.ejs
Normal file
@@ -0,0 +1,362 @@
|
||||
<!-- Content Header (Page header) -->
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1><i class="fas fa-briefcase mr-2"></i>Управление портфолио</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="/admin/dashboard">Админ</a></li>
|
||||
<li class="breadcrumb-item active">Портфолио</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<!-- Search and Filter Bar -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="input-group">
|
||||
<input type="text" id="searchInput" class="form-control" placeholder="Поиск проектов...">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="searchProjects()">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select id="categoryFilter" class="form-control" onchange="filterByCategory()">
|
||||
<option value="">Все категории</option>
|
||||
<option value="web-development">Веб-разработка</option>
|
||||
<option value="mobile-app">Мобильное приложение</option>
|
||||
<option value="ui-ux-design">UI/UX дизайн</option>
|
||||
<option value="e-commerce">E-commerce</option>
|
||||
<option value="enterprise">Корпоративное</option>
|
||||
<option value="other">Другое</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select id="statusFilter" class="form-control" onchange="filterByStatus()">
|
||||
<option value="">Все статусы</option>
|
||||
<option value="published">Опубликовано</option>
|
||||
<option value="draft">Черновик</option>
|
||||
<option value="featured">Рекомендуемое</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 text-right">
|
||||
<a href="/admin/portfolio/add" class="btn btn-primary">
|
||||
<i class="fas fa-plus mr-1"></i> Добавить проект
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Portfolio Table -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Список проектов</h3>
|
||||
<div class="card-tools">
|
||||
<span class="badge badge-secondary" id="projectCount">
|
||||
Всего: <%= portfolio ? portfolio.length : 0 %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<table class="table table-hover text-nowrap">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 60px;">Фото</th>
|
||||
<th>Название</th>
|
||||
<th>Категория</th>
|
||||
<th>Статус</th>
|
||||
<th>Технологии</th>
|
||||
<th>Создано</th>
|
||||
<th style="width: 150px;">Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% if (portfolio && portfolio.length > 0) { %>
|
||||
<% portfolio.forEach(item => { %>
|
||||
<tr class="portfolio-item" data-category="<%= item.category %>" data-title="<%= item.title.toLowerCase() %>" data-status="<%= item.isPublished ? 'published' : 'draft' %><%= item.featured ? ' featured' : '' %>">
|
||||
<td>
|
||||
<% if (item.images && item.images.length > 0) { %>
|
||||
<img src="<%= item.images[0].url %>" alt="<%= item.title %>" class="img-circle img-size-32">
|
||||
<% } else { %>
|
||||
<div class="img-circle bg-secondary d-flex align-items-center justify-content-center" style="width: 32px; height: 32px;">
|
||||
<i class="fas fa-image text-white"></i>
|
||||
</div>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<strong><%= item.title %></strong>
|
||||
<% if (item.featured) { %>
|
||||
<span class="badge badge-warning ml-1">
|
||||
<i class="fas fa-star"></i>
|
||||
</span>
|
||||
<% } %>
|
||||
</div>
|
||||
<small class="text-muted"><%= item.shortDescription || 'Описание не указано' %></small>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-info">
|
||||
<%= item.category.replace('-', ' ') %>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<% if (item.isPublished) { %>
|
||||
<span class="badge badge-success">
|
||||
<i class="fas fa-check-circle mr-1"></i>Опубликовано
|
||||
</span>
|
||||
<% } else { %>
|
||||
<span class="badge badge-secondary">
|
||||
<i class="fas fa-clock mr-1"></i>Черновик
|
||||
</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<% if (item.technologies && item.technologies.length > 0) { %>
|
||||
<% item.technologies.slice(0, 2).forEach(tech => { %>
|
||||
<span class="badge badge-light mr-1"><%= tech %></span>
|
||||
<% }) %>
|
||||
<% if (item.technologies.length > 2) { %>
|
||||
<span class="text-muted">+<%= item.technologies.length - 2 %></span>
|
||||
<% } %>
|
||||
<% } else { %>
|
||||
<span class="text-muted">—</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<small class="text-muted">
|
||||
<%= new Date(item.createdAt).toLocaleDateString('ru-RU') %>
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<% if (item.isPublished) { %>
|
||||
<a href="/portfolio/<%= item.id %>" target="_blank" class="btn btn-info btn-sm" title="Просмотр">
|
||||
<i class="fas fa-external-link-alt"></i>
|
||||
</a>
|
||||
<% } %>
|
||||
<button type="button" class="btn btn-<%= item.isPublished ? 'warning' : 'success' %> btn-sm"
|
||||
onclick="togglePublish('<%= item.id %>', '<%= item.isPublished %>')"
|
||||
title="<%= item.isPublished ? 'Скрыть' : 'Опубликовать' %>">
|
||||
<i class="fas <%= item.isPublished ? 'fa-eye-slash' : 'fa-eye' %>"></i>
|
||||
</button>
|
||||
<a href="/admin/portfolio/edit/<%= item.id %>" class="btn btn-primary btn-sm" title="Редактировать">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
onclick="deletePortfolio('<%= item.id %>', '<%= item.title %>')"
|
||||
title="Удалить">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
<% } else { %>
|
||||
<tr>
|
||||
<td colspan="7" class="text-center py-4">
|
||||
<div class="text-muted">
|
||||
<i class="fas fa-briefcase fa-3x mb-3"></i>
|
||||
<p>Проекты не найдены</p>
|
||||
<a href="/admin/portfolio/add" class="btn btn-primary">
|
||||
Добавить первый проект
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Pagination -->
|
||||
<% if (pagination && pagination.total > 1) { %>
|
||||
<div class="card-footer clearfix">
|
||||
<ul class="pagination pagination-sm m-0 float-right">
|
||||
<% if (pagination.hasPrev) { %>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page=<%= pagination.current - 1 %>">«</a>
|
||||
</li>
|
||||
<% } %>
|
||||
|
||||
<% for (let i = 1; i <= pagination.total; i++) { %>
|
||||
<li class="page-item <%= pagination.current === i ? 'active' : '' %>">
|
||||
<a class="page-link" href="?page=<%= i %>"><%= i %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
|
||||
<% if (pagination.hasNext) { %>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page=<%= pagination.current + 1 %>">»</a>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
function deletePortfolio(id, title) {
|
||||
Swal.fire({
|
||||
title: 'Удалить проект?',
|
||||
text: `Вы уверены, что хотите удалить проект "${title}"?`,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#d33',
|
||||
cancelButtonColor: '#3085d6',
|
||||
confirmButtonText: 'Да, удалить!',
|
||||
cancelButtonText: 'Отмена'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
fetch(`/api/admin/portfolio/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire('Удалено!', 'Проект был удален.', 'success').then(() => {
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
Swal.fire('Ошибка!', data.message || 'Ошибка при удалении проекта', 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire('Ошибка!', 'Произошла ошибка при удалении проекта', 'error');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function togglePublish(id, currentStatus) {
|
||||
const isPublished = currentStatus === 'true';
|
||||
|
||||
fetch(`/api/admin/portfolio/${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
isPublished: !isPublished
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
toastr.success(`Проект ${!isPublished ? 'опубликован' : 'скрыт'}`);
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
toastr.error(data.message || 'Ошибка при изменении статуса');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
toastr.error('Произошла ошибка при изменении статуса');
|
||||
});
|
||||
}
|
||||
|
||||
function searchProjects() {
|
||||
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
||||
const items = document.querySelectorAll('.portfolio-item');
|
||||
let visibleCount = 0;
|
||||
|
||||
items.forEach(item => {
|
||||
const title = item.dataset.title;
|
||||
if (title.includes(searchTerm)) {
|
||||
item.style.display = 'table-row';
|
||||
visibleCount++;
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
updateProjectCount(visibleCount, items.length);
|
||||
}
|
||||
|
||||
function filterByCategory() {
|
||||
const selectedCategory = document.getElementById('categoryFilter').value;
|
||||
const selectedStatus = document.getElementById('statusFilter').value;
|
||||
filterPortfolio(selectedCategory, selectedStatus);
|
||||
}
|
||||
|
||||
function filterByStatus() {
|
||||
const selectedCategory = document.getElementById('categoryFilter').value;
|
||||
const selectedStatus = document.getElementById('statusFilter').value;
|
||||
filterPortfolio(selectedCategory, selectedStatus);
|
||||
}
|
||||
|
||||
function filterPortfolio(category, status) {
|
||||
const items = document.querySelectorAll('.portfolio-item');
|
||||
let visibleCount = 0;
|
||||
|
||||
items.forEach(item => {
|
||||
const itemCategory = item.dataset.category;
|
||||
const itemStatus = item.dataset.status;
|
||||
|
||||
let showItem = true;
|
||||
|
||||
if (category && itemCategory !== category) {
|
||||
showItem = false;
|
||||
}
|
||||
|
||||
if (status && !itemStatus.includes(status)) {
|
||||
showItem = false;
|
||||
}
|
||||
|
||||
if (showItem) {
|
||||
item.style.display = 'table-row';
|
||||
visibleCount++;
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
updateProjectCount(visibleCount, items.length);
|
||||
}
|
||||
|
||||
function updateProjectCount(visible, total) {
|
||||
const countElement = document.getElementById('projectCount');
|
||||
if (countElement) {
|
||||
if (visible !== undefined) {
|
||||
countElement.textContent = `Показано: ${visible} из ${total}`;
|
||||
} else {
|
||||
countElement.textContent = `Всего: ${total}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search on Enter key
|
||||
document.getElementById('searchInput').addEventListener('keyup', function(event) {
|
||||
if (event.key === 'Enter') {
|
||||
searchProjects();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto search on input
|
||||
document.getElementById('searchInput').addEventListener('input', function() {
|
||||
searchProjects();
|
||||
});
|
||||
</script>
|
||||
362
.history/views/admin/portfolio/list_20251026221355.ejs
Normal file
362
.history/views/admin/portfolio/list_20251026221355.ejs
Normal file
@@ -0,0 +1,362 @@
|
||||
<!-- Content Header (Page header) -->
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1><i class="fas fa-briefcase mr-2"></i>Управление портфолио</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="/admin/dashboard">Админ</a></li>
|
||||
<li class="breadcrumb-item active">Портфолио</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<!-- Search and Filter Bar -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="input-group">
|
||||
<input type="text" id="searchInput" class="form-control" placeholder="Поиск проектов...">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="searchProjects()">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select id="categoryFilter" class="form-control" onchange="filterByCategory()">
|
||||
<option value="">Все категории</option>
|
||||
<option value="web-development">Веб-разработка</option>
|
||||
<option value="mobile-app">Мобильное приложение</option>
|
||||
<option value="ui-ux-design">UI/UX дизайн</option>
|
||||
<option value="e-commerce">E-commerce</option>
|
||||
<option value="enterprise">Корпоративное</option>
|
||||
<option value="other">Другое</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select id="statusFilter" class="form-control" onchange="filterByStatus()">
|
||||
<option value="">Все статусы</option>
|
||||
<option value="published">Опубликовано</option>
|
||||
<option value="draft">Черновик</option>
|
||||
<option value="featured">Рекомендуемое</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 text-right">
|
||||
<a href="/admin/portfolio/add" class="btn btn-primary">
|
||||
<i class="fas fa-plus mr-1"></i> Добавить проект
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Portfolio Table -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Список проектов</h3>
|
||||
<div class="card-tools">
|
||||
<span class="badge badge-secondary" id="projectCount">
|
||||
Всего: <%= portfolio ? portfolio.length : 0 %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body table-responsive p-0">
|
||||
<table class="table table-hover text-nowrap">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 60px;">Фото</th>
|
||||
<th>Название</th>
|
||||
<th>Категория</th>
|
||||
<th>Статус</th>
|
||||
<th>Технологии</th>
|
||||
<th>Создано</th>
|
||||
<th style="width: 150px;">Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% if (portfolio && portfolio.length > 0) { %>
|
||||
<% portfolio.forEach(item => { %>
|
||||
<tr class="portfolio-item" data-category="<%= item.category %>" data-title="<%= item.title.toLowerCase() %>" data-status="<%= item.isPublished ? 'published' : 'draft' %><%= item.featured ? ' featured' : '' %>">
|
||||
<td>
|
||||
<% if (item.images && item.images.length > 0) { %>
|
||||
<img src="<%= item.images[0].url %>" alt="<%= item.title %>" class="img-circle img-size-32">
|
||||
<% } else { %>
|
||||
<div class="img-circle bg-secondary d-flex align-items-center justify-content-center" style="width: 32px; height: 32px;">
|
||||
<i class="fas fa-image text-white"></i>
|
||||
</div>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<strong><%= item.title %></strong>
|
||||
<% if (item.featured) { %>
|
||||
<span class="badge badge-warning ml-1">
|
||||
<i class="fas fa-star"></i>
|
||||
</span>
|
||||
<% } %>
|
||||
</div>
|
||||
<small class="text-muted"><%= item.shortDescription || 'Описание не указано' %></small>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-info">
|
||||
<%= item.category.replace('-', ' ') %>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<% if (item.isPublished) { %>
|
||||
<span class="badge badge-success">
|
||||
<i class="fas fa-check-circle mr-1"></i>Опубликовано
|
||||
</span>
|
||||
<% } else { %>
|
||||
<span class="badge badge-secondary">
|
||||
<i class="fas fa-clock mr-1"></i>Черновик
|
||||
</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<% if (item.technologies && item.technologies.length > 0) { %>
|
||||
<% item.technologies.slice(0, 2).forEach(tech => { %>
|
||||
<span class="badge badge-light mr-1"><%= tech %></span>
|
||||
<% }) %>
|
||||
<% if (item.technologies.length > 2) { %>
|
||||
<span class="text-muted">+<%= item.technologies.length - 2 %></span>
|
||||
<% } %>
|
||||
<% } else { %>
|
||||
<span class="text-muted">—</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<small class="text-muted">
|
||||
<%= new Date(item.createdAt).toLocaleDateString('ru-RU') %>
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<% if (item.isPublished) { %>
|
||||
<a href="/portfolio/<%= item.id %>" target="_blank" class="btn btn-info btn-sm" title="Просмотр">
|
||||
<i class="fas fa-external-link-alt"></i>
|
||||
</a>
|
||||
<% } %>
|
||||
<button type="button" class="btn btn-<%= item.isPublished ? 'warning' : 'success' %> btn-sm"
|
||||
onclick="togglePublish('<%= item.id %>', '<%= item.isPublished %>')"
|
||||
title="<%= item.isPublished ? 'Скрыть' : 'Опубликовать' %>">
|
||||
<i class="fas <%= item.isPublished ? 'fa-eye-slash' : 'fa-eye' %>"></i>
|
||||
</button>
|
||||
<a href="/admin/portfolio/edit/<%= item.id %>" class="btn btn-primary btn-sm" title="Редактировать">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-danger btn-sm"
|
||||
onclick="deletePortfolio('<%= item.id %>', '<%= item.title %>')"
|
||||
title="Удалить">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
<% } else { %>
|
||||
<tr>
|
||||
<td colspan="7" class="text-center py-4">
|
||||
<div class="text-muted">
|
||||
<i class="fas fa-briefcase fa-3x mb-3"></i>
|
||||
<p>Проекты не найдены</p>
|
||||
<a href="/admin/portfolio/add" class="btn btn-primary">
|
||||
Добавить первый проект
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<% } %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Pagination -->
|
||||
<% if (pagination && pagination.total > 1) { %>
|
||||
<div class="card-footer clearfix">
|
||||
<ul class="pagination pagination-sm m-0 float-right">
|
||||
<% if (pagination.hasPrev) { %>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page=<%= pagination.current - 1 %>">«</a>
|
||||
</li>
|
||||
<% } %>
|
||||
|
||||
<% for (let i = 1; i <= pagination.total; i++) { %>
|
||||
<li class="page-item <%= pagination.current === i ? 'active' : '' %>">
|
||||
<a class="page-link" href="?page=<%= i %>"><%= i %></a>
|
||||
</li>
|
||||
<% } %>
|
||||
|
||||
<% if (pagination.hasNext) { %>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page=<%= pagination.current + 1 %>">»</a>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
function deletePortfolio(id, title) {
|
||||
Swal.fire({
|
||||
title: 'Удалить проект?',
|
||||
text: `Вы уверены, что хотите удалить проект "${title}"?`,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#d33',
|
||||
cancelButtonColor: '#3085d6',
|
||||
confirmButtonText: 'Да, удалить!',
|
||||
cancelButtonText: 'Отмена'
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
fetch(`/api/admin/portfolio/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire('Удалено!', 'Проект был удален.', 'success').then(() => {
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
Swal.fire('Ошибка!', data.message || 'Ошибка при удалении проекта', 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
Swal.fire('Ошибка!', 'Произошла ошибка при удалении проекта', 'error');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function togglePublish(id, currentStatus) {
|
||||
const isPublished = currentStatus === 'true';
|
||||
|
||||
fetch(`/api/admin/portfolio/${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
isPublished: !isPublished
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
toastr.success(`Проект ${!isPublished ? 'опубликован' : 'скрыт'}`);
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
toastr.error(data.message || 'Ошибка при изменении статуса');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
toastr.error('Произошла ошибка при изменении статуса');
|
||||
});
|
||||
}
|
||||
|
||||
function searchProjects() {
|
||||
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
||||
const items = document.querySelectorAll('.portfolio-item');
|
||||
let visibleCount = 0;
|
||||
|
||||
items.forEach(item => {
|
||||
const title = item.dataset.title;
|
||||
if (title.includes(searchTerm)) {
|
||||
item.style.display = 'table-row';
|
||||
visibleCount++;
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
updateProjectCount(visibleCount, items.length);
|
||||
}
|
||||
|
||||
function filterByCategory() {
|
||||
const selectedCategory = document.getElementById('categoryFilter').value;
|
||||
const selectedStatus = document.getElementById('statusFilter').value;
|
||||
filterPortfolio(selectedCategory, selectedStatus);
|
||||
}
|
||||
|
||||
function filterByStatus() {
|
||||
const selectedCategory = document.getElementById('categoryFilter').value;
|
||||
const selectedStatus = document.getElementById('statusFilter').value;
|
||||
filterPortfolio(selectedCategory, selectedStatus);
|
||||
}
|
||||
|
||||
function filterPortfolio(category, status) {
|
||||
const items = document.querySelectorAll('.portfolio-item');
|
||||
let visibleCount = 0;
|
||||
|
||||
items.forEach(item => {
|
||||
const itemCategory = item.dataset.category;
|
||||
const itemStatus = item.dataset.status;
|
||||
|
||||
let showItem = true;
|
||||
|
||||
if (category && itemCategory !== category) {
|
||||
showItem = false;
|
||||
}
|
||||
|
||||
if (status && !itemStatus.includes(status)) {
|
||||
showItem = false;
|
||||
}
|
||||
|
||||
if (showItem) {
|
||||
item.style.display = 'table-row';
|
||||
visibleCount++;
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
updateProjectCount(visibleCount, items.length);
|
||||
}
|
||||
|
||||
function updateProjectCount(visible, total) {
|
||||
const countElement = document.getElementById('projectCount');
|
||||
if (countElement) {
|
||||
if (visible !== undefined) {
|
||||
countElement.textContent = `Показано: ${visible} из ${total}`;
|
||||
} else {
|
||||
countElement.textContent = `Всего: ${total}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search on Enter key
|
||||
document.getElementById('searchInput').addEventListener('keyup', function(event) {
|
||||
if (event.key === 'Enter') {
|
||||
searchProjects();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto search on input
|
||||
document.getElementById('searchInput').addEventListener('input', function() {
|
||||
searchProjects();
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user