diff --git a/src/app/api/dashboard/detail/route.ts b/src/app/api/dashboard/detail/route.ts
index 9534e37..5fbfb07 100644
--- a/src/app/api/dashboard/detail/route.ts
+++ b/src/app/api/dashboard/detail/route.ts
@@ -2,9 +2,19 @@ import { NextResponse } from 'next/server';
import { getDashboardDetailTodos } from '@/shared/lib/customApi/getDashboardDetailTodos';
-export async function GET() {
+const parseGoalIds = (rawGoalIds: string | null): number[] => {
+ if (!rawGoalIds) return [];
+ return rawGoalIds
+ .split(',')
+ .map((value) => Number(value.trim()))
+ .filter((id) => Number.isInteger(id) && id > 0);
+};
+
+export async function GET(request: Request) {
try {
- const result = await getDashboardDetailTodos();
+ const { searchParams } = new URL(request.url);
+ const goalIds = parseGoalIds(searchParams.get('goalIds'));
+ const result = await getDashboardDetailTodos(goalIds);
return NextResponse.json(result, {
status: result.hasAnySuccess ? 200 : 502,
diff --git a/src/features/dashboard/components/DashboardDetail/index.tsx b/src/features/dashboard/components/DashboardDetail/index.tsx
index 2f0679c..5359ac4 100644
--- a/src/features/dashboard/components/DashboardDetail/index.tsx
+++ b/src/features/dashboard/components/DashboardDetail/index.tsx
@@ -8,14 +8,14 @@ import Empty from '@/shared/components/Empty';
import PageSubTitle from '@/shared/components/PageSubTitle';
import GoalBox from '../GoalBox';
-import { dashboardQueries } from '@/shared/lib/query/queryFunction';
+import { dashboardQueries, goalQueries } from '@/shared/lib/query/queryFunction';
import { useTodoModeStore } from '@/shared/stores/useTodoModeStore';
import { useLanguage } from '@/shared/contexts/LanguageContext';
import { GITHUB_DISCONNECTED_SESSION_KEY } from '@/shared/constants/github';
export default function DashboardDetail() {
const mode = useTodoModeStore((state) => state.mode);
- const { data: goalDetail } = useQuery(dashboardQueries.detailTodos());
+ const { data: goals, isFetched: isGoalsFetched } = useQuery(goalQueries.list({ limit: 100 }));
const { t } = useLanguage();
const [isGithubDisconnectedSession] = useState(() => {
@@ -23,12 +23,23 @@ export default function DashboardDetail() {
return window.sessionStorage.getItem(GITHUB_DISCONNECTED_SESSION_KEY) === 'true';
});
- const visibleGoals =
+ const visibleGoalIds =
mode === 'GITHUB' && isGithubDisconnectedSession
? []
- : (goalDetail?.items?.filter((item) => item.goal.source === mode) ?? []);
+ : (goals?.goals?.filter((goal) => goal.source === mode).map((goal) => goal.id) ?? []);
- if (mode === 'MANUAL' && visibleGoals.length === 0) {
+ const { data: goalDetail } = useQuery({
+ ...dashboardQueries.detailTodosByGoals(visibleGoalIds),
+ enabled: visibleGoalIds.length > 0,
+ });
+
+ const visibleGoals = goalDetail?.items ?? [];
+
+ if (!isGoalsFetched) {
+ return null;
+ }
+
+ if (mode === 'MANUAL' && visibleGoalIds.length === 0) {
return {t.dashboard.noFirstGoal};
}
diff --git a/src/features/dashboard/components/TaskCardWrapper/index.tsx b/src/features/dashboard/components/TaskCardWrapper/index.tsx
index 5d104e6..dfee088 100644
--- a/src/features/dashboard/components/TaskCardWrapper/index.tsx
+++ b/src/features/dashboard/components/TaskCardWrapper/index.tsx
@@ -60,13 +60,13 @@ export default function TaskCardWrapper({
error instanceof ApiError
? error.message
: todoDetail.type === 'ISSUE'
- ? 'GitHub Issue close�� �����߽��ϴ�. ��� �� �ٽ� �õ����ּ���.'
- : 'GitHub PR merge�� �����߽��ϴ�. ��� �� �ٽ� �õ����ּ���.';
+ ? 'GitHub Issue close에 실패했습니다. 다시 시도해주세요.'
+ : 'GitHub PR merge에 실패했습니다. 다시 시도해주세요.';
showToast(message, 'fail');
} else {
- showToast('���� ���� ������Ʈ�� �����߽��ϴ�.', 'fail');
+ showToast('할 일 상태 업데이트에 실패했습니다.', 'fail');
}
- console.error('���� ���� ������Ʈ ����:', error);
+ console.error('할 일 상태 업데이트 오류:', error);
}
};
@@ -83,7 +83,7 @@ export default function TaskCardWrapper({
showToast(nextStarred ? t.mutations.favoriteAdded : t.mutations.favoriteRemoved);
},
onError: (error) => {
- console.error(error);
+ console.error('즐겨찾기 상태 업데이트 오류:', error);
setStarred(!nextStarred);
},
});
diff --git a/src/shared/lib/api/fetchDashboard.ts b/src/shared/lib/api/fetchDashboard.ts
index 6cfc630..90436a9 100644
--- a/src/shared/lib/api/fetchDashboard.ts
+++ b/src/shared/lib/api/fetchDashboard.ts
@@ -36,8 +36,15 @@ class FetchDashboard {
return payload.data;
};
- getDashboardDetailTodos = async (): Promise<{ items: DashboardDetailTodosResponse[] }> => {
- const response = await fetch('/api/dashboard/detail', {
+ getDashboardDetailTodos = async (goalIds?: number[]): Promise<{ items: DashboardDetailTodosResponse[] }> => {
+ const params = new URLSearchParams();
+ const normalizedGoalIds = (goalIds ?? []).filter((id) => Number.isInteger(id) && id > 0);
+ if (normalizedGoalIds.length > 0) {
+ params.set('goalIds', normalizedGoalIds.join(','));
+ }
+
+ const requestUrl = params.size > 0 ? `/api/dashboard/detail?${params.toString()}` : '/api/dashboard/detail';
+ const response = await fetch(requestUrl, {
method: 'GET',
headers: {
Accept: 'application/json',
diff --git a/src/shared/lib/customApi/getDashboardDetailTodos.ts b/src/shared/lib/customApi/getDashboardDetailTodos.ts
index 23e8900..1ece065 100644
--- a/src/shared/lib/customApi/getDashboardDetailTodos.ts
+++ b/src/shared/lib/customApi/getDashboardDetailTodos.ts
@@ -19,14 +19,25 @@ const toProgressPercent = (completedCount: number, todoCount: number): number =>
return Math.round((completedCount / todoCount) * 100);
};
-export const getDashboardDetailTodos = async (): Promise => {
+const normalizeGoalIds = (goalIds?: number[]): number[] => {
+ if (!goalIds) return [];
+ return [...new Set(goalIds.filter((id) => Number.isInteger(id) && id > 0))];
+};
+
+export const getDashboardDetailTodos = async (goalIds?: number[]): Promise => {
+ const targetGoalIds = normalizeGoalIds(goalIds);
+ const targetGoalIdSet = new Set(targetGoalIds);
+ const todoFetchLimit = Math.max(SAFE_LIMIT * Math.max(targetGoalIds.length, 1), SAFE_LIMIT * 5);
+
const [goalsRes, openTodosRes, doneTodosRes] = await Promise.allSettled([
- fetchGoals.getGoals(),
- fetchTodos.getTodos({ sort: 'LATEST', search: '', limit: 300, done: false }),
- fetchTodos.getTodos({ sort: 'LATEST', search: '', limit: 300, done: true }),
+ fetchGoals.getGoals({ limit: Math.max(targetGoalIds.length, 50) }),
+ fetchTodos.getTodos({ sort: 'LATEST', search: '', limit: todoFetchLimit, done: false }),
+ fetchTodos.getTodos({ sort: 'LATEST', search: '', limit: todoFetchLimit, done: true }),
]);
- const goals = goalsRes.status === 'fulfilled' ? goalsRes.value.goals : [];
+ const allGoals = goalsRes.status === 'fulfilled' ? goalsRes.value.goals : [];
+ const goals =
+ targetGoalIds.length > 0 ? allGoals.filter((goal) => targetGoalIdSet.has(goal.id)) : allGoals;
const openTodos = openTodosRes.status === 'fulfilled' ? openTodosRes.value.todos : [];
const doneTodos = doneTodosRes.status === 'fulfilled' ? doneTodosRes.value.todos : [];
diff --git a/src/shared/lib/query/keyFactory.ts b/src/shared/lib/query/keyFactory.ts
index 2c19a9c..b56e160 100644
--- a/src/shared/lib/query/keyFactory.ts
+++ b/src/shared/lib/query/keyFactory.ts
@@ -59,4 +59,5 @@ export const dashboardKeys = {
all: ['dashboard'] as const,
summary: () => [...dashboardKeys.all, 'summary'] as const,
detailTodos: () => [...dashboardKeys.all, 'detailTodos'] as const,
+ detailTodosByGoals: (goalIds: number[]) => [...dashboardKeys.detailTodos(), goalIds] as const,
};
diff --git a/src/shared/lib/query/queryFunction.ts b/src/shared/lib/query/queryFunction.ts
index 20aa8e3..838961d 100644
--- a/src/shared/lib/query/queryFunction.ts
+++ b/src/shared/lib/query/queryFunction.ts
@@ -177,4 +177,11 @@ export const dashboardQueries = {
queryFn: () => fetchDashboard.getDashboardDetailTodos(),
staleTime: DASHBOARD_STALE_TIME,
}),
+
+ detailTodosByGoals: (goalIds: number[]) =>
+ queryOptions({
+ queryKey: dashboardKeys.detailTodosByGoals(goalIds),
+ queryFn: () => fetchDashboard.getDashboardDetailTodos(goalIds),
+ staleTime: DASHBOARD_STALE_TIME,
+ }),
};