Gogs 4 viikkoa sitten
vanhempi
sitoutus
2bb99b9064

+ 77 - 37
README.md

@@ -4,7 +4,7 @@
 
 **Название:** Интернет-магазин лакокрасочной продукции "Браер-Колор" с админ панелью, мудьтидоменной и мультиязычной структурой
 **Тип:** SPA (Single Page Application) с современным минималистичным дизайном
-**Аналоги:** Функциональность m-kraski.ru с дизайном impasto-color.ru
+**Аналоги:** Функциональность m-kraski.ru с дизайном https://braer-color.ru/
 
 ## 🛠 Технический стек
 
@@ -76,60 +76,98 @@ div(class="")
 
 **Инициализация приложения (app/index.coffee):**
 ```coffee
-# Подключение файлов
+# Инициализация глобальных переменных
 globalThis.renderFns = require 'pug.json'
-globalThis.stylFns   = require 'styl.json'
+globalThis.stylFns = require 'styl.json'
 
 
-# Создание Vue приложения
+PouchDBService = require 'app/utils/pouch.coffee'
+
+# Инициализация сервиса данных
+pouchService = PouchDBService
+
+document.head.insertAdjacentHTML 'beforeend','<meta charset="UTF-8">'
+document.head.insertAdjacentHTML 'beforeend','<meta name="viewport" content="width=device-width, initial-scale=1.0">'
+
+
+document.head.insertAdjacentHTML('beforeend','<style  type="text/css">'+stylFns['app/index.styl']+'</style>')
+
+document.head.insertAdjacentHTML('beforeend','<title></title>')
+
+
+# Создание главного приложения Vue
 app = Vue.createApp({
   data: ->
     return {
-      theme: 'light'
+      theme: localStorage.getItem('theme') || 'light'
+      companyName: 'Браер-Колор'
       loading: false
       cartItems: []
       user: null
+      currentDomain: window.location.hostname
+      languages: ['ru', 'en']
+      currentLanguage: 'ru'
+      currentDomainSettings: null
     }
   
   render: (new Function '_ctx', '_cache', renderFns['app/index.pug'])()
   
+  computed:
+    isAdmin: -> @user?.role == 'admin'
+  
   methods:
     toggleTheme: ->
       @theme = if @theme == 'light' then 'dark' else 'light'
       localStorage.setItem 'theme', @theme
       document.documentElement.classList.toggle 'dark'
+    
+    setLanguage: (lang) ->
+      if @languages.includes lang
+        @currentLanguage = lang
+        localStorage.setItem 'language', @lang
+    
+    loadDomainSettings: ->
+      pouchService.getDocument("domain_settings:#{@currentDomain}")
+        .then (settings) =>
+          @currentDomainSettings = settings
+          # Устанавливаем заголовок страницы
+          if settings?.companyName
+            document.title = settings.companyName
+        .catch (error) =>
+          console.log 'Настройки домена не найдены, используются значения по умолчанию'
+          @currentDomainSettings = null
+    
+    showNotification: (message, type = 'success') ->
+      # Реализация системы уведомлений
+      console.log "#{type.toUpperCase()}: #{message}"
+  
+  mounted: ->
+    # Инициализация темы
+    if @theme == 'dark'
+      document.documentElement.classList.add 'dark'
+    
+    # Инициализация PouchDB
+    try
+      await pouchService.init()
+      console.log 'PouchDB инициализирован'
+      # Загружаем настройки домена
+      await @loadDomainSettings()
+    catch error
+      console.error 'Ошибка инициализации PouchDB:', error
+    
+    # Загрузка пользователя (если авторизован)
+    @loadUserData()
+  
+  loadUserData: ->
+    # Загрузка данных пользователя из localStorage или PouchDB
+    userData = localStorage.getItem 'user'
+    if userData
+      try
+        @user = JSON.parse userData
+      catch
+        @user = null
 })
 
-# Настройка маршрутизатора
-router = VueRouter.createRouter({
-  history: VueRouter.createWebHistory()
-  routes: [
-      { path: '/', component: require 'app/pages/Home' }
-      { 
-          path: '/admin'
-          component: require 'app/pages/Admin'
-          redirect: '/admin/Settings'
-          children: [
-              { path: 'slider', component: require 'app/pages/Admin/Slider' }
-              { path: 'products', component: require 'app/pages/Admin/Products' }
-              { path: 'clients', component: require 'app/pages/Admin/Clients' }
-              { path: 'blog', component: require 'app/pages/Admin/Blog' }
-              { path: 'routes', component: require 'app/pages/Admin/Routes' }
-              { path: 'settings', component: require 'app/pages/Admin/Settings' }
-            ]
-      
-      }
-
-      # Динамические маршруты для статей блога
-      { path: '/blog/:slug', component: require './pages/BlogArticle' }
-      # Пользовательские маршруты
-      { path: '/contacts', component: require './pages/Contacts' }
-      { path: '/about', component: require './pages/About' }
-  ]
-})
-
-app.use(router)
-app.mount('#app')
 ```
 
 ### 📊 Работа с данными (PouchDB)
