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 });
}
});
}