- ${categoriesArray.map(category => `
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
@@ -1204,6 +1303,11 @@ function renderToolkitsTab(tabId, toolsList, categoriesArray) {
class="form-control"
id="${tabId}-search-input"
placeholder="Поиск по названию и описанию...">
+
@@ -1222,28 +1326,44 @@ function renderToolkitsTab(tabId, toolsList, categoriesArray) {
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 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 ||
- (tool.title && tool.title.toLowerCase().includes(filterText.toLowerCase())) ||
- (tool.description && tool.description.toLowerCase().includes(filterText.toLowerCase()));
+ // Фильтр по скрытым инструментам
+ if (!showHiddenTools && tool.hidden) {
+ return false;
+ }
- 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}
- ${tool.quantity_min ? `
+ ${tool.quantity_min && accessData.view_all_toolboxes ? `
Мин: ${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 filterButtons = document.querySelectorAll(`#${tabId}-tab-content .filter-btn`);
- const showHiddenToolsCheckbox = document.getElementById(`showHiddenTools`);
+ const filterButtons = document.querySelectorAll(`#${tabId}-tab-optional-content .filter-btn`);
+ 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);
- // Текущие значения фильтров
- let currentFilter = {
- category: 'all',
- search: ''
+ const currentFilter = {
+ category: savedFilters.category || 'all',
+ search: savedFilters.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 = '';
+ 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 = '';
+ 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 => {
button.addEventListener('click', function () {
- // Убираем активный класс у всех кнопок
filterButtons.forEach(btn => btn.classList.remove('active'));
- // Добавляем активный класс текущей кнопке
this.classList.add('active');
currentFilter.category = this.dataset.category;
- renderToolkitCards(tabId, tools, categoriesMap, currentFilter.search, currentFilter.category);
+ render();
});
});
- // Обработчик для поля поиска
+ /* ---------- Поиск ---------- */
if (searchInput) {
let searchTimeout;
+
searchInput.addEventListener('input', function () {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
currentFilter.search = this.value.trim();
- renderToolkitCards(tabId, tools, categoriesMap, currentFilter.search, currentFilter.category);
+ render();
}, 300);
});
- // Очистка поиска
- searchInput.insertAdjacentHTML('afterend', `
-
- `);
-
- const clearBtn = document.getElementById(`${tabId}-clear-search`);
- if (clearBtn) {
- clearBtn.addEventListener('click', function () {
+ // Кнопка очистки поиска
+ if (clearSearchBtn) {
+ clearSearchBtn.addEventListener('click', () => {
searchInput.value = '';
currentFilter.search = '';
- renderToolkitCards(tabId, tools, categoriesMap, '', currentFilter.category);
- this.classList.add('d-none');
+ clearSearchBtn.classList.add('d-none');
+ render();
});
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) {
// Проверяем, существует ли уже модальное окно
let modal = document.getElementById('addToolboxModal');
@@ -1741,6 +1997,11 @@ function renderToolboxTab(tabData) {
}
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');
}
+ saveToStorage('toolbox', { toolboxId });
// Загружаем содержимое склада
await loadToolboxContent(toolboxId);
}
diff --git a/main.py b/main.py
index 5e2761f..598c13e 100644
--- a/main.py
+++ b/main.py
@@ -23,8 +23,8 @@ async def main():
from db.initialize import DatabaseInitializer
try:
- force = True
- reNewDB = True
+ force = False
+ reNewDB = False
await DatabaseInitializer(DATABASE_URL).initialize(force, reNewDB)
except Exception as e:
logger.error(f"Инициализация базы завершилась ошибкой: {str(e)}", exc_info=True)