Files
sam-react-prod/src/hooks/useDetailPageState.ts
유병철 2639724f9f feat(WEB): 상태 뱃지 공통화 및 상세 페이지 훅 시스템 추가
공통화:
- status-config.ts 신규 추가 (상태 설정 중앙 관리)
- StatusBadge 컴포넌트 개선
- 뱃지 공통화 가이드 문서 추가

상세 페이지 훅 시스템:
- useDetailData, useDetailPageState, useDetailPermissions, useCRUDHandlers 훅 신규 추가
- hooks/index.ts 진입점 추가
- BillDetailV2 신규 컴포넌트 추가

리팩토링:
- 회계(매입/어음/거래처), 품목, 단가, 견적, 주문 상태 뱃지 공통화 적용
- 생산(작업지시서/대시보드/작업자화면), 품질(검사관리) 상태 뱃지 적용
- 공사관리(칸반/프로젝트카드) 상태 뱃지 적용
- 고객센터(문의관리), 인사(직원CSV업로드) 개선
- 리스트 페이지 공통화 현황 분석 문서 추가

상수 정리:
- lib/constants/ 디렉토리 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 15:57:49 +09:00

184 lines
4.6 KiB
TypeScript

'use client';
import { useState, useCallback, useMemo } from 'react';
import { useRouter, useParams, useSearchParams } from 'next/navigation';
/**
* 상세 페이지 모드 타입
* - view: 조회 모드
* - edit: 수정 모드
* - create: 등록 모드
*/
export type DetailMode = 'view' | 'edit' | 'create';
export interface UseDetailPageStateOptions {
/** 기본 모드 (기본값: 'view') */
defaultMode?: DetailMode;
/** 목록 페이지 경로 (뒤로가기용) */
listPath?: string;
}
export interface UseDetailPageStateReturn<T> {
// ===== 라우터 정보 =====
/** 현재 아이템 ID (params.id) */
id: string | null;
/** 현재 locale */
locale: string;
/** Next.js router */
router: ReturnType<typeof useRouter>;
// ===== 모드 관리 =====
/** 현재 모드 */
mode: DetailMode;
/** 모드 변경 */
setMode: (mode: DetailMode) => void;
/** 조회 모드 여부 */
isViewMode: boolean;
/** 수정 모드 여부 */
isEditMode: boolean;
/** 등록 모드 여부 */
isCreateMode: boolean;
// ===== 데이터 상태 =====
/** 데이터 */
data: T | null;
/** 데이터 설정 */
setData: (data: T | null) => void;
/** 로딩 상태 */
isLoading: boolean;
/** 로딩 상태 설정 */
setIsLoading: (loading: boolean) => void;
/** 에러 메시지 */
error: string | null;
/** 에러 설정 */
setError: (error: string | null) => void;
// ===== 네비게이션 =====
/** 목록으로 이동 */
goToList: () => void;
/** 수정 모드로 전환 (URL도 변경) */
goToEdit: () => void;
/** 조회 모드로 전환 */
goToView: () => void;
}
/**
* 상세 페이지 공통 상태 관리 훅
*
* 등록/수정/상세 페이지에서 반복되는 상태 및 라우터 로직을 통합합니다.
*
* @example
* ```tsx
* // 상세/수정 페이지
* const {
* id, mode, isViewMode, isEditMode,
* data, setData, isLoading, setIsLoading,
* goToList, goToEdit
* } = useDetailPageState<Account>({ listPath: '/settings/accounts' });
*
* // 등록 페이지
* const { mode, isCreateMode, goToList } = useDetailPageState<Account>({
* defaultMode: 'create',
* listPath: '/settings/accounts'
* });
* ```
*/
export function useDetailPageState<T = unknown>(
options: UseDetailPageStateOptions = {}
): UseDetailPageStateReturn<T> {
const { defaultMode = 'view', listPath } = options;
// ===== 라우터 =====
const router = useRouter();
const params = useParams();
const searchParams = useSearchParams();
// ID 추출 (params.id가 string | string[] | undefined일 수 있음)
const id = useMemo(() => {
const rawId = params?.id;
if (Array.isArray(rawId)) return rawId[0] || null;
return rawId || null;
}, [params?.id]);
// locale 추출
const locale = useMemo(() => {
const rawLocale = params?.locale;
if (Array.isArray(rawLocale)) return rawLocale[0] || 'ko';
return rawLocale || 'ko';
}, [params?.locale]);
// ===== 모드 관리 =====
// URL의 ?mode=edit 파라미터 확인
const urlMode = searchParams?.get('mode');
const initialMode = urlMode === 'edit' ? 'edit' : defaultMode;
const [mode, setModeState] = useState<DetailMode>(initialMode);
const setMode = useCallback((newMode: DetailMode) => {
setModeState(newMode);
}, []);
const isViewMode = mode === 'view';
const isEditMode = mode === 'edit';
const isCreateMode = mode === 'create';
// ===== 데이터 상태 =====
const [data, setData] = useState<T | null>(null);
const [isLoading, setIsLoading] = useState(!isCreateMode); // 등록 모드는 로딩 불필요
const [error, setError] = useState<string | null>(null);
// ===== 네비게이션 =====
const goToList = useCallback(() => {
if (listPath) {
router.push(`/${locale}${listPath}`);
} else {
router.back();
}
}, [router, locale, listPath]);
const goToEdit = useCallback(() => {
setModeState('edit');
if (id) {
// URL에 mode=edit 추가 (현재 경로 유지)
const currentPath = window.location.pathname;
router.push(`${currentPath}?mode=edit`);
}
}, [router, id]);
const goToView = useCallback(() => {
setModeState('view');
if (id) {
// URL에서 mode 파라미터 제거
const currentPath = window.location.pathname;
router.push(currentPath);
}
}, [router, id]);
return {
// 라우터 정보
id,
locale,
router,
// 모드 관리
mode,
setMode,
isViewMode,
isEditMode,
isCreateMode,
// 데이터 상태
data,
setData,
isLoading,
setIsLoading,
error,
setError,
// 네비게이션
goToList,
goToEdit,
goToView,
};
}