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
File name
Commit message
Commit date
'use client';
import { useActionState, useEffect, useRef } from 'react';
import { FoxButton } from '@fox/core/components/fox-button';
import { FoxHelperText } from '@fox/core/components/fox-helper-text';
import { FoxInput } from '@fox/core/components/fox-input';
import { FoxModal } from '@fox/core/components/fox-modal';
import { useFeedback } from '@/app/_hooks/use-feedback';
import type { CommonCodeDetail } from '@/lib/domain/common-code';
import { INITIAL_COMMON_CODE_FORM_STATE } from '@/lib/domain/common-code-form';
import { createCodeDetailAction, updateCodeDetailAction } from '../_actions';
import styles from './code-form.module.scss';
interface CodeDetailModalProps {
/** 소속 공통코드 — 좌측에서 고른 값이라 팝업에서는 바꿀 수 없다. */
comCd: string;
/** 수정 대상. 없으면 등록 팝업이다. */
detail?: CommonCodeDetail;
/** 등록 시 채워 둘 정렬번호(기존 개수 + 1). */
nextSortSeq: number;
onClose: () => void;
}
/**
* 상세코드 등록/수정 팝업 — 기획 1842:16179 / 1842:16262.
*
* 코드ID는 좌측에서 고른 공통코드라 **항상 읽기 전용**이다(기획 ①: "상위코드 고정된 경우 비활성
* 처리"). 수정에서는 코드(`comDtlCd`)도 대상을 가리키는 값이라 바꿀 수 없다 — 백엔드가 두 코드로
* 행을 찾기 때문에 바꾸면 다른 행을 수정하게 된다.
*
* ⚠️ **등록 시 코드설명은 저장되지 않는다** — 백엔드 INSERT에 `DTL_CD_EXPLN`이 빠져 있다.
* 시안대로 칸은 두고 백엔드에 수정을 요청했다(Repository 주석 참조). 수정에서는 정상 저장된다.
*/
export function CodeDetailModal({
comCd,
detail,
nextSortSeq,
onClose,
}: CodeDetailModalProps) {
const isEdit = detail !== undefined;
const { showToast } = useFeedback();
const formRef = useRef<HTMLFormElement>(null);
const [state, formAction, isPending] = useActionState(
isEdit ? updateCodeDetailAction : createCodeDetailAction,
INITIAL_COMMON_CODE_FORM_STATE
);
useEffect(() => {
if (state.status === 'success') {
showToast({
variant: 'success',
message: isEdit ? '상세코드를 수정했습니다.' : '상세코드를 등록했습니다.',
});
onClose();
}
}, [state, showToast, onClose, isEdit]);
const errors = state.status === 'error' ? (state.errors ?? {}) : {};
return (
<FoxModal
open
size="sm"
title={isEdit ? '상세코드 수정' : '상세코드 등록'}
onClose={onClose}
actions={
<>
<FoxButton type="default" size="md" label="취소" onAction={onClose} />
<FoxButton
type="primary"
size="md"
label={isPending ? '저장 중...' : '저장'}
disabled={isPending}
onAction={() => formRef.current?.requestSubmit()}
/>
</>
}
>
<form ref={formRef} action={formAction} className={styles.fields}>
<input type="hidden" name={isEdit ? 'targetComCd' : 'comCd'} value={comCd} />
{isEdit && (
<input
type="hidden"
name="targetComDtlCd"
value={detail.comDtlCd}
/>
)}
<div className={styles.field}>
<FoxInput
size="md"
label="코드ID"
value={comCd}
readOnly
message="상위코드가 고정된 경우 비활성 처리됩니다."
/>
</div>
<div className={styles.field}>
<FoxInput
size="md"
name="comDtlCd"
label="코드"
requirement="required"
defaultValue={detail?.comDtlCd ?? ''}
readOnly={isEdit}
placeholder="코드를 입력하세요."
invalid={Boolean(errors.comDtlCd)}
message={errors.comDtlCd}
maxLength={50}
/>
</div>
<div className={styles.field}>
<FoxInput
size="md"
name="name"
label="코드값의미"
requirement="required"
defaultValue={detail?.name ?? ''}
placeholder="코드값의미를 입력하세요."
invalid={Boolean(errors.name)}
message={errors.name}
maxLength={100}
/>
</div>
<div className={styles.field}>
<FoxInput
size="md"
type="number"
name="sortSeq"
label="정렬번호"
requirement="required"
min={1}
step={1}
defaultValue={detail?.sortSeq ?? nextSortSeq}
invalid={Boolean(errors.sortSeq)}
message={errors.sortSeq}
/>
</div>
<div className={styles.field}>
<FoxInput
size="md"
name="description"
label="코드설명"
defaultValue={detail?.description ?? ''}
placeholder="코드설명을 입력하세요."
invalid={Boolean(errors.description)}
message={errors.description}
maxLength={500}
/>
</div>
{state.status === 'error' && state.message && (
<FoxHelperText type="danger" message={state.message} />
)}
</form>
</FoxModal>
);
}