Skip to content
Snippets Groups Projects
MyShows.vue 6.59 KiB
Newer Older
  • Learn to ignore specific revisions
  • <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">
    
          <span v-if="displayMode === 'grid' && !isLoading" class="tw-font-medium">
    
            {{
              t('myShows.showCount', {
                count: gridShowResultList.reduce((acc, cur) => acc + cur.items.length, 0),
                total: gridShowResultList[0]?.count ?? 0,
              })
            }}
    
          <span v-if="isLoading" role="alert" aria-live="polite">
    
            <span class="tw-flex tw-gap-2 tw-items-center">
              <Loading class="tw-h-1" />
              {{ t('loadingData', { items: t('show.plural') }) }}
            </span>
    
          <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')"
    
          <button class="btn btn-default" @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-justify-center tw-min-w-[100px]"
                @click="close"
              >
    
            </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-gap-3 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 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 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 = 12
    
    
    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>