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 { FoxBadge, type FoxBadgeColor } from '@fox/core/components/fox-badge';
import { FoxIconButton } from '@fox/core/components/fox-icon-button';
import { FoxStatusIndicator } from '@fox/core/components/fox-status-indicator';
import type { FoxListColumn } from '@fox/core/components/fox-list-container';
import type { CommonCode } from '@/lib/domain/common-code';
import { formatPhoneNumber } from '@/lib/domain/phone-number';
import {
EMPTY_FIELD_PLACEHOLDER,
formatAnswerStatusLabel,
IS_BOARD_CATEGORY_ENABLED,
formatBoardCategoryLabel,
formatFaqTypeLabel,
formatContentExcerpt,
formatInquiryTypeLabel,
formatOptionalValue,
formatVisibilityLabel,
type BoardPost,
type BoardType,
} from '@/lib/domain/board-post';
import { BoardRowActions } from './board-row-actions';
import { InquiryRowActions } from '../inquiries/_components/inquiry-row-actions';
function numberColumn(page: number, pageSize: number): FoxListColumn<BoardPost> {
return {
key: 'no',
header: '번호',
width: 80,
render: (_row, index) => (page - 1) * pageSize + index + 1,
};
}
// 백엔드에 구분 자리가 생기면 `IS_BOARD_CATEGORY_ENABLED`가 이 열을 다시 켠다.
function categoryColumn(): FoxListColumn<BoardPost> {
return {
key: 'category',
header: '구분',
width: 120,
render: (row) =>
row.categoryCode ? (
<FoxBadge type="pastel" color="neutral" size="md">
{formatBoardCategoryLabel(row.categoryCode)}
</FoxBadge>
) : (
EMPTY_FIELD_PLACEHOLDER
),
};
}
function visibilityColumn(): FoxListColumn<BoardPost> {
return {
key: 'isVisible',
header: '사용여부',
width: 120,
render: (row) => (
<FoxStatusIndicator
type={row.isVisible ? 'success' : 'danger'}
label={formatVisibilityLabel(row.isVisible)}
/>
),
};
}
function actionsColumn(
boardType: BoardType,
faqTypes: readonly CommonCode[] = []
): FoxListColumn<BoardPost> {
return {
key: 'actions',
header: '관리',
width: 120,
render: (row) => (
<span onClick={(event) => event.stopPropagation()}>
<BoardRowActions boardType={boardType} post={row} faqTypes={faqTypes} />
</span>
),
};
}
function noticeColumns(page: number, pageSize: number): FoxListColumn<BoardPost>[] {
return [
numberColumn(page, pageSize),
...(IS_BOARD_CATEGORY_ENABLED ? [categoryColumn()] : []),
{ key: 'title', header: '제목', width: 320, align: 'start', emphasis: true },
{
key: 'attachment',
header: '첨부',
width: 100,
render: (row) =>
row.attachmentId ? (
<FoxIconButton
type="default"
size="sm"
icon={<i className="fox-ico fox-ico-Paperclip" aria-hidden="true" />}
label={`${row.title} 첨부파일`}
/>
) : null,
},
{
key: 'authorName',
header: '작성자',
align: 'start',
width: 160,
render: (row) => formatOptionalValue(row.authorName),
},
visibilityColumn(),
{
key: 'createdAt',
header: '작성일',
width: 140,
render: (row) => formatOptionalValue(row.createdAt),
},
actionsColumn('notice'),
];
}
function faqColumns(
page: number,
pageSize: number,
faqTypes: readonly CommonCode[]
): FoxListColumn<BoardPost>[] {
const typeOptions = faqTypes.map((code) => ({
value: code.code,
label: code.label,
}));
return [
numberColumn(page, pageSize),
...(IS_BOARD_CATEGORY_ENABLED ? [categoryColumn()] : []),
{
key: 'type',
header: '유형',
width: 120,
// 서버가 조인해 준 이름을 그대로 쓴다. 코드표 조회는 이름이 안 온 행의 보루다.
render: (row) => row.typeName ?? formatFaqTypeLabel(row.typeCode, typeOptions),
},
{ key: 'title', header: '제목', width: 320, align: 'start', emphasis: true },
{
key: 'authorName',
header: '작성자',
align: 'start',
width: 160,
render: (row) => formatOptionalValue(row.authorName),
},
{
key: 'createdAt',
header: '등록일',
width: 140,
render: (row) => formatOptionalValue(row.createdAt),
},
visibilityColumn(),
actionsColumn('faq', faqTypes),
];
}
// TODO: BBS_ANS_CD의 실제 코드값이 확인되면 코드값 기준 표로 바꾼다(관리자 권한 배지와 같은 방식).
// 이슈: 코드값을 모르고 코드명도 코드관리 화면에서 바뀔 수 있어 코드명으로 색을 고른다.
// 앞에서부터 먼저 걸리는 것을 쓴다 — 「미처리」가 「처리」보다 앞에 있어야 한다.
const ANSWER_STATUS_BADGE_COLORS: readonly (readonly [string, FoxBadgeColor])[] = [
['미답변', 'warning'],
['미처리', 'warning'],
['완료', 'success'],
['반려', 'danger'],
['보류', 'danger'],
['취소', 'danger'],
['처리', 'information'],
['진행', 'information'],
['접수', 'information'],
['대기', 'warning'],
];
function answerStatusBadgeColor(label: string): FoxBadgeColor {
const matched = ANSWER_STATUS_BADGE_COLORS.find(([keyword]) =>
label.includes(keyword)
);
return matched?.[1] ?? 'neutral';
}
function inquiryColumns(
page: number,
pageSize: number,
answerStatuses: readonly CommonCode[]
): FoxListColumn<BoardPost>[] {
const statusOptions = answerStatuses.map((code) => ({
value: code.code,
label: code.label,
}));
return [
numberColumn(page, pageSize),
{
key: 'type',
header: '질문유형',
width: 120,
// 서버 조인 이름 우선 — 코드표(잠정값)는 이름이 안 온 행의 보루다.
render: (row) => row.typeName ?? formatInquiryTypeLabel(row.typeCode),
},
// 1:1문의는 제목이 없다(내용만 온다) — 내용 발췌가 이 줄의 대표 칸이다.
{
key: 'content',
header: '질문내용',
align: 'start',
width: 280,
emphasis: true,
render: (row) => formatContentExcerpt(row.content),
},
{
key: 'authorName',
header: '질문자명',
align: 'start',
width: 140,
render: (row) => formatOptionalValue(row.authorName),
},
{
key: 'authorPhoneNumber',
header: '전화번호',
align: 'start',
width: 160,
render: (row) => formatOptionalValue(formatPhoneNumber(row.authorPhoneNumber)),
},
{
key: 'createdAt',
header: '질문일',
width: 140,
render: (row) => formatOptionalValue(row.createdAt),
},
{
key: 'answerStatus',
header: '상태',
width: 120,
render: (row) => {
if (!row.answerStatusCode) {
return EMPTY_FIELD_PLACEHOLDER;
}
const label = formatAnswerStatusLabel(row.answerStatusCode, statusOptions);
return (
<FoxBadge type="pastel" color={answerStatusBadgeColor(label)} size="md">
{label}
</FoxBadge>
);
},
},
{
key: 'answeredAt',
header: '답변일',
width: 140,
render: (row) => formatOptionalValue(row.answeredAt),
},
{
key: 'actions',
header: '관리',
width: 160,
render: (row) => (
<InquiryRowActions post={row} answerStatuses={answerStatuses} />
),
},
];
}
export function boardPostColumns(
boardType: BoardType,
page: number,
pageSize: number,
answerStatuses: readonly CommonCode[] = [],
faqTypes: readonly CommonCode[] = []
): FoxListColumn<BoardPost>[] {
if (boardType === 'notice') return noticeColumns(page, pageSize);
if (boardType === 'faq') return faqColumns(page, pageSize, faqTypes);
return inquiryColumns(page, pageSize, answerStatuses);
}