|
@@ -16,80 +16,14 @@ document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" p
|
|
|
## базовой стиль приложения
|
|
## базовой стиль приложения
|
|
|
document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" page="root">'+stylFns['app/temp.styl']+'</style>')
|
|
document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" page="root">'+stylFns['app/temp.styl']+'</style>')
|
|
|
|
|
|
|
|
-# Создаем глобальную шину событий
|
|
|
|
|
-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
|
|
|
|
|
|
|
+# Подключаем ядро системы
|
|
|
|
|
+CouchDBService = require 'app/core/CouchdbClass'
|
|
|
|
|
|
|
|
# Маршруты
|
|
# Маршруты
|
|
|
routes = [
|
|
routes = [
|
|
|
{ path: '/', component: require 'app/pages/Home' }
|
|
{ path: '/', component: require 'app/pages/Home' }
|
|
|
{ path: '/events', component: require 'app/pages/Events' }
|
|
{ path: '/events', component: require 'app/pages/Events' }
|
|
|
|
|
+ { path: '/events/:id', component: require 'app/pages/EventDetail' }
|
|
|
{ path: '/about', component: require 'app/pages/About' }
|
|
{ path: '/about', component: require 'app/pages/About' }
|
|
|
{ path: '/contacts', component: require 'app/pages/Contacts' }
|
|
{ path: '/contacts', component: require 'app/pages/Contacts' }
|
|
|
]
|
|
]
|
|
@@ -107,8 +41,9 @@ app = Vue.createApp
|
|
|
loading: true
|
|
loading: true
|
|
|
error: null
|
|
error: null
|
|
|
modalState:
|
|
modalState:
|
|
|
- currentModal: null
|
|
|
|
|
- modalProps: {}
|
|
|
|
|
|
|
+ isVisible: false
|
|
|
|
|
+ component: null
|
|
|
|
|
+ props: {}
|
|
|
couchDBService: new CouchDBService()
|
|
couchDBService: new CouchDBService()
|
|
|
beforeMount: ()->
|
|
beforeMount: ()->
|
|
|
debug.log "start beforeMount"
|
|
debug.log "start beforeMount"
|
|
@@ -125,131 +60,58 @@ app = Vue.createApp
|
|
|
document.documentElement.classList.remove('dark')
|
|
document.documentElement.classList.remove('dark')
|
|
|
|
|
|
|
|
# Загрузка данных из CouchDB
|
|
# Загрузка данных из CouchDB
|
|
|
- await @loadEventsData()
|
|
|
|
|
-
|
|
|
|
|
- # Обработчик открытия модальных окон - ДОЛЖЕН БЫТЬ ЗДЕСЬ
|
|
|
|
|
- EventBus.on 'open-modal', (config) =>
|
|
|
|
|
- debug.log "Opening modal: "+config.component
|
|
|
|
|
- @modalState.currentModal = config.component
|
|
|
|
|
- @modalState.modalProps = config.props || {}
|
|
|
|
|
|
|
+ @loadEventsData()
|
|
|
methods:
|
|
methods:
|
|
|
toggleTheme: ->
|
|
toggleTheme: ->
|
|
|
@theme = if @theme == 'light' then 'dark' else 'light'
|
|
@theme = if @theme == 'light' then 'dark' else 'light'
|
|
|
localStorage.setItem 'theme', @theme
|
|
localStorage.setItem 'theme', @theme
|
|
|
document.documentElement.classList.toggle 'dark'
|
|
document.documentElement.classList.toggle 'dark'
|
|
|
- handleTicketBooking: (event) ->
|
|
|
|
|
- debug.log "Обработка покупки билета на: "+event.title
|
|
|
|
|
- openModal('SuccessModal', {
|
|
|
|
|
- title: 'Билет забронирован!'
|
|
|
|
|
- content: "Вы успешно забронировали билет на \""+event.title+"\". Подробности отправлены на вашу почту."
|
|
|
|
|
- })
|
|
|
|
|
|
|
+
|
|
|
|
|
+ openModal: (component, props = {}) ->
|
|
|
|
|
+ @modalState.component = component
|
|
|
|
|
+ @modalState.props = props
|
|
|
|
|
+ @modalState.isVisible = true
|
|
|
|
|
+
|
|
|
|
|
+ closeModal: ->
|
|
|
|
|
+ @modalState.isVisible = false
|
|
|
|
|
+ @modalState.component = null
|
|
|
|
|
+ @modalState.props = {}
|
|
|
|
|
+
|
|
|
loadEventsData: ->
|
|
loadEventsData: ->
|
|
|
@appState.loading = true
|
|
@appState.loading = true
|
|
|
- try
|
|
|
|
|
- [events, featuredEvents, sliderEvents] = await Promise.all([
|
|
|
|
|
- @couchDBService.getAllEvents()
|
|
|
|
|
- @couchDBService.getFeaturedEvents()
|
|
|
|
|
- @couchDBService.getSliderEvents()
|
|
|
|
|
- ])
|
|
|
|
|
-
|
|
|
|
|
|
|
+ @couchDBService.getAllEvents()
|
|
|
|
|
+ .then (events) =>
|
|
|
@appState.events = events
|
|
@appState.events = events
|
|
|
- @appState.featuredEvents = featuredEvents
|
|
|
|
|
- @appState.sliderEvents = sliderEvents
|
|
|
|
|
|
|
+ @appState.featuredEvents = events.filter((event) -> event.isFeatured).slice(0, 6)
|
|
|
|
|
+ @appState.sliderEvents = events.filter((event) -> event.inSlider).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
|
|
|
@appState.error = null
|
|
@appState.error = null
|
|
|
-
|
|
|
|
|
- catch error
|
|
|
|
|
|
|
+ .catch (error) =>
|
|
|
debug.log "Ошибка загрузки данных: "+error
|
|
debug.log "Ошибка загрузки данных: "+error
|
|
|
@appState.error = 'Не удалось загрузить данные мероприятий'
|
|
@appState.error = 'Не удалось загрузить данные мероприятий'
|
|
|
- @loadTestData()
|
|
|
|
|
- finally
|
|
|
|
|
|
|
+ .finally =>
|
|
|
@appState.loading = false
|
|
@appState.loading = false
|
|
|
|
|
|
|
|
- loadTestData: ->
|
|
|
|
|
- # Тестовые данные на случай недоступности CouchDB
|
|
|
|
|
- @appState.events = [
|
|
|
|
|
- {
|
|
|
|
|
- _id: '1'
|
|
|
|
|
- title: 'Концерт симфонического оркестра'
|
|
|
|
|
- date: '2020-04-18'
|
|
|
|
|
- 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
|
|
getEvents: -> @appState.events
|
|
|
getFeaturedEvents: -> @appState.featuredEvents
|
|
getFeaturedEvents: -> @appState.featuredEvents
|
|
|
getSliderEvents: -> @appState.sliderEvents
|
|
getSliderEvents: -> @appState.sliderEvents
|
|
|
isLoading: -> @appState.loading
|
|
isLoading: -> @appState.loading
|
|
|
hasError: -> @appState.error
|
|
hasError: -> @appState.error
|
|
|
-
|
|
|
|
|
- closeModal: ->
|
|
|
|
|
- @modalState.currentModal = null
|
|
|
|
|
- @modalState.modalProps = {}
|
|
|
|
|
components:
|
|
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'
|
|
|
|
|
|
|
+ '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'
|
|
'eventdetailmodal': require 'app/shared/EventDetailModal'
|
|
|
- 'successmodal': require 'app/shared/SuccessModal'
|
|
|
|
|
- 'applink': require 'app/shared/AppLink'
|
|
|
|
|
|
|
+ 'successmodal': require 'app/shared/SuccessModal'
|
|
|
|
|
+ 'app-link': require 'app/shared/AppLink'
|
|
|
|
|
|
|
|
app.use(VueRouter.createRouter({
|
|
app.use(VueRouter.createRouter({
|
|
|
routes: routes
|
|
routes: routes
|
|
@@ -261,13 +123,6 @@ app.use(VueRouter.createRouter({
|
|
|
return { x: 0, y: 0 }
|
|
return { x: 0, y: 0 }
|
|
|
}))
|
|
}))
|
|
|
|
|
|
|
|
-# Глобальные функции для работы с модальными окнами
|
|
|
|
|
-globalThis.openModal = (component, props = {}) ->
|
|
|
|
|
- EventBus.emit 'open-modal', { component, props }
|
|
|
|
|
-
|
|
|
|
|
-globalThis.closeModal = ->
|
|
|
|
|
- EventBus.emit 'close-modal'
|
|
|
|
|
-
|
|
|
|
|
# подключаем в body ОБЯЗАТЕЛЬНО!!!
|
|
# подключаем в body ОБЯЗАТЕЛЬНО!!!
|
|
|
app.mount('body')
|
|
app.mount('body')
|
|
|
|
|
|