index.coffee 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. # app/shared/EventDetailModal/index.coffee
  2. document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" component="EventDetailModal">'+stylFns['app/shared/EventDetailModal/index.styl']+'</style>')
  3. module.exports =
  4. name: 'EventDetailModal'
  5. render: (new Function '_ctx', '_cache', renderFns['app/shared/EventDetailModal/index.pug'])()
  6. props:
  7. isVisible:
  8. type: Boolean
  9. default: false
  10. event:
  11. type: Object
  12. required: true
  13. validator: (value) ->
  14. value && typeof value == 'object'
  15. data: ->
  16. categoryLabels:
  17. classical: 'Классическая музыка'
  18. folk: 'Фольклор'
  19. jazz: 'Джаз'
  20. pop: 'Поп-музыка'
  21. dance: 'Танцевальное шоу'
  22. experimental: 'Экспериментальная музыка'
  23. theater: 'Театр'
  24. opera: 'Опера'
  25. mounted: ->
  26. @setupKeyboardListeners()
  27. beforeUnmount: ->
  28. @removeKeyboardListeners()
  29. watch:
  30. isVisible: (newVal) ->
  31. if newVal
  32. @$nextTick => @focusFirstInteractiveElement()
  33. methods:
  34. getCategoryLabel: (category) ->
  35. @categoryLabels[category] || category
  36. getCategoryBadgeClass: (category) ->
  37. classes =
  38. classical: 'bg-blue-500 text-white'
  39. folk: 'bg-green-500 text-white'
  40. jazz: 'bg-purple-500 text-white'
  41. pop: 'bg-pink-500 text-white'
  42. dance: 'bg-orange-500 text-white'
  43. experimental: 'bg-indigo-500 text-white'
  44. theater: 'bg-red-500 text-white'
  45. opera: 'bg-teal-500 text-white'
  46. classes[category] || 'bg-gray-500 text-white'
  47. formatDateTime: (dateString, timeString) ->
  48. try
  49. date = new Date(dateString)
  50. options = {
  51. weekday: 'long',
  52. year: 'numeric',
  53. month: 'long',
  54. day: 'numeric'
  55. }
  56. formattedDate = date.toLocaleDateString('ru-RU', options)
  57. "#{formattedDate}, #{timeString}"
  58. catch error
  59. console.error 'Error formatting date:', error
  60. "#{dateString}, #{timeString}"
  61. # Стало (правильно)
  62. bookTickets: ->
  63. if @event?.availableTickets > 0
  64. debug.log "Бронирование билетов на: "+@event.title
  65. @$emit 'ticket-booking', @event
  66. # Имитация процесса покупки
  67. openModal('SuccessModal', {
  68. title: 'Билет забронирован'
  69. content: "Вы успешно забронировали билет на \""+@event.title+"\""
  70. })
  71. else
  72. openModal('SuccessModal', {
  73. title: 'Билеты распроданы'
  74. content: 'К сожалению, все билеты на это мероприятие уже распроданы.'
  75. })
  76. addToCalendar: ->
  77. console.log 'Добавление в календарь:', @event.title
  78. @$emit 'add-to-calendar', @event
  79. # Создание ссылки для добавления в календарь
  80. try
  81. startDate = new Date("#{@event.date}T#{@event.time}")
  82. endDate = new Date(startDate.getTime() + 2 * 60 * 60 * 1000) # +2 часа
  83. googleCalendarUrl = @generateGoogleCalendarUrl(startDate, endDate)
  84. window.open(googleCalendarUrl, '_blank')
  85. catch error
  86. console.error 'Error generating calendar event:', error
  87. # Fallback: показать инструкции
  88. @showCalendarInstructions()
  89. generateGoogleCalendarUrl: (startDate, endDate) ->
  90. title = encodeURIComponent(@event.title)
  91. details = encodeURIComponent(@event.description)
  92. location = encodeURIComponent('Концертный зал "Кохи Борбад", Душанбе')
  93. startStr = startDate.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'
  94. endStr = endDate.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'
  95. "https://calendar.google.com/calendar/render?action=TEMPLATE&text=#{title}&details=#{details}&location=#{location}&dates=#{startStr}/#{endStr}"
  96. showCalendarInstructions: ->
  97. @$root.$emit('open-modal', {
  98. component: 'InfoModal'
  99. props: {
  100. title: 'Добавление в календарь'
  101. content: 'Скопируйте информацию о мероприятии и добавьте её в ваш календарь вручную.'
  102. }
  103. })
  104. shareOnFacebook: ->
  105. try
  106. url = encodeURIComponent(window.location.href)
  107. text = encodeURIComponent("Посетите "+@event.title+" в Кохи Борбад")
  108. shareUrl = "https://www.facebook.com/sharer/sharer.php?u="+url+"&quote="+text
  109. window.open(shareUrl, '_blank', 'width=600,height=400')
  110. catch error
  111. console.error 'Error sharing on Facebook:', error
  112. shareOnTwitter: ->
  113. try
  114. text = encodeURIComponent("\"#{@event.title}\" - #{@formatDateTime(@event.date, @event.time)} в Кохи Борбад")
  115. shareUrl = "https://twitter.com/intent/tweet?text=#{text}"
  116. window.open(shareUrl, '_blank', 'width=600,height=400')
  117. catch error
  118. console.error 'Error sharing on Twitter:', error
  119. setupKeyboardListeners: ->
  120. document.addEventListener 'keydown', @handleKeydown
  121. removeKeyboardListeners: ->
  122. document.removeEventListener 'keydown', @handleKeydown
  123. handleKeydown: (event) ->
  124. if event.key == 'Escape' and @isVisible
  125. @$emit 'update:isVisible', false
  126. else if event.key == 'Tab' and @isVisible
  127. @trapFocus(event)
  128. trapFocus: (event) ->
  129. modal = @$el.querySelector('.modal-content')
  130. return unless modal
  131. focusableElements = modal.querySelectorAll(
  132. 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  133. )
  134. firstElement = focusableElements[0]
  135. lastElement = focusableElements[focusableElements.length - 1]
  136. if event.shiftKey
  137. if document.activeElement == firstElement
  138. event.preventDefault()
  139. lastElement.focus()
  140. else
  141. if document.activeElement == lastElement
  142. event.preventDefault()
  143. firstElement.focus()
  144. focusFirstInteractiveElement: ->
  145. @$nextTick ->
  146. modal = @$el.querySelector('.modal-content')
  147. return unless modal
  148. firstInteractive = modal.querySelector(
  149. 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  150. )
  151. firstInteractive?.focus()
  152. getTicketAvailabilityClass: ->
  153. if @event?.availableTickets > 20
  154. return 'text-green-600 dark:text-green-400'
  155. else if @event?.availableTickets > 5
  156. return 'text-orange-600 dark:text-orange-400'
  157. else if @event?.availableTickets > 0
  158. return 'text-red-600 dark:text-red-400'
  159. else
  160. return 'text-gray-500 dark:text-gray-400'
  161. emits: ['update:isVisible', 'ticket-booking', 'add-to-calendar']