Gogs 4 settimane fa
parent
commit
9b9a92a53a

+ 34 - 81
README.md

@@ -10,10 +10,10 @@
 
 ### Frontend
 - **Шаблонизатор:** Pug (включая Vue компоненты)
-- **Стилизация:** Tailwind CSS (исключительно через классы) в файлах styl, d качестве имён классов использоваьть методологию BEM
+- **Стилизация:**  Stylus в файлах styl, в качестве имён классов использоваьть методологию BEM
 - **Логика:** CoffeeScript с Vue.js 3
 - **Маршрутизация:** Vue Router
-- **Анимации:** Утилиты Tailwind (transition-, transform-, hover:)
+- **Анимации:** Утилиты Stylus (transition-, transform-, hover: focus: down: ) или предложи другой вариант
 - **База данных:** PouchDB(локальный кешь, открытых данных) с синхронизацией CouchDB (центральное хранилище данных, и сессий пользователей.)
 
 ### Архитектура
@@ -21,7 +21,7 @@
 app/
 ├── index.pug (основной layout)
 ├── index.coffee (инициализация Vue и роутера)
-├── index.styl (файл хранения описания классов по системе BEM с использованием Tailwind)
+├── index.styl (файл хранения описания классов по системе BEM )
 ├── utils/
 │   └── pouch.coffee (сервис работы с PouchDB)
 ├── design/
@@ -42,46 +42,15 @@ app/
 ## 💻 ПРИМЕРЫ КОДА И ПОДХОДЫ
 
 ### 🎨 Дизайн-система и организация стилей
-Важна должна храниться в БД привязанная к конкретному домену/доменам
-**theme.config.coffee:**
-```coffee
-module.exports = {
-  colors: {
-    primary: {
-      50: '#fef2f2', 100: '#fee2e2', 200: '#fecaca', 300: '#fca5a5',
-      400: '#f87171', 500: '#ef4444', 600: '#dc2626', 700: '#b91c1c',
-      800: '#991b1b', 900: '#7f1d1d'
-    }
-    accent: {
-      50: '#f0f9ff', 100: '#e0f2fe', 200: '#bae6fd', 300: '#7dd3fc',
-      400: '#38bdf8', 500: '#0ea5e9', 600: '#0284c7', 700: '#0369a1',
-      800: '#075985', 900: '#0c4a6e'
-    }
-  },
-  typography: {
-    fonts: {
-      sans: ['Inter', 'ui-sans-serif', 'system-ui']
-    },
-    sizes: {
-      xs: '0.75rem', sm: '0.875rem', base: '1rem', lg: '1.125rem',
-      xl: '1.25rem', '2xl': '1.5rem', '3xl': '1.875rem'
-    }
-  }
-}
-```
+
 
 **Организация стилей (.styl файлы):**
 ```stylus
-// Использование @css для Tailwind классов
-@css {
-  .admin--sidebar {
-    @apply w-64 bg-white dark:bg-gray-800 shadow-md h-screen fixed;
-  }
-  .menu-item--active {
-    @apply bg-primary-100 text-primary-700 dark:bg-primary-900 dark:text-primary-300;
-  }
-}
+
+
 ```
+использовать @import '../../index.styl' не нужно, приложение одностраничное.
+
 
 ### 🏗️ Структура компонентов
 
@@ -110,14 +79,13 @@ div(class="")
 # Подключение файлов
 globalThis.renderFns = require 'pug.json'
 globalThis.stylFns   = require 'styl.json'
-globalThis.themeConfig = require 'app/theme.config.coffee'
+
 
 # Создание Vue приложения
 app = Vue.createApp({
   data: ->
     return {
       theme: 'light'
-      companyName: 'Браер-Колор'
       loading: false
       cartItems: []
       user: null
@@ -540,37 +508,22 @@ https://cdn1.ozone.ru/s3/multimedia-1-p/7663352533.jpg";;;ЭкоКрас;4673764
 
 ### ✅ Реализовано
   
-  app/index.coffee
-  
-  app/index.pug
-  
-  app/index.styl
-  
-  app/theme.config.coffee
-  
-  app/utils/pouch.db
-  
-  app/desing/admin.coffee
-  
-  app/desing/site.coffee
-  
-  app/pages/Home/index.coffee
-  
-  app/pages/Home/index.pug
-  
-  app/pages/Home/index.styl
-  
-  app/pages/Admin/index.coffee
-  
-  app/pages/Admin/index.pug
-  
-  app/pages/Admin/index.styl
-  
-  app/pages/Admin/Settings/index.coffee
-  
-  app/pages/Admin/Settings/index.pug
-  
-  app/pages/Admin/Settings/index.styl
+-  app/index.coffee
+-  app/index.pug
+-  app/index.styl
+-  app/theme.config.coffee
+-  app/utils/pouch.db
+-  app/desing/admin.coffee
+-  app/desing/site.coffee
+-  app/pages/Home/index.coffee
+-  app/pages/Home/index.pug
+-  app/pages/Home/index.styl
+-  app/pages/Admin/index.coffee
+-  app/pages/Admin/index.pug
+-  app/pages/Admin/index.styl
+-  app/pages/Admin/Settings/index.coffee
+-  app/pages/Admin/Settings/index.pug
+-  app/pages/Admin/Settings/index.styl
 
 
 ### 🚧 В процессе
@@ -579,12 +532,12 @@ https://cdn1.ozone.ru/s3/multimedia-1-p/7663352533.jpg";;;ЭкоКрас;4673764
  
   Написать файлы
 
-  app/pages/Admin/Slider/index.coffee
-  app/pages/Admin/Slider/index.pug
-  app/pages/Admin/Slider/index.styl
-  app/pages/Admin/Products/index.coffee
-  app/pages/Admin/Products/index.pug
-  app/pages/Admin/Products/index.styl
-  app/pages/Admin/Blog/index.coffee
-  app/pages/Admin/Blog/index.pug
-  app/pages/Admin/Blog/index.styl
+-  app/pages/Admin/Slider/index.coffee
+-  app/pages/Admin/Slider/index.pug
+-  app/pages/Admin/Slider/index.styl
+-  app/pages/Admin/Products/index.coffee
+-  app/pages/Admin/Products/index.pug
+-  app/pages/Admin/Products/index.styl
+-  app/pages/Admin/Blog/index.coffee
+-  app/pages/Admin/Blog/index.pug
+-  app/pages/Admin/Blog/index.styl

+ 4 - 18
app/index.coffee

@@ -1,20 +1,6 @@
 # Инициализация глобальных переменных
 globalThis.renderFns = require 'pug.json'
 globalThis.stylFns = require 'styl.json'
-globalThis.themeConfig = require 'app/theme.config.coffee'
-
-# Инициализация Tailwind с кастомной конфигурацией
-tailwind.config = {
-  theme: {
-    extend: {
-      colors: themeConfig.colors
-      fontFamily: themeConfig.typography.fonts
-      borderRadius: themeConfig.borderRadius
-    }
-  }
-  darkMode: 'class'
-}
-
 
 
 PouchDBService = require 'app/utils/pouch.coffee'
@@ -25,10 +11,10 @@ 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>'+stylFns['main.css']+'</style>')
-document.head.insertAdjacentHTML('beforeend','<style  type="text/tailwindcss">'+stylFns['app/index.styl']+'</style>')
 
-document.head.insertAdjacentHTML('beforeend','<title> Кохи Борбад - Концертный зал Душанбе</title>')
+document.head.insertAdjacentHTML('beforeend','<style  type="text/css">'+stylFns['app/index.styl']+'</style>')
+
+document.head.insertAdjacentHTML('beforeend','<title></title>')
 
 
 
@@ -107,7 +93,7 @@ router = VueRouter.createRouter({
         #{ 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: 'blog', component: require 'app/pages/Admin/Blog' }
         #{ path: 'routes', component: require 'app/pages/Admin/Routes' }
         { path: 'settings', component: require 'app/pages/Admin/Settings' }
       ]

+ 680 - 139
app/index.styl

@@ -1,139 +1,680 @@
-@css {
-  /* Основные стили приложения */
-  #app {
-    @apply min-h-screen bg-white dark:bg-gray-900 transition-colors duration-300;
-  }
-  
-  /* Header */
-  .header {
-    @apply bg-white dark:bg-gray-800 shadow-sm sticky top-0 z-40;
-  }
-  
-  .header__nav {
-    @apply container mx-auto px-4 py-3;
-  }
-  
-  .header__nav-block {
-    @apply flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3;
-  }
-  
-  .header__nav-name {
-    @apply text-xl sm:text-2xl font-bold bg-gradient-to-r from-primary-500 to-accent-500 bg-clip-text text-transparent text-center sm:text-left;
-  }
-  
-  .header__nav-menu {
-    @apply flex flex-wrap justify-center sm:justify-end items-center gap-2 sm:gap-4;
-  }
-  
-  .header__menu-item {
-    @apply flex items-center;
-  }
-  
-  .header__menu-link {
-    @apply text-gray-600 dark:text-gray-300 hover:text-primary-500 transition-colors duration-200 text-sm sm:text-base px-2 py-1 rounded;
-  }
-  
-  .header__theme-toggle {
-    @apply p-2 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors duration-200 text-sm;
-  }
-  
-  /* Main content */
-  .main {
-    @apply flex-1 container mx-auto px-4 py-6;
-  }
-  
-  /* Page transitions */
-  .page-slide-enter-active,
-  .page-slide-leave-active {
-    @apply transition-all duration-300 ease-in-out;
-  }
-  
-  .page-slide-enter-from {
-    @apply opacity-0 transform translate-x-4;
-  }
-  
-  .page-slide-leave-to {
-    @apply opacity-0 transform -translate-x-4;
-  }
-  
-  /* Footer */
-  .footer {
-    @apply bg-gray-800 dark:bg-gray-900 text-white py-8;
-  }
-  
-  .footer__content {
-    @apply container mx-auto px-4;
-  }
-  
-  .footer__sections {
-    @apply grid grid-cols-1 md:grid-cols-3 gap-6 md:gap-8;
-  }
-  
-  .footer__section {
-    @apply flex flex-col text-center md:text-left;
-  }
-  
-  .footer__section-title {
-    @apply text-lg font-bold mb-3 md:mb-4;
-  }
-  
-  .footer__section-text {
-    @apply text-gray-400 mb-2 text-sm md:text-base;
-  }
-  
-  .footer__links {
-    @apply flex flex-col space-y-2;
-  }
-  
-  .footer__link {
-    @apply text-gray-400 hover:text-white transition-colors duration-200 text-sm md:text-base;
-  }
-  
-  /* Утилитарные классы */
-  .btn-primary {
-    @apply bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-lg transition-colors duration-200 font-medium text-sm sm:text-base;
-  }
-  
-  .btn-secondary {
-    @apply bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-white px-4 py-2 rounded-lg transition-colors duration-200 font-medium text-sm sm:text-base;
-  }
-  
-  .card {
-    @apply bg-white dark:bg-gray-800 rounded-lg shadow-md p-4 sm:p-6;
-  }
-  
-  .form-input {
-    @apply w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent text-sm sm:text-base;
-  }
-  
-  .form-label {
-    @apply block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2;
-  }
-
-  /* Mobile menu */
-  .mobile-menu-btn {
-    @apply sm:hidden p-2 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300;
-  }
-
-  .mobile-menu {
-    @apply sm:hidden absolute top-full left-0 right-0 bg-white dark:bg-gray-800 shadow-lg border-t border-gray-200 dark:border-gray-700;
-  }
-}
-
-/* Темная тема */
-@media (prefers-color-scheme: dark) {
-  :root {
-    color-scheme: dark;
-  }
-}
-
-/* Медиа-запросы для очень маленьких экранов */
-@media (max-width: 360px) {
-  .header__nav-menu {
-    @apply gap-1;
-  }
-  
-  .header__menu-link {
-    @apply text-xs px-1;
-  }
-}
+// Переменные цветов
+$primary-50 = #fef2f2
+$primary-100 = #fee2e2
+$primary-200 = #fecaca
+$primary-300 = #fca5a5
+$primary-400 = #f87171
+$primary-500 = #ef4444
+$primary-600 = #dc2626
+$primary-700 = #b91c1c
+$primary-800 = #991b1b
+$primary-900 = #7f1d1d
+
+$accent-50 = #f0f9ff
+$accent-100 = #e0f2fe
+$accent-200 = #bae6fd
+$accent-300 = #7dd3fc
+$accent-400 = #38bdf8
+$accent-500 = #0ea5e9
+$accent-600 = #0284c7
+$accent-700 = #0369a1
+$accent-800 = #075985
+$accent-900 = #0c4a6e
+
+$gray-50 = #f9fafb
+$gray-100 = #f3f4f6
+$gray-200 = #e5e7eb
+$gray-300 = #d1d5db
+$gray-400 = #9ca3af
+$gray-500 = #6b7280
+$gray-600 = #4b5563
+$gray-700 = #374151
+$gray-800 = #1f2937
+$gray-900 = #111827
+
+$white = #ffffff
+$black = #000000
+
+// Типографика
+$font-family-sans = 'Inter', 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif
+$font-family-serif = 'Georgia', 'Times New Roman', serif
+$font-family-mono = 'SF Mono', 'Fira Code', 'Consolas', 'Liberation Mono', monospace
+
+$font-size-xs = 0.75rem    // 12px
+$font-size-sm = 0.875rem   // 14px
+$font-size-base = 1rem     // 16px
+$font-size-lg = 1.125rem   // 18px
+$font-size-xl = 1.25rem    // 20px
+$font-size-2xl = 1.5rem    // 24px
+$font-size-3xl = 1.875rem  // 30px
+$font-size-4xl = 2.25rem   // 36px
+$font-size-5xl = 3rem      // 48px
+
+$font-weight-light = 300
+$font-weight-normal = 400
+$font-weight-medium = 500
+$font-weight-semibold = 600
+$font-weight-bold = 700
+$font-weight-extrabold = 800
+
+$line-height-tight = 1.25
+$line-height-normal = 1.5
+$line-height-relaxed = 1.75
+
+$letter-spacing-tight = -0.025em
+$letter-spacing-normal = 0
+$letter-spacing-wide = 0.025em
+
+// Макеты и отступы
+$spacing-1 = 0.25rem   // 4px
+$spacing-2 = 0.5rem    // 8px
+$spacing-3 = 0.75rem   // 12px
+$spacing-4 = 1rem      // 16px
+$spacing-5 = 1.25rem   // 20px
+$spacing-6 = 1.5rem    // 24px
+$spacing-8 = 2rem      // 32px
+$spacing-10 = 2.5rem   // 40px
+$spacing-12 = 3rem     // 48px
+$spacing-16 = 4rem     // 64px
+$spacing-20 = 5rem     // 80px
+
+// Брейкпоинты
+$breakpoint-sm = 640px
+$breakpoint-md = 768px
+$breakpoint-lg = 1024px
+$breakpoint-xl = 1280px
+$breakpoint-2xl = 1536px
+
+// Тени
+$shadow-sm = 0 1px 2px 0 rgba(0, 0, 0, 0.05)
+$shadow-base = 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)
+$shadow-md = 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)
+$shadow-lg = 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)
+$shadow-xl = 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)
+
+// Скругления
+$radius-sm = 0.125rem
+$radius-base = 0.25rem
+$radius-md = 0.375rem
+$radius-lg = 0.5rem
+$radius-xl = 0.75rem
+$radius-2xl = 1rem
+$radius-full = 9999px
+
+// Миксины
+flex-center()
+  display: flex
+  align-items: center
+  justify-content: center
+
+flex-between()
+  display: flex
+  align-items: center
+  justify-content: space-between
+
+flex-col()
+  display: flex
+  flex-direction: column
+
+container()
+  max-width: 1200px
+  margin: 0 auto
+  padding: 0 $spacing-4
+
+transition-all($duration = 0.3s)
+  transition: all $duration ease-in-out
+
+text-ellipsis()
+  overflow: hidden
+  text-overflow: ellipsis
+  white-space: nowrap
+
+// Базовые сбросы
+*
+  margin: 0
+  padding: 0
+  box-sizing: border-box
+
+html
+  font-size: 16px
+  line-height: $line-height-normal
+  -webkit-text-size-adjust: 100%
+  scroll-behavior: smooth
+
+body
+  font-family: $font-family-sans
+  font-size: $font-size-base
+  font-weight: $font-weight-normal
+  line-height: $line-height-normal
+  color: $gray-900
+  background-color: $white
+  -webkit-font-smoothing: antialiased
+  -moz-osx-font-smoothing: grayscale
+
+  .dark &
+    color: $white
+    background-color: $gray-900
+
+// Типографические классы
+.text-xs
+  font-size: $font-size-xs
+  line-height: $line-height-tight
+
+.text-sm
+  font-size: $font-size-sm
+  line-height: $line-height-tight
+
+.text-base
+  font-size: $font-size-base
+  line-height: $line-height-normal
+
+.text-lg
+  font-size: $font-size-lg
+  line-height: $line-height-normal
+
+.text-xl
+  font-size: $font-size-xl
+  line-height: $line-height-normal
+
+.text-2xl
+  font-size: $font-size-2xl
+  line-height: $line-height-tight
+
+.text-3xl
+  font-size: $font-size-3xl
+  line-height: $line-height-tight
+
+.text-4xl
+  font-size: $font-size-4xl
+  line-height: $line-height-tight
+
+.text-5xl
+  font-size: $font-size-5xl
+  line-height: 1
+
+.font-light
+  font-weight: $font-weight-light
+
+.font-normal
+  font-weight: $font-weight-normal
+
+.font-medium
+  font-weight: $font-weight-medium
+
+.font-semibold
+  font-weight: $font-weight-semibold
+
+.font-bold
+  font-weight: $font-weight-bold
+
+.font-extrabold
+  font-weight: $font-weight-extrabold
+
+.leading-tight
+  line-height: $line-height-tight
+
+.leading-normal
+  line-height: $line-height-normal
+
+.leading-relaxed
+  line-height: $line-height-relaxed
+
+.tracking-tight
+  letter-spacing: $letter-spacing-tight
+
+.tracking-normal
+  letter-spacing: $letter-spacing-normal
+
+.tracking-wide
+  letter-spacing: $letter-spacing-wide
+
+.text-center
+  text-align: center
+
+.text-left
+  text-align: left
+
+.text-right
+  text-align: right
+
+// Базовые стили приложения
+#app
+  min-height: 100vh
+  background-color: $white
+  display: flex
+  flex-direction: column
+
+  .dark &
+    background-color: $gray-900
+
+// Header
+.header
+  background-color: $white
+  box-shadow: $shadow-sm
+  position: sticky
+  top: 0
+  z-index: 100
+  border-bottom: 1px solid $gray-200
+
+  .dark &
+    background-color: $gray-800
+    border-bottom-color: $gray-700
+
+.header__nav
+  container()
+  padding-top: $spacing-3
+  padding-bottom: $spacing-3
+
+.header__nav-block
+  flex-between()
+  gap: $spacing-4
+
+.header__nav-name
+  font-size: $font-size-2xl
+  font-weight: $font-weight-bold
+  background: linear-gradient(135deg, $primary-500, $accent-500)
+  -webkit-background-clip: text
+  -webkit-text-fill-color: transparent
+  background-clip: text
+  letter-spacing: $letter-spacing-tight
+
+.header__nav-menu
+  display: flex
+  align-items: center
+  gap: $spacing-4
+
+.header__menu-item
+  display: flex
+  align-items: center
+
+.header__menu-link
+  font-size: $font-size-sm
+  font-weight: $font-weight-medium
+  color: $gray-600
+  text-decoration: none
+  padding: $spacing-2 $spacing-3
+  border-radius: $radius-md
+  transition: all 0.2s ease-in-out
+  position: relative
+
+  &:hover
+    color: $primary-600
+    background-color: $primary-50
+
+  .dark &
+    color: $gray-300
+
+    &:hover
+      color: $primary-400
+      background-color: $primary-900
+
+.header__theme-toggle
+  padding: $spacing-2
+  border-radius: $radius-md
+  background-color: $gray-100
+  color: $gray-600
+  transition: all 0.2s ease-in-out
+  border: none
+  cursor: pointer
+  flex-center()
+  width: 2.5rem
+  height: 2.5rem
+
+  &:hover
+    background-color: $gray-200
+    transform: scale(1.05)
+
+  .dark &
+    background-color: $gray-700
+    color: $gray-300
+
+    &:hover
+      background-color: $gray-600
+
+// Main content
+.main
+  flex: 1
+  display: flex
+  flex-direction: column
+
+// Page transitions
+.page-slide
+  &-enter-active,
+  &-leave-active
+    transition: all 0.3s ease-in-out
+
+  &-enter-from
+    opacity: 0
+    transform: translateX($spacing-4)
+
+  &-leave-to
+    opacity: 0
+    transform: translateX(-$spacing-4)
+
+// Footer
+.footer
+  background: linear-gradient(135deg, $gray-800, $gray-900)
+  color: $white
+  padding: $spacing-12 0 $spacing-8
+  margin-top: auto
+
+  .dark &
+    background: linear-gradient(135deg, $gray-900, $black)
+
+.footer__content
+  container()
+
+.footer__sections
+  display: grid
+  grid-template-columns: 1fr
+  gap: $spacing-8
+  margin-bottom: $spacing-8
+
+  @media (min-width: $breakpoint-md)
+    grid-template-columns: repeat(3, 1fr)
+
+.footer__section
+  flex-col()
+  gap: $spacing-4
+
+.footer__section-title
+  font-size: $font-size-lg
+  font-weight: $font-weight-semibold
+  color: $white
+  margin-bottom: $spacing-2
+
+.footer__section-text
+  font-size: $font-size-sm
+  color: $gray-400
+  line-height: $line-height-relaxed
+
+.footer__links
+  flex-col()
+  gap: $spacing-2
+
+.footer__link
+  font-size: $font-size-sm
+  color: $gray-400
+  text-decoration: none
+  transition: color 0.2s ease-in-out
+  padding: $spacing-1 0
+
+  &:hover
+    color: $white
+    transform: translateX($spacing-1)
+
+.footer__bottom
+  border-top: 1px solid $gray-700
+  padding-top: $spacing-6
+  text-align: center
+
+  .footer__copyright
+    font-size: $font-size-sm
+    color: $gray-500
+
+// Утилитарные классы
+.btn
+  display: inline-flex
+  align-items: center
+  justify-content: center
+  padding: $spacing-2 $spacing-4
+  border-radius: $radius-lg
+  font-size: $font-size-sm
+  font-weight: $font-weight-medium
+  text-decoration: none
+  border: none
+  cursor: pointer
+  transition: all 0.2s ease-in-out
+  gap: $spacing-2
+  position: relative
+  overflow: hidden
+
+  &:focus
+    outline: none
+    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2)
+
+  &::before
+    content: ''
+    position: absolute
+    top: 0
+    left: -100%
+    width: 100%
+    height: 100%
+    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent)
+    transition: left 0.5s
+
+  &:hover::before
+    left: 100%
+
+.btn-primary
+  background: linear-gradient(135deg, $primary-500, $primary-600)
+  color: $white
+  box-shadow: $shadow-md
+
+  &:hover
+    transform: translateY(-1px)
+    box-shadow: $shadow-lg
+
+.btn-secondary
+  background-color: $gray-100
+  color: $gray-700
+  border: 1px solid $gray-300
+
+  &:hover
+    background-color: $gray-200
+    transform: translateY(-1px)
+
+  .dark &
+    background-color: $gray-700
+    color: $gray-300
+    border-color: $gray-600
+
+    &:hover
+      background-color: $gray-600
+
+.btn-outline
+  background-color: transparent
+  color: $primary-600
+  border: 2px solid $primary-500
+
+  &:hover
+    background-color: $primary-500
+    color: $white
+    transform: translateY(-1px)
+
+.card
+  background-color: $white
+  border-radius: $radius-xl
+  box-shadow: $shadow-base
+  border: 1px solid $gray-200
+  overflow: hidden
+  transition: all 0.3s ease-in-out
+
+  .dark &
+    background-color: $gray-800
+    border-color: $gray-700
+
+  &:hover
+    transform: translateY(-2px)
+    box-shadow: $shadow-lg
+
+.card__header
+  padding: $spacing-6
+  border-bottom: 1px solid $gray-200
+
+  .dark &
+    border-bottom-color: $gray-700
+
+.card__title
+  font-size: $font-size-xl
+  font-weight: $font-weight-semibold
+  color: $gray-900
+  margin-bottom: $spacing-2
+
+  .dark &
+    color: $white
+
+.card__content
+  padding: $spacing-6
+
+.card__footer
+  padding: $spacing-6
+  border-top: 1px solid $gray-200
+  background-color: $gray-50
+
+  .dark &
+    border-top-color: $gray-700
+    background-color: $gray-700
+
+.form-group
+  flex-col()
+  gap: $spacing-2
+  margin-bottom: $spacing-4
+
+.form-label
+  font-size: $font-size-sm
+  font-weight: $font-weight-medium
+  color: $gray-700
+  display: block
+
+  .dark &
+    color: $gray-300
+
+.form-input
+  width: 100%
+  padding: $spacing-3
+  border: 2px solid $gray-300
+  border-radius: $radius-lg
+  background-color: $white
+  color: $gray-900
+  font-size: $font-size-base
+  font-family: $font-family-sans
+  transition: all 0.2s ease-in-out
+
+  &:focus
+    outline: none
+    border-color: $primary-500
+    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1)
+    transform: translateY(-1px)
+
+  .dark &
+    background-color: $gray-700
+    color: $white
+    border-color: $gray-600
+
+    &:focus
+      border-color: $primary-500
+
+.form-textarea
+  @extend .form-input
+  resize: vertical
+  min-height: 6rem
+  line-height: $line-height-relaxed
+
+.form-select
+  @extend .form-input
+
+// Сетка
+.grid
+  display: grid
+  gap: $spacing-6
+
+.grid-cols-1
+  grid-template-columns: 1fr
+
+.grid-cols-2
+  grid-template-columns: repeat(2, 1fr)
+
+.grid-cols-3
+  grid-template-columns: repeat(3, 1fr)
+
+.grid-cols-4
+  grid-template-columns: repeat(4, 1fr)
+
+@media (min-width: $breakpoint-sm)
+  .sm\:grid-cols-2
+    grid-template-columns: repeat(2, 1fr)
+
+@media (min-width: $breakpoint-md)
+  .md\:grid-cols-2
+    grid-template-columns: repeat(2, 1fr)
+  .md\:grid-cols-3
+    grid-template-columns: repeat(3, 1fr)
+
+@media (min-width: $breakpoint-lg)
+  .lg\:grid-cols-3
+    grid-template-columns: repeat(3, 1fr)
+  .lg\:grid-cols-4
+    grid-template-columns: repeat(4, 1fr)
+
+// Отступы
+.p-0 { padding: 0 }
+.p-1 { padding: $spacing-1 }
+.p-2 { padding: $spacing-2 }
+.p-3 { padding: $spacing-3 }
+.p-4 { padding: $spacing-4 }
+.p-6 { padding: $spacing-6 }
+.p-8 { padding: $spacing-8 }
+
+.py-2 { padding-top: $spacing-2; padding-bottom: $spacing-2 }
+.py-3 { padding-top: $spacing-3; padding-bottom: $spacing-3 }
+.py-4 { padding-top: $spacing-4; padding-bottom: $spacing-4 }
+.py-6 { padding-top: $spacing-6; padding-bottom: $spacing-6 }
+.py-8 { padding-top: $spacing-8; padding-bottom: $spacing-8 }
+
+.px-3 { padding-left: $spacing-3; padding-right: $spacing-3 }
+.px-4 { padding-left: $spacing-4; padding-right: $spacing-4 }
+.px-6 { padding-left: $spacing-6; padding-right: $spacing-6 }
+
+// Маржины
+.m-0 { margin: 0 }
+.m-4 { margin: $spacing-4 }
+.m-6 { margin: $spacing-6 }
+
+.mt-2 { margin-top: $spacing-2 }
+.mt-4 { margin-top: $spacing-4 }
+.mt-6 { margin-top: $spacing-6 }
+.mt-8 { margin-top: $spacing-8 }
+
+.mb-2 { margin-bottom: $spacing-2 }
+.mb-4 { margin-bottom: $spacing-4 }
+.mb-6 { margin-bottom: $spacing-6 }
+.mb-8 { margin-bottom: $spacing-8 }
+
+// Темная тема
+@media (prefers-color-scheme: dark)
+  :root
+    color-scheme: dark
+
+// Адаптивность
+@media (max-width: $breakpoint-md)
+  .header__nav-block
+    flex-direction: column
+    gap: $spacing-3
+
+  .header__nav-menu
+    width: 100%
+    justify-content: center
+    flex-wrap: wrap
+
+  .footer__sections
+    grid-template-columns: 1fr
+    gap: $spacing-6
+
+@media (max-width: $breakpoint-sm)
+  .header__nav-name
+    font-size: $font-size-xl
+    text-align: center
+
+  .header__menu-link
+    padding: $spacing-1 $spacing-2
+    font-size: $font-size-xs
+
+  .btn
+    padding: $spacing-2 $spacing-3
+    font-size: $font-size-xs
+
+  .card__header,
+  .card__content,
+  .card__footer
+    padding: $spacing-4

