From f0b318e19aa6ca24984e00af53e0184a9c927050 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 20:51:28 +0000 Subject: [PATCH] Refactor: Simplify conditional logic in script.js using TYPE_CONFIG This change addresses a code health issue where there were over 30 instances of long if-else chains checking `effectiveParent.type === 'root'` or `lastStep.type === 'house'`, etc. By introducing a centralized `TYPE_CONFIG` object, this logic has been drastically simplified, making the code much easier to read and maintain. Changes: - Added `TYPE_CONFIG` to map each type (`root`, `house`, `room`, `cabinet`, `shelf`, `container`, `pocket`) to its properties (allowed child types, API endpoint, parent ID field, and view action). - Created a `refreshView(step)` helper to handle navigation and view updates autonomously. - Refactored `updateForm` to dynamically populate UI using `TYPE_CONFIG`. - Refactored `handleSubmitItemForm` to determine the proper request payload using `TYPE_CONFIG.apiMap`. - Refactored the restoration logic across breadcrumbs, item listing, form handling, and deletion to utilize `refreshView(step)`. Co-authored-by: deitaur <113350206+deitaur@users.noreply.github.com> --- public/script.js | 233 ++++++++++++++++++++++++++--------------------- 1 file changed, 127 insertions(+), 106 deletions(-) diff --git a/public/script.js b/public/script.js index bcf568f..787aa58 100644 --- a/public/script.js +++ b/public/script.js @@ -37,9 +37,111 @@ let focusedAutocompleteIndex = -1; let lastLoadedItems = []; // ====================================== -// ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ +// ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ И КОНФИГУРАЦИЯ // ====================================== +const TYPE_CONFIG = { + 'root': { + optionsHtml: ` + + + + + `, + hideDescription: true, + apiMap: { + 'house': { url: '/api/houses', parentField: null }, + 'item': { url: '/api/items', parentField: null }, + 'container': { url: '/api/items', parentField: null }, + 'pocket': { url: '/api/items', parentField: null } + }, + viewAction: async () => await showHouses() + }, + 'house': { + optionsHtml: ` + + + + + `, + hideDescription: true, + apiMap: { + 'room': { url: '/api/rooms', parentField: 'house_id' }, + 'item': { url: '/api/items', parentField: 'house_id' }, + 'container': { url: '/api/items', parentField: 'house_id' }, + 'pocket': { url: '/api/items', parentField: 'house_id' } + }, + viewAction: async (step) => await showRooms(step.id, step.name) + }, + 'room': { + optionsHtml: ` + + + + + `, + hideDescription: false, + apiMap: { + 'item': { url: '/api/items', parentField: 'room_id' }, + 'cabinet': { url: '/api/items', parentField: 'room_id' }, + 'container': { url: '/api/items', parentField: 'room_id' }, + 'pocket': { url: '/api/items', parentField: 'room_id' } + }, + viewAction: async (step) => await showItems(step.id, step.name, step.type) + }, + 'cabinet': { + optionsHtml: ` + + + + + `, + hideDescription: false, + apiMap: { + 'shelf': { url: '/api/items', parentField: 'parent_item_id' }, + 'container': { url: '/api/items', parentField: 'parent_item_id' }, + 'pocket': { url: '/api/items', parentField: 'parent_item_id' }, + 'item': { url: '/api/items', parentField: 'parent_item_id' } + }, + viewAction: async (step) => await showItems(step.id, step.name, step.type) + }, + 'shelf': { + optionsHtml: ``, + hideDescription: false, + apiMap: { + 'item': { url: '/api/items', parentField: 'parent_item_id' } + }, + viewAction: async (step) => await showItems(step.id, step.name, step.type) + }, + 'container': { + optionsHtml: ``, + hideDescription: false, + apiMap: { + 'item': { url: '/api/items', parentField: 'parent_item_id' } + }, + viewAction: async (step) => await showItems(step.id, step.name, step.type) + }, + 'pocket': { + optionsHtml: ``, + hideDescription: false, + apiMap: { + 'item': { url: '/api/items', parentField: 'parent_item_id' } + }, + viewAction: async (step) => await showItems(step.id, step.name, step.type) + } +}; + +async function refreshView(step) { + if (!step) { + return await showHouses(); + } + const config = TYPE_CONFIG[step.type]; + if (config && config.viewAction) { + return await config.viewAction(step); + } + return await showItems(step.id, step.name, step.type); +} + /** * Определяет фактического родителя для нового предмета с учетом флага "Бросить на пол/Участок". * (Реализация принципов паттерна "Цепочка Обязанностей" для размещения) @@ -99,43 +201,13 @@ function updateForm() { const lastStep = currentPath[currentPath.length - 1]; // --- Настройка типов элементов --- - if (lastStep.type === 'root') { - // На корневом уровне можно создать Дом ИЛИ сразу сбросить предмет на Глобальный Участок - itemTypeSelect.innerHTML = ` - - - - - `; - descriptionInput.style.display = 'none'; - - } else if (lastStep.type === 'house') { - // В Доме можно создать Комнату ИЛИ сбросить предмет на Участок Дома - itemTypeSelect.innerHTML = ` - - - - - `; - descriptionInput.style.display = 'none'; - - } else if (lastStep.type === 'room') { - itemTypeSelect.innerHTML = ` - - - - - `; - } else if (lastStep.type === 'cabinet') { - itemTypeSelect.innerHTML = ` - - - - - `; - } else if (lastStep.type === 'shelf' || lastStep.type === 'container' || lastStep.type === 'pocket') { - itemTypeSelect.innerHTML = ``; - + const config = TYPE_CONFIG[lastStep.type]; + + if (config) { + itemTypeSelect.innerHTML = config.optionsHtml; + if (config.hideDescription) { + descriptionInput.style.display = 'none'; + } } else { itemTypeSelect.innerHTML = ''; descriptionInput.style.display = 'none'; @@ -313,13 +385,7 @@ async function deleteItem(id, level) { throw new Error(error.error || 'Ошибка при удалении.'); } const lastStep = currentPath[currentPath.length - 1]; - if (lastStep.type === 'root') { - showHouses(); - } else if (lastStep.type === 'house') { - showRooms(lastStep.id, lastStep.name); - } else { - showItems(lastStep.id, lastStep.name, lastStep.type); - } + await refreshView(lastStep); } catch (error) { console.error('Ошибка:', error); errorMessageEl.textContent = error.message; @@ -422,16 +488,10 @@ async function editItem(item, level) { } }; - const restoreView = () => { + const restoreView = async () => { // Восстановим отображение в текущем контексте — но без перезагрузки данных, если уже обновили DOM const lastStep = currentPath[currentPath.length - 1]; - if (!lastStep || lastStep.type === 'root') { - showHouses(); - } else if (lastStep.type === 'house') { - showRooms(lastStep.id, lastStep.name); - } else { - showItems(lastStep.id, lastStep.name, lastStep.type); - } + await refreshView(lastStep); }; const handleSave = async () => { @@ -619,41 +679,18 @@ async function handleSubmitItemForm(e) { // 2. ФОРМИРОВАНИЕ ЗАПРОСА В ЗАВИСИМОСТИ ОТ ЭФФЕКТИВНОГО РОДИТЕЛЯ - if (effectiveParent.type === 'root') { - if (type === 'house') { - url = '/api/houses'; // Создание Дома - } else if (type === 'item' || type === 'container' || type === 'pocket') { - url = '/api/items'; // Глобальный Участок (без house_id/room_id) - } else { - errorMessageEl.textContent = 'Невозможно добавить элемент на корневом уровне.'; - return; - } - - } else if (effectiveParent.type === 'house') { - if (type === 'room') { - url = '/api/rooms'; // Создание Комнаты - finalPayload.house_id = effectiveParent.id; - } else if (type === 'item' || type === 'container' || type === 'pocket') { - url = '/api/items'; // Участок Дома (с house_id, без room_id/parent_item_id) - finalPayload.house_id = effectiveParent.id; - } else { - errorMessageEl.textContent = 'Невозможно добавить элемент на уровне Дома, кроме Комнаты или базовых контейнеров/вещей.'; - return; - } - - } else if (effectiveParent.type === 'room') { - url = '/api/items'; // Пол Комнаты (с room_id) - finalPayload.room_id = effectiveParent.id; - - } else if (effectiveParent.type === 'cabinet' || effectiveParent.type === 'shelf' || effectiveParent.type === 'container' || effectiveParent.type === 'pocket') { - url = '/api/items'; // Внутри Контейнера (с parent_item_id) - finalPayload.parent_item_id = effectiveParent.id; - - } else { - errorMessageEl.textContent = 'Невозможно добавить элемент на текущем уровне.'; + const config = TYPE_CONFIG[effectiveParent.type]; + if (!config || !config.apiMap[type]) { + errorMessageEl.textContent = 'Невозможно добавить элемент выбранного типа на текущем уровне.'; return; } + const apiConfig = config.apiMap[type]; + url = apiConfig.url; + if (apiConfig.parentField) { + finalPayload[apiConfig.parentField] = effectiveParent.id; + } + // 3. Отправка данных try { const response = await fetch(url, { @@ -672,13 +709,7 @@ async function handleSubmitItemForm(e) { document.getElementById('description').value = ''; // Перезагрузка текущего содержимого (возвращаемся к effectiveParent) - if (effectiveParent.type === 'root') { - await showHouses(); - } else if (effectiveParent.type === 'house') { - await showRooms(effectiveParent.id, effectiveParent.name); - } else { - await showItems(effectiveParent.id, effectiveParent.name, effectiveParent.type); - } + await refreshView(effectiveParent); } catch (error) { console.error('Ошибка при отправке формы:', error); @@ -1070,7 +1101,7 @@ document.addEventListener('DOMContentLoaded', () => { // --- НАВИГАЦИЯ (ХЛЕБНЫЕ КРОШКИ) --- if (breadcrumbsDiv) { - breadcrumbsDiv.addEventListener('click', (e) => { + breadcrumbsDiv.addEventListener('click', async (e) => { if (e.target.tagName === 'A') { const link = e.target; const index = parseInt(link.dataset.index); @@ -1079,19 +1110,13 @@ document.addEventListener('DOMContentLoaded', () => { currentPath = currentPath.slice(0, index + 1); const step = currentPath[currentPath.length - 1]; - if (step.type === 'root') { - showHouses(); - } else if (step.type === 'house') { - showRooms(step.id, step.name); - } else { - showItems(step.id, step.name, step.type); - } + await refreshView(step); } }); } // --- НАВИГАЦИЯ (СПИСОК ЭЛЕМЕНТОВ) --- if (contentDiv) { - contentDiv.addEventListener('click', (e) => { + contentDiv.addEventListener('click', async (e) => { const link = e.target.closest('a'); if (link && link.dataset.id && link.closest('.item-list')) { @@ -1104,11 +1129,7 @@ if (contentDiv) { // в showRooms/showItems, что делает код чище и избегает дублирования // currentPath.push({ id: id, type: type, name: name }); - if (type === 'house') { - showRooms(id, name); - } else if (type === 'room' || type === 'cabinet' || type === 'container' || type === 'pocket' || type === 'shelf') { - showItems(id, name, type); - } + await refreshView({ id, name, type }); } }); }