temp.coffee 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. # обязательно подключение глобальных массивов
  2. globalThis.renderFns = require 'pug.json'
  3. globalThis.stylFns = require 'styl.json'
  4. # подключение мета информации (строго в данном фиде)
  5. document.head.insertAdjacentHTML 'beforeend','<meta charset="UTF-8">'
  6. document.head.insertAdjacentHTML 'beforeend','<meta name="viewport" content="width=device-width, initial-scale=1.0">'
  7. document.head.insertAdjacentHTML('beforeend','<title>Кохи Борбад - Концертный зал Душанбе</title>')
  8. # Настройка tailwind
  9. tailwind.config = require 'tailwind.config.js'
  10. # подключение основных стилей
  11. ## tailwind
  12. document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" page="main">'+stylFns['main.css']+'</style>')
  13. ## базовой стиль приложения
  14. document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" page="root">'+stylFns['app/temp.styl']+'</style>')
  15. # Создаем глобальную шину событий
  16. class AppEventBus
  17. constructor: ->
  18. @events = {}
  19. on: (event, callback) ->
  20. if !@events[event]
  21. @events[event] = []
  22. @events[event].push(callback)
  23. emit: (event, data) ->
  24. if @events[event]
  25. for callback in @events[event]
  26. try
  27. callback(data)
  28. catch error
  29. debug.log "Event bus error: " + error
  30. off: (event, callback) ->
  31. if @events[event]
  32. @events[event] = @events[event].filter (cb) -> cb != callback
  33. # Создаем глобально
  34. globalThis.EventBus = new AppEventBus()
  35. # CouchDB сервис
  36. class CouchDBService
  37. constructor: ->
  38. @baseUrl = 'http://localhost:5984'
  39. @dbName = 'kohi_borbad_events'
  40. @headers =
  41. 'Content-Type': 'application/json'
  42. 'Authorization': 'Basic ' + btoa('admin:password')
  43. getAllEvents: ->
  44. try
  45. response = await fetch("#{@baseUrl}/#{@dbName}/_all_docs?include_docs=true", {
  46. method: 'GET'
  47. headers: @headers
  48. })
  49. if response.ok
  50. data = await response.json()
  51. events = data.rows.map (row) -> row.doc
  52. return events.filter (event) -> !event._id.startsWith('_design/')
  53. else
  54. debug.log "Ошибка получения мероприятий: "+response.statusText
  55. return []
  56. catch error
  57. debug.log "Ошибка подключения к CouchDB: "+error
  58. return []
  59. getFeaturedEvents: ->
  60. events = await @getAllEvents()
  61. events
  62. .filter (event) -> event.isFeatured || false
  63. .slice(0, 6)
  64. getSliderEvents: ->
  65. events = await @getAllEvents()
  66. events
  67. .filter (event) -> event.inSlider || false
  68. .map (event) ->
  69. id: event._id
  70. image: event.image || '/images/default-event.jpg'
  71. title: event.title
  72. description: event.shortDescription || event.description
  73. cta: event.cta || 'Подробнее'
  74. category: event.category
  75. # Маршруты
  76. routes = [
  77. { path: '/', component: require 'app/pages/Home' }
  78. { path: '/events', component: require 'app/pages/Events' }
  79. { path: '/about', component: require 'app/pages/About' }
  80. { path: '/contacts', component: require 'app/pages/Contacts' }
  81. ]
  82. # Глобальное определение vuejs приложения
  83. app = Vue.createApp
  84. name: 'app'
  85. data: ()->
  86. return
  87. theme: 'light'
  88. appState:
  89. events: []
  90. featuredEvents: []
  91. sliderEvents: []
  92. loading: true
  93. error: null
  94. modalState:
  95. currentModal: null
  96. modalProps: {}
  97. couchDBService: new CouchDBService()
  98. beforeMount: ()->
  99. debug.log "start beforeMount"
  100. # определение контекста vuejs приложения как глобальной переменной _
  101. globalThis._ = @
  102. render: (new Function '_ctx', '_cache', renderFns['app/temp.pug'])()
  103. mounted: ->
  104. # Предзагрузка темы
  105. if localStorage.theme == 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
  106. @theme = 'dark'
  107. document.documentElement.classList.add('dark')
  108. else
  109. @theme = 'light'
  110. document.documentElement.classList.remove('dark')
  111. # Загрузка данных из CouchDB
  112. await @loadEventsData()
  113. # Обработчик открытия модальных окон - ДОЛЖЕН БЫТЬ ЗДЕСЬ
  114. EventBus.on 'open-modal', (config) =>
  115. debug.log "Opening modal: "+config.component
  116. @modalState.currentModal = config.component
  117. @modalState.modalProps = config.props || {}
  118. methods:
  119. toggleTheme: ->
  120. @theme = if @theme == 'light' then 'dark' else 'light'
  121. localStorage.setItem 'theme', @theme
  122. document.documentElement.classList.toggle 'dark'
  123. handleTicketBooking: (event) ->
  124. debug.log "Обработка покупки билета на: "+event.title
  125. openModal('SuccessModal', {
  126. title: 'Билет забронирован!'
  127. content: "Вы успешно забронировали билет на \""+event.title+"\". Подробности отправлены на вашу почту."
  128. })
  129. loadEventsData: ->
  130. @appState.loading = true
  131. try
  132. [events, featuredEvents, sliderEvents] = await Promise.all([
  133. @couchDBService.getAllEvents()
  134. @couchDBService.getFeaturedEvents()
  135. @couchDBService.getSliderEvents()
  136. ])
  137. @appState.events = events
  138. @appState.featuredEvents = featuredEvents
  139. @appState.sliderEvents = sliderEvents
  140. @appState.error = null
  141. catch error
  142. debug.log "Ошибка загрузки данных: "+error
  143. @appState.error = 'Не удалось загрузить данные мероприятий'
  144. @loadTestData()
  145. finally
  146. @appState.loading = false
  147. loadTestData: ->
  148. # Тестовые данные на случай недоступности CouchDB
  149. @appState.events = [
  150. {
  151. _id: '1'
  152. title: 'Концерт симфонического оркестра'
  153. date: '2020-04-18'
  154. time: '19:00'
  155. description: 'Произведения Чайковского и Рахманинова в исполнении Национального симфонического оркестра'
  156. image: '/images/event-classical.jpg'
  157. category: 'classical'
  158. price: 50
  159. venue: 'Большой зал'
  160. duration: '2 часа 30 минут'
  161. ageRestriction: '12+'
  162. availableTickets: 45
  163. isFeatured: true
  164. inSlider: true
  165. shortDescription: 'Шедевры классической музыки'
  166. cta: 'Купить билеты'
  167. }
  168. {
  169. _id: '2'
  170. title: 'Вечер таджикской народной музыки'
  171. date: '2025-10-20'
  172. time: '18:30'
  173. description: 'Выступление фольклорного ансамбля "Шашмаком" с программой традиционных мелодий и танцев'
  174. image: '/images/event-folk.jpg'
  175. category: 'folk'
  176. price: 30
  177. venue: 'Малый зал'
  178. duration: '2 часа'
  179. ageRestriction: '6+'
  180. availableTickets: 28
  181. isFeatured: true
  182. inSlider: true
  183. shortDescription: 'Традиционные мелодии и танцы Таджикистана'
  184. cta: 'Узнать больше'
  185. }
  186. {
  187. _id: '3'
  188. title: 'Джазовый фестиваль "Borbad Jazz"'
  189. date: '2025-10-25'
  190. time: '20:00'
  191. description: 'Международные джазовые коллективы из Европы и Азии в уникальной акустике зала'
  192. image: '/images/event-jazz.jpg'
  193. category: 'jazz'
  194. price: 70
  195. venue: 'Большой зал'
  196. duration: '3 часа'
  197. ageRestriction: '16+'
  198. availableTickets: 15
  199. isFeatured: true
  200. inSlider: true
  201. shortDescription: 'Международные джазовые коллективы'
  202. cta: 'Смотреть расписание'
  203. }
  204. ]
  205. @appState.featuredEvents = @appState.events.filter((event) -> event.isFeatured).slice(0, 6)
  206. @appState.sliderEvents = @appState.events.filter((event) -> event.inSlider).map (event) ->
  207. id: event._id
  208. image: event.image
  209. title: event.title
  210. description: event.shortDescription
  211. cta: event.cta
  212. category: event.category
  213. getEvents: -> @appState.events
  214. getFeaturedEvents: -> @appState.featuredEvents
  215. getSliderEvents: -> @appState.sliderEvents
  216. isLoading: -> @appState.loading
  217. hasError: -> @appState.error
  218. closeModal: ->
  219. @modalState.currentModal = null
  220. @modalState.modalProps = {}
  221. components:
  222. 'themetoggle': require 'app/shared/ThemeToggle'
  223. 'multilevelmenu': require 'app/shared/MultiLevelMenu'
  224. 'imageslider': require 'app/shared/ImageSlider'
  225. 'modalwindow': require 'app/shared/ModalWindow'
  226. 'formvalidator': require 'app/shared/FormValidator'
  227. 'filtersort': require 'app/shared/FilterSort'
  228. 'eventdetailmodal': require 'app/shared/EventDetailModal'
  229. 'successmodal': require 'app/shared/SuccessModal'
  230. 'applink': require 'app/shared/AppLink'
  231. app.use(VueRouter.createRouter({
  232. routes: routes
  233. history: VueRouter.createWebHistory()
  234. scrollBehavior: (to, from, savedPosition) ->
  235. if savedPosition
  236. return savedPosition
  237. else
  238. return { x: 0, y: 0 }
  239. }))
  240. # Глобальные функции для работы с модальными окнами
  241. globalThis.openModal = (component, props = {}) ->
  242. EventBus.emit 'open-modal', { component, props }
  243. globalThis.closeModal = ->
  244. EventBus.emit 'close-modal'
  245. # подключаем в body ОБЯЗАТЕЛЬНО!!!
  246. app.mount('body')
  247. debug.log "Vue application initialized successfully"