# обязательно подключение глобальных массивов globalThis.renderFns = require 'pug.json' globalThis.stylFns = require 'styl.json' # подключение мета информации (строго в данном фиде) document.head.insertAdjacentHTML 'beforeend','' document.head.insertAdjacentHTML 'beforeend','' document.head.insertAdjacentHTML('beforeend','Кохи Борбад - Концертный зал Душанбе') # Настройка tailwind tailwind.config = require 'tailwind.config.js' # подключение основных стилей ## tailwind document.head.insertAdjacentHTML('beforeend','') ## базовой стиль приложения document.head.insertAdjacentHTML('beforeend','') # Создаем глобальную шину событий class AppEventBus constructor: -> @events = {} on: (event, callback) -> if !@events[event] @events[event] = [] @events[event].push(callback) emit: (event, data) -> if @events[event] for callback in @events[event] try callback(data) catch error debug.log "Event bus error: " + error off: (event, callback) -> if @events[event] @events[event] = @events[event].filter (cb) -> cb != callback # Создаем глобально globalThis.EventBus = new AppEventBus() # CouchDB сервис class CouchDBService constructor: -> @baseUrl = 'http://localhost:5984' @dbName = 'kohi_borbad_events' @headers = 'Content-Type': 'application/json' 'Authorization': 'Basic ' + btoa('admin:password') 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 # Маршруты 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' } ] # Глобальное определение vuejs приложения app = Vue.createApp 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: -> # Предзагрузка темы 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' 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' app.use(VueRouter.createRouter({ 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 } globalThis.closeModal = -> EventBus.emit 'close-modal' # подключаем в body ОБЯЗАТЕЛЬНО!!! app.mount('body') debug.log "Vue application initialized successfully"