Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
738b1f7735 | ||
|
|
3381e16573 | ||
|
|
1b57670830 |
@@ -10,4 +10,5 @@ NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
|
|||||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||||
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com
|
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com
|
||||||
|
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
|
||||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083
|
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083
|
||||||
+3
-4
@@ -4,16 +4,15 @@ NEXT_PUBLIC_AUTH_USER_ID = token-id
|
|||||||
NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
||||||
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
||||||
NEXT_PUBLIC_PORT = 3002
|
NEXT_PUBLIC_PORT = 3002
|
||||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3002
|
# NEXT_PUBLIC_PROXY_HOST = http://localhost:3002
|
||||||
|
NEXT_PUBLIC_PROXY_HOST = http://sn.onasys.co.kr:3002
|
||||||
NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
|
NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
|
||||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083
|
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083
|
||||||
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com
|
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com
|
||||||
NEXT_PUBLIC_DEV_CM_LOGIN_PAGE_URL = http://localhost:3000/auth/login
|
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
|
||||||
NEXT_PUBLIC_VDI_DEV_CM_LOGIN_PAGE_URL = http://localhost:3000/auth/login
|
|
||||||
NEXT_PUBLIC_VDI_DEV_CM_LOGIN_PAGE_URL = http://localhost:3000/auth/login
|
|
||||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883
|
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883
|
||||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.222:8883
|
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.222:8883
|
||||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883
|
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883
|
||||||
@@ -11,3 +11,4 @@ NEXT_PUBLIC_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BE
|
|||||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||||
NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||||
|
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
NEXT_PUBLIC_SITE_ID = SN
|
||||||
|
NEXT_PUBLIC_CLAIM_NAME = Authorization
|
||||||
|
NEXT_PUBLIC_AUTH_USER_ID = token-id
|
||||||
|
NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
||||||
|
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
||||||
|
NEXT_PUBLIC_PORT = 3000
|
||||||
|
NEXT_PUBLIC_PROXY_HOST = https://sn.hasuon.or.kr
|
||||||
|
NEXT_PUBLIC_GEOSERVER_URL = http://172.29.143.87:8080/geoserver
|
||||||
|
NEXT_PUBLIC_LX_GEOSERVER_URL = http://172.29.143.87:8080/geoserver_swl
|
||||||
|
NEXT_PUBLIC_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BEF39DAFEC293AB6730150F222AC
|
||||||
|
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||||
|
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||||
|
NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||||
|
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
BarElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
type ChartOptions,
|
||||||
|
} from "chart.js";
|
||||||
|
import { Bar } from "react-chartjs-2";
|
||||||
|
|
||||||
|
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const labels = ["한강", "낙동강", "영산강", "금강"];
|
||||||
|
|
||||||
|
// 데이터: [기타, 오수관로, 우수관로]
|
||||||
|
const datasets = [
|
||||||
|
{
|
||||||
|
label: "기타",
|
||||||
|
data: [15481, 6959, 1938, 1967],
|
||||||
|
backgroundColor: "rgba(255, 178, 150, 0.9)", // 연한 주황(파스텔톤)
|
||||||
|
borderColor: "rgba(255,178,150,1)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "오수관로",
|
||||||
|
data: [15878, 16336, 5670, 8073],
|
||||||
|
backgroundColor: "rgba(180, 240, 190, 0.95)", // 연한 초록(파스텔톤)
|
||||||
|
borderColor: "rgba(180,240,190,1)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "우수관로",
|
||||||
|
data: [12892, 10491, 4280, 4838],
|
||||||
|
backgroundColor: "rgba(170, 210, 245, 0.95)", // 연한 파랑(파스텔톤)
|
||||||
|
borderColor: "rgba(170,210,245,1)",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 총합(라벨당) 계산
|
||||||
|
const totals = labels.map((_, i) =>
|
||||||
|
datasets.reduce((s, ds) => s + (ds.data[i] as number), 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
const options: ChartOptions<"bar"> = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: "top",
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: (ctx) => `${ctx.dataset.label}: ${Number(ctx.parsed.y).toLocaleString()}km`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true,
|
||||||
|
beginAtZero: true,
|
||||||
|
ticks: {
|
||||||
|
callback: (value) => `${Number(value).toLocaleString()}km`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 각 스택 위에 총합 표시
|
||||||
|
animation: {
|
||||||
|
onComplete: function (animation) {
|
||||||
|
const chart = animation.chart;
|
||||||
|
const ctx = chart.ctx;
|
||||||
|
const lastMeta = chart.getDatasetMeta(datasets.length - 1);
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = "#111";
|
||||||
|
ctx.font = "gray bold 12px Arial";
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
|
||||||
|
totals.forEach((total, i) => {
|
||||||
|
const bar = lastMeta.data[i];
|
||||||
|
if (!bar) return;
|
||||||
|
const x = bar.x;
|
||||||
|
// y는 마지막 스택의 y 좌표(스택의 최상단)에서 조금 위로
|
||||||
|
const y = bar.y - 8;
|
||||||
|
ctx.fillText(`총 ${total.toLocaleString()}km`, x, y);
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = { labels, datasets };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ width: "100%", maxWidth: 800, height: 360 }}>
|
||||||
|
<Bar options={options} data={data} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
BarElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
type ChartOptions,
|
||||||
|
} from "chart.js";
|
||||||
|
import { Bar } from "react-chartjs-2";
|
||||||
|
import { Card, CardHeader, CardTitle, CardContent } from "@common/business_components/ui/chart/lib/card";
|
||||||
|
import { Button } from "@common/components";
|
||||||
|
|
||||||
|
// ChartJS 컴포넌트 등록
|
||||||
|
ChartJS.register(
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
BarElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function TestPage() {
|
||||||
|
// 차트 데이터
|
||||||
|
const labels = ["한강", "낙동강", "영산강", "금강"];
|
||||||
|
|
||||||
|
const datasets = [
|
||||||
|
{
|
||||||
|
label: '20-25년',
|
||||||
|
data: [3859, 1066, 1929, 2828],
|
||||||
|
backgroundColor: '#3B82F6', // 파란색
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '25년-30년',
|
||||||
|
data: [3153, 1420, 1204, 2877],
|
||||||
|
backgroundColor: '#EF4444', // 빨간색
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '30년이상',
|
||||||
|
data: [9404, 1835, 1821, 8115],
|
||||||
|
backgroundColor: '#F87171', // 연한 빨간색/분홍색
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 각 지역별 총 길이 계산
|
||||||
|
const totalLengths = [44251, 33786, 11888, 14878];
|
||||||
|
|
||||||
|
// 차트 옵션 설정
|
||||||
|
const options: ChartOptions<"bar"> = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: {
|
||||||
|
display: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
min: 0,
|
||||||
|
max: 10000,
|
||||||
|
ticks: {
|
||||||
|
stepSize: 2000,
|
||||||
|
callback: function(value) {
|
||||||
|
return value + 'km';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: 'rgba(200, 200, 200, 0.3)',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
align: 'start',
|
||||||
|
labels: {
|
||||||
|
boxWidth: 15,
|
||||||
|
padding: 15,
|
||||||
|
usePointStyle: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
return `${context.dataset.label}: ${context.parsed.y}km`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 총 길이 데이터 표시를 위한 애니메이션 후 함수 설정
|
||||||
|
animation: {
|
||||||
|
onComplete: function(animation) {
|
||||||
|
const chart = animation.chart;
|
||||||
|
const ctx = chart.ctx;
|
||||||
|
|
||||||
|
// 각 데이터셋의 마지막 데이터셋의 인덱스
|
||||||
|
const lastDatasetIndex = datasets.length - 1;
|
||||||
|
|
||||||
|
// 텍스트 스타일 설정
|
||||||
|
ctx.font = '11px Arial';
|
||||||
|
ctx.fillStyle = '#333';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
|
||||||
|
// 각 지역별로 총 길이 표시
|
||||||
|
labels.forEach((_, i) => {
|
||||||
|
const xPos = chart.scales.x ? chart.scales.x.getPixelForTick(i) : 0;
|
||||||
|
const yPos = chart.chartArea.top; // 차트 영역 최상단 위에 표시
|
||||||
|
|
||||||
|
// 막대 위에 총 길이 텍스트 표시
|
||||||
|
if (totalLengths[i]) ctx.fillText(`총 ${totalLengths[i].toLocaleString()}km`, xPos, yPos);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
labels,
|
||||||
|
datasets,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="w-full">
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||||
|
<CardTitle className="text-base">노후하수관로현황</CardTitle>
|
||||||
|
<Button size="sm">더보기</Button>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="pb-4">
|
||||||
|
{/* 너비 400px, 높이 150px로 조정 */}
|
||||||
|
<div className="w-[400px] h-[150px] mx-auto">
|
||||||
|
<Bar options={options} data={data} />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
// ...existing code...
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Chart as ChartJS, ArcElement, Tooltip, Legend, type ChartOptions } from "chart.js";
|
||||||
|
import { Pie } from "react-chartjs-2";
|
||||||
|
|
||||||
|
ChartJS.register(ArcElement, Tooltip, Legend);
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const labels = ["한강", "낙동강", "영산강", "금강"];
|
||||||
|
const datasets = [
|
||||||
|
{ label: "20년 미만", data: [27835, 29465, 6934, 1058], color: "rgba(169, 169, 169, 0.8)" },
|
||||||
|
{ label: "20-25년", data: [3859, 1066, 1929, 2828], color: "rgba(255, 160, 122, 0.8)" },
|
||||||
|
{ label: "25년-30년", data: [3153, 1420, 1204, 2877], color: "rgba(152, 251, 152, 0.8)" },
|
||||||
|
{ label: "30년이상", data: [9404, 1835, 1821, 8115], color: "rgba(135, 206, 235, 0.8)" },
|
||||||
|
];
|
||||||
|
// 각 지역별 총 길이 계산
|
||||||
|
const totalLengths = [44251, 33786, 11888, 14878];
|
||||||
|
// 외부 툴팁을 사용하여 컨테이너 넘침과 관계없이 전체 툴팁을 보여주고,
|
||||||
|
// 각 항목의 전체에서 차지하는 비율(%)을 표시합니다.
|
||||||
|
const pieOptions: ChartOptions<"pie"> = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: { display: false },
|
||||||
|
tooltip: {
|
||||||
|
enabled: false, // 기본 캔버스 툴팁 비활성화 (외부 툴팁 사용)
|
||||||
|
external: function (context) {
|
||||||
|
const { chart, tooltip } = context;
|
||||||
|
let tooltipEl = document.getElementById("chartjs-external-tooltip");
|
||||||
|
|
||||||
|
// tooltip DOM이 없으면 생성
|
||||||
|
if (!tooltipEl) {
|
||||||
|
tooltipEl = document.createElement("div");
|
||||||
|
tooltipEl.id = "chartjs-external-tooltip";
|
||||||
|
tooltipEl.style.position = "absolute";
|
||||||
|
tooltipEl.style.pointerEvents = "auto";
|
||||||
|
tooltipEl.style.background = "rgba(0,0,0,0.75)";
|
||||||
|
tooltipEl.style.color = "#fff";
|
||||||
|
tooltipEl.style.padding = "6px 8px";
|
||||||
|
tooltipEl.style.borderRadius = "6px";
|
||||||
|
tooltipEl.style.fontSize = "12px";
|
||||||
|
tooltipEl.style.zIndex = "9999";
|
||||||
|
tooltipEl.style.transform = "translate(-50%, -120%)";
|
||||||
|
document.body.appendChild(tooltipEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 사라짐 처리
|
||||||
|
if (tooltip.opacity === 0) {
|
||||||
|
tooltipEl.style.opacity = "0";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 툴팁 내용 구성 (파이의 전체 합 대비 비율 표시)
|
||||||
|
const data = chart.data;
|
||||||
|
const dataset = data.datasets && data.datasets[0] && data.datasets[0].data
|
||||||
|
? (data.datasets[0].data as number[])
|
||||||
|
: [];
|
||||||
|
const total = dataset.reduce((s, n) => s + (Number(n) || 0), 0);
|
||||||
|
|
||||||
|
const innerHtmlParts: string[] = [];
|
||||||
|
if (tooltip.dataPoints) {
|
||||||
|
tooltip.dataPoints.forEach((dp) => {
|
||||||
|
const idx = dp.dataIndex;
|
||||||
|
const label = (data.labels && data.labels[idx]) || dp.label || "";
|
||||||
|
const value = Number(dp.raw ?? dp.parsed ?? 0);
|
||||||
|
const pct = total > 0 ? (value / total) * 100 : 0;
|
||||||
|
innerHtmlParts.push(
|
||||||
|
`<div style="white-space:nowrap">
|
||||||
|
<strong style="display:inline-block;width:8px;height:8px;border-radius:2px;background:${(dp as any).element?.options?.backgroundColor || (dataset as any)[idx] || "#999"};margin-right:6px;vertical-align:middle"></strong>
|
||||||
|
<span style="vertical-align:middle">${label}: ${value.toLocaleString()}km</span>
|
||||||
|
<span style="margin-left:8px;color:#ddd">(${pct.toFixed(1)}%)</span>
|
||||||
|
</div>`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltipEl.innerHTML = innerHtmlParts.join("");
|
||||||
|
tooltipEl.style.opacity = "1";
|
||||||
|
|
||||||
|
// 위치: 차트 캔버스 기준 툴팁 caret 위치에 맞춤 (윈도우 스크롤 보정 포함)
|
||||||
|
const canvasRect = chart.canvas.getBoundingClientRect();
|
||||||
|
const caretX = tooltip.caretX ?? (canvasRect.left + canvasRect.width / 2);
|
||||||
|
const caretY = tooltip.caretY ?? (canvasRect.top + canvasRect.height / 2);
|
||||||
|
tooltipEl.style.left = `${canvasRect.left + window.pageXOffset + caretX}px`;
|
||||||
|
tooltipEl.style.top = `${canvasRect.top + window.pageYOffset + caretY}px`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 상단에 공통 범례(연도) 가로로 표시
|
||||||
|
const SharedLegend = () => (
|
||||||
|
<div className="flex gap-4 items-center justify-center">
|
||||||
|
{datasets.map((d) => (
|
||||||
|
<div key={d.label} className="flex items-center gap-2">
|
||||||
|
<span style={{ width: 12, height: 12, backgroundColor: d.color, borderRadius: 3, display: "inline-block" }} />
|
||||||
|
<span className="text-xl text-gray-600">{d.label}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 레이블(강) 당 pie 데이터 생성 (레이블은 아래로 이동)
|
||||||
|
const Charts = () =>
|
||||||
|
labels.map((river, i) => {
|
||||||
|
const data = {
|
||||||
|
labels: datasets.map((d) => d.label),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
data: datasets.map((d) => d.data[i]),
|
||||||
|
backgroundColor: datasets.map((d) => d.color),
|
||||||
|
borderColor: datasets.map(() => "rgba(255,255,255,0.8)"),
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={river} className="flex flex-col items-center gap-2">
|
||||||
|
<div style={{ width: 80, height: 80 }}>
|
||||||
|
<Pie data={data} options={pieOptions} />
|
||||||
|
</div>
|
||||||
|
{/* 레이블을 차트 하단으로 이동 */}
|
||||||
|
<div className="text-lg font-medium text-gray-600">{totalLengths[i]?.toLocaleString()} km</div>
|
||||||
|
<div className="text-xl font-bold text-gray-600">{river}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4 w-[400px]">
|
||||||
|
{/* 상단 공통 범례 */}
|
||||||
|
<div className="mb-3">
|
||||||
|
<SharedLegend />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start justify-between gap-6">
|
||||||
|
<Charts />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// ...existing code...
|
||||||
@@ -10,7 +10,7 @@ import { getCookie } from "@/shared/lib";
|
|||||||
import { LoginApi } from "../api/login-service";
|
import { LoginApi } from "../api/login-service";
|
||||||
|
|
||||||
|
|
||||||
const LOGIN_PAGE_URL = process.env.NEXT_PUBLIC_DEV_CM_LOGIN_PAGE_URL || "http://localhost:3000/auth/login"; // 로그인 페이지 URL
|
const LOGIN_PAGE_URL = process.env.NEXT_PUBLIC_CM_LOGIN_PAGE_URL || "http://dev.onasys.co.kr:3000/auth/login"; // 로그인 페이지 URL
|
||||||
|
|
||||||
const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
|
const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|||||||
Reference in New Issue
Block a user