mirror of
https://github.com/mmahdium/TBW.git
synced 2025-12-20 04:33:54 +01:00
Compare commits
2 Commits
de8c0486cc
...
2bd1783abb
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bd1783abb | |||
| 66ab88c924 |
@@ -3,6 +3,7 @@ import { useMediaStore } from '@/stores/media'
|
||||
import { computed } from 'vue'
|
||||
import type { MediaType } from '@/types/Media'
|
||||
import ImageWithFallback from './ImageWithFallback.vue'
|
||||
import ScoreRing from './ScoreRing.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
media: MediaType
|
||||
@@ -20,7 +21,7 @@ const alreadyAdded = computed(() => store.mediaList.some((media) => media.Id ===
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="card relative w-full h-full overflow-hidden bg-white/70 backdrop-blur-md border border-gray-200/60 shadow-md hover:shadow-xl transition-all duration-300"
|
||||
class="card relative w-full h-full overflow-hidden bg-white/70 backdrop-blur-md border border-gray-200/60 shadow-xs hover:shadow-xl transition-all duration-300"
|
||||
v-motion-fade-visible-once
|
||||
>
|
||||
<!-- Poster wrapper with fixed aspect -->
|
||||
@@ -38,93 +39,96 @@ const alreadyAdded = computed(() => store.mediaList.some((media) => media.Id ===
|
||||
</router-link>
|
||||
|
||||
<!-- Radial progress placed on the right edge, centered on the seam between poster and body -->
|
||||
<div class="absolute left-3 bottom-0 z-20 translate-y-1/2" aria-hidden="true">
|
||||
<div
|
||||
class="radial-progress bg-primary text-primary-content border-primary border-4 font-bold flex items-center justify-center"
|
||||
:style="{
|
||||
'--value': props.media.VoteAverage * 10,
|
||||
'--size': '2rem',
|
||||
'--thickness': '3px',
|
||||
}"
|
||||
:aria-valuenow="props.media.VoteAverage"
|
||||
role="progressbar"
|
||||
>
|
||||
<span class="flex items-baseline text-[0.7em]">
|
||||
{{ props.media.VoteAverage === 0 ? '?' : (props.media.VoteAverage * 10).toFixed(0) }}
|
||||
<span class="text-[0.5em] relative -top-1 ml-0.5">%</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="absolute right-3 bottom-0 z-20 translate-y-1/2" aria-hidden="true">
|
||||
<ScoreRing :VoteAverage="props.media.VoteAverage" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Body: add top padding to ensure title sits below the ring seam -->
|
||||
<!-- Body -->
|
||||
<div class="card-body p-4 flex flex-col pt-6">
|
||||
<router-link
|
||||
:to="{ name: 'details', params: { type: props.media.MediaType, id: props.media.Id } }"
|
||||
class="block"
|
||||
>
|
||||
<h2
|
||||
class="card-title text-base font-semibold bg-linear-to-r from-gray-700 to-gray-500 bg-clip-text text-transparent wrap-break-word"
|
||||
class="card-title text-base font-semibold bg-linear-to-r from-gray-700 to-gray-500 bg-clip-text text-transparent break-words"
|
||||
>
|
||||
{{ props.media.Title }}
|
||||
</h2>
|
||||
<time
|
||||
class="text-sm text-gray-400"
|
||||
:datetime="new Date(props.media.ReleaseDate).toISOString()"
|
||||
:datetime="
|
||||
new Date(
|
||||
props.media.ReleaseDate === '' ? props.media.FirstAirDate : props.media.ReleaseDate,
|
||||
).toISOString()
|
||||
"
|
||||
>
|
||||
{{
|
||||
new Intl.DateTimeFormat('default', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
}).format(new Date(props.media.ReleaseDate))
|
||||
}).format(
|
||||
new Date(
|
||||
props.media.ReleaseDate === '' ? props.media.FirstAirDate : props.media.ReleaseDate,
|
||||
),
|
||||
)
|
||||
}}
|
||||
</time>
|
||||
</router-link>
|
||||
|
||||
<div class="card-actions justify-end mt-auto">
|
||||
<button
|
||||
v-motion-fade-visible-once
|
||||
v-if="!alreadyAdded"
|
||||
class="btn btn-circle btn-md px-1.5 bg-linear-to-r from-gray-100 to-gray-200 border-gray-300 text-gray-700 hover:from-gray-200 hover:to-gray-300 hover:text-gray-900 transition"
|
||||
@click="emit('add-media', props.media)"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6"
|
||||
<!-- Actions row -->
|
||||
<div class="card-actions flex items-center justify-between mt-auto">
|
||||
<!-- Type badge -->
|
||||
<span class="badge badge-outline text-xs font-medium px-2 py-1">
|
||||
{{ props.media.MediaType === 'movie' ? 'Movie' : 'Show' }}
|
||||
</span>
|
||||
|
||||
<!-- Bookmark buttons -->
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
v-motion-fade-visible-once
|
||||
v-if="!alreadyAdded"
|
||||
class="btn btn-circle btn-md px-1.5 bg-linear-to-r from-gray-100 to-gray-200 border-gray-300 text-gray-700 hover:from-gray-200 hover:to-gray-300 hover:text-gray-900 transition"
|
||||
@click="emit('add-media', props.media)"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0 1 11.186 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
v-motion-fade-visible-once
|
||||
v-else
|
||||
class="btn btn-circle btn-md px-1.5 bg-linear-to-r from-red-50 to-red-100 border border-red-200 text-red-600 hover:from-red-100 hover:to-red-200 hover:text-red-700 transition"
|
||||
@click="emit('remove-media', props.media.Id)"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6"
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0 1 11.186 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
v-motion-fade-visible-once
|
||||
v-else
|
||||
class="btn btn-circle btn-md px-1.5 bg-linear-to-r from-red-50 to-red-100 border border-red-200 text-red-600 hover:from-red-100 hover:to-red-200 hover:text-red-700 transition"
|
||||
@click="emit('remove-media', props.media.Id)"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m3 3 1.664 1.664M21 21l-1.5-1.5m-5.485-1.242L12 17.25 4.5 21V8.742m.164-4.078a2.15 2.15 0 0 1 1.743-1.342 48.507 48.507 0 0 1 11.186 0c1.1.128 1.907 1.077 1.907 2.185V19.5M4.664 4.664 19.5 19.5"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m3 3 1.664 1.664M21 21l-1.5-1.5m-5.485-1.242L12 17.25 4.5 21V8.742m.164-4.078a2.15 2.15 0 0 1 1.743-1.342 48.507 48.507 0 0 1 11.186 0c1.1.128 1.907 1.077 1.907 2.185V19.5M4.664 4.664 19.5 19.5"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
21
src/components/ScoreRing.vue
Executable file
21
src/components/ScoreRing.vue
Executable file
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{ VoteAverage: number }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="radial-progress bg-primary text-primary-content border-primary border-4 font-bold flex items-center justify-center"
|
||||
:style="{
|
||||
'--value': props.VoteAverage * 10,
|
||||
'--size': '2rem',
|
||||
'--thickness': '3px',
|
||||
}"
|
||||
:aria-valuenow="props.VoteAverage"
|
||||
role="progressbar"
|
||||
>
|
||||
<span class="flex items-baseline text-[0.7em]">
|
||||
{{ props.VoteAverage === 0 ? '?' : (props.VoteAverage * 10).toFixed(0) }}
|
||||
<span class="text-[0.5em] relative -top-1 ml-0.5">%</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -41,7 +41,7 @@ export const loadMedia = async (
|
||||
const responses = await Promise.all(requests)
|
||||
|
||||
// If only one type was requested, handle that directly
|
||||
if (filters.onlyMovies) {
|
||||
if (filters.onlyMovies && !filters.onlySeries) {
|
||||
const movieSearch = responses[0]
|
||||
if (movieSearch.status !== 200) {
|
||||
return {
|
||||
@@ -62,7 +62,7 @@ export const loadMedia = async (
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.onlySeries) {
|
||||
if (filters.onlySeries && !filters.onlyMovies) {
|
||||
const tvSearch = responses[0]
|
||||
if (tvSearch.status !== 200) {
|
||||
return {
|
||||
|
||||
@@ -41,7 +41,6 @@ export const useSearchPageStore = defineStore('searchPage', () => {
|
||||
searchQuery.value = query
|
||||
mediaList.value = medias
|
||||
searchFilters.value = filters
|
||||
console.log('setState', page, query, medias, filters)
|
||||
}
|
||||
return { searchPage, searchQuery, mediaList, searchFilters, setState }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user