Skip to content

Commit

Permalink
Merge pull request #24 from LetsExercise/feat-personalGoal
Browse files Browse the repository at this point in the history
Feat personal goal
  • Loading branch information
castberry10 authored Aug 6, 2024
2 parents 7c8b126 + 82ce225 commit 429c455
Show file tree
Hide file tree
Showing 16 changed files with 1,280 additions and 32 deletions.
941 changes: 915 additions & 26 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
"lint": "next lint"
},
"dependencies": {

"@emotion/styled": "^11.13.0",
"@mui/x-date-pickers": "^7.12.0",
"dayjs": "^1.11.12",
"axios": "^1.7.3",
"firebase": "^10.12.3",
"next": "14.2.5",
Expand Down
29 changes: 29 additions & 0 deletions src/app/_common/_util/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { PersonalGoals } from "@/lib/firebase/api/personalGoalAPI";

const initStorage = <T extends keyof StorageKey>(key: T, storage: Storage) => {
const storageKey = `${key}`;

const get = (): StorageKey[T] => {
const value = storage.getItem(storageKey);

return JSON.parse(value as string);
};
const set = (value?: StorageKey[T]) => {
if (value == undefined || value == null) {
return storage.removeItem(storageKey);
}

const stringifiedValue = JSON.stringify(value);

storage.setItem(storageKey, stringifiedValue);
};

return { get, set };
};

export const personalGoalsStorage = initStorage("personalGoals", sessionStorage);


interface StorageKey {
personalGoals?: PersonalGoals;
}
9 changes: 4 additions & 5 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.scss";
import Head from 'next/head';
import Head from "next/head";
import favicon from "./favicon.ico";
const inter = Inter({ subsets: ["latin"] });
import style from "./layout.module.scss";

export const metadata: Metadata = {
title: "dailypet",
description: "My Daily Companion, Daily Pet",
icons: {
icon: '/favicon.ico',
icon: "/favicon.ico",
},
};

Expand All @@ -25,9 +26,7 @@ export default function RootLayout({
</Head> */}
<body>
<div className={style.parent}>
<div className={style.child}>
{children}
</div>
<div className={style.child}>{children}</div>
</div>
</body>
</html>
Expand Down
26 changes: 26 additions & 0 deletions src/app/mypage/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";

export default function GoalsLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
/*
TODO:
MUI 사용하려면 이걸로 컴포넌트 둘러야 합니다.
루트페이지 layout에 두르려고 했는데
메타데이터 형식때문에 use client가 안써져서
임시로 여기다 둘렀습니다.
다른 페이지에도 필요하면 그때 고쳐보아요
*/
<LocalizationProvider dateAdapter={AdapterDayjs}>
{children}
</LocalizationProvider>
);
}
15 changes: 14 additions & 1 deletion src/app/mypage/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
"use client";

import Link from "next/link";
import styles from "./page.module.scss";
import { usePersonalGoals } from "@/lib/firebase/api/personalGoalAPI";

export default function MyPage() {
const { data, isLoading, isError } = usePersonalGoals();

if (isLoading) return <div>Loading...</div>;
if (isError) return <div>error!</div>;
if (!data) return <div></div>;

console.log(data);
return (
<div className={styles.main}>
MyPage
MyPage
<div>{data.diet?.calories}</div>
<Link href="/mypage/personalGoals">식단 목표 설정하기</Link>
</div>
);
}
21 changes: 21 additions & 0 deletions src/app/mypage/personalGoals/diet/DietEl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { MobileTimePicker } from "@mui/x-date-pickers";
import dayjs from "dayjs";
import { formName } from "./formName";

export default function DietEl({ index }: { index: number }) {
return (
<div>
<h3>{index}번 째 식단</h3>
<input
type="text"
name={formName.label(index)}
placeholder="식단이름설정 ex) 아점, 아침"
/>
<MobileTimePicker
label="식사 시간"
name={formName.time(index)}
defaultValue={dayjs(new Date())}
/>
</div>
);
}
6 changes: 6 additions & 0 deletions src/app/mypage/personalGoals/diet/formName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const formName = {
calories: "calories",
dietTime: "dietTime",
label: (index: number) => `label_${index}`,
time: (index: number) => `time_${index}`,
};
56 changes: 56 additions & 0 deletions src/app/mypage/personalGoals/diet/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client";

import { FormEvent, useState } from "react";
import DietEl from "./DietEl";
import { useRouter } from "next/navigation";
import { formName } from "./formName";
import { timeFormat } from "../../utils";
import { personalGoalsStorage } from "@/app/_common/_util/storage";

export default function DietGoalPage() {
const [dietElList, setDietElList] = useState<JSX.Element[]>([]);
const router = useRouter();

const addDiet = () => {
setDietElList([
...dietElList,
<DietEl key={dietElList.length} index={dietElList.length} />,
]);
};
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const calories = Number(formData.get(formName.calories));
const dietList = dietElList.map((dietEl) => {
const label = String(formData.get(formName.label(dietEl.props.index)));
const time = timeFormat(
String(formData.get(formName.time(dietEl.props.index)))
);
return { label, time };
});

personalGoalsStorage.set({
...personalGoalsStorage.get(),
diet: { calories, dietTime: dietList },
});
router.push("/mypage/personalGoals/workout");
};

