import {isLoginRequiredError} from '@_api/apiClient'; import {fetchSidoList, fetchSigunguList} from '@_api/region/regionAPI'; import type {SidoResponse, SigunguResponse} from '@_api/region/regionTypes'; import {createStore, deleteStore, getStoreDetail, getStores, updateStore} from '@_api/store/storeAPI'; import { type SimpleStoreResponse, type StoreDetailResponse, type StoreRegisterRequest, type UpdateStoreRequest } from '@_api/store/storeTypes'; import {useAdminAuth} from '@_context/AdminAuthContext'; import '@_css/admin-store-page.css'; import {formatDisplayDateTime} from '@_util/DateTimeFormatter'; import React, {Fragment, useEffect, useState} from 'react'; import {useLocation, useNavigate} from 'react-router-dom'; const AdminStorePage: React.FC = () => { const [stores, setStores] = useState([]); const [isAdding, setIsAdding] = useState(false); const [newStore, setNewStore] = useState({ name: '', address: '', contact: '', businessRegNum: '', regionCode: '' }); const [expandedStoreId, setExpandedStoreId] = useState(null); const [detailedStores, setDetailedStores] = useState<{ [key: string]: StoreDetailResponse }>({}); const [isLoadingDetails, setIsLoadingDetails] = useState(false); const [isEditing, setIsEditing] = useState(false); const [editingStore, setEditingStore] = useState(null); const [sidoList, setSidoList] = useState([]); const [sigunguList, setSigunguList] = useState([]); const [selectedSido, setSelectedSido] = useState(''); const [selectedSigungu, setSelectedSigungu] = useState(''); const navigate = useNavigate(); const location = useLocation(); const { type: adminType } = useAdminAuth(); const handleError = (err: any) => { if (isLoginRequiredError(err)) { alert('로그인이 필요합니다.'); navigate('/admin/login', { state: { from: location } }); } else { const message = err.response?.data?.message || '알 수 없는 오류가 발생했습니다.'; alert(message); console.error(err); } }; const fetchStores = async () => { try { const storesData = (await getStores(selectedSido || undefined, selectedSigungu || undefined)).stores; setStores(storesData); } catch (error) { handleError(error); }; } useEffect(() => { if (adminType !== 'HQ') { alert('접근 권한이 없습니다.'); navigate('/admin'); return; } const fetchInitialData = async () => { try { const sidoRes = await fetchSidoList(); setSidoList(sidoRes.sidoList); } catch (error) { handleError(error); } }; fetchInitialData(); }, [adminType, navigate]); useEffect(() => { const fetchSigungu = async () => { if (selectedSido) { try { const sigunguRes = await fetchSigunguList(selectedSido); setSigunguList(sigunguRes.sigunguList); } catch (error) { handleError(error); } } else { setSigunguList([]); } setSelectedSigungu(''); }; fetchSigungu(); }, [selectedSido]); useEffect(() => { fetchStores();}, [selectedSido, selectedSigungu]); const handleInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setNewStore(prev => ({ ...prev, [name]: value })); }; const handleAddStore = async () => { if (Object.values(newStore).some(val => val === '')) { alert('모든 필드를 입력해주세요.'); return; } try { await createStore(newStore); const storesData = (await getStores(selectedSido || undefined, selectedSigungu || undefined)).stores; setStores(storesData); setIsAdding(false); setNewStore({ name: '', address: '', contact: '', businessRegNum: '', regionCode: '' }); } catch (error) { handleError(error); } }; const handleToggleDetails = async (storeId: string) => { const isAlreadyExpanded = expandedStoreId === storeId; setIsEditing(false); if (isAlreadyExpanded) { setExpandedStoreId(null); } else { setExpandedStoreId(storeId); if (!detailedStores[storeId]) { setIsLoadingDetails(true); try { const details = await getStoreDetail(storeId); setDetailedStores(prev => ({ ...prev, [storeId]: details })); } catch (error) { handleError(error); } finally { setIsLoadingDetails(false); } } } }; const handleDeleteStore = async (storeId: string) => { if (window.confirm('정말 이 매장을 삭제하시겠습니까? 관련 데이터가 모두 삭제될 수 있습니다.')) { try { await deleteStore(storeId); fetchStores(); setExpandedStoreId(null); } catch (error) { handleError(error); } } }; const handleEditClick = (store: StoreDetailResponse) => { setEditingStore({ name: store.name, address: store.address, contact: store.contact }); setIsEditing(true); }; const handleCancelEdit = () => { setIsEditing(false); setEditingStore(null); }; const handleEditChange = (e: React.ChangeEvent) => { const { name, value } = e.target; if (editingStore) { setEditingStore(prev => ({ ...prev!, [name]: value })); } }; const handleSave = async (storeId: string) => { if (!editingStore) return; try { await updateStore(storeId, editingStore); const updatedStore = await getStoreDetail(storeId); setDetailedStores(prev => ({ ...prev, [storeId]: updatedStore })); setStores(prev => prev.map(s => s.id === String(storeId) ? { ...s, name: updatedStore.name } : s)); setIsEditing(false); setEditingStore(null); alert('매장 정보가 성공적으로 업데이트되었습니다.'); } catch (error) { handleError(error); } }; return (

매장 관리

{isAdding && (
)}
{stores.map(store => ( {expandedStoreId === store.id && ( )} ))}
ID 매장명 관리
{store.id} {store.name}
{isLoadingDetails ?

로딩 중...

: detailedStores[store.id] ? (

상세 정보

주소: {detailedStores[store.id].address}

연락처: {detailedStores[store.id].contact}

사업자등록번호: {detailedStores[store.id].businessRegNum}

지역 코드: {detailedStores[store.id].region.code}

생성일: {formatDisplayDateTime(detailedStores[store.id].audit.createdAt)}

수정일: {formatDisplayDateTime(detailedStores[store.id].audit.updatedAt)}

생성자: {detailedStores[store.id].audit.createdBy.name}({detailedStores[store.id].audit.createdBy.id})

수정자: {detailedStores[store.id].audit.updatedBy.name}({detailedStores[store.id].audit.updatedBy.id})

{isEditing && editingStore ? (
) : (
)}
) :

상세 정보를 불러올 수 없습니다.

}
); }; export default AdminStorePage;