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 } from 'react';
import { Button } from '@/components/ui/button';
import { Field } from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import { Modal } from '@/components/ui/modal';
import { RadioGroup } from '@/components/ui/radio-group';
import type { StudentMember } from '@/lib/domain/student-member';
import {
updateStudentActiveStatus,
type UpdateStudentActiveStatusState,
} from '../_actions';
interface StudentDetailModalProps {
member: StudentMember;
onClose: () => void;
}
const ACTIVE_STATUS_OPTIONS = [
{ value: 'true', label: '활성' },
{ value: 'false', label: '비활성' },
];
const INITIAL_STATE: UpdateStudentActiveStatusState = { status: 'idle' };
const DETAIL_FORM_ID = 'student-active-status-form';
/**
* 학생 회원 조회 팝업 — 사용여부를 제외한 모든 항목은 readOnly다. 데이터는 목록 행에서 이미
* 갖고 있는 member를 props로 그대로 받으므로 재조회하지 않는다. 저장(Server Action) 성공 시
* 자동으로 닫힌다.
*
* Modal이 내부적으로 portal을 쓸 수도 있어(구현 미확정 — components/ui/modal.tsx는 design
* 레인 소관) 저장 버튼은 footer 슬롯에서 `form={DETAIL_FORM_ID}` 속성으로 폼과 연결한다.
* 네이티브 HTML의 `form` 속성은 버튼이 실제 DOM상 form의 자손이 아니어도 같은 문서 안에서
* id만 일치하면 그 form을 제출한다 — footer가 body와 다른 DOM 위치에 렌더돼도 안전하다.
*/
export function StudentDetailModal({ member, onClose }: StudentDetailModalProps) {
const [state, formAction, isPending] = useActionState(
updateStudentActiveStatus,
INITIAL_STATE
);
useEffect(() => {
if (state.status === 'success') {
onClose();
}
}, [state, onClose]);
const gradeClassNumber = `${member.grade}학년 ${member.classNumber}반 ${member.studentNumber}번`;
return (
<Modal
title="학생 회원 조회"
onClose={onClose}
footer={
<>
<Button type="button" variant="ghost" onClick={onClose}>
취소
</Button>
<Button
type="submit"
form={DETAIL_FORM_ID}
variant="primary"
disabled={isPending}
>
{isPending ? '저장 중...' : '저장'}
</Button>
</>
}
>
<form id={DETAIL_FORM_ID} action={formAction} className="flex flex-col gap-4">
<input type="hidden" name="id" value={member.id} />
<p className="text-body-sm text-foreground-muted">
본 화면의 모든 항목은 조회 전용입니다. (단, 사용여부는 수정 가능)
</p>
<Field label="이름">
<Input value={member.name} readOnly />
</Field>
<Field label="ID">
<Input value={member.loginId} readOnly />
</Field>
<Field label="휴대전화 번호">
<Input value={member.phoneNumber} readOnly />
</Field>
<Field label="이메일">
<Input value={member.email} readOnly />
</Field>
<Field label="생년월일">
<Input value={member.birthDate} readOnly />
</Field>
<Field label="보호자 이름">
<Input value={member.guardianName} readOnly />
</Field>
<Field label="보호자 연락처">
<Input value={member.guardianPhoneNumber} readOnly />
</Field>
<Field label="학교">
<Input value={member.schoolName} readOnly />
</Field>
<Field label="학년/반/번호">
<Input value={gradeClassNumber} readOnly />
</Field>
<Field label="사용여부">
<RadioGroup
name="isActive"
options={ACTIVE_STATUS_OPTIONS}
defaultValue={member.isActive ? 'true' : 'false'}
/>
</Field>
{state.status === 'error' && (
<p className="text-body-sm text-danger">{state.error}</p>
)}
<p className="text-body-sm text-foreground-muted">
회원 가입일 : {member.joinedAt}
</p>
</form>
</Modal>
);
}