feat(theme): complete Minecraft overhaul for all pages including admin/utility
这个提交包含在:
@@ -1,6 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
ArrowRightLeft,
|
||||
Calendar,
|
||||
CheckCircle2,
|
||||
History,
|
||||
IdCard,
|
||||
RefreshCw,
|
||||
ShoppingBag,
|
||||
TrendingUp,
|
||||
TrendingDown,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
|
||||
import { PixelAvatar } from "@/components/pixel-avatar";
|
||||
import { apiFetch, listRatingHistory, type RatingHistoryItem } from "@/lib/api";
|
||||
@@ -243,17 +255,19 @@ export default function MePage() {
|
||||
Level {Math.floor(profile.rating / 100)}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-[color:var(--mc-stone-dark)]">UID: {profile.id}</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 space-y-2 border-t border-black/20 pt-4">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-zinc-800">{tx("绿宝石 (Rating)", "Emeralds (Rating)")}</span>
|
||||
<span className="font-bold text-[color:var(--mc-green)] text-shadow-sm">{profile.rating}</span>
|
||||
<div className="flex justify-between text-sm items-center">
|
||||
<span className="text-zinc-800 flex items-center gap-1">
|
||||
<IdCard size={14} className="text-zinc-500" />
|
||||
UID
|
||||
</span>
|
||||
<span className="text-zinc-600 font-mono">{profile.id}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-zinc-800">{tx("加入时间", "Joined")}</span>
|
||||
<span className="text-zinc-600">{new Date(profile.created_at * 1000).toLocaleDateString()}</span>
|
||||
<div className="flex justify-between text-sm items-center">
|
||||
<span className="text-zinc-800 flex items-center gap-1">
|
||||
<Calendar size={14} className="text-zinc-500" />
|
||||
{tx("加入时间", "Joined")}
|
||||
</span>
|
||||
<span className="text-zinc-600 font-mono">{new Date(profile.created_at * 1000).toLocaleDateString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -271,13 +285,16 @@ export default function MePage() {
|
||||
<div key={idx} className="bg-[color:var(--mc-surface-soft)] p-3 border-2 border-[color:var(--mc-stone-dark)] relative group hover:border-[color:var(--mc-stone)] transition-colors">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={`w-5 h-5 border-2 border-[color:var(--mc-stone-dark)] flex items-center justify-center bg-black/30 mt-0.5 ${task.completed ? 'bg-[color:var(--mc-green)]/20' : ''}`}>
|
||||
{task.completed && <span className="text-[color:var(--mc-green)] text-sm">✓</span>}
|
||||
{task.completed && <CheckCircle2 size={16} className="text-[color:var(--mc-green)]" />}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex justify-between items-start mb-1">
|
||||
<h3 className="text-[color:var(--mc-plank-light)] text-lg font-bold leading-tight">
|
||||
<h3 className="text-[color:var(--mc-plank-light)] text-lg font-bold leading-tight flex items-center gap-2">
|
||||
{task.title}
|
||||
<span className="ml-2 text-[color:var(--mc-gold)] text-base font-minecraft">+{task.reward} XP</span>
|
||||
<span className="ml-2 text-[color:var(--mc-gold)] text-base font-minecraft flex items-center gap-1">
|
||||
<Zap size={14} />
|
||||
+{task.reward} XP
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-[color:var(--mc-stone)] text-base leading-snug">
|
||||
@@ -299,18 +316,21 @@ export default function MePage() {
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="flex gap-2 text-black">
|
||||
<select
|
||||
className="flex-1 rounded-none border-2 border-black bg-[color:var(--surface)] px-2 py-1 text-base font-bold"
|
||||
value={selectedItemId}
|
||||
onChange={(e) => setSelectedItemId(Number(e.target.value))}
|
||||
>
|
||||
<option value={0}>{tx("选择战利品...", "Select loot...")}</option>
|
||||
{items.map((item) => (
|
||||
<option key={item.id} value={item.id}>
|
||||
{itemName(item.name)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="relative flex-1">
|
||||
<ShoppingBag className="absolute left-2 top-2 text-black/50 pointer-events-none" size={16} />
|
||||
<select
|
||||
className="w-full rounded-none border-2 border-black bg-[color:var(--surface)] px-2 py-1 pl-8 text-base font-bold appearance-none"
|
||||
value={selectedItemId}
|
||||
onChange={(e) => setSelectedItemId(Number(e.target.value))}
|
||||
>
|
||||
<option value={0}>{tx("选择战利品...", "Select loot...")}</option>
|
||||
{items.map((item) => (
|
||||
<option key={item.id} value={item.id}>
|
||||
{itemName(item.name)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<input
|
||||
className="w-20 rounded-none border-2 border-black bg-[color:var(--surface)] px-2 py-1 text-base font-bold text-center"
|
||||
type="number"
|
||||
@@ -340,10 +360,11 @@ export default function MePage() {
|
||||
<option value="studyday">{tx("工作日价格", "Workday Price")}</option>
|
||||
</select>
|
||||
<button
|
||||
className="mc-btn mc-btn-success text-xs px-4"
|
||||
className="mc-btn mc-btn-success text-xs px-4 flex items-center gap-2"
|
||||
onClick={() => void redeem()}
|
||||
disabled={redeemLoading || !selectedItemId}
|
||||
>
|
||||
<ArrowRightLeft size={14} />
|
||||
{tx("交易", "Trade")}
|
||||
</button>
|
||||
</div>
|
||||
@@ -355,12 +376,16 @@ export default function MePage() {
|
||||
|
||||
{/* Rating History Section */}
|
||||
<section className="mt-4 rounded-none border-[3px] border-black bg-[color:var(--mc-surface)] p-4 shadow-[4px_4px_0_rgba(0,0,0,0.5)]">
|
||||
<h2 className="text-base font-bold text-black mb-2">{tx("积分变动记录", "Rating History")}</h2>
|
||||
<h2 className="text-base font-bold text-black mb-2 flex items-center gap-2">
|
||||
<History size={18} />
|
||||
{tx("积分变动记录", "Rating History")}
|
||||
</h2>
|
||||
<div className="max-h-60 overflow-y-auto space-y-1">
|
||||
{historyItems.map((item, idx) => (
|
||||
<div key={idx} className="flex justify-between text-xs text-zinc-800 border-b border-zinc-200 pb-1">
|
||||
<span>
|
||||
<span className={`font-bold ${item.change > 0 ? 'text-[color:var(--mc-green)]' : 'text-[color:var(--mc-red)]'}`}>
|
||||
<span className={`font-bold flex items-center gap-1 ${item.change > 0 ? 'text-[color:var(--mc-green)]' : 'text-[color:var(--mc-red)]'}`}>
|
||||
{item.change > 0 ? <TrendingUp size={14} /> : <TrendingDown size={14} />}
|
||||
{item.change > 0 ? `+${item.change}` : item.change}
|
||||
</span>
|
||||
<span className="ml-2">{item.note}</span>
|
||||
@@ -381,10 +406,11 @@ export default function MePage() {
|
||||
<div className="flex items-center justify-between gap-2 mb-2">
|
||||
<h2 className="text-base font-bold text-black">{tx("交易记录", "Trade History")}</h2>
|
||||
<button
|
||||
className="text-xs text-[color:var(--mc-stone-dark)] underline"
|
||||
className="text-xs text-[color:var(--mc-stone-dark)] underline flex items-center gap-1 hover:text-black"
|
||||
onClick={() => void loadAll()}
|
||||
disabled={loading}
|
||||
>
|
||||
<RefreshCw size={12} className={loading ? "animate-spin" : ""} />
|
||||
{tx("刷新", "Refresh")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
在新工单中引用
屏蔽一个用户