forked from Moaguide-develop/moaguide_front
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
378 additions
and
0 deletions.
There are no files selected for viewing
210 changes: 210 additions & 0 deletions
210
src/components/product/detail/building/chart/AccommodationRateChart.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
168 changes: 168 additions & 0 deletions
168
src/components/product/detail/building/chart/AccommodationVisitorsChart.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |