태그를 관리용/일반용으로 분리하고 관리자 드래그 정렬을 추가.
댓글/회원/관리자 인증·프로필 흐름 보완과 관련 마이그레이션 및 문서를 함께 반영해 운영 동선을 안정화. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -59,7 +59,8 @@ const mapTagRow = (row) => ({
|
||||
slug: row.slug,
|
||||
description: row.description,
|
||||
sortOrder: row.sort_order,
|
||||
color: row.color
|
||||
color: row.color,
|
||||
tagType: row.tag_type || 'managed'
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -532,22 +533,45 @@ export const getPageBySlug = async (slug) => {
|
||||
* 공개 태그 목록 조회
|
||||
* @returns {Promise<Array>} 태그 목록
|
||||
*/
|
||||
export const listTags = async () => {
|
||||
export const listTags = async ({ tagType } = {}) => {
|
||||
const sql = getPostgresClient()
|
||||
|
||||
if (!sql) {
|
||||
return getSampleTags()
|
||||
const sampleTags = getSampleTags().map((tag) => ({
|
||||
...tag,
|
||||
tagType: 'managed'
|
||||
}))
|
||||
if (!tagType) {
|
||||
return sampleTags
|
||||
}
|
||||
return sampleTags.filter((tag) => tag.tagType === tagType)
|
||||
}
|
||||
|
||||
const rows = await sql`
|
||||
SELECT *
|
||||
FROM tags
|
||||
ORDER BY sort_order ASC, name ASC
|
||||
`
|
||||
const rows = tagType
|
||||
? await sql`
|
||||
SELECT *
|
||||
FROM tags
|
||||
WHERE tag_type = ${tagType}
|
||||
ORDER BY sort_order ASC, name ASC
|
||||
`
|
||||
: await sql`
|
||||
SELECT *
|
||||
FROM tags
|
||||
ORDER BY
|
||||
CASE tag_type WHEN 'managed' THEN 0 ELSE 1 END ASC,
|
||||
sort_order ASC,
|
||||
name ASC
|
||||
`
|
||||
|
||||
return rows.map(mapTagRow)
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 태그 목록 조회
|
||||
* @returns {Promise<Array>} 관리자 태그 목록
|
||||
*/
|
||||
export const listAdminTags = async () => listTags()
|
||||
|
||||
const SEARCH_TAG_LIMIT = 12
|
||||
const SEARCH_POST_LIMIT = 12
|
||||
const SEARCH_POST_CANDIDATE_LIMIT = 48
|
||||
@@ -823,8 +847,8 @@ export const createAdminTag = async (input) => {
|
||||
}
|
||||
|
||||
const rows = await sql`
|
||||
INSERT INTO tags (name, slug, description, sort_order, color)
|
||||
VALUES (${input.name}, ${input.slug}, ${input.description}, ${input.sortOrder}, ${input.color})
|
||||
INSERT INTO tags (name, slug, description, sort_order, color, tag_type)
|
||||
VALUES (${input.name}, ${input.slug}, ${input.description}, ${input.sortOrder}, ${input.color}, ${input.tagType})
|
||||
RETURNING *
|
||||
`
|
||||
|
||||
@@ -852,6 +876,7 @@ export const updateAdminTag = async (id, input) => {
|
||||
description = ${input.description},
|
||||
sort_order = ${input.sortOrder},
|
||||
color = ${input.color},
|
||||
tag_type = ${input.tagType},
|
||||
updated_at = now()
|
||||
WHERE id = ${id}
|
||||
RETURNING *
|
||||
@@ -860,6 +885,35 @@ export const updateAdminTag = async (id, input) => {
|
||||
return rows[0] ? mapTagRow(rows[0]) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 관리용 태그 순서를 일괄 갱신
|
||||
* @param {Array<string>} tagIds - 정렬된 태그 ID 목록
|
||||
* @returns {Promise<Array>} 갱신된 태그 목록
|
||||
*/
|
||||
export const reorderManagedTags = async (tagIds) => {
|
||||
const sql = getPostgresClient()
|
||||
|
||||
if (!sql) {
|
||||
throw new Error('DATABASE_REQUIRED')
|
||||
}
|
||||
|
||||
await sql.begin(async (transaction) => {
|
||||
for (let index = 0; index < tagIds.length; index += 1) {
|
||||
const tagId = tagIds[index]
|
||||
await transaction`
|
||||
UPDATE tags
|
||||
SET
|
||||
sort_order = ${(index + 1) * 10},
|
||||
updated_at = now()
|
||||
WHERE id = ${tagId}
|
||||
AND tag_type = 'managed'
|
||||
`
|
||||
}
|
||||
})
|
||||
|
||||
return listTags({ tagType: 'managed' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 태그 삭제
|
||||
* @param {string} id - 태그 ID
|
||||
|
||||
Reference in New Issue
Block a user