Files
jiang13-bbs/frontend/components/home/CheckinCard.tsx
freefire 5507552ca5 polish(frontend): 侧栏与徽章可读性,智能悬停提示
降权全站字重;优化右栏公告/数据块;类型状态徽章用固定浅色区分;TipHost 仅在文案不一致、非文字或省略时显示。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 03:54:14 +08:00

104 lines
3.3 KiB
TypeScript

"use client";
import { useState } from "react";
import Link from "next/link";
import { CalendarDays, CalendarCheck, LogIn, Loader2 } from "lucide-react";
import { apiDoCheckin, type CheckinStatus } from "@/lib/api";
/**
* 每日签到紧凑条,内嵌在右栏「站点数据」卡底部。
* initial 为 undefined 表示游客;否则为 SSR 注入的登录用户签到状态。
*/
export function PulseCheckin({ initial }: { initial?: CheckinStatus | null }) {
const [status, setStatus] = useState<CheckinStatus | null>(initial ?? null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const checked = status?.checked_today ?? false;
const guest = initial === undefined;
async function doCheckin() {
if (loading || checked) return;
setLoading(true);
setError("");
try {
setStatus(await apiDoCheckin());
} catch (e) {
setError(e instanceof Error ? e.message : "签到失败");
} finally {
setLoading(false);
}
}
return (
<div aria-label="每日签到">
<div className="flex items-center gap-2.5">
<span
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg"
style={{
background: checked || guest ? "var(--accent-soft)" : "var(--tint)",
color: checked || guest ? "var(--accent)" : "var(--ink-2)",
}}
>
{checked ? <CalendarCheck size={16} /> : <CalendarDays size={16} />}
</span>
<div className="min-w-0 flex-1">
<p className="text-[12.5px] leading-tight" style={{ color: "var(--ink)" }}>
每日签到
</p>
<p className="meta mt-0.5 truncate text-[11px]">
{guest
? "登录后每日签到领积分"
: checked
? `连签 ${status?.streak ?? 0} 天 · 余额 ${status?.total_points ?? 0}`
: `连签 ${status?.streak ?? 0} 天 · 今日 +${status?.today_points ?? 5}`}
{!guest && (
<>
{" · "}
<Link href="/points" className="hover:text-[var(--accent)] transition-colors">
流水
</Link>
</>
)}
</p>
</div>
{guest ? (
<Link
href="/login?redirect=/"
className="btn btn-line btn-sm j13-pulse-checkin-btn shrink-0"
>
<LogIn size={13} /> 登录
</Link>
) : checked ? (
<span
className="j13-pulse-checkin-done shrink-0"
style={{ background: "var(--accent-soft)", color: "var(--accent)" }}
>
<CalendarCheck size={12.5} /> 已签
</span>
) : (
<button
type="button"
onClick={doCheckin}
disabled={loading}
className="btn btn-primary btn-sm j13-pulse-checkin-btn shrink-0"
>
{loading ? (
<>
<Loader2 size={13} className="animate-spin" /> 签到中
</>
) : (
`签到 +${status?.today_points ?? 5}`
)}
</button>
)}
</div>
{error && (
<p className="mt-1.5 text-[11.5px]" style={{ color: "var(--clay)" }} role="alert">
{error}
</p>
)}
</div>
);
}