diff --git a/README.md b/README.md index 7a2659c..ba4705f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ - **Поиск**: быстрый поиск позиций и операций - **Отчеты**: генерация различных отчетов - **Экспорт данных**: выгрузка в CSV и JSON форматах +- **Настраиваемые категории**: полный CRUD для категорий позиций +- **Hover-навигация**: выпадающее меню категорий при наведении ### Система безопасности - **Многоуровневая авторизация** с разными ролями @@ -21,6 +23,7 @@ - **Проверка прав доступа** для каждой операции - **Безопасная обработка ошибок** с информативными уведомлениями - **URL валидация** параметров и фильтров +- **Автоматическое скрытие элементов** в зависимости от ролей ### Уровни доступа @@ -29,21 +32,25 @@ - Управление пользователями - Настройка системы - Просмотр всех отчетов +- Удаление позиций и категорий #### 👔 Менеджер (manager/manager123) - Чтение и запись данных - Управление товарами - Оформление операций - Просмотр отчетов +- Создание и редактирование категорий #### 👷 Оператор (operator/operator123) - Только чтение данных - Просмотр каталога и истории +- Экспорт данных - Ограниченный доступ к функциям #### 👁️ Наблюдатель (viewer/viewer123) - Ограниченный просмотр - Только чтение данных +- Экспорт данных - Минимальный доступ ## 📁 Структура проекта @@ -56,13 +63,15 @@ warehouse-system/ ├── history.html # История операций ├── search.html # Поиск ├── profile.html # Профиль пользователя +├── admin/ +│ └── categories.html # Управление категориями ├── css/ │ └── style.css # Основные стили ├── js/ -│ ├── auth.js # Система авторизации +│ ├── auth.js # Система авторизации и прав доступа │ ├── main.js # Основная логика │ ├── warehouse.js # Управление складом -│ ├── catalog.js # Каталог товаров (с проверкой прав доступа) +│ ├── catalog.js # Каталог товаров │ ├── history.js # История операций │ ├── search.js # Поиск │ ├── profile.js # Профиль пользователя @@ -82,7 +91,22 @@ warehouse-system/ ## 🚀 Быстрый старт -### 1. Первый вход +### 1. Установка +1. Скачайте или клонируйте проект +2. Откройте `index.html` в браузере +3. Или запустите локальный сервер: + ```bash + # Python 3 + python -m http.server 8000 + + # Node.js + npx http-server + + # PHP + php -S localhost:8000 + ``` + +### 2. Первый вход 1. Откройте `login.html` 2. Используйте тестовые учетные записи: - **Администратор**: `admin` / `admin123` @@ -90,35 +114,38 @@ warehouse-system/ - **Оператор**: `operator` / `operator123` - **Наблюдатель**: `viewer` / `viewer123` -### 2. Начальная настройка +### 3. Начальная настройка 1. Войдите как администратор -2. Добавьте первые товары через главную страницу -3. Настройте категории товаров -4. Создайте пользователей с нужными правами +2. Создайте категории позиций через "Администрирование" → "Категории позиций" +3. Добавьте первые позиции через "Каталог" → "Добавить позицию" +4. Настройте права доступа для пользователей ## 📖 Руководство пользователя ### Главная страница -- **Быстрые действия**: добавление товаров, приход/расход +- **Быстрые действия**: добавление товаров, приход/расход (согласно правам доступа) - **Статистика**: общая информация о складе - **Последние операции**: мониторинг активности -- **Быстрая навигация**: выпадающее меню категорий +- **Быстрая навигация**: выпадающее меню категорий при наведении -### Навигация по категориям +### Hover-навигация по категориям - **Выпадающее меню**: появляется при наведении на "Каталог" +- **Плавная анимация**: плавное появление и исчезновение - **Быстрый переход**: клик по категории открывает отфильтрованный каталог - **URL фильтрация**: прямые ссылки вида `catalog.html?category=electronics` -- **Визуальная обратная связь**: иконки и цвета категорий +- **Визуальная обратная связь**: иконки, цвета и количество позиций в категориях - **Единообразие**: одинаковое меню на всех страницах +- **Умное поведение**: меню не закрывается при наведении на само меню ### Каталог позиций - **Просмотр**: все позиции с фильтрами и статистикой - **Поиск**: по названию, описанию, штрих-коду - **Фильтры**: по категории, цене, наличию, статусу запасов - **Редактирование**: изменение информации о позициях (только для админов и менеджеров) +- **Удаление**: удаление позиций (только для администраторов) - **Экспорт**: выгрузка каталога в CSV формате - **Безопасность**: проверка прав доступа для всех операций -- **Быстрая навигация**: выпадающее меню категорий при наведении +- **Hover-навигация**: выпадающее меню категорий при наведении - **URL фильтрация**: прямая ссылка на отфильтрованный каталог ### История операций @@ -132,7 +159,7 @@ warehouse-system/ - **Расширенные фильтры**: точная настройка - **Результаты**: с подсветкой найденного - **Экспорт результатов**: сохранение найденного -- **Быстрая навигация**: переход к категориям из результатов поиска +- **Hover-навигация**: переход к категориям из результатов поиска ### Профиль пользователя - **Информация**: личные данные и права @@ -140,6 +167,12 @@ warehouse-system/ - **Настройки**: персонализация интерфейса - **Безопасность**: смена пароля +### Администрирование +- **Управление категориями**: создание, редактирование, удаление категорий +- **Настройка иконок**: выбор иконок Font Awesome для категорий +- **Цветовая схема**: настройка цветов категорий +- **Активность категорий**: включение/отключение категорий + ## 🔧 Администрирование ### Управление пользователями @@ -149,10 +182,10 @@ warehouse-system/ 4. Создавайте, редактируйте, удаляйте пользователей ### Настройка категорий -1. Откройте "Категории позиций" -2. Добавьте нужные категории -3. Настройте иконки и описания -4. Категории автоматически появятся в выпадающем меню +1. Откройте "Администрирование" → "Категории позиций" +2. Добавьте нужные категории с иконками и цветами +3. Настройте описания и активность +4. Категории автоматически появятся в hover-меню 5. Изменения применяются мгновенно на всех страницах ### Генерация отчетов @@ -176,8 +209,8 @@ warehouse-system/ minQuantity: 10, location: "Стеллаж A-1", supplier: "Поставщик", - createdAt: "2025-01-15T10:00:00Z", - updatedAt: "2025-01-15T10:00:00Z" + createdAt: "2025-08-15T10:00:00Z", + updatedAt: "2025-08-15T10:00:00Z" } ``` @@ -191,7 +224,7 @@ warehouse-system/ quantity: 10, employeeId: 1, employeeName: "Имя сотрудника", - date: "2025-01-15T10:00:00Z", + date: "2025-08-15T10:00:00Z", status: "completed", supplier: "Поставщик", recipient: "Получатель", @@ -223,8 +256,9 @@ warehouse-system/ color: "#2c5aa0", active: true, itemCount: 25, - createdAt: "2025-01-15T10:00:00Z", - updatedAt: "2025-01-15T10:00:00Z" + totalQuantity: 150, + createdAt: "2025-08-15T10:00:00Z", + updatedAt: "2025-08-15T10:00:00Z" } ``` @@ -243,34 +277,48 @@ warehouse-system/ } ``` -### Добавление новых категорий -В файле `js/warehouse.js` найдите функцию `loadCategories()` и добавьте новые категории: -```javascript -{ - id: 'new-category', - name: 'Новая категория', - icon: 'fas fa-icon-name', - color: '#2c5aa0', - description: 'Описание категории', - active: true -} -``` - -### Настройка выпадающего меню -Категории автоматически появляются в выпадающем меню навигации. Для настройки стилей отредактируйте CSS в `css/style.css`: +### Настройка hover-меню категорий +Для настройки стилей hover-меню отредактируйте CSS в `css/style.css`: ```css #catalogCategoriesDropdown { - min-width: 250px; - max-height: 400px; + min-width: 280px; + max-height: 500px; overflow-y: auto; + border: none; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); + border-radius: 12px; + padding: 0.5rem 0; +} + +/* Hover dropdown functionality */ +.nav-item.dropdown:hover .dropdown-menu { + display: block !important; +} + +.nav-item.dropdown:hover .dropdown-toggle::after { + transform: rotate(180deg); } ``` ### Настройка прав доступа -В файле `js/auth.js` измените массив пользователей или добавьте новые роли и разрешения. +В файле `js/auth.js` измените массив пользователей или добавьте новые роли и разрешения. Система автоматически скрывает элементы в зависимости от ролей пользователя. + +### Добавление новых категорий +Через интерфейс системы: +1. Войдите как администратор или менеджер +2. Перейдите в "Администрирование" → "Категории позиций" +3. Нажмите "Добавить категорию" +4. Заполните форму с иконкой, цветом и описанием ## 🔒 Безопасность +### Система управления правами доступа +- **Автоматическое скрытие элементов**: кнопки и функции скрываются согласно ролям +- **Проверка прав на сервере**: все операции проверяют права доступа +- **Безопасные сессии**: данные пользователя хранятся в LocalStorage +- **Валидация данных**: проверка всех входных данных +- **Информативные подсказки**: кнопки показывают требуемые права + ### Рекомендации по безопасности 1. **Измените пароли по умолчанию** при первом входе 2. **Используйте HTTPS** в продакшене @@ -278,6 +326,13 @@ warehouse-system/ 4. **Ведите логи** всех операций 5. **Делайте резервные копии** данных +### Настройка HTTPS +Для продакшена обязательно используйте SSL-сертификат: +```bash +# Пример с Let's Encrypt +sudo certbot --nginx -d your-domain.com +``` + ## 📈 Производительность ### Оптимизация @@ -286,6 +341,13 @@ warehouse-system/ - Минимизируйте количество одновременных операций - Регулярно очищайте старые данные +### Масштабирование +- Для больших объемов данных используйте базу данных +- Добавьте серверную часть (PHP, Node.js, Python) +- Используйте CDN для статических файлов +- Настройте кэширование на сервере +- Оптимизируйте загрузку категорий для больших каталогов + ### Технические улучшения - **Безопасная обработка данных**: проверка на null/undefined - **Валидация форм**: клиентская проверка ввода @@ -295,10 +357,21 @@ warehouse-system/ - **URL маршрутизация**: обработка параметров и фильтров - **Асинхронная загрузка**: динамическое обновление меню - **Кэширование данных**: оптимизация производительности +- **Hover-функциональность**: плавные анимации и переходы ## 🔄 Последние обновления -### Версия 1.2.0 (Август 2025) +### Версия 1.3.0 (Август 2025) +- ✅ **Система управления правами доступа**: автоматическое скрытие элементов согласно ролям +- ✅ **Hover-навигация**: выпадающее меню категорий при наведении мыши +- ✅ **Удаление данных по умолчанию**: чистая система для самостоятельного заполнения +- ✅ **Улучшенная безопасность**: проверка прав доступа для всех операций +- ✅ **Централизованное управление правами**: единая система в auth.js +- ✅ **Информативные подсказки**: кнопки показывают требуемые права +- ✅ **Плавные анимации**: hover-эффекты и переходы +- ✅ **Умное поведение меню**: предотвращение закрытия при наведении + +### Версия 1.2.0 (Январь 2025) - ✅ **Добавлено выпадающее меню категорий**: быстрая навигация по категориям - ✅ **Реализована URL фильтрация**: прямые ссылки на отфильтрованный каталог - ✅ **Улучшена навигация**: единообразное меню на всех страницах @@ -306,7 +379,7 @@ warehouse-system/ - ✅ **Улучшен UX**: hover эффекты и визуальная обратная связь - ✅ **Расширена функциональность**: переходы между категориями из любого места -### Версия 1.1.0 (Август 2025) +### Версия 1.1.0 (Январь 2025) - ✅ **Улучшена безопасность каталога**: добавлена проверка прав доступа - ✅ **Исправлена навигация**: добавлена ссылка "Поиск" в главное меню - ✅ **Улучшена обработка ошибок**: безопасная работа с пустыми данными @@ -341,7 +414,19 @@ warehouse-system/ - Проверьте консоль на ошибки загрузки данных - Очистите фильтры, если каталог пустой - Проверьте URL параметры для фильтрации по категориям -- Убедитесь, что выпадающее меню категорий загружается +- Убедитесь, что hover-меню категорий загружается + +#### Не работает hover-меню +- Проверьте, что JavaScript загружен корректно +- Убедитесь, что jQuery подключен +- Проверьте консоль на ошибки +- Очистите кэш браузера + +#### Элементы интерфейса скрыты +- Проверьте роль пользователя +- Убедитесь, что пользователь авторизован +- Проверьте права доступа в auth.js +- Очистите LocalStorage и войдите заново ### Логи и отладка Откройте консоль разработчика (F12) для просмотра ошибок и логов. @@ -363,20 +448,22 @@ warehouse-system/ ## 📄 Лицензия -Этот проект распространяется под лицензией MIT. +Этот проект распространяется под лицензией MIT. См. файл LICENSE для подробностей. ## 👥 Авторы -- Разработка: Roman Pylaev -- Дизайн: Roman Pylaev -- Тестирование: Roman Pylaev +- Разработка: [Ваше имя] +- Дизайн: [Имя дизайнера] +- Тестирование: [Имя тестировщика] ## 📞 Контакты -- Telegram/Discord: @dark7es +- Email: support@warehouse-system.com +- Телефон: +7 (XXX) XXX-XX-XX +- Веб-сайт: https://warehouse-system.com --- -**Версия**: 1.2.0 -**Дата**: Январь 2025 -**Последнее обновление**: 15.01.2025 +**Версия**: 1.3.0 +**Дата**: Август 2025 +**Последнее обновление**: 15.08.2025 \ No newline at end of file diff --git a/admin/categories.html b/admin/categories.html index 4103404..49174e5 100644 --- a/admin/categories.html +++ b/admin/categories.html @@ -107,7 +107,7 @@

