refactor(front): Radio detail, radio cards and radio page

This commit is contained in:
ArneBo 2025-02-05 00:12:47 +01:00
parent 79ac7d826c
commit 6fbe027876
3 changed files with 214 additions and 218 deletions

View File

@ -113,144 +113,148 @@ const paginateOptions = computed(() => sortedUniq([12, 25, 50, paginateBy.value]
</script>
<template>
<Layout main stack>
<Layout main stack gap-64>
<Header :h1="t('components.library.Radios.header.browse')" />
<Section alignLeft :h2="t('components.library.Radios.header.instance')">
</Section>
<Layout flex>
<radio-card
v-if="isAuthenticated"
:type="'actor-content'"
:object-id="store.state.auth.fullUsername"
/>
<radio-card
v-if="isAuthenticated && hasFavorites"
:type="'favorites'"
/>
<radio-card
v-if="scope === 'all'"
:type="'random'"
/>
<radio-card
v-if="scope === 'me'"
:type="'random_library'"
/>
<radio-card :type="'recently-added'" />
<radio-card
v-if="store.state.auth.authenticated && scope === 'all'"
:type="'less-listened'"
/>
<radio-card
v-if="store.state.auth.authenticated && scope === 'me'"
:type="'less-listened_library'"
/>
</Layout>
<Spacer />
<Section alignLeft :h2="t('components.library.Radios.header.user')" :action="{ text:t('components.library.Radios.button.create'), to:'/library/radios/build' }" />
<Layout flex form
:class="['ui', {'loading': isLoading}, 'form']"
@submit.prevent="search"
>
<Input search
id="radios-search"
v-model="query"
name="search"
:label="t('components.library.Radios.label.search')"
:placeholder="labels.searchPlaceholder"
/>
<Layout stack noGap label for="radios-ordering">
<span class="label">
{{ t('components.library.Radios.ordering.label') }}
</span>
<select
id="radios-ordering"
v-model="ordering"
class="dropdown"
>
<option
v-for="(option, key) in orderingOptions"
:key="key"
:value="option[0]"
>
{{ sharedLabels.filters[option[1]] }}
</option>
</select>
</Layout>
<Layout stack noGap label for="radios-ordering-direction">
<span class="label">
{{ t('components.library.Radios.ordering.direction.label') }}
</span>
<select
id="radios-ordering-direction"
v-model="orderingDirection"
class="dropdown"
>
<option value="+">
{{ t('components.library.Radios.ordering.direction.ascending') }}
</option>
<option value="-">
{{ t('components.library.Radios.ordering.direction.descending') }}
</option>
</select>
</Layout>
<Layout stack noGap label for="radios-results">
<span class="label">
{{ t('components.library.Radios.pagination.results') }}
</span>
<select
id="radios-results"
v-model="paginateBy"
class="dropdown"
>
<option
v-for="opt in paginateOptions"
:key="opt"
:value="opt"
>
{{ opt }}
</option>
</select>
</Layout>
</Layout>
<Alert
blue
style="align-items: center;"
v-if="result && result.results.length === 0"
>
<i class="bi bi-broadcast-pin" style="font-size: 80px" />
<Spacer />
{{ t('components.library.Radios.empty.noResults') }}
<Spacer />
<Button
v-if="store.state.auth.authenticated"
primary
style="align-self:center;"
:to="{name: 'library.radios.build'}"
icon="bi-boombox-fill"
>
{{ t('components.library.Radios.button.add') }}
</Button>
</Alert>
<div
v-if="result && result.results.length > 0"
class="ui cards"
>
<Pagination
v-if="result && result.count > paginateBy"
v-model:page="page"
:pages="Math.ceil(result.count / paginateBy)"
<radio-card
v-if="isAuthenticated"
:type="'actor-content'"
:object-id="store.state.auth.fullUsername"
/>
<radio-card
v-for="radio in result.results"
:key="radio.id"
type="custom"
:custom-radio="radio"
v-if="isAuthenticated && hasFavorites"
:type="'favorites'"
/>
<Pagination
v-if="result && result.count > paginateBy"
v-model:page="page"
:pages="Math.ceil(result.count / paginateBy)"
<radio-card
v-if="scope === 'all'"
:type="'random'"
/>
</div>
<radio-card
v-if="scope === 'me'"
:type="'random_library'"
/>
<radio-card :type="'recently-added'" />
<radio-card
v-if="store.state.auth.authenticated && scope === 'all'"
:type="'less-listened'"
/>
<radio-card
v-if="store.state.auth.authenticated && scope === 'me'"
:type="'less-listened_library'"
/>
</Section>
<Section
alignLeft
no-items
solid
primary
icon="bi-plus"
:h2="t('components.library.Radios.header.user')"
:action="{ text:t('components.library.Radios.button.create'), to:'/library/radios/build' }"
/>
<Layout flex form
:class="['ui', {'loading': isLoading}, 'form']"
@submit.prevent="search"
>
<Input search
id="radios-search"
v-model="query"
name="search"
:label="t('components.library.Radios.label.search')"
:placeholder="labels.searchPlaceholder"
/>
<Layout stack noGap label for="radios-ordering">
<span class="label">
{{ t('components.library.Radios.ordering.label') }}
</span>
<select
id="radios-ordering"
v-model="ordering"
class="dropdown"
>
<option
v-for="(option, key) in orderingOptions"
:key="key"
:value="option[0]"
>
{{ sharedLabels.filters[option[1]] }}
</option>
</select>
</Layout>
<Layout stack noGap label for="radios-ordering-direction">
<span class="label">
{{ t('components.library.Radios.ordering.direction.label') }}
</span>
<select
id="radios-ordering-direction"
v-model="orderingDirection"
class="dropdown"
>
<option value="+">
{{ t('components.library.Radios.ordering.direction.ascending') }}
</option>
<option value="-">
{{ t('components.library.Radios.ordering.direction.descending') }}
</option>
</select>
</Layout>
<Layout stack noGap label for="radios-results">
<span class="label">
{{ t('components.library.Radios.pagination.results') }}
</span>
<select
id="radios-results"
v-model="paginateBy"
class="dropdown"
>
<option
v-for="opt in paginateOptions"
:key="opt"
:value="opt"
>
{{ opt }}
</option>
</select>
</Layout>
</Layout>
<Alert
blue
style="align-items: center;"
v-if="result && result.results.length === 0"
>
<i class="bi bi-broadcast-pin" style="font-size: 80px" />
<Spacer />
{{ t('components.library.Radios.empty.noResults') }}
<Spacer />
<Button
v-if="store.state.auth.authenticated"
primary
style="align-self:center;"
:to="{name: 'library.radios.build'}"
icon="bi-boombox-fill"
>
{{ t('components.library.Radios.button.add') }}
</Button>
</Alert>
<Layout flex
v-if="result && result.results.length > 0"
>
<Pagination
v-if="result && result.count > paginateBy"
v-model:page="page"
:pages="Math.ceil(result.count / paginateBy)"
/>
<radio-card
v-for="radio in result.results"
:key="radio.id"
type="custom"
:custom-radio="radio"
/>
<Pagination
v-if="result && result.count > paginateBy"
v-model:page="page"
:pages="Math.ceil(result.count / paginateBy)"
/>
</Layout>
</Layout>
</template>

View File

@ -39,6 +39,7 @@ const customRadioId = computed(() => props.customRadio?.id ?? null)
<Card
v-if="radio.id"
small
blue
:title="radio.name"
:to="{name: 'library.radios.detail', params: {id: radio.id}}"
>
@ -82,19 +83,20 @@ const customRadioId = computed(() => props.customRadio?.id ?? null)
<Card
v-else
small
solid
blue
icon="bi-boombox-fill"
:title="radio.name"
>
<template #topright>
</template>
<template #default>
<template #default style="background-color: red;">
<user-link
v-if="radio.user"
discrete
:user="radio.user"
:avatar="false"
/>
<Spacer />
<Spacer v-if="radio.user" />
<div
class="description"
:class="{expanded: isDescriptionExpanded}"

View File

@ -10,7 +10,13 @@ import axios from 'axios'
import TrackTable from '~/components/audio/track/Table.vue'
import RadioButton from '~/components/radios/Button.vue'
import Pagination from '~/components/vui/Pagination.vue'
import Layout from '~/components/ui/Layout.vue'
import Header from '~/components/ui/Header.vue'
import Section from '~/components/ui/Section.vue'
import Spacer from '~/components/ui/Spacer.vue'
import Pagination from '~/components/ui/Pagination.vue'
import Alert from '~/components/ui/Alert.vue'
import Button from '~/components/ui/Button.vue'
import useErrorHandler from '~/composables/useErrorHandler'
@ -66,99 +72,83 @@ const deleteRadio = async () => {
</script>
<template>
<main>
<div
<Layout stack main>
<Loader
v-if="isLoading"
v-title="labels.title"
class="ui vertical segment"
>
<div :class="['ui', 'centered', 'active', 'inline', 'loader']" />
</div>
<section
/>
<Header
v-if="!isLoading && radio"
v-title="radio.name"
class="ui head vertical center aligned stripe segment"
>
<div class="segment-content">
<h2 class="ui center aligned icon header">
<i class="circular inverted feed primary icon" />
<div class="content">
{{ radio.name }}
<div class="sub header">
{{ t('views.radios.Detail.header.radio', {tracks: totalTracks}) }}<username :username="radio.user.username" />
</div>
</div>
</h2>
<div class="ui hidden divider" />
<radio-button
type="custom"
:custom-radio-id="radio.id"
/>
<template v-if="store.state.auth.username === radio.user.username">
<router-link
class="ui icon labeled button"
:to="{name: 'library.radios.edit', params: {id: radio.id}}"
>
<i class="pencil icon" />
{{ t('views.radios.Detail.button.edit') }}
</router-link>
<dangerous-button
class="ui labeled danger icon button"
:action="deleteRadio"
>
<i class="trash icon" /> {{ t('views.radios.Detail.button.delete') }}
<template #modal-header>
<p>
{{ t('views.radios.Detail.modal.delete.header', {radio: radio.name}) }}
</p>
</template>
<template #modal-content>
<p>
{{ t('views.radios.Detail.modal.delete.content.warning') }}
</p>
</template>
<template #modal-confirm>
<p>
{{ t('views.radios.Detail.button.confirm') }}
</p>
</template>
</dangerous-button>
</template>
</div>
</section>
<section
:h1="radio.name"
/>
<h2 class="sub header">
{{ t('views.radios.Detail.header.radio', {tracks: totalTracks}) }}<username :username="radio?.user.username" />
</h2>
<Layout flex>
<radio-button
:custom-radio-id="radio?.id"
/>
<template v-if="store.state.auth.username === radio?.user.username">
<Button
icon="bi-pencil"
secondary
:to="{name: 'library.radios.edit', params: {id: radio?.id}}"
>
{{ t('views.radios.Detail.button.edit') }}
</Button>
<dangerous-button
class="ui labeled danger icon button"
:action="deleteRadio"
>
<i class="bi bi-trash" /> {{ t('views.radios.Detail.button.delete') }}
<template #modal-header>
<p>
{{ t('views.radios.Detail.modal.delete.header', {radio: radio.name}) }}
</p>
</template>
<template #modal-content>
<p>
{{ t('views.radios.Detail.modal.delete.content.warning') }}
</p>
</template>
<template #modal-confirm>
<p>
{{ t('views.radios.Detail.button.confirm') }}
</p>
</template>
</dangerous-button>
</template>
</Layout>
<Section
v-if="totalTracks > 0"
class="ui vertical stripe segment"
:h2="t('views.radios.Detail.header.tracks')"
>
<h2>
{{ t('views.radios.Detail.header.tracks') }}
</h2>
<track-table :tracks="tracks" />
<div class="ui center aligned basic segment">
<pagination
<Pagination
v-if="totalTracks > 25"
v-model:current="page"
:paginate-by="25"
:total="totalTracks"
/>
</div>
</section>
<div
</Section>
<Alert
blue
v-else-if="!isLoading && totalTracks === 0"
class="ui placeholder segment"
>
<div class="ui icon header">
<i class="rss icon" />
<Layout stack style="text-align: center;">
<i class="bi bi-broadcast-pin" style="font-size: 5em;" />
{{ t('views.radios.Detail.empty.noTracks') }}
</div>
<router-link
v-if="store.state.auth.username === radio?.user.username"
class="ui success icon labeled button"
:to="{name: 'library.radios.edit', params: { id: radio?.id }}"
>
<i class="pencil icon" />
{{ t('views.radios.Detail.button.edit') }}
</router-link>
</div>
</main>
<Button
v-if="store.state.auth.username === radio?.user.username"
primary
icon="bi-pencil"
style="align-self: center;"
:to="{name: 'library.radios.edit', params: { id: radio?.id }}"
>
{{ t('views.radios.Detail.button.edit') }}
</Button>
</Layout>
</Alert>
</Layout>
</template>