refactor(frontend): 重构站点数据面板与签到组件
将原有的站点统计卡片重构为bento布局样式,把签到组件内嵌到站点数据面板中,统一UI风格并简化代码结构
This commit is contained in:
@@ -6,40 +6,16 @@ import { CalendarDays, CalendarCheck, LogIn, Loader2 } from "lucide-react";
|
||||
import { apiDoCheckin, type CheckinStatus } from "@/lib/api";
|
||||
|
||||
/**
|
||||
* 每日签到卡(右栏)。
|
||||
* 每日签到紧凑条,内嵌在右栏「站点数据」卡底部。
|
||||
* initial 为 undefined 表示游客;否则为 SSR 注入的登录用户签到状态。
|
||||
*/
|
||||
export default function CheckinCard({ initial }: { initial?: CheckinStatus | null }) {
|
||||
export function PulseCheckin({ initial }: { initial?: CheckinStatus | null }) {
|
||||
const [status, setStatus] = useState<CheckinStatus | null>(initial ?? null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
// 游客态
|
||||
if (initial === undefined) {
|
||||
return (
|
||||
<section className="j13-module" aria-label="每日签到">
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className="w-10 h-10 rounded-xl flex items-center justify-center shrink-0"
|
||||
style={{ background: "var(--accent-soft)", color: "var(--accent)" }}
|
||||
>
|
||||
<CalendarDays size={19} />
|
||||
</span>
|
||||
<div className="min-w-0">
|
||||
<p className="text-[14px] font-bold" style={{ color: "var(--ink)" }}>
|
||||
每日签到
|
||||
</p>
|
||||
<p className="meta text-[12px] mt-0.5">登录后每日签到领积分</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/login?redirect=/" className="btn btn-line btn-sm w-full justify-center mt-3.5">
|
||||
<LogIn size={14} /> 登录后签到
|
||||
</Link>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const checked = status?.checked_today ?? false;
|
||||
const guest = initial === undefined;
|
||||
|
||||
async function doCheckin() {
|
||||
if (loading || checked) return;
|
||||
@@ -55,59 +31,65 @@ export default function CheckinCard({ initial }: { initial?: CheckinStatus | nul
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="j13-module" aria-label="每日签到">
|
||||
<div className="flex items-center gap-3">
|
||||
<div aria-label="每日签到">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<span
|
||||
className="w-10 h-10 rounded-xl flex items-center justify-center shrink-0"
|
||||
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg"
|
||||
style={{
|
||||
background: checked ? "var(--accent-soft)" : "var(--panel-2)",
|
||||
color: checked ? "var(--accent)" : "var(--ink-2)",
|
||||
background: checked || guest ? "var(--accent-soft)" : "var(--tint)",
|
||||
color: checked || guest ? "var(--accent)" : "var(--ink-2)",
|
||||
}}
|
||||
>
|
||||
{checked ? <CalendarCheck size={19} /> : <CalendarDays size={19} />}
|
||||
{checked ? <CalendarCheck size={16} /> : <CalendarDays size={16} />}
|
||||
</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-[14px] font-bold" style={{ color: "var(--ink)" }}>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-[12.5px] font-bold leading-tight" style={{ color: "var(--ink)" }}>
|
||||
每日签到
|
||||
</p>
|
||||
<p className="meta text-[12px] mt-0.5">
|
||||
{checked
|
||||
? `已连续签到 ${status?.streak ?? 0} 天 · 累计 ${status?.total_points ?? 0} 积分`
|
||||
: `连续 ${status?.streak ?? 0} 天 · 今日可得 ${status?.today_points ?? 5} 积分`}
|
||||
<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}`}
|
||||
</p>
|
||||
</div>
|
||||
{!checked && (
|
||||
<span className="text-[17px] font-extrabold tabular-nums shrink-0" style={{ color: "var(--accent)" }}>
|
||||
+{status?.today_points ?? 5}
|
||||
{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>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={doCheckin}
|
||||
disabled={loading || checked}
|
||||
aria-disabled={loading || checked}
|
||||
className={`btn w-full justify-center mt-3.5 ${checked ? "btn-line" : "btn-primary"}`}
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<Loader2 size={15} className="animate-spin" /> 签到中…
|
||||
</>
|
||||
) : checked ? (
|
||||
<>
|
||||
<CalendarCheck size={15} /> 今日已签到
|
||||
</>
|
||||
) : (
|
||||
"立即签到"
|
||||
)}
|
||||
</button>
|
||||
|
||||
{error && (
|
||||
<p className="text-[12px] mt-2" style={{ color: "var(--clay)" }} role="alert">
|
||||
<p className="mt-1.5 text-[11.5px]" style={{ color: "var(--clay)" }} role="alert">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user