Skip to content
Snippets Groups Projects
ModalEdit.vue 14 KiB
Newer Older
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
  <div>
    <b-modal
      ref="modalEmissionManagerEdit"
      :title="$t('scheduleEditor.titleEdit', { show: sanitizeHTML(selectedShow.name) })"
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
      size="lg"
    >
      <server-errors :errors="serverErrors" />

      <div v-if="timeslot && loaded.schedule">
        <p
          class="tw-mb-0"
          v-html="
            $t('scheduleEditor.timeslotRuns', {
              firstDate: prettyDate(timeslot.start),
              startTime: prettyTime(timeslot.start),
              endTime: prettyTime(timeslot.end),
            })
          "
        />

        <div v-if="scheduleRRule && scheduleRRule.count === 1">
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
          <p>
            {{ $t('scheduleEditor.singleEmission') }}
            <span v-if="!loaded.scheduleTimeslots">
              <br />
              <img src="/assets/radio.gif" :alt="$t('loading')" />
            </span>
            <span v-else>
              <span
                v-if="scheduleTimeslots.length > 1"
                v-html="
                  '<br>' +
                  $t('scheduleEditor.coexistingTimeslot', {
                    firstDate:
                      timeslot.start === scheduleTimeslots[0].start
                        ? prettyDate(scheduleTimeslots[1].start)
                        : prettyDate(scheduleTimeslots[0].start),
                    startTime:
                      timeslot.start === scheduleTimeslots[0].start
                        ? prettyTime(scheduleTimeslots[1].start)
                        : prettyTime(scheduleTimeslots[0].start),
                    endTime:
                      timeslot.start === scheduleTimeslots[0].start
                        ? prettyTime(scheduleTimeslots[1].end)
                        : prettyTime(scheduleTimeslots[0].end),
                  })
                "
              />
            </span>
          </p>
        </div>
          <SafeHTML
            as="p"
            sanitize-preset="safe-html"
            :html="
              $t(
                schedule.lastDate
                  ? 'scheduleEditor.recurringSchedule'
                  : 'scheduleEditor.recurringScheduleNoEnd',
                {
                  rrule: scheduleRRule.name,
                  lastDate: schedule.lastDate ? prettyDate(schedule.lastDate) : '',
                },
              )
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
            "
          />

          <b-table
            id="emission-table"
            striped
            :per-page="perPage"
            :current-page="currentPage"
            :fields="[
              { key: 'start', label: $t('scheduleEditor.start') },
              { key: 'end', label: $t('scheduleEditor.end') },
            ]"
            :items="scheduleTimeslots"
            :busy="!loaded.scheduleTimeslots"
            :tbody-tr-class="trClass"
          >
            <template #cell(start)="data">
              {{ prettyDateTime(data.item.start) }}
            </template>
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed

            <template #cell(end)="data">
              {{ prettyDateTime(data.item.end) }}
            </template>
          </b-table>

          <div class="tw-w-full tw-flex tw-justify-end">
            <b-pagination
              v-model="currentPage"
              :total-rows="scheduleTimeslots.length"
              :per-page="perPage"
              aria-controls="emission-table"
            />
          </div>
        </div>
      </div>
      <div v-else>
        <img src="/assets/radio.gif" :alt="$t('loading')" />
      </div>

      <APermissionGuard show-permissions="program.add_schedule">
        <hr />

        <h4>{{ $t('scheduleEditor.addRepetition') }}</h4>
        <b-row>
          <b-col cols="6">
            <label class="tw-leading-loose">
              {{ $t('scheduleEditor.whenToRepeat') }}

              <b-form-select v-model="repetitionRule" :options="repetitionOptions" />
            </label>

            <b-checkbox v-if="repetitionRule !== 4" v-model="useSameTime">
              {{ $t('scheduleEditor.useSameTime') }}
            </b-checkbox>
          </b-col>
          <b-col v-if="!useSameTime" cols="6">
            <label class="tw-leading-loose">
              {{ $t('scheduleEditor.repeatAt') }}
              <b-form-input v-model="repetitionTime" type="time" />
            </label>
          </b-col>
        </b-row>

        <b-row v-if="repetitionRule === 3" class="tw-mt-4">
          <b-col cols="6">
            <label class="tw-leading-loose">
              {{ $t('scheduleEditor.addNoOfDays') }}

              <b-form-input v-model="addNoOfDays" type="number" />
            </label>

            <b-checkbox v-model="onlyBusinessDays">
              {{ $t('scheduleEditor.onlyBusinessDays') }}
            </b-checkbox>
          </b-col>
        </b-row>

        <b-row class="my-4">
          <b-col>
            <b-button variant="primary" size="sm" @click="createRepetitionSchedule">
              {{ $t('scheduleEditor.addRepetition') }}
            </b-button>
          </b-col>
        </b-row>
      </APermissionGuard>
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed

      <template #modal-footer>
        <template v-if="loaded.scheduleTimeslots">
          <template v-if="isInTheFuture(timeslot)">
            <APermissionGuard show-permissions="program.delete_schedule">
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
              <b-button variant="danger" size="sm" @click="deleteFullSchedule(schedule.id)">
                <span v-if="scheduleTimeslots.length === 1">{{
                  $t('scheduleEditor.delete.delete')
                }}</span>
                <span v-else-if="scheduleRRule?.count === 1">{{
                  $t('scheduleEditor.delete.both')
                }}</span>
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
                <span v-else>{{ $t('scheduleEditor.delete.scheduleTimeslots') }}</span>
              </b-button>
            </APermissionGuard>
            <APermissionGuard show-permissions="program.delete_timeslot">
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
              <b-button
                v-if="scheduleRRule?.count !== 1 && scheduleTimeslots.length > 1"
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
                variant="danger"
                size="sm"
                @click="deleteSingleTimeslot(schedule.id, timeslot.id)"
              >
                {{ $t('scheduleEditor.delete.timeslot') }}
              </b-button>
            </APermissionGuard>
          </template>
          <APermissionGuard show-permissions="program.delete_timeslot">
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
            <b-button
              v-if="scheduleRRule?.count !== 1 && scheduleTimeslots.length > 1"
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
              variant="danger"
              size="sm"
              class="tw-ml-auto"
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
              @click="deleteAllFutureTimeslots(schedule.id, timeslot.id)"
            >
              {{ $t('scheduleEditor.delete.allTimeslots') }}
            </b-button>
          </APermissionGuard>
        </template>
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
      </template>
    </b-modal>

    <b-modal
      ref="modalEmissionManagerDeleteTimeslots"
      :title="$t('scheduleEditor.delete.timeslotsTitle')"
      size="lg"
      centered
      hide-footer
      no-close-on-esc
      no-close-on-backdrop
    >
      <div>
        <img src="/assets/radio.gif" :alt="$t('loading')" />
        <b-progress :value="deletion.count" :max="deletion.amount" variant="primary" animated />
      </div>
    </b-modal>
  </div>
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
import { mapGetters } from 'vuex'
import prettyDate, { formatSeconds, hmsToSeconds } from '../../mixins/prettyDate'
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
import ServerErrors from '@/components/ServerErrors.vue'
import APermissionGuard from '@/components/generic/APermissionGuard.vue'
import { sanitizeHTML } from '@/util'
import { useRRuleStore } from '@/stores/rrules'
import SafeHTML from '@/components/generic/SafeHTML.ts'
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed

