refactor: 로딩 스피너 표준화 및 프로젝트 헬스 개선

- LoadingSpinner 컴포넌트 5가지 변형 구현
  - LoadingSpinner (인라인/버튼용)
  - ContentLoadingSpinner (상세/수정 페이지)
  - PageLoadingSpinner (페이지 전환)
  - TableLoadingSpinner (테이블/리스트)
  - ButtonSpinner (버튼 내부)
- 18개+ 페이지 로딩 UI 표준화
  - HR 페이지 (사원, 휴가, 부서, 급여, 근태)
  - 영업 페이지 (견적, 거래처)
  - 게시판, 팝업관리, 품목기준정보
- API 키 보안 개선 (NEXT_PUBLIC_API_KEY → API_KEY)
- Textarea 다크모드 스타일 개선
- DropdownField Radix UI Select 버그 수정 (key prop)
- 프로젝트 헬스 개선 계획서 문서화

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2025-12-20 14:33:11 +09:00
parent c6b605200d
commit d7f491fa84
50 changed files with 666 additions and 246 deletions

View File

@@ -55,7 +55,24 @@ export function DropdownField({
unitOptions,
}: DynamicFieldRendererProps) {
const fieldKey = field.field_key || `field_${field.id}`;
const stringValue = value !== null && value !== undefined ? String(value) : '';
// is_active 필드인지 확인
const isActiveField = fieldKey === 'is_active' || fieldKey.endsWith('_is_active');
// 옵션을 먼저 정규화 (is_active 값 변환에 필요)
const rawOptions = normalizeOptions(field.options);
// is_active 필드일 때 boolean 값을 옵션에 맞게 변환
let stringValue = '';
if (value !== null && value !== undefined) {
if (isActiveField && rawOptions.length >= 2) {
// boolean/숫자 값을 첫번째(활성) 또는 두번째(비활성) 옵션 값으로 매핑
const isActive = value === true || value === 'true' || value === 1 || value === '1' || value === '활성';
stringValue = isActive ? rawOptions[0].value : rawOptions[1].value;
} else {
stringValue = String(value);
}
}
// field_key 또는 field_name이 '단위'/'unit' 관련이면 unitOptions 사용
const isUnitField =
@@ -73,8 +90,8 @@ export function DropdownField({
value: u.value,
}));
} else {
// field.options를 정규화
options = normalizeOptions(field.options);
// rawOptions는 이미 위에서 정규화
options = rawOptions;
}
// 옵션이 없으면 드롭다운을 disabled로 표시