Skip to content

Commit 3ec1119

Browse files
authored
Mypage 구현 (#27)
* feat: implement 4-step onboarding flow with Figma design integration - Add main onboarding page with state management for 4-step flow - Create LoginScreen component with Google/Kakao login options - Create GoalInputScreen with validation and tooltip functionality - Create PeriodSelectionScreen with month selector and calendar - Create CompletionScreen with animated check and goal summary - Update Tailwind config to integrate design tokens from tokens.css - Add onboarding entry point to main page - Implement responsive mobile-first design patterns - Add TypeScript interfaces and proper type safety * feat: set layout * feat: add dummy login and redirection * update rule * fix AppBar * fix margin * fix appBar with thick height * remove Home indicator * add BottunRound * update rule * replace with ButtonRound * replace with ButtonRound on CompletionScreen * replace with ToolTip-1 * revert tailwind * fix ToolTip * add mt-number-16 * add moti-check * impl login - 1 * impl mypage base * add profile-default * add text-black * add NotificationSetup * impl TermsOfServcie * add EditProfile * impl delete account * add DoneItemDetail * add CupertinoPicker * extract CupertinoPicker * set url to picsum random picture * hide title * convert filled to outlined * handle svg * remove gestureBar * useSafeRouter to handle storybook err
1 parent a5ef53d commit 3ec1119

39 files changed

+2166
-20
lines changed

.cursor/rules/global-rule.mdc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ alwaysApply: true
88
- For every styling, use tailwind css.
99
- For tokens, use [tokens.css](mdc:app/tokens.css).
1010
- If there is Appbar (time, wifi, battery, ..), Home Indicator and so on.. just ignore for now. We are building desktop web browser version first.
11+
- Ignore Home Indicator (GestureBar)
1112

1213
# Component
1314

app/mypage/done/[id]/page.tsx

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"use client";
2+
3+
import { DoneItemDetail } from "@/components/mypage";
4+
import { useParams } from "next/navigation";
5+
6+
export default function DoneItemDetailPage() {
7+
const params = useParams();
8+
const goalId = params.id as string;
9+
10+
// Mock data - in a real app, this would come from an API based on goalId
11+
const getGoalDetailById = (id: string) => {
12+
const goalDetails = {
13+
"1": {
14+
id: "1",
15+
title: "해외로 취업하자!",
16+
subGoals: [
17+
{
18+
id: "1",
19+
title: "포트폴리오 및 이력서 준비하기",
20+
completedTodos: [
21+
{
22+
id: "1",
23+
title: "투두 리스트 타이틀이 들어갑니다.",
24+
completedDate: "2025.05.12",
25+
attachment: {
26+
type: "image" as const,
27+
url: "https://picsum.photos/200",
28+
},
29+
},
30+
{
31+
id: "2",
32+
title: "투두 리스트 타이틀이 들어갑니다.",
33+
completedDate: "2025.05.12",
34+
attachment: {
35+
type: "file" as const,
36+
url: "https://picsum.photos/200",
37+
name: "파일명이 여기에.pdf",
38+
},
39+
},
40+
{
41+
id: "3",
42+
title: "투두 리스트 타이틀이 들어갑니다.",
43+
completedDate: "2025.05.12",
44+
},
45+
],
46+
},
47+
{
48+
id: "2",
49+
title: "세부목표명이 여기에 들어갑니다.",
50+
completedTodos: [
51+
{
52+
id: "4",
53+
title: "투두 리스트 타이틀이 들어갑니다.",
54+
completedDate: "2025.05.12",
55+
attachment: {
56+
type: "image" as const,
57+
url: "https://picsum.photos/200",
58+
},
59+
},
60+
{
61+
id: "5",
62+
title: "투두 리스트 타이틀이 들어갑니다.",
63+
completedDate: "2025.05.12",
64+
},
65+
],
66+
},
67+
],
68+
},
69+
"2": {
70+
id: "2",
71+
title: "건강한 생활 습관 만들기",
72+
subGoals: [
73+
{
74+
id: "3",
75+
title: "매일 운동하기",
76+
completedTodos: [
77+
{
78+
id: "6",
79+
title: "헬스장 등록하기",
80+
completedDate: "2025.03.01",
81+
},
82+
{
83+
id: "7",
84+
title: "운동 계획표 작성",
85+
completedDate: "2025.03.02",
86+
attachment: {
87+
type: "file" as const,
88+
url: "/workout-plan.pdf",
89+
name: "운동계획표.pdf",
90+
},
91+
},
92+
],
93+
},
94+
],
95+
},
96+
};
97+
98+
return goalDetails[id as keyof typeof goalDetails] || goalDetails["1"];
99+
};
100+
101+
const goalDetail = getGoalDetailById(goalId);
102+
103+
return <DoneItemDetail goalDetail={goalDetail} />;
104+
}

app/mypage/done/page.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"use client";
2+
3+
import { DoneItems } from "@/components/mypage";
4+
5+
export default function DoneItemsPage() {
6+
// Mock data - in a real app, this would come from an API or state management
7+
const completedGoals = [
8+
{
9+
id: "1",
10+
title: "해외로 취업하자!",
11+
period: "6개월",
12+
totalTodos: 6,
13+
totalRecords: 2,
14+
},
15+
{
16+
id: "2",
17+
title: "건강한 생활 습관 만들기",
18+
period: "3개월",
19+
totalTodos: 40,
20+
totalRecords: 10,
21+
},
22+
{
23+
id: "3",
24+
title: "독서 습관 완성하기",
25+
period: "2025.05.01",
26+
totalTodos: 40,
27+
totalRecords: 10,
28+
},
29+
];
30+
31+
return <DoneItems goals={completedGoals} />;
32+
}

app/mypage/edit/page.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"use client";
2+
3+
import { EditProfile } from "@/components/mypage";
4+
5+
export default function EditProfilePage() {
6+
const handleSave = (data: { name: string; bio: string }) => {
7+
console.log("Saving profile data:", data);
8+
// TODO: Implement actual save logic
9+
};
10+
11+
const handleDeleteAccount = () => {
12+
console.log("Delete account requested");
13+
// TODO: Implement account deletion logic
14+
};
15+
16+
const handleAddInterests = () => {
17+
console.log("Add interests requested");
18+
// TODO: Navigate to interests selection page
19+
};
20+
21+
return (
22+
<EditProfile
23+
initialName="홍길동"
24+
initialBio=""
25+
profileImageUrl="/profile-default.png"
26+
onSave={handleSave}
27+
onDeleteAccount={handleDeleteAccount}
28+
onAddInterests={handleAddInterests}
29+
/>
30+
);
31+
}

app/mypage/notifications/page.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"use client";
2+
3+
import { NotificationSetup } from "@/components/mypage";
4+
import { useRouter } from "next/navigation";
5+
6+
export default function NotificationSetupPage() {
7+
const router = useRouter();
8+
9+
const handleBack = () => {
10+
router.back();
11+
};
12+
13+
return <NotificationSetup onBack={handleBack} />;
14+
}

app/mypage/page.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use client";
2+
3+
import { MyPage } from "@/components/mypage";
4+
5+
export default function MyPageRoute() {
6+
return <MyPage />;
7+
}

app/mypage/terms/page.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"use client";
2+
3+
import { TermsOfService } from "@/components/mypage";
4+
import { useRouter } from "next/navigation";
5+
6+
export default function TermsOfServicePage() {
7+
const router = useRouter();
8+
9+
const handleBack = () => {
10+
router.back();
11+
};
12+
13+
return <TermsOfService onBack={handleBack} />;
14+
}

app/onboarding/_components/LoginScreen.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
OAUTH_ENDPOINTS,
99
FRONTEND_BASE_URL,
1010
} from "@/lib/constants";
11+
import MotimoLogoBlack from "@/components/shared/public/MOTIMO_LOGO_BLACK.svg"
1112

