Skip to content
Snippets Groups Projects
MyShows.vue 6.94 KiB
Newer Older
<template>
  <b-container fluid>
    <b-container>
      <PageHeader :title="t('myShows.title')">
        <AddShowButton />

        <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>
      </PageHeader>
    </b-container>

    <b-container>
      <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>

        <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"
      />
    </b-container>
  </b-container>
</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>