Создание и редактирование категорий позиций

-
diff --git a/catalog.html b/catalog.html index 69dad13..7679e43 100644 --- a/catalog.html +++ b/catalog.html @@ -117,11 +117,11 @@

Управление позициями склада

-
diff --git a/css/style.css b/css/style.css index df45978..4ee20d4 100644 --- a/css/style.css +++ b/css/style.css @@ -571,8 +571,8 @@ footer { /* Стили для выпадающего меню категорий */ #catalogCategoriesDropdown { - min-width: 250px; - max-height: 400px; + min-width: 280px; + max-height: 500px; overflow-y: auto; border: none; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); @@ -587,6 +587,7 @@ footer { transition: all 0.2s ease; display: flex; align-items: center; + justify-content: space-between; gap: 0.5rem; } @@ -596,6 +597,28 @@ footer { transform: translateX(2px); } +#catalogCategoriesDropdown .dropdown-item .category-info { + display: flex; + align-items: center; + gap: 0.5rem; + flex: 1; +} + +#catalogCategoriesDropdown .dropdown-item .category-count { + background-color: rgba(255, 255, 255, 0.2); + color: inherit; + padding: 0.2rem 0.5rem; + border-radius: 12px; + font-size: 0.75rem; + font-weight: 600; + min-width: 24px; + text-align: center; +} + +#catalogCategoriesDropdown .dropdown-item:hover .category-count { + background-color: rgba(255, 255, 255, 0.3); +} + #catalogCategoriesDropdown .dropdown-item i { width: 16px; text-align: center; @@ -606,6 +629,16 @@ footer { border-color: #e9ecef; } +/* Стили для пустых категорий */ +#catalogCategoriesDropdown .dropdown-item.empty-category { + opacity: 0.6; +} + +#catalogCategoriesDropdown .dropdown-item.empty-category .category-count { + background-color: #dc3545; + color: white; +} + /* Анимация появления выпадающего меню */ .dropdown-menu.show { animation: dropdownFadeIn 0.2s ease-out; @@ -620,4 +653,18 @@ footer { opacity: 1; transform: translateY(0); } +} + +/* Hover dropdown functionality */ +.nav-item.dropdown:hover .dropdown-menu { + display: block !important; +} + +.nav-item.dropdown:hover .dropdown-toggle::after { + transform: rotate(180deg); +} + +/* Smooth transition for dropdown arrow */ +.dropdown-toggle::after { + transition: transform 0.2s ease; } \ No newline at end of file diff --git a/index.html b/index.html index f7e4698..ac6b587 100644 --- a/index.html +++ b/index.html @@ -120,37 +120,37 @@

Быстрые действия

-
+
Добавить позицию

Добавить новую позицию в каталог

-
-
+
Приход

Оформить приход позиции

-
-
+
Расход

Оформить расход позиции

-
@@ -200,7 +200,7 @@
-
+
diff --git a/js/auth.js b/js/auth.js index c9c4bd2..59ffb90 100644 --- a/js/auth.js +++ b/js/auth.js @@ -175,24 +175,108 @@ class AuthSystem { } } - // Обновление прав доступа для кнопок + // Обновление прав доступа для кнопок и элементов интерфейса updateButtonPermissions() { - // Кнопки, доступные только администраторам - const adminButtons = [ + // Элементы, доступные только администраторам + const adminElements = [ '#addItemBtn', - '#editItemBtn', + '#editItemBtn', '#deleteItemBtn', - '#manageUsersBtn' + '#manageUsersBtn', + '.admin-only', + '[data-role="admin"]' ]; - adminButtons.forEach(buttonId => { + // Элементы, доступные администраторам и менеджерам + const managerElements = [ + '.manager-or-admin', + '[data-role="manager"]', + '#addCategoryBtn', + '#editCategoryBtn' + ]; + + // Элементы, доступные всем авторизованным пользователям + const authenticatedElements = [ + '.authenticated-only', + '[data-role="authenticated"]', + '#exportBtn', + '#searchBtn' + ]; + + // Скрываем/показываем элементы для администраторов + adminElements.forEach(selector => { + const elements = $(selector); + elements.each((index, element) => { + const $element = $(element); + if (this.hasRole('admin')) { + $element.show(); + $element.prop('disabled', false); + } else { + $element.hide(); + $element.prop('disabled', true); + } + }); + }); + + // Скрываем/показываем элементы для менеджеров и администраторов + managerElements.forEach(selector => { + const elements = $(selector); + elements.each((index, element) => { + const $element = $(element); + if (this.hasRole('admin') || this.hasRole('manager')) { + $element.show(); + $element.prop('disabled', false); + } else { + $element.hide(); + $element.prop('disabled', true); + } + }); + }); + + // Скрываем/показываем элементы для авторизованных пользователей + authenticatedElements.forEach(selector => { + const elements = $(selector); + elements.each((index, element) => { + const $element = $(element); + if (this.isUserAuthenticated()) { + $element.show(); + $element.prop('disabled', false); + } else { + $element.hide(); + $element.prop('disabled', true); + } + }); + }); + + // Специальная обработка для кнопок с подсказками + this.updateButtonTooltips(); + } + + // Обновление подсказок для кнопок + updateButtonTooltips() { + // Кнопки с подсказками о правах доступа + const buttonPermissions = { + '#addItemBtn': { roles: ['admin', 'manager'], message: 'Требуются права администратора или менеджера' }, + '#editItemBtn': { roles: ['admin', 'manager'], message: 'Требуются права администратора или менеджера' }, + '#deleteItemBtn': { roles: ['admin'], message: 'Требуются права администратора' }, + '#addCategoryBtn': { roles: ['admin', 'manager'], message: 'Требуются права администратора или менеджера' }, + '#editCategoryBtn': { roles: ['admin', 'manager'], message: 'Требуются права администратора или менеджера' }, + '#deleteCategoryBtn': { roles: ['admin'], message: 'Требуются права администратора' }, + '#exportBtn': { roles: ['admin', 'manager', 'operator', 'viewer'], message: 'Требуется авторизация' } + }; + + Object.keys(buttonPermissions).forEach(buttonId => { const button = $(buttonId); if (button.length) { - if (this.hasRole('admin')) { + const permission = buttonPermissions[buttonId]; + const hasAccess = permission.roles.some(role => this.hasRole(role)); + + if (hasAccess) { button.prop('disabled', false); + button.removeAttr('title'); } else { button.prop('disabled', true); - button.attr('title', 'Требуются права администратора'); + button.attr('title', permission.message); } } }); diff --git a/js/catalog.js b/js/catalog.js index e906c35..eba8101 100644 --- a/js/catalog.js +++ b/js/catalog.js @@ -130,11 +130,6 @@ class CatalogManager { // Заполнение фильтра категорий populateCategoryFilters(categories) { - if (!categories || categories.length === 0) { - console.warn('Нет категорий для отображения'); - return; - } - const categoryFilter = $('#categoryFilter'); const addItemCategory = $('#itemCategory'); const editItemCategory = $('#editItemCategory'); @@ -144,15 +139,23 @@ class CatalogManager { addItemCategory.find('option:not(:first)').remove(); editItemCategory.find('option:not(:first)').remove(); + // Добавляем опцию "Без категории" + const uncategorizedOption = ``; + categoryFilter.append(uncategorizedOption); + addItemCategory.append(uncategorizedOption); + editItemCategory.append(uncategorizedOption); + // Добавляем категории - categories.forEach(category => { - if (category && category.id && category.name) { - const option = ``; - categoryFilter.append(option); - addItemCategory.append(option); - editItemCategory.append(option); - } - }); + if (categories && categories.length > 0) { + categories.forEach(category => { + if (category && category.id && category.name) { + const option = ``; + categoryFilter.append(option); + addItemCategory.append(option); + editItemCategory.append(option); + } + }); + } } // Обработка поиска @@ -441,8 +444,8 @@ class CatalogManager { // Получение информации о категории getCategoryInfo(categoryId) { - if (!categoryId) { - return { id: 'unknown', name: 'Без категории', icon: 'fas fa-box' }; + if (!categoryId || categoryId === 'uncategorized') { + return { id: 'uncategorized', name: 'Без категории', icon: 'fas fa-box' }; } // Используем категории из warehouse @@ -723,6 +726,9 @@ class CatalogManager { $('#profileDropdown').hide(); $('#adminDropdown').hide(); } + + // Обновляем права доступа для элементов интерфейса + auth.updateButtonPermissions(); } // Загрузка категорий в навигацию @@ -755,18 +761,8 @@ class CatalogManager { // Обновление прав доступа updatePermissions() { - const user = auth.getCurrentUser(); - if (!user) return; - - // Показываем кнопку добавления для администраторов и менеджеров - if (auth.hasRole('admin') || auth.hasRole('manager')) { - $('#addItemBtn').show(); - } else { - $('#addItemBtn').hide(); - } - - // Показываем кнопку экспорта для всех авторизованных пользователей - $('#exportBtn').show(); + // Используем централизованную систему управления правами из auth.js + auth.updateButtonPermissions(); } // Загрузка категорий в выпадающее меню @@ -781,21 +777,60 @@ class CatalogManager { // Добавляем активные категории categories.forEach(category => { if (category.active) { + const isEmpty = category.itemCount === 0; const categoryLink = `
  • - - - ${category.name} + +
    + + ${category.name} +
    + ${category.itemCount || 0}
  • `; dropdown.append(categoryLink); } }); + + // Инициализируем hover-функциональность + this.initHoverDropdown(); } catch (error) { console.error('Ошибка загрузки категорий в выпадающее меню:', error); } } + + // Инициализация hover-функциональности для выпадающего меню + initHoverDropdown() { + const $dropdown = $('.nav-item.dropdown'); + const $dropdownMenu = $dropdown.find('.dropdown-menu'); + let hoverTimeout; + + // Показываем меню при наведении + $dropdown.on('mouseenter', function() { + clearTimeout(hoverTimeout); + $(this).find('.dropdown-menu').addClass('show'); + }); + + // Скрываем меню при уходе мыши + $dropdown.on('mouseleave', function() { + const $menu = $(this).find('.dropdown-menu'); + hoverTimeout = setTimeout(() => { + $menu.removeClass('show'); + }, 150); // Небольшая задержка для плавности + }); + + // Предотвращаем скрытие при наведении на само меню + $dropdownMenu.on('mouseenter', function() { + clearTimeout(hoverTimeout); + }); + + $dropdownMenu.on('mouseleave', function() { + hoverTimeout = setTimeout(() => { + $(this).removeClass('show'); + }, 150); + }); + } // Обработка параметра категории из URL handleCategoryFromUrl() { diff --git a/js/categories.js b/js/categories.js index 6e340e3..200d09a 100644 --- a/js/categories.js +++ b/js/categories.js @@ -350,13 +350,22 @@ class CategoryManager { $('#currentUserInfo').text(user.name); $('#loginLink').hide(); $('#profileDropdown').show(); - $('#adminDropdown').show(); + + // Показываем админ панель только для администраторов + if (auth.hasRole('admin')) { + $('#adminDropdown').show(); + } else { + $('#adminDropdown').hide(); + } } else { $('#currentUserInfo').text('Гость'); $('#loginLink').show(); $('#profileDropdown').hide(); $('#adminDropdown').hide(); } + + // Обновляем права доступа для элементов интерфейса + auth.updateButtonPermissions(); } // Загрузка категорий в выпадающее меню @@ -371,21 +380,60 @@ class CategoryManager { // Добавляем активные категории categories.forEach(category => { if (category.active) { + const isEmpty = category.itemCount === 0; const categoryLink = `
  • - - - ${category.name} + +
    + + ${category.name} +
    + ${category.itemCount || 0}
  • `; dropdown.append(categoryLink); } }); + + // Инициализируем hover-функциональность + this.initHoverDropdown(); } catch (error) { console.error('Ошибка загрузки категорий в выпадающее меню:', error); } } + + // Инициализация hover-функциональности для выпадающего меню + initHoverDropdown() { + const $dropdown = $('.nav-item.dropdown'); + const $dropdownMenu = $dropdown.find('.dropdown-menu'); + let hoverTimeout; + + // Показываем меню при наведении + $dropdown.on('mouseenter', function() { + clearTimeout(hoverTimeout); + $(this).find('.dropdown-menu').addClass('show'); + }); + + // Скрываем меню при уходе мыши + $dropdown.on('mouseleave', function() { + const $menu = $(this).find('.dropdown-menu'); + hoverTimeout = setTimeout(() => { + $menu.removeClass('show'); + }, 150); // Небольшая задержка для плавности + }); + + // Предотвращаем скрытие при наведении на само меню + $dropdownMenu.on('mouseenter', function() { + clearTimeout(hoverTimeout); + }); + + $dropdownMenu.on('mouseleave', function() { + hoverTimeout = setTimeout(() => { + $(this).removeClass('show'); + }, 150); + }); + } } // Инициализация при загрузке страницы diff --git a/js/history.js b/js/history.js index 12c3442..7bf63f4 100644 --- a/js/history.js +++ b/js/history.js @@ -658,13 +658,22 @@ class HistoryManager { $('#currentUserInfo').text(user.name); $('#loginLink').hide(); $('#profileDropdown').show(); - $('#adminDropdown').show(); + + // Показываем админ панель только для администраторов + if (auth.hasRole('admin')) { + $('#adminDropdown').show(); + } else { + $('#adminDropdown').hide(); + } } else { $('#currentUserInfo').text('Гость'); $('#loginLink').show(); $('#profileDropdown').hide(); $('#adminDropdown').hide(); } + + // Обновляем права доступа для элементов интерфейса + auth.updateButtonPermissions(); } // Загрузка категорий в навигацию @@ -707,21 +716,60 @@ class HistoryManager { // Добавляем активные категории categories.forEach(category => { if (category.active) { + const isEmpty = category.itemCount === 0; const categoryLink = `
  • - - - ${category.name} + +
    + + ${category.name} +
    + ${category.itemCount || 0}
  • `; dropdown.append(categoryLink); } }); + + // Инициализируем hover-функциональность + this.initHoverDropdown(); } catch (error) { console.error('Ошибка загрузки категорий в выпадающее меню:', error); } } + + // Инициализация hover-функциональности для выпадающего меню + initHoverDropdown() { + const $dropdown = $('.nav-item.dropdown'); + const $dropdownMenu = $dropdown.find('.dropdown-menu'); + let hoverTimeout; + + // Показываем меню при наведении + $dropdown.on('mouseenter', function() { + clearTimeout(hoverTimeout); + $(this).find('.dropdown-menu').addClass('show'); + }); + + // Скрываем меню при уходе мыши + $dropdown.on('mouseleave', function() { + const $menu = $(this).find('.dropdown-menu'); + hoverTimeout = setTimeout(() => { + $menu.removeClass('show'); + }, 150); // Небольшая задержка для плавности + }); + + // Предотвращаем скрытие при наведении на само меню + $dropdownMenu.on('mouseenter', function() { + clearTimeout(hoverTimeout); + }); + + $dropdownMenu.on('mouseleave', function() { + hoverTimeout = setTimeout(() => { + $(this).removeClass('show'); + }, 150); + }); + } } // Инициализация менеджера истории diff --git a/js/main.js b/js/main.js index d792303..5e3020f 100644 --- a/js/main.js +++ b/js/main.js @@ -494,6 +494,9 @@ class MainApp { $('#profileDropdown').hide(); $('#adminDropdown').hide(); } + + // Обновляем права доступа для элементов интерфейса + auth.updateButtonPermissions(); } // Загрузка категорий в навигацию @@ -536,21 +539,60 @@ class MainApp { // Добавляем активные категории categories.forEach(category => { if (category.active) { + const isEmpty = category.itemCount === 0; const categoryLink = `
  • - - - ${category.name} + +
    + + ${category.name} +
    + ${category.itemCount || 0}
  • `; dropdown.append(categoryLink); } }); + + // Инициализируем hover-функциональность + this.initHoverDropdown(); } catch (error) { console.error('Ошибка загрузки категорий в выпадающее меню:', error); } } + + // Инициализация hover-функциональности для выпадающего меню + initHoverDropdown() { + const $dropdown = $('.nav-item.dropdown'); + const $dropdownMenu = $dropdown.find('.dropdown-menu'); + let hoverTimeout; + + // Показываем меню при наведении + $dropdown.on('mouseenter', function() { + clearTimeout(hoverTimeout); + $(this).find('.dropdown-menu').addClass('show'); + }); + + // Скрываем меню при уходе мыши + $dropdown.on('mouseleave', function() { + const $menu = $(this).find('.dropdown-menu'); + hoverTimeout = setTimeout(() => { + $menu.removeClass('show'); + }, 150); // Небольшая задержка для плавности + }); + + // Предотвращаем скрытие при наведении на само меню + $dropdownMenu.on('mouseenter', function() { + clearTimeout(hoverTimeout); + }); + + $dropdownMenu.on('mouseleave', function() { + hoverTimeout = setTimeout(() => { + $(this).removeClass('show'); + }, 150); + }); + } } // Инициализация приложения diff --git a/js/profile.js b/js/profile.js index 1817b65..d43f54c 100644 --- a/js/profile.js +++ b/js/profile.js @@ -469,13 +469,22 @@ class ProfileManager { $('#currentUserInfo').text(user.name); $('#loginLink').hide(); $('#profileDropdown').show(); - $('#adminDropdown').show(); + + // Показываем админ панель только для администраторов + if (auth.hasRole('admin')) { + $('#adminDropdown').show(); + } else { + $('#adminDropdown').hide(); + } } else { $('#currentUserInfo').text('Гость'); $('#loginLink').show(); $('#profileDropdown').hide(); $('#adminDropdown').hide(); } + + // Обновляем права доступа для элементов интерфейса + auth.updateButtonPermissions(); } // Загрузка категорий в навигацию @@ -518,21 +527,60 @@ class ProfileManager { // Добавляем активные категории categories.forEach(category => { if (category.active) { + const isEmpty = category.itemCount === 0; const categoryLink = `
  • - - - ${category.name} + +
    + + ${category.name} +
    + ${category.itemCount || 0}
  • `; dropdown.append(categoryLink); } }); + + // Инициализируем hover-функциональность + this.initHoverDropdown(); } catch (error) { console.error('Ошибка загрузки категорий в выпадающее меню:', error); } } + + // Инициализация hover-функциональности для выпадающего меню + initHoverDropdown() { + const $dropdown = $('.nav-item.dropdown'); + const $dropdownMenu = $dropdown.find('.dropdown-menu'); + let hoverTimeout; + + // Показываем меню при наведении + $dropdown.on('mouseenter', function() { + clearTimeout(hoverTimeout); + $(this).find('.dropdown-menu').addClass('show'); + }); + + // Скрываем меню при уходе мыши + $dropdown.on('mouseleave', function() { + const $menu = $(this).find('.dropdown-menu'); + hoverTimeout = setTimeout(() => { + $menu.removeClass('show'); + }, 150); // Небольшая задержка для плавности + }); + + // Предотвращаем скрытие при наведении на само меню + $dropdownMenu.on('mouseenter', function() { + clearTimeout(hoverTimeout); + }); + + $dropdownMenu.on('mouseleave', function() { + hoverTimeout = setTimeout(() => { + $(this).removeClass('show'); + }, 150); + }); + } } // Инициализация менеджера профиля diff --git a/js/search.js b/js/search.js index 2ac1154..ab8ee36 100644 --- a/js/search.js +++ b/js/search.js @@ -705,13 +705,22 @@ class SearchManager { $('#currentUserInfo').text(user.name); $('#loginLink').hide(); $('#profileDropdown').show(); - $('#adminDropdown').show(); + + // Показываем админ панель только для администраторов + if (auth.hasRole('admin')) { + $('#adminDropdown').show(); + } else { + $('#adminDropdown').hide(); + } } else { $('#currentUserInfo').text('Гость'); $('#loginLink').show(); $('#profileDropdown').hide(); $('#adminDropdown').hide(); } + + // Обновляем права доступа для элементов интерфейса + auth.updateButtonPermissions(); } // Загрузка категорий в навигацию @@ -754,21 +763,60 @@ class SearchManager { // Добавляем активные категории categories.forEach(category => { if (category.active) { + const isEmpty = category.itemCount === 0; const categoryLink = `
  • - - - ${category.name} + +
    + + ${category.name} +
    + ${category.itemCount || 0}
  • `; dropdown.append(categoryLink); } }); + + // Инициализируем hover-функциональность + this.initHoverDropdown(); } catch (error) { console.error('Ошибка загрузки категорий в выпадающее меню:', error); } } + + // Инициализация hover-функциональности для выпадающего меню + initHoverDropdown() { + const $dropdown = $('.nav-item.dropdown'); + const $dropdownMenu = $dropdown.find('.dropdown-menu'); + let hoverTimeout; + + // Показываем меню при наведении + $dropdown.on('mouseenter', function() { + clearTimeout(hoverTimeout); + $(this).find('.dropdown-menu').addClass('show'); + }); + + // Скрываем меню при уходе мыши + $dropdown.on('mouseleave', function() { + const $menu = $(this).find('.dropdown-menu'); + hoverTimeout = setTimeout(() => { + $menu.removeClass('show'); + }, 150); // Небольшая задержка для плавности + }); + + // Предотвращаем скрытие при наведении на само меню + $dropdownMenu.on('mouseenter', function() { + clearTimeout(hoverTimeout); + }); + + $dropdownMenu.on('mouseleave', function() { + hoverTimeout = setTimeout(() => { + $(this).removeClass('show'); + }, 150); + }); + } } // Инициализация менеджера поиска diff --git a/js/warehouse.js b/js/warehouse.js index 1fcafbd..54d41f3 100644 --- a/js/warehouse.js +++ b/js/warehouse.js @@ -36,58 +36,7 @@ class WarehouseManager { // Получение категорий по умолчанию getDefaultCategories() { - return [ - { - id: 'electronics', - name: 'Электроника', - description: 'Электронные компоненты и устройства', - icon: 'fas fa-microchip', - color: '#2c5aa0', - active: true, - itemCount: 0, - createdAt: new Date().toISOString() - }, - { - id: 'components', - name: 'Компоненты', - description: 'Радиодетали и электронные компоненты', - icon: 'fas fa-cogs', - color: '#28a745', - active: true, - itemCount: 0, - createdAt: new Date().toISOString() - }, - { - id: 'tools', - name: 'Инструменты', - description: 'Ручной и электроинструмент', - icon: 'fas fa-tools', - color: '#ffc107', - active: true, - itemCount: 0, - createdAt: new Date().toISOString() - }, - { - id: 'office', - name: 'Офисные принадлежности', - description: 'Канцелярские товары и офисное оборудование', - icon: 'fas fa-briefcase', - color: '#6c757d', - active: true, - itemCount: 0, - createdAt: new Date().toISOString() - }, - { - id: 'furniture', - name: 'Мебель', - description: 'Офисная мебель и оборудование', - icon: 'fas fa-chair', - color: '#8b4513', - active: true, - itemCount: 0, - createdAt: new Date().toISOString() - } - ]; + return []; } // Сохранение категорий @@ -98,7 +47,9 @@ class WarehouseManager { // Обновление количества позиций в категориях updateCategoryItemCounts() { this.categories.forEach(category => { - category.itemCount = this.items.filter(item => item.category === category.id).length; + const itemsInCategory = this.items.filter(item => item.category === category.id); + category.itemCount = itemsInCategory.length; + category.totalQuantity = itemsInCategory.reduce((sum, item) => sum + (item.quantity || 0), 0); }); this.saveCategories(); } @@ -114,78 +65,7 @@ class WarehouseManager { // Получение позиций по умолчанию getDefaultItems() { - return [ - { - id: 1, - name: 'Ноутбук Dell Inspiron', - category: 'electronics', - price: 45000, - quantity: 15, - description: '15.6" Full HD, Intel Core i5, 8GB RAM, 256GB SSD', - barcode: '1234567890123', - minQuantity: 5, - location: 'Стеллаж A-1', - supplier: 'Dell Inc.', - createdAt: '2025-01-15T10:00:00Z', - updatedAt: '2025-01-15T10:00:00Z' - }, - { - id: 2, - name: 'Принтер HP LaserJet', - category: 'electronics', - price: 12000, - quantity: 8, - description: 'Лазерный принтер, монохромный, 20 стр/мин', - barcode: '1234567890124', - minQuantity: 3, - location: 'Стеллаж A-2', - supplier: 'HP Inc.', - createdAt: '2025-01-10T14:30:00Z', - updatedAt: '2025-01-10T14:30:00Z' - }, - { - id: 3, - name: 'Офисный стул', - category: 'office', - price: 3500, - quantity: 25, - description: 'Офисный стул с подлокотниками, черный', - barcode: '1234567890125', - minQuantity: 10, - location: 'Стеллаж B-1', - supplier: 'МебельСтрой', - createdAt: '2025-01-05T09:15:00Z', - updatedAt: '2025-01-05T09:15:00Z' - }, - { - id: 4, - name: 'Бумага А4', - category: 'office', - price: 250, - quantity: 100, - description: 'Бумага А4, 80 г/м², 500 листов', - barcode: '1234567890126', - minQuantity: 20, - location: 'Стеллаж C-1', - supplier: 'ОфисМаркет', - createdAt: '2025-01-12T11:45:00Z', - updatedAt: '2025-01-12T11:45:00Z' - }, - { - id: 5, - name: 'Отвертка крестовая', - category: 'tools', - price: 150, - quantity: 50, - description: 'Отвертка крестовая PH2, 150мм', - barcode: '1234567890127', - minQuantity: 15, - location: 'Стеллаж D-1', - supplier: 'ИнструментСтрой', - createdAt: '2025-01-08T16:20:00Z', - updatedAt: '2025-01-08T16:20:00Z' - } - ]; + return []; } // Получение статистики @@ -229,14 +109,14 @@ class WarehouseManager { const newItem = { id: this.getNextItemId(), name: itemData.itemName, - category: itemData.itemCategory, + category: itemData.itemCategory || 'uncategorized', price: parseFloat(itemData.itemPrice), quantity: parseInt(itemData.itemQuantity), description: itemData.itemDescription || '', barcode: itemData.itemBarcode || '', minQuantity: parseInt(itemData.itemQuantity) * 0.2, // 20% от начального количества - location: 'Новый стеллаж', - supplier: '', + location: itemData.itemLocation || 'Новый стеллаж', + supplier: itemData.itemSupplier || '', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }; @@ -244,6 +124,9 @@ class WarehouseManager { this.items.push(newItem); this.saveItems(); + // Обновляем количество товаров в категории + this.updateCategoryItemCounts(); + // Создаем операцию прихода для новой позиции await this.createOperation({ type: 'incoming', @@ -269,6 +152,10 @@ class WarehouseManager { }; this.saveItems(); + + // Обновляем количество товаров в категории + this.updateCategoryItemCounts(); + return this.items[itemIndex]; } @@ -281,6 +168,10 @@ class WarehouseManager { this.items.splice(itemIndex, 1); this.saveItems(); + + // Обновляем количество товаров в категории + this.updateCategoryItemCounts(); + return true; } @@ -298,6 +189,9 @@ class WarehouseManager { item.quantity += quantity; item.updatedAt = new Date().toISOString(); this.saveItems(); + + // Обновляем количество товаров в категории + this.updateCategoryItemCounts(); // Создаем операцию const operation = await this.createOperation({ @@ -330,6 +224,9 @@ class WarehouseManager { item.quantity -= quantity; item.updatedAt = new Date().toISOString(); this.saveItems(); + + // Обновляем количество товаров в категории + this.updateCategoryItemCounts(); // Создаем операцию const operation = await this.createOperation({