1213
interface LoginScreenProps {
1314
onNext: () => void;
@@ -188,11 +189,9 @@ export default function LoginScreen({ onNext }: LoginScreenProps) {
188189

189190
{/* Logo Icon */}
190191
<div className="flex justify-center">
191-
<div className="w-[219px] h-[36px] bg-label-normal rounded flex items-center justify-center">
192-
<span className="text-background-alternative font-bold text-lg">
193-
MOTIMO
194-
</span>
195-
</div>
192+
<MotimoLogoBlack
193+
className="w-[219px] h-[36px]"
194+
/>
196195
</div>
197196
</div>
198197
</div>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
interface ChevronRightIconProps {
2+
className?: string;
3+
size?: number;
4+
}
5+
6+
export function ChevronRightIcon({
7+
className = "w-6 h-6",
8+
size = 24
9+
}: ChevronRightIconProps) {
10+
return (
11+
<svg
12+
width={size}
13+
height={size}
14+
viewBox="0 0 24 24"
15+
fill="none"
16+
xmlns="http://www.w3.org/2000/svg"
17+
className={className}
18+
>
19+
<path
20+
d="M9 18L15 12L9 6"
21+
stroke="currentColor"
22+
strokeWidth={1.5}
23+
strokeLinecap="round"
24+
strokeLinejoin="round"
25+
/>
26+
</svg>
27+
);
28+
}

components/icons/EditIcon.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from "react";
2+
3+
interface EditIconProps {
4+
className?: string;
5+
}
6+
7+
export const EditIcon: React.FC<EditIconProps> = ({ className = "" }) => {
8+
return (
9+
<svg
10+
width="16"
11+
height="16"
12+
viewBox="0 0 16 16"
13+
fill="none"
14+
xmlns="http://www.w3.org/2000/svg"
15+
className={className}
16+
>
17+
<path
18+
d="M9.94 2.19L3.7 8.78C3.49 9.01 3.29 9.45 3.25 9.76L2.98 12.25C2.89 13.04 3.45 13.58 4.24 13.46L6.71 13.1C7.02 13.05 7.45 12.83 7.67 12.61L13.91 6.02C14.85 5.02 15.28 3.87 13.81 2.49C12.35 1.12 11.23 1.58 10.29 2.58L9.94 2.19Z"
19+
stroke="#8A949E"
20+
strokeWidth="1.5"
21+
strokeLinecap="round"
22+
strokeLinejoin="round"
23+
/>
24+
<path
25+
d="M9.17 2.98C9.63 4.81 11.09 6.16 12.95 6.52"
26+
stroke="#8A949E"
27+
strokeWidth="1.5"
28+
strokeLinecap="round"
29+
strokeLinejoin="round"
30+
/>
31+
</svg>
32+
);
33+
};

0 commit comments

Comments
 (0)