File name
Commit message
Commit date
File name
Commit message
Commit date
09-04
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 type { ReactNode } from 'react';
import { FoxButton } from '@fox/core/components/fox-button';
import { FoxButtonGroup } from '@fox/core/components/fox-button-group';
import { FoxSelectText } from '@fox/core/components/fox-select-text';
import {
FoxListContainer,
type FoxListColumn,
} from '@fox/core/components/fox-list-container';
import { CONTENT_KIND_LABEL, type ContentKind } from '@/lib/domain/content';
import {
CONTENT_PAGE_SIZE_OPTIONS,
CONTENT_SEARCH_FIELDS,
CONTENT_SORT_OPTIONS,
buildContentHref,
contentBreadcrumb,
contentCreatePath,
type ContentPageSize,
type ContentQuery,
type ContentSortOption,
} from '@/lib/domain/content-query';
import { ContentListFilters } from './content-list-filters';
import type { CommonCode } from '@/lib/domain/common-code';
import type { GradesBySchool } from '@/lib/domain/content';
interface ContentListShellProps<T> {
kind: ContentKind;
columns: FoxListColumn<T>[];
rows: T[];
rowKey: (row: T) => string;
query: ContentQuery;
currentPage: number;
totalPages: number;
totalCount: number;
schools: readonly CommonCode[];
gradesBySchool: GradesBySchool;
/** 툴바 오른쪽에 덧붙일 것(금융OX퀴즈의 엑셀 버튼). */
extraActions?: ReactNode;
}
/** 콘텐츠관리 목록 3종이 공유하는 껍데기 — 열만 화면마다 다르다. */
export function ContentListShell<T>({
kind,
columns,
rows,
rowKey,
query,
currentPage,
totalPages,
totalCount,
schools,
gradesBySchool,
extraActions,
}: ContentListShellProps<T>) {
const router = useRouter();
function go(patch: Partial<ContentQuery>) {
router.push(buildContentHref(kind, query, patch));
}
return (
<FoxListContainer<T>
title={`콘텐츠 목록 (${CONTENT_KIND_LABEL[kind]})`}
breadcrumb={contentBreadcrumb(kind)}
caption={`${CONTENT_KIND_LABEL[kind]} 콘텐츠 목록`}
columns={columns}
rows={rows}
rowKey={rowKey}
totalCount={totalCount}
emptyMessage="등록된 콘텐츠가 없습니다."
filter={
<ContentListFilters
query={query}
schools={schools}
gradesBySchool={gradesBySchool}
// 백엔드가 searchLevelCd를 받기만 하고 조건에 쓰지 않아, 골라도 목록이 그대로다.
// SQL에 조건이 생기면 `kind === 'quiz'`로 되돌린다.
withLevel={false}
onChange={go}
/>
}
sorts={
<>
<FoxSelectText
size="sm"
ariaLabel="정렬"
icon={<i className="fox-ico fox-ico-ArrowsDownUp" aria-hidden="true" />}
options={CONTENT_SORT_OPTIONS.map((option) => ({
value: option.value,
label: option.label,
}))}
value={query.sort}
onValueChange={(value) => go({ sort: value as ContentSortOption })}
/>
<FoxSelectText
size="sm"
ariaLabel="페이지 크기"
options={CONTENT_PAGE_SIZE_OPTIONS.map((size) => ({
value: String(size),
label: `${size}개씩 보기`,
}))}
value={String(query.pageSize)}
onValueChange={(value) =>
go({ pageSize: Number(value) as ContentPageSize, page: 1 })
}
/>
</>
}
searchHidden={CONTENT_SEARCH_FIELDS[kind].length === 0}
searchFields={CONTENT_SEARCH_FIELDS[kind].map((field) => ({
value: field.value,
label: field.label,
}))}
searchField={query.searchField}
keyword={query.keyword}
searchPlaceholder="검색어를 입력하세요."
toolbarActions={
<FoxButtonGroup size="md">
{extraActions}
<FoxButton
type="primary"
size="md"
leadingIcon={<i className="fox-ico fox-ico-Plus" aria-hidden="true" />}
label="등록"
onAction={() => router.push(contentCreatePath(kind))}
/>
</FoxButtonGroup>
}
page={currentPage}
totalPages={totalPages}
buildHref={(patch) => buildContentHref(kind, query, patch as Partial<ContentQuery>)}
onQueryChange={go}
/>
);
}