export default {
  components: { APermissionGuard, SafeHTML, ServerErrors },
  emits: ['conflict', 'update'],
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed

  data() {
    return {
      currentPage: 1,
      perPage: 5,
      timeslot: null,
      deletion: {
        amount: 0,
        count: 0,
      },
      useSameTime: true,
      onlyBusinessDays: false,
      addNoOfDays: 1,
      repetitionRule: 1,
      repetitionTime: '',
      serverErrors: [],
    }
  },

  computed: {
    loaded() {
      return {
        shows: this.$store.state.shows.loaded.shows,
        schedule: this.$store.state.shows.loaded.schedule,
        scheduleTimeslots: this.$store.state.shows.loaded.scheduleTimeslots,
      }
    },

    repetitionOptions() {
      return [
        { value: 1, text: this.$t('scheduleEditor.repetition.followingDay') },
        { value: 2, text: this.$t('scheduleEditor.repetition.followingBusinessDay') },
        { value: 3, text: this.$t('scheduleEditor.repetition.numberOfDaysLater') },
      ]
    },

    ...mapGetters({
      selectedShow: 'shows/selectedShow',
      schedule: 'shows/schedule',
      scheduleTimeslots: 'shows/scheduleTimeslots',
    }),

    ...mapStores(useRRuleStore),

    scheduleRRule() {
      return this.rrulesStore.itemMap.get(this.schedule.rruleId)
    },
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
  },

  methods: {
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
    trClass(item, type) {
      if (!item || type !== 'row') {
        return ''
      }

      return item.id === this.timeslot.id ? 'table-info' : ''
    },

    isInTheFuture(timeslot) {
      const start = new Date(timeslot.start)
      const now = new Date()

      return start > now
    },

    async createRepetitionSchedule() {
      const { onlyBusinessDays, addNoOfDays } = this.getRepetitionParameters()
      const { firstDate, rruleId, lastDate, defaultPlaylistId, automationId, byWeekday } =
      let { startTime, endTime } = this.schedule
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed

      if (this.repetitionTime.length > 0) {
        const newStartTime = hmsToSeconds(this.repetitionTime)
        const oldStartTime = hmsToSeconds(startTime)
        const oldEndTime = hmsToSeconds(endTime)
        startTime = formatSeconds(newStartTime, true)
        endTime = formatSeconds(newStartTime + (oldEndTime - oldStartTime), true)
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
      }

      const newSchedule = {
        schedule: {
          firstDate,
          startTime,
          endTime,
          rruleId,
          lastDate,
          defaultPlaylistId,
          automationId,
          byWeekday,
          isRepetition: true,
          addBusinessDaysOnly: onlyBusinessDays,
          addDaysNo: parseInt(addNoOfDays, 10),
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
      }

      this.serverErrors = []
      try {
        await this.$store.dispatch('shows/submitSchedule', {
          showId: this.selectedShow.id,
          schedule: newSchedule,
        })
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
      } catch (e) {
        if (e.response?.status === 409) {
          this.$log.debug('Timeslot conflict. Switching to resolve mode.')
          this.$emit('conflict', e.response.data)
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
        } else {
          this.serverErrors = e.errors ?? []
        }
      } finally {
        this.$refs.modalEmissionManagerEdit.hide()
      }
    },

    deleteFullSchedule(id) {
      this.$store.dispatch('shows/deleteSchedule', {
        showId: this.selectedShow.id,
        scheduleId: id,
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
        callback: () => {
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
          this.$refs.modalEmissionManagerEdit.hide()
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
      })
    },

    deleteSingleTimeslot(scheduleId, timeslotId) {
      this.$store.dispatch('shows/deleteTimeslot', {
        showId: this.selectedShow.id,
        scheduleId: scheduleId,
        timeslotId: timeslotId,
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
        callback: () => {
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
          this.$refs.modalEmissionManagerEdit.hide()
        },
      })
    },

    deleteAllFutureTimeslots(scheduleId) {
      const startDate = new Date(this.timeslot.start)
      const toDelete = []
      for (const slot of this.scheduleTimeslots) {
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
        if (new Date(slot.start) >= startDate) {
          toDelete.push(slot.id)
        }
      }

      if (toDelete.length === this.scheduleTimeslots.length) {
        this.$log.debug('deleting full schedule')
        this.deleteFullSchedule(scheduleId)
      } else {
        this.deletion.amount = toDelete.length
        this.deletion.count = 0
        this.$refs.modalEmissionManagerDeleteTimeslots.show()

        for (const i in toDelete) {
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
          this.$log.debug('Deleting timeslot', toDelete[i])
          this.$store.dispatch('shows/deleteTimeslot', {
            showId: this.selectedShow.id,
            scheduleId: scheduleId,
            timeslotId: toDelete[i],
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
            callback: () => {
              this.deletion.count++
              this.$log.debug('deleted ' + this.deletion.count + ' timeslots')
              if (this.deletion.count === this.deletion.amount) {
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
                this.$refs.modalEmissionManagerDeleteTimeslots.hide()
                this.$refs.modalEmissionManagerEdit.hide()
              }
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
          })
        }
      }
    },

    loadSchedule(scheduleId) {
      this.$store.dispatch('shows/fetchSchedule', {
        showId: this.selectedShow.id,
        scheduleId,
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
        callback: () => {
          this.loadScheduleTimeslots(scheduleId)
        },
      })
    },

    loadScheduleTimeslots(scheduleId) {
      this.$store.dispatch('shows/fetchTimeslots', {
        scheduleId,
        start: this.schedule.firstDate,
        end: this.schedule.lastDate,
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
      })
    },

    getRepetitionParameters() {
      if (this.repetitionRule == 1) {
        return {
          onlyBusinessDays: false,
          addNoOfDays: 1,
        }
      }
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
      if (this.repetitionRule == 2) {
        return {
          onlyBusinessDays: true,
          addNoOfDays: 1,
Konrad Mohrfeldt's avatar
Konrad Mohrfeldt committed
      }

      return {
        onlyBusinessDays: this.onlyBusinessDays,
        addNoOfDays: this.addNoOfDays,
      }
    },

    // initialise a new schedule and open the modal
    open(timeslot) {
      const timeslotIndex = this.scheduleTimeslots.findIndex((item) => item.id === timeslot.id)
      let timeslotPage = Math.ceil(timeslotIndex / this.perPage)
      const timeslotIsFirstEntryOnPage = timeslotIndex === timeslotPage * this.perPage

      // The math above is slightly off for every first entry on the page
      // So we add 1 to adjust the page to the correct one
      if (timeslotIsFirstEntryOnPage) {
        timeslotPage += 1
      }

      this.timeslot = timeslot
      this.currentPage = timeslotPage
      this.$refs.modalEmissionManagerEdit.show()
      this.loadSchedule(timeslot.scheduleId)