diff --git a/package-lock.json b/package-lock.json index c99a842..bc575a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,7 +71,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -3229,7 +3228,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.12.tgz", "integrity": "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/query-core": "5.90.12" }, @@ -3340,7 +3338,6 @@ "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -3351,7 +3348,6 @@ "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3431,7 +3427,6 @@ "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", @@ -3679,7 +3674,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "devOptional": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3939,7 +3933,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4735,7 +4728,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6959,7 +6951,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6969,7 +6960,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -6989,7 +6979,6 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", - "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -7090,8 +7079,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -7943,7 +7931,6 @@ "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "devOptional": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -8118,7 +8105,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8332,7 +8318,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz", "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -8724,7 +8709,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -8782,7 +8766,6 @@ "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -9051,7 +9034,6 @@ "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/apis/banner/bannerApi.ts b/src/apis/banner/bannerApi.ts new file mode 100644 index 0000000..b863490 --- /dev/null +++ b/src/apis/banner/bannerApi.ts @@ -0,0 +1,27 @@ +import instance from '../axios/instance'; + +export interface BannerApiResponse { + Bitcoin: 'RISE,' | 'DECLINE,' | 'STABLE'; + Ethereum: 'RISE,' | 'DECLINE,' | 'STABLE'; + Solana: 'RISE,' | 'DECLINE,' | 'STABLE'; +} + +export interface TransformedBannerData { + value: string; + status: 'RISE,' | 'DECLINE,' | 'STABLE'; +} + +const bannerApi = async (): Promise => { + const response = await instance.get( + `api/emotional-index/change-directions`, + ); + + const data = response.data; + + return Object.entries(data).map(([key, value]) => ({ + value: key, + status: value, + })); +}; + +export default bannerApi; diff --git a/src/apis/chart/chartApi.ts b/src/apis/chart/chartApi.ts new file mode 100644 index 0000000..8430617 --- /dev/null +++ b/src/apis/chart/chartApi.ts @@ -0,0 +1,23 @@ +import instance from '../axios/instance'; + +export interface chartApiProps { + type?: 'ALL' | 'Bitcoin' | 'Ethereum' | 'Solana'; +} + +export interface ChartDataPoint { + time: string; + Bitcoin?: number; + Ethereum?: number; + Solana?: number; +} + +const chartApi = async ({ type = 'ALL' }: chartApiProps = {}): Promise< + ChartDataPoint[] +> => { + const response = await instance.get(`api/emotional-index`, { + params: { type }, + }); + return response.data; +}; + +export default chartApi; diff --git a/src/assets/banner/stable.png b/src/assets/banner/stable.png new file mode 100644 index 0000000..0d88ab1 Binary files /dev/null and b/src/assets/banner/stable.png differ diff --git a/src/hooks/useQuery/useGetBannerData.ts b/src/hooks/useQuery/useGetBannerData.ts new file mode 100644 index 0000000..c50516e --- /dev/null +++ b/src/hooks/useQuery/useGetBannerData.ts @@ -0,0 +1,21 @@ +import { useQuery, type UseQueryResult } from '@tanstack/react-query'; +import bannerApi, { + type TransformedBannerData, +} from '../../apis/banner/bannerApi'; + +const QUERY_KEYS = { + bannerData: ['bannerData'] as const, +}; + +function useGetBannerData(): UseQueryResult { + return useQuery({ + queryKey: ['bannerData'], + queryFn: bannerApi, + staleTime: 1 * 60 * 1000, + refetchInterval: 1 * 60 * 1000, + refetchIntervalInBackground: true, + }); +} + +export default useGetBannerData; +export { QUERY_KEYS }; diff --git a/src/hooks/useQuery/useGetChartData.ts b/src/hooks/useQuery/useGetChartData.ts new file mode 100644 index 0000000..9c24185 --- /dev/null +++ b/src/hooks/useQuery/useGetChartData.ts @@ -0,0 +1,27 @@ +import { useQuery, type UseQueryResult } from '@tanstack/react-query'; +import chartApi, { + type chartApiProps, + type ChartDataPoint, +} from '../../apis/chart/chartApi'; + +interface UseGetChartDataProps { + type?: chartApiProps['type']; + enabled?: boolean; +} + +function useGetChartData({ + type = 'ALL', + enabled = true, +}: UseGetChartDataProps = {}): UseQueryResult { + return useQuery({ + queryKey: ['chartData', type], + queryFn: () => chartApi({ type }), + enabled, + staleTime: 1 * 60 * 1000, + gcTime: 5 * 60 * 1000, + refetchOnWindowFocus: true, + retry: 2, + }); +} + +export default useGetChartData; diff --git a/src/pages/home/components/Banner/Banner.tsx b/src/pages/home/components/Banner/Banner.tsx index 2e69959..bc859fb 100644 --- a/src/pages/home/components/Banner/Banner.tsx +++ b/src/pages/home/components/Banner/Banner.tsx @@ -1,19 +1,49 @@ import bannerLogo from '../../../../assets/banner/banner.png'; import up from '../../../../assets/banner/up.png'; import down from '../../../../assets/banner/down.png'; +import stable from '../../../../assets/banner/stable.png'; import { motion } from 'framer-motion'; - -const stocks = [ - { value: 'Bitcoin', status: 'up' }, - { value: 'Ethereum', status: 'down' }, - { value: 'Solana', status: 'up' }, -]; +import useGetBannerData from '../../../../hooks/useQuery/useGetBannerData'; function Banner() { + const { data } = useGetBannerData(); + + const stocks = data || [ + { value: 'Bitcoin', status: 'RISE' }, + { value: 'Ethereum', status: 'STABLE' }, + { value: 'Solana', status: 'DECLINE,' }, + ]; + + const getStatusImage = (status: string) => { + switch (status) { + case 'RISE': + return up; + case 'DECLINE': + return down; + case 'STABLE': + return stable; + default: + return stable; + } + }; + + const getStatusColor = (status: string) => { + switch (status) { + case 'RISE': + return 'text-green'; + case 'DECLINE': + return 'text-red'; + case 'STABLE': + return 'text-main'; + default: + return 'text-main'; + } + }; + return (
-

+

Emotional index

@@ -31,14 +61,14 @@ function Banner() { className="flex items-center gap-[0.4rem]" > {stock.value} {stock.status} ))} diff --git a/src/pages/home/components/Chart/ChartItem.tsx b/src/pages/home/components/Chart/ChartItem.tsx index c1ced0b..0361d2b 100644 --- a/src/pages/home/components/Chart/ChartItem.tsx +++ b/src/pages/home/components/Chart/ChartItem.tsx @@ -10,32 +10,18 @@ import { import ChartSInfo from './ChartSInfo'; import { useFilterStore } from '../../../../stores/filterStore'; import ChartGradients from './ChartGradients'; - -interface ChartDataItem { - name: string; - subject1: number; - subject2: number; - subject3: number; -} +import useGetChartData from '../../../../hooks/useQuery/useGetChartData'; type StockType = 'ALL' | 'Bitcoin' | 'Ethereum' | 'Solana'; -const data: ChartDataItem[] = [ - { name: '10:00', subject1: 0.5, subject2: 1.0, subject3: 0.8 }, - { name: '11:00', subject1: 0.7, subject2: 0.1, subject3: 0.4 }, - { name: '12:00', subject1: 0.2, subject2: -0.3, subject3: 0.6 }, - { name: '13:00', subject1: 0.5, subject2: 0, subject3: 0.2 }, - { name: '14:00', subject1: -0.1, subject2: -0.7, subject3: 0.9 }, - { name: '15:00', subject1: 0.9, subject2: -0.8, subject3: 1.0 }, - { name: '16:00', subject1: -0.7, subject2: 0.6, subject3: 0.3 }, - { name: '17:00', subject1: 0.9, subject2: 0.4, subject3: -0.3 }, - { name: '18:00', subject1: -0.7, subject2: 1.0, subject3: 0.9 }, -]; - function ChartItem() { const currentState = useFilterStore((state) => state.selectedFilter); const filter = currentState as StockType; + const { data } = useGetChartData({ + type: filter, + }); + return (
@@ -55,7 +41,7 @@ function ChartItem() { />