index.coffee 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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. bookTickets: ->
  62. if @event?.availableTickets > 0
  63. console.log 'Бронирование билетов на:', @event.title
  64. @$emit 'ticket-booking', @event
  65. # Имитация процесса покупки
  66. @$root.$emit('open-modal', {
  67. component: 'TicketPurchaseModal'
  68. props: { event: @event }
  69. })
  70. else
  71. @$root.$emit('open-modal', {
  72. component: 'ErrorModal'
  73. props: {
  74. title: 'Билеты распроданы'
  75. content: 'К сожалению, все билеты на это мероприятие уже распроданы.'
  76. }
  77. })
  78. addToCalendar: ->
  79. console.log 'Добавление в календарь:', @event.title
  80. @$emit 'add-to-calendar', @event
  81. # Создание ссылки для добавления в календарь
  82. try
  83. startDate = new Date("#{@event.date}T#{@event.time}")
  84. endDate = new Date(startDate.getTime() + 2 * 60 * 60 * 1000) # +2 часа
  85. googleCalendarUrl = @generateGoogleCalendarUrl(startDate, endDate)
  86. window.open(googleCalendarUrl, '_blank')
  87. catch error
  88. console.error 'Error generating calendar event:', error
  89. # Fallback: показать инструкции
  90. @showCalendarInstructions()
  91. generateGoogleCalendarUrl: (startDate, endDate) ->
  92. title = encodeURIComponent(@event.title)
  93. details = encodeURIComponent(@event.description)
  94. location = encodeURIComponent('Концертный зал "Кохи Борбад", Душанбе')
  95. startStr = startDate.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'
  96. endStr = endDate.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'
  97. "https://calendar.google.com/calendar/render?action=TEMPLATE&text=#{title}&details=#{details}&location=#{location}&dates=#{startStr}/#{endStr}"
  98. showCalendarInstructions: ->
  99. @$root.$emit('open-modal', {
  100. component: 'InfoModal'
  101. props: {
  102. title: 'Добавление в календарь'
  103. content: 'Скопируйте информацию о мероприятии и добавьте её в ваш календарь вручную.'
  104. }
  105. })
  106. shareOnFacebook: ->
  107. try
  108. url = encodeURIComponent(window.location.href)
  109. text = encodeURIComponent("Посетите \"#{@event.title}\" в Кохи Борбад")
  110. shareUrl = "https://www.facebook.com/sharer/sharer.php?u=#{url}&quote=#{text}"
  111. window.open(shareUrl, '_blank', 'width=600,height=400')
  112. catch error
  113. console.error 'Error sharing on Facebook:', error
  114. shareOnTwitter: ->
  115. try
  116. text = encodeURIComponent("\"#{@event.title}\" - #{@formatDateTime(@event.date, @event.time)} в Кохи Борбад")
  117. shareUrl = "https://twitter.com/intent/tweet?text=#{text}"
  118. window.open(shareUrl, '_blank', 'width=600,height=400')
  119. catch error
  120. console.error 'Error sharing on Twitter:', error
  121. setupKeyboardListeners: ->
  122. document.addEventListener 'keydown', @handleKeydown
  123. removeKeyboardListeners: ->
  124. document.removeEventListener 'keydown', @handleKeydown
  125. handleKeydown: (event) ->
  126. if event.key == 'Escape' and @isVisible
  127. @$emit 'update:isVisible', false
  128. else if event.key == 'Tab' and @isVisible
  129. @trapFocus(event)
  130. trapFocus: (event) ->
  131. modal = @$el.querySelector('.modal-content')
  132. return unless modal
  133. focusableElements = modal.querySelectorAll(
  134. 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  135. )
  136. firstElement = focusableElements[0]
  137. lastElement = focusableElements[focusableElements.length - 1]
  138. if event.shiftKey
  139. if document.activeElement == firstElement
  140. event.preventDefault()
  141. lastElement.focus()
  142. else
  143. if document.activeElement == lastElement
  144. event.preventDefault()
  145. firstElement.focus()
  146. focusFirstInteractiveElement: ->
  147. @$nextTick ->
  148. modal = @$el.querySelector('.modal-content')
  149. return unless modal
  150. firstInteractive = modal.querySelector(
  151. 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
  152. )
  153. firstInteractive?.focus()
  154. getTicketAvailabilityClass: ->
  155. if @event?.availableTickets > 20
  156. return 'text-green-600 dark:text-green-400'
  157. else if @event?.availableTickets > 5
  158. return 'text-orange-600 dark:text-orange-400'
  159. else if @event?.availableTickets > 0
  160. return 'text-red-600 dark:text-red-400'
  161. else
  162. return 'text-gray-500 dark:text-gray-400'
  163. emits: ['update:isVisible', 'ticket-booking', 'add-to-calendar']