закончил раздел работы с инструментом
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
background-image: url("../images/background.png");
|
background-image: url("../images/background.svg");
|
||||||
background-repeat: no-repeat;
|
background-repeat: repeat;
|
||||||
background-size: cover;
|
background-size: 512px auto;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 223 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 342 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 92 KiB |
+341
-79
@@ -173,9 +173,9 @@ function prepareTabs() {
|
|||||||
role="tabpanel">
|
role="tabpanel">
|
||||||
<div class="card border-0 shadow-sm mb-1">
|
<div class="card border-0 shadow-sm mb-1">
|
||||||
|
|
||||||
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between mb-2">
|
<div class="row d-flex flex-column flex-md-row align-items-md-center justify-content-between mb-2">
|
||||||
|
|
||||||
<div class="card-body py-2">
|
<div class="card-body py-2 col-12 col-md-3">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="bg-primary bg-opacity-10 p-3 rounded me-3">
|
<div class="bg-primary bg-opacity-10 p-3 rounded me-3">
|
||||||
<i class="${tabData.icon} fs-3 text-primary"></i>
|
<i class="${tabData.icon} fs-3 text-primary"></i>
|
||||||
@@ -187,7 +187,7 @@ function prepareTabs() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="${tabId}-tab-optional-content" class="px-4 mt-3" style="max-width: 65%;"></div>
|
<div id="${tabId}-tab-optional-content" class="px-4 mt-3 col-12 col-md-9"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1122,21 +1122,107 @@ function renderToolkitsTab(tabId, toolsList, categoriesArray) {
|
|||||||
const tabOptionalContent = document.getElementById(`${tabId}-tab-optional-content`);
|
const tabOptionalContent = document.getElementById(`${tabId}-tab-optional-content`);
|
||||||
const hiddenToolCount = toolsList.filter(tool => tool.hidden).length;
|
const hiddenToolCount = toolsList.filter(tool => tool.hidden).length;
|
||||||
|
|
||||||
|
let categoriesData = {};
|
||||||
|
categoriesArray.forEach(cat => {
|
||||||
|
categoriesData[cat.id] = { id: cat.id, title: cat.title, description: cat.description };
|
||||||
|
});
|
||||||
|
|
||||||
|
let specData = {}
|
||||||
|
|
||||||
|
toolsList.forEach(tool => {
|
||||||
|
tool['category'] = categoriesData[tool.category_id]?.title || '';
|
||||||
|
tool['category_desc'] = categoriesData[tool.category_id]?.description || '';
|
||||||
|
Object.entries(tool.specifications || {}).forEach(([name, value]) => {
|
||||||
|
if (specData[name]) {
|
||||||
|
if (!specData[name].includes(value)) {
|
||||||
|
specData[name].push(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
specData[name] = [value];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function smartCompare(a, b, locale = 'ru') {
|
||||||
|
const normalizeNumber = (v) => {
|
||||||
|
if (typeof v === 'number') return v;
|
||||||
|
|
||||||
|
if (typeof v === 'string') {
|
||||||
|
const n = v.replace(',', '.').trim();
|
||||||
|
if (!isNaN(n) && n !== '') return Number(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const numA = normalizeNumber(a);
|
||||||
|
const numB = normalizeNumber(b);
|
||||||
|
|
||||||
|
// Оба — числа
|
||||||
|
if (numA !== null && numB !== null) {
|
||||||
|
return numA - numB;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Один число, другой строка → число выше
|
||||||
|
if (numA !== null) return -1;
|
||||||
|
if (numB !== null) return 1;
|
||||||
|
|
||||||
|
// Оба строки
|
||||||
|
return String(a).localeCompare(String(b), locale, {
|
||||||
|
numeric: true,
|
||||||
|
sensitivity: 'base'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Сортируем ключи и значения specData
|
||||||
|
const sortedSpecData = Object.fromEntries(
|
||||||
|
Object.entries(specData)
|
||||||
|
.sort(([keyA], [keyB]) => smartCompare(keyA, keyB))
|
||||||
|
.map(([key, values]) => [
|
||||||
|
key,
|
||||||
|
[...values].sort((a, b) => smartCompare(a, b))
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
toolsList.sort((a, b) => a.title.localeCompare(b.title, 'ru'));
|
||||||
|
categoriesArray.sort((a, b) => a.title.localeCompare(b.title, 'ru'));
|
||||||
|
|
||||||
if (accessData.tools_creation) {
|
if (accessData.tools_creation) {
|
||||||
tabOptionalContent.innerHTML = `
|
tabOptionalContent.innerHTML = `
|
||||||
<div class="row">
|
<div class="row align-items-start">
|
||||||
<div class="col-12">
|
<!-- Категории слева -->
|
||||||
<div>
|
<div class="col-12 col-md-7 mb-3 mb-md-0">
|
||||||
|
<div class="d-flex flex-wrap gap-2">
|
||||||
|
${categoriesArray.map(category => `
|
||||||
|
<button class="btn filter-btn"
|
||||||
|
data-category="${category.id}">
|
||||||
|
${category.title}
|
||||||
|
</button>
|
||||||
|
`).join('')}
|
||||||
|
<button class="btn filter-btn active" data-category="all">
|
||||||
|
Все категории
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Управления справа -->
|
||||||
|
<div class="col-12 col-md-5 d-flex flex-column align-items-md-end gap-2">
|
||||||
|
<!-- Группа кнопок -->
|
||||||
|
<div class="btn-group" role="group" aria-label="Управление категориями и инструментами">
|
||||||
<button class="btn btn-outline-secondary" id="manageCategoryBtn">
|
<button class="btn btn-outline-secondary" id="manageCategoryBtn">
|
||||||
<i class="bi bi-gear-wide-connected me-2"></i>Категории
|
<i class="bi bi-gear-wide-connected me-2"></i>Категории
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-secondary ms-2" id="addToolBtn">
|
|
||||||
|
<button class="btn btn-outline-secondary" id="addToolBtn">
|
||||||
<i class="bi bi-plus-circle me-2"></i>Добавить инструмент
|
<i class="bi bi-plus-circle me-2"></i>Добавить инструмент
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-switch d-flex flex-column flex-md-row align-items-md-center justify-content-left mt-1">
|
|
||||||
|
<!-- Переключатель -->
|
||||||
|
<div class="form-check form-switch d-flex align-items-center justify-content-md-end">
|
||||||
<input class="form-check-input" type="checkbox" role="switch" id="showHiddenTools">
|
<input class="form-check-input" type="checkbox" role="switch" id="showHiddenTools">
|
||||||
<label class="form-check-label ms-2 text-muted" for="showHiddenTools">Отображать скрытые (${hiddenToolCount})</label>
|
<label class="form-check-label ms-2 text-muted" for="showHiddenTools">
|
||||||
|
Отображать скрытые (${hiddenToolCount})
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1160,42 +1246,55 @@ function renderToolkitsTab(tabId, toolsList, categoriesArray) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let categoriesData = {};
|
// Создаем HTML структуру с двумя выпадающими списками
|
||||||
categoriesArray.forEach(cat => {
|
|
||||||
categoriesData[cat.id] = { id: cat.id, title: cat.title, description: cat.description };
|
|
||||||
});
|
|
||||||
|
|
||||||
toolsList.forEach(tool => {
|
|
||||||
tool['category'] = categoriesData[tool.category_id]?.title || '';
|
|
||||||
tool['category_desc'] = categoriesData[tool.category_id]?.description || '';
|
|
||||||
});
|
|
||||||
|
|
||||||
toolsList.sort((a, b) => a.title.localeCompare(b.title, 'ru'));
|
|
||||||
categoriesArray.sort((a, b) => a.title.localeCompare(b.title, 'ru'));
|
|
||||||
|
|
||||||
// Создаем HTML структуру
|
|
||||||
tabContent.innerHTML = `
|
tabContent.innerHTML = `
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card border-0 shadow-sm">
|
<div class="card border-0 shadow-sm">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<!-- Блок фильтров -->
|
<!-- Блок фильтров -->
|
||||||
<div class="row mb-4">
|
<div class="row mb-4 align-items-center">
|
||||||
<div class="col-12 col-md-8 mb-3 mb-md-0">
|
<!-- Фильтры по параметрам -->
|
||||||
<div class="d-flex flex-wrap gap-2">
|
<div class="col-12 col-lg-8 mb-3 mb-lg-0">
|
||||||
<button class="btn filter-btn active"
|
<div class="row g-2">
|
||||||
data-category="all">
|
<div class="col-12 col-md-6 col-lg-4">
|
||||||
Все категории
|
<div class="input-group">
|
||||||
</button>
|
<span class="input-group-text">
|
||||||
${categoriesArray.map(category => `
|
<i class="bi bi-tags"></i>
|
||||||
<button class="btn filter-btn"
|
</span>
|
||||||
data-category="${category.id}">
|
<select class="form-select" id="${tabId}-param-select">
|
||||||
${category.title}
|
<option value="">Все параметры</option>
|
||||||
</button>
|
${Object.keys(sortedSpecData).map(param => `
|
||||||
|
<option value="${param}">${param}</option>
|
||||||
`).join('')}
|
`).join('')}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4">
|
<div class="col-12 col-md-6 col-lg-4">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<i class="bi bi-filter"></i>
|
||||||
|
</span>
|
||||||
|
<select class="form-select" id="${tabId}-value-select" disabled>
|
||||||
|
<option value="">Все значения</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-12 col-lg-4">
|
||||||
|
<div class="btn-group w-100" role="group" aria-label="Фильтр спецификаций">
|
||||||
|
<button class="btn btn-primary" type="button" id="${tabId}-find-spec-btn" disabled>
|
||||||
|
<i class="bi bi-search me-1"></i>Найти
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary" type="button" id="${tabId}-reset-spec-btn">
|
||||||
|
<i class="bi bi-x-circle me-1"></i>Сброс
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Поиск -->
|
||||||
|
<div class="col-12 col-lg-4">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">
|
<span class="input-group-text">
|
||||||
<i class="bi bi-search"></i>
|
<i class="bi bi-search"></i>
|
||||||
@@ -1204,6 +1303,11 @@ function renderToolkitsTab(tabId, toolsList, categoriesArray) {
|
|||||||
class="form-control"
|
class="form-control"
|
||||||
id="${tabId}-search-input"
|
id="${tabId}-search-input"
|
||||||
placeholder="Поиск по названию и описанию...">
|
placeholder="Поиск по названию и описанию...">
|
||||||
|
<button class="btn btn-outline-secondary d-none"
|
||||||
|
type="button"
|
||||||
|
id="${tabId}-clear-search">
|
||||||
|
<i class="bi bi-x-lg"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1222,28 +1326,44 @@ function renderToolkitsTab(tabId, toolsList, categoriesArray) {
|
|||||||
renderToolkitCards(tabId, toolsList, categoriesData);
|
renderToolkitCards(tabId, toolsList, categoriesData);
|
||||||
|
|
||||||
// Добавляем обработчики событий для фильтров
|
// Добавляем обработчики событий для фильтров
|
||||||
setupFilters(tabId, toolsList, categoriesData);
|
setupFilters(tabId, toolsList, categoriesData, sortedSpecData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Функция для рендеринга карточек
|
// Функция для рендеринга карточек
|
||||||
function renderToolkitCards(tabId, tools, categoriesMap, filterText = '', categoryFilter = 'all', showHiddenTools = false) {
|
function renderToolkitCards(tabId, tools, categoriesMap, filterText = '', categoryFilter = 'all', showHiddenTools = false, specParam = '', specValue = '') {
|
||||||
const container = document.getElementById(`${tabId}-cards-container`);
|
const container = document.getElementById(`${tabId}-cards-container`);
|
||||||
|
|
||||||
// Фильтруем инструменты
|
// Фильтруем инструменты
|
||||||
const filteredTools = tools.filter(tool => {
|
const filteredTools = tools.filter(tool => {
|
||||||
// Показываем скрытые инструменты, если флаг установлен
|
|
||||||
const showHidden = showHiddenTools || !tool.hidden;
|
|
||||||
|
|
||||||
// Фильтр по категории
|
// Фильтр по категории
|
||||||
const categoryMatch = categoryFilter === 'all' || tool.category_id == categoryFilter;
|
if (categoryFilter !== 'all' && tool.category_id !== parseInt(categoryFilter)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Фильтр по тексту
|
// Фильтр по скрытым инструментам
|
||||||
const searchMatch = !filterText ||
|
if (!showHiddenTools && tool.hidden) {
|
||||||
(tool.title && tool.title.toLowerCase().includes(filterText.toLowerCase())) ||
|
return false;
|
||||||
(tool.description && tool.description.toLowerCase().includes(filterText.toLowerCase()));
|
}
|
||||||
|
|
||||||
return categoryMatch && searchMatch && showHidden;
|
// Фильтр по поисковому запросу
|
||||||
|
if (filterText) {
|
||||||
|
const searchLower = filterText.toLowerCase();
|
||||||
|
const titleMatch = tool.title.toLowerCase().includes(searchLower);
|
||||||
|
const descriptionMatch = tool.description.toLowerCase().includes(searchLower);
|
||||||
|
if (!titleMatch && !descriptionMatch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Фильтр по спецификациям
|
||||||
|
if (specParam && specValue) {
|
||||||
|
if (!tool.specifications || tool.specifications[specParam] !== specValue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Рендерим карточки
|
// Рендерим карточки
|
||||||
@@ -1288,7 +1408,7 @@ function renderToolkitCards(tabId, tools, categoriesMap, filterText = '', catego
|
|||||||
${description}
|
${description}
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
${tool.quantity_min ? `
|
${tool.quantity_min && accessData.view_all_toolboxes ? `
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
<i class="bi bi-box me-1"></i>
|
<i class="bi bi-box me-1"></i>
|
||||||
Мин: ${tool.quantity_min}
|
Мин: ${tool.quantity_min}
|
||||||
@@ -1312,71 +1432,207 @@ function renderToolkitCards(tabId, tools, categoriesMap, filterText = '', catego
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Функция для настройки фильтров
|
// Функция для настройки фильтров
|
||||||
function setupFilters(tabId, tools, categoriesMap) {
|
function setupFilters(tabId, tools, categoriesMap, specData) {
|
||||||
const searchInput = document.getElementById(`${tabId}-search-input`);
|
const searchInput = document.getElementById(`${tabId}-search-input`);
|
||||||
const filterButtons = document.querySelectorAll(`#${tabId}-tab-content .filter-btn`);
|
const filterButtons = document.querySelectorAll(`#${tabId}-tab-optional-content .filter-btn`);
|
||||||
const showHiddenToolsCheckbox = document.getElementById(`showHiddenTools`);
|
const showHiddenToolsCheckbox = document.getElementById('showHiddenTools');
|
||||||
|
|
||||||
showHiddenToolsCheckbox.addEventListener('change', () => {
|
// Новые элементы для фильтрации по спецификациям
|
||||||
renderToolkitCards(tabId, tools, categoriesMap, currentFilter.search, currentFilter.category, showHiddenToolsCheckbox.checked);
|
const paramSelect = document.getElementById(`${tabId}-param-select`);
|
||||||
});
|
const valueSelect = document.getElementById(`${tabId}-value-select`);
|
||||||
|
const findSpecBtn = document.getElementById(`${tabId}-find-spec-btn`);
|
||||||
|
const resetSpecBtn = document.getElementById(`${tabId}-reset-spec-btn`);
|
||||||
|
const clearSearchBtn = document.getElementById(`${tabId}-clear-search`);
|
||||||
|
|
||||||
|
const savedFilters = loadFromStorage(tabId);
|
||||||
|
|
||||||
// Текущие значения фильтров
|
const currentFilter = {
|
||||||
let currentFilter = {
|
category: savedFilters.category || 'all',
|
||||||
category: 'all',
|
search: savedFilters.search || '',
|
||||||
search: ''
|
showHidden: savedFilters.showHidden ?? false,
|
||||||
|
specParam: savedFilters.specParam || '',
|
||||||
|
specValue: savedFilters.specValue || ''
|
||||||
};
|
};
|
||||||
|
|
||||||
// Обработчик для кнопок категорий
|
/* ---------- Восстановление UI ---------- */
|
||||||
|
if (searchInput) {
|
||||||
|
searchInput.value = currentFilter.search;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showHiddenToolsCheckbox) {
|
||||||
|
showHiddenToolsCheckbox.checked = currentFilter.showHidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterButtons.forEach(btn => {
|
||||||
|
btn.classList.toggle(
|
||||||
|
'active',
|
||||||
|
btn.dataset.category === currentFilter.category
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Восстановление выбранного параметра
|
||||||
|
if (paramSelect && currentFilter.specParam) {
|
||||||
|
paramSelect.value = currentFilter.specParam;
|
||||||
|
updateValueSelect(currentFilter.specParam);
|
||||||
|
|
||||||
|
// Восстановление выбранного значения после обновления списка значений
|
||||||
|
setTimeout(() => {
|
||||||
|
if (valueSelect && currentFilter.specValue) {
|
||||||
|
valueSelect.value = currentFilter.specValue;
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const render = () => {
|
||||||
|
renderToolkitCards(
|
||||||
|
tabId,
|
||||||
|
tools,
|
||||||
|
categoriesMap,
|
||||||
|
currentFilter.search,
|
||||||
|
currentFilter.category,
|
||||||
|
currentFilter.showHidden,
|
||||||
|
currentFilter.specParam,
|
||||||
|
currentFilter.specValue
|
||||||
|
);
|
||||||
|
|
||||||
|
saveToStorage(tabId, currentFilter);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---------- Обновление списка значений при выборе параметра ---------- */
|
||||||
|
function updateValueSelect(selectedParam) {
|
||||||
|
if (valueSelect) {
|
||||||
|
valueSelect.innerHTML = '<option value="">Все значения</option>';
|
||||||
|
findSpecBtn.disabled = true;
|
||||||
|
|
||||||
|
if (selectedParam && specData[selectedParam]) {
|
||||||
|
valueSelect.disabled = false;
|
||||||
|
specData[selectedParam].forEach(value => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = value;
|
||||||
|
option.textContent = value;
|
||||||
|
valueSelect.appendChild(option);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
valueSelect.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Сброс фильтра спецификаций ---------- */
|
||||||
|
function resetSpecFilter() {
|
||||||
|
if (paramSelect) {
|
||||||
|
paramSelect.value = '';
|
||||||
|
}
|
||||||
|
if (valueSelect) {
|
||||||
|
valueSelect.innerHTML = '<option value="">Все значения</option>';
|
||||||
|
valueSelect.disabled = true;
|
||||||
|
}
|
||||||
|
currentFilter.specParam = '';
|
||||||
|
currentFilter.specValue = '';
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Обработчик выбора параметра ---------- */
|
||||||
|
if (paramSelect) {
|
||||||
|
paramSelect.addEventListener('change', function () {
|
||||||
|
const selectedParam = this.value;
|
||||||
|
updateValueSelect(selectedParam);
|
||||||
|
// Сбрасываем выбранное значение при изменении параметра
|
||||||
|
if (valueSelect) {
|
||||||
|
valueSelect.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/* ---------- Обработчик выбора значения ---------- */
|
||||||
|
if (valueSelect) {
|
||||||
|
valueSelect.addEventListener('change', function () {
|
||||||
|
findSpecBtn.disabled = !this.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Обработчик кнопки "Найти" для спецификаций ---------- */
|
||||||
|
if (findSpecBtn) {
|
||||||
|
findSpecBtn.addEventListener('click', function () {
|
||||||
|
currentFilter.specParam = paramSelect ? paramSelect.value : '';
|
||||||
|
currentFilter.specValue = valueSelect && !valueSelect.disabled ? valueSelect.value : '';
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Обработчик кнопки "Сброс" для спецификаций ---------- */
|
||||||
|
if (resetSpecBtn) {
|
||||||
|
resetSpecBtn.addEventListener('click', resetSpecFilter);
|
||||||
|
findSpecBtn.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Чекбокс ---------- */
|
||||||
|
if (showHiddenToolsCheckbox) {
|
||||||
|
showHiddenToolsCheckbox.addEventListener('change', () => {
|
||||||
|
currentFilter.showHidden = showHiddenToolsCheckbox.checked;
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Категории ---------- */
|
||||||
filterButtons.forEach(button => {
|
filterButtons.forEach(button => {
|
||||||
button.addEventListener('click', function () {
|
button.addEventListener('click', function () {
|
||||||
// Убираем активный класс у всех кнопок
|
|
||||||
filterButtons.forEach(btn => btn.classList.remove('active'));
|
filterButtons.forEach(btn => btn.classList.remove('active'));
|
||||||
// Добавляем активный класс текущей кнопке
|
|
||||||
this.classList.add('active');
|
this.classList.add('active');
|
||||||
|
|
||||||
currentFilter.category = this.dataset.category;
|
currentFilter.category = this.dataset.category;
|
||||||
renderToolkitCards(tabId, tools, categoriesMap, currentFilter.search, currentFilter.category);
|
render();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Обработчик для поля поиска
|
/* ---------- Поиск ---------- */
|
||||||
if (searchInput) {
|
if (searchInput) {
|
||||||
let searchTimeout;
|
let searchTimeout;
|
||||||
|
|
||||||
searchInput.addEventListener('input', function () {
|
searchInput.addEventListener('input', function () {
|
||||||
clearTimeout(searchTimeout);
|
clearTimeout(searchTimeout);
|
||||||
searchTimeout = setTimeout(() => {
|
searchTimeout = setTimeout(() => {
|
||||||
currentFilter.search = this.value.trim();
|
currentFilter.search = this.value.trim();
|
||||||
renderToolkitCards(tabId, tools, categoriesMap, currentFilter.search, currentFilter.category);
|
render();
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Очистка поиска
|
// Кнопка очистки поиска
|
||||||
searchInput.insertAdjacentHTML('afterend', `
|
if (clearSearchBtn) {
|
||||||
<button class="btn btn-outline-secondary d-none"
|
clearSearchBtn.addEventListener('click', () => {
|
||||||
type="button"
|
|
||||||
id="${tabId}-clear-search">
|
|
||||||
<i class="bi bi-x-lg"></i>
|
|
||||||
</button>
|
|
||||||
`);
|
|
||||||
|
|
||||||
const clearBtn = document.getElementById(`${tabId}-clear-search`);
|
|
||||||
if (clearBtn) {
|
|
||||||
clearBtn.addEventListener('click', function () {
|
|
||||||
searchInput.value = '';
|
searchInput.value = '';
|
||||||
currentFilter.search = '';
|
currentFilter.search = '';
|
||||||
renderToolkitCards(tabId, tools, categoriesMap, '', currentFilter.category);
|
clearSearchBtn.classList.add('d-none');
|
||||||
this.classList.add('d-none');
|
render();
|
||||||
});
|
});
|
||||||
|
|
||||||
searchInput.addEventListener('input', function () {
|
searchInput.addEventListener('input', function () {
|
||||||
clearBtn.classList.toggle('d-none', !this.value);
|
clearSearchBtn.classList.toggle('d-none', !this.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
clearSearchBtn.classList.toggle('d-none', !searchInput.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Первый рендер ---------- */
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadFromStorage(tabId) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(`toolboxStotage:${tabId}`)) || {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveToStorage(tabId, storageData) {
|
||||||
|
localStorage.setItem(
|
||||||
|
`toolboxStotage:${tabId}`,
|
||||||
|
JSON.stringify(storageData)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function addToolbox(editData = null) {
|
function addToolbox(editData = null) {
|
||||||
// Проверяем, существует ли уже модальное окно
|
// Проверяем, существует ли уже модальное окно
|
||||||
let modal = document.getElementById('addToolboxModal');
|
let modal = document.getElementById('addToolboxModal');
|
||||||
@@ -1741,6 +1997,11 @@ function renderToolboxTab(tabData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tabContent.innerHTML = toolboxContent;
|
tabContent.innerHTML = toolboxContent;
|
||||||
|
|
||||||
|
const choiceToolbox = loadFromStorage('toolbox');
|
||||||
|
if (choiceToolbox.toolboxId) {
|
||||||
|
window.selectToolbox(choiceToolbox.toolboxId).then(() => { }).catch(() => { });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для выбора склада
|
// Функция для выбора склада
|
||||||
@@ -1756,6 +2017,7 @@ window.selectToolbox = async function (toolboxId, index) {
|
|||||||
selectedBtn.classList.add('active');
|
selectedBtn.classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveToStorage('toolbox', { toolboxId });
|
||||||
// Загружаем содержимое склада
|
// Загружаем содержимое склада
|
||||||
await loadToolboxContent(toolboxId);
|
await loadToolboxContent(toolboxId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ async def main():
|
|||||||
from db.initialize import DatabaseInitializer
|
from db.initialize import DatabaseInitializer
|
||||||
|
|
||||||
try:
|
try:
|
||||||
force = True
|
force = False
|
||||||
reNewDB = True
|
reNewDB = False
|
||||||
await DatabaseInitializer(DATABASE_URL).initialize(force, reNewDB)
|
await DatabaseInitializer(DATABASE_URL).initialize(force, reNewDB)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Инициализация базы завершилась ошибкой: {str(e)}", exc_info=True)
|
logger.error(f"Инициализация базы завершилась ошибкой: {str(e)}", exc_info=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user