"use client"; import { getDocumentIntlLocale } from "@/lib/i18n/browser"; import { useTranslations } from "@/components/i18n/i18n-provider"; import { LocalizedFields, type LocalizedFieldLocale, type LocalizedFieldValues } from "@/components/i18n/localized-fields"; import { contentTranslationRegistry } from "@/lib/i18n/content"; import { createDailyLogRecord, deleteDailyLogRecord, updateDailyLogRecord, } from "@/app/(dashboard)/journal/actions"; import { Badge, Button, Card, CardContent, Input, Label } from "poyraz-ui/atoms"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, toast, } from "poyraz-ui/molecules"; import { Activity, Battery, CalendarDays, LineChart as LineChartIcon, Pencil, Plus, Smile, Trash2, } from "lucide-react"; import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts"; import { useMemo, useState } from "react"; import { StatCard } from "@/components/system/stat-card"; export type DailyLogItem = { id: string; log_date: string; mood_score: number; energy_score: number; work_satisfaction_score: number | null; mood_label: string | null; note: string | null; translations?: LocalizedFieldValues; }; type JournalClientProps = { logs: DailyLogItem[]; localization: { defaultLocale: string; locales: LocalizedFieldLocale[]; }; }; const scoreLabels = (t: ReturnType) => ({ 1: t("journal.scores.veryLow"), 2: t("journal.scores.low"), 3: t("journal.scores.medium"), 4: t("journal.scores.high"), 5: t("journal.scores.veryHigh"), }); export function JournalClient({ logs, localization }: JournalClientProps) { const t = useTranslations(); const summary = useMemo(() => calculateSummary(logs), [logs]); const chartData = useMemo( () => [...logs] .sort((a, b) => a.log_date.localeCompare(b.log_date)) .map((log) => ({ date: formatShortDate(log.log_date), mood: log.mood_score, energy: log.energy_score, satisfaction: log.work_satisfaction_score, })), [logs], ); return (

{t("journal.title")}

{t("journal.charts.trend.title")}

{t("journal.charts.trend.description")}

{chartData.length > 0 ? (
) : ( )}

{t("journal.charts.insights.title")}

{t("journal.charts.insights.description")}

{summary.length === 0 ? (
{t("journal.insights.noTrend")}
) : ( <>
{t("journal.insights.totalDays", { count: summary.length })}
{summary.energyAverage && summary.energyAverage < 3 ? t("journal.insights.lowEnergy") : t("journal.insights.balancedEnergy")}
{summary.moodAverage && summary.moodAverage >= 4 ? t("journal.insights.strongMood") : t("journal.insights.watchMood")}
)}

{t("journal.list.title")}

{t("journal.list.description", { count: logs.length })}

{logs.length > 0 ? (
{t("journal.list.headers.date")} {t("journal.list.headers.mood")} {t("journal.list.headers.energy")} {t("journal.list.headers.note")} {t("journal.list.headers.action")}
{logs.map((log) => ( ))}
) : ( )}
); } function DailyLogRow({ log, localization }: { log: DailyLogItem; localization: JournalClientProps["localization"] }) { const t = useTranslations(); return (
{formatDate(log.log_date)}
{formatWeekday(log.log_date)}
{log.mood_label ? (

{log.mood_label}

) : null}

{log.note || t("journal.empty.noNote")}

{log.work_satisfaction_score ? (

{t("journal.fields.satisfaction")}: {log.work_satisfaction_score}/5

) : null}
); } function DailyLogDialog({ mode, log, localization }: { mode: "create" | "edit"; log?: DailyLogItem; localization: JournalClientProps["localization"] }) { const t = useTranslations(); const [open, setOpen] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const action = mode === "create" ? createDailyLogRecord : updateDailyLogRecord; async function handleSubmit(formData: FormData) { setIsSubmitting(true); try { await action(formData); setOpen(false); toast.success(mode === "create" ? t("journal.form.messages.createSuccess") : t("journal.form.messages.updateSuccess")); } catch (error) { toast.error(resolveTranslatedError(t, error, "journal.form.messages.error")); } finally { setIsSubmitting(false); } } return (
{log ? : null} {mode === "create" ? t("journal.form.createTitle") : t("journal.form.editTitle")} {t("journal.form.description")}
); } function DailyLogFormFields({ log, localization }: { log?: DailyLogItem; localization: JournalClientProps["localization"] }) { const t = useTranslations(); const [moodScore, setMoodScore] = useState(log?.mood_score || 3); const [energyScore, setEnergyScore] = useState(log?.energy_score || 3); const [satisfactionScore, setSatisfactionScore] = useState(log?.work_satisfaction_score || 3); return (
({ ...f, label: t(`journal.fields.${f.name}`) || f.label, placeholder: "placeholder" in f && typeof f.placeholder === "string" ? t(`journal.placeholders.${f.name}`) || f.placeholder : undefined, }))} values={log?.translations} fallbackValues={{ moodLabel: log?.mood_label, note: log?.note, }} />
); } function ScorePicker({ name, label, value, onChange, }: { name: string; label: string; value: number; onChange: (value: number) => void; }) { const t = useTranslations(); const labels = scoreLabels(t); return (
{labels[value as keyof typeof labels]}
{[1, 2, 3, 4, 5].map((score) => ( ))}
); } function ScoreBadge({ score, tone }: { score: number; tone: "primary" | "green" }) { const t = useTranslations(); const labels = scoreLabels(t); const className = tone === "green" ? "border-emerald-200 bg-emerald-50 text-emerald-700" : "border-primary/20 bg-primary/10 text-primary"; return {score}/5 ยท {labels[score as keyof typeof labels]}; } function resolveTranslatedError( t: ReturnType, error: unknown, fallbackKey: string, ) { if (!(error instanceof Error)) return t(fallbackKey); if (/^(journal|api|validation)\./.test(error.message)) return t(error.message); return error.message || t(fallbackKey); } function EmptyState() { const t = useTranslations(); return (

{t("journal.empty.noRecordTitle")}

{t("journal.empty.noRecordDesc")}

); } function calculateSummary(logs: DailyLogItem[]) { const moodAverage = average(logs.map((log) => log.mood_score)); const energyAverage = average(logs.map((log) => log.energy_score)); const satisfactionAverage = average( logs .map((log) => log.work_satisfaction_score) .filter((score): score is number => typeof score === "number"), ); return { moodAverage, energyAverage, satisfactionAverage, length: logs.length }; } function average(values: number[]) { if (values.length === 0) return 0; return values.reduce((sum, value) => sum + value, 0) / values.length; } function formatDate(value: string) { return new Intl.DateTimeFormat(getDocumentIntlLocale(), { day: "2-digit", month: "short", year: "numeric", }).format(new Date(`${value}T00:00:00`)); } function formatShortDate(value: string) { return new Intl.DateTimeFormat(getDocumentIntlLocale(), { day: "2-digit", month: "short", }).format(new Date(`${value}T00:00:00`)); } function formatWeekday(value: string) { return new Intl.DateTimeFormat(getDocumentIntlLocale(), { weekday: "long", }).format(new Date(`${value}T00:00:00`)); }