Files
web-page/src/pages/realestate/RealEstate.jsx

191 lines
7.9 KiB
JavaScript

import React, { useState, useMemo } from 'react';
import { Link } from 'react-router-dom';
import {
STATUS_CONFIG, TABS,
} from './realEstateUtils';
import useComplexes from './hooks/useComplexes';
import ComplexCard from './components/ComplexCard';
import RightPanel from './components/RightPanel';
import ScheduleView from './components/ScheduleView';
import PriceAnalysis from './components/PriceAnalysis';
import ComplexModal from './components/ComplexModal';
import 'leaflet/dist/leaflet.css';
import './RealEstate.css';
// ── 메인 컴포넌트 ──────────────────────────────────────────────────────────────
const RealEstate = () => {
const { complexes, addComplex, updateComplex, deleteComplex } = useComplexes();
const [selectedComplex, setSelectedComplex] = useState(null);
const [activeTab, setActiveTab] = useState('목록');
const [filterStatus, setFilterStatus] = useState('전체');
const [showModal, setShowModal] = useState(false);
const [editingComplex, setEditingComplex] = useState(null);
const handleAdd = (data) => {
addComplex(data);
setShowModal(false);
};
const handleUpdate = (data) => {
updateComplex(data);
if (selectedComplex?.id === data.id) setSelectedComplex(data);
setEditingComplex(null);
setShowModal(false);
};
const handleDelete = (id) => {
if (!confirm('삭제하시겠습니까?')) return;
deleteComplex(id);
if (selectedComplex?.id === id) setSelectedComplex(null);
};
const handleModalSave = (data) => {
if (editingComplex) {
handleUpdate({ ...editingComplex, ...data });
} else {
handleAdd(data);
}
};
const filteredComplexes = useMemo(() => {
if (filterStatus === '전체') return complexes;
return complexes.filter((c) => c.status === filterStatus);
}, [complexes, filterStatus]);
const stats = useMemo(() => ({
total: complexes.length,
upcoming: complexes.filter((c) => c.status === '청약예정').length,
active: complexes.filter((c) => c.status === '청약중').length,
avgPrice: complexes.length
? Math.round(complexes.reduce((s, c) => s + c.avgPricePerPyeong, 0) / complexes.length)
: 0,
}), [complexes]);
return (
<div className="re">
{/* 헤더 */}
<header className="re-header">
<div>
<p className="re-kicker">부동산 정보</p>
<h1>관심 단지 관리</h1>
<p className="re-sub">관심 있는 아파트 단지 정보를 수집하고 분석합니다.</p>
<div className="re-header-actions">
<button
className="button primary"
onClick={() => { setEditingComplex(null); setShowModal(true); }}
>
+ 단지 추가
</button>
<Link to="/realestate" className="button ghost"> 청약 대시보드</Link>
<a href="https://www.applyhome.co.kr" target="_blank" rel="noreferrer" className="button ghost">
청약홈
</a>
</div>
</div>
<div className="re-stats-bar">
<div className="re-stat-item">
<p className="re-stat-item__value">{stats.total}</p>
<p className="re-stat-item__label">관심 단지</p>
</div>
<div className="re-stat-item">
<p className="re-stat-item__value" style={{ color: '#00d4ff' }}>{stats.upcoming}</p>
<p className="re-stat-item__label">청약 예정</p>
</div>
<div className="re-stat-item">
<p className="re-stat-item__value" style={{ color: '#34d399' }}>{stats.active}</p>
<p className="re-stat-item__label">청약 </p>
</div>
<div className="re-stat-item">
<p className="re-stat-item__value" style={{ color: '#f59e0b' }}>
{stats.avgPrice.toLocaleString()}
</p>
<p className="re-stat-item__label">평균 평당가</p>
</div>
</div>
</header>
{/* 탭 바 */}
<div className="re-tabs-bar">
<div className="re-tabs">
{TABS.map((tab) => (
<button
key={tab}
className={`re-tab ${activeTab === tab ? 'is-active' : ''}`}
onClick={() => setActiveTab(tab)}
>
{tab}
</button>
))}
</div>
{activeTab === '목록' && (
<div className="re-filter">
{['전체', ...Object.keys(STATUS_CONFIG)].map((s) => (
<button
key={s}
className={`re-filter-btn ${filterStatus === s ? 'is-active' : ''}`}
onClick={() => setFilterStatus(s)}
>
{s}
</button>
))}
</div>
)}
</div>
{/* 목록 탭 — 카드 + 지도/상세 */}
{activeTab === '목록' && (
<div className="re-list-layout">
<div className="re-card-grid">
{filteredComplexes.length === 0 ? (
<p className="re-empty">등록된 단지가 없습니다.</p>
) : (
filteredComplexes.map((c) => (
<ComplexCard
key={c.id}
complex={c}
isSelected={selectedComplex?.id === c.id}
onClick={() => setSelectedComplex(c)}
/>
))
)}
</div>
<RightPanel
complexes={complexes}
selectedComplex={selectedComplex}
onSelectComplex={setSelectedComplex}
onEdit={() => { setEditingComplex(selectedComplex); setShowModal(true); }}
onDelete={() => handleDelete(selectedComplex.id)}
/>
</div>
)}
{activeTab === '일정' && (
<div className="re-panel">
<div className="re-panel__head">
<div>
<p className="re-panel__eyebrow">캘린더</p>
<h3>청약 일정</h3>
<p className="re-panel__sub">청약 시작·마감·당첨 발표일을 타임라인으로 확인합니다.</p>
</div>
</div>
<ScheduleView complexes={complexes} />
</div>
)}
{activeTab === '분석' && (
<PriceAnalysis complexes={complexes} />
)}
{showModal && (
<ComplexModal
complex={editingComplex}
onClose={() => { setShowModal(false); setEditingComplex(null); }}
onSave={handleModalSave}
/>
)}
</div>
);
};
export default RealEstate;