'use client'; import { useId, type ReactNode } from 'react'; import { FoxChip } from '@fox/core/components/fox-chip'; import { FoxChipArea } from '@fox/core/components/fox-chip-area'; import { FoxFormLabel } from '@fox/core/components/fox-form-label'; import { FoxPhoneNumber } from '@fox/core/components/fox-phone-number'; import { FoxSelect } from '@fox/core/components/fox-select'; import { ADMIN_MENU_OPTIONS, ADMIN_ROLE_OPTIONS, } from '@/lib/domain/admin-member'; import type { AdminMemberFormErrors } from '@/lib/domain/admin-member-form'; import styles from './admin-member-modal.module.scss'; interface AdminMemberFormFieldsProps { /** 숫자만 담긴 휴대전화번호. 하이픈은 화면이 그리고 값에는 넣지 않는다. */ phoneNumber: string; onPhoneNumberChange: (phoneNumber: string) => void; roleCode: string; onRoleCodeChange: (roleCode: string) => void; menuCodes: string[]; onMenuCodesChange: (menuCodes: string[]) => void; /** * 휴대전화 번호와 역할 선택 **사이**에 들어가는 이메일 칸. 두 시안이 이 자리에 서로 다른 것을 * 놓아(등록은 도메인 셀렉트가 붙은 form-email, 수정은 단일 input) 슬롯으로 받는다 — 자리 순서는 * 두 시안이 같으므로 여기가 갖고, 무엇을 놓을지는 각 팝업이 정한다. */ emailField: ReactNode; errors: AdminMemberFormErrors; } /** * 등록·수정 팝업이 공유하는 입력 항목 — 휴대전화 번호 / (이메일 슬롯) / 역할 선택 / 메뉴 선택. * 두 시안(ADM_ADM_102_p 5227:2035 / ADM_ADM_103_p 5227:3351)에서 자리와 순서가 같은 부분이다. * 이름·ID·비밀번호·잠김여부는 두 시안이 서로 달라 각 팝업이 직접 그린다. * * 세 칸 모두 @fox의 제어 위젯이라 값이 FormData에 실리지 않는다(`FoxSelect`는 네이티브 select가 * 아니라 버튼+리스트박스, `FoxPhoneNumber`·칩도 마찬가지다). 그래서 값은 호출부가 들고, 제출용 * hidden input을 여기서 함께 낸다 — 폼 제출 규약(Server Action + FormData)을 그대로 두기 위해서다. * * ⚠️ **메뉴 선택은 저장되지 않는다.** 백엔드에 관리자별 메뉴 권한이 테이블·VO·SQL 어디에도 없다 * (`/api/v1/common/menu`는 개인 북마크용이다). 시안대로 자리는 유지하되 **필수 검증은 걸지 않는다** * (사용자 확정) — 저장되지도 않는 값 때문에 등록이 막히면 안 되기 때문이다. 권한 API가 생기면 * 여기는 그대로 두고 Server Action이 값을 넘기기만 하면 된다. */ export function AdminMemberFormFields({ phoneNumber, onPhoneNumberChange, roleCode, onRoleCodeChange, menuCodes, onMenuCodesChange, emailField, errors, }: AdminMemberFormFieldsProps) { const phoneLabelId = useId(); const menuLabelId = useId(); const allMenuValues = ADMIN_MENU_OPTIONS.map((option) => option.value); const isAllSelected = menuCodes.length === allMenuValues.length; function toggleMenu(value: string) { onMenuCodesChange( menuCodes.includes(value) ? menuCodes.filter((code) => code !== value) : [...menuCodes, value] ); } return ( <> {/* 칸이 셋으로 나뉘어 있어 `