Skip to content

Commit e143ceb

Browse files
committed
Merge branch 'develop' into epic/home
2 parents ef76340 + ce603a6 commit e143ceb

File tree

35 files changed

+276
-295
lines changed

35 files changed

+276
-295
lines changed

mocks/domain/todo.ts

Lines changed: 0 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { http, HttpResponse } from 'msw';
22
import { Todo, DAY_OF_THE_WEEK } from '@/shared/type/Todo';
3-
import { Goal, GoalCategoryEnum } from '@/shared/type/goal';
43

54
// CORS preflight 요청 처리
65
export const optionsHandler = http.options('*', () => {
@@ -14,79 +13,6 @@ export const optionsHandler = http.options('*', () => {
1413
});
1514
});
1615

17-
// 테스트용 Goal 데이터
18-
export const mockGoals: Goal[] = [
19-
{
20-
id: 'goal-1',
21-
name: '프론트엔드 개발 마스터하기',
22-
mentor: 'TIM_COOK',
23-
duration: {
24-
startDate: '2024-01-01',
25-
endDate: '2024-03-31',
26-
},
27-
toBe: 'React 전문가가 되어 프로젝트를 주도할 수 있음',
28-
category: GoalCategoryEnum.STUDY,
29-
plans: [
30-
{
31-
id: 'plan-1',
32-
content: 'React 컴포넌트 설계 및 리팩토링',
33-
weekOfMonth: 1,
34-
},
35-
{
36-
id: 'plan-2',
37-
content: 'TypeScript와 React 연동',
38-
weekOfMonth: 2,
39-
},
40-
],
41-
},
42-
{
43-
id: 'goal-2',
44-
name: '디자인 시스템 구축',
45-
mentor: 'CONFUCIUS',
46-
duration: {
47-
startDate: '2024-01-15',
48-
endDate: '2024-02-28',
49-
},
50-
toBe: '일관성 있는 디자인 시스템',
51-
category: GoalCategoryEnum.FINANCE,
52-
plans: [
53-
{
54-
id: 'plan-3',
55-
content: '디자인 토큰 정의 및 컴포넌트 설계',
56-
weekOfMonth: 1,
57-
},
58-
{
59-
id: 'plan-4',
60-
content: '스토리북을 활용한 컴포넌트 문서화',
61-
weekOfMonth: 2,
62-
},
63-
],
64-
},
65-
{
66-
id: 'goal-3',
67-
name: '프로젝트 관리 역량 강화',
68-
mentor: 'WARREN_BUFFETT',
69-
duration: {
70-
startDate: '2024-01-01',
71-
endDate: '2024-04-30',
72-
},
73-
toBe: '프로젝트를 기획하고 관리할 수 있는 역량',
74-
category: GoalCategoryEnum.STUDY,
75-
plans: [
76-
{
77-
id: 'plan-5',
78-
content: '프로젝트 기획 및 요구사항 분석',
79-
weekOfMonth: 1,
80-
},
81-
{
82-
id: 'plan-6',
83-
content: '팀 협업 및 커뮤니케이션',
84-
weekOfMonth: 2,
85-
},
86-
],
87-
},
88-
];
89-
9016
// 테스트용 Todo 데이터 (요일별로 구성)
9117
export const mockTodosByPlan: Record<string, Record<DAY_OF_THE_WEEK, Todo[]>> = {
9218
'plan-1': {
@@ -266,24 +192,6 @@ const getAllTodos = (): Todo[] => {
266192
return Object.values(mockTodosByPlan).flatMap(planTodos => Object.values(planTodos).flat());
267193
};
268194

269-
// Goal 목록 조회
270-
export const getGoals = http.get('/mock/goals', () => {
271-
return HttpResponse.json(
272-
{
273-
data: mockGoals,
274-
message: 'Goal 목록을 성공적으로 조회했습니다.',
275-
},
276-
{
277-
status: 200,
278-
headers: {
279-
'Access-Control-Allow-Origin': '*',
280-
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
281-
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
282-
},
283-
}
284-
);
285-
});
286-
287195
// 주간 Todo 리스트 조회 (요일별로 그룹화)
288196
export const getWeeklyTodoList = http.get('/mock/todos', ({ request }) => {
289197
const url = new URL(request.url);
@@ -690,7 +598,6 @@ function getDayOfWeek(dateString: string): DAY_OF_THE_WEEK {
690598
// 모든 Todo 핸들러들을 배열로 export
691599
export const todoHandlers = [
692600
optionsHandler,
693-
getGoals,
694601
getWeeklyTodoList,
695602
getTodos,
696603
getTodoById,

next.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ const nextConfig: NextConfig = {
55
plugins: {
66
'@tailwindcss/postcss': {},
77
},
8+
images: {
9+
remotePatterns: [
10+
{
11+
protocol: 'https',
12+
hostname: 'growit-images.s3.ap-northeast-2.amazonaws.com',
13+
port: '',
14+
pathname: '/**',
15+
},
16+
],
17+
},
818
};
919

1020
export default nextConfig;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export default function EndedGoalsListLoader() {
2+
return (
3+
<div className="p-5">
4+
<nav className="px-2 pt-8 pb-4 w-full border-b border-line-normal flex items-center justify-center">
5+
<div className="h-8 bg-elevated-normal rounded w-1/3" />
6+
</nav>
7+
<div className="animate-pulse space-y-4">
8+
<div className="h-8 bg-elevated-normal rounded w-1/3" />
9+
<div className="grid grid-cols-2 gap-3">
10+
{[1, 2, 3, 4, 5, 6, 7, 8].map(i => (
11+
<div key={i} className="h-32 bg-elevated-normal rounded" />
12+
))}
13+
</div>
14+
</div>
15+
</div>
16+
);
17+
}

src/app/(home)/goal/ended/page.tsx

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,7 @@
11
import EndedGoalsContainer from '@/composite/goal/endedGoalsContainer';
2-
import { Suspense } from 'react';
32

43
export const dynamic = 'force-dynamic';
54

65
export default function EndedGoalsPage() {
7-
return (
8-
<Suspense fallback={<EndedGoalsListLoader />}>
9-
<EndedGoalsContainer />
10-
</Suspense>
11-
);
12-
}
13-
14-
function EndedGoalsListLoader() {
15-
return (
16-
<div>
17-
<nav className="px-2 pt-8 pb-4 w-full border-b border-line-normal flex items-center justify-center">
18-
<div className="h-8 bg-elevated-normal rounded w-1/3" />
19-
</nav>
20-
<div className="p-5">
21-
<div className="animate-pulse space-y-4">
22-
<div className="h-8 bg-elevated-normal rounded w-1/3" />
23-
<div className="grid grid-cols-2 gap-3">
24-
{[1, 2, 3, 4].map(i => (
25-
<div key={i} className="h-32 bg-elevated-normal rounded" />
26-
))}
27-
</div>
28-
</div>
29-
</div>
30-
</div>
31-
);
6+
return <EndedGoalsContainer />;
327
}

src/app/(home)/retrospect/page.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import Navigation from '@/composite/retrospect/navigation/component';
2-
31
const RetrospectPage = () => {
4-
return <Navigation />;
2+
return <></>;
53
};
64

75
export default RetrospectPage;

src/composite/create-goal/createGoalForm/component.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@ import { CreateGoalFormElement } from '@/feature/goal';
44
import Button from '@/shared/components/input/Button';
55
import { PageHeader } from '@/shared/components/layout/PageHeader';
66
import { createCreateGoalMutation } from '@/model/goal/hooks';
7-
import { useMutation } from '@tanstack/react-query';
7+
import { useMutation, useQueryClient } from '@tanstack/react-query';
88
import { useRouter } from 'next/navigation';
99
import { ROUTES } from '@/shared/constants/routes';
1010
import { useToast } from '@/shared/components/feedBack/toast';
11+
import { GoalQueryKeys } from '@/model/goal/queryKeys';
1112

1213
export const CreateGoalForm = () => {
1314
const router = useRouter();
15+
const queryClient = useQueryClient();
1416
const { showToast } = useToast();
1517
const { mutate: createGoal } = useMutation(
1618
createCreateGoalMutation({
1719
onSuccess: () => {
20+
queryClient.invalidateQueries({ queryKey: GoalQueryKeys.progress() });
1821
router.push(ROUTES.GOAL);
1922
showToast('목표 생성이 완료되었습니다.', 'success');
2023
},

src/composite/goal/endedGoalsContainer/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default function EndedGoalsContainer() {
4444

4545
const handleDelete = async () => {
4646
if (!isEditMode) return;
47-
await deleteGoals(checkedGoals);
47+
if (checkedGoals.size > 0) await deleteGoals(checkedGoals);
4848
setCheckedGoals(new Set());
4949
setIsEditMode(false);
5050
};

src/composite/goal/goalEditForm/GoalEditForm.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ export const GoalEditForm = ({ currentGoal }: { currentGoal: Goal }) => {
3636
const { mutate: editGoal } = useMutation(
3737
createEditGoalMutation({
3838
onSuccess: () => {
39-
queryClient.invalidateQueries({ queryKey: GoalQueryKeys.all() });
4039
queryClient.invalidateQueries({ queryKey: GoalQueryKeys.progress() });
4140
showToast('수정이 완료되었습니다.', 'success');
4241
router.push(ROUTES.GOAL);

src/composite/goal/goalProgressSheet/index.tsx

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,38 @@ import {
1010
} from '@/shared/components/dropdown-menu';
1111
import { TrashIcon, WarningIcon } from '@/shared/constants/icons';
1212
import { useGoalSelector } from '@/model/goal/context';
13-
import { getDaysUntilEndDate } from '@/shared/lib/utils';
13+
import { getDaysUntilEndDate, getProgressPercentageByDateRange } from '@/shared/lib/utils';
1414
import { Modal } from '@/shared/components/feedBack/Modal';
1515
import { useState } from 'react';
1616
import Button from '@/shared/components/input/Button';
17+
import { useMutation, useQueryClient } from '@tanstack/react-query';
18+
import { createDeleteGoalMutation } from '@/model/goal/hooks';
19+
import { GoalQueryKeys } from '@/model/goal/queryKeys';
1720

1821
export default function GoalProgressSheet() {
1922
const router = useRouter();
23+
const queryClient = useQueryClient();
2024
const { currentGoal } = useGoalSelector();
2125
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
2226

23-
if (currentGoal === undefined || null) return <div className="w-full h-30 bg-transparent" />;
27+
const { mutateAsync: deleteGoal } = useMutation(
28+
createDeleteGoalMutation({
29+
onSuccess: () => {
30+
queryClient.invalidateQueries({ queryKey: GoalQueryKeys.progress() });
31+
setIsDeleteModalOpen(false);
32+
},
33+
onError: () => {
34+
showToast('목표 삭제에 실패했습니다.', 'error');
35+
},
36+
})
37+
);
38+
39+
if (!currentGoal) return <div className="w-full h-30 bg-transparent" />;
2440

25-
const progressPercentage = currentGoal?.plans.length
26-
? (currentGoal.plans.length / currentGoal.plans.length) * 100
41+
const progressPercentage = currentGoal
42+
? getProgressPercentageByDateRange(currentGoal.duration.startDate, currentGoal.duration.endDate)
2743
: 0;
28-
const daysUntilEndDate = currentGoal?.duration.endDate ? getDaysUntilEndDate(currentGoal.duration.endDate) : 0;
44+
const daysUntilEndDate = currentGoal ? getDaysUntilEndDate(currentGoal.duration.endDate) : 0;
2945

3046
return (
3147
<div className="w-full max-h-40 h-full bg-elevated-normal rounded-t-lg border-t border-line-normal">
@@ -93,7 +109,11 @@ export default function GoalProgressSheet() {
93109
{currentGoal?.duration.startDate} ~ {currentGoal?.duration.endDate}
94110
</p>
95111
</div>
96-
<DeleteGoalModal open={isDeleteModalOpen} onClose={() => setIsDeleteModalOpen(false)} onDelete={() => {}} />
112+
<DeleteGoalModal
113+
open={isDeleteModalOpen}
114+
onClose={() => setIsDeleteModalOpen(false)}
115+
onDelete={() => deleteGoal(currentGoal.id)}
116+
/>
97117
</div>
98118
);
99119
}
@@ -128,3 +148,6 @@ function DeleteGoalModal({ open, onClose, onDelete }: DeleteGoalModalProps) {
128148
/>
129149
);
130150
}
151+
function showToast(arg0: string, arg1: string) {
152+
throw new Error('Function not implemented.');
153+
}

src/composite/goal/planetSelector/PlanetSelector.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@ import { GoalProvider, useGoalSelector } from '@/model/goal/context';
88
import 'swiper/css';
99
import 'swiper/css/pagination';
1010
import { PlanetItem } from '@/feature/goal/planetItem';
11-
import { useSuspenseQueries } from '@tanstack/react-query';
12-
import { createAllGoalsQuery, createProgressGoalsQuery } from '@/model/goal/hooks';
11+
import { useSuspenseQuery } from '@tanstack/react-query';
12+
import { createProgressGoalsQuery } from '@/model/goal/hooks';
1313
import { useMemo } from 'react';
1414
import { Goal } from '@/shared/type/goal';
1515
import { BottomSheet, useBottomSheet } from '@/shared/components/feedBack/BottomSheet';
1616
import CreateNewGoal from './components/CreateNewGoal';
1717
import Button from '@/shared/components/input/Button';
1818
import { Swiper as SwiperType } from 'swiper/types';
1919
import { useShowEndedGoalsSheet } from './hooks';
20+
import { getMsUntilEndOfDay } from '@/shared/lib/utils';
2021

2122
export default function PlanetSelectorScene() {
2223
return (
@@ -28,12 +29,17 @@ export default function PlanetSelectorScene() {
2829
}
2930

3031
export function PlanetSelector() {
31-
const [{ data: progressGoals }, { data: allGoals }] = useSuspenseQueries({
32-
queries: [createProgressGoalsQuery(), createAllGoalsQuery()],
33-
});
32+
const msUntilEndOfDay = getMsUntilEndOfDay();
33+
const { data: progressGoals } = useSuspenseQuery(
34+
createProgressGoalsQuery({
35+
// 캐시 무효화가 없다면, 현재 시간부터 하루가 끝나기전까지 유지되어도 괜찮다 판단
36+
staleTime: msUntilEndOfDay,
37+
gcTime: msUntilEndOfDay,
38+
})
39+
);
3440
const { setCurrentGoal } = useGoalSelector();
3541
const { isOpen, showSheet, closeSheet } = useBottomSheet();
36-
useShowEndedGoalsSheet(allGoals, showSheet);
42+
useShowEndedGoalsSheet(showSheet);
3743

3844
const goalsWithAddGoalSlot: (Goal | 'add-goal-section')[] = useMemo(() => {
3945
return [...progressGoals, 'add-goal-section'];

0 commit comments

Comments
 (0)