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 { useState } from 'react';
import { FoxButton } from '@fox/core/components/fox-button';
import { FoxButtonGroup } from '@fox/core/components/fox-button-group';
import { FoxIconButton } from '@fox/core/components/fox-icon-button';
import { useFeedback } from '@/app/_hooks/use-feedback';
import { useBlockingAction } from '@/app/_hooks/use-blocking-action';
import type { BoardPost, BoardType } from '@/lib/domain/board-post';
import type { CommonCode } from '@/lib/domain/common-code';
import { deleteBoardPostAction, loadBoardPostAction } from '../_actions';
import { BoardPostFormModal } from './board-post-form-modal';
interface BoardRowActionsProps {
boardType: BoardType;
post: BoardPost;
/** 공통코드 `BBS_FAQ_CD` — FAQ 수정 팝업의 유형 선택지. */
faqTypes?: readonly CommonCode[];
}
/**
* 목록 행의 "관리" 셀(시안 A_BOA_006 ④ / 014 ④) — 수정 팝업의 열림 상태만 소유하는 최말단
* 상호작용 경계다(테이블 전체를 클라이언트로 내리지 않기 위해 이 셀만 분리했다).
*
* 삭제는 시안대로 **확인 얼럿을 거친 뒤** 실행한다. 백엔드는 물리 삭제가 아니라 `DEL_YN='Y'`로
* 표시만 바꾸지만, 관리자 화면에서 다시 볼 방법이 없으므로 사용자에게는 되돌릴 수 없는 동작으로
* 안내한다.
*
* 시안의 연필·휴지통 아이콘 버튼이다. 버튼은 `components/ui/button`이 아니라 `FoxIconButton`을
* 쓴다 — 아이콘은 스스로 크기를 갖지 않고 감싼 슬롯이 정하는데, 그 슬롯 크기를 실제로 정의하는
* 건 @fox 쪽뿐이다(components/ui는 Tailwind 제거 이후 무스타일이다).
*/
export function BoardRowActions({
boardType,
post,
faqTypes,
}: BoardRowActionsProps) {
const { showAlert, hideAlert, showToast } = useFeedback();
// 목록 행이 아니라 단건 조회 결과로 폼을 채운다 — 행은 화면에 떠 있는 동안 낡을 수 있다.
const [editPost, setEditPost] = useState<BoardPost | null>(null);
const [isLoading, startLoading] = useBlockingAction();
const [isDeleting, startDeleting] = useBlockingAction();
function openEdit() {
startLoading(async () => {
const result = await loadBoardPostAction(post.id);
if (result.status === 'error') {
showToast({ variant: 'danger', message: result.message });
return;
}
setEditPost(result.post);
});
}
function runDelete() {
hideAlert();
startDeleting(async () => {
const result = await deleteBoardPostAction(boardType, post.id);
if (result.status === 'error') {
showToast({
variant: 'danger',
message: result.message ?? '삭제하지 못했습니다.',
});
return;
}
showToast({ variant: 'success', message: '게시물을 삭제했습니다.' });
});
}
function confirmDelete() {
showAlert({
variant: 'danger',
title: '게시물을 삭제하시겠습니까?',
message: `"${post.title}" 게시물을 삭제합니다. 삭제 후에는 되돌릴 수 없습니다.`,
actions: (
<FoxButtonGroup size="md">
<FoxButton type="default" size="md" label="취소" onAction={hideAlert} />
<FoxButton type="primary" size="md" label="삭제" onAction={runDelete} />
</FoxButtonGroup>
),
});
}
return (
<>
<FoxButtonGroup size="sm" align="center">
<FoxIconButton
type="default"
size="sm"
icon={<i className="fox-ico fox-ico-PencilSimple" aria-hidden="true" />}
label={isLoading ? '불러오는 중' : '수정'}
disabled={isLoading}
onAction={openEdit}
/>
<FoxIconButton
type="default"
size="sm"
icon={<i className="fox-ico fox-ico-Trash" aria-hidden="true" />}
label={isDeleting ? '삭제 중' : '삭제'}
disabled={isDeleting}
onAction={confirmDelete}
/>
</FoxButtonGroup>
{editPost && (
<BoardPostFormModal
boardType={boardType}
mode="edit"
faqTypes={faqTypes}
post={editPost}
onClose={() => setEditPost(null)}
/>
)}
</>
);
}