Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
100 lines
3.5 KiB
Vue
100 lines
3.5 KiB
Vue
<script setup lang="ts">
|
|
import { Badge } from '@/components/ui/badge'
|
|
import HallCoverStack from '@/components/HallCoverStack.vue'
|
|
import UserHoverCard from '@/components/UserHoverCard.vue'
|
|
import type { HallVisibility } from '@/components/AddToHallPopover.vue'
|
|
|
|
export interface HallCardItem {
|
|
id: string
|
|
title: string
|
|
description: string | null
|
|
visibility: HallVisibility
|
|
isStandard: boolean
|
|
userId: number
|
|
userName: string
|
|
userColor: string | null
|
|
userAvatarUrl: string | null
|
|
itemCount: number
|
|
coverThumbUrl: string | null
|
|
coverThumbUrls?: string[]
|
|
firstUploadId: string | null
|
|
}
|
|
|
|
const props = defineProps<{
|
|
hall: HallCardItem
|
|
}>()
|
|
|
|
const visibilityIcon = computed(() => {
|
|
if (props.hall.isStandard || props.hall.visibility === 'public') return 'ph:globe-fill'
|
|
if (props.hall.visibility === 'unlisted') return 'ph:link-bold'
|
|
return 'ph:lock-simple-fill'
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<NuxtLink
|
|
:to="`/halls/${hall.id}`"
|
|
class="group flex flex-col overflow-hidden rounded-md border bg-card"
|
|
>
|
|
<div class="relative aspect-video w-full overflow-hidden bg-muted">
|
|
<HallCoverStack
|
|
:thumbs="hall.coverThumbUrls"
|
|
:fallback-thumb="hall.coverThumbUrl"
|
|
:alt="hall.title"
|
|
/>
|
|
|
|
<div class="absolute top-2 left-2 z-10 flex items-center gap-1">
|
|
<Badge v-if="hall.isStandard" variant="default" class="gap-1 px-1.5 py-0 text-xs">
|
|
<Icon name="ph:star-fill" class="size-3" />
|
|
{{ $t('halls.standardBadge') }}
|
|
</Badge>
|
|
<Badge v-else variant="secondary" class="gap-1 bg-background/85 px-1.5 py-0 text-xs backdrop-blur-xs">
|
|
<Icon :name="visibilityIcon" class="size-3" />
|
|
{{ $t(`halls.visibility.${hall.visibility}`) }}
|
|
</Badge>
|
|
</div>
|
|
|
|
<!-- Hover-Overlay wie bei UploadTile auf der Mainseite -->
|
|
<div class="absolute inset-0 bg-primary/30 opacity-0 transition-opacity group-hover:opacity-100" />
|
|
<div class="absolute inset-0 flex items-end bg-gradient-to-t from-black/70 via-black/20 to-transparent p-2 opacity-0 transition-opacity group-hover:opacity-100">
|
|
<div class="flex w-full items-end justify-between gap-2 text-xs text-white">
|
|
<UserHoverCard
|
|
v-if="!hall.isStandard"
|
|
:user-id="hall.userId"
|
|
:user-name="hall.userName"
|
|
:user-color="hall.userColor"
|
|
:user-avatar-url="hall.userAvatarUrl"
|
|
class="min-w-0"
|
|
>
|
|
<span class="truncate">{{ hall.userName }}</span>
|
|
</UserHoverCard>
|
|
<span v-else />
|
|
<span class="flex shrink-0 items-center gap-1">
|
|
<Icon name="ph:images-square-fill" class="size-3.5" />
|
|
{{ hall.itemCount }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-1 flex-col gap-1 p-3">
|
|
<h3 class="line-clamp-1 font-semibold">
|
|
{{ hall.title }}
|
|
</h3>
|
|
<p v-if="hall.description" class="line-clamp-2 text-xs text-muted-foreground">
|
|
{{ hall.description }}
|
|
</p>
|
|
<div class="mt-auto flex items-center justify-between gap-2 pt-2 text-xs text-muted-foreground">
|
|
<span
|
|
v-if="!hall.isStandard"
|
|
class="truncate font-medium"
|
|
:style="hall.userColor ? { color: hall.userColor } : undefined"
|
|
>
|
|
{{ hall.userName }}
|
|
</span>
|
|
<span :class="hall.isStandard && 'ml-auto'">{{ $t('halls.itemsCount', { n: hall.itemCount }) }}</span>
|
|
</div>
|
|
</div>
|
|
</NuxtLink>
|
|
</template>
|