128 lines
3.9 KiB
TypeScript
128 lines
3.9 KiB
TypeScript
// app/session/end/page.tsx
|
|
"use client";
|
|
|
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
import { Suspense, useMemo, useState } from "react";
|
|
|
|
type Mode = "freeflow" | "sprint" | "deepwork";
|
|
|
|
const BG = "#E9EEF6";
|
|
const BORDER = "#C9D7F5";
|
|
const PRIMARY = "#2F6FED";
|
|
const PRIMARY_HOVER = "#295FD1";
|
|
|
|
function clampMode(v: string | null): Mode {
|
|
if (v === "sprint" || v === "deepwork" || v === "freeflow") return v;
|
|
return "freeflow";
|
|
}
|
|
|
|
function modeLabel(mode: Mode) {
|
|
if (mode === "sprint") return "스프린트";
|
|
if (mode === "deepwork") return "딥워크";
|
|
return "프리플로우";
|
|
}
|
|
|
|
function hhmmss(total: number) {
|
|
const s = Math.max(0, Math.floor(total));
|
|
const hh = String(Math.floor(s / 3600)).padStart(2, "0");
|
|
const mm = String(Math.floor((s % 3600) / 60)).padStart(2, "0");
|
|
const ss = String(s % 60).padStart(2, "0");
|
|
return `${hh}:${mm}:${ss}`;
|
|
}
|
|
|
|
export default function Page() {
|
|
return (
|
|
<Suspense
|
|
fallback={
|
|
<main className="min-h-screen w-full" style={{ backgroundColor: BG }} />
|
|
}
|
|
>
|
|
<SessionEndInner />
|
|
</Suspense>
|
|
);
|
|
}
|
|
|
|
function SessionEndInner() {
|
|
const router = useRouter();
|
|
const params = useSearchParams();
|
|
|
|
const [elapsed, setElapsed] = useState(() => {
|
|
const v = Number(localStorage.getItem("hushroom:session-elapsed") ?? "0");
|
|
return Number.isFinite(v) ? v : 0;
|
|
});
|
|
|
|
const [goal, setGoal] = useState(() => {
|
|
const goal = localStorage.getItem("hushroom:session-goal");
|
|
return goal;
|
|
});
|
|
|
|
const mode = useMemo(() => clampMode(params.get("mode")), [params]);
|
|
|
|
return (
|
|
<main className="min-h-screen w-full" style={{ backgroundColor: BG }}>
|
|
<header className="px-5 pt-6">
|
|
<div className="select-none text-xl font-bold tracking-tight leading-none text-slate-800">
|
|
hushroom
|
|
</div>
|
|
</header>
|
|
|
|
<section className="mx-auto flex min-h-[calc(100vh-64px)] max-w-lg flex-col justify-center px-5 pb-10 pt-6">
|
|
<div
|
|
className="rounded-3xl border bg-white px-6 py-6 shadow-sm"
|
|
style={{ borderColor: BORDER }}
|
|
>
|
|
<div className="text-sm font-semibold text-slate-600">
|
|
{modeLabel(mode)}
|
|
</div>
|
|
<div className="mt-3 text-[44px] font-semibold leading-none text-slate-900 tabular-nums">
|
|
{hhmmss(elapsed)}
|
|
</div>
|
|
|
|
{/* Goal */}
|
|
{goal && (
|
|
<div
|
|
className="mt-4 rounded-2xl border bg-[#F1F5FF] px-4 py-3"
|
|
style={{ borderColor: BORDER }}
|
|
>
|
|
<div className="text-xs font-semibold text-slate-600">
|
|
이번 목표
|
|
</div>
|
|
<div className="mt-1 text-xl font-semibold text-slate-900 line-clamp-2">
|
|
{goal}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="mt-3 text-base text-slate-700">세션이 종료됐어요</div>
|
|
</div>
|
|
|
|
<div className="mt-5 grid grid-cols-2 gap-3">
|
|
<button
|
|
type="button"
|
|
onClick={() => router.push("/")}
|
|
className="rounded-3xl border bg-white px-5 py-4 text-base font-semibold text-slate-800 shadow-sm transition active:scale-[0.99]"
|
|
style={{ borderColor: BORDER }}
|
|
>
|
|
홈
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={() => router.push(`/session?mode=${mode}`)}
|
|
className="rounded-3xl px-5 py-4 text-base font-semibold text-white shadow-sm transition active:scale-[0.99]"
|
|
style={{ backgroundColor: PRIMARY }}
|
|
onMouseEnter={(e) =>
|
|
(e.currentTarget.style.backgroundColor = PRIMARY_HOVER)
|
|
}
|
|
onMouseLeave={(e) =>
|
|
(e.currentTarget.style.backgroundColor = PRIMARY)
|
|
}
|
|
>
|
|
다시
|
|
</button>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
);
|
|
}
|