Newer
Older
<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')"
</template>
</FormGroup>
<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"
>
{{ t('ok') }}
</button>
</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(),
isWritable: '1',
}),
)
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>