From 612629125728f3be1c7c2f643d1747186aabc2e3 Mon Sep 17 00:00:00 2001 From: dsyoon Date: Wed, 8 Apr 2026 20:20:41 +0900 Subject: [PATCH] =?UTF-8?q?feat(ai-cases-write):=20=EB=93=B1=EB=A1=9D?= =?UTF-8?q?=EB=90=9C=20=EC=82=AC=EB=A1=80=20=EB=AA=A9=EB=A1=9D=205?= =?UTF-8?q?=EA=B1=B4=20=EB=8B=A8=EC=9C=84=20=ED=8E=98=EC=9D=B4=EC=A7=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 최신순 정렬 후 페이지당 5개, 이전/다음 네비 - edit·page 쿼리 유지, 목록 영역 스타일 보강 Made-with: Cursor --- public/styles.css | 10 ++++++++++ server.js | 24 +++++++++++++++++++++++- views/ai-cases-write.ejs | 30 +++++++++++++++++++++++++++--- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/public/styles.css b/public/styles.css index 025f6f4..5919bc7 100644 --- a/public/styles.css +++ b/public/styles.css @@ -677,6 +677,16 @@ button.ai-case-inline-link:hover { margin-left: auto; } +.admin-story-list-pagination { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + margin-top: 14px; + padding-top: 12px; + border-top: 1px solid #f3f4f6; +} + .btn-sm { padding: 6px 10px; font-size: 13px; diff --git a/server.js b/server.js index a34e429..1881235 100644 --- a/server.js +++ b/server.js @@ -1167,6 +1167,8 @@ pageRouter.get("/ai-explore/task-checklist", (req, res) => opsState: normalizeOpsState(), }) ); +const AI_SUCCESS_ADMIN_LIST_PAGE_SIZE = 5; + pageRouter.get("/ai-cases/write", (req, res) => { if (!res.locals.adminMode) { return res.status(403).send( @@ -1180,11 +1182,31 @@ pageRouter.get("/ai-cases/write", (req, res) => { const m = meta.find((x) => x.slug === editSlug); if (m) story = enrichAiSuccessStory(m); } + const sortedStories = [...meta].sort((a, b) => { + const da = new Date(a.publishedAt || a.updatedAt || a.createdAt || 0); + const db = new Date(b.publishedAt || b.updatedAt || b.createdAt || 0); + return db - da; + }); + const pageRaw = req.query.page; + const pageNum = Math.max(1, parseInt(Array.isArray(pageRaw) ? pageRaw[0] : pageRaw, 10) || 1); + const totalCount = sortedStories.length; + const totalPages = Math.max(1, Math.ceil(totalCount / AI_SUCCESS_ADMIN_LIST_PAGE_SIZE)); + const currentPage = Math.min(pageNum, totalPages); + const start = (currentPage - 1) * AI_SUCCESS_ADMIN_LIST_PAGE_SIZE; + const allStories = sortedStories.slice(start, start + AI_SUCCESS_ADMIN_LIST_PAGE_SIZE); + const listPagination = { + page: currentPage, + totalPages, + totalCount, + hasPrev: currentPage > 1, + hasNext: currentPage < totalPages, + }; res.render("ai-cases-write", { activeMenu: "ai-cases", adminMode: true, story, - allStories: meta, + allStories, + listPagination, editSlug: editSlug || null, }); }); diff --git a/views/ai-cases-write.ejs b/views/ai-cases-write.ejs index 78f7c68..e3f40e6 100644 --- a/views/ai-cases-write.ejs +++ b/views/ai-cases-write.ejs @@ -46,18 +46,42 @@

슬러그는 URL에 쓰이므로 영문·숫자·하이픈만 사용하세요. 원문 PDF 경로(/public/...)가 있으면 상세는 PDF 페이지 이미지로 보여 주며, 이때 본문(Markdown)은 비워도 됩니다. PDF가 없을 때는 본문이 필수입니다.

- <% if (allStories.length) { %> + <% if (typeof listPagination !== 'undefined' && listPagination.totalCount > 0) { %>
-

등록된 사례

+
+

등록된 사례

+ <% if (listPagination.totalPages > 1) { %> + 총 <%= listPagination.totalCount %>건 · <%= listPagination.page %>/<%= listPagination.totalPages %> 페이지 + <% } else { %> + 총 <%= listPagination.totalCount %>건 + <% } %> +
+ <% if (listPagination.totalPages > 1) { %> + + <% } %>
<% } %>