Skip to content

Commit

Permalink
feat: 호텔 전용 차트 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
eun-hak committed Oct 25, 2024
1 parent 7ff4b68 commit 097685f
Show file tree
Hide file tree
Showing 2 changed files with 378 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<number>(2022);
const [endYear, setEndYear] = useState<number>(2024);
const fetchData = async () => {
try {
const response = await axios.get<IStayRateData>(
`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 (
<div>
<div className="flex justify-end mb-4">
<div>
<select
id="startYear"
value={startYear}
onChange={(e) => setStartYear(parseInt(e.target.value))}
className="border rounded p-1">
{[2022, 2023, 2024].map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
~
<select
id="endYear"
value={endYear}
onChange={(e) => setEndYear(parseInt(e.target.value))}
className="border rounded p-1">
{[2022, 2023, 2024].map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
</div>
</div>

<div className="flex flex-col items-center justify-center h-full bg-gray-50 mb-[100px]">
<div className="w-full max-w-5xl h-[500px]">
<Chart type="bar" data={data} options={options} />
</div>
</div>
</div>
);
};

export default AccommoationRateChart;
Original file line number Diff line number Diff line change
@@ -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<number>(2022);
const [endYear, setEndYear] = useState<number>(2024);
const fetchData = async () => {
const response = await axios.get<IStayDayData>(
`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 (
<div>
<div className="flex justify-end"></div>
<div className="flex justify-end mb-4">
<div>
<select
id="startYear"
value={startYear}
onChange={(e) => setStartYear(parseInt(e.target.value))}
className="border rounded p-1">
{[2022, 2023, 2024].map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
~
<select
id="endYear"
value={endYear}
onChange={(e) => setEndYear(parseInt(e.target.value))}
className="border rounded p-1">
{[2022, 2023, 2024].map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
</div>
</div>

<div className="flex flex-col items-center justify-center h-full bg-gray-50 mb-[100px]">
<div className="w-full max-w-4xl h-[400px]">
{chartData ? (
<Bar data={chartData} options={options} />
) : (
<div>Loading chart...</div>
)}
</div>
</div>
</div>
);
};

export default AccommodationVisitorsChart;

0 comments on commit 097685f

Please sign in to comment.