Skip to content

Commit fc46ca3

Browse files
committed
Merge feat/#2 into master
2 parents 75b34a0 + fae8fe8 commit fc46ca3

20 files changed

Lines changed: 421 additions & 122 deletions

entities/goal/type/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export interface MyGoalDTO {
2+
id: string;
3+
title: string;
4+
createdAt: string;
5+
updatedAt: string;
6+
totalTaskCount: number;
7+
completedTaskCount: number;
8+
achievementRate: number;
9+
}

entities/task/type/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
type TaskStatus = 'TODO' | 'DONE';
22

33
interface Task {
4-
id: number;
4+
id: string;
55
title: string;
66
status: TaskStatus;
7-
goalId: number;
7+
goalId: string;
88
taskDate: Date;
99
createdAt: Date;
1010
updatedAt: Date;
@@ -17,10 +17,10 @@ interface TaskFilter {
1717
}
1818

1919
interface TaskDTO {
20-
id: number;
20+
id: string;
2121
title: string;
2222
status: TaskStatus;
23-
goalId: number;
23+
goalId: string;
2424
taskDate: string;
2525
createdAt: string;
2626
updatedAt: string;

features/goals/Goals.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,35 @@ import { GoalCard } from 'features/goals/ui/GoalCard';
99
export interface Goal {
1010
id: string;
1111
title: string;
12-
completed: number;
13-
total: number;
12+
completedTaskCount: number;
13+
totalTaskCount: number;
14+
achievementRate?: number;
1415
}
1516

1617
export function Goals() {
1718
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
18-
const { goals, addGoal } = useGoals();
19+
const { goals, addGoal, loading, error } = useGoals();
1920

2021
const handleAddGoalClick = () => {
2122
setIsAddModalOpen(true);
2223
};
2324

25+
if (loading) {
26+
return (
27+
<div className="flex min-h-full flex-col items-center justify-center bg-gray-50 p-10">
28+
<p className="text-sub2">로딩 중...</p>
29+
</div>
30+
);
31+
}
32+
33+
if (error) {
34+
return (
35+
<div className="flex min-h-full flex-col items-center justify-center bg-gray-50 p-10">
36+
<p className="text-red-500">목표를 불러오는 중 오류가 발생했습니다.</p>
37+
</div>
38+
);
39+
}
40+
2441
if (goals.length === 0) {
2542
return (
2643
<div className="flex min-h-full flex-col bg-gray-50 p-10">
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { gql } from '@apollo/client';
2+
3+
export const CREATE_GOAL_MUTATION = gql`
4+
mutation createGoal($input: CreateGoalInput!) {
5+
createGoal(input: $input) {
6+
id
7+
title
8+
createdAt
9+
updatedAt
10+
totalTaskCount
11+
completedTaskCount
12+
achievementRate
13+
}
14+
}
15+
`;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { gql } from '@apollo/client';
2+
3+
export const MY_GOALS_QUERY = gql`
4+
query myGoals {
5+
myGoals {
6+
id
7+
title
8+
createdAt
9+
updatedAt
10+
totalTaskCount
11+
completedTaskCount
12+
achievementRate
13+
}
14+
}
15+
`;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { gql } from '@apollo/client';
2+
3+
export const UPDATE_GOAL_MUTATION = gql`
4+
mutation updateGoal($input: UpdateGoalInput!) {
5+
updateGoal(input: $input) {
6+
id
7+
title
8+
createdAt
9+
updatedAt
10+
totalTaskCount
11+
completedTaskCount
12+
achievementRate
13+
}
14+
}
15+
`;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useMutation } from '@apollo/client/react';
2+
import { CREATE_GOAL_MUTATION } from '../api/createGoal.mutation';
3+
4+
interface CreateGoalInput {
5+
title: string;
6+
}
7+
8+
interface CreateGoalDTO {
9+
id: string | number;
10+
title: string;
11+
createdAt: string;
12+
updatedAt: string;
13+
totalTaskCount: number;
14+
completedTaskCount: number;
15+
achievementRate: number;
16+
}
17+
18+
interface CreateGoalResponse {
19+
createGoal: CreateGoalDTO;
20+
}
21+
22+
export function useCreateGoal() {
23+
const [mutate, { loading, error }] = useMutation<
24+
CreateGoalResponse,
25+
{ input: CreateGoalInput }
26+
>(CREATE_GOAL_MUTATION);
27+
28+
const createGoal = (input: CreateGoalInput) =>
29+
mutate({ variables: { input } });
30+
31+
return { createGoal, loading, error };
32+
}

features/goals/model/useMyGoals.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { useQuery } from '@apollo/client/react';
2+
import { MY_GOALS_QUERY } from '../api/myGoals.query';
3+
import type { MyGoalDTO } from 'entities/goal/type';
4+
5+
interface MyGoalsResponse {
6+
myGoals: MyGoalDTO[];
7+
}
8+
9+
export interface MyGoal {
10+
id: string;
11+
title: string;
12+
createdAt: Date;
13+
updatedAt: Date;
14+
totalTaskCount: number;
15+
completedTaskCount: number;
16+
achievementRate: number;
17+
}
18+
19+
export function useMyGoals() {
20+
const { data, loading, error, refetch } = useQuery<MyGoalsResponse>(
21+
MY_GOALS_QUERY
22+
);
23+
24+
const myGoals: MyGoal[] =
25+
data?.myGoals?.map((dto) => ({
26+
id: dto.id,
27+
title: dto.title,
28+
createdAt: new Date(dto.createdAt),
29+
updatedAt: new Date(dto.updatedAt),
30+
totalTaskCount: dto.totalTaskCount,
31+
completedTaskCount: dto.completedTaskCount,
32+
achievementRate: dto.achievementRate,
33+
})) ?? [];
34+
35+
return { myGoals, loading, error, refetch };
36+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useMutation } from '@apollo/client/react';
2+
import { UPDATE_GOAL_MUTATION } from '../api/updateGoal.mutation';
3+
import type { MyGoalDTO } from 'entities/goal/type';
4+
5+
interface UpdateGoalInput {
6+
id: string;
7+
title: string;
8+
}
9+
10+
interface UpdateGoalResponse {
11+
updateGoal: MyGoalDTO;
12+
}
13+
14+
export function useUpdateGoal() {
15+
const [mutate, { loading, error }] = useMutation<
16+
UpdateGoalResponse,
17+
{ input: UpdateGoalInput }
18+
>(UPDATE_GOAL_MUTATION);
19+
20+
const updateGoal = (input: UpdateGoalInput) =>
21+
mutate({ variables: { input } });
22+
23+
return { updateGoal, loading, error };
24+
}

features/goals/ui/GoalCard.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ export interface GoalCardProps {
55
goal: {
66
id: string;
77
title: string;
8-
completed: number;
9-
total: number;
8+
completedTaskCount: number;
9+
totalTaskCount: number;
10+
achievementRate?: number;
1011
};
1112
}
1213

1314
export function GoalCard({ goal }: GoalCardProps) {
1415
const navigate = useNavigate();
1516
const progressPercent =
16-
goal.total > 0 ? (goal.completed / goal.total) * 100 : 0;
17+
goal.achievementRate ?? (goal.totalTaskCount > 0
18+
? (goal.completedTaskCount / goal.totalTaskCount) * 100
19+
: 0);
1720

1821
const handleClick = () => {
1922
navigate(`/goals/${goal.id}`);
@@ -44,7 +47,7 @@ export function GoalCard({ goal }: GoalCardProps) {
4447
/>
4548
</div>
4649
<span className="shrink-0 text-sm text-sub2">
47-
{goal.completed} / {goal.total}
50+
{goal.completedTaskCount} / {goal.totalTaskCount}
4851
</span>
4952
</div>
5053
</div>

0 commit comments

Comments
 (0)