diff --git a/src/api/insightApi.ts b/src/api/insightApi.ts new file mode 100644 index 00000000..a13d31b9 --- /dev/null +++ b/src/api/insightApi.ts @@ -0,0 +1,18 @@ +import { getRequest } from '@/api'; +import { ApiMonthlyInsightResponse, ApiWeeklyInsightResponse } from '@/interfaces/insight'; + +export const getWeeklyInsight = async (date: string): Promise => { + const apiResponse: ApiWeeklyInsightResponse = await getRequest( + `/heatmaps/todo-timers/insight/weekly/${date}`, + ); + + return apiResponse; +}; + +export const getMonthlyInsight = async (yearMonth: string): Promise => { + const apiResponse: ApiMonthlyInsightResponse = await getRequest( + `/heatmaps/todo-timers/insight/monthly/${yearMonth}`, + ); + + return apiResponse; +}; diff --git a/src/components/heatmaps/HeatmapSection.tsx b/src/components/heatmaps/HeatmapSection.tsx index ec620ed5..0fe79e6d 100644 --- a/src/components/heatmaps/HeatmapSection.tsx +++ b/src/components/heatmaps/HeatmapSection.tsx @@ -19,8 +19,14 @@ export default function HeatmapSection() { const [period, setPeriod] = useState<'week' | 'month'>('week'); const isWeek = period === 'week'; - const { weeklyHeatmapData, monthlyHeatmapData, hasError, handleRetry } = - useHeatmapSection(period); + const { + weeklyHeatmapData, + weeklyInsightData, + monthlyHeatmapData, + monthlyInsightData, + hasError, + handleRetry, + } = useHeatmapSection(period); const infoButtonRef = useRef(null); const cardContainerRef = useRef(null); @@ -48,26 +54,31 @@ export default function HeatmapSection() { // 인사이트 카드 렌더링 const renderInsightCard = () => { - return ; - - // TODO - // if (period === 'week') { - // return weeklyInsightData?.success && weeklyInsightData.data.insights.length > 0 ? ( - // - // ) : ( - // - // ); - // } - - // if (period === 'month') { - // return monthlyInsightData?.success && monthlyInsightData.data.insights.length > 0 ? ( - // - // ) : ( - // - // ); - // } - - // return null; + if (period === 'week') { + if (!weeklyInsightData || !weeklyInsightData.success) { + return ; + } + + if (!weeklyInsightData.data.insights || weeklyInsightData.data.insights.trim() === '') { + return ; + } + + return ; + } + + if (period === 'month') { + if (!monthlyInsightData || !monthlyInsightData.success) { + return ; + } + + if (!monthlyInsightData.data.insights || monthlyInsightData.data.insights.trim() === '') { + return ; + } + + return ; + } + + return null; }; const cardTitle = ( diff --git a/src/components/insight/InsightCard.tsx b/src/components/insight/InsightCard.tsx index 5c3f2bc4..3744c50b 100644 --- a/src/components/insight/InsightCard.tsx +++ b/src/components/insight/InsightCard.tsx @@ -2,13 +2,13 @@ import { cn } from '@/lib/utils'; interface InsightCardProps { variant: 'no-data' | 'no-work-time' | 'weekly' | 'monthly'; - items?: React.ReactNode[]; + item?: string; className?: string; } import TimerIcon from '@/assets/icons/timer.svg'; -const InsightCard = ({ variant, items = [], className }: InsightCardProps) => { +const InsightCard = ({ variant, item = '', className }: InsightCardProps) => { const baseClasses = 'rounded-20 flex w-full flex-col py-12 px-12 md:px-20 bg-insightContainer gap-12 h-full'; @@ -19,8 +19,8 @@ const InsightCard = ({ variant, items = [], className }: InsightCardProps) => {

- 이번 주 작업 기록이 없어
- 인사이트를 제공할 수 없어요 :( + 작업 기록을 불러올 수 없어요
+ 잠시 후 다시 시도해주세요 :(

작업을 시작해보세요!

@@ -51,9 +51,7 @@ const InsightCard = ({ variant, items = [], className }: InsightCardProps) => {
{title}
- {items.map((item, idx) => ( -

{item}

- ))} +

{item}

); diff --git a/src/hooks/useHeatmap.ts b/src/hooks/useHeatmap.ts index 9e42161c..38b7631d 100644 --- a/src/hooks/useHeatmap.ts +++ b/src/hooks/useHeatmap.ts @@ -10,8 +10,6 @@ export const useWeeklyHeatmap = (date: string, opts?: Opts) => { queryKey: ['weeklyHeatmap', date], queryFn: () => getWeeklyHeatmap(date), enabled: !!date && (opts?.enabled ?? true), - refetchInterval: 10 * 1000, // 10초마다 새로고침 - refetchOnWindowFocus: true, // 윈도우 포커스 시 새로고침 }); }; @@ -20,7 +18,5 @@ export const useMonthlyHeatmap = (yearMonth: string, opts?: Opts) => { queryKey: ['monthlyHeatmap', yearMonth], queryFn: () => getMonthlyHeatmap(yearMonth), enabled: !!yearMonth && (opts?.enabled ?? true), - refetchInterval: 10 * 1000, // 10초마다 새로고침 - refetchOnWindowFocus: true, // 윈도우 포커스 시 새로고침 }); }; diff --git a/src/hooks/useHeatmapsSection.ts b/src/hooks/useHeatmapsSection.ts index b205bc1c..2d0a5f3c 100644 --- a/src/hooks/useHeatmapsSection.ts +++ b/src/hooks/useHeatmapsSection.ts @@ -13,8 +13,8 @@ export const useHeatmapSection = (period: 'week' | 'month') => { const weeklyHeatmap = useWeeklyHeatmap(getCurrentDate(), { enabled: isWeek }); const monthlyHeatmap = useMonthlyHeatmap(getCurrentMonth(), { enabled: !isWeek }); - const weeklyInsight = useWeeklyInsight(); - const monthlyInsight = useMonthlyInsight(); + const weeklyInsight = useWeeklyInsight(getCurrentDate(), { enabled: isWeek }); + const monthlyInsight = useMonthlyInsight(getCurrentMonth(), { enabled: !isWeek }); const currentHeatmap = isWeek ? weeklyHeatmap : monthlyHeatmap; const currentInsight = isWeek ? weeklyInsight : monthlyInsight; diff --git a/src/hooks/useInsight.ts b/src/hooks/useInsight.ts index 53c1e8d4..6030e728 100644 --- a/src/hooks/useInsight.ts +++ b/src/hooks/useInsight.ts @@ -1,23 +1,22 @@ import { useQuery } from '@tanstack/react-query'; -import { MonthlyInsightResponse, WeeklyInsightResponse } from '@/interfaces/insight'; +import { getMonthlyInsight, getWeeklyInsight } from '@/api/insightApi'; +import { ApiMonthlyInsightResponse, ApiWeeklyInsightResponse } from '@/interfaces/insight'; -export const useWeeklyInsight = () => { - return useQuery({ - queryKey: ['weeklyInsight'], - queryFn: async () => { - const res = await fetch('/insights/weekly'); - return res.json(); - }, +type Opts = { enabled?: boolean }; + +export const useWeeklyInsight = (date: string, opts?: Opts) => { + return useQuery({ + queryKey: ['weeklyInsight', date], + queryFn: () => getWeeklyInsight(date), + enabled: !!date && (opts?.enabled ?? true), }); }; -export const useMonthlyInsight = () => { - return useQuery({ - queryKey: ['monthlyInsight'], - queryFn: async () => { - const res = await fetch('/insights/monthly'); - return res.json(); - }, +export const useMonthlyInsight = (yearMonth: string, opts?: Opts) => { + return useQuery({ + queryKey: ['monthlyInsight', yearMonth], + queryFn: () => getMonthlyInsight(yearMonth), + enabled: !!yearMonth && (opts?.enabled ?? true), }); }; diff --git a/src/interfaces/insight.ts b/src/interfaces/insight.ts index db5954a0..17cb4cab 100644 --- a/src/interfaces/insight.ts +++ b/src/interfaces/insight.ts @@ -1,19 +1,16 @@ -// 주간 인사이트 응답 데이터 형식 -export interface WeeklyInsightResponse { +// API 명세서 기반 응답 타입 +export interface ApiWeeklyInsightResponse { success: boolean; data: { - week_start: string; - week_end: string; - insights: string[]; + date: string; + insights: string; }; } -// 월간 인사이트 응답 데이터 형식 -export interface MonthlyInsightResponse { +export interface ApiMonthlyInsightResponse { success: boolean; data: { - month: string; - month_name: string; - insights: string[]; + yearMonth: string; + insights: string; }; } diff --git a/src/mocks/handlers/insightHandlers.ts b/src/mocks/handlers/insightHandlers.ts index be55b55d..4988f755 100644 --- a/src/mocks/handlers/insightHandlers.ts +++ b/src/mocks/handlers/insightHandlers.ts @@ -4,11 +4,11 @@ import { monthlyInsightRes } from '@/mocks/mockResponses/insight/monthlyInsightR import { weeklyInsightRes } from '@/mocks/mockResponses/insight/weeklyInsightResponse'; export const insightsHandlers = [ - http.get('/insights/weekly', async () => { + http.get('/heatmaps/todo-timers/insight/weekly/:date', async () => { return HttpResponse.json(weeklyInsightRes); }), - http.get('/insights/monthly', () => { + http.get('/heatmaps/todo-timers/insight/monthly/:yearMonth', () => { return HttpResponse.json(monthlyInsightRes); }), ]; diff --git a/src/mocks/mockResponses/insight/monthlyInsightResponse.ts b/src/mocks/mockResponses/insight/monthlyInsightResponse.ts index 8d73c1db..a9e76c79 100644 --- a/src/mocks/mockResponses/insight/monthlyInsightResponse.ts +++ b/src/mocks/mockResponses/insight/monthlyInsightResponse.ts @@ -1,12 +1,7 @@ export const monthlyInsightRes = { success: true, data: { - month: '2024-01', - month_name: '1월', - insights: [ - '월초 대비 월말 작업량 23.5% 증가! 꾸준한 상승세', - '오후 집중도가 월간 베스트!', - '이번달 총 작업시간 213시간 45분, 지난달보다 33시간 45분 증가', - ], + yearMonth: '2025-09', + insights: '이번 달은 1주 차를 열심히 보냈네요!', }, }; diff --git a/src/mocks/mockResponses/insight/weeklyInsightResponse.ts b/src/mocks/mockResponses/insight/weeklyInsightResponse.ts index f27d429b..3a47ed43 100644 --- a/src/mocks/mockResponses/insight/weeklyInsightResponse.ts +++ b/src/mocks/mockResponses/insight/weeklyInsightResponse.ts @@ -1,11 +1,7 @@ export const weeklyInsightRes = { success: true, data: { - week_start: '2024-01-01', - week_end: '2024-01-07', - insights: [ - '이번주는 금요일이 10시간 20분으로 최고', - '이번주는 오후 집중도가 최고! 총 23시간 30분으로 압도적', - ], + date: '2025-09-02', + insights: '이번 주 골든 타임 1회 달성!', }, }; diff --git a/src/stories/insight/InsightCard.stories.tsx b/src/stories/insight/InsightCard.stories.tsx index 9f394b9a..f7814062 100644 --- a/src/stories/insight/InsightCard.stories.tsx +++ b/src/stories/insight/InsightCard.stories.tsx @@ -38,17 +38,13 @@ export const NoData: Story = { export const Weekly: Story = { args: { variant: 'weekly', - items: ['아침 시간대의 집중력이 높아요.', '목표 3개 중 2개를 성공적으로 달성했어요.'], + item: '이번 주 골든 타임 1회 달성!', }, }; export const Monthly: Story = { args: { variant: 'monthly', - items: [ - '가장 많이 활동한 요일은 화요일이에요.', - '전체 목표 달성률은 85%입니다.', - '작업 시간은 평균 3.2시간으로 꾸준했어요.', - ], + item: '이번 달은 1주 차를 열심히 보냈네요!', }, };