Files
BRETT/app/components/HallCard.vue
T
FliesentischKingandDevin 8a0c3f57d5 Apply UI guidelines to admin, halls, stats, channels, users and login pages
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-24 18:44:25 +02:00

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>