Gogs 3 долоо хоног өмнө
parent
commit
77161d3ab2

+ 51 - 15
README.md

@@ -1384,7 +1384,35 @@ Drag & drop интерфейс
 Проверка роли пользователя (только admin)
 
 Редирект при отсутствии прав доступа
+Полнофункциональный редактор категорий:
 
+Древовидное отображение с раскрывающимися узлами
+
+Создание, редактирование и удаление категорий
+
+Загрузка изображений для категорий
+
+Управление иерархией и порядком сортировки
+
+Медиа-менеджер:
+
+Загрузка файлов с drag&drop интерфейсом
+
+Превью изображений и иконки для документов
+
+Фильтрация и поиск по файлам
+
+Пакетное удаление файлов
+
+Интеграция с CouchDB attachments
+
+Интеграция компонентов:
+
+CategoryNode для рекурсивного отображения дерева
+
+MediaUpload для загрузки файлов
+
+Полная связь между компонентами через события
 
 
 ### 🎯 БЛИЖАЙШИЕ ЗАДАЧИ
@@ -1434,34 +1462,42 @@ Drag & drop интерфейс
 напиши файлы реализующие следующую задачу.
  ⚠️ ПРИОРИТЕТ
 
-ЭТАП 1.8: РЕАЛИЗАЦИЯ РЕДАКТОРА КАТЕГОРИЙ И МЕДИА-МЕНЕДЖЕРА
+ЭТАП 1.9: ИНТЕГРАЦИЯ И ОПТИМИЗАЦИЯ АДМИН-ПАНЕЛИ
 
-Редактор категорий:
+Drag & Drop для категорий:
 
-Древовидное отображение с drag&drop
+Перетаскивание категорий для изменения иерархии
 
-Загрузка изображений категорий
+Визуальные индикаторы при перетаскивании
 
-Редактирование метаданных и SEO
+Автоматическое обновление порядка
 
-Медиа-менеджер:
+Оптимизация медиа-менеджера:
+
+Ленивая загрузка изображений
+
+Кэширование превью
+
+Оптимизация производительности при большом количестве файлов
+
+Интеграция с товарами:
 
-Загрузка изображений с превью
+Привязка медиа-файлов к товарам
 
-Прикрепление файлов к документам CouchDB
+Галерея изображений товаров
 
-Система кэширования и оптимизации
+Управление основным изображением
 
-Интеграция:
+Система кэширования:
 
-Связь медиа-файлов с товарами и категориями
+Service Worker для офлайн-работы
 
-Управление версиями файлов
+Кэширование часто используемых данных
 
-Оптимизация производительности
+Оптимизация загрузки медиа-файлов
 
-Приоритет: Критический 🚨 (завершает базовый функционал админ-панели)
+Приоритет: Высокий ⚠️ (оптимизация производительности и улучшение UX)
 
-Приоритет: Высокий ⚠️ (необходимо для наполнения магазина товарами)
+Статус: Базовый функционал админ-панели завершен! 🎉
 
 

+ 19 - 6
app/pages/Admin/Categories/index.coffee

