Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/api/insightApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getRequest } from '@/api';
import { ApiMonthlyInsightResponse, ApiWeeklyInsightResponse } from '@/interfaces/insight';

export const getWeeklyInsight = async (date: string): Promise<ApiWeeklyInsightResponse> => {
const apiResponse: ApiWeeklyInsightResponse = await getRequest(
`/heatmaps/todo-timers/insight/weekly/${date}`,
);

return apiResponse;
};

export const getMonthlyInsight = async (yearMonth: string): Promise<ApiMonthlyInsightResponse> => {
const apiResponse: ApiMonthlyInsightResponse = await getRequest(
`/heatmaps/todo-timers/insight/monthly/${yearMonth}`,
);

return apiResponse;
};
55 changes: 33 additions & 22 deletions src/components/heatmaps/HeatmapSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLButtonElement>(null);
const cardContainerRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -48,26 +54,31 @@ export default function HeatmapSection() {

// 인사이트 카드 렌더링
const renderInsightCard = () => {
return <InsightCard variant="no-work-time" />;

// TODO
// if (period === 'week') {
// return weeklyInsightData?.success && weeklyInsightData.data.insights.length > 0 ? (
// <InsightCard variant="weekly" items={weeklyInsightData.data.insights} />
// ) : (
// <InsightCard variant="no-data" />
// );
// }

// if (period === 'month') {
// return monthlyInsightData?.success && monthlyInsightData.data.insights.length > 0 ? (
// <InsightCard variant="monthly" items={monthlyInsightData.data.insights} />
// ) : (
// <InsightCard variant="no-data" />
// );
// }

// return null;
if (period === 'week') {
if (!weeklyInsightData || !weeklyInsightData.success) {
return <InsightCard variant="no-data" />;
}

if (!weeklyInsightData.data.insights || weeklyInsightData.data.insights.trim() === '') {
return <InsightCard variant="no-work-time" />;
}

return <InsightCard variant="weekly" item={weeklyInsightData.data.insights} />;
}

if (period === 'month') {
if (!monthlyInsightData || !monthlyInsightData.success) {
return <InsightCard variant="no-data" />;
}

if (!monthlyInsightData.data.insights || monthlyInsightData.data.insights.trim() === '') {
return <InsightCard variant="no-work-time" />;
}

return <InsightCard variant="monthly" item={monthlyInsightData.data.insights} />;
}

return null;
};

const cardTitle = (
Expand Down
12 changes: 5 additions & 7 deletions src/components/insight/InsightCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -19,8 +19,8 @@ const InsightCard = ({ variant, items = [], className }: InsightCardProps) => {
<TimerIcon className="text-inactive" width={32} height={32} fill="currentColor" />

<p>
이번 주 작업 기록이 없어 <br className="md:hidden" />
인사이트를 제공할 수 없어요 :(
작업 기록을 불러올 수 없어요 <br className="md:hidden" />
잠시 후 다시 시도해주세요 :(
</p>
<p>작업을 시작해보세요!</p>
</div>
Expand Down Expand Up @@ -51,9 +51,7 @@ const InsightCard = ({ variant, items = [], className }: InsightCardProps) => {
<div className={cn(baseClasses, className)}>
<div className="text-text-01 text-body-b-16">{title}</div>
<div className="text-body-16 text-text-01 flex flex-col gap-10 leading-tight">
{items.map((item, idx) => (
<p key={idx}>{item}</p>
))}
<p>{item}</p>
</div>
</div>
);
Expand Down
4 changes: 0 additions & 4 deletions src/hooks/useHeatmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, // 윈도우 포커스 시 새로고침
});
};

Expand All @@ -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, // 윈도우 포커스 시 새로고침
});
};
4 changes: 2 additions & 2 deletions src/hooks/useHeatmapsSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
29 changes: 14 additions & 15 deletions src/hooks/useInsight.ts
Original file line number Diff line number Diff line change
@@ -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<WeeklyInsightResponse>({
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<ApiWeeklyInsightResponse>({
queryKey: ['weeklyInsight', date],
queryFn: () => getWeeklyInsight(date),
enabled: !!date && (opts?.enabled ?? true),
});
};

export const useMonthlyInsight = () => {
return useQuery<MonthlyInsightResponse>({
queryKey: ['monthlyInsight'],
queryFn: async () => {
const res = await fetch('/insights/monthly');
return res.json();
},
export const useMonthlyInsight = (yearMonth: string, opts?: Opts) => {
return useQuery<ApiMonthlyInsightResponse>({
queryKey: ['monthlyInsight', yearMonth],
queryFn: () => getMonthlyInsight(yearMonth),
enabled: !!yearMonth && (opts?.enabled ?? true),
});
};
17 changes: 7 additions & 10 deletions src/interfaces/insight.ts
Original file line number Diff line number Diff line change
@@ -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;
};
}
4 changes: 2 additions & 2 deletions src/mocks/handlers/insightHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}),
];
9 changes: 2 additions & 7 deletions src/mocks/mockResponses/insight/monthlyInsightResponse.ts
Original file line number Diff line number Diff line change
@@ -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주 차를 열심히 보냈네요!',
},
};
8 changes: 2 additions & 6 deletions src/mocks/mockResponses/insight/weeklyInsightResponse.ts
Original file line number Diff line number Diff line change
@@ -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회 달성!',
},
};
8 changes: 2 additions & 6 deletions src/stories/insight/InsightCard.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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주 차를 열심히 보냈네요!',
},
};