Files
Gnuboard7/templates/_bundled/sirsoft-basic/layouts/users/posts.json
T
HeuJung 0d1105abfc fix(basic,admin_basic): 상한 목록의 페이저 소비단 정합 및 화면 표기
저장소가 총 건수를 상한까지만 세게 되면서 마지막 페이지 번호가 없는 목록이
생겼는데, 화면은 그 값을 1 로 접거나 페이저 노출 조건으로만 쓰고 있었다. 앞은
"1페이지뿐" 으로 잘못 말하고 뒤는 페이저를 통째로 지워 뒤쪽 페이지로 갈 방법
자체를 없앤다. 둘 다 예외도 404 도 내지 않아 화면은 정상으로 보이고 기능만 없다.

Pagination 컴포넌트가 세 형태를 모두 이해하도록 넓혔다. 마지막 페이지를 알면
페이지 산술이 우선이고, 모를 때만 다음 페이지 존재 여부로 판정한다. 처음·마지막
버튼을 따로 켜고 끌 수 있게 나눠, 마지막 페이지를 모르는 목록에서는 그 버튼만
감춰지고 나머지 이동은 그대로 열려 있다. DataGrid 는 페이저를 그리는 두 경로
모두에 그 값을 전달한다 — 한쪽만 전달하면 그 화면만 첫 페이지에 갇힌다.

마이페이지 주문·마일리지·위시리스트·알림함, 게시판 목록, 상품 리뷰·문의, 내가 쓴
글, 관리자 설정 탭과 스케줄 이력의 소비 지점을 전건 옮겼다.

검색 화면은 총 건수를 정확히 세지 못한 경우 "N건 이상" 으로 표기하고 검색어를
좁히면 정확한 건수를 볼 수 있다고 안내한다. 탭 배지도 세지 못한 값을 정확한
숫자처럼 보여주지 않는다. 최신순은 첫 페이지부터 커서로 이동하며, 주소로 특정
페이지를 지목한 딥링크는 종전 동작을 유지한다.

쇼핑 첫 화면과 분류 화면을 단일 요청으로 재배선하고, 섹션 partial 이 부모가
선언한 이름을 읽도록 통일했다. 데이터소스 이름에 직결돼 있으면 다른 화면이
재사용할 때 폴백에 걸려 예외도 로그도 없이 섹션이 사라진다. 상품 상세의 "최근 본
상품" 이 계속 비어 있던 것은 존재하지 않는 라우트 파라미터를 읽고 있어서였다.

게시판 목록 세 스킨의 블라인드 아이콘 대비와 이미지 위 공통 색의 다크 모드 표기를
정리했다.
2026-08-06 11:13:53 +09:00

503 lines
21 KiB
JSON