@@ -213,7 +251,7 @@ module.exports = new PouchDBService({
   appVersion: '1.0.0'
 })
 ```
-Выжно: при реализации кода, проверяй наличие баз данных, если их нет то создай, также проверяй наличие _desing документов, если их нет создай.
+Выжно: при реализации кода, проверяй наличие баз данных, если их нет то создай, также проверяй наличие _desing документов, если их нет создай, если нет документа настройки домена тоже создай его.
 
 
 ### 🎛️ Компоненты админ-панели
@@ -535,7 +573,9 @@ https://cdn1.ozone.ru/s3/multimedia-1-p/7663352533.jpg";;;ЭкоКрас;4673764
 ### 🚧 В процессе
   Анализировать реализованный код, по git репозитарию https://gogs.osvoj.ru/oleg/s5l.ru-crm.git
   Проверяй промт и изменения в нём по адресу https://gogs.osvoj.ru/oleg/s5l.ru-crm/raw/master/README.md
- 
+  Важно Всегда приводи только полные листинги файлов
+  в стил файлах не используй @import '../../index.styl', только стили текущего элемента
+  
   Убери из -  app/pages/Admin дубль header, в app/pages/Admin/Settings добавь добавление фото лого и других необходимых изображений.
   А также названия компании и другой выводимой информации, переделай app/index.* исходя из этих изменений. 
   путь в изображениям согласно основному промту

+ 29 - 16
app/index.coffee

@@ -17,11 +17,8 @@ document.head.insertAdjacentHTML('beforeend','<style  type="text/css">'+stylFns[
 document.head.insertAdjacentHTML('beforeend','<title></title>')
 
 
-
-
-
 # Создание главного приложения Vue
-globalThis.app = Vue.createApp({
+app = Vue.createApp({
   data: ->
     return {
       theme: localStorage.getItem('theme') || 'light'
@@ -32,6 +29,7 @@ globalThis.app = Vue.createApp({
       currentDomain: window.location.hostname
       languages: ['ru', 'en']
       currentLanguage: 'ru'
+      currentDomainSettings: null
     }
   
   render: (new Function '_ctx', '_cache', renderFns['app/index.pug'])()
@@ -48,19 +46,23 @@ globalThis.app = Vue.createApp({
     setLanguage: (lang) ->
       if @languages.includes lang
         @currentLanguage = lang
-        localStorage.setItem 'language', lang
+        localStorage.setItem 'language', @lang
+    
+    loadDomainSettings: ->
+      pouchService.getDocument("domain_settings:#{@currentDomain}")
+        .then (settings) =>
+          @currentDomainSettings = settings
+          # Устанавливаем заголовок страницы
+          if settings?.companyName
+            document.title = settings.companyName
+        .catch (error) =>
+          console.log 'Настройки домена не найдены, используются значения по умолчанию'
+          @currentDomainSettings = null
     
     showNotification: (message, type = 'success') ->
       # Реализация системы уведомлений
-      console.dir "#{type.toUpperCase()}: #{message}"
-    loadUserData: ->
-        # Загрузка данных пользователя из localStorage или PouchDB
-        userData = localStorage.getItem 'user'
-        if userData
-          try
-            @user = JSON.parse userData
-          catch
-            @user = null
+      console.log "#{type.toUpperCase()}: #{message}"
+  
   mounted: ->
     # Инициализация темы
     if @theme == 'dark'
@@ -69,16 +71,27 @@ globalThis.app = Vue.createApp({
     # Инициализация PouchDB
     try
       await pouchService.init()
-      console.dir 'PouchDB инициализирован'
+      console.log 'PouchDB инициализирован'
+      # Загружаем настройки домена
+      await @loadDomainSettings()
     catch error
       console.error 'Ошибка инициализации PouchDB:', error
     
     # Загрузка пользователя (если авторизован)
     @loadUserData()
   
-
+  loadUserData: ->
+    # Загрузка данных пользователя из localStorage или PouchDB
+    userData = localStorage.getItem 'user'
+    if userData
+      try
+        @user = JSON.parse userData
+      catch
+        @user = null
 })
 
+
+
 # Настройка маршрутизатора
 router = VueRouter.createRouter({
   history: VueRouter.createWebHistory()

+ 12 - 8
app/index.pug

@@ -1,10 +1,16 @@
 div(id="app")
+  // Единый header для всего приложения
   header(class="header")
     nav(class="header__nav")
       div(class="header__nav-block")
         div(class="header__brand")
-          img(src="/images/logo.png" alt="Браер-Колор" class="header__logo")
-          span(class="header__company-name") Браер-Колор
+          img(
+            v-if="currentDomainSettings && currentDomainSettings.logo"
+            :src="currentDomainSettings.logo" 
+            :alt="currentDomainSettings.companyName || 'Браер-Колор'"
+            class="header__logo"
+          )
+          span(v-else class="header__nav-name") {{ companyName }}
         div(class="header__nav-menu")
           div(class="header__menu-item")
             router-link(to="/catalog" class="header__menu-link") Каталог
@@ -27,14 +33,12 @@ div(id="app")
     div(class="footer__content")
       div(class="footer__sections")
         div(class="footer__section")
-          div(class="footer__brand")
-            img(src="/images/logo.png" alt="Браер-Колор" class="footer__logo")
-            h3(class="footer__section-title") Браер-Колор
-          p(class="footer__section-text") Интернет-магазин лакокрасочной продукции
+          h3(class="footer__section-title") {{ currentDomainSettings?.companyName || companyName }}
+          p(class="footer__section-text") {{ currentDomainSettings?.description || 'Интернет-магазин лакокрасочной продукции' }}
         div(class="footer__section")
           h3(class="footer__section-title") Контакты
-          p(class="footer__section-text") Email: info@braer-color.ru
-          p(class="footer__section-text") Телефон: +7 (999) 999-99-99
+          p(class="footer__section-text") {{ currentDomainSettings?.email || 'info@braer-color.ru' }}
+          p(class="footer__section-text") {{ currentDomainSettings?.phone || '+7 (999) 999-99-99' }}
         div(class="footer__section")
           h3(class="footer__section-title") Быстрые ссылки
           div(class="footer__links")

+ 84 - 69
app/index.styl

@@ -1,7 +1,4 @@
-// =============================================
 // Переменные цветов
-// =============================================
-
 $primary-50 = #fef2f2
 $primary-100 = #fee2e2
 $primary-200 = #fecaca
@@ -38,10 +35,7 @@ $gray-900 = #111827
 $white = #ffffff
 $black = #000000
 
-// =============================================
 // Миксины
-// =============================================
-
 flex-center()
   display: flex
   align-items: center
@@ -55,10 +49,7 @@ flex-between()
 transition-all($duration = 0.3s)
   transition: all $duration ease-in-out
 
-// =============================================
-// Базовые стили приложения
-// =============================================
-
+// Базовые стили
 #app
   min-height: 100vh
   background-color: $white
@@ -66,10 +57,7 @@ transition-all($duration = 0.3s)
     background-color: $gray-900
   transition: background-color 0.3s ease-in-out
 
-// =============================================
-// Стили header
-// =============================================
-
+// Header
 .header
   background-color: $white
   box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)
@@ -94,12 +82,12 @@ transition-all($duration = 0.3s)
   gap: 0.75rem
 
 .header__logo
-  width: 2rem
-  height: 2rem
+  width: auto
+  height: 5rem
   object-fit: contain
 
-.header__company-name
-  font-size: 1.25rem
+.header__nav-name
+  font-size: 1.5rem
   font-weight: bold
   color: $gray-900
 
@@ -148,33 +136,25 @@ transition-all($duration = 0.3s)
     &:hover
       background-color: $gray-600
 
-// =============================================
-// Стили main content
-// =============================================
-
+// Main content
 .main
   flex: 1
 
-// =============================================
-// Анимации переходов между страницами
-// =============================================
-
-.page-slide-enter-active,
-.page-slide-leave-active
-  transition: all 0.3s ease-in-out
+// Page transitions
+.page-slide
+  &-enter-active,
+  &-leave-active
+    transition: all 0.3s ease-in-out
 
-.page-slide-enter-from
-  opacity: 0
-  transform: translateX(1rem)
+  &-enter-from
+    opacity: 0
+    transform: translateX(1rem)
 
-.page-slide-leave-to
-  opacity: 0
-  transform: translateX(-1rem)
-
-// =============================================
-// Стили footer
-// =============================================
+  &-leave-to
+    opacity: 0
+    transform: translateX(-1rem)
 
+// Footer
 .footer
   background-color: $gray-800
   color: $white
@@ -200,17 +180,6 @@ transition-all($duration = 0.3s)
   display: flex
   flex-direction: column
 
-.footer__brand
-  display: flex
-  align-items: center
-  gap: 0.5rem
-  margin-bottom: 0.5rem
-
-.footer__logo
-  width: 1.5rem
-  height: 1.5rem
-  object-fit: contain
-
 .footer__section-title
   font-size: 1.125rem
   font-weight: bold
@@ -233,10 +202,7 @@ transition-all($duration = 0.3s)
   &:hover
     color: $white
 
-// =============================================
 // Утилитарные классы
-// =============================================
-
 .btn-primary
   background-color: $primary-500
   color: $white
@@ -318,43 +284,92 @@ transition-all($duration = 0.3s)
   .dark &
     color: $gray-300
 
-// =============================================
-// Адаптивные стили
-// =============================================
+// Admin panel overrides
+.admin
+  .header
+    position: static
+    box-shadow: none
+    border-bottom: 1px solid $gray-200
+
+    .dark &
+      border-bottom-color: $gray-700
 
+// Responsive design
 @media (max-width: 768px)
+  .header__nav
+    padding: 0.5rem
+
   .header__nav-block
     flex-direction: column
     gap: 1rem
 
-  .header__brand
+  .header__nav-menu
+    width: 100%
     justify-content: center
 
-  .header__nav-menu
+  .header__brand
     justify-content: center
-    flex-wrap: wrap
 
   .footer__sections
     grid-template-columns: 1fr
     gap: 1.5rem
 
-@media (max-width: 480px)
-  .header__nav
-    padding: 0.5rem
+  .footer__content
+    padding: 0 0.5rem
 
-  .header__company-name
-    font-size: 1.1rem
+@media (max-width: 480px)
+  .header__nav-menu
+    flex-wrap: wrap
+    gap: 0.5rem
 
   .header__menu-link
     font-size: 0.875rem
 
-  .footer__content
-    padding: 0 0.5rem
+  .header__logo
+    width: 1.5rem
+    height: 1.5rem
+
+  .header__nav-name
+    font-size: 1.25rem
+
+// Print styles
+@media print
+  .header,
+  .footer
+    display: none
+
+  #app
+    background-color: $white
+
+// High contrast mode support
+@media (prefers-contrast: high)
+  .header__menu-link
+    color: $black
+
+    .dark &
+      color: $white
+
+  .btn-primary
+    background-color: $black
+    color: $white
+
+    .dark &
+      background-color: $white
+      color: $black
+
+// Reduced motion support
+@media (prefers-reduced-motion: reduce)
+  .page-slide
+    &-enter-active,
+    &-leave-active
+      transition: none
 
-// =============================================
-// Темная тема (автоматическая)
-// =============================================
+  .header__menu-link,
+  .btn-primary,
+  .btn-secondary
+    transition: none
 
+// Темная тема
 @media (prefers-color-scheme: dark)
   :root
     color-scheme: dark

+ 60 - 132
app/pages/Admin/Settings/index.coffee

@@ -1,4 +1,4 @@
-document.head.insertAdjacentHTML('beforeend','<style type="text/css">'+stylFns['app/pages/Admin/Settings/index.styl']+'</style>')
+document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss">'+stylFns['app/pages/Admin/Settings/index.styl']+'</style>')
 
 PouchDB = require 'app/utils/pouch'
 
@@ -9,41 +9,25 @@ module.exports =
   
   data: ->
     return {
-      activeTab: 'domains'
+      activeTab: 'brand'
       tabs: [
+        { id: 'brand', name: 'Бренд' }
+        { id: 'favicon', name: 'Фавикон' }
         { id: 'domains', name: 'Домены' }
         { id: 'languages', name: 'Языки' }
-        { id: 'general', name: 'Общие настройки' }
       ]
-      domains: []
-      languages: [
-        { code: 'ru', name: 'Русский', enabled: true }
-        { code: 'en', name: 'English', enabled: false }
-        { code: 'de', name: 'Deutsch', enabled: false }
-      ]
-      availableLanguages: [
-        { code: 'ru', name: 'Русский' }
-        { code: 'en', name: 'English' }
-        { code: 'de', name: 'Deutsch' }
-      ]
-      generalSettings: {
-        companyName: 'Браер-Колор'
-        notificationEmail: 'admin@braer-color.ru'
-        currency: 'RUB'
-      }
-      showDomainModal: false
-      editingDomain: null
-      domainForm: {
-        domain: ''
+      brandSettings: {
+        logo: ''
+        favicon: ''
         companyName: ''
-        languages: ['ru']
+        description: ''
+        email: ''
+        phone: ''
       }
+      domains: []
+      currentDomain: window.location.hostname
     }
   
-  computed:
-    currentDomain: ->
-      window.location.hostname
-  
   methods:
     getTabClass: (tab) ->
       baseClass = 'admin-settings__tab'
@@ -54,125 +38,69 @@ module.exports =
       else
         return "#{baseClass} admin-settings__tab--inactive"
     
-    getLanguageBtnClass: (lang) ->
-      baseClass = 'admin-settings__language-btn'
-      if lang.enabled
-        return "#{baseClass} admin-settings__language-btn--enabled"
-      else
-        return "#{baseClass} admin-settings__language-btn--disabled"
-    
-    loadDomains: ->
-      PouchDB.queryView('admin', 'domain_settings', { include_docs: true })
-        .then (result) =>
-          @domains = result.rows.map (row) -> row.doc
-        .catch (error) =>
-          console.error 'Ошибка загрузки доменов:', error
-          @showNotification 'Ошибка загрузки доменов', 'error'
-    
-    loadGeneralSettings: ->
-      PouchDB.getDocument('settings:general')
+    loadBrandSettings: ->
+      PouchDB.getDocument("domain_settings:#{@currentDomain}")
         .then (settings) =>
-          @generalSettings = { ...@generalSettings, ...settings }
+          if settings
+            @brandSettings = { ...@brandSettings, ...settings }
         .catch (error) =>
-          debug.log 'Общие настройки не найдены, используются значения по умолчанию'
+          console.log 'Настройки бренда не найдены'
     
-    saveGeneralSettings: ->
-      settingsDoc = {
-        _id: 'settings:general'
-        type: 'settings'
-        ...@generalSettings
-        updatedAt: new Date().toISOString()
-      }
-      
-      PouchDB.saveToRemote(settingsDoc)
-        .then (result) =>
-          @showNotification 'Общие настройки сохранены'
-        .catch (error) =>
-          console.error 'Ошибка сохранения настроек:', error
-          @showNotification 'Ошибка сохранения настроек', 'error'
+    onLogoUpload: (event) ->
+      file = event.target.files[0]
+      if file
+        # В реальном приложении здесь будет загрузка файла на сервер
+        # и получение пути к изображению
+        reader = new FileReader()
+        reader.onload = (e) =>
+          # Временное решение - используем data URL
+          # В продакшене нужно загружать на сервер и получать путь /d/[db]/[doc]/[filename]
+          @brandSettings.logo = e.target.result
+        reader.readAsDataURL(file)
     
-    addDomain: ->
-      @editingDomain = null
-      @domainForm = {
-        domain: ''
-        companyName: ''
-        languages: ['ru']
-      }
-      @showDomainModal = true
+    removeLogo: ->
+      @brandSettings.logo = ''
     
-    editDomain: (domain) ->
-      @editingDomain = domain
-      @domainForm = {
-        domain: domain.domain
-        companyName: domain.companyName
-        languages: domain.languages || ['ru']
-      }
-      @showDomainModal = true
+    onFaviconUpload: (event) ->
+      file = event.target.files[0]
+      if file
+        reader = new FileReader()
+        reader.onload = (e) =>
+          @brandSettings.favicon = e.target.result
+        reader.readAsDataURL(file)
     
-    saveDomain: ->
-      if !@domainForm.domain
-        @showNotification 'Введите домен', 'error'
-        return
-      
+    removeFavicon: ->
+      @brandSettings.favicon = ''
+    
+    saveBrandSettings: ->
       domainDoc = {
-        _id: "domain_settings:#{@domainForm.domain}"
+        _id: "domain_settings:#{@currentDomain}"
         type: 'domain_settings'
-        domain: @domainForm.domain
-        companyName: @domainForm.companyName
-        languages: @domainForm.languages
-        createdAt: if @editingDomain then @editingDomain.createdAt else new Date().toISOString()
+        domain: @currentDomain
+        ...@brandSettings
         updatedAt: new Date().toISOString()
       }
       
-      if @editingDomain
-        domainDoc._rev = @editingDomain._rev
-      
-      PouchDB.saveToRemote(domainDoc)
-        .then (result) =>
-          @showDomainModal = false
-          @loadDomains()
-          @showNotification 'Домен сохранен'
+      # Проверяем существование документа
+      PouchDB.getDocument(domainDoc._id)
+        .then (existingDoc) =>
+          domainDoc._rev = existingDoc._rev
+          return PouchDB.saveToRemote(domainDoc)
         .catch (error) =>
-          console.error 'Ошибка сохранения домена:', error
-          @showNotification 'Ошибка сохранения домена', 'error'
-    
-    deleteDomain: (domainId) ->
-      if confirm('Вы уверены, что хотите удалить этот домен?')
-        PouchDB.getDocument(domainId)
-          .then (doc) ->
-            PouchDB.saveToRemote({ ...doc, _deleted: true })
-          .then (result) =>
-            @loadDomains()
-            @showNotification 'Домен удален'
-          .catch (error) =>
-            console.error 'Ошибка удаления домена:', error
-            @showNotification 'Ошибка удаления домена', 'error'
-    
-    toggleLanguage: (langCode) ->
-      language = @languages.find (lang) -> lang.code == langCode
-      if language
-        language.enabled = !language.enabled
-        # Сохраняем настройки языков
-        @saveLanguageSettings()
-    
-    saveLanguageSettings: ->
-      languagesDoc = {
-        _id: 'settings:languages'
-        type: 'settings'
-        languages: @languages.filter((lang) -> lang.enabled).map((lang) -> lang.code)
-        updatedAt: new Date().toISOString()
-      }
-      
-      PouchDB.saveToRemote(languagesDoc)
+          if error.status == 404
+            return PouchDB.saveToRemote(domainDoc)
+          else
+            throw error
         .then (result) =>
-          @showNotification 'Настройки языков обновлены'
+          @showNotification 'Настройки бренда сохранены'
+          # Обновляем данные в главном приложении
+          @$root.loadDomainSettings?()
         .catch (error) =>
-          console.error 'Ошибка сохранения языков:', error
-          @showNotification 'Ошибка сохранения языков', 'error'
+          console.error 'Ошибка сохранения настроек бренда:', error
+          @showNotification 'Ошибка сохранения настроек бренда', 'error'
     
     showNotification: (message, type = 'success') ->
-      @$root.showNotification?(message, type) || debug.log("#{type}: #{message}")
+      @$root.showNotification?(message, type) || console.log("#{type}: #{message}")
   
   mounted: ->
-    @loadDomains()
-    @loadGeneralSettings()
+    @loadBrandSettings()

+ 73 - 114
app/pages/Admin/Settings/index.pug

@@ -1,7 +1,7 @@
 div(class="admin-settings")
   div(class="admin-settings__header")
-    h1(class="admin-settings__title") Настройки системы
-    p(class="admin-settings__subtitle") Управление доменами, языками и основными параметрами
+    h1(class="admin-settings__title") Настройки бренда и домена
+    p(class="admin-settings__subtitle") Управление логотипом, названием компании и контактной информацией
   
   div(class="admin-settings__content")
     div(class="admin-settings__tabs")
@@ -13,140 +13,99 @@ div(class="admin-settings")
       ) {{ tab.name }}
     
     div(class="admin-settings__tab-content")
-      // Вкладка доменов
-      div(v-if="activeTab === 'domains'" class="admin-settings__domains")
+      // Вкладка бренда
+      div(v-if="activeTab === 'brand'" class="admin-settings__brand")
         div(class="admin-settings__section")
-          h2(class="admin-settings__section-title") Управление доменами
-          p(class="admin-settings__section-description") Настройка мультидоменной структуры магазина
-        
-        div(class="admin-settings__domains-list")
-          div(
-            v-for="domain in domains"
-            :key="domain._id"
-            class="admin-settings__domain-item"
-          )
-            div(class="admin-settings__domain-info")
-              h3(class="admin-settings__domain-name") {{ domain.domain }}
-              p(class="admin-settings__domain-desc") {{ domain.companyName }}
-            div(class="admin-settings__domain-actions")
-              button(
-                @click="editDomain(domain)"
-                class="admin-settings__btn admin-settings__btn--secondary"
-              ) Редактировать
-              button(
-                @click="deleteDomain(domain._id)"
-                class="admin-settings__btn admin-settings__btn--danger"
-              ) Удалить
-        
-        button(
-          @click="showDomainModal = true"
-          class="admin-settings__btn admin-settings__btn--primary"
-        ) Добавить домен
-      
-      // Вкладка языков
-      div(v-if="activeTab === 'languages'" class="admin-settings__languages")
-        div(class="admin-settings__section")
-          h2(class="admin-settings__section-title") Настройка языков
-          p(class="admin-settings__section-description") Управление мультиязычностью сайта
-        
-        div(class="admin-settings__languages-list")
-          div(
-            v-for="lang in languages"
-            :key="lang.code"
-            class="admin-settings__language-item"
-          )
-            div(class="admin-settings__language-info")
-              span(class="admin-settings__language-code") {{ lang.code.toUpperCase() }}
-              span(class="admin-settings__language-name") {{ lang.name }}
-            div(class="admin-settings__language-actions")
-              button(
-                @click="toggleLanguage(lang.code)"
-                :class="getLanguageBtnClass(lang)"
-              ) {{ lang.enabled ? 'Отключить' : 'Включить' }}
-      
-      // Вкладка общих настроек
-      div(v-if="activeTab === 'general'" class="admin-settings__general")
-        div(class="admin-settings__section")
-          h2(class="admin-settings__section-title") Общие настройки
-          p(class="admin-settings__section-description") Основные параметры системы
+          h2(class="admin-settings__section-title") Настройки бренда
+          p(class="admin-settings__section-description") Управление логотипом и визуальным представлением компании
         
         div(class="admin-settings__form")
+          div(class="admin-settings__form-group")
+            label(class="admin-settings__label") Логотип компании
+            div(class="admin-settings__logo-upload")
+              div(v-if="brandSettings.logo" class="admin-settings__logo-preview")
+                img(:src="brandSettings.logo" :alt="brandSettings.companyName" class="admin-settings__logo-image")
+                button(
+                  @click="removeLogo"
+                  class="admin-settings__btn admin-settings__btn--danger"
+                ) Удалить
+              div(v-else class="admin-settings__logo-placeholder") Логотип не загружен
+              
+              div(class="admin-settings__upload-controls")
+                input(
+                  type="file"
+                  ref="logoInput"
+                  @change="onLogoUpload"
+                  accept="image/*"
+                  class="admin-settings__file-input"
+                  id="logo-upload"
+                )
+                label(for="logo-upload" class="admin-settings__btn admin-settings__btn--secondary") Выбрать файл
+                p(class="admin-settings__help-text") Рекомендуемый размер: 200x60px. Форматы: PNG, JPG, SVG
+        
           div(class="admin-settings__form-group")
             label(class="admin-settings__label") Название компании
             input(
-              v-model="generalSettings.companyName"
+              v-model="brandSettings.companyName"
               type="text"
               class="admin-settings__input"
               placeholder="Введите название компании"
             )
           
           div(class="admin-settings__form-group")
-            label(class="admin-settings__label") Email для уведомлений
+            label(class="admin-settings__label") Описание компании
+            textarea(
+              v-model="brandSettings.description"
+              class="admin-settings__textarea"
+              placeholder="Краткое описание компании для футера"
+              rows="3"
+            )
+          
+          div(class="admin-settings__form-group")
+            label(class="admin-settings__label") Email для связи
             input(
-              v-model="generalSettings.notificationEmail"
+              v-model="brandSettings.email"
               type="email"
               class="admin-settings__input"
-              placeholder="admin@example.com"
+              placeholder="info@example.com"
             )
           
           div(class="admin-settings__form-group")
-            label(class="admin-settings__label") Валюта
-            select(v-model="generalSettings.currency" class="admin-settings__select")
-              option(value="RUB") RUB - Российский рубль
-              option(value="USD") USD - Доллар США
-              option(value="EUR") EUR - Евро
+            label(class="admin-settings__label") Телефон
+            input(
+              v-model="brandSettings.phone"
+              type="tel"
+              class="admin-settings__input"
+              placeholder="+7 (999) 999-99-99"
+            )
           
           button(
-            @click="saveGeneralSettings"
+            @click="saveBrandSettings"
             class="admin-settings__btn admin-settings__btn--primary"
-          ) Сохранить настройки
-  
-  // Модальное окно добавления домена
-  div(v-if="showDomainModal" class="admin-settings__modal")
-    div(class="admin-settings__modal-content")
-      h3(class="admin-settings__modal-title") {{ editingDomain ? 'Редактирование' : 'Добавление' }} домена
+          ) Сохранить настройки бренда
       
-      div(class="admin-settings__modal-form")
-        div(class="admin-settings__form-group")
-          label(class="admin-settings__label") Домен
-          input(
-            v-model="domainForm.domain"
-            type="text"
-            class="admin-settings__input"
-            placeholder="example.com"
-          )
-        
-        div(class="admin-settings__form-group")
-          label(class="admin-settings__label") Название компании
-          input(
-            v-model="domainForm.companyName"
-            type="text"
-            class="admin-settings__input"
-            placeholder="Название компании"
-          )
+      // Вкладка фавиконов
+      div(v-if="activeTab === 'favicon'" class="admin-settings__favicon")
+        div(class="admin-settings__section")
+          h2(class="admin-settings__section-title") Фавикон и иконки
+          p(class="admin-settings__section-description") Управление иконками для браузера и мобильных устройств
         
-        div(class="admin-settings__form-group")
-          label(class="admin-settings__label") Языки домена
-          div(class="admin-settings__checkbox-group")
-            label(
-              v-for="lang in availableLanguages"
-              :key="lang.code"
-              class="admin-settings__checkbox-label"
-            )
+        div(class="admin-settings__form")
+          div(class="admin-settings__form-group")
+            label(class="admin-settings__label") Фавикон (16x16px)
+            div(class="admin-settings__favicon-upload")
+              div(v-if="brandSettings.favicon" class="admin-settings__favicon-preview")
+                img(:src="brandSettings.favicon" alt="Favicon" class="admin-settings__favicon-image")
+                button(
+                  @click="removeFavicon"
+                  class="admin-settings__btn admin-settings__btn--danger"
+                ) Удалить
+              div(v-else class="admin-settings__favicon-placeholder") Фавикон не загружен
+              
               input(
-                type="checkbox"
-                :value="lang.code"
-                v-model="domainForm.languages"
-                class="admin-settings__checkbox"
+                type="file"
+                @change="onFaviconUpload"
+                accept="image/*"
+                class="admin-settings__file-input"
               )
-              span {{ lang.name }}
-      
-      div(class="admin-settings__modal-actions")
-        button(
-          @click="saveDomain"
-          class="admin-settings__btn admin-settings__btn--primary"
-        ) {{ editingDomain ? 'Обновить' : 'Добавить' }}
-        button(
-          @click="showDomainModal = false"
-          class="admin-settings__btn admin-settings__btn--secondary"
-        ) Отмена
+              p(class="admin-settings__help-text") Рекомендуемый размер: 16x16px или 32x32px. Формат: ICO или PNG

+ 142 - 11
app/pages/Admin/index.coffee

@@ -2,25 +2,50 @@ document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss">'+s
 
 PouchDB = require 'app/utils/pouch'
 
-# Иконки для меню (упрощенные компоненты Vue)
+# Иконки для меню (упрощенные компоненты)
 MenuIcons =
   SliderIcon:
-    template: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h16M4 16h16"></path></svg>'
+    template: """
+      <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
+        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h16M4 16h16"></path>
+      </svg>
+    """
   
   ProductsIcon:
-    template: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path></svg>'
+    template: """
+      <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
+        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path>
+      </svg>
+    """
   
   ClientsIcon:
-    template: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path></svg>'
+    template: """
+      <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
+        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
+      </svg>
+    """
   
   BlogIcon:
-    template: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path></svg>'
+    template: """
+      <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
+        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
+      </svg>
+    """
   
   RoutesIcon:
-    template: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path></svg>'
+    template: """
+      <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
+        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path>
+      </svg>
+    """
   
   SettingsIcon:
-    template: '<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>'
+    template: """
+      <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
+        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
+        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
+      </svg>
+    """
 
 module.exports =
   name: 'AdminPanel'
@@ -31,7 +56,13 @@ module.exports =
   data: ->
     return {
       currentDomain: window.location.hostname
-      menuItems: [
+      theme: localStorage.getItem('theme') || 'light'
+      mobileMenuOpen: false
+      sidebarCollapsed: false
+      notificationsOpen: false
+      currentDomainSettings: null
+      user: null
+      mainMenuItems: [
         {
           id: 'slider'
           name: 'Слайдер'
@@ -50,6 +81,8 @@ module.exports =
           path: '/admin/clients'
           icon: 'ClientsIcon'
         }
+      ]
+      contentMenuItems: [
         {
           id: 'blog'
           name: 'Блог'
@@ -62,6 +95,8 @@ module.exports =
           path: '/admin/routes'
           icon: 'RoutesIcon'
         }
+      ]
+      systemMenuItems: [
         {
           id: 'settings'
           name: 'Настройки'
@@ -69,14 +104,26 @@ module.exports =
           icon: 'SettingsIcon'
         }
       ]
+      breadcrumbs: []
+      notifications: []
+      unreadNotifications: 0
     }
   
   computed:
     currentRoute: ->
       @$route.path.split('/').pop() || 'settings'
+    
+    userInitials: ->
+      return 'АД' unless @user?.name
+      names = @user.name.split(' ')
+      if names.length >= 2
+        (names[0][0] + names[1][0]).toUpperCase()
+      else
+        @user.name.substring(0, 2).toUpperCase()
   
   methods:
     navigateTo: (path) ->
+      @mobileMenuOpen = false
       @$router.push(path)
     
     getMenuItemClass: (item) ->
@@ -88,13 +135,97 @@ module.exports =
       else
         return "#{baseClass} admin__nav-item--inactive"
     
+    toggleTheme: ->
+      @theme = if @theme == 'light' then 'dark' else 'light'
+      localStorage.setItem 'theme', @theme
+      document.documentElement.classList.toggle 'dark'
+      @$root.theme = @theme
+    
+    refreshData: ->
+      @showNotification 'Данные обновлены'
+    
+    markAsRead: (notificationId) ->
+      notification = @notifications.find (n) -> n.id == notificationId
+      if notification && !notification.read
+        notification.read = true
+        @unreadNotifications -= 1
+    
+    logout: ->
+      localStorage.removeItem 'user'
+      @user = null
+      @$router.push('/')
+      @showNotification 'Вы успешно вышли из системы'
+    
     loadDomainSettings: ->
       PouchDB.getDocument("domain_settings:#{@currentDomain}")
         .then (settings) =>
-          @domainSettings = settings
+          @currentDomainSettings = settings
         .catch (error) =>
-          debug.log 'Настройки домена не найдены, используются значения по умолчанию'
-          @domainSettings = null
+          console.log 'Настройки домена не найдены, используются значения по умолчанию'
+          @currentDomainSettings = null
+    
+    loadUserData: ->
+      userData = localStorage.getItem 'user'
+      if userData
+        try
+          @user = JSON.parse userData
+        catch
+          @user = null
+      else
+        # Заглушка для демонстрации
+        @user = { name: 'Администратор Системы', role: 'admin' }
+    
+    updateBreadcrumbs: ->
+      routeName = @currentRoute
+      breadcrumbMap =
+        slider: { title: 'Слайдер' }
+        products: { title: 'Товары' }
+        clients: { title: 'Клиенты' }
+        blog: { title: 'Блог' }
+        routes: { title: 'Маршруты' }
+        settings: { title: 'Настройки' }
+      
+      current = breadcrumbMap[routeName]
+      if current
+        @breadcrumbs = [
+          { title: 'Главная', path: '/admin' }
+          { title: current.title }
+        ]
+      else
+        @breadcrumbs = [{ title: 'Главная' }]
+    
+    showNotification: (message, type = 'success') ->
+      @$root.showNotification?(message, type) || console.log("#{type}: #{message}")
+  
+  watch:
+    currentRoute:
+      immediate: true
+      handler: ->
+        @updateBreadcrumbs()
   
   mounted: ->
     @loadDomainSettings()
+    @loadUserData()
+    
+    # Инициализация темы
+    if @theme == 'dark'
+      document.documentElement.classList.add 'dark'
+    
+    # Заглушка для уведомлений
+    @notifications = [
+      {
+        id: 1
+        title: 'Новый заказ получен'
+        time: '5 мин назад'
+        read: false
+        icon: 'SettingsIcon'
+      }
+      {
+        id: 2
+        title: 'Обновление системы'
+        time: '1 час назад'
+        read: false
+        icon: 'SettingsIcon'
+      }
+    ]
+    @unreadNotifications = @notifications.filter((n) -> !n.read).length

+ 195 - 19
app/pages/Admin/index.pug

@@ -1,25 +1,201 @@
 div(class="admin")
-  div(class="admin__header")
-    div(class="admin__header-content")
-      div(class="admin__brand")
-        img(src="/images/logo.png" alt="Браер-Колор" class="admin__logo")
-        span(class="admin__company-name") Браер-Колор
-      div(class="admin__domain-info") {{ currentDomain }}
-      div(class="admin__user-menu")
-        button(@click="toggleTheme" class="admin__theme-toggle") {{ theme === 'light' ? '🌙' : '☀️' }}
+  // Мобильное меню - кнопка бургер
+  div(class="admin__mobile-toggle" @click="mobileMenuOpen = !mobileMenuOpen")
+    div(class="admin__burger" :class="{'admin__burger--active': mobileMenuOpen}")
+      span
+      span
+      span
   
-  nav(class="admin__nav")
-    div(class="admin__nav-content")
-      a(
-        v-for="item in menuItems"
-        :key="item.id"
-        :href="item.path"
-        @click.prevent="navigateTo(item.path)"
-        :class="getMenuItemClass(item)"
+  // Боковое меню для десктопа и мобильных устройств
+  div(
+    class="admin__sidebar" 
+    :class="{'admin__sidebar--mobile-open': mobileMenuOpen, 'admin__sidebar--collapsed': sidebarCollapsed}"
+  )
+    div(class="admin__sidebar-header")
+      div(class="admin__brand" v-if="!sidebarCollapsed")
+        img(
+          v-if="currentDomainSettings && currentDomainSettings.logo"
+          :src="currentDomainSettings.logo" 
+          :alt="currentDomainSettings.companyName || 'Браер-Колор'"
+          class="admin__logo"
+        )
+        span(class="admin__company-name") {{ currentDomainSettings?.companyName || 'Браер-Колор' }}
+      div(class="admin__sidebar-toggle" @click="sidebarCollapsed = !sidebarCollapsed")
+        div(class="admin__toggle-icon" :class="{'admin__toggle-icon--collapsed': sidebarCollapsed}")
+          svg(fill="none" stroke="currentColor" viewBox="0 0 24 24")
+            path(stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7")
+    
+    nav(class="admin__nav")
+      div(class="admin__nav-section")
+        h3(
+          v-if="!sidebarCollapsed" 
+          class="admin__nav-section-title"
+        ) Основные
+        ul(class="admin__nav-list")
+          li(
+            v-for="item in mainMenuItems"
+            :key="item.id"
+            class="admin__nav-item"
+          )
+            a(
+              :href="item.path"
+              @click.prevent="navigateTo(item.path)"
+              :class="getMenuItemClass(item)"
+              class="admin__nav-link"
+            )
+              component(:is="item.icon" class="admin__nav-icon")
+              span(
+                v-if="!sidebarCollapsed" 
+                class="admin__nav-text"
+              ) {{ item.name }}
+              span(
+                v-if="!sidebarCollapsed && item.badge"
+                class="admin__nav-badge"
+              ) {{ item.badge }}
+      
+      div(class="admin__nav-section")
+        h3(
+          v-if="!sidebarCollapsed" 
+          class="admin__nav-section-title"
+        ) Контент
+        ul(class="admin__nav-list")
+          li(
+            v-for="item in contentMenuItems"
+            :key="item.id"
+            class="admin__nav-item"
+          )
+            a(
+              :href="item.path"
+              @click.prevent="navigateTo(item.path)"
+              :class="getMenuItemClass(item)"
+              class="admin__nav-link"
+            )
+              component(:is="item.icon" class="admin__nav-icon")
+              span(
+                v-if="!sidebarCollapsed" 
+                class="admin__nav-text"
+              ) {{ item.name }}
+      
+      div(class="admin__nav-section")
+        h3(
+          v-if="!sidebarCollapsed" 
+          class="admin__nav-section-title"
+        ) Система
+        ul(class="admin__nav-list")
+          li(
+            v-for="item in systemMenuItems"
+            :key="item.id"
+            class="admin__nav-item"
+          )
+            a(
+              :href="item.path"
+              @click.prevent="navigateTo(item.path)"
+              :class="getMenuItemClass(item)"
+              class="admin__nav-link"
+            )
+              component(:is="item.icon" class="admin__nav-icon")
+              span(
+                v-if="!sidebarCollapsed" 
+                class="admin__nav-text"
+              ) {{ item.name }}
+    
+    div(class="admin__sidebar-footer" v-if="!sidebarCollapsed")
+      div(class="admin__user-info")
+        div(class="admin__user-avatar") {{ userInitials }}
+        div(class="admin__user-details")
+          span(class="admin__user-name") {{ user?.name || 'Администратор' }}
+          span(class="admin__user-role") {{ user?.role || 'Admin' }}
+      button(
+        @click="logout"
+        class="admin__logout-btn"
       )
-        component(:is="item.icon" class="admin__nav-icon")
-        span(class="admin__nav-text") {{ item.name }}
+        svg(fill="none" stroke="currentColor" viewBox="0 0 24 24")
+          path(stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1")
+        span Выйти
   
-  main(class="admin__main")
+  // Основной контент
+  div(
+    class="admin__main" 
+    :class="{'admin__main--expanded': sidebarCollapsed, 'admin__main--mobile-open': mobileMenuOpen}"
+  )
+    div(class="admin__topbar")
+      div(class="admin__breadcrumbs")
+        span(
+          v-for="(breadcrumb, index) in breadcrumbs"
+          :key="index"
+          class="admin__breadcrumb"
+        )
+          a(
+            v-if="breadcrumb.path && index !== breadcrumbs.length - 1"
+            :href="breadcrumb.path"
+            @click.prevent="navigateTo(breadcrumb.path)"
+            class="admin__breadcrumb-link"
+          ) {{ breadcrumb.title }}
+          span(v-else class="admin__breadcrumb-current") {{ breadcrumb.title }}
+          span(
+            v-if="index !== breadcrumbs.length - 1"
+            class="admin__breadcrumb-separator"
+          ) /
+      
+      div(class="admin__actions")
+        button(
+          @click="toggleTheme"
+          class="admin__action-btn"
+          :title="theme === 'light' ? 'Темная тема' : 'Светлая тема'"
+        )
+          svg(v-if="theme === 'light'" fill="none" stroke="currentColor" viewBox="0 0 24 24")
+            path(stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z")
+          svg(v-else fill="none" stroke="currentColor" viewBox="0 0 24 24")
+            path(stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z")
+        
+        button(
+          @click="refreshData"
+          class="admin__action-btn"
+          title="Обновить данные"
+        )
+          svg(fill="none" stroke="currentColor" viewBox="0 0 24 24")
+            path(stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15")
+        
+        div(class="admin__notifications")
+          button(
+            @click="notificationsOpen = !notificationsOpen"
+            class="admin__action-btn admin__notifications-btn"
+            :class="{'admin__notifications-btn--active': notificationsOpen}"
+          )
+            svg(fill="none" stroke="currentColor" viewBox="0 0 24 24")
+              path(stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-5 5v-5zM4.5 6.5h15M4.5 12h15")
+            span(v-if="unreadNotifications > 0" class="admin__notification-badge") {{ unreadNotifications }}
+          
+          div(
+            v-if="notificationsOpen"
+            class="admin__notifications-dropdown"
+          )
+            div(class="admin__notifications-header")
+              h4 Уведомления
+              span(class="admin__notifications-count") {{ unreadNotifications }} новых
+            div(class="admin__notifications-list")
+              div(
+                v-for="notification in notifications"
+                :key="notification.id"
+                class="admin__notification-item"
+                :class="{'admin__notification-item--unread': !notification.read}"
+              )
+                div(class="admin__notification-icon")
+                  component(:is="notification.icon")
+                div(class="admin__notification-content")
+                  p(class="admin__notification-title") {{ notification.title }}
+                  span(class="admin__notification-time") {{ notification.time }}
+                button(
+                  @click="markAsRead(notification.id)"
+                  class="admin__notification-action"
+                ) ×
+    
     div(class="admin__content")
       router-view
+  
+  // Оверлей для мобильного меню
+  div(
+    v-if="mobileMenuOpen"
+    class="admin__overlay"
+    @click="mobileMenuOpen = false"
+  )

+ 494 - 136
app/pages/Admin/index.styl

@@ -1,31 +1,92 @@
+// Admin panel responsive styles
 .admin
   display: flex
-  flex-direction: column
   min-height: 100vh
-  background-color: $gray-50
+  background-color: #f9fafb
+  position: relative
+
+  &.dark
+    background-color: #111827
+
+// Mobile toggle button
+.admin__mobile-toggle
+  display: none
+  position: fixed
+  top: 1rem
+  left: 1rem
+  z-index: 1000
+  padding: 0.5rem
+  background-color: #ef4444
+  border-radius: 0.375rem
+  cursor: pointer
+  border: none
+
+  @media (max-width: 768px)
+    display: block
 
-  .dark &
-    background-color: $gray-900
+.admin__burger
+  width: 1.5rem
+  height: 1.125rem
+  position: relative
+  transition: all 0.3s ease-in-out
 
-.admin__header
-  background-color: $white
-  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1)
-  position: sticky
+  span
+    display: block
+    height: 2px
+    width: 100%
+    background-color: #ffffff
+    border-radius: 1px
+    transition: all 0.3s ease-in-out
+    transform-origin: center
+
+    &:nth-child(2)
+      margin: 0.25rem 0
+
+.admin__burger--active
+  span
+    &:nth-child(1)
+      transform: rotate(45deg) translate(6px, 6px)
+    &:nth-child(2)
+      opacity: 0
+    &:nth-child(3)
+      transform: rotate(-45deg) translate(6px, -6px)
+
+// Sidebar styles
+.admin__sidebar
+  width: 16rem
+  background-color: #ffffff
+  box-shadow: 0 0 20px rgba(0, 0, 0, 0.1)
+  transition: all 0.3s ease-in-out
+  display: flex
+  flex-direction: column
+  position: fixed
+  left: 0
   top: 0
-  z-index: 50
-  border-bottom: 1px solid $gray-200
+  height: 100vh
+  z-index: 100
+  overflow-y: auto
+
+  &.dark
+    background-color: #1f2937
 
-  .dark &
-    background-color: $gray-800
-    border-bottom-color: $gray-700
+  @media (max-width: 768px)
+    transform: translateX(-100%)
+    
+    &.admin__sidebar--mobile-open
+      transform: translateX(0)
 
-.admin__header-content
+  &.admin__sidebar--collapsed
+    width: 5rem
+
+.admin__sidebar-header
+  padding: 1.5rem 1rem
+  border-bottom: 1px solid #e5e7eb
   display: flex
   align-items: center
   justify-content: space-between
-  padding: 1rem 1.5rem
-  max-width: 1200px
-  margin: 0 auto
+
+  &.dark
+    border-bottom-color: #374151
 
 .admin__brand
   display: flex
@@ -38,181 +99,478 @@
   object-fit: contain
 
 .admin__company-name
-  font-size: 1.25rem
+  font-size: 1.125rem
   font-weight: bold
-  color: $gray-900
+  color: #111827
+  white-space: nowrap
+  overflow: hidden
+  text-overflow: ellipsis
 
-  .dark &
-    color: $white
+  &.dark
+    color: #ffffff
 
-.admin__domain-info
-  font-size: 0.875rem
-  color: $gray-600
-  background-color: $gray-100
-  padding: 0.25rem 0.75rem
+.admin__sidebar-toggle
+  padding: 0.5rem
   border-radius: 0.375rem
+  cursor: pointer
+  border: none
+  background: none
+  color: #6b7280
+  transition: all 0.2s ease-in-out
+
+  &:hover
+    background-color: #f3f4f6
+    color: #111827
+
+  &.dark
+    color: #9ca3af
+
+    &:hover
+      background-color: #374151
+      color: #ffffff
+
+.admin__toggle-icon
+  width: 1rem
+  height: 1rem
+  transition: transform 0.3s ease-in-out
 
-  .dark &
-    color: $gray-400
-    background-color: $gray-700
+  &.admin__toggle-icon--collapsed
+    transform: rotate(180deg)
 
-.admin__user-menu
+// Navigation styles
+.admin__nav
+  flex: 1
+  padding: 1rem 0
   display: flex
-  align-items: center
-  gap: 0.5rem
+  flex-direction: column
+  gap: 1.5rem
+
+.admin__nav-section
+  padding: 0 1rem
+
+.admin__nav-section-title
+  font-size: 0.75rem
+  font-weight: 600
+  color: #6b7280
+  text-transform: uppercase
+  letter-spacing: 0.05em
+  margin-bottom: 0.75rem
+
+  &.dark
+    color: #9ca3af
+
+.admin__nav-list
+  list-style: none
+  margin: 0
+  padding: 0
+  display: flex
+  flex-direction: column
+  gap: 0.25rem
 
-.admin__theme-toggle
-  padding: 0.5rem
-  border-radius: 0.375rem
-  background-color: $gray-100
-  color: $gray-600
+.admin__nav-item
+  margin: 0
+
+.admin__nav-link
+  display: flex
+  align-items: center
+  gap: 0.75rem
+  padding: 0.75rem 1rem
+  border-radius: 0.5rem
+  text-decoration: none
   transition: all 0.2s ease-in-out
-  border: none
-  cursor: pointer
+  color: #374151
+  position: relative
 
   &:hover
-    background-color: $gray-200
+    background-color: #f3f4f6
+    color: #111827
 
-  .dark &
-    background-color: $gray-700
-    color: $gray-300
+  &.dark
+    color: #d1d5db
 
     &:hover
-      background-color: $gray-600
+      background-color: #374151
+      color: #ffffff
 
-.admin__nav
-  background-color: $white
-  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1)
-  border-bottom: 1px solid $gray-200
-  position: sticky
-  top: 64px
-  z-index: 40
+  &.admin__nav-item--active
+    background-color: #fef2f2
+    color: #dc2626
+    font-weight: 500
+
+    &.dark
+      background-color: #7f1d1d
+      color: #fca5a5
+
+.admin__nav-icon
+  width: 1.25rem
+  height: 1.25rem
+  flex-shrink: 0
+
+.admin__nav-text
+  white-space: nowrap
+  overflow: hidden
+  text-overflow: ellipsis
+  flex: 1
+
+.admin__sidebar--collapsed .admin__nav-text
+  display: none
+
+.admin__nav-badge
+  background-color: #ef4444
+  color: #ffffff
+  font-size: 0.75rem
+  padding: 0.125rem 0.5rem
+  border-radius: 9999px
+  font-weight: 500
+
+// Sidebar footer
+.admin__sidebar-footer
+  padding: 1rem
+  border-top: 1px solid #e5e7eb
+  margin-top: auto
 
-  .dark &
-    background-color: $gray-800
-    border-bottom-color: $gray-700
+  &.dark
+    border-top-color: #374151
 
-.admin__nav-content
+.admin__user-info
   display: flex
-  gap: 0
-  overflow-x: auto
-  padding: 0 1.5rem
-  max-width: 1200px
-  margin: 0 auto
+  align-items: center
+  gap: 0.75rem
+  margin-bottom: 1rem
+
+.admin__user-avatar
+  width: 2.5rem
+  height: 2.5rem
+  border-radius: 50%
+  background-color: #ef4444
+  color: #ffffff
+  display: flex
+  align-items: center
+  justify-content: center
+  font-weight: 600
+  font-size: 0.875rem
 
-  &::-webkit-scrollbar
-    display: none
+.admin__user-details
+  display: flex
+  flex-direction: column
+  flex: 1
+  min-width: 0
+
+.admin__user-name
+  font-weight: 600
+  color: #111827
+  font-size: 0.875rem
+  white-space: nowrap
+  overflow: hidden
+  text-overflow: ellipsis
 
-  -ms-overflow-style: none
-  scrollbar-width: none
+  &.dark
+    color: #ffffff
 
-.admin__nav-item
+.admin__user-role
+  font-size: 0.75rem
+  color: #6b7280
+
+  &.dark
+    color: #9ca3af
+
+.admin__logout-btn
+  width: 100%
   display: flex
   align-items: center
-  gap: 0.5rem
+  gap: 0.75rem
   padding: 0.75rem 1rem
-  color: $gray-600
-  text-decoration: none
+  border-radius: 0.5rem
+  background: none
+  border: 1px solid #e5e7eb
+  color: #374151
+  cursor: pointer
   transition: all 0.2s ease-in-out
-  border: none
+  font-size: 0.875rem
+
+  &:hover
+    background-color: #f9fafb
+    border-color: #d1d5db
+
+  &.dark
+    border-color: #4b5563
+    color: #d1d5db
+
+    &:hover
+      background-color: #374151
+      border-color: #6b7280
+
+// Main content area
+.admin__main
+  flex: 1
+  margin-left: 16rem
+  transition: all 0.3s ease-in-out
+  min-height: 100vh
+  display: flex
+  flex-direction: column
+
+  &.admin__main--expanded
+    margin-left: 5rem
+
+  @media (max-width: 768px)
+    margin-left: 0
+    
+    &.admin__main--mobile-open
+      transform: translateX(16rem)
+
+.admin__topbar
+  background-color: #ffffff
+  border-bottom: 1px solid #e5e7eb
+  padding: 1rem 1.5rem
+  display: flex
+  align-items: center
+  justify-content: space-between
+
+  &.dark
+    background-color: #1f2937
+    border-bottom-color: #374151
+
+.admin__breadcrumbs
+  display: flex
+  align-items: center
+  gap: 0.5rem
+  font-size: 0.875rem
+
+.admin__breadcrumb-link
+  color: #6b7280
+  text-decoration: none
+  transition: color 0.2s ease-in-out
+
+  &:hover
+    color: #ef4444
+
+  &.dark
+    color: #9ca3af
+
+.admin__breadcrumb-current
+  color: #111827
+  font-weight: 500
+
+  &.dark
+    color: #ffffff
+
+.admin__breadcrumb-separator
+  color: #9ca3af
+  margin-left: 0.5rem
+
+.admin__actions
+  display: flex
+  align-items: center
+  gap: 0.5rem
+
+.admin__action-btn
+  padding: 0.5rem
+  border-radius: 0.375rem
   background: none
+  border: none
+  color: #6b7280
   cursor: pointer
-  white-space: nowrap
-  border-bottom: 2px solid transparent
+  transition: all 0.2s ease-in-out
+  position: relative
 
   &:hover
-    color: $gray-900
-    background-color: $gray-50
+    background-color: #f3f4f6
+    color: #111827
 
-    .dark &
-      color: $white
-      background-color: $gray-700
+  &.dark
+    color: #9ca3af
 
-.admin__nav-item--active
-  color: $primary-600
-  border-bottom-color: $primary-600
-  background-color: $primary-50
+    &:hover
+      background-color: #374151
+      color: #ffffff
+
+// Notifications
+.admin__notifications
+  position: relative
+
+.admin__notifications-btn
+  position: relative
+
+.admin__notification-badge
+  position: absolute
+  top: -0.25rem
+  right: -0.25rem
+  background-color: #ef4444
+  color: #ffffff
+  font-size: 0.75rem
+  padding: 0.125rem 0.375rem
+  border-radius: 9999px
+  min-width: 1.25rem
+  text-align: center
+
+.admin__notifications-dropdown
+  position: absolute
+  top: 100%
+  right: 0
+  width: 20rem
+  background-color: #ffffff
+  border: 1px solid #e5e7eb
+  border-radius: 0.5rem
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1)
+  z-index: 50
+  margin-top: 0.5rem
+
+  &.dark
+    background-color: #1f2937
+    border-color: #374151
+
+.admin__notifications-header
+  padding: 1rem
+  border-bottom: 1px solid #e5e7eb
+  display: flex
+  align-items: center
+  justify-content: space-between
 
-  .dark &
-    color: $primary-400
-    border-bottom-color: $primary-400
-    background-color: $primary-900
+  &.dark
+    border-bottom-color: #374151
+
+.admin__notifications-count
+  font-size: 0.875rem
+  color: #ef4444
+  font-weight: 500
+
+.admin__notifications-list
+  max-height: 20rem
+  overflow-y: auto
+
+.admin__notification-item
+  padding: 1rem
+  border-bottom: 1px solid #f3f4f6
+  display: flex
+  align-items: flex-start
+  gap: 0.75rem
+  transition: background-color 0.2s ease-in-out
 
   &:hover
-    color: $primary-700
-    background-color: $primary-100
+    background-color: #f9fafb
 
-    .dark &
-      color: $primary-300
-      background-color: $primary-800
+  &.dark
+    border-bottom-color: #374151
 
-.admin__nav-item--inactive
-  color: $gray-600
+    &:hover
+      background-color: #374151
 
-  .dark &
-    color: $gray-400
+  &.admin__notification-item--unread
+    background-color: #fef2f2
 
-.admin__nav-icon
-  width: 1.25rem
-  height: 1.25rem
+    &.dark
+      background-color: #7f1d1d
+
+.admin__notification-icon
+  width: 1.5rem
+  height: 1.5rem
   flex-shrink: 0
+  color: #ef4444
 
-.admin__nav-text
-  font-weight: 500
+.admin__notification-content
+  flex: 1
+  min-width: 0
+
+.admin__notification-title
   font-size: 0.875rem
+  color: #111827
+  margin: 0 0 0.25rem 0
+  line-height: 1.4
 
-.admin__main
-  flex: 1
-  padding: 1.5rem
+  &.dark
+    color: #ffffff
+
+.admin__notification-time
+  font-size: 0.75rem
+  color: #6b7280
 
+.admin__notification-action
+  padding: 0.25rem
+  background: none
+  border: none
+  color: #9ca3af
+  cursor: pointer
+  border-radius: 0.25rem
+  font-size: 1.125rem
+  line-height: 1
+
+  &:hover
+    color: #6b7280
+    background-color: #f3f4f6
+
+  &.dark
+    &:hover
+      background-color: #4b5563
+      color: #d1d5db
+
+// Content area
 .admin__content
-  max-width: 1200px
-  margin: 0 auto
+  flex: 1
+  padding: 1.5rem
+  overflow-y: auto
 
-// Mobile styles
-@media (max-width: 768px)
-  .admin__header-content
-    padding: 0.75rem 1rem
-    flex-wrap: wrap
-    gap: 0.5rem
+// Overlay for mobile menu
+.admin__overlay
+  position: fixed
+  inset: 0
+  background-color: rgba(0, 0, 0, 0.5)
+  z-index: 90
+  display: none
 
-  .admin__brand
-    order: 1
+  @media (max-width: 768px)
+    display: block
 
-  .admin__domain-info
-    order: 3
-    width: 100%
-    text-align: center
-    margin-top: 0.5rem
+// Responsive adjustments
+@media (max-width: 1024px)
+  .admin__sidebar
+    width: 14rem
 
-  .admin__user-menu
-    order: 2
+  .admin__main
+    margin-left: 14rem
 
-  .admin__nav-content
-    padding: 0 1rem
-    gap: 0
+  .admin__main--expanded
+    margin-left: 4rem
 
-  .admin__nav-item
-    padding: 0.75rem 0.5rem
-    font-size: 0.875rem
+  .admin__sidebar--collapsed
+    width: 4rem
 
-  .admin__nav-text
-    font-size: 0.8rem
+@media (max-width: 768px)
+  .admin__sidebar
+    width: 16rem
 
   .admin__main
+    margin-left: 0
+
+  .admin__main--mobile-open
+    transform: translateX(16rem)
+
+  .admin__topbar
+    padding: 1rem
+
+  .admin__content
     padding: 1rem
 
-// Small mobile devices
+  .admin__breadcrumbs
+    font-size: 0.8rem
+
+  .admin__notifications-dropdown
+    width: 18rem
+    right: -1rem
+
 @media (max-width: 480px)
-  .admin__company-name
-    font-size: 1.1rem
+  .admin__mobile-toggle
+    top: 0.5rem
+    left: 0.5rem
 
-  .admin__nav-item
-    padding: 0.5rem 0.25rem
+  .admin__topbar
+    flex-direction: column
+    gap: 1rem
+    align-items: flex-start
 
-  .admin__nav-icon
-    width: 1rem
-    height: 1rem
+  .admin__actions
+    width: 100%
+    justify-content: flex-end
 
-  .admin__nav-text
-    font-size: 0.75rem
+  .admin__notifications-dropdown
+    width: 16rem
+    right: -2rem