import { useEffect, useState } from 'react' import { getContacts, type Contact, type ContactCategory } from '../api/client' const FOOTER_NOTE = '담당자 등록·수정은 담당자관리 메뉴에서 합니다.' /** 채널관리에서 담당자 3종(신청기관/문정원/변호사)을 배정할 때 여는 선택 모달. * 담당자 정보를 여기서 입력하지 않는다 - 등록/수정은 항상 담당자관리 화면에서만 한다. */ export default function ContactPickerModal({ category, title, onSelect, onClose, }: { category: ContactCategory title: string onSelect: (contact: Contact) => void onClose: () => void }) { const [contacts, setContacts] = useState([]) const [loading, setLoading] = useState(true) const [keyword, setKeyword] = useState('') const [error, setError] = useState(null) useEffect(() => { let cancelled = false setLoading(true) getContacts(category) .then((data) => { if (!cancelled) { setContacts(data) } }) .catch((e) => { if (!cancelled) { setError(e instanceof Error ? e.message : '담당자 목록을 불러오지 못했습니다.') } }) .finally(() => { if (!cancelled) { setLoading(false) } }) return () => { cancelled = true } }, [category]) useEffect(() => { function onKeyDown(e: KeyboardEvent) { if (e.key === 'Escape') { onClose() } } window.addEventListener('keydown', onKeyDown) return () => window.removeEventListener('keydown', onKeyDown) }, [onClose]) const keywordTrimmed = keyword.trim() const visible = contacts.filter((c) => { if (keywordTrimmed === '') { return true } return c.name.includes(keywordTrimmed) || (c.affiliation ?? '').includes(keywordTrimmed) }) return (

{title}

setKeyword(e.target.value)} placeholder="이름·소속으로 검색" aria-label="담당자 검색" className="mt-3 w-full rounded-md border border-gray-300 px-2.5 py-1.5 text-sm" /> {error &&

{error}

}
{loading ? (

불러오는 중…

) : visible.length === 0 ? (

등록된 담당자가 없습니다.

) : (
    {visible.map((contact) => (
  • {contact.name} {contact.affiliation && ( · {contact.affiliation} )}

    {[contact.deptName, contact.phone, contact.email].filter(Boolean).join(' · ') || '-'}

  • ))}
)}

{FOOTER_NOTE}

) }