임동욱 임동욱 08-11
feat: 꾸미기 아이템 목록 테이블 추가
Co-Authored-By: Claude Opus 5 
@8e13fd84a08263160272530e003aade06f5ca194
 
app/(protected)/(basic)/decoration-items/_components/decoration-item-table.tsx (added)
+++ app/(protected)/(basic)/decoration-items/_components/decoration-item-table.tsx
@@ -0,0 +1,106 @@
+import {
+  Table,
+  TableBody,
+  TableCell,
+  TableHead,
+  TableHeaderCell,
+  TableRow,
+} from '@/components/ui/table';
+import {
+  formatDecorationItemActiveLabel,
+  formatDecorationItemPoints,
+  formatDecorationItemUpdatedAt,
+  getDecorationItemThumbnailLabel,
+  type DecorationItem,
+} from '@/lib/domain/decoration-item';
+import { DecorationItemRowActions } from './decoration-item-row-actions';
+
+interface DecorationItemTableProps {
+  items: DecorationItem[];
+  /** 1-based 현재 페이지. "번호" 열의 표시 순번 계산에 쓴다. */
+  page: number;
+  pageSize: number;
+  /** 수정 팝업의 "(총 등록 N개)" 힌트로 그대로 흘려보낸다(현재 유형 전체 등록 건수). */
+  typeTotalCount: number;
+}
+
+const COLUMN_HEADERS = [
+  '번호',
+  '아이템ID',
+  '썸네일',
+  '아이템명',
+  '카테고리',
+  '포인트',
+  '사용여부',
+  '정렬순서',
+  '수정일시',
+  '관리',
+];
+
+/**
+ * 꾸미기 아이템 목록 테이블(시안 ADM_ITM_101, 10컬럼).
+ *
+ * **"번호"는 admins와 달리 오름차순 표시 순번이다**(`offset + index + 1`) — 이 화면의 기본
+ * 정렬이 정렬순서 오름차순이라(Repository가 보장) "가장 작은 정렬순서가 1번"이 직관적이다.
+ * admins가 전체 건수에서 거꾸로 세는 이유(생성일 최신순 정렬)가 여기에는 없다.
+ *
+ * **썸네일은 아직 실제 이미지가 없다** — 시안은 업로드 필드를 요구하지만 사용자가 "썸네일을
+ * 따로 등록하지 않고 추후 백엔드가 자동생성해주는 형식"이라고 확정했고(등록/수정 폼에 업로드
+ * 필드가 없는 이유, `decoration-item-form-fields.tsx` 참조), 그 백엔드 자체가 아직 없어
+ * 지금은 생성해 줄 이미지도 없다. 열 자리는 유지하고 아이템ID의 중간 세그먼트(예:
+ * `ITEM-HAT-002` → `HAT`)를 뽑아 보여주는 텍스트 박스로 대신한다 — 백엔드가 실제 썸네일을
+ * 내려주면 이 셀만 `<img>`로 바꾸면 된다.
+ *
+ * "관리" 열은 행별 수정/삭제 트리거(`DecorationItemRowActions`)에 위임한다 — 상호작용이
+ * 필요한 것은 그 셀뿐이라 이 테이블 자체는 Server Component로 유지하고 최말단만 클라이언트
+ * 경계로 뗀다.
+ */
+export function DecorationItemTable({
+  items,
+  page,
+  pageSize,
+  typeTotalCount,
+}: DecorationItemTableProps) {
+  const offset = (page - 1) * pageSize;
+
+  return (
+    <Table>
+      <TableHead>
+        <TableRow>
+          {COLUMN_HEADERS.map((header) => (
+            <TableHeaderCell key={header}>{header}</TableHeaderCell>
+          ))}
+        </TableRow>
+      </TableHead>
+      <TableBody>
+        {items.map((item, index) => (
+          <TableRow key={item.itemId}>
+            <TableCell>{offset + index + 1}</TableCell>
+            <TableCell>{item.itemId}</TableCell>
+            <TableCell>
+              <span
+                aria-hidden="true"
+                className="inline-flex size-9 items-center justify-center rounded-md bg-surface-muted text-label-sm text-foreground-muted"
+              >
+                {getDecorationItemThumbnailLabel(item.itemId)}
+              </span>
+              <span className="sr-only">썸네일 준비 중</span>
+            </TableCell>
+            <TableCell>{item.name}</TableCell>
+            <TableCell>{item.category}</TableCell>
+            <TableCell>{formatDecorationItemPoints(item.points)}</TableCell>
+            <TableCell>{formatDecorationItemActiveLabel(item.isActive)}</TableCell>
+            <TableCell>{item.sortOrder}</TableCell>
+            <TableCell>{formatDecorationItemUpdatedAt(item.updatedAt)}</TableCell>
+            <TableCell>
+              <DecorationItemRowActions
+                item={item}
+                typeTotalCount={typeTotalCount}
+              />
+            </TableCell>
+          </TableRow>
+        ))}
+      </TableBody>
+    </Table>
+  );
+}
Add a comment
List