@@ -6,7 +6,7 @@ if globalThis.stylFns and globalThis.stylFns['app/pages/Admin/Categories/index.s
   document.head.appendChild(styleElement)
 else
   log '⚠️ Стили страницы категорий не найдены'
-
+{ Category } = require 'app/types/data'
 CategoryService = require 'app/services/CategoryService'
 CategoryNode = require 'app/components/Admin/CategoryNode/index.coffee'
 MediaUpload = require 'app/components/Admin/MediaUpload/index.coffee'
@@ -42,10 +42,13 @@ module.exports = {
       @flattenCategories(@hierarchicalCategories)
 
   methods:
+    # И обновите метод loadCategories:
     loadCategories: ->
       @loading = true
       try
-        @categories = await CategoryService.getAllCategories()
+        categoriesData = await CategoryService.getAllCategories()
+        # Преобразуем данные из базы в объекты Category
+        @categories = categoriesData.map (categoryData) -> new Category(categoryData)
         log '✅ Категории загружены: '+@categories.length
       catch error
         log '❌ Ошибка загрузки категорий: '+error.message
@@ -53,16 +56,26 @@ module.exports = {
       finally
         @loading = false
 
+    # В методе buildHierarchy замените текущую реализацию на эту:
     buildHierarchy: (categories, parentId = null) ->
       hierarchy = []
+      
+      # Фильтруем категории по parentId и сортируем по order
       categories
-        .filter (cat) -> cat.parent == parentId
-        .sort (a, b) -> a.order - b.order
+        .filter (cat) -> 
+          # Обрабатываем случаи когда parent может быть null, undefined или пустой строкой
+          currentParent = cat.parent or null
+          currentParent == parentId
+        .sort (a, b) -> (a.order or 0) - (b.order or 0)
         .forEach (category) =>
+          # Создаем новый объект Category с данными из базы
+          categoryData = new Category(category)
+          # Рекурсивно строим детей
           children = @buildHierarchy(categories, category._id)
           if children.length > 0
-            category.children = children
-          hierarchy.push(category)
+            categoryData.children = children
+          hierarchy.push(categoryData)
+      
       return hierarchy
 
     flattenCategories: (hierarchical, level = 0) ->

+ 1 - 1
app/pages/Admin/Categories/index.pug

@@ -10,7 +10,7 @@ div(class="categories-page")
     div(class="categories-tree")
       h2 Древовидная структура
       div(v-if="loading" class="loading-state") Загрузка категорий...
-      div(v-else class="tree-container")
+      div(v-else class="tree-container") 
         category-node(
           v-for="category in hierarchicalCategories"
           :key="category._id"

+ 48 - 17
app/services/CategoryService.coffee

@@ -18,25 +18,30 @@ class CategoryService
       log '❌ Ошибка инициализации CategoryService: ' + error
       return Promise.reject(error)
 
+    # В app/services/CategoryService.coffee обновите метод getAllCategories:
   getAllCategories: ->
-    await @ensureInit()
-    
-    try
-      result = await @pouchService.queryView('categories', 'all_active', {
-        include_docs: true
-      })
-      
-      categories = result.rows.map (row) ->
-        new Category(row.doc)
-      
-      # Сортировка по порядку
-      categories.sort (a, b) -> a.order - b.order
+      await @ensureInit()
       
-      log '📂 Загружены категории: ' + categories.length
-      return categories
-    catch error
-      log '❌ Ошибка загрузки категорий: ' + error
-      throw error
+      try
+        result = await @pouchService.queryView('categories', 'all_active', {
+          include_docs: true
+        })
+        
+        categories = result.rows.map (row) ->
+          # Используем конструктор Category с данными из базы
+          debug.dir row
+          d = new Category(row.doc)
+          debug.dir d
+          return d
+        
+        # Сортировка по порядку
+        categories.sort (a, b) -> (a.order or 0) - (b.order or 0)
+        
+        log '📂 Загружены категории: '+categories.length
+        return categories
+      catch error
+        log '❌ Ошибка загрузки категорий: '+error.message
+        throw error
 
   getCategoryBySlug: (slug) ->
     await @ensureInit()
@@ -94,6 +99,32 @@ class CategoryService
       log '❌ Ошибка построения иерархии категорий: ' + error
       throw error
 
+  deleteCategory: (categoryId) ->
+    await @ensureInit()
+    
+    try
+      # Получаем документ для получения _rev
+      categoryDoc = await @pouchService.getDocument(categoryId)
+      
+      # Устанавливаем флаг deleted вместо полного удаления
+      categoryDoc.active = false
+      categoryDoc.deleted = true
+      categoryDoc.updatedAt = new Date().toISOString()
+      
+      result = await @pouchService.saveDocument(categoryDoc)
+      log '✅ Категория помечена как удаленная: '+categoryId
+      return result
+    catch error
+      if error.status == 404
+        log '⚠️ Категория не найдена для удаления: '+categoryId
+        throw new Error('Категория не найдена')
+      else if error.status == 403
+        log '🚫 Доступ запрещен при удалении категории: '+error.message
+        throw new Error('Нет прав для удаления категории: '+error.message)
+      else
+        log '❌ Ошибка удаления категории: '+error.message
+        throw error
+
   buildHierarchy: (categories, parentId = null) ->
     hierarchy = []
     

+ 94 - 98
app/types/data.coffee

@@ -1,117 +1,113 @@
-# Базовый класс для всех сущностей
+# app/types/data.coffee
 class DomainEntity
-  constructor: ->
-    @_id = ''
-    @_rev = ''
-    @type = ''
-    @domains = []
-    @createdAt = new Date().toISOString()
-    @updatedAt = new Date().toISOString()
-    @active = true
-    @createdBy = ''
-    @updatedBy = ''
+  constructor: (data = {}) ->
+    @_id = data._id or ''
+    @_rev = data._rev or ''
+    @type = data.type or ''
+    @domains = data.domains or []
+    @createdAt = data.createdAt or new Date().toISOString()
+    @updatedAt = data.updatedAt or new Date().toISOString()
+    @active = if data.active? then data.active else true
+    @createdBy = data.createdBy or ''
+    @updatedBy = data.updatedBy or ''
 
-# Товар
-class Product extends DomainEntity
-  constructor: ->
-    super()
-    @type = 'product'
-    @name = ''
-    @sku = ''
-    @price = 0
-    @oldPrice = null
-    @category = ''
-    @brand = ''
-    @description = ''
-    @shortDescription = ''
-    @attributes = {}
-    @images = []
-    @richContent = null
-    @inStock = true
-    @stockQuantity = 0
-    @weight = 0
-    @volume = 0
-    @dimensions = {}
-    @seo = {}
-    @tags = []
-    @relatedProducts = []
-
-# Категория
 class Category extends DomainEntity
-  constructor: ->
-    super()
+  constructor: (data = {}) ->
+    super(data)
     @type = 'category'
-    @name = ''
-    @slug = ''
-    @parent = null
-    @order = 0
-    @image = ''
-    @description = ''
-    @seo = {}
-    @attributesConfig = {} # Конфигурация атрибутов для категории
+    @name = data.name or ''
+    @slug = data.slug or ''
+    @parent = data.parent or null
+    @order = data.order or 0
+    @image = data.image or ''
+    @description = data.description or ''
+    @seo = data.seo or {}
+    @attributesConfig = data.attributesConfig or {}
+    # Добавляем поле children для иерархической структуры
+    @children = data.children or []
+
+class Product extends DomainEntity
+  constructor: (data = {}) ->
+    super(data)
+    @type = 'product'
+    @name = data.name or ''
+    @sku = data.sku or ''
+    @price = data.price or 0
+    @oldPrice = data.oldPrice or null
+    @category = data.category or ''
+    @brand = data.brand or ''
+    @description = data.description or ''
+    @shortDescription = data.shortDescription or ''
+    @attributes = data.attributes or {}
+    @images = data.images or []
+    @richContent = data.richContent or null
+    @inStock = if data.inStock? then data.inStock else true
+    @stockQuantity = data.stockQuantity or 0
+    @weight = data.weight or 0
+    @volume = data.volume or 0
+    @dimensions = data.dimensions or {}
+    @seo = data.seo or {}
+    @tags = data.tags or []
+    @relatedProducts = data.relatedProducts or []
 
-# Слайд главной страницы
 class HeroSlide extends DomainEntity
-  constructor: ->
-    super()
+  constructor: (data = {}) ->
+    super(data)
     @type = 'hero_slide'
-    @title = ''
-    @subtitle = ''
-    @image = ''
-    @buttonText = 'В каталог'
-    @buttonLink = '/catalog'
-    @order = 0
-    @active = true
+    @title = data.title or ''
+    @subtitle = data.subtitle or ''
+    @image = data.image or ''
+    @buttonText = data.buttonText or 'В каталог'
+    @buttonLink = data.buttonLink or '/catalog'
+    @order = data.order or 0
+    @active = if data.active? then data.active else true
 
-# Настройки домена
 class DomainSettings extends DomainEntity
-  constructor: ->
-    super()
+  constructor: (data = {}) ->
+    super(data)
     @type = 'domain_settings'
-    @domain = ''
-    @companyName = ''
-    @companyLogo = ''
-    @languages = ['ru']
-    @defaultLanguage = 'ru'
-    @theme = 'light'
-    @contacts = {}
-    @seo = {}
-    @social = {}
-    @paymentMethods = []
-    @shippingMethods = []
+    @domain = data.domain or ''
+    @companyName = data.companyName or ''
+    @companyLogo = data.companyLogo or ''
+    @languages = data.languages or ['ru']
+    @defaultLanguage = data.defaultLanguage or 'ru'
+    @theme = data.theme or 'light'
+    @contacts = data.contacts or {}
+    @seo = data.seo or {}
+    @social = data.social or {}
+    @paymentMethods = data.paymentMethods or []
+    @shippingMethods = data.shippingMethods or []
 
-# Пользователь
 class User extends DomainEntity
-  constructor: ->
-    super()
+  constructor: (data = {}) ->
+    super(data)
     @type = 'user'
-    @username = ''
-    @email = ''
-    @role = 'user' # user, admin, manager
-    @firstName = ''
-    @lastName = ''
-    @phone = ''
-    @address = {}
-    @orders = []
-    @favorites = []
-    @cart = []
+    @username = data.username or ''
+    @email = data.email or ''
+    @role = data.role or 'user'
+    @firstName = data.firstName or ''
+    @lastName = data.lastName or ''
+    @phone = data.phone or ''
+    @address = data.address or {}
+    @orders = data.orders or []
+    @favorites = data.favorites or []
+    @cart = data.cart or []
 
-# Заказ
 class Order extends DomainEntity
-  constructor: ->
-    super()
+  constructor: (data = {}) ->
+    super(data)
     @type = 'order'
-    @userId = ''
-    @items = []
-    @total = 0
-    @status = 'pending' # pending, confirmed, shipped, delivered, cancelled
-    @paymentStatus = 'pending'
-    @shippingAddress = {}
-    @billingAddress = {}
-    @paymentMethod = ''
-    @shippingMethod = ''
-    @trackingNumber = ''
-    @notes = ''
+    @userId = data.userId or ''
+    @items = data.items or []
+    @total = data.total or 0
+    @status = data.status or 'pending'
+    @paymentStatus = data.paymentStatus or 'pending'
+    @shippingAddress = data.shippingAddress or {}
+    @billingAddress = data.billingAddress or {}
+    @paymentMethod = data.paymentMethod or ''
+    @shippingMethod = data.shippingMethod or ''
+    @trackingNumber = data.trackingNumber or ''
+    @notes = data.notes or ''
 
 module.exports = {
   DomainEntity,