+ 182 - 0
app/pages/Admin/Blog/index.coffee

@@ -0,0 +1,182 @@
+document.head.insertAdjacentHTML('beforeend','<style type="text/css">'+stylFns['app/pages/Admin/Blog/index.styl']+'</style>')
+
+PouchDB = require 'app/utils/pouch'
+
+module.exports =
+  name: 'AdminBlog'
+  
+  render: (new Function '_ctx', '_cache', renderFns['app/pages/Admin/Blog/index.pug'])()
+  
+  data: ->
+    return {
+      articles: []
+      searchQuery: ''
+      selectedStatus: ''
+      showArticleModal: false
+      editingArticle: null
+      availableDomains: []
+      articleForm: {
+        title: ''
+        slug: ''
+        excerpt: ''
+        image: ''
+        author: ''
+        content: ''
+        published: false
+        domains: []
+      }
+    }
+  
+  computed:
+    filteredArticles: ->
+      articles = @articles
+      
+      # Фильтр по поиску
+      if @searchQuery
+        query = @searchQuery.toLowerCase()
+        articles = articles.filter (article) =>
+          article.title?.toLowerCase().includes(query) ||
+          article.excerpt?.toLowerCase().includes(query)
+      
+      # Фильтр по статусу
+      if @selectedStatus == 'published'
+        articles = articles.filter (article) => article.published
+      else if @selectedStatus == 'draft'
+        articles = articles.filter (article) => !article.published
+      
+      return articles
+  
+  methods:
+    loadArticles: ->
+      PouchDB.queryView('admin', 'blog_articles', { include_docs: true })
+        .then (result) =>
+          @articles = result.rows.map (row) -> row.doc
+          # Сортируем по дате создания (новые сначала)
+          @articles.sort (a, b) -> new Date(b.createdAt) - new Date(a.createdAt)
+        .catch (error) =>
+          console.error 'Ошибка загрузки статей:', error
+          @showNotification 'Ошибка загрузки статей', 'error'
+    
+    loadDomains: ->
+      PouchDB.queryView('admin', 'domain_settings', { include_docs: true })
+        .then (result) =>
+          @availableDomains = result.rows.map (row) -> row.doc
+        .catch (error) =>
+          console.error 'Ошибка загрузки доменов:', error
+    
+    editArticle: (article) ->
+      @editingArticle = article
+      @articleForm = {
+        title: article.title || ''
+        slug: article.slug || ''
+        excerpt: article.excerpt || ''
+        image: article.image || ''
+        author: article.author || ''
+        content: article.content || ''
+        published: article.published || false
+        domains: article.domains || []
+      }
+      @showArticleModal = true
+    
+    saveArticle: ->
+      if !@articleForm.title
+        @showNotification 'Введите заголовок статьи', 'error'
+        return
+      
+      if !@articleForm.slug
+        @showNotification 'Введите URL slug', 'error'
+        return
+      
+      articleData = {
+        type: 'blog_article'
+        ...@articleForm
+        updatedAt: new Date().toISOString()
+      }
+      
+      if @editingArticle
+        articleData._id = @editingArticle._id
+        articleData._rev = @editingArticle._rev
+        articleData.createdAt = @editingArticle.createdAt
+      else
+        articleData._id = "blog_article:#{Date.now()}"
+        articleData.createdAt = new Date().toISOString()
+      
+      PouchDB.saveToRemote(articleData)
+        .then (result) =>
+          @showArticleModal = false
+          @resetArticleForm()
+          @loadArticles()
+          @showNotification 'Статья успешно сохранена'
+        .catch (error) =>
+          console.error 'Ошибка сохранения статьи:', error
+          @showNotification 'Ошибка сохранения статьи', 'error'
+    
+    deleteArticle: (articleId) ->
+      if confirm('Вы уверены, что хотите удалить эту статью?')
+        PouchDB.getDocument(articleId)
+          .then (doc) ->
+            PouchDB.saveToRemote({ ...doc, _deleted: true })
+          .then (result) =>
+            @loadArticles()
+            @showNotification 'Статья удалена'
+          .catch (error) =>
+            console.error 'Ошибка удаления статьи:', error
+            @showNotification 'Ошибка удаления статьи', 'error'
+    
+    toggleArticleStatus: (article) ->
+      updatedArticle = {
+        ...article
+        published: !article.published
+        updatedAt: new Date().toISOString()
+      }
+      
+      PouchDB.saveToRemote(updatedArticle)
+        .then (result) =>
+          @loadArticles()
+          @showNotification 'Статус статьи обновлен'
+        .catch (error) =>
+          console.error 'Ошибка обновления статуса:', error
+          @showNotification 'Ошибка обновления статуса', 'error'
+    
+    resetArticleForm: ->
+      @editingArticle = null
+      @articleForm = {
+        title: ''
+        slug: ''
+        excerpt: ''
+        image: ''
+        author: ''
+        content: ''
+        published: false
+        domains: []
+      }
+    
+    formatDate: (dateString) ->
+      return '' if !dateString
+      date = new Date(dateString)
+      date.toLocaleDateString('ru-RU', {
+        year: 'numeric'
+        month: 'long'
+        day: 'numeric'
+      })
+    
+    getStatusClass: (isPublished) ->
+      baseClass = 'admin-blog__item-status'
+      if isPublished
+        return "#{baseClass} admin-blog__item-status--published"
+      else
+        return "#{baseClass} admin-blog__item-status--draft"
+    
+    getToggleBtnClass: (isPublished) ->
+      baseClass = 'admin-blog__btn'
+      if isPublished
+        return "#{baseClass} admin-blog__btn--secondary"
+      else
+        return "#{baseClass} admin-blog__btn--primary"
+    
+    showNotification: (message, type = 'success') ->
+      @$root.showNotification?(message, type) || debug.log("#{type}: #{message}")
+  
+  mounted: ->
+    @loadArticles()
+    @loadDomains()

