From 097685f058575d48bef4dd0c942ebfbd5090c9da Mon Sep 17 00:00:00 2001 From: eunhak Date: Fri, 25 Oct 2024 23:43:36 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=98=B8=ED=85=94=20=EC=A0=84=EC=9A=A9?= =?UTF-8?q?=20=EC=B0=A8=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../building/chart/AccommodationRateChart.tsx | 210 ++++++++++++++++++ .../chart/AccommodationVisitorsChart.tsx | 168 ++++++++++++++ 2 files changed, 378 insertions(+) create mode 100644 src/components/product/detail/building/chart/AccommodationRateChart.tsx create mode 100644 src/components/product/detail/building/chart/AccommodationVisitorsChart.tsx diff --git a/src/components/product/detail/building/chart/AccommodationRateChart.tsx b/src/components/product/detail/building/chart/AccommodationRateChart.tsx new file mode 100644 index 00000000..246acc04 --- /dev/null +++ b/src/components/product/detail/building/chart/AccommodationRateChart.tsx @@ -0,0 +1,210 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; +import { usePathname } from 'next/navigation'; +import { IStayRateData } from '@/types/BuildingProductType'; +import { Chart } from 'react-chartjs-2'; +import { TooltipItem } from 'chart.js'; + +const AccommoationRateChart = () => { + const pathname = usePathname(); + const lastSegment = pathname.split('/').pop(); + const [startYear, setStartYear] = useState(2022); + const [endYear, setEndYear] = useState(2024); + const fetchData = async () => { + try { + const response = await axios.get( + `https://api.moaguide.com/detail/building/stay/rate/${lastSegment}?syear=${startYear}&eyear=${endYear}` + ); + return response.data; + } catch (error) { + console.error('Error fetching data:', error); + throw error; // 에러를 다시 던져서 useQuery의 onError로 전달 + } + }; + + const { + data: CopyRightFeeData, + isLoading, + error + } = useQuery({ + queryKey: ['AccommodationRate', startYear, lastSegment, endYear], + queryFn: fetchData + }); + + const AccommodationRateDay = CopyRightFeeData?.object.map((item) => item.day); + + const AccommodationRatevalue = CopyRightFeeData?.object.map((item) => item.value); + + const AccommodationRateRate = CopyRightFeeData?.object.map((item) => item.rate); + + const data = { + labels: AccommodationRateDay, + datasets: [ + { + type: 'bar' as const, + label: '평균 숙박 일수', + data: AccommodationRatevalue, + backgroundColor: (context: any) => { + const chart = context.chart; + const { ctx, chartArea } = chart; + + if (!chartArea) { + return null; + } + + const gradient = ctx.createLinearGradient( + 0, + chartArea.bottom, + 0, + chartArea.top + ); + gradient.addColorStop(0, 'rgba(140, 192, 250, 0.8)'); + gradient.addColorStop(1, '#2575d1'); + + return gradient; + }, + yAxisID: 'y1', + borderRadius: 5, + barThickness: 20, + barPercentage: 0.2, + categoryPercentage: 0.5, + + datalabels: { + display: false, + align: 'end' as const, + anchor: 'end' as const, + color: '#000', + formatter: (value: number) => `${value}` + } + }, + { + type: 'line' as const, + label: '숙박 방문자 비율', + data: AccommodationRateRate, + borderColor: '#0000FF', + backgroundColor: '#0000FF', + pointBackgroundColor: '#0000FF', + pointBorderColor: '#0000FF', + fill: false, + tension: 0.4, + yAxisID: 'y2', + pointStyle: 'circle', + pointRadius: 6, + datalabels: { + display: false + } + } + ] + }; + + const options = { + responsive: true, + maintainAspectRatio: false, + aspectRatio: 2, + scales: { + x: { + display: true, + grid: { + display: false + } + }, + y1: { + type: 'linear' as const, + position: 'left' as const, + grid: { + display: false + }, + beginAtZero: true, + // max: BarnewVariable, + ticks: { + stepSize: 10 + } + }, + y2: { + type: 'linear' as const, + position: 'right' as const, + grid: { + display: false + }, + // max: LinenewVariable, + beginAtZero: true + } + }, + plugins: { + legend: { + display: false, + position: 'top' as const + }, + tooltip: { + enabled: true, + intersect: false, + callbacks: { + label: function (context: TooltipItem<'bar' | 'line'>) { + const label = context.dataset.label || ''; + const value = context.raw as number; + const unit = context.dataset.type === 'bar' ? '일' : '%'; + // return `${label}: ${value.toLocaleString()}${unit}`; + return ``; + }, + beforeBody: function (context: TooltipItem<'bar' | 'line'>[]) { + const index = context[0].dataIndex; + const datasets = context[0].chart.data.datasets; + + let tooltipText = ''; + datasets.forEach((dataset) => { + const value = dataset.data[index] as number; + const unit = dataset.type === 'bar' ? '일' : '%'; + tooltipText += `${dataset.label}: ${value.toLocaleString()}${unit}\n`; + }); + + return tooltipText; + } + } + }, + datalabels: { + display: true + } + } + }; + + return ( +
+
+
+ + ~ + +
+
+ +
+
+ +
+
+
+ ); +}; + +export default AccommoationRateChart; diff --git a/src/components/product/detail/building/chart/AccommodationVisitorsChart.tsx b/src/components/product/detail/building/chart/AccommodationVisitorsChart.tsx new file mode 100644 index 00000000..b20d6a86 --- /dev/null +++ b/src/components/product/detail/building/chart/AccommodationVisitorsChart.tsx @@ -0,0 +1,168 @@ +import React, { useState } from 'react'; +import { Bar } from 'react-chartjs-2'; +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; +import { usePathname } from 'next/navigation'; +import { IStayDayData } from '@/types/BuildingProductType'; +import { TooltipItem } from 'chart.js'; + +const AccommodationVisitorsChart = () => { + const pathname = usePathname(); + const lastSegment = pathname.split('/').pop(); // 경로의 마지막 부분 추출 + const [startYear, setStartYear] = useState(2022); + const [endYear, setEndYear] = useState(2024); + const fetchData = async () => { + const response = await axios.get( + `https://api.moaguide.com/detail/building/stay/day/${lastSegment}?syear=${startYear}&eyear=${endYear}` + ); + return response.data; + }; + // useQuery로 데이터 패칭 + const { data, error, isLoading } = useQuery({ + queryKey: ['accommodationVisitordata', lastSegment, startYear, endYear], + queryFn: fetchData + }); + + const accommodationVisitorDay = data?.object.map((item) => item.day); + + const accommodationVisitorData = (() => { + if (!data?.object) return []; + + const totalArray = data.object.map((item) => item.total); + const nodayArray = data.object.map((item) => item.noday); + const onedayArray = data.object.map((item) => item.oneday); + const twodayArray = data.object.map((item) => item.twoday); + const threedayArray = data.object.map((item) => item.threeday); + + return [totalArray, nodayArray, onedayArray, twodayArray, threedayArray]; + })(); + + const chartData = { + labels: accommodationVisitorDay, + datasets: [ + { + label: '전체', + data: accommodationVisitorData[0], + backgroundColor: '#89a3ff' + }, + { + label: '무박', + data: accommodationVisitorData[1], + backgroundColor: '#6243ee' + }, + { + label: '1박', + data: accommodationVisitorData[2], + backgroundColor: '#aea0e9' + }, + { + label: '2박', + data: accommodationVisitorData[3], + backgroundColor: '#3343f1' + }, + { + label: '3박이상', + data: accommodationVisitorData[4], + backgroundColor: '#e435ed' + } + ] + }; + + const options = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: true, + position: 'bottom' as const + }, + tooltip: { + enabled: true, + intersect: false, + callbacks: { + label: function (context: TooltipItem<'bar'>) { + const label = context.dataset.label || ''; + const value = context.raw; + return ``; + }, + beforeBody: function (context: TooltipItem<'bar'>[]) { + const index = context[0].dataIndex; + const datasets = context[0].chart.data.datasets; + + let tooltipText = ''; + datasets.forEach((dataset) => { + const value = dataset.data[index] as number; + tooltipText += `${dataset.label}: ${value.toLocaleString()}명\n`; + }); + + return tooltipText; + } + } + }, + datalabels: { + display: false // 데이터 레이블 숨기기 + } + // customText: customTextPlugin + }, + scales: { + x: { + stacked: true, + grid: { + display: false + } + }, + y: { + stacked: true, + beginAtZero: true, + grid: { + display: true + } + } + } + }; + + return ( +
+
+
+
+ + ~ + +
+
+ +
+
+ {chartData ? ( + + ) : ( +
Loading chart...
+ )} +
+
+
+ ); +}; + +export default AccommodationVisitorsChart;