팝업 수정 가능하게 수정

This commit is contained in:
dohyeons
2025-11-12 19:08:41 +09:00
parent 5e8e714e8a
commit 800bd85811
4 changed files with 359 additions and 55 deletions

View File

@@ -919,7 +919,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
// 첫 번째 데이터 소스의 새로고침 간격 사용 (초)
const firstDataSource = dataSources[0];
const refreshInterval = firstDataSource?.refreshInterval ?? 5;
// 0이면 자동 새로고침 비활성화
if (refreshInterval === 0) {
return;
@@ -1123,12 +1123,12 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
// 첫 번째 데이터 소스의 마커 종류 가져오기
const firstDataSource = dataSources?.[0];
const markerType = firstDataSource?.markerType || "circle";
let markerIcon: any;
if (typeof window !== "undefined") {
const L = require("leaflet");
const heading = marker.heading || 0;
if (markerType === "arrow") {
// 화살표 마커
markerIcon = L.divIcon({
@@ -1216,63 +1216,117 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
return (
<Marker key={marker.id} position={[marker.lat, marker.lng]} icon={markerIcon}>
<Popup maxWidth={350}>
<div className="max-w-[350px] min-w-[250px]">
{/* 제목 */}
<div className="mb-2 border-b pb-2">
<div className="text-base font-bold">{marker.name}</div>
{marker.source && <div className="text-muted-foreground mt-1 text-xs">📡 {marker.source}</div>}
</div>
<div className="max-w-[350px] min-w-[250px]" dir="ltr">
{/* 데이터 소스명만 표시 */}
{marker.source && (
<div className="mb-2 border-b pb-2">
<div className="text-muted-foreground text-xs">📡 {marker.source}</div>
</div>
)}
{/* 상세 정보 */}
<div className="space-y-2">
{marker.description && (
<div className="bg-muted rounded p-2">
<div className="text-foreground mb-1 text-xs font-semibold"> </div>
<div className="text-muted-foreground text-xs whitespace-pre-wrap">
{(() => {
try {
const parsed = JSON.parse(marker.description);
return (
<div className="space-y-1">
{parsed.incidenteTypeCd === "1" && (
<div className="text-destructive font-semibold">🚨 </div>
)}
{parsed.incidenteTypeCd === "2" && (
<div className="text-warning font-semibold">🚧 </div>
)}
{parsed.addressJibun && <div>📍 {parsed.addressJibun}</div>}
{parsed.addressNew && parsed.addressNew !== parsed.addressJibun && (
<div>📍 {parsed.addressNew}</div>
)}
{parsed.roadName && <div>🛣 {parsed.roadName}</div>}
{parsed.linkName && <div>🔗 {parsed.linkName}</div>}
{parsed.incidentMsg && (
<div className="mt-2 border-t pt-2">💬 {parsed.incidentMsg}</div>
)}
{parsed.eventContent && (
<div className="mt-2 border-t pt-2">📝 {parsed.eventContent}</div>
)}
{parsed.startDate && <div className="text-[10px]">🕐 {parsed.startDate}</div>}
{parsed.endDate && <div className="text-[10px]">🕐 : {parsed.endDate}</div>}
</div>
);
} catch {
return marker.description;
}
})()}
</div>
</div>
)}
{marker.description &&
(() => {
const firstDataSource = dataSources?.[0];
const popupFields = firstDataSource?.popupFields;
{marker.status && (
<div className="text-xs">
<span className="font-semibold">:</span> {marker.status}
</div>
)}
// popupFields가 설정되어 있으면 설정된 필드만 표시
if (popupFields && popupFields.length > 0) {
try {
const parsed = JSON.parse(marker.description);
return (
<div className="bg-muted rounded p-2">
<div className="text-foreground mb-1 text-xs font-semibold"> </div>
<div className="space-y-2">
{popupFields.map((field, idx) => {
const value = parsed[field.fieldName];
if (value === undefined || value === null) return null;
// 포맷팅 적용
let formattedValue = value;
if (field.format === "date" && value) {
formattedValue = new Date(value).toLocaleDateString("ko-KR");
} else if (field.format === "datetime" && value) {
formattedValue = new Date(value).toLocaleString("ko-KR");
} else if (field.format === "number" && typeof value === "number") {
formattedValue = value.toLocaleString();
} else if (
field.format === "url" &&
typeof value === "string" &&
value.startsWith("http")
) {
return (
<div key={idx} className="text-xs">
<span className="text-muted-foreground font-medium">{field.label}:</span>{" "}
<a
href={value}
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
</a>
</div>
);
}
return (
<div key={idx} className="text-xs">
<span className="text-muted-foreground font-medium">{field.label}:</span>{" "}
<span className="text-foreground">{String(formattedValue)}</span>
</div>
);
})}
</div>
</div>
);
} catch (error) {
return (
<div className="bg-muted rounded p-2">
<div className="text-foreground mb-1 text-xs font-semibold"> </div>
<div className="text-muted-foreground text-xs">{marker.description}</div>
</div>
);
}
}
// popupFields가 없으면 전체 데이터 표시 (기본 동작)
try {
const parsed = JSON.parse(marker.description);
return (
<div className="bg-muted rounded p-2">
<div className="text-foreground mb-1 text-xs font-semibold"> </div>
<div className="space-y-2">
{Object.entries(parsed).map(([key, value], idx) => {
if (value === undefined || value === null) return null;
// 좌표 필드는 제외 (하단에 별도 표시)
if (["lat", "lng", "latitude", "longitude", "x", "y"].includes(key)) return null;
return (
<div key={idx} className="text-xs">
<span className="text-muted-foreground font-medium">{key}:</span>{" "}
<span className="text-foreground">{String(value)}</span>
</div>
);
})}
</div>
</div>
);
} catch (error) {
return (
<div className="bg-muted rounded p-2">
<div className="text-foreground mb-1 text-xs font-semibold"> </div>
<div className="text-muted-foreground text-xs">{marker.description}</div>
</div>
);
}
})()}
{/* 좌표 */}
<div className="text-muted-foreground border-t pt-2 text-[10px]">
📍 {marker.lat.toFixed(6)}, {marker.lng.toFixed(6)}
{marker.lat.toFixed(6)}, {marker.lng.toFixed(6)}
</div>
</div>
</div>
@@ -1280,7 +1334,6 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
</Marker>
);
})}
</MapContainer>
)}
</div>