Skip to content
Snippets Groups Projects
MyShows.vue 6.65 KiB
Newer Older
<template>
  <div>
    <PageHeader :title="t('myShows.title')">
      <AddShowButton />
    </PageHeader>
    <div class="tw-flex tw-gap-6 tw-justify-end tw-items-center tw-mb-6">
      <Tag v-if="displayMode === 'grid'" class="tw-self-stretch tw-justify-self-start tw-px-3">
        {{
          t('myShows.showCount', {
            count: gridShowResultList.reduce((acc, cur) => acc + cur.items.length, 0),
            total: gridShowResultList[0]?.count ?? 0,
          })
        }}
      </Tag>
      <Tag v-if="isLoading" role="alert" aria-live="polite" class="tw-px-3 tw-self-stretch">
        <span class="tw-flex tw-gap-2 tw-items-center">
          <Loading class="tw-h-1" />
          {{ t('loadingData', { items: t('show.plural') }) }}
        </span>
      </Tag>
      <span class="tw-mr-auto" />
      <FormGroup class="tw-m-0">
        <template #iconLeft="attrs">
          <icon-system-uicons-search v-bind="attrs" />
        </template>
        <template #default="attrs">
          <input
            v-model="searchTerm"
            v-bind="attrs"
            :aria-label="t('myShows.searchLabel')"
            :placeholder="t('myShows.searchPlaceholder')"
        </template>
      </FormGroup>

      <button
        class="btn btn-default tw-flex tw-items-center tw-gap-2"
        @click="orderFilterDialog.open()"
      >
        <icon-system-uicons-sort />
        {{ t('myShows.sortShows') }}
      </button>
      <RadioGroup v-model="displayMode" :choices="['table', 'grid']" name="display-mode">
        <template #icon="{ value }">
          <icon-system-uicons-table-header v-if="value === 'table'" />
          <icon-system-uicons-grid-small v-else-if="value === 'grid'" />
        </template>
      </RadioGroup>

      <AEditDialog ref="orderFilterDialog" :title="t('myShows.showOrder')" class="tw-w-min">
        <OrderFilter
          v-model="order"
          translate-base="showFilter.order.choices"
          :choices="[
            'id',
            'slug',
            { name: 'is_active', directions: ['desc', 'asc'] },
            { name: 'updated_at', directions: ['desc', 'asc'] },
            'updated_by',
            { name: 'is_owner', directions: ['desc', 'asc'] },
          ]"
        />
        <template #footer="{ close }">
          <button type="button" class="btn btn-default tw-min-w-[100px]" @click="close">
            {{ t('ok') }}
          </button>
        </template>
      </AEditDialog>
    </div>

    <ShowListTable v-if="displayMode === 'table'" :shows="tableShowResult">
      <template #footer>
        <div
          v-if="tableShowResult.count > 0 || !isLoading"
          class="tw-flex tw-items-center tw-px-6 tw-mt-3 tw-py-3 tw-border-0 tw-border-t tw-border-solid tw-border-gray-200 empty:tw-hidden"
        >
          <PaginationRange v-if="tableShowResult.count > 0" :pagination-data="tableShowResult" />
          <FormGroup v-slot="attrs" class="tw-ml-auto tw-m-0 tw-mr-9 last:tw-mr-0">
            <label class="tw-flex tw-items-center tw-gap-3 tw-m-0">
              <span>{{ t('myShows.itemsPerPage') }}</span>
              <input
                v-model.lazy="tableShowsPerPage"
                type="number"
                min="1"
                step="1"
                v-bind="attrs"
                class="tw-w-[80px] tw-self-center"
              />
            </label>
          </FormGroup>
          <Pagination
            v-model="tableShowsPage"
            :items-per-page="tableShowsPerPage"
            :count="tableShowResult.count"
          />
        </div>
      </template>
    </ShowListTable>
    <ShowListGrid
      v-else
      :shows="gridShowResultList"
      :has-more="gridShowResult.hasNext"
      :split="order[0] === '-is_owner'"
      @load-more="loadMore"
    />
  </div>
</template>

<script lang="ts" setup>
import { PaginatedListResult, usePaginatedList } from '@rokoli/bnb/drf'
import { useStorage } from '@vueuse/core'
import { computed, Ref, ref, watch } from 'vue'
import { useI18n } from '@/i18n'
import { computedDebounced } from '@/util'
import { Show } from '@/types'
import { useShowStore } from '@/stores/shows'
import PageHeader from '@/components/PageHeader.vue'
import ShowListTable from '@/components/shows/ShowListTable.vue'
import FormGroup from '@/components/generic/FormGroup.vue'
import ShowListGrid from '@/components/shows/ShowListGrid.vue'
import RadioGroup from '@/components/generic/RadioGroup.vue'
import Loading from '@/components/generic/Loading.vue'
import Tag from '@/components/generic/Tag.vue'
import AEditDialog from '@/components/generic/AEditDialog.vue'
import OrderFilter from '@/components/OrderFilter.vue'
import PaginationRange from '@/components/generic/PaginationRange.vue'
import Pagination from '@/components/generic/Pagination.vue'
import AddShowButton from '@/components/shows/AddShowButton.vue'

const TABLE_SHOWS_PER_PAGE = 12
const GRID_SHOWS_PER_PAGE = 10

type DisplayMode = 'grid' | 'table'
type OrderField = 'is_owner' | 'is_active' | 'slug' | 'updatedAt' | 'updatedBy' | 'id'
type ShowOrder = OrderField | `-${OrderField}`

const { t } = useI18n()
const { listIsolated } = useShowStore()

const orderFilterDialog = ref()
const searchTerm = ref('')
const debouncedSearchTerm = computedDebounced(searchTerm, (q: string) => (q.trim() ? 0.3 : 0))
const order = useStorage<ShowOrder[]>('aura:myShows:order', ['-is_owner', '-is_active', 'slug'])
const query = computed(
  () =>
    new URLSearchParams({
      order: order.value.join(','),
      search: debouncedSearchTerm.value.trim(),
    }),
)

const displayMode: Ref<DisplayMode> = useStorage<DisplayMode>('aura:myShows:displayMode', 'grid')

const tableShowsPage = ref(1)
const tableShowsPerPage = useStorage('aura:myShows:tableShowsPerPage', TABLE_SHOWS_PER_PAGE)
const { result: tableShowResult, isLoading: isLoadingTableShows } = usePaginatedList(
  listIsolated,
  tableShowsPage,
  tableShowsPerPage,
  { query },
)

const gridShowsPage = ref(1)
const gridShowsPerPage = useStorage('aura:myShows:gridShowsPerPage', GRID_SHOWS_PER_PAGE)
const gridShowResultMap = ref(new Map<number, PaginatedListResult<Show>>())
const gridShowResultList = computed(() => Array.from(gridShowResultMap.value.values()))
const { result: gridShowResult, isLoading: isLoadingGridShows } = usePaginatedList(
  listIsolated,
  gridShowsPage,
  gridShowsPerPage,
  { query },
)
watch(query, () => {
  gridShowsPage.value = 1
  gridShowResultMap.value.clear()
})
watch(gridShowResult, (newShows: PaginatedListResult<Show>) => {
  gridShowResultMap.value.set(newShows.page, newShows)
})
function loadMore() {
  if (gridShowResult.value.hasNext) {
    gridShowsPage.value += 1
  }
}

const isLoading = computed(() => isLoadingTableShows.value || isLoadingGridShows.value)
</script>