{
"version": "1.0.0",
"layout_name": "users/posts",
"extends": "_user_base",
"permissions": [],
"meta": {
"seo": {
"enabled": true,
"data_sources": ["userProfile", "userPosts"],
"priority": 0.3,
"changefreq": "weekly",
"og": {
"type": "profile"
}
}
},
"data_sources": [
{
"id": "userProfile",
"label_key": "$t:editor.data_source.userProfile",
"type": "api",
"endpoint": "/api/users/{{route.userId}}/profile",
"method": "GET",
"auto_fetch": true,
"auth_required": true,
"refetchOnMount": true,
"loading_strategy": "progressive",
"errorHandling": {
"404": {
"handler": "showErrorPage",
"params": {
"target": "content"
}
}
}
},
{
"id": "userPosts",
"label_key": "$t:editor.data_source.userPosts",
"type": "api",
"endpoint": "/api/modules/sirsoft-board/users/{{route.userId}}/posts",
"method": "GET",
"auto_fetch": true,
"auth_required": false,
"loading_strategy": "progressive",
"params": {
"page": "{{query.page || 1}}",
"per_page": 20
}
}
],
"slots": {
"content": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "min-h-screen bg-gray-50 dark:bg-gray-900"
},
"children": [
{
"type": "layout",
"name": "Container",
"props": {
"className": "py-8"
},
"responsive": {
"portable": {
"props": {
"className": "py-4"
}
}
},
"children": [
{
"comment": "로딩 상태",
"type": "basic",
"name": "Div",
"if": "{{userPosts.loading && !userPosts.data}}",
"props": {
"className": "flex justify-center items-center py-20"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 dark:border-white"
}
}
]
},
{
"comment": "게시글 목록",
"type": "basic",
"name": "Div",
"if": "{{userPosts.data && !userPosts.error}}",
"props": {
"className": "space-y-6"
},
"children": [
{
"comment": "브레드크럼 (1행)",
"type": "basic",
"name": "Div",
"props": {
"className": "flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 mb-2"
},
"children": [
{
"type": "basic",
"name": "Button",
"props": {
"className": "hover:text-blue-600 dark:hover:text-blue-400 cursor-pointer transition-colors"
},
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/users/{{route.userId}}"
}
}
],
"text": "{{userProfile.data?.name ?? ''}} $t:user.profile.back_to_profile"
},
{
"type": "basic",
"name": "Span",
"text": ">"
},
{
"type": "basic",
"name": "Span",
"props": {
"className": "text-gray-900 dark:text-white font-medium"
},
"text": "$t:user.profile.posts_page_title"
}
]
},
{
"comment": "타이틀 + 총 게시글 수 (2행, 3행)",
"type": "basic",
"name": "Div",
"props": {
"className": "mb-4"
},
"children": [
{
"comment": "2행: 타이틀",
"type": "basic",
"name": "Div",
"props": {
"className": "flex items-center gap-2"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "file-lines",
"size": "xl",
"className": "text-gray-900 dark:text-white"
}
},
{
"type": "basic",
"name": "H1",
"props": {
"className": "text-2xl font-bold text-gray-900 dark:text-white"
},
"text": "{{userProfile.data?.name ?? ''}}$t:user.profile.posts_suffix"
}
]
},
{
"comment": "3행: 총 게시글 수",
"type": "basic",
"name": "Span",
"props": {
"className": "text-sm text-gray-500 dark:text-gray-400 mt-1 block text-right"
},
"text": "$t:user.profile.total_posts|count={{userPosts.data?.total ?? 0}}"
}
]
},
{
"comment": "게시글 테이블",
"type": "basic",
"name": "Div",
"props": {
"className": "bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden"
},
"children": [
{
"comment": "게시글 없음",
"type": "basic",
"name": "Div",
"if": "{{!userPosts.data?.data?.length}}",
"props": {
"className": "text-center py-12"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "file-circle-xmark",
"size": "2xl",
"className": "text-gray-300 dark:text-gray-600 mb-4"
}
},
{
"type": "basic",
"name": "P",
"props": {
"className": "text-gray-500 dark:text-gray-400"
},
"text": "$t:user.profile.no_posts"
}
]
},
{
"comment": "테이블 헤더",
"type": "basic",
"name": "Div",
"if": "{{userPosts.data?.data?.length > 0}}",
"props": {
"className": "hidden md:grid grid-cols-12 gap-2 px-4 py-3 bg-gray-50 dark:bg-gray-700/50 border-b border-gray-200 dark:border-gray-700 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
},
"children": [
{
"type": "basic",
"name": "Div",
"props": {
"className": "col-span-2"
},
"text": "$t:user.profile.post_board"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "col-span-6"
},
"text": "$t:user.profile.post_title"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "col-span-1 text-center"
},
"text": "$t:user.profile.post_views"
},
{
"type": "basic",
"name": "Div",
"props": {
"className": "col-span-3 text-right"
},
"text": "$t:user.profile.post_date"
}
]
},
{
"comment": "게시글 목록 (한 줄 표시)",
"type": "basic",
"name": "Div",
"if": "{{userPosts.data?.data?.length > 0}}",
"props": {
"className": "divide-y divide-gray-200 dark:divide-gray-700"
},
"children": [
{
"type": "basic",
"name": "Button",
"iteration": {
"source": "{{userPosts.data?.data ?? []}}",
"item_var": "post"
},
"props": {
"className": "w-full text-left grid grid-cols-1 md:grid-cols-12 gap-2 px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors cursor-pointer items-center"
},
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/board/{{post.board_slug}}/{{post.id}}"
}
}
],
"children": [
{
"comment": "게시판명 컬럼",
"type": "basic",
"name": "Div",
"props": {
"className": "hidden md:block col-span-2 text-sm text-gray-600 dark:text-gray-400 truncate"
},
"text": "{{post.board_name}}"
},
{
"comment": "제목 영역 (배지 + 제목 + 댓글수)",
"type": "basic",
"name": "Div",
"props": {
"className": "col-span-1 md:col-span-6 flex items-center gap-2 min-w-0 overflow-hidden"
},
"children": [
{
"comment": "게시판명 배지 (모바일용)",
"type": "basic",
"name": "Span",
"props": {
"className": "md:hidden flex-shrink-0 text-xs px-1.5 py-0.5 bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300 rounded whitespace-nowrap"
},
"text": "{{post.board_name}}"
},
{
"comment": "카테고리 배지",
"type": "basic",
"name": "Span",
"if": "{{post.category}}",
"props": {
"className": "flex-shrink-0 text-xs px-1.5 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 rounded whitespace-nowrap"
},
"text": "{{post.category}}"
},
{
"comment": "비밀글 아이콘",
"type": "basic",
"name": "Icon",
"if": "{{post.is_secret}}",
"props": {
"name": "lock",
"size": "xs",
"className": "flex-shrink-0 text-yellow-500 dark:text-yellow-400"
}
},
{
"comment": "블라인드 아이콘",
"type": "basic",
"name": "Icon",
"if": "{{post.status === 'blinded'}}",
"props": {
"name": "eye-slash",
"size": "xs",
"className": "flex-shrink-0 text-orange-500 dark:text-orange-400"
}
},
{
"comment": "제목 + 댓글수 컨테이너",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-1 min-w-0 flex items-center gap-1"
},
"children": [
{
"comment": "제목",
"type": "basic",
"name": "Span",
"props": {
"className": "truncate font-medium text-gray-900 dark:text-white"
},
"text": "{{post.title}}"
},
{
"comment": "댓글 수",
"type": "basic",
"name": "Span",
"if": "{{post.comment_count > 0}}",
"props": {
"className": "flex-shrink-0 text-xs text-blue-600 dark:text-blue-400 font-medium"
},
"text": "[{{post.comment_count}}]"
}
]
}
]
},
{
"comment": "조회수",
"type": "basic",
"name": "Div",
"props": {
"className": "hidden md:flex col-span-1 justify-center items-center gap-1 text-sm text-gray-500 dark:text-gray-400"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "eye",
"size": "xs"
}
},
{
"type": "basic",
"name": "Span",
"text": "{{post.view_count ?? 0}}"
}
]
},
{
"comment": "작성일",
"type": "basic",
"name": "Span",
"props": {
"className": "hidden md:block col-span-3 text-right text-sm text-gray-500 dark:text-gray-400",
"title": "{{post.created_at ?? ''}}"
},
"text": "{{post.created_at_formatted ?? ''}}"
},
{
"comment": "모바일용 하단 정보",
"type": "basic",
"name": "Div",
"props": {
"className": "md:hidden flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400 mt-1"
},
"children": [
{
"type": "basic",
"name": "Span",
"props": {
"className": "flex items-center gap-1"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "eye",
"size": "xs"
}
},
{
"type": "basic",
"name": "Span",
"text": "{{post.view_count ?? 0}}"
}
]
},
{
"type": "basic",
"name": "Span",
"props": { "title": "{{post.created_at ?? ''}}" },
"text": "{{post.created_at_formatted ?? ''}}"
}
]
}
]
}
]
}
]
},
{
"comment": "페이지네이션 (항상 표시)",
"type": "basic",
"name": "Div",
"props": {
"className": "flex justify-center mt-6"
},
"children": [
{
"type": "composite",
"name": "Pagination",
"props": {
"currentPage": "{{userPosts.data?.current_page ?? 1}}",
"totalPages": "{{userPosts.data?.last_page ?? null}}",
"hasMorePages": "{{userPosts.data?.has_more_pages ?? false}}"
},
"actions": [
{
"event": "onPageChange",
"handler": "navigate",
"params": {
"path": "/users/{{route.userId}}/posts",
"mergeQuery": true,
"query": {
"page": "{{$args[0]}}"
}
}
}
]
}
]
}
]
}
]
}
]
}
]
}
}