+ 160 - 0
app/pages/Admin/Blog/index.pug

@@ -0,0 +1,160 @@
+div(class="admin-blog")
+  div(class="admin-blog__header")
+    h1(class="admin-blog__title") Управление блогом
+    button(
+      @click="showArticleModal = true"
+      class="admin-blog__add-btn"
+    ) Новая статья
+  
+  div(class="admin-blog__content")
+    div(class="admin-blog__filters")
+      div(class="admin-blog__filter-group")
+        label(class="admin-blog__label") Поиск
+        input(
+          v-model="searchQuery"
+          type="text"
+          class="admin-blog__input"
+          placeholder="Поиск по заголовку..."
+        )
+      
+      div(class="admin-blog__filter-group")
+        label(class="admin-blog__label") Статус
+        select(v-model="selectedStatus" class="admin-blog__select")
+          option(value="") Все
+          option(value="published") Опубликованные
+          option(value="draft") Черновики
+    
+    div(class="admin-blog__list")
+      div(
+        v-for="article in filteredArticles"
+        :key="article._id"
+        class="admin-blog__item"
+      )
+        div(class="admin-blog__item-content")
+          div(class="admin-blog__item-image")
+            img(
+              v-if="article.image"
+              :src="article.image"
+              :alt="article.title"
+              class="admin-blog__image"
+            )
+            div(v-else class="admin-blog__no-image") Нет изображения
+          
+          div(class="admin-blog__item-info")
+            h3(class="admin-blog__item-title") {{ article.title }}
+            p(class="admin-blog__item-excerpt") {{ article.excerpt || 'Без описания' }}
+            div(class="admin-blog__item-meta")
+              span(class="admin-blog__item-date") {{ formatDate(article.createdAt) }}
+              span(class="admin-blog__item-author") {{ article.author || 'Автор не указан' }}
+              span(
+                :class="getStatusClass(article.published)"
+              ) {{ article.published ? 'Опубликовано' : 'Черновик' }}
+        
+        div(class="admin-blog__item-actions")
+          button(
+            @click="editArticle(article)"
+            class="admin-blog__btn admin-blog__btn--edit"
+          ) Редактировать
+          button(
+            @click="toggleArticleStatus(article)"
+            :class="getToggleBtnClass(article.published)"
+          ) {{ article.published ? 'В черновик' : 'Опубликовать' }}
+          button(
+            @click="deleteArticle(article._id)"
+            class="admin-blog__btn admin-blog__btn--delete"
+          ) Удалить
+  
+  // Модальное окно статьи
+  div(v-if="showArticleModal" class="admin-blog__modal")
+    div(class="admin-blog__modal-content")
+      h3(class="admin-blog__modal-title") {{ editingArticle ? 'Редактирование' : 'Создание' }} статьи
+      
+      div(class="admin-blog__modal-form")
+        div(class="admin-blog__form-group")
+          label(class="admin-blog__label") Заголовок
+          input(
+            v-model="articleForm.title"
+            type="text"
+            class="admin-blog__input"
+            placeholder="Введите заголовок статьи"
+          )
+        
+        div(class="admin-blog__form-group")
+          label(class="admin-blog__label") URL slug
+          input(
+            v-model="articleForm.slug"
+            type="text"
+            class="admin-blog__input"
+            placeholder="url-slug"
+          )
+        
+        div(class="admin-blog__form-group")
+          label(class="admin-blog__label") Краткое описание
+          textarea(
+            v-model="articleForm.excerpt"
+            class="admin-blog__textarea"
+            placeholder="Краткое описание статьи"
+            rows="3"
+          )
+        
+        div(class="admin-blog__form-group")
+          label(class="admin-blog__label") Ссылка на изображение
+          input(
+            v-model="articleForm.image"
+            type="text"
+            class="admin-blog__input"
+            placeholder="https://example.com/image.jpg"
+          )
+        
+        div(class="admin-blog__form-group")
+          label(class="admin-blog__label") Автор
+          input(
+            v-model="articleForm.author"
+            type="text"
+            class="admin-blog__input"
+            placeholder="Имя автора"
+          )
+        
+        div(class="admin-blog__form-group")
+          label(class="admin-blog__label") Содержание (Markdown)
+          textarea(
+            v-model="articleForm.content"
+            class="admin-blog__textarea admin-blog__textarea--content"
+            placeholder="Напишите содержание статьи в формате Markdown..."
+            rows="10"
+          )
+        
+        div(class="admin-blog__form-group")
+          label(class="admin-blog__checkbox-label")
+            input(
+              v-model="articleForm.published"
+              type="checkbox"
+              class="admin-blog__checkbox"
+            )
+            span Опубликовать сразу
+        
+        div(class="admin-blog__form-group")
+          label(class="admin-blog__label") Домены
+          div(class="admin-blog__domains-list")
+            label(
+              v-for="domain in availableDomains"
+              :key="domain._id"
+              class="admin-blog__domain-label"
+            )
+              input(
+                type="checkbox"
+                :value="domain.domain"
+                v-model="articleForm.domains"
+                class="admin-blog__domain-checkbox"
+              )
+              span {{ domain.domain }}
+      
+      div(class="admin-blog__modal-actions")
+        button(
+          @click="saveArticle"
+          class="admin-blog__btn admin-blog__btn--primary"
+        ) {{ editingArticle ? 'Обновить' : 'Создать' }}
+        button(
+          @click="showArticleModal = false"
+          class="admin-blog__btn admin-blog__btn--secondary"
+        ) Отмена

+ 431 - 0
app/pages/Admin/Blog/index.styl

@@ -0,0 +1,431 @@
+// Admin Products styles
+.admin-products
+  display: flex
+  flex-direction: column
+  gap: 1.5rem
+
+.admin-products__header
+  display: flex
+  justify-content: space-between
+  align-items: center
+  border-bottom: 1px solid $gray-200
+  padding-bottom: 1.5rem
+
+  .dark &
+    border-bottom-color: $gray-700
+
+.admin-products__title
+  font-size: 1.875rem
+  font-weight: bold
+  color: $gray-900
+
+  .dark &
+    color: $white
+
+.admin-products__actions
+  display: flex
+  gap: 0.75rem
+
+// Button styles
+.admin-products__btn
+  padding: 0.5rem 1rem
+  border-radius: 0.5rem
+  font-weight: 500
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+  border: none
+  cursor: pointer
+  text-decoration: none
+  display: inline-flex
+  align-items: center
+  justify-content: center
+
+.admin-products__btn--primary
+  background-color: $primary-600
+  color: $white
+
+  &:hover
+    background-color: $primary-700
+
+.admin-products__btn--secondary
+  background-color: $gray-200
+  color: $gray-700
+
+  &:hover
+    background-color: $gray-300
+
+  .dark &
+    background-color: $gray-700
+    color: $gray-300
+
+    &:hover
+      background-color: $gray-600
+
+// Content area
+.admin-products__content
+  background-color: $white
+  border-radius: 0.5rem
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)
+  padding: 1.5rem
+
+  .dark &
+    background-color: $gray-800
+
+// Filters
+.admin-products__filters
+  display: grid
+  grid-template-columns: 1fr
+  gap: 1rem
+  margin-bottom: 1.5rem
+
+  @media (min-width: 768px)
+    grid-template-columns: repeat(3, 1fr)
+
+.admin-products__filter-group
+  display: flex
+  flex-direction: column
+  gap: 0.5rem
+
+.admin-products__label
+  display: block
+  font-size: 0.875rem
+  font-weight: 500
+  color: $gray-700
+
+  .dark &
+    color: $gray-300
+
+.admin-products__input
+  width: 100%
+  padding: 0.5rem 0.75rem
+  border: 1px solid $gray-300
+  border-radius: 0.5rem
+  background-color: $white
+  color: $gray-900
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+
+  &:focus
+    outline: none
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    color: $white
+    border-color: $gray-600
+
+    &:focus
+      border-color: $primary-500
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+.admin-products__select
+  width: 100%
+  padding: 0.5rem 0.75rem
+  border: 1px solid $gray-300
+  border-radius: 0.5rem
+  background-color: $white
+  color: $gray-900
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+
+  &:focus
+    outline: none
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    color: $white
+    border-color: $gray-600
+
+    &:focus
+      border-color: $primary-500
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+// Table styles
+.admin-products__table-container
+  overflow-x: auto
+
+.admin-products__table
+  width: 100%
+  border-collapse: collapse
+  background-color: $white
+
+  .dark &
+    background-color: $gray-800
+
+.admin-products__th
+  background-color: $gray-50
+  padding: 0.75rem 1rem
+  text-align: left
+  font-size: 0.75rem
+  font-weight: 500
+  color: $gray-500
+  text-transform: uppercase
+  letter-spacing: 0.05em
+  border-bottom: 1px solid $gray-200
+
+  .dark &
+    background-color: $gray-700
+    color: $gray-300
+    border-bottom-color: $gray-600
+
+.admin-products__tr
+  &:hover
+    background-color: $gray-50
+
+    .dark &
+      background-color: $gray-700
+
+.admin-products__td
+  padding: 0.75rem 1rem
+  border-bottom: 1px solid $gray-200
+  font-size: 0.875rem
+
+  .dark &
+    border-bottom-color: $gray-600
+
+.admin-products__image
+  width: 2.5rem
+  height: 2.5rem
+  object-fit: cover
+  border-radius: 0.25rem
+
+.admin-products__no-image
+  width: 2.5rem
+  height: 2.5rem
+  background-color: $gray-200
+  border-radius: 0.25rem
+  display: flex
+  align-items: center
+  justify-content: center
+  font-size: 0.75rem
+  color: $gray-500
+
+  .dark &
+    background-color: $gray-600
+    color: $gray-400
+
+.admin-products__name
+  font-weight: 500
+  color: $gray-900
+
+  .dark &
+    color: $white
+
+.admin-products__price
+  font-weight: 500
+  color: #16a34a
+
+  .dark &
+    color: #4ade80
+
+.admin-products__old-price
+  font-size: 0.75rem
+  color: $gray-500
+  text-decoration: line-through
+
+  .dark &
+    color: $gray-400
+
+.admin-products__status
+  padding: 0.25rem 0.5rem
+  border-radius: 0.25rem
+  font-size: 0.75rem
+  font-weight: 500
+
+.admin-products__status--active
+  background-color: #f0fdf4
+  color: #16a34a
+
+  .dark &
+    background-color: #14532d
+    color: #4ade80
+
+.admin-products__status--inactive
+  background-color: #fef2f2
+  color: #dc2626
+
+  .dark &
+    background-color: #7f1d1d
+    color: #fca5a5
+
+.admin-products__action-buttons
+  display: flex
+  gap: 0.5rem
+
+.admin-products__action-btn
+  padding: 0.25rem 0.5rem
+  border-radius: 0.25rem
+  font-size: 0.75rem
+  font-weight: 500
+  transition: all 0.2s ease-in-out
+  border: none
+  cursor: pointer
+
+.admin-products__action-btn--edit
+  background-color: #dbeafe
+  color: #1e40af
+
+  &:hover
+    background-color: #bfdbfe
+
+  .dark &
+    background-color: #1e3a8a
+    color: #93c5fd
+
+    &:hover
+      background-color: #1e40af
+
+.admin-products__action-btn--toggle
+  background-color: $gray-100
+  color: $gray-700
+
+  &:hover
+    background-color: $gray-200
+
+  .dark &
+    background-color: $gray-600
+    color: $gray-300
+
+    &:hover
+      background-color: $gray-500
+
+.admin-products__action-btn--delete
+  background-color: #fee2e2
+  color: #dc2626
+
+  &:hover
+    background-color: #fecaca
+
+  .dark &
+    background-color: #7f1d1d
+    color: #fca5a5
+
+    &:hover
+      background-color: #991b1b
+
+// Modal styles
+.admin-products__modal
+  position: fixed
+  inset: 0
+  background-color: rgba(0, 0, 0, 0.5)
+  display: flex
+  align-items: center
+  justify-content: center
+  z-index: 50
+
+.admin-products__modal-content
+  background-color: $white
+  border-radius: 0.5rem
+  padding: 1.5rem
+  max-width: 48rem
+  width: 100%
+  margin: 0 1rem
+  max-height: 90vh
+  overflow-y: auto
+
+  .dark &
+    background-color: $gray-800
+
+.admin-products__modal-title
+  font-size: 1.125rem
+  font-weight: 500
+  color: $gray-900
+  margin-bottom: 1rem
+
+  .dark &
+    color: $white
+
+// Import form styles
+.admin-products__import-form
+  display: flex
+  flex-direction: column
+  gap: 1rem
+  margin-bottom: 1.5rem
+
+.admin-products__file-input
+  display: block
+  width: 100%
+  font-size: 0.875rem
+  color: $gray-500
+
+  &::file-selector-button
+    margin-right: 1rem
+    padding: 0.5rem 1rem
+    border-radius: 9999px
+    border: none
+    background-color: $primary-50
+    color: $primary-700
+    font-size: 0.875rem
+    font-weight: 500
+    cursor: pointer
+    transition: background-color 0.2s ease-in-out
+
+    &:hover
+      background-color: $primary-100
+
+    .dark &
+      background-color: $primary-900
+      color: $primary-300
+
+      &:hover
+        background-color: $primary-800
+
+.admin-products__file-info
+  padding: 1rem
+  background-color: $gray-50
+  border-radius: 0.5rem
+
+  .dark &
+    background-color: $gray-700
+
+.admin-products__import-results
+  padding: 1rem
+  border-radius: 0.5rem
+
+.admin-products__success
+  color: #16a34a
+  font-weight: 500
+  margin-bottom: 0.5rem
+
+  .dark &
+    color: #4ade80
+
+.admin-products__error
+  color: #dc2626
+  font-weight: 500
+  margin-bottom: 0.5rem
+
+  .dark &
+    color: #fca5a5
+
+.admin-products__modal-actions
+  display: flex
+  gap: 0.75rem
+  justify-content: flex-end
+
+// Responsive adjustments
+@media (max-width: 768px)
+  .admin-products__header
+    flex-direction: column
+    align-items: flex-start
+    gap: 1rem
+
+  .admin-products__actions
+    width: 100%
+    justify-content: flex-start
+
+  .admin-products__filters
+    grid-template-columns: 1fr
+
+  .admin-products__action-buttons
+    flex-direction: column
+
+  .admin-products__action-btn
+    text-align: center
+
+  .admin-products__modal-content
+    margin: 0 0.5rem
+    padding: 1rem

+ 213 - 0
app/pages/Admin/Products/index.coffee

