|
|
@@ -1,52 +1,246 @@
|
|
|
+# обязательно подключение глобальных массивов
|
|
|
globalThis.renderFns = require 'pug.json'
|
|
|
globalThis.stylFns = require 'styl.json'
|
|
|
|
|
|
-debug.log "000"
|
|
|
+# подключение мета информации (строго в данном фиде)
|
|
|
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','<title>Кохи Борбад - Концертный зал Душанбе</title>')
|
|
|
|
|
|
-document.head.insertAdjacentHTML('beforeend','<style>'+stylFns['main.css']+'</style>')
|
|
|
+# Настройка tailwind
|
|
|
+tailwind.config = require 'tailwind.config.js'
|
|
|
+
|
|
|
+# подключение основных стилей
|
|
|
+## tailwind
|
|
|
+document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" page="main">'+stylFns['main.css']+'</style>')
|
|
|
+## базовой стиль приложения
|
|
|
document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" page="root">'+stylFns['app/temp.styl']+'</style>')
|
|
|
|
|
|
-document.head.insertAdjacentHTML('beforeend','<title> Кохи Борбад - Концертный зал Душанбе</title>')
|
|
|
+# CouchDB сервис
|
|
|
+class CouchDBService
|
|
|
+ constructor: ->
|
|
|
+ @baseUrl = 'https://couchdb.favt.ru.net'
|
|
|
+ @dbName = 'kohi_borbad_events'
|
|
|
+ @headers =
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ 'Authorization': 'Basic ' + btoa('oleg:631074')
|
|
|
+
|
|
|
+ getAllEvents: ->
|
|
|
+ try
|
|
|
+ response = await fetch("#{@baseUrl}/#{@dbName}/_all_docs?include_docs=true", {
|
|
|
+ method: 'GET'
|
|
|
+ headers: @headers
|
|
|
+ })
|
|
|
+
|
|
|
+ if response.ok
|
|
|
+ data = await response.json()
|
|
|
+ events = data.rows.map (row) -> row.doc
|
|
|
+ return events.filter (event) -> !event._id.startsWith('_design/')
|
|
|
+ else
|
|
|
+ debug.log "Ошибка получения мероприятий: "+response.statusText
|
|
|
+ return []
|
|
|
+ catch error
|
|
|
+ debug.log "Ошибка подключения к CouchDB: "+error
|
|
|
+ return []
|
|
|
+
|
|
|
+ getFeaturedEvents: ->
|
|
|
+ events = await @getAllEvents()
|
|
|
+ events
|
|
|
+ .filter (event) -> event.isFeatured || false
|
|
|
+ .slice(0, 6)
|
|
|
+
|
|
|
+ getSliderEvents: ->
|
|
|
+ events = await @getAllEvents()
|
|
|
+ events
|
|
|
+ .filter (event) -> event.inSlider || false
|
|
|
+ .map (event) ->
|
|
|
+ id: event._id
|
|
|
+ image: event.image || '/images/default-event.jpg'
|
|
|
+ title: event.title
|
|
|
+ description: event.shortDescription || event.description
|
|
|
+ cta: event.cta || 'Подробнее'
|
|
|
+ category: event.category
|
|
|
|
|
|
-debug.log "001"
|
|
|
# Маршруты
|
|
|
routes = [
|
|
|
- { path: '/', component: require 'app/pages/Home' }
|
|
|
- { path: '/events', component: require 'app/pages/Events' }
|
|
|
- { path: '/about', component: require 'app/pages/About' }
|
|
|
- #{ path: '/contacts', component: require 'app/pages/Contacts' }
|
|
|
+ { path: '/', component: require 'app/pages/Home' }
|
|
|
+ { path: '/events', component: require 'app/pages/Events' }
|
|
|
+ { path: '/about', component: require 'app/pages/About' }
|
|
|
+ { path: '/contacts', component: require 'app/pages/Contacts' }
|
|
|
]
|
|
|
-tailwind.config = require 'tailwind.config.js'
|
|
|
|
|
|
-debug.log "002"
|
|
|
-# Глобальное состояние темы
|
|
|
+# Глобальное определение vuejs приложения
|
|
|
app = Vue.createApp
|
|
|
- name: 'app'
|
|
|
- data: ()->
|
|
|
- return {}
|
|
|
- beforeMount: ()->
|
|
|
+ name: 'app'
|
|
|
+ data: ()->
|
|
|
+ return
|
|
|
+ theme: 'light'
|
|
|
+ appState:
|
|
|
+ events: []
|
|
|
+ featuredEvents: []
|
|
|
+ sliderEvents: []
|
|
|
+ loading: true
|
|
|
+ error: null
|
|
|
+ modalState:
|
|
|
+ currentModal: null
|
|
|
+ modalProps: {}
|
|
|
+ couchDBService: new CouchDBService()
|
|
|
+ beforeMount: ()->
|
|
|
debug.log "start beforeMount"
|
|
|
+ # определение контекста vuejs приложения как глобальной переменной _
|
|
|
globalThis._ = @
|
|
|
- render: (new Function '_ctx', '_cache', renderFns['app/temp.pug'])()
|
|
|
- mounted: ->{}
|
|
|
- methods: {}
|
|
|
- components:
|
|
|
- 'themetoggle': require 'app/shared/ThemeToggle'
|
|
|
- 'multilevelmenu': require 'app/shared/MultiLevelMenu'
|
|
|
- 'imageslider': require 'app/shared/ImageSlider'
|
|
|
+ render: (new Function '_ctx', '_cache', renderFns['app/temp.pug'])()
|
|
|
+ mounted: ->
|
|
|
+ # Предзагрузка темы
|
|
|
+ if localStorage.theme == 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
|
+ @theme = 'dark'
|
|
|
+ document.documentElement.classList.add('dark')
|
|
|
+ else
|
|
|
+ @theme = 'light'
|
|
|
+ document.documentElement.classList.remove('dark')
|
|
|
+
|
|
|
+ # Загрузка данных из CouchDB
|
|
|
+ await @loadEventsData()
|
|
|
+
|
|
|
+ # Обработчик открытия модальных окон
|
|
|
+ EventBus.$on 'open-modal', (config) =>
|
|
|
+ @modalState.currentModal = config.component
|
|
|
+ @modalState.modalProps = config.props || {}
|
|
|
+ methods:
|
|
|
+ toggleTheme: ->
|
|
|
+ @theme = if @theme == 'light' then 'dark' else 'light'
|
|
|
+ localStorage.setItem 'theme', @theme
|
|
|
+ document.documentElement.classList.toggle 'dark'
|
|
|
+ @$emit 'theme-changed', @theme
|
|
|
+
|
|
|
+ loadEventsData: ->
|
|
|
+ @appState.loading = true
|
|
|
+ try
|
|
|
+ [events, featuredEvents, sliderEvents] = await Promise.all([
|
|
|
+ @couchDBService.getAllEvents()
|
|
|
+ @couchDBService.getFeaturedEvents()
|
|
|
+ @couchDBService.getSliderEvents()
|
|
|
+ ])
|
|
|
+
|
|
|
+ @appState.events = events
|
|
|
+ @appState.featuredEvents = featuredEvents
|
|
|
+ @appState.sliderEvents = sliderEvents
|
|
|
+ @appState.error = null
|
|
|
+
|
|
|
+ catch error
|
|
|
+ debug.log "Ошибка загрузки данных: "+error
|
|
|
+ @appState.error = 'Не удалось загрузить данные мероприятий'
|
|
|
+ @loadTestData()
|
|
|
+ finally
|
|
|
+ @appState.loading = false
|
|
|
+
|
|
|
+ loadTestData: ->
|
|
|
+ # Тестовые данные на случай недоступности CouchDB
|
|
|
+ @appState.events = [
|
|
|
+ {
|
|
|
+ _id: '1'
|
|
|
+ title: 'Концерт симфонического оркестра'
|
|
|
+ date: '2025-10-15'
|
|
|
+ time: '19:00'
|
|
|
+ description: 'Произведения Чайковского и Рахманинова в исполнении Национального симфонического оркестра'
|
|
|
+ image: '/images/event-classical.jpg'
|
|
|
+ category: 'classical'
|
|
|
+ price: 50
|
|
|
+ venue: 'Большой зал'
|
|
|
+ duration: '2 часа 30 минут'
|
|
|
+ ageRestriction: '12+'
|
|
|
+ availableTickets: 45
|
|
|
+ isFeatured: true
|
|
|
+ inSlider: true
|
|
|
+ shortDescription: 'Шедевры классической музыки'
|
|
|
+ cta: 'Купить билеты'
|
|
|
+ }
|
|
|
+ {
|
|
|
+ _id: '2'
|
|
|
+ title: 'Вечер таджикской народной музыки'
|
|
|
+ date: '2025-10-20'
|
|
|
+ time: '18:30'
|
|
|
+ description: 'Выступление фольклорного ансамбля "Шашмаком" с программой традиционных мелодий и танцев'
|
|
|
+ image: '/images/event-folk.jpg'
|
|
|
+ category: 'folk'
|
|
|
+ price: 30
|
|
|
+ venue: 'Малый зал'
|
|
|
+ duration: '2 часа'
|
|
|
+ ageRestriction: '6+'
|
|
|
+ availableTickets: 28
|
|
|
+ isFeatured: true
|
|
|
+ inSlider: true
|
|
|
+ shortDescription: 'Традиционные мелодии и танцы Таджикистана'
|
|
|
+ cta: 'Узнать больше'
|
|
|
+ }
|
|
|
+ {
|
|
|
+ _id: '3'
|
|
|
+ title: 'Джазовый фестиваль "Borbad Jazz"'
|
|
|
+ date: '2025-10-25'
|
|
|
+ time: '20:00'
|
|
|
+ description: 'Международные джазовые коллективы из Европы и Азии в уникальной акустике зала'
|
|
|
+ image: '/images/event-jazz.jpg'
|
|
|
+ category: 'jazz'
|
|
|
+ price: 70
|
|
|
+ venue: 'Большой зал'
|
|
|
+ duration: '3 часа'
|
|
|
+ ageRestriction: '16+'
|
|
|
+ availableTickets: 15
|
|
|
+ isFeatured: true
|
|
|
+ inSlider: true
|
|
|
+ shortDescription: 'Международные джазовые коллективы'
|
|
|
+ cta: 'Смотреть расписание'
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ @appState.featuredEvents = @appState.events.filter((event) -> event.isFeatured).slice(0, 6)
|
|
|
+ @appState.sliderEvents = @appState.events.filter((event) -> event.inSlider).map (event) ->
|
|
|
+ id: event._id
|
|
|
+ image: event.image
|
|
|
+ title: event.title
|
|
|
+ description: event.shortDescription
|
|
|
+ cta: event.cta
|
|
|
+ category: event.category
|
|
|
+
|
|
|
+ getEvents: -> @appState.events
|
|
|
+ getFeaturedEvents: -> @appState.featuredEvents
|
|
|
+ getSliderEvents: -> @appState.sliderEvents
|
|
|
+ isLoading: -> @appState.loading
|
|
|
+ hasError: -> @appState.error
|
|
|
+
|
|
|
+ closeModal: ->
|
|
|
+ @modalState.currentModal = null
|
|
|
+ @modalState.modalProps = {}
|
|
|
+ components:
|
|
|
+ 'themetoggle': require 'app/shared/ThemeToggle'
|
|
|
+ 'multilevelmenu': require 'app/shared/MultiLevelMenu'
|
|
|
+ 'imageslider': require 'app/shared/ImageSlider'
|
|
|
+ 'modalwindow': require 'app/shared/ModalWindow'
|
|
|
+ 'formvalidator': require 'app/shared/FormValidator'
|
|
|
+ 'filtersort': require 'app/shared/FilterSort'
|
|
|
+ 'eventdetailmodal': require 'app/shared/EventDetailModal'
|
|
|
+ 'successmodal': require 'app/shared/SuccessModal'
|
|
|
+
|
|
|
+# Создаем глобальную шину событий
|
|
|
+globalThis.EventBus = new Vue()
|
|
|
+
|
|
|
app.use(VueRouter.createRouter({
|
|
|
- routes: routes
|
|
|
- history: VueRouter.createWebHistory()
|
|
|
- scrollBehavior: (to, from, savedPosition) ->
|
|
|
- if savedPosition
|
|
|
- return savedPosition
|
|
|
- else
|
|
|
- return { x: 0, y: 0 }
|
|
|
+ routes: routes
|
|
|
+ history: VueRouter.createWebHistory()
|
|
|
+ scrollBehavior: (to, from, savedPosition) ->
|
|
|
+ if savedPosition
|
|
|
+ return savedPosition
|
|
|
+ else
|
|
|
+ return { x: 0, y: 0 }
|
|
|
}))
|
|
|
|
|
|
+# Глобальные функции для работы с модальными окнами
|
|
|
+globalThis.openModal = (component, props = {}) ->
|
|
|
+ EventBus.$emit 'open-modal', { component, props }
|
|
|
|
|
|
-app.mount('body')
|
|
|
+globalThis.closeModal = ->
|
|
|
+ EventBus.$emit 'close-modal'
|
|
|
|
|
|
+# подключаем в body ОБЯЗАТЕЛЬНО!!!
|
|
|
+app.mount('body')
|
|
|
|
|
|
+debug.log "Vue application initialized successfully"
|