Files
web-page/src/pages/realestate/realEstateUtils.js

142 lines
4.9 KiB
JavaScript

import L from 'leaflet';
// ── 샘플 데이터 ────────────────────────────────────────────────────────────────
export const SAMPLE_COMPLEXES = [
{
id: 1,
name: '래미안 원베일리',
address: '서울 서초구 반포동',
lat: 37.5065,
lng: 126.9942,
units: 2990,
types: ['59㎡', '84㎡', '114㎡'],
avgPricePerPyeong: 9500,
subscriptionStart: '2024-01-08',
subscriptionEnd: '2024-01-10',
resultDate: '2024-01-15',
status: '완료',
priority: 'high',
tags: ['강남권', '한강뷰', '역세권', '브랜드'],
naverUrl: '',
floorPlanUrl: '',
memo: '반포동 재건축 단지. 경쟁률 수백대 1 예상.',
},
{
id: 2,
name: '올림픽파크 포레온',
address: '서울 강동구 둔촌동',
lat: 37.5284,
lng: 127.1340,
units: 12032,
types: ['39㎡', '49㎡', '59㎡', '84㎡'],
avgPricePerPyeong: 3800,
subscriptionStart: '2022-12-05',
subscriptionEnd: '2022-12-07',
resultDate: '2022-12-12',
status: '완료',
priority: 'high',
tags: ['대단지', '역세권', '재건축'],
naverUrl: '',
floorPlanUrl: '',
memo: '역대 최대 규모 재건축 단지.',
},
{
id: 3,
name: '힐스테이트 동탄',
address: '경기 화성시 동탄2신도시',
lat: 37.2001,
lng: 127.0724,
units: 1534,
types: ['59㎡', '74㎡', '84㎡'],
avgPricePerPyeong: 1850,
subscriptionStart: '2026-04-10',
subscriptionEnd: '2026-04-12',
resultDate: '2026-04-17',
status: '청약예정',
priority: 'normal',
tags: ['동탄2', '신도시', 'SRT'],
naverUrl: '',
floorPlanUrl: '',
memo: '동탄 핵심 입지. 교통 개선 기대.',
},
{
id: 4,
name: '롯데캐슬 마곡',
address: '서울 강서구 마곡동',
lat: 37.5626,
lng: 126.8295,
units: 868,
types: ['59㎡', '84㎡'],
avgPricePerPyeong: 4200,
subscriptionStart: '2026-03-20',
subscriptionEnd: '2026-03-22',
resultDate: '2026-03-27',
status: '청약중',
priority: 'high',
tags: ['마곡', '9호선', '공항철도'],
naverUrl: '',
floorPlanUrl: '',
memo: '마곡 업무지구 직주근접. 강서 핵심 입지.',
},
];
export const STATUS_CONFIG = {
'청약예정': { color: '#00d4ff', bg: 'rgba(0,212,255,0.12)' },
'청약중': { color: '#34d399', bg: 'rgba(52,211,153,0.12)' },
'결과발표': { color: '#f59e0b', bg: 'rgba(245,158,11,0.12)' },
'완료': { color: '#6b7280', bg: 'rgba(107,114,128,0.10)' },
};
export const PRIORITY_LABELS = { high: '★ 최우선', normal: '보통', low: '낮음' };
export const EMPTY_FORM = {
name: '', address: '', lat: '', lng: '',
units: '', types: '', avgPricePerPyeong: '',
subscriptionStart: '', subscriptionEnd: '', resultDate: '',
status: '청약예정', priority: 'normal',
tags: '', naverUrl: '', floorPlanUrl: '', memo: '',
};
export const TABS = ['목록', '일정', '분석'];
// ── 유틸 함수 ──────────────────────────────────────────────────────────────────
export const formatDate = (d) => {
if (!d) return '-';
return new Date(d).toLocaleDateString('ko-KR', { year: 'numeric', month: 'long', day: 'numeric' });
};
export const formatPrice = (v) => {
if (!v) return '-';
return `${v.toLocaleString()}만원`;
};
export const getDDays = (dateStr) => {
if (!dateStr) return null;
const target = new Date(dateStr);
const today = new Date();
today.setHours(0, 0, 0, 0);
target.setHours(0, 0, 0, 0);
const diff = Math.ceil((target - today) / (1000 * 60 * 60 * 24));
if (diff === 0) return 'D-Day';
if (diff > 0) return `D-${diff}`;
return `D+${Math.abs(diff)}`;
};
export const createMarkerIcon = (status, isSelected = false) => {
const cfg = STATUS_CONFIG[status] || STATUS_CONFIG['완료'];
const size = isSelected ? 18 : 12;
return L.divIcon({
className: '',
html: `<div style="
width:${size}px;height:${size}px;border-radius:50%;
background:${cfg.color};
box-shadow:0 0 ${isSelected ? 16 : 8}px ${cfg.color};
border:2px solid rgba(255,255,255,${isSelected ? 0.6 : 0.25});
transition:all 0.2s;
"></div>`,
iconSize: [size, size],
iconAnchor: [size / 2, size / 2],
popupAnchor: [0, -(size / 2 + 4)],
});
};