@@ -0,0 +1,213 @@
+document.head.insertAdjacentHTML('beforeend','<style type="text/css">'+stylFns['app/pages/Admin/Products/index.styl']+'</style>')
+
+PouchDB = require 'app/utils/pouch'
+Papa = require 'papaparse'
+
+module.exports =
+  name: 'AdminProducts'
+  
+  render: (new Function '_ctx', '_cache', renderFns['app/pages/Admin/Products/index.pug'])()
+  
+  data: ->
+    return {
+      products: []
+      categories: []
+      searchQuery: ''
+      selectedCategory: ''
+      selectedStatus: ''
+      showProductModal: false
+      showImportModal: false
+      editingProduct: null
+      selectedFile: null
+      importing: false
+      importResults: null
+    }
+  
+  computed:
+    filteredProducts: ->
+      products = @products
+      
+      # Фильтр по поиску
+      if @searchQuery
+        query = @searchQuery.toLowerCase()
+        products = products.filter (product) =>
+          product.name?.toLowerCase().includes(query) ||
+          product.sku?.toLowerCase().includes(query)
+      
+      # Фильтр по категории
+      if @selectedCategory
+        products = products.filter (product) =>
+          product.category == @selectedCategory
+      
+      # Фильтр по статусу
+      if @selectedStatus == 'active'
+        products = products.filter (product) => product.active
+      else if @selectedStatus == 'inactive'
+        products = products.filter (product) => !product.active
+      
+      return products
+  
+  methods:
+    loadProducts: ->
+      PouchDB.queryView('admin', 'products', { include_docs: true })
+        .then (result) =>
+          @products = result.rows.map (row) -> row.doc
+        .catch (error) =>
+          console.error 'Ошибка загрузки товаров:', error
+          @showNotification 'Ошибка загрузки товаров', 'error'
+    
+    loadCategories: ->
+      PouchDB.queryView('admin', 'categories', { include_docs: true })
+        .then (result) =>
+          @categories = result.rows.map (row) -> row.doc
+        .catch (error) =>
+          console.error 'Ошибка загрузки категорий:', error
+    
+    getCategoryName: (categoryId) ->
+      category = @categories.find (cat) -> cat._id == categoryId
+      category?.name || 'Без категории'
+    
+    editProduct: (product) ->
+      @editingProduct = product
+      @showProductModal = true
+    
+    toggleProductStatus: (product) ->
+      updatedProduct = {
+        ...product
+        active: !product.active
+        updatedAt: new Date().toISOString()
+      }
+      
+      PouchDB.saveToRemote(updatedProduct)
+        .then (result) =>
+          @loadProducts()
+          @showNotification 'Статус товара обновлен'
+        .catch (error) =>
+          console.error 'Ошибка обновления статуса:', error
+          @showNotification 'Ошибка обновления статуса', 'error'
+    
+    deleteProduct: (productId) ->
+      if confirm('Вы уверены, что хотите удалить этот товар?')
+        PouchDB.getDocument(productId)
+          .then (doc) ->
+            PouchDB.saveToRemote({ ...doc, _deleted: true })
+          .then (result) =>
+            @loadProducts()
+            @showNotification 'Товар удален'
+          .catch (error) =>
+            console.error 'Ошибка удаления товара:', error
+            @showNotification 'Ошибка удаления товара', 'error'
+    
+    onFileSelect: (event) ->
+      @selectedFile = event.target.files[0]
+      @importResults = null
+    
+    importProducts: ->
+      if !@selectedFile
+        @showNotification 'Выберите файл для импорта', 'error'
+        return
+      
+      @importing = true
+      @importResults = null
+      
+      reader = new FileReader()
+      reader.onload = (e) =>
+        try
+          results = Papa.parse e.target.result, {
+            header: true
+            delimiter: ';'
+            skipEmptyLines: true
+            encoding: 'UTF-8'
+          }
+          
+          products = results.data.filter (row) => 
+            row && row['Артикул*'] && row['Название товара'] && row['Цена, руб.*']
+          
+          couchProducts = products.map (product, index) =>
+            @transformProductData(product, index)
+          
+          # Пакетное сохранение
+          PouchDB.bulkDocs(couchProducts)
+            .then (result) =>
+              @importResults = { success: true, processed: couchProducts.length }
+              @importing = false
+              @loadProducts()
+              @showNotification "Импортировано #{couchProducts.length} товаров"
+            .catch (error) =>
+              @importResults = { success: false, error: error.message, processed: 0 }
+              @importing = false
+              @showNotification "Ошибка импорта: #{error.message}", 'error'
+        
+        catch error
+          @importResults = { success: false, error: error.message, processed: 0 }
+          @importing = false
+          @showNotification "Ошибка обработки файла: #{error.message}", 'error'
+      
+      reader.readAsText(@selectedFile, 'UTF-8')
+    
+    transformProductData: (product, index) ->
+      # Базовые поля
+      productData = {
+        _id: "product:#{Date.now()}-#{index}"
+        type: 'product'
+        name: product['Название товара']
+        sku: product['Артикул*']
+        price: parseFloat(product['Цена, руб.*'].replace(/\s/g, '').replace(',', '.')) || 0
+        active: true
+        createdAt: new Date().toISOString()
+        updatedAt: new Date().toISOString()
+      }
+      
+      # Дополнительные поля
+      if product['Цена до скидки, руб.']
+        productData.oldPrice = parseFloat(product['Цена до скидки, руб.'].replace(/\s/g, '').replace(',', '.'))
+      
+      if product['Ссылка на главное фото*']
+        productData.image = product['Ссылка на главное фото*']
+      
+      if product['Бренд*']
+        productData.brand = product['Бренд*']
+      
+      if product['Тип*']
+        productData.productType = product['Тип*']
+      
+      # Rich content преобразование
+      if product['Rich-контент JSON']
+        try
+          richContent = JSON.parse(product['Rich-контент JSON'])
+          productData.description = @richContentToMarkdown(richContent)
+        catch
+          productData.description = product['Аннотация'] || ''
+      else
+        productData.description = product['Аннотация'] || ''
+      
+      # Домены (все товары доступны на всех доменах по умолчанию)
+      productData.domains = @availableDomains?.map((d) -> d.domain) || []
+      
+      return productData
+    
+    richContentToMarkdown: (richContent) ->
+      # Простое преобразование rich content в markdown
+      return JSON.stringify(richContent) # Временная реализация
+    
+    formatPrice: (price) ->
+      return '0 ₽' if !price
+      new Intl.NumberFormat('ru-RU', {
+        style: 'currency'
+        currency: 'RUB'
+        minimumFractionDigits: 0
+      }).format(price)
+    
+    getStatusClass: (isActive) ->
+      baseClass = 'admin-products__status'
+      if isActive
+        return "#{baseClass} admin-products__status--active"
+      else
+        return "#{baseClass} admin-products__status--inactive"
+    
+    showNotification: (message, type = 'success') ->
+      @$root.showNotification?(message, type) || debug.log("#{type}: #{message}")
+  
+  mounted: ->
+    @loadProducts()
+    @loadCategories()

+ 136 - 0
app/pages/Admin/Products/index.pug

@@ -0,0 +1,136 @@
+div(class="admin-products")
+  div(class="admin-products__header")
+    h1(class="admin-products__title") Управление товарами
+    div(class="admin-products__actions")
+      button(
+        @click="showProductModal = true"
+        class="admin-products__btn admin-products__btn--primary"
+      ) Добавить товар
+      button(
+        @click="showImportModal = true"
+        class="admin-products__btn admin-products__btn--secondary"
+      ) Импорт из CSV
+  
+  div(class="admin-products__content")
+    div(class="admin-products__filters")
+      div(class="admin-products__filter-group")
+        label(class="admin-products__label") Поиск
+        input(
+          v-model="searchQuery"
+          type="text"
+          class="admin-products__input"
+          placeholder="Название или артикул..."
+        )
+      
+      div(class="admin-products__filter-group")
+        label(class="admin-products__label") Категория
+        select(v-model="selectedCategory" class="admin-products__select")
+          option(value="") Все категории
+          option(
+            v-for="category in categories"
+            :key="category._id"
+            :value="category._id"
+          ) {{ category.name }}
+      
+      div(class="admin-products__filter-group")
+        label(class="admin-products__label") Статус
+        select(v-model="selectedStatus" class="admin-products__select")
+          option(value="") Все
+          option(value="active") Активные
+          option(value="inactive") Неактивные
+    
+    div(class="admin-products__list")
+      div(class="admin-products__table-container")
+        table(class="admin-products__table")
+          thead
+            tr
+              th(class="admin-products__th") Изобр.
+              th(class="admin-products__th") Название
+              th(class="admin-products__th") Артикул
+              th(class="admin-products__th") Цена
+              th(class="admin-products__th") Категория
+              th(class="admin-products__th") Статус
+              th(class="admin-products__th") Действия
+          tbody
+            tr(
+              v-for="product in filteredProducts"
+              :key="product._id"
+              class="admin-products__tr"
+            )
+              td(class="admin-products__td")
+                img(
+                  v-if="product.image"
+                  :src="product.image"
+                  :alt="product.name"
+                  class="admin-products__image"
+                )
+                div(v-else class="admin-products__no-image") Нет
+              td(class="admin-products__td")
+                div(class="admin-products__name") {{ product.name }}
+              td(class="admin-products__td") {{ product.sku }}
+              td(class="admin-products__td")
+                div(class="admin-products__price") {{ formatPrice(product.price) }}
+                div(
+                  v-if="product.oldPrice"
+                  class="admin-products__old-price"
+                ) {{ formatPrice(product.oldPrice) }}
+              td(class="admin-products__td") {{ getCategoryName(product.category) }}
+              td(class="admin-products__td")
+                span(
+                  :class="getStatusClass(product.active)"
+                ) {{ product.active ? 'Активен' : 'Неактивен' }}
+              td(class="admin-products__td")
+                div(class="admin-products__action-buttons")
+                  button(
+                    @click="editProduct(product)"
+                    class="admin-products__action-btn admin-products__action-btn--edit"
+                  ) Редакт.
+                  button(
+                    @click="toggleProductStatus(product)"
+                    class="admin-products__action-btn admin-products__action-btn--toggle"
+                  ) {{ product.active ? 'Выкл.' : 'Вкл.' }}
+                  button(
+                    @click="deleteProduct(product._id)"
+                    class="admin-products__action-btn admin-products__action-btn--delete"
+                  ) Удалить
+  
+  // Модальное окно товара
+  div(v-if="showProductModal" class="admin-products__modal")
+    div(class="admin-products__modal-content")
+      h3(class="admin-products__modal-title") {{ editingProduct ? 'Редактирование' : 'Добавление' }} товара
+      // Форма будет реализована в следующем шаге
+  
+  // Модальное окно импорта
+  div(v-if="showImportModal" class="admin-products__modal")
+    div(class="admin-products__modal-content")
+      h3(class="admin-products__modal-title") Импорт товаров из CSV
+      
+      div(class="admin-products__import-form")
+        div(class="admin-products__form-group")
+          label(class="admin-products__label") Выберите CSV файл
+          input(
+            type="file"
+            @change="onFileSelect"
+            accept=".csv"
+            class="admin-products__file-input"
+          )
+        
+        div(v-if="selectedFile" class="admin-products__file-info")
+          p Выбран файл: {{ selectedFile.name }}
+          button(
+            @click="importProducts"
+            :disabled="importing"
+            class="admin-products__btn admin-products__btn--primary"
+          ) {{ importing ? 'Импорт...' : 'Начать импорт' }}
+        
+        div(v-if="importResults" class="admin-products__import-results")
+          h4(v-if="importResults.success" class="admin-products__success") Импорт успешно завершен!
+          h4(v-else class="admin-products__error") Ошибка импорта
+          p Обработано товаров: {{ importResults.processed }}
+          p(v-if="importResults.error") Ошибка: {{ importResults.error }}
+      
+      div(class="admin-products__modal-actions")
+        button(
+          @click="showImportModal = false"
+          class="admin-products__btn admin-products__btn--secondary"
+        ) Закрыть

+ 431 - 0
app/pages/Admin/Products/index.styl

