60 lines
2.0 KiB
Vue
60 lines
2.0 KiB
Vue
<script setup lang="ts">
|
|
import type { WebNovel } from '~/types'
|
|
|
|
interface Props {
|
|
novels?: WebNovel[]
|
|
loading?: boolean
|
|
}
|
|
|
|
withDefaults(defineProps<Props>(), {
|
|
loading: false
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<div class="flex items-center justify-between mb-6">
|
|
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">
|
|
Popular Now
|
|
</h2>
|
|
<NuxtLink to="/novels" class="text-sm text-blue-600 dark:text-blue-400 hover:underline">
|
|
Browse All
|
|
</NuxtLink>
|
|
</div>
|
|
|
|
<div v-if="loading" class="space-y-3">
|
|
<USkeleton v-for="i in 3" :key="i" class="h-20 rounded-lg" />
|
|
</div>
|
|
|
|
<div v-else-if="novels && novels.length > 0" class="space-y-3">
|
|
<div
|
|
v-for="(novel, idx) in novels.slice(0, 5)"
|
|
:key="novel.id"
|
|
class="flex items-center gap-4 p-4 rounded-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800 transition cursor-pointer"
|
|
>
|
|
<div class="flex-shrink-0">
|
|
<span class="inline-flex items-center justify-center h-8 w-8 rounded-full bg-blue-100 dark:bg-blue-900 text-sm font-semibold text-blue-600 dark:text-blue-300">
|
|
{{ idx + 1 }}
|
|
</span>
|
|
</div>
|
|
<NuxtLink :to="`/novels/${novel.slug}`" class="flex-1 min-w-0">
|
|
<p class="text-sm font-semibold text-gray-900 dark:text-white truncate">
|
|
{{ novel.title }}
|
|
</p>
|
|
<p class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ novel.author }} • {{ novel.views }}M views
|
|
</p>
|
|
</NuxtLink>
|
|
<div class="flex-shrink-0 flex items-center gap-1">
|
|
<UIcon name="i-lucide-star" class="w-4 h-4 text-yellow-400" />
|
|
<span class="text-sm font-semibold text-gray-900 dark:text-white">{{ novel.rating }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else class="py-8 text-center text-gray-500 dark:text-gray-400">
|
|
<p>No novels available</p>
|
|
</div>
|
|
</div>
|
|
</template>
|