"use client";
import { useCallback, useEffect, useMemo, useState } from "react";
import { Link, useLocale, useTranslation } from "@/app/digital/i18n/navigation";
import AuthModal from "@/app/digital/components/AuthModal";
import DigitalThemeIcon, { type IconName } from "@/app/digital/components/DigitalThemeIcon";
import { getStoredToken, getStoredUser, pbLogout, pbSaveAuth } from "@/app/digital/lib/pocketbase";
import { useCrossSiteUrls } from "@/app/digital/lib/useCrossSiteUrls";
type ProfileUser = {
id: string;
email: string;
};
type AccountState = {
user: ProfileUser | null;
vip: boolean;
expiresAt: string | null;
};
type ActionItem = {
icon: IconName;
emoji: string;
title: string;
description: string;
href: string;
tag: string;
external?: boolean;
};
function getAvatarLetter(email: string): string {
const local = email.split("@")[0];
return (local || "?").slice(0, 1).toUpperCase();
}
function formatDate(value: string, locale: string): string {
const date = new Date(value);
if (Number.isNaN(date.getTime())) return value;
return new Intl.DateTimeFormat(locale === "zh" ? "zh-CN" : "en-US", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}).format(date);
}
function ActionCard({ item }: { item: ActionItem }) {
const content = (
<>
{item.title}
{item.tag}
{item.description}
>
);
const className =
"dn-action-card group flex min-h-[150px] items-start gap-4 rounded-2xl border border-slate-200 bg-white p-5 text-left shadow-sm transition-all hover:border-sky-300 hover:shadow-md dark:border-slate-700 dark:bg-slate-900 dark:hover:border-sky-600";
if (item.external) {
return (
{content}
);
}
return (
{content}
);
}
export default function ProfileContent() {
const locale = useLocale();
const { t } = useTranslation("profile");
const { vipUrl } = useCrossSiteUrls();
const [account, setAccount] = useState({
user: null,
vip: false,
expiresAt: null,
});
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [authOpen, setAuthOpen] = useState(false);
const refreshAccount = useCallback(async (manual = false) => {
if (manual) setRefreshing(true);
else setLoading(true);
try {
const meRes = await fetch("/api/digital/auth/me", {
credentials: "include",
cache: "no-store",
});
const meData = await meRes.json().catch(() => null);
let user = meData?.user?.id && meData?.user?.email
? { id: meData.user.id as string, email: meData.user.email as string }
: null;
if (!user) {
const storedUser = getStoredUser();
const storedToken = getStoredToken();
if (storedUser && storedToken) {
user = { id: storedUser.id, email: storedUser.email };
await fetch("/api/digital/auth/sync-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token: storedToken, record: storedUser }),
credentials: "include",
}).catch(() => null);
} else {
setAccount({ user: null, vip: false, expiresAt: null });
return;
}
}
if (meData?.token) {
pbSaveAuth(meData.token, user);
}
const vipRes = await fetch(`/api/digital/vip/check?email=${encodeURIComponent(user.email)}`, {
credentials: "include",
cache: "no-store",
});
const vipData = await vipRes.json().catch(() => null);
setAccount({
user,
vip: vipData?.vip === true,
expiresAt: typeof vipData?.expires_at === "string" ? vipData.expires_at : null,
});
} catch {
const storedUser = getStoredUser();
setAccount({
user: storedUser ? { id: storedUser.id, email: storedUser.email } : null,
vip: false,
expiresAt: null,
});
} finally {
setLoading(false);
setRefreshing(false);
}
}, []);
useEffect(() => {
refreshAccount();
}, [refreshAccount]);
const handleLogout = async () => {
await pbLogout();
setAccount({ user: null, vip: false, expiresAt: null });
window.dispatchEvent(new CustomEvent("auth:updated"));
};
const primaryActions = useMemo(() => [
{
icon: "globe",
emoji: "🌐",
title: t("actions.community.title"),
description: t("actions.community.description"),
href: vipUrl,
tag: account.vip ? t("tags.active") : t("tags.apply"),
external: true,
},
{
icon: "course",
emoji: "🎓",
title: t("actions.course.title"),
description: t("actions.course.description"),
href: "/course",
tag: account.vip ? t("tags.unlocked") : t("tags.trial"),
},
{
icon: "book",
emoji: "📚",
title: t("actions.ebook.title"),
description: t("actions.ebook.description"),
href: "/ebook",
tag: t("tags.ready"),
},
{
icon: "phone",
emoji: "📱",
title: t("actions.app.title"),
description: t("actions.app.description"),
href: "/app",
tag: t("tags.download"),
},
], [account.vip, t, vipUrl]);
const workflowActions = useMemo(() => [
{
icon: "compass",
emoji: "🧭",
title: t("workflow.roadmap.title"),
description: t("workflow.roadmap.description"),
href: "/#roadmap",
tag: t("tags.start"),
},
{
icon: "tool",
emoji: "🛠️",
title: t("workflow.tools.title"),
description: t("workflow.tools.description"),
href: "/#tools",
tag: t("tags.open"),
},
{
icon: "file",
emoji: "📄",
title: t("workflow.resources.title"),
description: t("workflow.resources.description"),
href: "/#resources",
tag: t("tags.curated"),
},
{
icon: "check",
emoji: "✅",
title: t("workflow.privacy.title"),
description: t("workflow.privacy.description"),
href: "/privacy",
tag: t("tags.policy"),
},
], [t]);
if (loading) {
return (
);
}
if (!account.user) {
return (
{t("signedOutBadge")}
{t("signedOutTitle")}
{t("signedOutDescription")}
{t("homeCta")}
setAuthOpen(false)}
onSuccess={() => refreshAccount(true)}
/>
);
}
const expiryText = account.vip
? account.expiresAt
? formatDate(account.expiresAt, locale)
: t("lifetime")
: t("notVip");
return (
{t("eyebrow")}
{t("title")}
{t("description")}
{t("loggedIn")}
{account.vip ? t("vipActive") : t("vipInactive")}
{t("expiry")}: {expiryText}
{t("accessEyebrow")}
{t("accessTitle")}
{primaryActions.map((item) => (
))}
{t("workflowEyebrow")}
{t("workflowTitle")}
{workflowActions.map((item) => (
))}
);
}