@@ -0,0 +1,431 @@
+// Admin Products styles
+.admin-products
+  display: flex
+  flex-direction: column
+  gap: 1.5rem
+
+.admin-products__header
+  display: flex
+  justify-content: space-between
+  align-items: center
+  border-bottom: 1px solid $gray-200
+  padding-bottom: 1.5rem
+
+  .dark &
+    border-bottom-color: $gray-700
+
+.admin-products__title
+  font-size: 1.875rem
+  font-weight: bold
+  color: $gray-900
+
+  .dark &
+    color: $white
+
+.admin-products__actions
+  display: flex
+  gap: 0.75rem
+
+// Button styles
+.admin-products__btn
+  padding: 0.5rem 1rem
+  border-radius: 0.5rem
+  font-weight: 500
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+  border: none
+  cursor: pointer
+  text-decoration: none
+  display: inline-flex
+  align-items: center
+  justify-content: center
+
+.admin-products__btn--primary
+  background-color: $primary-600
+  color: $white
+
+  &:hover
+    background-color: $primary-700
+
+.admin-products__btn--secondary
+  background-color: $gray-200
+  color: $gray-700
+
+  &:hover
+    background-color: $gray-300
+
+  .dark &
+    background-color: $gray-700
+    color: $gray-300
+
+    &:hover
+      background-color: $gray-600
+
+// Content area
+.admin-products__content
+  background-color: $white
+  border-radius: 0.5rem
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)
+  padding: 1.5rem
+
+  .dark &
+    background-color: $gray-800
+
+// Filters
+.admin-products__filters
+  display: grid
+  grid-template-columns: 1fr
+  gap: 1rem
+  margin-bottom: 1.5rem
+
+  @media (min-width: 768px)
+    grid-template-columns: repeat(3, 1fr)
+
+.admin-products__filter-group
+  display: flex
+  flex-direction: column
+  gap: 0.5rem
+
+.admin-products__label
+  display: block
+  font-size: 0.875rem
+  font-weight: 500
+  color: $gray-700
+
+  .dark &
+    color: $gray-300
+
+.admin-products__input
+  width: 100%
+  padding: 0.5rem 0.75rem
+  border: 1px solid $gray-300
+  border-radius: 0.5rem
+  background-color: $white
+  color: $gray-900
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+
+  &:focus
+    outline: none
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    color: $white
+    border-color: $gray-600
+
+    &:focus
+      border-color: $primary-500
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+.admin-products__select
+  width: 100%
+  padding: 0.5rem 0.75rem
+  border: 1px solid $gray-300
+  border-radius: 0.5rem
+  background-color: $white
+  color: $gray-900
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+
+  &:focus
+    outline: none
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    color: $white
+    border-color: $gray-600
+
+    &:focus
+      border-color: $primary-500
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+// Table styles
+.admin-products__table-container
+  overflow-x: auto
+
+.admin-products__table
+  width: 100%
+  border-collapse: collapse
+  background-color: $white
+
+  .dark &
+    background-color: $gray-800
+
+.admin-products__th
+  background-color: $gray-50
+  padding: 0.75rem 1rem
+  text-align: left
+  font-size: 0.75rem
+  font-weight: 500
+  color: $gray-500
+  text-transform: uppercase
+  letter-spacing: 0.05em
+  border-bottom: 1px solid $gray-200
+
+  .dark &
+    background-color: $gray-700
+    color: $gray-300
+    border-bottom-color: $gray-600
+
+.admin-products__tr
+  &:hover
+    background-color: $gray-50
+
+    .dark &
+      background-color: $gray-700
+
+.admin-products__td
+  padding: 0.75rem 1rem
+  border-bottom: 1px solid $gray-200
+  font-size: 0.875rem
+
+  .dark &
+    border-bottom-color: $gray-600
+
+.admin-products__image
+  width: 2.5rem
+  height: 2.5rem
+  object-fit: cover
+  border-radius: 0.25rem
+
+.admin-products__no-image
+  width: 2.5rem
+  height: 2.5rem
+  background-color: $gray-200
+  border-radius: 0.25rem
+  display: flex
+  align-items: center
+  justify-content: center
+  font-size: 0.75rem
+  color: $gray-500
+
+  .dark &
+    background-color: $gray-600
+    color: $gray-400
+
+.admin-products__name
+  font-weight: 500
+  color: $gray-900
+
+  .dark &
+    color: $white
+
+.admin-products__price
+  font-weight: 500
+  color: #16a34a
+
+  .dark &
+    color: #4ade80
+
+.admin-products__old-price
+  font-size: 0.75rem
+  color: $gray-500
+  text-decoration: line-through
+
+  .dark &
+    color: $gray-400
+
+.admin-products__status
+  padding: 0.25rem 0.5rem
+  border-radius: 0.25rem
+  font-size: 0.75rem
+  font-weight: 500
+
+.admin-products__status--active
+  background-color: #f0fdf4
+  color: #16a34a
+
+  .dark &
+    background-color: #14532d
+    color: #4ade80
+
+.admin-products__status--inactive
+  background-color: #fef2f2
+  color: #dc2626
+
+  .dark &
+    background-color: #7f1d1d
+    color: #fca5a5
+
+.admin-products__action-buttons
+  display: flex
+  gap: 0.5rem
+
+.admin-products__action-btn
+  padding: 0.25rem 0.5rem
+  border-radius: 0.25rem
+  font-size: 0.75rem
+  font-weight: 500
+  transition: all 0.2s ease-in-out
+  border: none
+  cursor: pointer
+
+.admin-products__action-btn--edit
+  background-color: #dbeafe
+  color: #1e40af
+
+  &:hover
+    background-color: #bfdbfe
+
+  .dark &
+    background-color: #1e3a8a
+    color: #93c5fd
+
+    &:hover
+      background-color: #1e40af
+
+.admin-products__action-btn--toggle
+  background-color: $gray-100
+  color: $gray-700
+
+  &:hover
+    background-color: $gray-200
+
+  .dark &
+    background-color: $gray-600
+    color: $gray-300
+
+    &:hover
+      background-color: $gray-500
+
+.admin-products__action-btn--delete
+  background-color: #fee2e2
+  color: #dc2626
+
+  &:hover
+    background-color: #fecaca
+
+  .dark &
+    background-color: #7f1d1d
+    color: #fca5a5
+
+    &:hover
+      background-color: #991b1b
+
+// Modal styles
+.admin-products__modal
+  position: fixed
+  inset: 0
+  background-color: rgba(0, 0, 0, 0.5)
+  display: flex
+  align-items: center
+  justify-content: center
+  z-index: 50
+
+.admin-products__modal-content
+  background-color: $white
+  border-radius: 0.5rem
+  padding: 1.5rem
+  max-width: 48rem
+  width: 100%
+  margin: 0 1rem
+  max-height: 90vh
+  overflow-y: auto
+
+  .dark &
+    background-color: $gray-800
+
+.admin-products__modal-title
+  font-size: 1.125rem
+  font-weight: 500
+  color: $gray-900
+  margin-bottom: 1rem
+
+  .dark &
+    color: $white
+
+// Import form styles
+.admin-products__import-form
+  display: flex
+  flex-direction: column
+  gap: 1rem
+  margin-bottom: 1.5rem
+
+.admin-products__file-input
+  display: block
+  width: 100%
+  font-size: 0.875rem
+  color: $gray-500
+
+  &::file-selector-button
+    margin-right: 1rem
+    padding: 0.5rem 1rem
+    border-radius: 9999px
+    border: none
+    background-color: $primary-50
+    color: $primary-700
+    font-size: 0.875rem
+    font-weight: 500
+    cursor: pointer
+    transition: background-color 0.2s ease-in-out
+
+    &:hover
+      background-color: $primary-100
+
+    .dark &
+      background-color: $primary-900
+      color: $primary-300
+
+      &:hover
+        background-color: $primary-800
+
+.admin-products__file-info
+  padding: 1rem
+  background-color: $gray-50
+  border-radius: 0.5rem
+
+  .dark &
+    background-color: $gray-700
+
+.admin-products__import-results
+  padding: 1rem
+  border-radius: 0.5rem
+
+.admin-products__success
+  color: #16a34a
+  font-weight: 500
+  margin-bottom: 0.5rem
+
+  .dark &
+    color: #4ade80
+
+.admin-products__error
+  color: #dc2626
+  font-weight: 500
+  margin-bottom: 0.5rem
+
+  .dark &
+    color: #fca5a5
+
+.admin-products__modal-actions
+  display: flex
+  gap: 0.75rem
+  justify-content: flex-end
+
+// Responsive adjustments
+@media (max-width: 768px)
+  .admin-products__header
+    flex-direction: column
+    align-items: flex-start
+    gap: 1rem
+
+  .admin-products__actions
+    width: 100%
+    justify-content: flex-start
+
+  .admin-products__filters
+    grid-template-columns: 1fr
+
+  .admin-products__action-buttons
+    flex-direction: column
+
+  .admin-products__action-btn
+    text-align: center
+
+  .admin-products__modal-content
+    margin: 0 0.5rem
+    padding: 1rem

+ 1 - 1
app/pages/Admin/Settings/index.coffee

@@ -1,4 +1,4 @@
-document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss">'+stylFns['app/pages/Admin/Settings/index.styl']+'</style>')
+document.head.insertAdjacentHTML('beforeend','<style type="text/css">'+stylFns['app/pages/Admin/Settings/index.styl']+'</style>')
 
 PouchDB = require 'app/utils/pouch'
 

+ 437 - 188
app/pages/Admin/Settings/index.styl

@@ -1,188 +1,437 @@
-@css {
-  .admin-settings {
-    @apply space-y-4 sm:space-y-6;
-  }
-
-  .admin-settings__header {
-    @apply border-b border-gray-200 dark:border-gray-700 pb-4 sm:pb-6;
-  }
-
-  .admin-settings__title {
-    @apply text-xl sm:text-2xl font-bold text-gray-900 dark:text-white;
-  }
-
-  .admin-settings__subtitle {
-    @apply text-gray-600 dark:text-gray-400 mt-1 sm:mt-2 text-sm sm:text-base;
-  }
-
-  .admin-settings__content {
-    @apply bg-white dark:bg-gray-800 rounded-lg shadow-sm;
-  }
-
-  .admin-settings__tabs {
-    @apply border-b border-gray-200 dark:border-gray-700 flex space-x-2 sm:space-x-8 px-4 sm:px-6 overflow-x-auto;
-  }
-
-  .admin-settings__tab {
-    @apply py-3 sm:py-4 px-2 sm:px-1 border-b-2 font-medium text-sm transition-colors duration-200 whitespace-nowrap flex-shrink-0;
-  }
-
-  .admin-settings__tab--active {
-    @apply border-primary-500 text-primary-600 dark:text-primary-400;
-  }
-
-  .admin-settings__tab--inactive {
-    @apply border-transparent text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-600;
-  }
-
-  .admin-settings__tab-content {
-    @apply p-4 sm:p-6;
-  }
-
-  .admin-settings__section {
-    @apply mb-6 sm:mb-8;
-  }
-
-  .admin-settings__section-title {
-    @apply text-base sm:text-lg font-medium text-gray-900 dark:text-white mb-2;
-  }
-
-  .admin-settings__section-description {
-    @apply text-gray-600 dark:text-gray-400 mb-3 sm:mb-4 text-sm sm:text-base;
-  }
-
-  .admin-settings__domains-list {
-    @apply space-y-3 sm:space-y-4 mb-4 sm:mb-6;
-  }
-
-  .admin-settings__domain-item {
-    @apply flex flex-col sm:flex-row sm:items-center sm:justify-between p-3 sm:p-4 border border-gray-200 dark:border-gray-700 rounded-lg gap-3;
-  }
-
-  .admin-settings__domain-info {
-    @apply flex-1;
-  }
-
-  .admin-settings__domain-name {
-    @apply font-medium text-gray-900 dark:text-white text-sm sm:text-base;
-  }
-
-  .admin-settings__domain-desc {
-    @apply text-gray-600 dark:text-gray-400 text-xs sm:text-sm;
-  }
-
-  .admin-settings__domain-actions {
-    @apply flex space-x-2 self-end sm:self-auto;
-  }
-
-  .admin-settings__languages-list {
-    @apply grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4;
-  }
-
-  .admin-settings__language-item {
-    @apply flex items-center justify-between p-3 sm:p-4 border border-gray-200 dark:border-gray-700 rounded-lg;
-  }
-
-  .admin-settings__language-info {
-    @apply flex items-center space-x-2 sm:space-x-3;
-  }
-
-  .admin-settings__language-code {
-    @apply bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded text-xs sm:text-sm font-medium;
-  }
-
-  .admin-settings__language-name {
-    @apply text-gray-700 dark:text-gray-300 text-sm sm:text-base;
-  }
-
-  .admin-settings__language-btn {
-    @apply px-2 sm:px-3 py-1 rounded text-xs sm:text-sm font-medium transition-colors duration-200 whitespace-nowrap;
-  }
-
-  .admin-settings__language-btn--enabled {
-    @apply bg-red-100 text-red-700 hover:bg-red-200 dark:bg-red-900 dark:text-red-300 dark:hover:bg-red-800;
-  }
-
-  .admin-settings__language-btn--disabled {
-    @apply bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900 dark:text-green-300 dark:hover:bg-green-800;
-  }
-
-  .admin-settings__form {
-    @apply max-w-full sm:max-w-md space-y-3 sm:space-y-4;
-  }
-
-  .admin-settings__form-group {
-    @apply space-y-1 sm:space-y-2;
-  }
-
-  .admin-settings__label {
-    @apply block text-sm font-medium text-gray-700 dark:text-gray-300;
-  }
-
-  .admin-settings__input {
-    @apply w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent text-sm sm:text-base;
-  }
-
-  .admin-settings__select {
-    @apply w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent text-sm sm:text-base;
-  }
-
-  .admin-settings__checkbox-group {
-    @apply space-y-2;
-  }
-
-  .admin-settings__checkbox-label {
-    @apply flex items-center space-x-2 text-gray-700 dark:text-gray-300 text-sm sm:text-base;
-  }
-
-  .admin-settings__checkbox {
-    @apply rounded border-gray-300 text-primary-600 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 w-4 h-4;
-  }
-
-  .admin-settings__btn {
-    @apply px-3 sm:px-4 py-2 rounded-lg font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 text-sm sm:text-base;
-  }
-
-  .admin-settings__btn--primary {
-    @apply bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500;
-  }
-
-  .admin-settings__btn--secondary {
-    @apply bg-gray-200 text-gray-700 hover:bg-gray-300 focus:ring-gray-500 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600;
-  }
-
-  .admin-settings__btn--danger {
-    @apply bg-red-600 text-white hover:bg-red-700 focus:ring-red-500;
-  }
-
-  .admin-settings__modal {
-    @apply fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4;
-  }
-
-  .admin-settings__modal-content {
-    @apply bg-white dark:bg-gray-800 rounded-lg p-4 sm:p-6 max-w-md w-full mx-auto max-h-[90vh] overflow-y-auto;
-  }
-
-  .admin-settings__modal-title {
-    @apply text-lg font-medium text-gray-900 dark:text-white mb-3 sm:mb-4;
-  }
-
-  .admin-settings__modal-form {
-    @apply space-y-3 sm:space-y-4 mb-4 sm:mb-6;
-  }
-
-  .admin-settings__modal-actions {
-    @apply flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-3 justify-end;
-  }
-}
-
-/* Медиа-запросы для очень маленьких экранов */
-@media (max-width: 360px) {
-  .admin-settings__domain-actions {
-    @apply flex-col space-y-2 space-x-0;
-  }
-  
-  .admin-settings__btn {
-    @apply px-2 py-1 text-xs;
-  }
-}
+// Admin Settings styles
+.admin-settings
+  display: flex
+  flex-direction: column
+  gap: 1.5rem
+
+.admin-settings__header
+  border-bottom: 1px solid $gray-200
+  padding-bottom: 1.5rem
+
+  .dark &
+    border-bottom-color: $gray-700
+
+.admin-settings__title
+  font-size: 1.875rem
+  font-weight: bold
+  color: $gray-900
+  margin-bottom: 0.5rem
+
+  .dark &
+    color: $white
+
+.admin-settings__subtitle
+  color: $gray-600
+  font-size: 1.125rem
+
+  .dark &
+    color: $gray-400
+
+.admin-settings__content
+  background-color: $white
+  border-radius: 0.5rem
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)
+
+  .dark &
+    background-color: $gray-800
+
+.admin-settings__tabs
+  border-bottom: 1px solid $gray-200
+  display: flex
+  gap: 2rem
+  padding: 0 1.5rem
+
+  .dark &
+    border-bottom-color: $gray-700
+
+.admin-settings__tab
+  padding: 1rem 0.25rem
+  border-bottom: 2px solid transparent
+  font-size: 0.875rem
+  font-weight: 500
+  transition: all 0.2s ease-in-out
+  cursor: pointer
+  border: none
+  background: none
+  color: $gray-500
+
+  &:hover
+    color: $gray-700
+    border-bottom-color: $gray-300
+
+    .dark &
+      color: $gray-300
+      border-bottom-color: $gray-600
+
+.admin-settings__tab--active
+  border-bottom-color: $primary-500
+  color: $primary-600
+
+  .dark &
+    color: $primary-400
+
+  &:hover
+    border-bottom-color: $primary-500
+    color: $primary-600
+
+    .dark &
+      color: $primary-400
+
+.admin-settings__tab--inactive
+  border-bottom-color: transparent
+
+.admin-settings__tab-content
+  padding: 1.5rem
+
+.admin-settings__section
+  margin-bottom: 2rem
+
+  &:last-child
+    margin-bottom: 0
+
+.admin-settings__section-title
+  font-size: 1.125rem
+  font-weight: 500
+  color: $gray-900
+  margin-bottom: 0.5rem
+
+  .dark &
+    color: $white
+
+.admin-settings__section-description
+  color: $gray-600
+  margin-bottom: 1rem
+
+  .dark &
+    color: $gray-400
+
+// Domains list
+.admin-settings__domains-list
+  display: flex
+  flex-direction: column
+  gap: 1rem
+  margin-bottom: 1.5rem
+
+.admin-settings__domain-item
+  display: flex
+  align-items: center
+  justify-content: space-between
+  padding: 1rem
+  border: 1px solid $gray-200
+  border-radius: 0.5rem
+
+  .dark &
+    border-color: $gray-700
+
+.admin-settings__domain-info
+  flex: 1
+
+.admin-settings__domain-name
+  font-weight: 500
+  color: $gray-900
+  margin-bottom: 0.25rem
+
+  .dark &
+    color: $white
+
+.admin-settings__domain-desc
+  color: $gray-600
+  font-size: 0.875rem
+
+  .dark &
+    color: $gray-400
+
+.admin-settings__domain-actions
+  display: flex
+  gap: 0.5rem
+
+// Languages list
+.admin-settings__languages-list
+  display: grid
+  grid-template-columns: 1fr
+  gap: 1rem
+
+  @media (min-width: 768px)
+    grid-template-columns: repeat(2, 1fr)
+
+.admin-settings__language-item
+  display: flex
+  align-items: center
+  justify-content: space-between
+  padding: 1rem
+  border: 1px solid $gray-200
+  border-radius: 0.5rem
+
+  .dark &
+    border-color: $gray-700
+
+.admin-settings__language-info
+  display: flex
+  align-items: center
+  gap: 0.75rem
+
+.admin-settings__language-code
+  background-color: $gray-100
+  padding: 0.25rem 0.5rem
+  border-radius: 0.25rem
+  font-size: 0.75rem
+  font-weight: 500
+
+  .dark &
+    background-color: $gray-700
+
+.admin-settings__language-name
+  color: $gray-700
+
+  .dark &
+    color: $gray-300
+
+.admin-settings__language-btn
+  padding: 0.25rem 0.75rem
+  border-radius: 0.25rem
+  font-size: 0.75rem
+  font-weight: 500
+  transition: all 0.2s ease-in-out
+  border: none
+  cursor: pointer
+
+.admin-settings__language-btn--enabled
+  background-color: #fef2f2
+  color: #dc2626
+
+  &:hover
+    background-color: #fee2e2
+
+  .dark &
+    background-color: #7f1d1d
+    color: #fca5a5
+
+    &:hover
+      background-color: #991b1b
+
+.admin-settings__language-btn--disabled
+  background-color: #f0fdf4
+  color: #16a34a
+
+  &:hover
+    background-color: #dcfce7
+
+  .dark &
+    background-color: #14532d
+    color: #4ade80
+
+    &:hover
+      background-color: #166534
+
+// Form styles
+.admin-settings__form
+  max-width: 28rem
+  display: flex
+  flex-direction: column
+  gap: 1rem
+
+.admin-settings__form-group
+  display: flex
+  flex-direction: column
+  gap: 0.5rem
+
+.admin-settings__label
+  display: block
+  font-size: 0.875rem
+  font-weight: 500
+  color: $gray-700
+
+  .dark &
+    color: $gray-300
+
+.admin-settings__input
+  width: 100%
+  padding: 0.5rem 0.75rem
+  border: 1px solid $gray-300
+  border-radius: 0.5rem
+  background-color: $white
+  color: $gray-900
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+
+  &:focus
+    outline: none
+    border-color: $primary-500
+    // Fixed: Use rgba with actual RGB values instead of variable
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    color: $white
+    border-color: $gray-600
+
+    &:focus
+      border-color: $primary-500
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+.admin-settings__select
+  width: 100%
+  padding: 0.5rem 0.75rem
+  border: 1px solid $gray-300
+  border-radius: 0.5rem
+  background-color: $white
+  color: $gray-900
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+
+  &:focus
+    outline: none
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    color: $white
+    border-color: $gray-600
+
+    &:focus
+      border-color: $primary-500
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+.admin-settings__checkbox-group
+  display: flex
+  flex-direction: column
+  gap: 0.5rem
+
+.admin-settings__checkbox-label
+  display: flex
+  align-items: center
+  gap: 0.5rem
+  color: $gray-700
+  font-size: 0.875rem
+
+  .dark &
+    color: $gray-300
+
+.admin-settings__checkbox
+  border-radius: 0.25rem
+  border: 1px solid $gray-300
+  background-color: $white
+  color: $primary-600
+
+  &:focus
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    border-color: $gray-600
+
+// Button styles
+.admin-settings__btn
+  padding: 0.5rem 1rem
+  border-radius: 0.5rem
+  font-weight: 500
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+  border: none
+  cursor: pointer
+  text-decoration: none
+  display: inline-flex
+  align-items: center
+  justify-content: center
+
+  &:focus
+    outline: none
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+.admin-settings__btn--primary
+  background-color: $primary-600
+  color: $white
+
+  &:hover
+    background-color: $primary-700
+
+.admin-settings__btn--secondary
+  background-color: $gray-200
+  color: $gray-700
+
+  &:hover
+    background-color: $gray-300
+
+  .dark &
+    background-color: $gray-700
+    color: $gray-300
+
+    &:hover
+      background-color: $gray-600
+
+.admin-settings__btn--danger
+  background-color: #dc2626
+  color: $white
+
+  &:hover
+    background-color: #b91c1c
+
+// Modal styles
+.admin-settings__modal
+  position: fixed
+  inset: 0
+  background-color: rgba(0, 0, 0, 0.5)
+  display: flex
+  align-items: center
+  justify-content: center
+  z-index: 50
+
+.admin-settings__modal-content
+  background-color: $white
+  border-radius: 0.5rem
+  padding: 1.5rem
+  max-width: 28rem
+  width: 100%
+  margin: 0 1rem
+
+  .dark &
+    background-color: $gray-800
+
+.admin-settings__modal-title
+  font-size: 1.125rem
+  font-weight: 500
+  color: $gray-900
+  margin-bottom: 1rem
+
+  .dark &
+    color: $white
+
+.admin-settings__modal-form
+  display: flex
+  flex-direction: column
+  gap: 1rem
+  margin-bottom: 1.5rem
+
+.admin-settings__modal-actions
+  display: flex
+  gap: 0.75rem
+  justify-content: flex-end
+
+// Responsive adjustments
+@media (max-width: 768px)
+  .admin-settings__tabs
+    gap: 1rem
+    padding: 0 1rem
+
+  .admin-settings__tab-content
+    padding: 1rem
+
+  .admin-settings__domain-item
+    flex-direction: column
+    align-items: flex-start
+    gap: 1rem
+
+  .admin-settings__domain-actions
+    width: 100%
+    justify-content: flex-end
+
+  .admin-settings__language-item
+    flex-direction: column
+    align-items: flex-start
+    gap: 1rem
+
+  .admin-settings__modal-content
+    margin: 0 0.5rem
+    padding: 1rem

