feat: 다국어 화
This commit is contained in:
@@ -8,18 +8,12 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { useI18n } from "@/features/i18n/model/useI18n";
|
||||
import { DEBRIEF_STATUS_OPTIONS } from "@/shared/config/i18n";
|
||||
import { findRouteById } from "@/shared/config/routes";
|
||||
import { saveCurrentVoyage, saveToHistory } from "@/shared/lib/store";
|
||||
import { Voyage, VoyageStatus } from "@/shared/types";
|
||||
|
||||
const statusOptions: { value: VoyageStatus; label: string; desc: string }[] = [
|
||||
{ value: "completed", label: "계획대로", desc: "목표를 달성했습니다" },
|
||||
{ value: "partial", label: "부분 진행", desc: "절반의 성과를 만들었습니다" },
|
||||
{
|
||||
value: "reoriented",
|
||||
label: "방향 재설정",
|
||||
desc: "우선순위를 새로 정했습니다",
|
||||
},
|
||||
];
|
||||
const FINISH_HOLD_MS = 1000;
|
||||
|
||||
type FlightHudWidgetProps = {
|
||||
@@ -39,6 +33,7 @@ export function FlightHudWidget({
|
||||
handlePauseToggle,
|
||||
handleFinish,
|
||||
}: FlightHudWidgetProps) {
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const [isDebriefOpen, setIsDebriefOpen] = useState(false);
|
||||
const [finishedVoyage, setFinishedVoyage] = useState<Voyage | null>(null);
|
||||
@@ -130,11 +125,21 @@ export function FlightHudWidget({
|
||||
|
||||
if (!voyage) return null;
|
||||
|
||||
const route = findRouteById(voyage.routeId);
|
||||
const routeName = route
|
||||
? t(route.nameKey, undefined, voyage.routeName)
|
||||
: voyage.routeName;
|
||||
const statusOptions = DEBRIEF_STATUS_OPTIONS.map((option) => ({
|
||||
value: option.value as VoyageStatus,
|
||||
label: t(option.labelKey),
|
||||
desc: t(option.descKey),
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="absolute top-8 z-10 text-center">
|
||||
<span className="rounded-full border border-indigo-500/30 bg-indigo-950/50 px-4 py-1.5 text-xs font-medium uppercase tracking-widest text-indigo-300 shadow-[0_0_15px_rgba(99,102,241,0.3)] backdrop-blur">
|
||||
{voyage.routeName} · {isPaused ? "일시정지" : "순항 중"}
|
||||
{routeName} · {isPaused ? t("flight.badge.paused") : t("flight.badge.cruising")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -147,7 +152,7 @@ export function FlightHudWidget({
|
||||
<div className="relative z-10 mb-24 w-full max-w-2xl px-4">
|
||||
<section className="rounded-2xl border border-slate-600/60 bg-slate-950/55 p-4 text-left shadow-[0_16px_40px_rgba(2,6,23,0.35)] backdrop-blur md:p-6">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-slate-400 md:text-xs">
|
||||
이번 항해 목표
|
||||
{t("flight.missionLabel")}
|
||||
</p>
|
||||
<p className="mt-2 max-h-32 overflow-y-auto break-words whitespace-pre-wrap pr-1 text-base leading-relaxed text-slate-100 md:text-lg">
|
||||
{voyage.missionText}
|
||||
@@ -160,7 +165,7 @@ export function FlightHudWidget({
|
||||
onClick={handlePauseToggle}
|
||||
className="rounded-full border border-slate-600 bg-slate-900/50 px-8 py-3 text-sm font-bold uppercase tracking-wide text-slate-300 backdrop-blur transition-all hover:border-slate-400 hover:bg-slate-800/80 hover:text-white"
|
||||
>
|
||||
{isPaused ? "다시 시작" : "일시정지"}
|
||||
{isPaused ? t("flight.resume") : t("flight.pause")}
|
||||
</button>
|
||||
<div className="relative">
|
||||
<button
|
||||
@@ -190,7 +195,9 @@ export function FlightHudWidget({
|
||||
style={{ transform: `scaleX(${holdProgress})` }}
|
||||
/>
|
||||
<span className="relative z-10">
|
||||
{isCountdownCompleted ? "도착 (회고)" : "항해 종료"}
|
||||
{isCountdownCompleted
|
||||
? t("flight.finish.debrief")
|
||||
: t("flight.finish.end")}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -200,34 +207,36 @@ export function FlightHudWidget({
|
||||
<DialogContent className="max-h-[90vh] overflow-y-auto border-slate-700/80 bg-slate-950 text-slate-100">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-white">
|
||||
이번 항해를 정리하세요
|
||||
{t("flight.debrief.title")}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-slate-400">
|
||||
짧게 기록하고 항해일지에 저장합니다.
|
||||
{t("flight.debrief.description")}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleDebriefSubmit} className="space-y-6">
|
||||
<section>
|
||||
<label className="mb-3 block text-sm font-medium text-slate-300">
|
||||
항해 결과
|
||||
{t("debrief.status.label")}
|
||||
</label>
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
|
||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-3">
|
||||
{statusOptions.map((opt) => (
|
||||
<button
|
||||
key={opt.value}
|
||||
type="button"
|
||||
onClick={() => setStatus(opt.value)}
|
||||
className={`rounded-xl border p-4 text-left transition-all ${
|
||||
className={`flex min-h-[116px] flex-col justify-between rounded-xl border px-4 py-3.5 text-left transition-all ${
|
||||
status === opt.value
|
||||
? "border-indigo-500 bg-indigo-900/40 ring-1 ring-indigo-500"
|
||||
: "border-slate-800 bg-slate-900/50 hover:bg-slate-800"
|
||||
}`}
|
||||
>
|
||||
<div className="mb-1 font-bold text-slate-200">
|
||||
<div className="text-sm leading-snug font-bold text-slate-200 break-keep">
|
||||
{opt.label}
|
||||
</div>
|
||||
<div className="text-xs text-slate-500">{opt.desc}</div>
|
||||
<div className="mt-2 text-[11px] leading-relaxed text-slate-500">
|
||||
{opt.desc}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -235,13 +244,13 @@ export function FlightHudWidget({
|
||||
|
||||
<section>
|
||||
<label className="mb-2 block text-sm font-medium text-slate-300">
|
||||
이번 항해에서 확보한 것
|
||||
{t("debrief.reflection.label")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={progress}
|
||||
onChange={(event) => setProgress(event.target.value)}
|
||||
placeholder="예: 기획안 목차 구성 완료"
|
||||
placeholder={t("debrief.reflection.placeholder")}
|
||||
className="w-full rounded-lg border border-slate-800 bg-slate-900/30 px-4 py-3 text-slate-200 outline-none transition-all focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500"
|
||||
/>
|
||||
</section>
|
||||
@@ -252,14 +261,14 @@ export function FlightHudWidget({
|
||||
onClick={() => setIsDebriefOpen(false)}
|
||||
className="rounded-xl border border-slate-700 bg-slate-900/60 px-4 py-3 font-semibold text-slate-300 transition-colors hover:border-slate-500 hover:text-white"
|
||||
>
|
||||
취소
|
||||
{t("boarding.cancel")}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!status}
|
||||
className="rounded-xl bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-500 disabled:bg-slate-800 disabled:text-slate-500"
|
||||
>
|
||||
항해일지 저장
|
||||
{t("debrief.save")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user