Skip to content

Commit 5bddd53

Browse files
committed
feat: 오늘의 목표 달성률 표시 구현
1 parent d804be2 commit 5bddd53

5 files changed

Lines changed: 56 additions & 75 deletions

File tree

app/(with-sidebar)/(home)/page.tsx

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,22 @@ import ButtonSection from '@/components/home/ButtonSection';
88

99
import { useEffect, useState } from 'react';
1010
import { useAuthUser } from '@/hooks/useAuthUser';
11-
12-
import {
13-
Todo,
14-
fetchTodos,
15-
AddTodo,
16-
toggleTodoStatus,
17-
} from '@/services/home/todoService.service';
11+
import { useTodayPlanItems } from '@/hooks/useTodayPlanItems';
1812
import { getProfile, Profile } from '@/services/home/profileService.service';
1913

2014
const Page = () => {
2115
// 현재 사용자 정보
2216
const { user: currentUser, authLoading } = useAuthUser();
23-
2417
const [profile, setProfile] = useState<Profile | null>(null);
25-
const [todos, setTodos] = useState<Todo[]>([]);
2618
const [error, setError] = useState<string | null>(null);
2719

28-
// 1. 할 일 목록 불러오기
29-
const loadTodos = async (uid: string) => {
30-
try {
31-
setError(null);
32-
33-
const fetchedTodos = await fetchTodos(uid);
34-
setTodos(fetchedTodos);
35-
} catch (err) {
36-
console.error(err);
37-
setError('데이터를 불러오는 데 실패하였습니다');
38-
}
39-
};
40-
41-
// 3. 할 일 상태 토글
42-
const handleToggleTodo = async (id: string, currentStatus: boolean) => {
43-
if (!currentUser) return;
44-
try {
45-
setError(null);
46-
47-
await toggleTodoStatus(currentUser.uid, id, currentStatus);
48-
await loadTodos(currentUser.uid);
49-
} catch (err) {
50-
console.error(err);
51-
setError('상태를 업데이트하는 데 실패하였습니다');
52-
}
53-
};
20+
const {
21+
items,
22+
loading: planLoading,
23+
error: planError,
24+
toggle,
25+
progressText,
26+
} = useTodayPlanItems(currentUser?.uid);
5427

5528
// 사용자가 존재하면 데이터 불러옴
5629
useEffect(() => {
@@ -59,7 +32,6 @@ const Page = () => {
5932
try {
6033
const userProfile = await getProfile(currentUser.uid);
6134
setProfile(userProfile);
62-
await loadTodos(currentUser.uid);
6335
} catch (err) {
6436
console.error(err);
6537
setError('프로필 정보를 불러오는 데 실패하였습니다');
@@ -96,6 +68,7 @@ const Page = () => {
9668
className="grid grid-cols-1 gap-4 md:grid-cols-3"
9769
profile={profile}
9870
uid={currentUser.uid}
71+
progressText={progressText}
9972
/>
10073

10174
{/* 2-2. GraphSection */}
@@ -104,7 +77,10 @@ const Page = () => {
10477
{/* 2-3. BottomSection */}
10578
<BottomSection
10679
className="grid grid-cols-1 gap-4 md:grid-cols-2"
107-
uid={currentUser.uid}
80+
items={items}
81+
loading={planLoading}
82+
error={planError}
83+
onToggle={toggle}
10884
/>
10985

11086
{/* 3. ButtonSection */}

components/home/BottomSection.tsx

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,33 @@
11
'use client';
22

33
import TodayPlanContainer from './TodayPlanContainer';
4+
import type { PlanItem } from '@/services/plans/planManageService.service';
5+
46
interface BottomSectionProps {
5-
uid: string;
67
className?: string;
8+
items: PlanItem[];
9+
loading: boolean;
10+
error: string | null;
11+
onToggle: (id: string, checked: boolean) => void;
712
}
813

9-
// const TODAY_DUMMY = [
10-
// { id: 't1', text: 'React Hooks 정리', isChecked: false },
11-
// { id: 't2', text: 'GSAP ScrollTrigger 복습', isChecked: true },
12-
// { id: 't3', text: '알고리즘 1문제 풀기', isChecked: false },
13-
// ];
14-
15-
// const UPCOMING_DUMMY = [
16-
// { id: 'u1', text: 'Next.js App Router 정리', isChecked: false },
17-
// { id: 'u2', text: '포트폴리오 리팩토링', isChecked: false },
18-
// ];
19-
20-
export default function BottomSection({ className, uid }: BottomSectionProps) {
21-
// const [today, setToday] = useState<ChecklistItem[]>(TODAY_DUMMY);
22-
// const [upcoming, setUpcoming] = useState<ChecklistItem[]>(UPCOMING_DUMMY);
23-
24-
// const toggleToday = (id: string) => {
25-
// setToday((prev) =>
26-
// prev.map((it) =>
27-
// it.id === id ? { ...it, isChecked: !it.isChecked } : it
28-
// )
29-
// );
30-
// };
14+
export default function BottomSection({
15+
className,
16+
items,
17+
loading,
18+
error,
19+
onToggle,
20+
}: BottomSectionProps) {
3121

32-
// const toggleUpcoming = (id: string) => {
33-
// setUpcoming((prev) =>
34-
// prev.map((it) =>
35-
// it.id === id ? { ...it, isChecked: !it.isChecked } : it
36-
// )
37-
// );
38-
// };
3922

4023
return (
4124
<div className={className}>
42-
<TodayPlanContainer uid={uid}></TodayPlanContainer>
25+
<TodayPlanContainer
26+
items={items}
27+
loading={loading}
28+
error={error}
29+
onToggle={onToggle}
30+
></TodayPlanContainer>
4331

4432
{/* <Card title="다가오는 일정">
4533
<CheckList

components/home/ProfileSection.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ interface ProfileSectionProps {
77
className?: string;
88
profile: Profile | null;
99
uid: string;
10+
progressText: string;
1011
}
1112

12-
const ProfileSection = ({ className, profile, uid }: ProfileSectionProps) => {
13+
const ProfileSection = ({
14+
className,
15+
profile,
16+
uid,
17+
progressText,
18+
}: ProfileSectionProps) => {
1319
const icon = getRandomProfileIcon(uid);
1420
const avatarUrl = profile?.avatar_url;
1521

@@ -49,7 +55,9 @@ const ProfileSection = ({ className, profile, uid }: ProfileSectionProps) => {
4955

5056
<div className="md:col-span-1">
5157
<Card className="flex h-full flex-col items-center justify-center text-center">
52-
<div className="text-primary mb-1 text-4xl font-bold">3/5</div>
58+
<div className="text-primary mb-1 text-4xl font-bold">
59+
{progressText}
60+
</div>
5361
<div className="text-sm font-medium text-gray-500">
5462
오늘의 목표 달성률
5563
</div>

components/home/TodayPlanContainer.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,21 @@
22

33
import Card from '@/components/home/Card';
44
import CheckList from '@/components/home/CheckList';
5-
import { useTodayPlanItems } from '@/hooks/useTodayPlanItems';
5+
import { PlanItem } from '@/services/plans/planManageService.service';
66

7-
export default function TodayPlanContainer({ uid }: { uid: string }) {
8-
const { items, loading, error, toggle } = useTodayPlanItems(uid);
7+
type TodayPlanContainerProps = {
8+
items: PlanItem[];
9+
loading: boolean;
10+
error: string | null;
11+
onToggle: (id: string, checked: boolean) => void;
12+
};
913

14+
export default function TodayPlanContainer({
15+
items,
16+
loading,
17+
error,
18+
onToggle,
19+
}: TodayPlanContainerProps) {
1020
if (loading) {
1121
return <div className="text-sm text-gray-400">불러오는 중...</div>;
1222
}
@@ -19,7 +29,7 @@ export default function TodayPlanContainer({ uid }: { uid: string }) {
1929
<Card title="오늘의 할 일">
2030
<CheckList
2131
items={items}
22-
onToggleTodo={(id, checked) => toggle(id, checked)}
32+
onToggleTodo={(id, checked) => onToggle(id, checked)}
2333
emptyText="오늘 완료할 계획이 없습니다"
2434
/>
2535
</Card>

hooks/useTodayPlanItems.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export const useTodayPlanItems = (uid?: string) => {
2626
}
2727
}, [uid]);
2828

29-
// ✅ 진행률 계산
3029
const total = useMemo(() => items.length, [items]);
3130
const completed = useMemo(
3231
() => items.reduce((acc, it) => acc + (it.isChecked ? 1 : 0), 0),

0 commit comments

Comments
 (0)