관리자 글 목록 고정 기능 추가
This commit is contained in:
@@ -59,6 +59,7 @@ const mapPostRow = (row) => ({
|
||||
featuredImageThumbnail: getExistingPostThumbnailUrl(row.featured_image),
|
||||
showFeaturedImage: Boolean(row.show_featured_image),
|
||||
isFeatured: Boolean(row.is_featured),
|
||||
adminPinned: Boolean(row.admin_pinned),
|
||||
commentCount: Number(row.comment_count || 0),
|
||||
totalViews: Number(row.total_views || 0),
|
||||
seoTitle: row.seo_title || '',
|
||||
@@ -98,6 +99,7 @@ const mapAdminPostSummaryRow = (row) => ({
|
||||
featuredImageThumbnail: getExistingPostThumbnailUrl(row.featured_image),
|
||||
showFeaturedImage: Boolean(row.show_featured_image),
|
||||
isFeatured: Boolean(row.is_featured),
|
||||
adminPinned: Boolean(row.admin_pinned),
|
||||
commentCount: Number(row.comment_count || 0),
|
||||
totalViews: Number(row.total_views || 0),
|
||||
status: row.status,
|
||||
@@ -574,6 +576,10 @@ export const listAdminPosts = async (options = {}) => {
|
||||
return matchesStatus && matchesTag && matchesFeatured && matchesSearch
|
||||
})
|
||||
samplePosts.sort((left, right) => {
|
||||
if (Boolean(left.adminPinned) !== Boolean(right.adminPinned)) {
|
||||
return left.adminPinned ? -1 : 1
|
||||
}
|
||||
|
||||
const leftTime = new Date(left.sortAt || 0).getTime()
|
||||
const rightTime = new Date(right.sortAt || 0).getTime()
|
||||
|
||||
@@ -597,7 +603,8 @@ export const listAdminPosts = async (options = {}) => {
|
||||
},
|
||||
counts: {
|
||||
total: getSamplePosts().length,
|
||||
featured: getSamplePosts().filter((post) => post.isFeatured).length
|
||||
featured: getSamplePosts().filter((post) => post.isFeatured).length,
|
||||
pinned: getSamplePosts().filter((post) => post.adminPinned).length
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -612,6 +619,7 @@ export const listAdminPosts = async (options = {}) => {
|
||||
posts.featured_image,
|
||||
posts.show_featured_image,
|
||||
posts.is_featured,
|
||||
posts.admin_pinned,
|
||||
posts.status,
|
||||
posts.published_at,
|
||||
posts.created_at,
|
||||
@@ -680,11 +688,13 @@ export const listAdminPosts = async (options = {}) => {
|
||||
posts.featured_image,
|
||||
posts.show_featured_image,
|
||||
posts.is_featured,
|
||||
posts.admin_pinned,
|
||||
posts.status,
|
||||
posts.published_at,
|
||||
posts.created_at,
|
||||
posts.updated_at
|
||||
ORDER BY
|
||||
posts.admin_pinned DESC,
|
||||
CASE WHEN ${normalizedSortOrder} = 'oldest' THEN COALESCE(posts.published_at, posts.updated_at) END ASC,
|
||||
CASE WHEN ${normalizedSortOrder} <> 'oldest' THEN COALESCE(posts.published_at, posts.updated_at) END DESC,
|
||||
posts.id DESC
|
||||
@@ -699,7 +709,8 @@ export const listAdminPosts = async (options = {}) => {
|
||||
const countsRows = await sql`
|
||||
SELECT
|
||||
COUNT(*)::int AS total,
|
||||
COUNT(*) FILTER (WHERE posts.is_featured = true)::int AS featured
|
||||
COUNT(*) FILTER (WHERE posts.is_featured = true)::int AS featured,
|
||||
COUNT(*) FILTER (WHERE posts.admin_pinned = true)::int AS pinned
|
||||
FROM posts
|
||||
`
|
||||
|
||||
@@ -759,7 +770,8 @@ export const listAdminPosts = async (options = {}) => {
|
||||
},
|
||||
counts: {
|
||||
total: Number(countsRows[0]?.total || 0),
|
||||
featured: Number(countsRows[0]?.featured || 0)
|
||||
featured: Number(countsRows[0]?.featured || 0),
|
||||
pinned: Number(countsRows[0]?.pinned || 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -944,6 +956,31 @@ export const updateAdminPostFeatured = async (id, isFeatured) => {
|
||||
return rows[0] ? getAdminPostById(rows[0].id) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 게시물 고정 상태 수정
|
||||
* @param {string} id - 게시물 ID
|
||||
* @param {boolean} adminPinned - 관리자 목록 상단 고정 여부
|
||||
* @returns {Promise<Object | null>} 수정된 게시물
|
||||
*/
|
||||
export const updateAdminPostPinned = async (id, adminPinned) => {
|
||||
const sql = getPostgresClient()
|
||||
|
||||
if (!sql) {
|
||||
throw new Error('DATABASE_REQUIRED')
|
||||
}
|
||||
|
||||
const rows = await sql`
|
||||
UPDATE posts
|
||||
SET
|
||||
admin_pinned = ${Boolean(adminPinned)},
|
||||
updated_at = now()
|
||||
WHERE id = ${id}
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
return rows[0] ? getAdminPostById(rows[0].id) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 게시물 삭제
|
||||
* @param {string} id - 게시물 ID
|
||||
|
||||
45
server/routes/admin/api/posts/[id]/pinned.put.js
Normal file
45
server/routes/admin/api/posts/[id]/pinned.put.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { createError, getRouterParam, readBody } from 'h3'
|
||||
import { z } from 'zod'
|
||||
import { requireAdminSession } from '../../../../../utils/admin-auth'
|
||||
import { updateAdminPostPinned } from '../../../../../repositories/content-repository'
|
||||
|
||||
const pinnedInputSchema = z.object({
|
||||
adminPinned: z.boolean()
|
||||
})
|
||||
|
||||
/**
|
||||
* 관리자 게시물 목록 고정 상태 변경 API
|
||||
* @param {import('h3').H3Event} event - 요청 이벤트
|
||||
* @returns {Promise<Object>} 수정된 게시물
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
requireAdminSession(event)
|
||||
|
||||
const id = String(getRouterParam(event, 'id') || '').trim()
|
||||
const parsedBody = pinnedInputSchema.safeParse(await readBody(event))
|
||||
|
||||
if (!id) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '게시물 ID가 필요합니다.'
|
||||
})
|
||||
}
|
||||
|
||||
if (!parsedBody.success) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: '고정 상태 입력 형식이 올바르지 않습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
const post = await updateAdminPostPinned(id, parsedBody.data.adminPinned)
|
||||
|
||||
if (!post) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
message: '게시물을 찾을 수 없습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
return post
|
||||
})
|
||||
Reference in New Issue
Block a user