File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
'use client';
import { useRouter } from 'next/navigation';
import { useState, useTransition, type ReactNode } from 'react';
import { FoxButton } from '@fox/core/components/fox-button';
import { FoxIconButton } from '@fox/core/components/fox-icon-button';
import {
FoxListContainer,
type FoxListColumn,
} from '@fox/core/components/fox-list-container';
import { FoxSelectText } from '@fox/core/components/fox-select-text';
import { FoxTab } from '@fox/core/components/fox-tab';
import { FoxTabItem } from '@fox/core/components/fox-tab-item';
import { FoxArrowsClockwiseIcon, FoxPlusIcon } from '@fox/core/icons';
import type { BoardPost, BoardType } from '@/lib/domain/board-post';
import {
BOARD_PAGE_SIZE_OPTIONS,
BOARD_SEARCH_FIELDS,
FILTER_ALL,
VISIBILITY_FILTER_OPTIONS,
buildBoardPostHref,
type BoardPageSize,
type BoardPostQuery,
} from '@/lib/domain/board-post-query';
import { BoardPostFormModal } from './board-post-form-modal';
import styles from './board-post-list.module.scss';
interface BoardPostListProps {
boardType: BoardType;
title: string;
breadcrumb: { label: string; href?: string }[];
columns: FoxListColumn<BoardPost>[];
items: BoardPost[];
query: BoardPostQuery;
currentPage: number;
totalPages: number;
totalCount: number;
/** 1:1문의처럼 관리자가 글을 쓰지 않는 게시판은 끈다. */
canCreate?: boolean;
/** 행의 "관리" 열을 화면마다 다르게 그려야 할 때. */
extraToolbar?: ReactNode;
}
const SORT_OPTIONS = [{ value: 'recent', label: '최근등록순' }];
export function BoardPostList({
boardType,
title,
breadcrumb,
columns,
items,
query,
currentPage,
totalPages,
totalCount,
canCreate = true,
extraToolbar,
}: BoardPostListProps) {
const router = useRouter();
const [isCreateOpen, setIsCreateOpen] = useState(false);
const [, startRefresh] = useTransition();
function go(patch: Partial<BoardPostQuery>) {
router.replace(buildBoardPostHref(boardType, query, { ...patch, page: 1 }));
}
return (
<>
<FoxListContainer<BoardPost>
title={title}
breadcrumb={breadcrumb}
caption={title}
columns={columns}
rows={items}
rowKey={(row) => row.id}
totalCount={totalCount}
emptyMessage="검색 결과가 없습니다. 다른 검색어로 다시 시도해 주세요."
sorts={
<>
<FoxSelectText
size="sm"
ariaLabel="정렬"
options={SORT_OPTIONS}
value="recent"
/>
<FoxSelectText
size="sm"
ariaLabel="페이지 크기"
options={BOARD_PAGE_SIZE_OPTIONS.map((size) => ({
value: String(size),
label: `${size}개씩 보기`,
}))}
value={String(query.pageSize)}
onValueChange={(value) =>
go({ pageSize: Number(value) as BoardPageSize })
}
/>
</>
}
searchFields={BOARD_SEARCH_FIELDS[boardType].map((field) => ({
value: field.value,
label: field.label,
}))}
searchField={query.searchField}
keyword={query.keyword}
toolbarActions={
canCreate ? (
<FoxButton
type="primary"
size="md"
leadingIcon={<FoxPlusIcon />}
label="신규 등록"
onAction={() => setIsCreateOpen(true)}
/>
) : (
extraToolbar
)
}
filter={
<div className={styles.filter}>
<FoxIconButton
type="default"
size="md"
icon={<FoxArrowsClockwiseIcon />}
label="목록 새로고침"
onAction={() => startRefresh(() => router.refresh())}
/>
<FoxTab
type="fill"
size="md"
label="노출 여부"
value={query.visibility}
onValueChange={(value) => go({ visibility: value })}
>
<FoxTabItem value={FILTER_ALL}>전체</FoxTabItem>
{VISIBILITY_FILTER_OPTIONS.map((option) => (
<FoxTabItem key={option.value} value={option.value}>
{option.label}
</FoxTabItem>
))}
</FoxTab>
</div>
}
page={currentPage}
totalPages={totalPages}
buildHref={(patch) =>
buildBoardPostHref(boardType, query, { page: Number(patch.page ?? 1) })
}
onQueryChange={(patch) => {
if (patch.keyword !== undefined || patch.searchField !== undefined) {
go({
keyword: String(patch.keyword ?? ''),
searchField: String(patch.searchField ?? query.searchField),
});
}
}}
/>
{isCreateOpen && (
<BoardPostFormModal
boardType={boardType}
mode="create"
onClose={() => setIsCreateOpen(false)}
/>
)}
</>
);
}