mirror of
https://github.com/ngthanhvu/kr-phim.git
synced 2026-08-10 15:27:46 +00:00
feat: fix lại mấy cái ở comuniti section
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
faArrowLeft,
|
faArrowLeft,
|
||||||
|
faArrowUp,
|
||||||
faArrowTurnDown,
|
faArrowTurnDown,
|
||||||
faArrowTrendDown,
|
faArrowTrendDown,
|
||||||
faArrowTrendUp,
|
faArrowTrendUp,
|
||||||
@@ -92,6 +93,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'
|
|||||||
|
|
||||||
library.add(
|
library.add(
|
||||||
faArrowLeft,
|
faArrowLeft,
|
||||||
|
faArrowUp,
|
||||||
faArrowTurnDown,
|
faArrowTurnDown,
|
||||||
faArrowTrendDown,
|
faArrowTrendDown,
|
||||||
faArrowTrendUp,
|
faArrowTrendUp,
|
||||||
|
|||||||
+22
-17
@@ -617,17 +617,18 @@ useHead({
|
|||||||
<section class="relative overflow-visible rounded-2xl border border-white/15 bg-[#0E111A]">
|
<section class="relative overflow-visible rounded-2xl border border-white/15 bg-[#0E111A]">
|
||||||
<!-- Bình luận mới -->
|
<!-- Bình luận mới -->
|
||||||
<div v-if="recentComments.length" class="relative border-b border-white/10 px-5 py-5 sm:px-8 sm:py-6 lg:px-12">
|
<div v-if="recentComments.length" class="relative border-b border-white/10 px-5 py-5 sm:px-8 sm:py-6 lg:px-12">
|
||||||
<div class="mb-5 flex items-center gap-3">
|
<div class="mb-4 flex items-center gap-3 lg:mb-5">
|
||||||
<AppIcon name="message-circle" class="size-6 text-yellow-400" />
|
<AppIcon name="message-circle" class="size-5 text-yellow-400 lg:size-6" />
|
||||||
<h2 class="text-2xl font-black uppercase text-white">Bình luận mới</h2>
|
<h2 class="text-base font-black uppercase text-white sm:text-lg lg:text-2xl">Bình luận mới</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div ref="commentsCarouselRef" class="no-scrollbar flex snap-x snap-mandatory gap-4 overflow-x-auto pb-2">
|
<div ref="commentsCarouselRef"
|
||||||
|
class="no-scrollbar flex snap-x snap-mandatory gap-4 overflow-x-auto overscroll-x-contain scroll-smooth pb-2">
|
||||||
<NuxtLink v-for="comment in recentComments" :key="comment.id" :to="movieLinkFromSection(comment.movie)"
|
<NuxtLink v-for="comment in recentComments" :key="comment.id" :to="movieLinkFromSection(comment.movie)"
|
||||||
class="group relative h-50 snap-start overflow-hidden rounded-2xl bg-[#181a22]
|
class="group relative h-48 min-w-[82%] snap-start overflow-hidden rounded-xl bg-[#181a22]
|
||||||
transition-all duration-800 ease-in-out sm:min-w-60 md:min-w-65 lg:min-w-67.5
|
transition-all duration-800 ease-in-out sm:h-50 sm:min-w-60 sm:rounded-2xl md:min-w-65
|
||||||
xl:min-w-72.5 2xl:min-w-77.5">
|
lg:min-w-67.5 xl:min-w-72.5 2xl:min-w-77.5">
|
||||||
<!-- Blurred background -->
|
<!-- Blurred background -->
|
||||||
<div v-if="comment.movie?.poster"
|
<div v-if="comment.movie?.poster"
|
||||||
class="absolute inset-0 transition-all duration-700 ease-in-out group-hover:blur-sm group-hover:saturate-150"
|
class="absolute inset-0 transition-all duration-700 ease-in-out group-hover:blur-sm group-hover:saturate-150"
|
||||||
@@ -654,17 +655,18 @@ useHead({
|
|||||||
<div class="flex items-end justify-between">
|
<div class="flex items-end justify-between">
|
||||||
<!-- Avatar -->
|
<!-- Avatar -->
|
||||||
<img v-if="comment.user?.avatar" :src="comment.user.avatar" :alt="comment.user?.name"
|
<img v-if="comment.user?.avatar" :src="comment.user.avatar" :alt="comment.user?.name"
|
||||||
class="mt-auto size-12 shrink-0 rounded-full object-cover shadow-md ring-2 ring-white/20">
|
class="mt-auto size-10 shrink-0 rounded-full object-cover shadow-md ring-2 ring-white/20 sm:size-12">
|
||||||
|
|
||||||
<!-- Fallback Avatar -->
|
<!-- Fallback Avatar -->
|
||||||
<div v-else class="mt-auto grid size-12 shrink-0 place-items-center rounded-full
|
<div v-else class="mt-auto grid size-10 shrink-0 place-items-center rounded-full
|
||||||
bg-linear-to-br from-yellow-400 to-orange-500 text-lg font-black text-slate-950
|
bg-linear-to-br from-yellow-400 to-orange-500 text-base font-black text-slate-950
|
||||||
shadow-md ring-2 ring-white/20">
|
shadow-md ring-2 ring-white/20 sm:size-12 sm:text-lg">
|
||||||
{{ (comment.user?.name || 'A').charAt(0).toUpperCase() }}
|
{{ (comment.user?.name || 'A').charAt(0).toUpperCase() }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Movie Poster -->
|
<!-- Movie Poster -->
|
||||||
<div class="h-19 w-13 shrink-0 overflow-hidden rounded-lg shadow-md ring-1 ring-white/10">
|
<div
|
||||||
|
class="h-18 w-12 shrink-0 overflow-hidden rounded-lg shadow-md ring-1 ring-white/10 sm:h-19 sm:w-13">
|
||||||
<img v-if="comment.movie?.poster" :src="comment.movie.poster" :alt="comment.movie?.name"
|
<img v-if="comment.movie?.poster" :src="comment.movie.poster" :alt="comment.movie?.name"
|
||||||
class="h-full w-full object-cover">
|
class="h-full w-full object-cover">
|
||||||
<img v-else-if="comment.movie?.thumb" :src="comment.movie.thumb" :alt="comment.movie?.name"
|
<img v-else-if="comment.movie?.thumb" :src="comment.movie.thumb" :alt="comment.movie?.name"
|
||||||
@@ -728,9 +730,11 @@ useHead({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Top sections -->
|
<!-- Top sections -->
|
||||||
<div class="grid items-stretch lg:grid-cols-3">
|
<div
|
||||||
|
class="no-scrollbar flex snap-x snap-mandatory items-stretch overflow-x-auto overscroll-x-contain scroll-smooth lg:grid lg:grid-cols-3 lg:overflow-visible">
|
||||||
<!-- Sôi nổi nhất -->
|
<!-- Sôi nổi nhất -->
|
||||||
<div class="flex h-full flex-col p-5 sm:p-6 lg:border-r lg:border-white/10">
|
<div
|
||||||
|
class="flex h-full min-w-[92%] snap-start flex-col p-5 sm:min-w-[70%] sm:p-6 lg:min-w-0 lg:border-r lg:border-white/10">
|
||||||
<div class="mb-3 flex items-center gap-2">
|
<div class="mb-3 flex items-center gap-2">
|
||||||
<AppIcon name="zap" class="size-4 text-orange-500" />
|
<AppIcon name="zap" class="size-4 text-orange-500" />
|
||||||
<h2 class="text-sm font-black uppercase text-white">
|
<h2 class="text-sm font-black uppercase text-white">
|
||||||
@@ -773,8 +777,8 @@ useHead({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Yêu thích nhất -->
|
<!-- Yêu thích nhất -->
|
||||||
<div class="flex h-full flex-col border-t border-white/10 p-5 sm:p-6
|
<div class="flex h-full min-w-[92%] snap-start flex-col border-l border-white/10 p-5
|
||||||
lg:border-t-0 lg:border-r lg:border-white/10">
|
sm:min-w-[70%] sm:p-6 lg:min-w-0 lg:border-l-0 lg:border-r lg:border-white/10">
|
||||||
<div class="mb-3 flex items-center gap-2">
|
<div class="mb-3 flex items-center gap-2">
|
||||||
<AppIcon name="heart" class="size-4 text-pink-500" />
|
<AppIcon name="heart" class="size-4 text-pink-500" />
|
||||||
<h2 class="text-sm font-black uppercase text-white">
|
<h2 class="text-sm font-black uppercase text-white">
|
||||||
@@ -817,7 +821,8 @@ useHead({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Thể loại hot -->
|
<!-- Thể loại hot -->
|
||||||
<div class="flex h-full flex-col border-t border-white/10 p-5 sm:p-6 lg:border-t-0">
|
<div
|
||||||
|
class="flex h-full min-w-[92%] snap-start flex-col border-l border-white/10 p-5 sm:min-w-[70%] sm:p-6 lg:min-w-0 lg:border-l-0">
|
||||||
<div class="mb-3 flex items-center gap-2">
|
<div class="mb-3 flex items-center gap-2">
|
||||||
<AppIcon name="layers" class="size-4 text-yellow-500" />
|
<AppIcon name="layers" class="size-4 text-yellow-500" />
|
||||||
<h2 class="text-sm font-black uppercase text-white">
|
<h2 class="text-sm font-black uppercase text-white">
|
||||||
|
|||||||
@@ -4,8 +4,28 @@ import { comments, users, movies } from '../../database/schema'
|
|||||||
export default defineEventHandler(async () => {
|
export default defineEventHandler(async () => {
|
||||||
const db = useDb()
|
const db = useDb()
|
||||||
|
|
||||||
const [recentComments, trending, mostRated, allCategories] = await Promise.all([
|
// Comment counts per movie (top-level only)
|
||||||
// Recent comments (top-level only) with user and movie info
|
const commentCountsRows = await db
|
||||||
|
.select({
|
||||||
|
source: comments.source,
|
||||||
|
slug: comments.slug,
|
||||||
|
cCount: count(),
|
||||||
|
})
|
||||||
|
.from(comments)
|
||||||
|
.where(isNull(comments.parentId))
|
||||||
|
.groupBy(comments.source, comments.slug)
|
||||||
|
|
||||||
|
const commentCountMap = new Map<string, number>()
|
||||||
|
for (const row of commentCountsRows) {
|
||||||
|
commentCountMap.set(`${row.source}:${row.slug}`, Number(row.cCount))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCommentCount(movie: any) {
|
||||||
|
return commentCountMap.get(`${movie.source}:${movie.slug}`) || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const [recentComments, rawMovies, allCategories] = await Promise.all([
|
||||||
|
// Recent comments
|
||||||
db
|
db
|
||||||
.select({
|
.select({
|
||||||
id: comments.id,
|
id: comments.id,
|
||||||
@@ -31,7 +51,7 @@ export default defineEventHandler(async () => {
|
|||||||
.orderBy(desc(comments.createdAt))
|
.orderBy(desc(comments.createdAt))
|
||||||
.limit(10),
|
.limit(10),
|
||||||
|
|
||||||
// Trending by views
|
// All active movies (used for trending + most rated in JS)
|
||||||
db
|
db
|
||||||
.select({
|
.select({
|
||||||
source: movies.source,
|
source: movies.source,
|
||||||
@@ -44,34 +64,27 @@ export default defineEventHandler(async () => {
|
|||||||
rating: movies.rating,
|
rating: movies.rating,
|
||||||
})
|
})
|
||||||
.from(movies)
|
.from(movies)
|
||||||
.where(eq(movies.active, true))
|
.where(eq(movies.active, true)),
|
||||||
.orderBy(desc(movies.views), desc(movies.rating))
|
|
||||||
.limit(5),
|
|
||||||
|
|
||||||
// Most rated / favorite
|
// Categories for hot genres
|
||||||
db
|
|
||||||
.select({
|
|
||||||
source: movies.source,
|
|
||||||
slug: movies.slug,
|
|
||||||
name: movies.name,
|
|
||||||
originName: movies.originName,
|
|
||||||
thumb: movies.thumb,
|
|
||||||
poster: movies.poster,
|
|
||||||
views: movies.views,
|
|
||||||
rating: movies.rating,
|
|
||||||
})
|
|
||||||
.from(movies)
|
|
||||||
.where(eq(movies.active, true))
|
|
||||||
.orderBy(desc(movies.rating), desc(movies.views))
|
|
||||||
.limit(5),
|
|
||||||
|
|
||||||
// All categories for hot genres calculation
|
|
||||||
db
|
db
|
||||||
.select({ categories: movies.categories })
|
.select({ categories: movies.categories })
|
||||||
.from(movies)
|
.from(movies)
|
||||||
.where(eq(movies.active, true)),
|
.where(eq(movies.active, true)),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// Trending: views + commentCount * 50
|
||||||
|
const trendingWithScore = rawMovies.map(m => ({
|
||||||
|
...m,
|
||||||
|
_score: Number(m.views) + getCommentCount(m) * 50,
|
||||||
|
})).sort((a, b) => b._score - a._score).slice(0, 5)
|
||||||
|
|
||||||
|
// Most rated: rating + commentCount * 2
|
||||||
|
const mostRatedWithScore = rawMovies.map(m => ({
|
||||||
|
...m,
|
||||||
|
_score: Number(m.rating) + getCommentCount(m) * 2,
|
||||||
|
})).sort((a, b) => b._score - a._score).slice(0, 5)
|
||||||
|
|
||||||
// Count replies for each recent comment
|
// Count replies for each recent comment
|
||||||
const commentIds = recentComments.map(c => c.id)
|
const commentIds = recentComments.map(c => c.id)
|
||||||
let replyCounts: Record<number, number> = {}
|
let replyCounts: Record<number, number> = {}
|
||||||
@@ -124,7 +137,7 @@ export default defineEventHandler(async () => {
|
|||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
})),
|
})),
|
||||||
trending: trending.map(m => ({
|
trending: trendingWithScore.map(m => ({
|
||||||
source: m.source,
|
source: m.source,
|
||||||
slug: m.slug,
|
slug: m.slug,
|
||||||
name: m.name,
|
name: m.name,
|
||||||
@@ -134,7 +147,7 @@ export default defineEventHandler(async () => {
|
|||||||
views: m.views,
|
views: m.views,
|
||||||
rating: m.rating,
|
rating: m.rating,
|
||||||
})),
|
})),
|
||||||
mostRated: mostRated.map(m => ({
|
mostRated: mostRatedWithScore.map(m => ({
|
||||||
source: m.source,
|
source: m.source,
|
||||||
slug: m.slug,
|
slug: m.slug,
|
||||||
name: m.name,
|
name: m.name,
|
||||||
|
|||||||
Reference in New Issue
Block a user