+ 150 - 0
app/pages/Admin/Slider/index.coffee

@@ -0,0 +1,150 @@
+document.head.insertAdjacentHTML('beforeend','<style type="text/css">'+stylFns['app/pages/Admin/Slider/index.styl']+'</style>')
+
+PouchDB = require 'app/utils/pouch'
+
+module.exports =
+  name: 'AdminSlider'
+  
+  render: (new Function '_ctx', '_cache', renderFns['app/pages/Admin/Slider/index.pug'])()
+  
+  data: ->
+    return {
+      slides: []
+      showSlideModal: false
+      editingSlide: null
+      availableDomains: []
+      slideForm: {
+        title: ''
+        subtitle: ''
+        image: ''
+        buttonText: 'В каталог'
+        buttonLink: '/catalog'
+        order: 0
+        active: true
+        domains: []
+      }
+    }
+  
+  methods:
+    loadSlides: ->
+      PouchDB.queryView('admin', 'slides', { include_docs: true })
+        .then (result) =>
+          @slides = result.rows.map (row) -> row.doc
+          # Сортируем по порядку
+          @slides.sort (a, b) -> a.order - b.order
+        .catch (error) =>
+          console.error 'Ошибка загрузки слайдов:', error
+          @showNotification 'Ошибка загрузки слайдов', 'error'
+    
+    loadDomains: ->
+      PouchDB.queryView('admin', 'domain_settings', { include_docs: true })
+        .then (result) =>
+          @availableDomains = result.rows.map (row) -> row.doc
+        .catch (error) =>
+          console.error 'Ошибка загрузки доменов:', error
+    
+    editSlide: (slide) ->
+      @editingSlide = slide
+      @slideForm = {
+        title: slide.title || ''
+        subtitle: slide.subtitle || ''
+        image: slide.image || ''
+        buttonText: slide.buttonText || 'В каталог'
+        buttonLink: slide.buttonLink || '/catalog'
+        order: slide.order || 0
+        active: slide.active != false
+        domains: slide.domains || []
+      }
+      @showSlideModal = true
+    
+    saveSlide: ->
+      if !@slideForm.title
+        @showNotification 'Введите заголовок слайда', 'error'
+        return
+      
+      slideData = {
+        type: 'hero_slide'
+        ...@slideForm
+        updatedAt: new Date().toISOString()
+      }
+      
+      if @editingSlide
+        slideData._id = @editingSlide._id
+        slideData._rev = @editingSlide._rev
+        slideData.createdAt = @editingSlide.createdAt
+      else
+        slideData._id = "hero_slide:#{Date.now()}"
+        slideData.createdAt = new Date().toISOString()
+        # Автоматически устанавливаем порядок для нового слайда
+        slideData.order = @slides.length
+      
+      PouchDB.saveToRemote(slideData)
+        .then (result) =>
+          @showSlideModal = false
+          @resetSlideForm()
+          @loadSlides()
+          @showNotification 'Слайд успешно сохранен'
+        .catch (error) =>
+          console.error 'Ошибка сохранения слайда:', error
+          @showNotification 'Ошибка сохранения слайда', 'error'
+    
+    deleteSlide: (slideId) ->
+      if confirm('Вы уверены, что хотите удалить этот слайд?')
+        PouchDB.getDocument(slideId)
+          .then (doc) ->
+            PouchDB.saveToRemote({ ...doc, _deleted: true })
+          .then (result) =>
+            @loadSlides()
+            @showNotification 'Слайд удален'
+          .catch (error) =>
+            console.error 'Ошибка удаления слайда:', error
+            @showNotification 'Ошибка удаления слайда', 'error'
+    
+    toggleSlideStatus: (slide) ->
+      updatedSlide = {
+        ...slide
+        active: !slide.active
+        updatedAt: new Date().toISOString()
+      }
+      
+      PouchDB.saveToRemote(updatedSlide)
+        .then (result) =>
+          @loadSlides()
+          @showNotification 'Статус слайда обновлен'
+        .catch (error) =>
+          console.error 'Ошибка обновления статуса:', error
+          @showNotification 'Ошибка обновления статуса', 'error'
+    
+    resetSlideForm: ->
+      @editingSlide = null
+      @slideForm = {
+        title: ''
+        subtitle: ''
+        image: ''
+        buttonText: 'В каталог'
+        buttonLink: '/catalog'
+        order: @slides.length
+        active: true
+        domains: []
+      }
+    
+    getStatusClass: (isActive) ->
+      baseClass = 'admin-slider__item-status'
+      if isActive
+        return "#{baseClass} admin-slider__item-status--active"
+      else
+        return "#{baseClass} admin-slider__item-status--inactive"
+    
+    getToggleBtnClass: (isActive) ->
+      baseClass = 'admin-slider__btn'
+      if isActive
+        return "#{baseClass} admin-slider__btn--delete"
+      else
+        return "#{baseClass} admin-slider__btn--primary"
+    
+    showNotification: (message, type = 'success') ->
+      @$root.showNotification?(message, type) || debug.log("#{type}: #{message}")
+  
+  mounted: ->
+    @loadSlides()
+    @loadDomains()

+ 140 - 0
app/pages/Admin/Slider/index.pug

@@ -0,0 +1,140 @@
+div(class="admin-slider")
+  div(class="admin-slider__header")
+    h1(class="admin-slider__title") Управление слайдером
+    button(
+      @click="showSlideModal = true"
+      class="admin-slider__add-btn"
+    ) Добавить слайд
+  
+  div(class="admin-slider__content")
+    div(class="admin-slider__list")
+      div(
+        v-for="(slide, index) in slides"
+        :key="slide._id"
+        class="admin-slider__item"
+      )
+        div(class="admin-slider__item-preview")
+          img(
+            v-if="slide.image"
+            :src="slide.image"
+            :alt="slide.title"
+            class="admin-slider__item-image"
+          )
+          div(v-else class="admin-slider__item-placeholder") Нет изображения
+          div(class="admin-slider__item-info")
+            h3(class="admin-slider__item-title") {{ slide.title || 'Без названия' }}
+            p(class="admin-slider__item-subtitle") {{ slide.subtitle || 'Без описания' }}
+            div(class="admin-slider__item-meta")
+              span(class="admin-slider__item-order") Порядок: {{ slide.order }}
+              span(
+                :class="getStatusClass(slide.active)"
+              ) {{ slide.active ? 'Активен' : 'Неактивен' }}
+        
+        div(class="admin-slider__item-actions")
+          button(
+            @click="editSlide(slide)"
+            class="admin-slider__btn admin-slider__btn--edit"
+          ) Редактировать
+          button(
+            @click="toggleSlideStatus(slide)"
+            :class="getToggleBtnClass(slide.active)"
+          ) {{ slide.active ? 'Деактивировать' : 'Активировать' }}
+          button(
+            @click="deleteSlide(slide._id)"
+            class="admin-slider__btn admin-slider__btn--delete"
+          ) Удалить
+  
+  // Модальное окно редактирования слайда
+  div(v-if="showSlideModal" class="admin-slider__modal")
+    div(class="admin-slider__modal-content")
+      h3(class="admin-slider__modal-title") {{ editingSlide ? 'Редактирование' : 'Добавление' }} слайда
+      
+      div(class="admin-slider__modal-form")
+        div(class="admin-slider__form-group")
+          label(class="admin-slider__label") Заголовок
+          input(
+            v-model="slideForm.title"
+            type="text"
+            class="admin-slider__input"
+            placeholder="Введите заголовок слайда"
+          )
+        
+        div(class="admin-slider__form-group")
+          label(class="admin-slider__label") Подзаголовок
+          textarea(
+            v-model="slideForm.subtitle"
+            class="admin-slider__textarea"
+            placeholder="Введите описание слайда"
+            rows="3"
+          )
+        
+        div(class="admin-slider__form-group")
+          label(class="admin-slider__label") Ссылка на изображение
+          input(
+            v-model="slideForm.image"
+            type="text"
+            class="admin-slider__input"
+            placeholder="https://example.com/image.jpg"
+          )
+        
+        div(class="admin-slider__form-group")
+          label(class="admin-slider__label") Текст кнопки
+          input(
+            v-model="slideForm.buttonText"
+            type="text"
+            class="admin-slider__input"
+            placeholder="В каталог"
+          )
+        
+        div(class="admin-slider__form-group")
+          label(class="admin-slider__label") Ссылка кнопки
+          input(
+            v-model="slideForm.buttonLink"
+            type="text"
+            class="admin-slider__input"
+            placeholder="/catalog"
+          )
+        
+        div(class="admin-slider__form-group")
+          label(class="admin-slider__label") Порядок
+          input(
+            v-model="slideForm.order"
+            type="number"
+            class="admin-slider__input"
+            min="0"
+          )
+        
+        div(class="admin-slider__form-group")
+          label(class="admin-slider__checkbox-label")
+            input(
+              v-model="slideForm.active"
+              type="checkbox"
+              class="admin-slider__checkbox"
+            )
+            span Активный слайд
+        
+        div(class="admin-slider__form-group")
+          label(class="admin-slider__label") Домены
+          div(class="admin-slider__domains-list")
+            label(
+              v-for="domain in availableDomains"
+              :key="domain._id"
+              class="admin-slider__domain-label"
+            )
+              input(
+                type="checkbox"
+                :value="domain.domain"
+                v-model="slideForm.domains"
+                class="admin-slider__domain-checkbox"
+              )
+              span {{ domain.domain }}
+      
+      div(class="admin-slider__modal-actions")
+        button(
+          @click="saveSlide"
+          class="admin-slider__btn admin-slider__btn--primary"
+        ) {{ editingSlide ? 'Обновить' : 'Добавить' }}
+        button(
+          @click="showSlideModal = false"
+          class="admin-slider__btn admin-slider__btn--secondary"
+        ) Отмена

+ 394 - 0
app/pages/Admin/Slider/index.styl

