|
@@ -0,0 +1,193 @@
|
|
|
|
|
+# app/shared/EventDetailModal/index.coffee
|
|
|
|
|
+document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss">'+stylFns['app/shared/EventDetailModal/index.styl']+'</style>')
|
|
|
|
|
+
|
|
|
|
|
+module.exports =
|
|
|
|
|
+ name: 'EventDetailModal'
|
|
|
|
|
+ render: (new Function '_ctx', '_cache', renderFns['app/shared/EventDetailModal/index.pug'])()
|
|
|
|
|
+ props:
|
|
|
|
|
+ isVisible:
|
|
|
|
|
+ type: Boolean
|
|
|
|
|
+ default: false
|
|
|
|
|
+ event:
|
|
|
|
|
+ type: Object
|
|
|
|
|
+ required: true
|
|
|
|
|
+ validator: (value) ->
|
|
|
|
|
+ value && typeof value == 'object'
|
|
|
|
|
+
|
|
|
|
|
+ data: ->
|
|
|
|
|
+ categoryLabels:
|
|
|
|
|
+ classical: 'Классическая музыка'
|
|
|
|
|
+ folk: 'Фольклор'
|
|
|
|
|
+ jazz: 'Джаз'
|
|
|
|
|
+ pop: 'Поп-музыка'
|
|
|
|
|
+ dance: 'Танцевальное шоу'
|
|
|
|
|
+ experimental: 'Экспериментальная музыка'
|
|
|
|
|
+ theater: 'Театр'
|
|
|
|
|
+ opera: 'Опера'
|
|
|
|
|
+
|
|
|
|
|
+ mounted: ->
|
|
|
|
|
+ @setupKeyboardListeners()
|
|
|
|
|
+
|
|
|
|
|
+ beforeUnmount: ->
|
|
|
|
|
+ @removeKeyboardListeners()
|
|
|
|
|
+
|
|
|
|
|
+ watch:
|
|
|
|
|
+ isVisible: (newVal) ->
|
|
|
|
|
+ if newVal
|
|
|
|
|
+ @$nextTick => @focusFirstInteractiveElement()
|
|
|
|
|
+
|
|
|
|
|
+ methods:
|
|
|
|
|
+ getCategoryLabel: (category) ->
|
|
|
|
|
+ @categoryLabels[category] || category
|
|
|
|
|
+
|
|
|
|
|
+ getCategoryBadgeClass: (category) ->
|
|
|
|
|
+ classes =
|
|
|
|
|
+ classical: 'bg-blue-500 text-white'
|
|
|
|
|
+ folk: 'bg-green-500 text-white'
|
|
|
|
|
+ jazz: 'bg-purple-500 text-white'
|
|
|
|
|
+ pop: 'bg-pink-500 text-white'
|
|
|
|
|
+ dance: 'bg-orange-500 text-white'
|
|
|
|
|
+ experimental: 'bg-indigo-500 text-white'
|
|
|
|
|
+ theater: 'bg-red-500 text-white'
|
|
|
|
|
+ opera: 'bg-teal-500 text-white'
|
|
|
|
|
+
|
|
|
|
|
+ classes[category] || 'bg-gray-500 text-white'
|
|
|
|
|
+
|
|
|
|
|
+ formatDateTime: (dateString, timeString) ->
|
|
|
|
|
+ try
|
|
|
|
|
+ date = new Date(dateString)
|
|
|
|
|
+ options = {
|
|
|
|
|
+ weekday: 'long',
|
|
|
|
|
+ year: 'numeric',
|
|
|
|
|
+ month: 'long',
|
|
|
|
|
+ day: 'numeric'
|
|
|
|
|
+ }
|
|
|
|
|
+ formattedDate = date.toLocaleDateString('ru-RU', options)
|
|
|
|
|
+ "#{formattedDate}, #{timeString}"
|
|
|
|
|
+ catch error
|
|
|
|
|
+ console.error 'Error formatting date:', error
|
|
|
|
|
+ "#{dateString}, #{timeString}"
|
|
|
|
|
+
|
|
|
|
|
+ bookTickets: ->
|
|
|
|
|
+ if @event?.availableTickets > 0
|
|
|
|
|
+ console.log 'Бронирование билетов на:', @event.title
|
|
|
|
|
+ @$emit 'ticket-booking', @event
|
|
|
|
|
+
|
|
|
|
|
+ # Имитация процесса покупки
|
|
|
|
|
+ @$root.$emit('open-modal', {
|
|
|
|
|
+ component: 'TicketPurchaseModal'
|
|
|
|
|
+ props: { event: @event }
|
|
|
|
|
+ })
|
|
|
|
|
+ else
|
|
|
|
|
+ @$root.$emit('open-modal', {
|
|
|
|
|
+ component: 'ErrorModal'
|
|
|
|
|
+ props: {
|
|
|
|
|
+ title: 'Билеты распроданы'
|
|
|
|
|
+ content: 'К сожалению, все билеты на это мероприятие уже распроданы.'
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ addToCalendar: ->
|
|
|
|
|
+ console.log 'Добавление в календарь:', @event.title
|
|
|
|
|
+ @$emit 'add-to-calendar', @event
|
|
|
|
|
+
|
|
|
|
|
+ # Создание ссылки для добавления в календарь
|
|
|
|
|
+ try
|
|
|
|
|
+ startDate = new Date("#{@event.date}T#{@event.time}")
|
|
|
|
|
+ endDate = new Date(startDate.getTime() + 2 * 60 * 60 * 1000) # +2 часа
|
|
|
|
|
+
|
|
|
|
|
+ googleCalendarUrl = @generateGoogleCalendarUrl(startDate, endDate)
|
|
|
|
|
+ window.open(googleCalendarUrl, '_blank')
|
|
|
|
|
+ catch error
|
|
|
|
|
+ console.error 'Error generating calendar event:', error
|
|
|
|
|
+ # Fallback: показать инструкции
|
|
|
|
|
+ @showCalendarInstructions()
|
|
|
|
|
+
|
|
|
|
|
+ generateGoogleCalendarUrl: (startDate, endDate) ->
|
|
|
|
|
+ title = encodeURIComponent(@event.title)
|
|
|
|
|
+ details = encodeURIComponent(@event.description)
|
|
|
|
|
+ location = encodeURIComponent('Концертный зал "Кохи Борбад", Душанбе')
|
|
|
|
|
+
|
|
|
|
|
+ startStr = startDate.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'
|
|
|
|
|
+ endStr = endDate.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'
|
|
|
|
|
+
|
|
|
|
|
+ "https://calendar.google.com/calendar/render?action=TEMPLATE&text=#{title}&details=#{details}&location=#{location}&dates=#{startStr}/#{endStr}"
|
|
|
|
|
+
|
|
|
|
|
+ showCalendarInstructions: ->
|
|
|
|
|
+ @$root.$emit('open-modal', {
|
|
|
|
|
+ component: 'InfoModal'
|
|
|
|
|
+ props: {
|
|
|
|
|
+ title: 'Добавление в календарь'
|
|
|
|
|
+ content: 'Скопируйте информацию о мероприятии и добавьте её в ваш календарь вручную.'
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ shareOnFacebook: ->
|
|
|
|
|
+ try
|
|
|
|
|
+ url = encodeURIComponent(window.location.href)
|
|
|
|
|
+ text = encodeURIComponent("Посетите \"#{@event.title}\" в Кохи Борбад")
|
|
|
|
|
+ shareUrl = "https://www.facebook.com/sharer/sharer.php?u=#{url}"e=#{text}"
|
|
|
|
|
+ window.open(shareUrl, '_blank', 'width=600,height=400')
|
|
|
|
|
+ catch error
|
|
|
|
|
+ console.error 'Error sharing on Facebook:', error
|
|
|
|
|
+
|
|
|
|
|
+ shareOnTwitter: ->
|
|
|
|
|
+ try
|
|
|
|
|
+ text = encodeURIComponent("\"#{@event.title}\" - #{@formatDateTime(@event.date, @event.time)} в Кохи Борбад")
|
|
|
|
|
+ shareUrl = "https://twitter.com/intent/tweet?text=#{text}"
|
|
|
|
|
+ window.open(shareUrl, '_blank', 'width=600,height=400')
|
|
|
|
|
+ catch error
|
|
|
|
|
+ console.error 'Error sharing on Twitter:', error
|
|
|
|
|
+
|
|
|
|
|
+ setupKeyboardListeners: ->
|
|
|
|
|
+ document.addEventListener 'keydown', @handleKeydown
|
|
|
|
|
+
|
|
|
|
|
+ removeKeyboardListeners: ->
|
|
|
|
|
+ document.removeEventListener 'keydown', @handleKeydown
|
|
|
|
|
+
|
|
|
|
|
+ handleKeydown: (event) ->
|
|
|
|
|
+ if event.key == 'Escape' and @isVisible
|
|
|
|
|
+ @$emit 'update:isVisible', false
|
|
|
|
|
+ else if event.key == 'Tab' and @isVisible
|
|
|
|
|
+ @trapFocus(event)
|
|
|
|
|
+
|
|
|
|
|
+ trapFocus: (event) ->
|
|
|
|
|
+ modal = @$el.querySelector('.modal-content')
|
|
|
|
|
+ return unless modal
|
|
|
|
|
+
|
|
|
|
|
+ focusableElements = modal.querySelectorAll(
|
|
|
|
|
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
|
|
|
+ )
|
|
|
|
|
+ firstElement = focusableElements[0]
|
|
|
|
|
+ lastElement = focusableElements[focusableElements.length - 1]
|
|
|
|
|
+
|
|
|
|
|
+ if event.shiftKey
|
|
|
|
|
+ if document.activeElement == firstElement
|
|
|
|
|
+ event.preventDefault()
|
|
|
|
|
+ lastElement.focus()
|
|
|
|
|
+ else
|
|
|
|
|
+ if document.activeElement == lastElement
|
|
|
|
|
+ event.preventDefault()
|
|
|
|
|
+ firstElement.focus()
|
|
|
|
|
+
|
|
|
|
|
+ focusFirstInteractiveElement: ->
|
|
|
|
|
+ @$nextTick ->
|
|
|
|
|
+ modal = @$el.querySelector('.modal-content')
|
|
|
|
|
+ return unless modal
|
|
|
|
|
+
|
|
|
|
|
+ firstInteractive = modal.querySelector(
|
|
|
|
|
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
|
|
|
+ )
|
|
|
|
|
+ firstInteractive?.focus()
|
|
|
|
|
+
|
|
|
|
|
+ getTicketAvailabilityClass: ->
|
|
|
|
|
+ if @event?.availableTickets > 20
|
|
|
|
|
+ return 'text-green-600 dark:text-green-400'
|
|
|
|
|
+ else if @event?.availableTickets > 5
|
|
|
|
|
+ return 'text-orange-600 dark:text-orange-400'
|
|
|
|
|
+ else if @event?.availableTickets > 0
|
|
|
|
|
+ return 'text-red-600 dark:text-red-400'
|
|
|
|
|
+ else
|
|
|
|
|
+ return 'text-gray-500 dark:text-gray-400'
|
|
|
|
|
+
|
|
|
|
|
+ emits: ['update:isVisible', 'ticket-booking', 'add-to-calendar']
|