165 lines
5.6 KiB
TypeScript
165 lines
5.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
|
|
const WECHAT_STORAGE_KEY = "salon_join_wechat";
|
|
|
|
interface RecordData {
|
|
id?: string;
|
|
wechatId?: string;
|
|
nickname?: string;
|
|
media?: string;
|
|
is_volunteer?: boolean;
|
|
}
|
|
|
|
interface UserRecord {
|
|
hasRecord: boolean;
|
|
record: RecordData | null;
|
|
isComplete: boolean;
|
|
isApproved: boolean;
|
|
isRejected: boolean;
|
|
vip: boolean;
|
|
}
|
|
|
|
function getStatusLabel(data: UserRecord): string {
|
|
if (data.isComplete || (data.vip && data.hasRecord)) return "报名成功";
|
|
if (data.isRejected) return "审核拒绝";
|
|
if (data.isApproved) return "审核通过";
|
|
return "审核中";
|
|
}
|
|
|
|
function getStatusColor(status: string): string {
|
|
if (status === "报名成功") return "bg-emerald-50 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300";
|
|
if (status === "审核通过") return "bg-emerald-50 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400";
|
|
if (status === "审核拒绝") return "bg-red-50 dark:bg-red-900/30 text-red-600 dark:text-red-400";
|
|
return "bg-amber-50 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400";
|
|
}
|
|
|
|
export default function MyRegistrationStatus() {
|
|
const [data, setData] = useState<UserRecord | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
const fetchStatus = () => {
|
|
if (typeof window === "undefined") return;
|
|
try {
|
|
const stored = localStorage.getItem(WECHAT_STORAGE_KEY);
|
|
const wechatId = (stored || "").trim();
|
|
if (!wechatId) {
|
|
setLoading(false);
|
|
setData(null);
|
|
return;
|
|
}
|
|
setLoading(true);
|
|
fetch(`/api/join/by-wechat?wechat_id=${encodeURIComponent(wechatId)}&_=${Date.now()}`, { cache: "no-store" })
|
|
.then((r) => r.json())
|
|
.then((d) => {
|
|
if (d?.hasRecord) {
|
|
const rec = d.record ?? null;
|
|
setData({
|
|
hasRecord: !!d.hasRecord,
|
|
record: rec ? { ...rec, wechatId: rec.wechatId || wechatId } : null,
|
|
isComplete: !!d.isComplete,
|
|
isApproved: !!d.isApproved,
|
|
isRejected: !!d.isRejected,
|
|
vip: !!d.vip,
|
|
});
|
|
} else {
|
|
setData(null);
|
|
}
|
|
})
|
|
.catch(() => setData(null))
|
|
.finally(() => setLoading(false));
|
|
} catch {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (typeof window === "undefined") return;
|
|
let cancelled = false;
|
|
fetchStatus();
|
|
const onUpdate = () => {
|
|
if (!cancelled) fetchStatus();
|
|
};
|
|
const onVisibilityChange = () => {
|
|
if (document.visibilityState === "visible" && !cancelled) fetchStatus();
|
|
};
|
|
const onPageShow = (e: PageTransitionEvent) => {
|
|
if (e.persisted && !cancelled) fetchStatus();
|
|
};
|
|
window.addEventListener("vip:updated", onUpdate);
|
|
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
window.addEventListener("pageshow", onPageShow);
|
|
return () => {
|
|
cancelled = true;
|
|
window.removeEventListener("vip:updated", onUpdate);
|
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
window.removeEventListener("pageshow", onPageShow);
|
|
};
|
|
}, []);
|
|
|
|
if (loading || !data?.hasRecord || !data.record) return null;
|
|
|
|
const rec = data.record;
|
|
const status = getStatusLabel(data);
|
|
const statusColor = getStatusColor(status);
|
|
|
|
return (
|
|
<section className="mb-8 sm:mb-10">
|
|
<div className="rounded-xl sm:rounded-2xl bg-white dark:bg-stone-900/80 border border-stone-200/80 dark:border-stone-700/80 p-4 sm:p-6 overflow-hidden">
|
|
<h3 className="text-sm font-medium text-stone-500 dark:text-stone-400 mb-3 flex items-center gap-2">
|
|
<span>👤</span> 我的报名
|
|
</h3>
|
|
<div className="flex items-center gap-3 sm:gap-4">
|
|
<div className="relative shrink-0">
|
|
<div className="relative w-12 h-12 sm:w-14 sm:h-14 rounded-full overflow-hidden border-2 border-stone-200 dark:border-stone-600">
|
|
{rec.media ? (
|
|
<Image
|
|
src={rec.media}
|
|
alt=""
|
|
fill
|
|
sizes="56px"
|
|
className="object-cover"
|
|
unoptimized
|
|
/>
|
|
) : (
|
|
<div className="w-full h-full flex items-center justify-center bg-amber-100 dark:bg-amber-900/40 text-xl sm:text-2xl">
|
|
👤
|
|
</div>
|
|
)}
|
|
</div>
|
|
{rec.is_volunteer && (
|
|
<span className="absolute -bottom-1 left-1/2 -translate-x-1/2 px-1.5 py-0.5 rounded text-[10px] font-medium bg-amber-500 text-white whitespace-nowrap">
|
|
志愿者
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="font-semibold text-stone-800 dark:text-stone-100 truncate">
|
|
{rec.nickname || "—"}
|
|
</p>
|
|
{rec.wechatId && (
|
|
<p className="text-xs text-stone-500 dark:text-stone-400 font-mono truncate mt-0.5">
|
|
微信号: {rec.wechatId}
|
|
</p>
|
|
)}
|
|
<span
|
|
className={`inline-block mt-2 px-2.5 py-1 rounded-lg text-xs font-medium ${statusColor}`}
|
|
>
|
|
{status}
|
|
</span>
|
|
</div>
|
|
<Link
|
|
href="/join"
|
|
className="shrink-0 text-sm font-medium text-amber-600 dark:text-amber-400 hover:text-amber-700 dark:hover:text-amber-300"
|
|
>
|
|
查看详情 →
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|