@@ -0,0 +1,394 @@
+// Admin Slider styles
+.admin-slider
+  display: flex
+  flex-direction: column
+  gap: 1.5rem
+
+.admin-slider__header
+  display: flex
+  justify-content: space-between
+  align-items: center
+  border-bottom: 1px solid $gray-200
+  padding-bottom: 1.5rem
+
+  .dark &
+    border-bottom-color: $gray-700
+
+.admin-slider__title
+  font-size: 1.875rem
+  font-weight: bold
+  color: $gray-900
+
+  .dark &
+    color: $white
+
+.admin-slider__add-btn
+  background-color: $primary-600
+  color: $white
+  padding: 0.5rem 1rem
+  border-radius: 0.5rem
+  font-weight: 500
+  transition: background-color 0.2s ease-in-out
+  border: none
+  cursor: pointer
+
+  &:hover
+    background-color: $primary-700
+
+// Slider list
+.admin-slider__list
+  display: grid
+  grid-template-columns: 1fr
+  gap: 1.5rem
+
+  @media (min-width: 768px)
+    grid-template-columns: repeat(2, 1fr)
+
+  @media (min-width: 1024px)
+    grid-template-columns: repeat(3, 1fr)
+
+.admin-slider__item
+  background-color: $white
+  border-radius: 0.5rem
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)
+  border: 1px solid $gray-200
+  overflow: hidden
+
+  .dark &
+    background-color: $gray-800
+    border-color: $gray-700
+
+.admin-slider__item-preview
+  padding: 1rem
+
+.admin-slider__item-image
+  width: 100%
+  height: 12rem
+  object-fit: cover
+  border-radius: 0.5rem
+  margin-bottom: 1rem
+
+.admin-slider__item-placeholder
+  width: 100%
+  height: 12rem
+  background-color: $gray-200
+  border-radius: 0.5rem
+  margin-bottom: 1rem
+  display: flex
+  align-items: center
+  justify-content: center
+  color: $gray-500
+  font-size: 0.875rem
+
+  .dark &
+    background-color: $gray-700
+    color: $gray-400
+
+.admin-slider__item-info
+  display: flex
+  flex-direction: column
+  gap: 0.5rem
+
+.admin-slider__item-title
+  font-size: 1.125rem
+  font-weight: 500
+  color: $gray-900
+  margin: 0
+
+  .dark &
+    color: $white
+
+.admin-slider__item-subtitle
+  color: $gray-600
+  font-size: 0.875rem
+  line-height: 1.4
+  display: -webkit-box
+  -webkit-line-clamp: 2
+  -webkit-box-orient: vertical
+  overflow: hidden
+
+  .dark &
+    color: $gray-400
+
+.admin-slider__item-meta
+  display: flex
+  justify-content: space-between
+  align-items: center
+  font-size: 0.75rem
+
+.admin-slider__item-order
+  color: $gray-500
+
+  .dark &
+    color: $gray-400
+
+.admin-slider__item-status
+  padding: 0.25rem 0.5rem
+  border-radius: 9999px
+  font-size: 0.75rem
+  font-weight: 500
+
+.admin-slider__item-status--active
+  background-color: #f0fdf4
+  color: #16a34a
+
+  .dark &
+    background-color: #14532d
+    color: #4ade80
+
+.admin-slider__item-status--inactive
+  background-color: #fef2f2
+  color: #dc2626
+
+  .dark &
+    background-color: #7f1d1d
+    color: #fca5a5
+
+.admin-slider__item-actions
+  display: flex
+  flex-direction: column
+  gap: 0.5rem
+  padding: 1rem
+  border-top: 1px solid $gray-200
+  background-color: $white
+
+  .dark &
+    border-top-color: $gray-700
+    background-color: $gray-800
+
+// Button styles
+.admin-slider__btn
+  padding: 0.5rem 0.75rem
+  border-radius: 0.25rem
+  font-size: 0.75rem
+  font-weight: 500
+  transition: all 0.2s ease-in-out
+  border: none
+  cursor: pointer
+  text-align: center
+
+.admin-slider__btn--primary
+  background-color: $primary-600
+  color: $white
+
+  &:hover
+    background-color: $primary-700
+
+.admin-slider__btn--secondary
+  background-color: $gray-200
+  color: $gray-700
+
+  &:hover
+    background-color: $gray-300
+
+  .dark &
+    background-color: $gray-700
+    color: $gray-300
+
+    &:hover
+      background-color: $gray-600
+
+.admin-slider__btn--edit
+  background-color: #dbeafe
+  color: #1e40af
+
+  &:hover
+    background-color: #bfdbfe
+
+  .dark &
+    background-color: #1e3a8a
+    color: #93c5fd
+
+    &:hover
+      background-color: #1e40af
+
+.admin-slider__btn--delete
+  background-color: #fee2e2
+  color: #dc2626
+
+  &:hover
+    background-color: #fecaca
+
+  .dark &
+    background-color: #7f1d1d
+    color: #fca5a5
+
+    &:hover
+      background-color: #991b1b
+
+// Modal styles
+.admin-slider__modal
+  position: fixed
+  inset: 0
+  background-color: rgba(0, 0, 0, 0.5)
+  display: flex
+  align-items: center
+  justify-content: center
+  z-index: 50
+
+.admin-slider__modal-content
+  background-color: $white
+  border-radius: 0.5rem
+  padding: 1.5rem
+  max-width: 42rem
+  width: 100%
+  margin: 0 1rem
+  max-height: 90vh
+  overflow-y: auto
+
+  .dark &
+    background-color: $gray-800
+
+.admin-slider__modal-title
+  font-size: 1.125rem
+  font-weight: 500
+  color: $gray-900
+  margin-bottom: 1rem
+
+  .dark &
+    color: $white
+
+.admin-slider__modal-form
+  display: flex
+  flex-direction: column
+  gap: 1rem
+  margin-bottom: 1.5rem
+
+.admin-slider__form-group
+  display: flex
+  flex-direction: column
+  gap: 0.5rem
+
+.admin-slider__label
+  display: block
+  font-size: 0.875rem
+  font-weight: 500
+  color: $gray-700
+
+  .dark &
+    color: $gray-300
+
+.admin-slider__input
+  width: 100%
+  padding: 0.5rem 0.75rem
+  border: 1px solid $gray-300
+  border-radius: 0.5rem
+  background-color: $white
+  color: $gray-900
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+
+  &:focus
+    outline: none
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    color: $white
+    border-color: $gray-600
+
+    &:focus
+      border-color: $primary-500
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+.admin-slider__textarea
+  width: 100%
+  padding: 0.5rem 0.75rem
+  border: 1px solid $gray-300
+  border-radius: 0.5rem
+  background-color: $white
+  color: $gray-900
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+  resize: vertical
+  min-height: 5rem
+
+  &:focus
+    outline: none
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    color: $white
+    border-color: $gray-600
+
+    &:focus
+      border-color: $primary-500
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+.admin-slider__checkbox-label
+  display: flex
+  align-items: center
+  gap: 0.5rem
+  color: $gray-700
+  font-size: 0.875rem
+
+  .dark &
+    color: $gray-300
+
+.admin-slider__checkbox
+  border-radius: 0.25rem
+  border: 1px solid $gray-300
+  background-color: $white
+  color: $primary-600
+
+  &:focus
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    border-color: $gray-600
+
+.admin-slider__domains-list
+  display: grid
+  grid-template-columns: repeat(2, 1fr)
+  gap: 0.5rem
+
+.admin-slider__domain-label
+  display: flex
+  align-items: center
+  gap: 0.5rem
+  font-size: 0.875rem
+  color: $gray-700
+
+  .dark &
+    color: $gray-300
+
+.admin-slider__domain-checkbox
+  border-radius: 0.25rem
+  border: 1px solid $gray-300
+  background-color: $white
+  color: $primary-600
+
+  &:focus
+    border-color: $primary-500
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  .dark &
+    background-color: $gray-700
+    border-color: $gray-600
+
+.admin-slider__modal-actions
+  display: flex
+  gap: 0.75rem
+  justify-content: flex-end
+
+// Responsive adjustments
+@media (max-width: 768px)
+  .admin-slider__header
+    flex-direction: column
+    align-items: flex-start
+    gap: 1rem
+
+  .admin-slider__list
+    grid-template-columns: 1fr
+
+  .admin-slider__domains-list
+    grid-template-columns: 1fr
+
+  .admin-slider__modal-content
+    margin: 0 0.5rem
+    padding: 1rem

+ 1 - 1
app/pages/Admin/index.coffee

@@ -1,4 +1,4 @@
-document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss">'+stylFns['app/pages/Admin/index.styl']+'</style>')
+document.head.insertAdjacentHTML('beforeend','<style type="text/css">'+stylFns['app/pages/Admin/index.styl']+'</style>')
 
 PouchDB = require 'app/utils/pouch'
 

+ 391 - 127
app/pages/Admin/index.styl

@@ -1,127 +1,391 @@
-@css {
-  .admin {
-    @apply min-h-screen flex flex-col bg-gray-50 dark:bg-gray-900;
-  }
-
-  /* Header */
-  .admin__header {
-    @apply bg-white dark:bg-gray-800 shadow-sm z-40 sticky top-0;
-  }
-
-  .admin__header-content {
-    @apply px-4 sm:px-6 py-3 sm:py-4;
-  }
-
-  .admin__header-top {
-    @apply flex items-center justify-between;
-  }
-
-  .admin__header-title {
-    @apply text-lg sm:text-xl font-bold text-gray-800 dark:text-white;
-  }
-
-  .admin__domain-info {
-    @apply text-xs sm:text-sm text-gray-600 dark:text-gray-400 mt-2;
-  }
-
-  .admin__mobile-menu-btn {
-    @apply lg:hidden p-2 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors duration-200;
-  }
-
-  /* Body layout */
-  .admin__body {
-    @apply flex flex-1 flex-col lg:flex-row;
-  }
-
-  /* Sidebar - positioned above header in mobile */
-  .admin__sidebar {
-    @apply w-full lg:w-64 bg-white dark:bg-gray-800 shadow-2xl lg:shadow-none z-50 fixed lg:static inset-0 transform transition-transform duration-300 ease-in-out;
-  }
-
-  .admin__sidebar--hidden {
-    @apply -translate-x-full lg:translate-x-0;
-  }
-
-  .admin__sidebar--visible {
-    @apply translate-x-0;
-  }
-
-  .admin__sidebar-overlay {
-    @apply lg:hidden fixed inset-0 bg-black bg-opacity-50 z-40;
-  }
-
-  .admin__sidebar-header {
-    @apply lg:hidden flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800;
-  }
-
-  .admin__sidebar-title {
-    @apply text-lg font-medium text-gray-800 dark:text-white;
-  }
-
-  .admin__sidebar-close {
-    @apply lg:hidden p-2 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors duration-200;
-  }
-
-  .admin__nav {
-    @apply p-4 space-y-2 h-full overflow-y-auto;
-  }
-
-  .admin__nav-item {
-    @apply flex items-center space-x-3 px-3 sm:px-4 py-2 sm:py-3 rounded-lg transition-colors duration-200 text-sm sm:text-base;
-  }
-
-  .admin__nav-item--active {
-    @apply bg-primary-100 dark:bg-primary-900 text-primary-700 dark:text-primary-300;
-  }
-
-  .admin__nav-item--inactive {
-    @apply text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white;
-  }
-
-  .admin__nav-item-content {
-    @apply flex items-center space-x-3;
-  }
-
-  .admin__nav-icon {
-    @apply w-4 h-4 sm:w-5 sm:h-5 flex-shrink-0;
-  }
-
-  .admin__nav-text {
-    @apply font-medium truncate;
-  }
-
-  /* Main content */
-  .admin__main {
-    @apply flex-1 p-4 sm:p-6 lg:p-8 overflow-auto min-h-0;
-  }
-}
-
-/* Медиа-запросы для планшетов */
-@media (max-width: 1024px) {
-  .admin__sidebar {
-    @apply w-80;
-  }
-}
-
-/* Медиа-запросы для очень маленьких экранов */
-@media (max-width: 360px) {
-  .admin__header-content {
-    @apply px-3 py-2;
-  }
-  
-  .admin__header-title {
-    @apply text-base;
-  }
-  
-  .admin__nav {
-    @apply p-3;
-  }
-  
-  .admin__nav-item {
-    @apply px-2 py-2 text-xs;
-  }
-  
-  .admin__main {
-    @apply p-3;
-  }
-}
+// Admin panel styles
+.admin
+  display: flex
+  min-height: 100vh
+  background-color: $gray-50
+
+  .dark &
+    background-color: $gray-900
+
+.admin__sidebar
+  width: 16rem
+  background-color: $white
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)
+  height: 100vh
+  position: fixed
+  left: 0
+  top: 0
+  z-index: 40
+
+  .dark &
+    background-color: $gray-800
+
+.admin__sidebar-header
+  padding: 1.5rem
+  border-bottom: 1px solid $gray-200
+
+  .dark &
+    border-bottom-color: $gray-700
+
+.admin__sidebar-title
+  font-size: 1.25rem
+  font-weight: bold
+  color: $gray-800
+  margin-bottom: 0.5rem
+
+  .dark &
+    color: $white
+
+.admin__domain-info
+  font-size: 0.875rem
+  color: $gray-600
+  background-color: $gray-100
+  padding: 0.25rem 0.75rem
+  border-radius: 9999px
+  display: inline-block
+
+  .dark &
+    color: $gray-400
+    background-color: $gray-700
+
+.admin__nav
+  padding: 1rem
+  display: flex
+  flex-direction: column
+  gap: 0.5rem
+
+.admin__nav-item
+  display: flex
+  align-items: center
+  gap: 0.75rem
+  padding: 0.75rem 1rem
+  border-radius: 0.5rem
+  transition: all 0.2s ease-in-out
+  text-decoration: none
+  color: inherit
+  border: none
+  background: none
+  cursor: pointer
+  width: 100%
+  text-align: left
+
+  &:hover
+    background-color: $gray-100
+    color: $gray-900
+
+  .dark &
+    &:hover
+      background-color: $gray-700
+      color: $white
+
+.admin__nav-item--active
+  background-color: $primary-100
+  color: $primary-700
+
+  .dark &
+    background-color: $primary-900
+    color: $primary-300
+
+  &:hover
+    background-color: $primary-200
+    color: $primary-800
+
+    .dark &
+      background-color: $primary-800
+      color: $primary-200
+
+.admin__nav-item--inactive
+  color: $gray-600
+
+  .dark &
+    color: $gray-400
+
+.admin__nav-item-content
+  display: flex
+  align-items: center
+  gap: 0.75rem
+
+.admin__nav-icon
+  width: 1.25rem
+  height: 1.25rem
+
+.admin__nav-text
+  font-weight: 500
+
+.admin__content
+  flex: 1
+  margin-left: 16rem
+  padding: 1.5rem
+
+// Admin content area styles
+.admin-content
+  &__header
+    margin-bottom: 2rem
+
+  &__title
+    font-size: 1.875rem
+    font-weight: bold
+    color: $gray-900
+    margin-bottom: 0.5rem
+
+    .dark &
+      color: $white
+
+  &__subtitle
+    color: $gray-600
+    font-size: 1.125rem
+
+    .dark &
+      color: $gray-400
+
+  &__card
+    background-color: $white
+    border-radius: 0.5rem
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)
+    padding: 1.5rem
+
+    .dark &
+      background-color: $gray-800
+
+  &__section
+    margin-bottom: 2rem
+
+    &:last-child
+      margin-bottom: 0
+
+  &__section-title
+    font-size: 1.25rem
+    font-weight: 600
+    color: $gray-900
+    margin-bottom: 1rem
+
+    .dark &
+      color: $white
+
+  &__section-description
+    color: $gray-600
+    margin-bottom: 1.5rem
+
+    .dark &
+      color: $gray-400
+
+// Admin form styles
+.admin-form
+  &__group
+    margin-bottom: 1.5rem
+
+  &__label
+    display: block
+    font-size: 0.875rem
+    font-weight: 500
+    color: $gray-700
+    margin-bottom: 0.5rem
+
+    .dark &
+      color: $gray-300
+
+  &__input
+    width: 100%
+    padding: 0.5rem 0.75rem
+    border: 1px solid $gray-300
+    border-radius: 0.5rem
+    background-color: $white
+    color: $gray-900
+    font-size: 0.875rem
+    transition: all 0.2s ease-in-out
+
+    &:focus
+      outline: none
+      border-color: $primary-500
+      // Исправлено: используем функцию rgba с компонентами цвета
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+    .dark &
+      background-color: $gray-700
+      color: $white
+      border-color: $gray-600
+
+      &:focus
+        border-color: $primary-500
+        box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  &__textarea
+    @extend .admin-form__input
+    resize: vertical
+    min-height: 5rem
+
+  &__select
+    @extend .admin-form__input
+
+  &__checkbox
+    border-radius: 0.25rem
+    border: 1px solid $gray-300
+    background-color: $white
+    color: $primary-600
+
+    &:focus
+      border-color: $primary-500
+      box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+    .dark &
+      background-color: $gray-700
+      border-color: $gray-600
+
+// Admin button styles
+.admin-btn
+  padding: 0.5rem 1rem
+  border-radius: 0.5rem
+  font-weight: 500
+  font-size: 0.875rem
+  transition: all 0.2s ease-in-out
+  border: none
+  cursor: pointer
+  text-decoration: none
+  display: inline-flex
+  align-items: center
+  gap: 0.5rem
+
+  &:focus
+    outline: none
+    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2)
+
+  &--primary
+    background-color: $primary-600
+    color: $white
+
+    &:hover
+      background-color: $primary-700
+
+  &--secondary
+    background-color: $gray-200
+    color: $gray-700
+
+    &:hover
+      background-color: $gray-300
+
+    .dark &
+      background-color: $gray-700
+      color: $gray-300
+
+      &:hover
+        background-color: $gray-600
+
+  &--danger
+    background-color: #dc2626
+    color: $white
+
+    &:hover
+      background-color: #b91c1c
+
+  &--success
+    background-color: #16a34a
+    color: $white
+
+    &:hover
+      background-color: #15803d
+
+// Admin table styles
+.admin-table
+  width: 100%
+  border-collapse: collapse
+  background-color: $white
+
+  .dark &
+    background-color: $gray-800
+
+  &__header
+    background-color: $gray-50
+
+    .dark &
+      background-color: $gray-700
+
+  &__th
+    padding: 0.75rem 1rem
+    text-align: left
+    font-size: 0.75rem
+    font-weight: 500
+    color: $gray-500
+    text-transform: uppercase
+    letter-spacing: 0.05em
+    border-bottom: 1px solid $gray-200
+
+    .dark &
+      color: $gray-300
+      border-bottom-color: $gray-600
+
+  &__tr
+    &:hover
+      background-color: $gray-50
+
+      .dark &
+        background-color: $gray-700
+
+  &__td
+    padding: 0.75rem 1rem
+    border-bottom: 1px solid $gray-200
+    font-size: 0.875rem
+
+    .dark &
+      border-bottom-color: $gray-600
+
+// Admin modal styles
+.admin-modal
+  position: fixed
+  inset: 0
+  background-color: rgba(0, 0, 0, 0.5)
+  display: flex
+  align-items: center
+  justify-content: center
+  z-index: 50
+
+  &__content
+    background-color: $white
+    border-radius: 0.5rem
+    padding: 1.5rem
+    max-width: 32rem
+    width: 100%
+    margin: 0 1rem
+    max-height: 90vh
+    overflow-y: auto
+
+    .dark &
+      background-color: $gray-800
+
+  &__title
+    font-size: 1.125rem
+    font-weight: 500
+    color: $gray-900
+    margin-bottom: 1rem
+
+    .dark &
+      color: $white
+
+  &__actions
+    display: flex
+    gap: 0.75rem
+    justify-content: flex-end
+    margin-top: 1.5rem
+
+// Responsive adjustments for admin panel
+@media (max-width: 1024px)
+  .admin__sidebar
+    width: 14rem
+
+  .admin__content
+    margin-left: 14rem
+
+@media (max-width: 768px)
+  .admin
+    flex-direction: column
+
+  .admin__sidebar
+    position: static
+    width: 100%
+    height: auto
+
+  .admin__content
+    margin-left: 0
+    padding: 1rem
+
+  .admin-modal__content
+    margin: 0 0.5rem

