feat: enhance comment section with reply functionality and pinning feature

- Added ability to pin comments for admin users.
- Implemented nested replies with a recursive component for better organization.
- Improved comment loading with time-ago formatting for replies.
- Refactored comment fetching logic to include replies in a structured format.
- Updated UI to reflect changes in comment state (pinned, anonymous).
- Added loading indicators for comment submission and reply actions.
- Enhanced error handling for comment operations.
This commit is contained in:
ngthanhvu
2026-07-24 03:39:06 -04:00
parent 3d075f320c
commit 320eed551e
4 changed files with 501 additions and 170 deletions
+29 -23
View File
@@ -1,4 +1,5 @@
import { eq } from 'drizzle-orm'
import mysql from 'mysql2/promise'
import { comments } from '../../database/schema'
import { getTokenFromEvent, verifyToken } from '../../utils/auth'
@@ -28,30 +29,35 @@ export default defineEventHandler(async (event) => {
}
}
const db = useDb()
const dbUrl = process.env.DATABASE_URL || 'mysql://cinek:cinekpassword@localhost:3306/cinek'
const conn = await mysql.createConnection(dbUrl)
const result = await db.insert(comments).values({
userId: payload.id,
source: source || '',
slug,
movieName: movieName || null,
content: content.trim(),
parentId: parentId ? Number(parentId) : null,
spoiler: spoiler ? true : false,
anonymous: anonymous ? true : false,
})
try {
const [result] = await conn.execute(
'INSERT INTO comments (user_id, source, slug, movie_name, content, parent_id, spoiler, anonymous) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
[payload.id, source || '', slug, movieName || null, content.trim(), parentId ? Number(parentId) : null, spoiler ? 1 : 0, anonymous ? 1 : 0]
)
return {
id: result.insertId,
userId: payload.id,
source: source || '',
slug,
movieName: movieName || null,
content: content.trim(),
spoiler: spoiler ? true : false,
anonymous: anonymous ? true : false,
parentId: parentId ? Number(parentId) : null,
likeCount: 0,
dislikeCount: 0,
const insertId = (result as any).insertId
if (!insertId) {
throw createError({ statusCode: 500, message: 'Không thể tạo bình luận' })
}
return {
id: Number(insertId),
userId: payload.id,
source: source || '',
slug,
movieName: movieName || null,
content: content.trim(),
spoiler: false,
anonymous: anonymous ? true : false,
parentId: parentId ? Number(parentId) : null,
likeCount: 0,
dislikeCount: 0,
createdAt: new Date().toISOString(),
}
} finally {
await conn.end()
}
})