return (
<div>
<h1>식단 목표 설정하기</h1>
<form onSubmit={handleSubmit}>
<input
type="number"
name={formName.calories}
placeholder="하루 섭취 칼로리"
/>
{dietElList}
<button type="button" onClick={addDiet}>
식단 추가
</button>
<button type="submit">다음</button>
</form>
</div>
);
}
14 changes: 14 additions & 0 deletions src/app/mypage/personalGoals/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Link from "next/link";

export default function PersonalGoalsPage() {
return (
<div>
<h1>Personal Goal Page</h1>
<div>
개인 목표를 설정해봅시다! 설정한 목표에 따라 점수가 계산되고 캐릭터의
상태가 달라져요~~~ 같은 설명 추가
</div>
<Link href="/mypage/personalGoals/sleep">설정하기</Link>
</div>
);
}
44 changes: 44 additions & 0 deletions src/app/mypage/personalGoals/sleep/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import { MobileTimePicker } from "@mui/x-date-pickers";
import dayjs from "dayjs";
import { useRouter } from "next/navigation";
import { FormEvent } from "react";
import { timeFormat } from "../../utils";
import { personalGoalsStorage } from "@/app/_common/_util/storage";

export default function SleepGoalPage() {
const router = useRouter();

const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();

const formData = new FormData(e.currentTarget);
const sleepStart = timeFormat(String(formData.get("sleepStart")));
const sleepEnd = timeFormat(String(formData.get("sleepEnd")));

personalGoalsStorage.set({
...personalGoalsStorage.get(),
sleep: { sleepStart, sleepEnd },
});
router.push("/mypage/personalGoals/diet");
};
return (
<div>
<h1>수면 목표 설정하기</h1>
<form onSubmit={(e) => handleSubmit(e)}>
<MobileTimePicker
label="취침 시간"
name="sleepStart"
defaultValue={dayjs("2022-04-17T21:00")}
/>
<MobileTimePicker
label="기상 시간"
name="sleepEnd"
defaultValue={dayjs("2022-04-17T06:00")}
/>
<button type="submit">다음</button>
</form>
</div>
);
}
30 changes: 30 additions & 0 deletions src/app/mypage/personalGoals/workout/WorkoutEl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { MobileTimePicker } from "@mui/x-date-pickers";
import { formName } from "./formName";
import dayjs from "dayjs";

export default function WorkoutEl({ index }: { index: number }) {
return (
<div>
<h3>{index}번 째 운동</h3>
<select name={formName.week(index)} id="week">
<option value="0"></option>
<option value="1"></option>
<option value="2"></option>
<option value="3"></option>
<option value="4"></option>
<option value="5"></option>
<option value="6"></option>
</select>
<MobileTimePicker
label="시작 시간"
name={formName.start(index)}
defaultValue={dayjs(new Date())}
/>
<MobileTimePicker
label="종료 시간"
name={formName.end(index)}
defaultValue={dayjs(new Date())}
/>
</div>
);
}
5 changes: 5 additions & 0 deletions src/app/mypage/personalGoals/workout/formName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const formName = {
week: (index: number) => `week_${index}`,
start: (index: number) => `start_${index}`,
end: (index: number) => `end_${index}`,
}
53 changes: 53 additions & 0 deletions src/app/mypage/personalGoals/workout/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use client";

import { useRouter } from "next/navigation";
import { useState } from "react";
import WorkoutEl from "./WorkoutEl";
import { personalGoalsStorage } from "@/app/_common/_util/storage";
import { timeFormat } from "../../utils";
import { postPersonalGoals } from "@/lib/firebase/api/personalGoalAPI";

export default function WorkoutGoalPage() {
const router = useRouter();
const [workoutElList, setWorkoutElList] = useState<JSX.Element[]>([]);

const addWorkout = () =>
setWorkoutElList([
...workoutElList,
<WorkoutEl key={workoutElList.length} index={workoutElList.length} />,
]);
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const workoutList = workoutElList.map((workoutEl) => {
const week = Number(formData.get(`week_${workoutEl.props.index}`));
const start = timeFormat(
String(formData.get(`start_${workoutEl.props.index}`))
);
const end = timeFormat(
String(formData.get(`end_${workoutEl.props.index}`))
);
return { week, start, end };
});

personalGoalsStorage.set({
...personalGoalsStorage.get(),
workout: workoutList,
});
postPersonalGoals(personalGoalsStorage.get()!);
personalGoalsStorage.set();
router.push("/mypage");
};
return (
<div>
<h1>운동 목표 설정하기</h1>
<form onSubmit={(e) => handleSubmit(e)}>
{workoutElList}
<button type="button" onClick={addWorkout}>
운동 추가
</button>
<button type="submit">다음</button>
</form>
</div>
);
}
9 changes: 9 additions & 0 deletions src/app/mypage/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* 06:00 PM -> 1800 (number) */
export const timeFormat = (time: string) => {
const [timeToken, meridiem] = time.split(" ");
const [hour, minute] = timeToken.split(":");

return meridiem === "PM"
? (Number(hour) + 12) * 100 + Number(minute)
: Number(hour) * 100 + Number(minute);
};
Loading

0 comments on commit 429c455

Please sign in to comment.