+ 1 - 1
app/pages/Home/index.coffee

@@ -1,4 +1,4 @@
-document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss">'+stylFns['app/pages/Home/index.styl']+'</style>')
+document.head.insertAdjacentHTML('beforeend','<style type="text/css">'+stylFns['app/pages/Home/index.styl']+'</style>')
 
 module.exports =
   name: 'HomePage'

+ 403 - 61
app/pages/Home/index.styl

@@ -1,61 +1,403 @@
-@css {
-  .home-page {
-    @apply min-h-screen bg-gray-50 dark:bg-gray-800 py-8;
-  }
-
-  .home-page__hero {
-    @apply text-center py-16 px-4 bg-white dark:bg-gray-900 shadow-sm mb-8;
-  }
-
-  .home-page__title {
-    @apply text-4xl md:text-5xl font-bold text-gray-800 dark:text-white mb-4;
-  }
-
-  .home-page__subtitle {
-    @apply text-xl text-gray-600 dark:text-gray-300 mb-8;
-  }
-
-  .home-page__actions {
-    @apply flex flex-col sm:flex-row gap-4 justify-center items-center;
-  }
-
-  .home-page__btn {
-    @apply px-6 py-3 rounded-lg font-medium transition-colors duration-200;
-  }
-
-  .home-page__btn--primary {
-    @apply bg-primary-500 hover:bg-primary-600 text-white;
-  }
-
-  .home-page__btn--secondary {
-    @apply bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-white;
-  }
-
-  .home-page__content {
-    @apply container mx-auto px-4;
-  }
-
-  .home-page__section {
-    @apply mb-8;
-  }
-
-  .home-page__section-title {
-    @apply text-2xl font-bold text-gray-800 dark:text-white mb-6 text-center;
-  }
-
-  .home-page__categories {
-    @apply grid grid-cols-2 md:grid-cols-4 gap-4;
-  }
-
-  .home-page__category {
-    @apply bg-white dark:bg-gray-700 rounded-lg shadow-md p-6 text-center text-gray-700 dark:text-gray-300 font-medium hover:shadow-lg transition-shadow duration-200;
-  }
-
-  .home-page__info {
-    @apply text-center py-8 px-4 bg-primary-50 dark:bg-primary-900 mt-8;
-  }
-
-  .home-page__info-text {
-    @apply text-primary-700 dark:text-primary-300 text-lg;
-  }
-}
+// Home page specific styles
+.home-page
+  container()
+  padding-top: $spacing-8
+  padding-bottom: $spacing-12
+  flex-col()
+  gap: $spacing-12
+
+// Hero section
+.home-hero
+  text-align: center
+  padding: $spacing-16 0
+  background: linear-gradient(135deg, $white 0%, $gray-50 100%)
+  border-radius: $radius-2xl
+  margin: -$spacing-8 0
+  position: relative
+  overflow: hidden
+
+  .dark &
+    background: linear-gradient(135deg, $gray-800 0%, $gray-900 100%)
+
+  &::before
+    content: ''
+    position: absolute
+    top: 0
+    left: 0
+    right: 0
+    bottom: 0
+    background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%239C92AC' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")
+    opacity: 0.3
+
+.home-hero__content
+  position: relative
+  z-index: 2
+  max-width: 48rem
+  margin: 0 auto
+  padding: 0 $spacing-6
+
+.home-hero__title
+  font-size: $font-size-5xl
+  font-weight: $font-weight-extrabold
+  line-height: 1.1
+  color: $gray-900
+  margin-bottom: $spacing-6
+  letter-spacing: $letter-spacing-tight
+
+  .dark &
+    color: $white
+
+  @media (max-width: $breakpoint-sm)
+    font-size: $font-size-4xl
+
+.home-hero__subtitle
+  font-size: $font-size-xl
+  font-weight: $font-weight-normal
+  line-height: $line-height-relaxed
+  color: $gray-600
+  margin-bottom: $spacing-8
+  max-width: 36rem
+  margin-left: auto
+  margin-right: auto
+
+  .dark &
+    color: $gray-400
+
+.home-hero__actions
+  display: flex
+  gap: $spacing-4
+  justify-content: center
+  flex-wrap: wrap
+
+// Features section
+.home-features
+  padding: $spacing-12 0
+
+.home-features__header
+  text-align: center
+  margin-bottom: $spacing-12
+
+.home-features__title
+  font-size: $font-size-4xl
+  font-weight: $font-weight-extrabold
+  color: $gray-900
+  margin-bottom: $spacing-4
+  letter-spacing: $letter-spacing-tight
+
+  .dark &
+    color: $white
+
+.home-features__description
+  font-size: $font-size-lg
+  color: $gray-600
+  max-width: 36rem
+  margin: 0 auto
+  line-height: $line-height-relaxed
+
+  .dark &
+    color: $gray-400
+
+.home-features__grid
+  display: grid
+  grid-template-columns: 1fr
+  gap: $spacing-8
+
+  @media (min-width: $breakpoint-md)
+    grid-template-columns: repeat(2, 1fr)
+
+  @media (min-width: $breakpoint-lg)
+    grid-template-columns: repeat(3, 1fr)
+
+.home-feature
+  background: $white
+  padding: $spacing-8
+  border-radius: $radius-xl
+  box-shadow: $shadow-base
+  border: 1px solid $gray-200
+  text-align: center
+  transition: all 0.3s ease-in-out
+  position: relative
+  overflow: hidden
+
+  .dark &
+    background: $gray-800
+    border-color: $gray-700
+
+  &:hover
+    transform: translateY(-4px)
+    box-shadow: $shadow-xl
+
+    .home-feature__icon
+      transform: scale(1.1)
+
+.home-feature__icon
+  width: 4rem
+  height: 4rem
+  margin: 0 auto $spacing-4
+  background: linear-gradient(135deg, $primary-500, $accent-500)
+  border-radius: $radius-lg
+  flex-center()
+  transition: transform 0.3s ease-in-out
+
+  svg
+    width: 2rem
+    height: 2rem
+    color: $white
+
+.home-feature__title
+  font-size: $font-size-xl
+  font-weight: $font-weight-semibold
+  color: $gray-900
+  margin-bottom: $spacing-3
+  line-height: $line-height-tight
+
+  .dark &
+    color: $white
+
+.home-feature__description
+  font-size: $font-size-base
+  color: $gray-600
+  line-height: $line-height-relaxed
+
+  .dark &
+    color: $gray-400
+
+// Categories section
+.home-categories
+  padding: $spacing-12 0
+
+.home-categories__header
+  text-align: center
+  margin-bottom: $spacing-8
+
+.home-categories__title
+  font-size: $font-size-4xl
+  font-weight: $font-weight-extrabold
+  color: $gray-900
+  margin-bottom: $spacing-4
+  letter-spacing: $letter-spacing-tight
+
+  .dark &
+    color: $white
+
+.home-categories__description
+  font-size: $font-size-lg
+  color: $gray-600
+  max-width: 36rem
+  margin: 0 auto
+
+  .dark &
+    color: $gray-400
+
+.home-categories__grid
+  display: grid
+  grid-template-columns: 1fr
+  gap: $spacing-6
+
+  @media (min-width: $breakpoint-sm)
+    grid-template-columns: repeat(2, 1fr)
+
+  @media (min-width: $breakpoint-lg)
+    grid-template-columns: repeat(4, 1fr)
+
+.home-category
+  background: $white
+  border-radius: $radius-xl
+  overflow: hidden
+  box-shadow: $shadow-base
+  border: 1px solid $gray-200
+  transition: all 0.3s ease-in-out
+  position: relative
+
+  .dark &
+    background: $gray-800
+    border-color: $gray-700
+
+  &:hover
+    transform: translateY(-3px)
+    box-shadow: $shadow-lg
+
+    .home-category__image
+      transform: scale(1.05)
+
+.home-category__image-container
+  width: 100%
+  height: 12rem
+  overflow: hidden
+  position: relative
+
+.home-category__image
+  width: 100%
+  height: 100%
+  object-fit: cover
+  transition: transform 0.3s ease-in-out
+
+.home-category__content
+  padding: $spacing-6
+
+.home-category__name
+  font-size: $font-size-lg
+  font-weight: $font-weight-semibold
+  color: $gray-900
+  margin-bottom: $spacing-2
+  line-height: $line-height-tight
+
+  .dark &
+    color: $white
+
+.home-category__count
+  font-size: $font-size-sm
+  color: $gray-500
+  font-weight: $font-weight-medium
+
+  .dark &
+    color: $gray-400
+
+// CTA section
+.home-cta
+  background: linear-gradient(135deg, $primary-600, $accent-600)
+  color: $white
+  padding: $spacing-16 $spacing-6
+  text-align: center
+  border-radius: $radius-2xl
+  position: relative
+  overflow: hidden
+
+  &::before
+    content: ''
+    position: absolute
+    top: 0
+    left: 0
+    right: 0
+    bottom: 0
+    background: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-opacity='0.1'%3E%3Cpath d='M0 0h20L0 20z'/%3E%3C/g%3E%3C/svg%3E")
+
+.home-cta__content
+  position: relative
+  z-index: 2
+  max-width: 48rem
+  margin: 0 auto
+
+.home-cta__title
+  font-size: $font-size-4xl
+  font-weight: $font-weight-extrabold
+  line-height: 1.1
+  margin-bottom: $spacing-4
+  letter-spacing: $letter-spacing-tight
+
+  @media (max-width: $breakpoint-sm)
+    font-size: $font-size-3xl
+
+.home-cta__description
+  font-size: $font-size-xl
+  line-height: $line-height-relaxed
+  margin-bottom: $spacing-8
+  opacity: 0.9
+  max-width: 36rem
+  margin-left: auto
+  margin-right: auto
+
+.home-cta__button
+  background-color: $white
+  color: $primary-600
+  padding: $spacing-4 $spacing-8
+  border-radius: $radius-lg
+  font-size: $font-size-lg
+  font-weight: $font-weight-semibold
+  text-decoration: none
+  display: inline-block
+  transition: all 0.3s ease-in-out
+  box-shadow: $shadow-lg
+
+  &:hover
+    background-color: $gray-100
+    transform: translateY(-2px)
+    box-shadow: $shadow-xl
+
+// Stats section
+.home-stats
+  padding: $spacing-12 0
+
+.home-stats__grid
+  display: grid
+  grid-template-columns: repeat(2, 1fr)
+  gap: $spacing-8
+
+  @media (min-width: $breakpoint-md)
+    grid-template-columns: repeat(4, 1fr)
+
+.home-stat
+  text-align: center
+  padding: $spacing-6
+
+.home-stat__number
+  font-size: $font-size-4xl
+  font-weight: $font-weight-extrabold
+  color: $primary-600
+  line-height: 1
+  margin-bottom: $spacing-2
+
+  .dark &
+    color: $primary-400
+
+.home-stat__label
+  font-size: $font-size-sm
+  font-weight: $font-weight-medium
+  color: $gray-600
+  text-transform: uppercase
+  letter-spacing: $letter-spacing-wide
+
+  .dark &
+    color: $gray-400
+
+// Responsive adjustments
+@media (max-width: $breakpoint-md)
+  .home-hero
+    padding: $spacing-12 0
+    margin: -$spacing-6 0
+
+  .home-hero__title
+    font-size: $font-size-4xl
+
+  .home-hero__subtitle
+    font-size: $font-size-lg
+
+  .home-features,
+  .home-categories,
+  .home-stats
+    padding: $spacing-8 0
+
+  .home-features__title,
+  .home-categories__title
+    font-size: $font-size-3xl
+
+  .home-cta
+    padding: $spacing-12 $spacing-4
+
+  .home-cta__title
+    font-size: $font-size-3xl
+
+  .home-cta__description
+    font-size: $font-size-lg
+
+@media (max-width: $breakpoint-sm)
+  .home-hero__title
+    font-size: $font-size-3xl
+
+  .home-hero__actions
+    flex-direction: column
+    align-items: center
+
+    .btn
+      width: 100%
+      max-width: 20rem
+
+  .home-features__grid
+    grid-template-columns: 1fr
+
+  .home-categories__grid
+    grid-template-columns: 1fr
+
+  .home-stats__grid
+    grid-template-columns: 1fr
+    gap: $spacing-6

+ 0 - 94
app/theme.config.coffee

@@ -1,94 +0,0 @@
-module.exports = {
-  version: '1.0.0',
-  
-  colors: {
-    primary: {
-      50: '#fef2f2', 100: '#fee2e2', 200: '#fecaca', 300: '#fca5a5',
-      400: '#f87171', 500: '#ef4444', 600: '#dc2626', 700: '#b91c1c',
-      800: '#991b1b', 900: '#7f1d1d'
-    },
-    accent: {
-      50: '#f0f9ff', 100: '#e0f2fe', 200: '#bae6fd', 300: '#7dd3fc',
-      400: '#38bdf8', 500: '#0ea5e9', 600: '#0284c7', 700: '#0369a1',
-      800: '#075985', 900: '#0c4a6e'
-    },
-    gray: {
-      50: '#f9fafb', 100: '#f3f4f6', 200: '#e5e7eb', 300: '#d1d5db',
-      400: '#9ca3af', 500: '#6b7280', 600: '#4b5563', 700: '#374151',
-      800: '#1f2937', 900: '#111827'
-    }
-  },
-  
-  
-  
-  
-  typography: {
-    fonts: {
-      sans: ['Inter', 'ui-sans-serif', 'system-ui', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'Noto Sans', 'sans-serif'],
-      serif: ['ui-serif', 'Georgia', 'Cambria', 'Times New Roman', 'Times', 'serif'],
-      mono: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', 'monospace']
-    },
-    sizes: {
-      xs: '0.75rem', 
-      sm: '0.875rem', 
-      base: '1rem', 
-      lg: '1.125rem',
-      xl: '1.25rem', 
-      '2xl': '1.5rem', 
-      '3xl': '1.875rem',
-      '4xl': '2.25rem',
-      '5xl': '3rem'
-    },
-    weights: {
-      light: 300,
-      normal: 400,
-      medium: 500,
-      semibold: 600,
-      bold: 700
-    }
-  },
-  
-  breakpoints: {
-    sm: '640px',
-    md: '768px', 
-    lg: '1024px',
-    xl: '1280px',
-    '2xl': '1536px'
-  },
-  
-  spacing: {
-    0: '0px',
-    1: '0.25rem',
-    2: '0.5rem',
-    3: '0.75rem', 
-    4: '1rem',
-    5: '1.25rem',
-    6: '1.5rem',
-    8: '2rem',
-    10: '2.5rem',
-    12: '3rem',
-    16: '4rem',
-    20: '5rem',
-    24: '6rem',
-    32: '8rem'
-  },
-  
-  borderRadius: {
-    none: '0px',
-    sm: '0.125rem',
-    default: '0.25rem',
-    md: '0.375rem',
-    lg: '0.5rem',
-    xl: '0.75rem',
-    '2xl': '1rem',
-    full: '9999px'
-  },
-  
-  shadows: {
-    sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
-    default: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
-    md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
-    lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
-    xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)'
-  }
-}

+ 0 - 21
tailwind.config.js

@@ -1,21 +0,0 @@
-const themeConfig = require('./app/theme.config.coffee');
-
-module.exports = {
-  content: [
-    "./app/**/*.{pug,coffee,styl}",
-    "./src/**/*.{html,js,vue}"
-  ],
-  darkMode: 'class',
-  theme: {
-    extend: {
-      colors: themeConfig.colors,
-      fontFamily: themeConfig.typography.fonts,
-      fontSize: themeConfig.typography.sizes,
-      fontWeight: themeConfig.typography.weights,
-      spacing: themeConfig.spacing,
-      borderRadius: themeConfig.borderRadius,
-      boxShadow: themeConfig.shadows
-    },
-  },
-  plugins: [],
-}