공통 번역 리소스 확장:
- common.json 에 aria / error / dialog / success / message 네임스페이스 추가
- ko/en 양쪽 동일 구조 유지 (aria 36 키 + error 7 키 + dialog 4 키 + message 5 키)
alert/confirm 11건 → t() 치환:
- parent-inference: ParentReview / LabelSession / ParentExclusion
- admin: PermissionsPanel / UserRoleAssignDialog / AccessControl
aria-label 한글 40+건 → t() 치환:
- parent-inference (group_key/sub_cluster/정답 parent MMSI/스코프 필터 등)
- admin (역할 코드/이름, 알림 제목/내용, 시작일/종료일, 코드 검색, 대분류 필터, 수신 현황 기준일)
- detection (그룹 유형/해역 필터, 관심영역, 필터 설정/초기화, 멤버 수, 미니맵/재생 닫기)
- enforcement (확인/선박 상세/단속 등록/오탐 처리)
- vessel/statistics/ai-operations (조회 시작/종료 시각, 업로드 패널 닫기, 전송, 예시 URL 복사)
- 공통 컴포넌트 (SearchInput, NotificationBanner)
MainLayout 언어 토글:
- title 삼항분기 → t('message.switchToEnglish'/'switchToKorean')
- aria-label="페이지 내 검색" → t('aria.searchInPage')
- 토글 버튼 자체에 aria-label={t('aria.languageToggle')} 추가
42 lines
1.3 KiB
TypeScript
42 lines
1.3 KiB
TypeScript
import { Search, X } from 'lucide-react';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
/*
|
|
* SFR-02 공통컴포넌트: 검색 입력
|
|
*/
|
|
|
|
interface SearchInputProps {
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
placeholder?: string;
|
|
className?: string;
|
|
}
|
|
|
|
export function SearchInput({ value, onChange, placeholder, className = '' }: SearchInputProps) {
|
|
const { t } = useTranslation('common');
|
|
const effectivePlaceholder = placeholder ?? `${t('action.search')}...`;
|
|
return (
|
|
<div className={`relative ${className}`}>
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-hint" />
|
|
<input
|
|
type="text"
|
|
aria-label={effectivePlaceholder}
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
placeholder={effectivePlaceholder}
|
|
className="w-full bg-surface-overlay border border-slate-700/50 rounded-lg pl-9 pr-8 py-2 text-[11px] text-label placeholder:text-hint focus:outline-none focus:border-blue-500/50"
|
|
/>
|
|
{value && (
|
|
<button
|
|
type="button"
|
|
aria-label={t('aria.clearSearch')}
|
|
onClick={() => onChange('')}
|
|
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-hint hover:text-muted-foreground"
|
|
>
|
|
<X className="w-3 h-3" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|