diff --git a/src/app/timer/TimerView.tsx b/src/app/timer/TimerView.tsx index 9d5488a3..e7ce822d 100644 --- a/src/app/timer/TimerView.tsx +++ b/src/app/timer/TimerView.tsx @@ -4,7 +4,7 @@ import { center } from '@/styled-system/patterns'; interface Props { isActive: boolean; - time: [number, number]; + time: [string, string]; category: string; } export default function TimerView({ category, time, isActive }: Props) { @@ -12,7 +12,11 @@ export default function TimerView({ category, time, isActive }: Props) {
{category}
-
+
{time[0]} : {time[1]} @@ -39,6 +43,5 @@ const timerTextCss = { animation: 'gradient 3s ease-in-out infinite', backgroundSize: '150% 200%!', '-webkit-background-clip': 'text!', - color: 'transparent', background: 'linear-gradient(108deg, #FF8C8C -1.04%, #5D8AFF 101.48%)', }; diff --git a/src/app/timer/layout.tsx b/src/app/timer/layout.tsx index c69b896a..3aadb9f1 100644 --- a/src/app/timer/layout.tsx +++ b/src/app/timer/layout.tsx @@ -1,21 +1,10 @@ import { type PropsWithChildren } from 'react'; -import Header from '@/components/Layout/Header'; import { css } from '@/styled-system/css'; export default function Layout({ children }: PropsWithChildren) { - return ( -
-
-
{children}
-
- ); + return
{children}
; } const containerCss = css({ - background: 'linear-gradient(136deg, #FFF1F2 4.76%, #E9EFFF 89.58%)', minHeight: '100vh', }); - -const mainCss = css({ - padding: '24px 16px', -}); diff --git a/src/app/timer/page.tsx b/src/app/timer/page.tsx index 5d2bc498..cd14a574 100644 --- a/src/app/timer/page.tsx +++ b/src/app/timer/page.tsx @@ -1,28 +1,72 @@ 'use client'; import TimerView from '@/app/timer/TimerView'; -import useStep from '@/app/timer/useStep'; +import useTimer from '@/app/timer/useTimer'; +import useTimerStatus from '@/app/timer/useTimerStatus'; +import Header from '@/components/Layout/Header'; import { css } from '@styled-system/css'; -const STEP_LABEL = { - ready: { - title: '준비 되셨나요?', - desc: '타이머를 눌러서 10분의 미션을 완성해 주세요!', - }, -} as const; - export default function TimerPage() { - const { step } = useStep(); + const { step, stepLabel, onNextStep } = useTimerStatus(); + const { formattedTime } = useTimer(step); + + const onFinish = () => { + onNextStep('stop'); + alert('정말 끝내시겠습니까?'); + }; return ( -
-

{STEP_LABEL[step].title}

-

{STEP_LABEL[step].desc}

+
+
+
+

{stepLabel.title}

+

{stepLabel.desc}

- + +
+ {step === 'ready' && ( + + )} + {step === 'progress' && ( + <> + + + + )} + {step === 'stop' && ( + <> + + + + )} +
+
); } +const bgCss = { + minHeight: '100vh', + transition: '1s ease', +}; + +const containerCss = { + padding: '24px 16px', +}; + const font24Css = { fontSize: '24px', fontFamily: 'Pretendard', @@ -40,3 +84,17 @@ const font14Css = { const titleCss = css(font24Css, { color: '#333D4B' }); const descCss = css(font14Css, { color: '#6B7684', marginBottom: '84px' }); + +const buttonContainerCss = { + margin: '28px auto', + display: 'flex', + justifyContent: 'center', + gap: '12px', + + '& button': { + backgroundColor: 'white', + borderRadius: '30px', + padding: '16px 24px', + boxSizing: '0px 4px 20px 0px rgba(18, 23, 41, 0.10)', + }, +}; diff --git a/src/app/timer/useStep.ts b/src/app/timer/useStep.ts deleted file mode 100644 index fa73394d..00000000 --- a/src/app/timer/useStep.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useState } from 'react'; - -type StepType = 'ready'; - -function useStep() { - const [step, setStep] = useState('ready'); - - const onNextStep = () => {}; - return { step, onNextStep }; -} - -export default useStep; diff --git a/src/app/timer/useTimer.ts b/src/app/timer/useTimer.ts new file mode 100644 index 00000000..31018aa2 --- /dev/null +++ b/src/app/timer/useTimer.ts @@ -0,0 +1,34 @@ +import { useEffect, useState } from 'react'; +import { type StepType } from '@/app/timer/useTimerStatus'; + +export default function useTimer(status: StepType, initSeconds = 600) { + const [second, setSecond] = useState(initSeconds); // 남은 시간 (단위: 초) + + const formattedTime = formatMMSS(second); + + useEffect(() => { + let timer: NodeJS.Timeout; + + if (second <= 0) { + return; + } + + if (status === 'progress') { + timer = setInterval(() => { + setSecond((prev) => prev - 1); + }, 1000); + } + return () => clearInterval(timer); + }, [second, status]); + + return { formattedTime }; +} + +const formatMMSS = (second: number): [string, string] => { + const minutes = Math.floor(second / 60); // 분 계산 + const seconds = second % 60; // 초 계산 + const formattedMinutes = String(minutes).padStart(2, '0'); // 두 자리로 변환 + const formattedSeconds = String(seconds).padStart(2, '0'); // 두 자리로 변환 + + return [formattedMinutes, formattedSeconds]; +}; diff --git a/src/app/timer/useTimerStatus.ts b/src/app/timer/useTimerStatus.ts new file mode 100644 index 00000000..73dc7964 --- /dev/null +++ b/src/app/timer/useTimerStatus.ts @@ -0,0 +1,32 @@ +import { useState } from 'react'; + +export type StepType = 'ready' | 'progress' | 'stop'; + +const TIMER_STATUS = { + ready: { + title: '준비 되셨나요?', + desc: '타이머를 눌러서 10분의 미션을 완성해 주세요!', + }, + progress: { + title: '시작!', + desc: '10분 동안 최선을 다해주세요!', + }, + stop: { + title: '잠시 멈췄어요', + desc: '준비가 되면 타이머를 다시 시작해주세요!', + }, +} as const; + +function useTimerStatus() { + const [step, setStep] = useState('ready'); + + const stepLabel = TIMER_STATUS[step]; + + const onNextStep = (nextStep: StepType) => { + setStep(nextStep); + }; + + return { step, onNextStep, stepLabel }; +} + +export default useTimerStatus; diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx index 28dda3dd..2afc2c6c 100644 --- a/src/components/Layout/Header.tsx +++ b/src/components/Layout/Header.tsx @@ -8,18 +8,30 @@ interface Props { function Header({ title }: Props) { return ( -
- -

{title}

-
+ <> +
+ +

{title}

+
+
+ ); } +const headerBlankCss = { + height: '42px;', + width: '100%', +}; + const wrapperCss = flex({ padding: '10px 16px', gap: '6px', + background: 'transparent', + position: 'fixed', + margin: '0 auto', + zIndex: 100, }); const headingCss = css({