我們致力於為讀者提供最新的健康資訊、營養建議以及生活方式的改變方案。透過專業的分析和實踐經驗,我們希望每一位讀者都能找到適合自己的健康之道,並在日常生活中實現身心的平衡與和諧。加入我們,一同探索健康生活的無限可能,讓福氣與健康常伴左右。
人生不是擁有得多,而是計較得少
人生的幸福不在於擁有多少,而在於計較得少
這個問題,每個人可能都有不同的看法。對某些人來說,家庭和愛情是最重要的;對其他人來說,可能是事業成就或個人成長。也有人認為健康和幸福是人生中最重要的。
你覺得什麼對你來說是最重要的呢?
對我而言,人生是一個選擇題而不是是非題。每個選擇都會帶來不同的結果和經歷,讓我們學習和成長。重要的是要勇敢面對每個選擇,並從中找到自己的道路。
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>114-2 學習歷程門戶 - 謝老師課程</title>
<!-- 核心資源載入:React, ReactDOM, Babel 與 Tailwind -->
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;700;900&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Noto Sans TC', 'Microsoft JhengHei', 'PingFang TC', 'Heiti TC', sans-serif;
background-color: #f8fafc;
}
.assignment-card { transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
.assignment-card:hover { transform: translateY(-5px); box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1); }
/* 滾動條樣式優化 */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: #f1f5f9; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
</style>
</head>
<body>
<div id="root"></div>
<!-- Firebase SDK 模組化載入 -->
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.1.0/firebase-app.js";
import { getFirestore, collection, doc, setDoc, onSnapshot, serverTimestamp } from "https://www.gstatic.com/firebasejs/11.1.0/firebase-firestore.js";
import { getAuth, signInAnonymously, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.1.0/firebase-auth.js";
// Firebase 配置資訊
const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {
apiKey: "",
authDomain: "YOUR_PROJECT.firebaseapp.com",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_PROJECT.appspot.com",
messagingSenderId: "YOUR_SENDER_ID",
appId: "YOUR_APP_ID"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const appId = typeof __app_id !== 'undefined' ? __app_id : 'career-path-114-2';
// 將 Firebase 掛載至全域視窗
window.fb = {
db, auth, collection, doc, setDoc, onSnapshot, serverTimestamp,
signInAnonymously, onAuthStateChanged, appId
};
</script>
<!-- React 應用程式邏輯 -->
<script type="text/babel">
const { useState, useEffect, useMemo } = React;
// 自定義 SVG 圖示組件 (解決外部庫加載失敗問題)
const Icons = {
User: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
),
Lock: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
),
BookOpen: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
),
MapPin: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
),
Film: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><rect x="2" y="2" width="20" height="20" rx="2.18" ry="2.18"/><line x1="7" y1="2" x2="7" y2="22"/><line x1="17" y1="2" x2="17" y2="22"/><line x1="2" y1="12" x2="22" y2="12"/><line x1="2" y1="7" x2="7" y2="7"/><line x1="2" y1="17" x2="7" y2="17"/><line x1="17" y1="17" x2="22" y2="17"/><line x1="17" y1="7" x2="22" y2="7"/></svg>
),
Rocket: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4.5c1.62-1.63 5-2.5 5-2.5"/><path d="M12 15v5s3.03-.55 4.5-2c1.63-1.62 2.5-5 2.5-5"/></svg>
),
History: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
),
LayoutDashboard: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><rect x="3" y="3" width="7" height="9"/><rect x="14" y="3" width="7" height="5"/><rect x="14" y="11" width="7" height="9"/><rect x="3" y="15" width="7" height="6"/></svg>
),
Send: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><line x1="22" y1="2" x2="11" y2="13"/><polyline points="22 2 15 22 11 13 2 9 22 2"/></svg>
),
LogOut: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
),
AlertCircle: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
),
CheckCircle2: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/><path d="m9 12 2 2 4-4"/></svg>
),
Calendar: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
),
Eye: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
),
EyeOff: ({size=20, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"/><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.52 13.52 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"/><line x1="2" y1="2" x2="22" y2="22"/></svg>
),
Wifi: ({size=12, className=""}) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M5 13a10 10 0 0 1 14 0"/><path d="M8.5 16.5a5 5 0 0 1 7 0"/><circle cx="12" cy="20" r="1"/></svg>
)
};
// 學生名冊
const STUDENT_REGISTRY = {
"D11360058": "career114", "D11360061": "career114", "D11360074": "career114",
"D11360187": "career114", "D11360203": "career114", "D11360216": "career114",
"D11360458": "career114", "D11360487": "career114", "D11360503": "career114",
"D11360545": "career114", "D11360587": "career114", "D13220023": "career114",
"D13220352": "career114", "D09360469": "career114", "D11360403": "career114",
"D11370492": "career114", "ADMIN": "admin123"
};
function App() {
const [user, setUser] = useState(null);
const [studentId, setStudentId] = useState('');
const [password, setPassword] = useState('');
const [isAuthorized, setIsAuthorized] = useState(false);
const [submissions, setSubmissions] = useState([]);
const [loading, setLoading] = useState(true);
const [submittingId, setSubmittingId] = useState(null);
const [activeTab, setActiveTab] = useState('dashboard');
const [errorMsg, setErrorMsg] = useState('');
const [showPassword, setShowPassword] = useState(false);
useEffect(() => {
const initAuth = async () => {
if (window.fb && window.fb.auth) {
try {
await window.fb.signInAnonymously(window.fb.auth);
} catch (err) { console.error("Firebase 連線失敗", err); }
}
};
initAuth();
const unsubscribe = window.fb.onAuthStateChanged(window.fb.auth, setUser);
return () => unsubscribe();
}, []);
useEffect(() => {
if (!user || !isAuthorized || !studentId || !window.fb) return;
const subCol = window.fb.collection(window.fb.db, 'artifacts', window.fb.appId, 'public', 'data', 'submissions');
const unsubscribe = window.fb.onSnapshot(subCol, (snapshot) => {
const data = snapshot.docs
.map(doc => ({ id: doc.id, ...doc.data() }))
.filter(item => item.sid === studentId);
setSubmissions(data);
setLoading(false);
}, (err) => {
console.error("資料監聽失敗", err);
setLoading(false);
});
return () => unsubscribe();
}, [user, isAuthorized, studentId]);
const assignments = [
{ id: 'expert', title: '專家演講心得', type: '平時 (W3,4,7,8)', weight: 20, icon: Icons.BookOpen, total: 4 },
{ id: 'trip', title: '企業參訪', type: '參訪 (W5)', weight: 10, icon: Icons.MapPin, total: 1 },
{ id: 'film', title: '美濃影展分析', type: '期中 (W9)', weight: 30, icon: Icons.Film, total: 1 },
{ id: 'final', title: '職涯行動計畫', type: '期末 (W15)', weight: 40, icon: Icons.Rocket, total: 1 },
];
const handleLogin = (e) => {
e.preventDefault();
const cleanId = studentId.trim().toUpperCase();
if (STUDENT_REGISTRY[cleanId] === password) {
setStudentId(cleanId);
setIsAuthorized(true);
setErrorMsg('');
} else {
setErrorMsg('學號或密碼錯誤。');
}
};
const handleSubmission = async (a) => {
if (!user || !isAuthorized || !window.fb) return;
const count = submissions.filter(s => s.categoryId === a.id).length;
if (count >= a.total) return;
setSubmittingId(a.id);
const docId = `${studentId}_${a.id}_${Date.now()}`;
const docRef = window.fb.doc(window.fb.db, 'artifacts', window.fb.appId, 'public', 'data', 'submissions', docId);
try {
await window.fb.setDoc(docRef, {
sid: studentId,
categoryId: a.id,
title: `${a.title} (${count + 1})`,
submittedAt: window.fb.serverTimestamp(),
status: '已繳交',
feedback: '謝老師閱:收到,學習紀錄已存入雲端。'
});
} catch (err) {
console.error("提交失敗", err);
alert("儲存失敗,請檢查網路連線。");
} finally {
setSubmittingId(null);
}
};
const totalScore = assignments.reduce((acc, a) => {
const count = submissions.filter(s => s.categoryId === a.id).length;
return acc + (Math.min(count / a.total, 1) * a.weight);
}, 0);
if (!isAuthorized) {
return (
<div className="min-h-screen bg-slate-950 flex items-center justify-center p-6">
<div className="bg-white p-10 rounded-[2.5rem] shadow-2xl w-full max-w-md border border-slate-100">
<div className="text-center mb-10">
<div className="w-20 h-20 bg-emerald-600 rounded-2xl mx-auto flex items-center justify-center mb-6 shadow-xl shadow-emerald-100 rotate-3">
<Icons.Lock className="text-white -rotate-3" size={40} />
</div>
<h2 className="text-3xl font-black text-slate-800 tracking-tight">114-2 學習歷程系統</h2>
<p className="text-slate-400 mt-2 font-bold">就業策略與核心職能 | 謝老師</p>
</div>
<form onSubmit={handleLogin} className="space-y-6">
{errorMsg && <p className="text-red-500 text-sm font-bold text-center">{errorMsg}</p>}
<div className="relative group">
<div className="absolute left-4 top-4 text-slate-300 group-focus-within:text-emerald-500 transition-colors">
<Icons.User size={20} />
</div>
<input type="text" placeholder="學號 (如 D11360058)" className="w-full pl-12 pr-4 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:ring-2 focus:ring-emerald-500 outline-none font-bold uppercase" onChange={e => setStudentId(e.target.value)} />
</div>
<div className="relative group">
<div className="absolute left-4 top-4 text-slate-300 group-focus-within:text-emerald-500 transition-colors">
<Icons.Lock size={20} />
</div>
<input type={showPassword ? "text" : "password"} placeholder="密碼" className="w-full pl-12 pr-12 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:ring-2 focus:ring-emerald-500 outline-none font-bold" onChange={e => setPassword(e.target.value)} />
<button type="button" onClick={() => setShowPassword(!showPassword)} className="absolute right-4 top-4 text-slate-300 hover:text-slate-600 transition-colors">
{showPassword ? <Icons.EyeOff size={20} /> : <Icons.Eye size={20} />}
</button>
</div>
<button className="w-full py-5 bg-slate-900 text-white rounded-2xl font-black text-lg hover:bg-emerald-600 transition-all shadow-xl shadow-slate-200 active:scale-95">進入歷程儀表板</button>
</form>
<div className="mt-8 text-center flex items-center justify-center gap-2 text-[10px] font-black text-slate-300 uppercase tracking-widest">
<Icons.Wifi size={12} className={user ? "text-emerald-400" : "animate-pulse"} />
{user ? "雲端伺服器連線正常" : "正在建立連線..."}
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-[#f1f5f9] flex flex-col md:flex-row">
<aside className="w-full md:w-72 bg-slate-900 text-white p-8 flex flex-col shadow-xl">
<div className="mb-12 flex items-center gap-3">
<Icons.Calendar className="text-emerald-500" size={24} />
<h3 className="text-xl font-black tracking-tighter uppercase">Career Path</h3>
</div>
<nav className="flex-1 space-y-3">
<button onClick={() => setActiveTab('dashboard')} className={`w-full flex items-center gap-4 px-5 py-4 rounded-2xl font-black transition-all ${activeTab === 'dashboard' ? 'bg-emerald-600 shadow-lg shadow-emerald-900/40 text-white' : 'text-slate-400 hover:bg-white/5'}`}>
<Icons.LayoutDashboard size={20} /> 儀表板總覽
</button>
<button onClick={() => setActiveTab('history')} className={`w-full flex items-center gap-4 px-5 py-4 rounded-2xl font-black transition-all ${activeTab === 'history' ? 'bg-emerald-600 shadow-lg shadow-emerald-900/40 text-white' : 'text-slate-400 hover:bg-white/5'}`}>
<Icons.History size={20} /> 詳細紀錄庫
</button>
</nav>
<div className="mt-auto pt-8 border-t border-white/10">
<div className="bg-white/5 p-4 rounded-2xl mb-4 border border-white/5 overflow-hidden">
<p className="text-xs font-black text-slate-500 uppercase tracking-widest mb-1">已登入學員</p>
<p className="font-black text-emerald-400 truncate">{studentId}</p>
</div>
<button onClick={() => window.location.reload()} className="w-full text-slate-500 hover:text-white font-bold text-sm transition-colors py-2 flex items-center justify-center gap-2">
<Icons.LogOut size={16} /> 安全登出
</button>
</div>
</aside>
<main className="flex-1 p-6 md:p-12 overflow-y-auto">
<header className="flex flex-col md:flex-row justify-between items-center mb-12 gap-6">
<h1 className="text-4xl font-black text-slate-800 tracking-tight">
{activeTab === 'dashboard' ? '全面記錄學習成就' : '歷史繳交清單'}
</h1>
<div className="flex items-center gap-3 bg-emerald-50 text-emerald-700 px-6 py-3 rounded-2xl border border-emerald-100 font-black text-sm shadow-sm">
<Icons.CheckCircle2 size={18} /> 證據導向:記錄職能成長
</div>
</header>
{activeTab === 'dashboard' ? (
<div className="space-y-12">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{assignments.map(a => {
const count = submissions.filter(s => s.categoryId === a.id).length;
const progress = Math.min((count / a.total) * 100, 100);
const Icon = a.icon;
return (
<div key={a.id} className="assignment-card bg-white p-8 rounded-[2.5rem] shadow-sm border border-slate-100">
<div className="flex justify-between items-start mb-6">
<div className={`p-4 rounded-2xl bg-slate-50 text-slate-900 shadow-sm`}><Icon size={24} /></div>
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest bg-slate-50 px-2 py-1 rounded-lg">{a.type}</span>
</div>
<h4 className="text-xl font-black text-slate-800 mb-1">{a.title}</h4>
<p className="text-xs font-bold text-slate-400 mb-6">學期權重 {a.weight}%</p>
<div className="space-y-2">
<div className="flex justify-between text-xs font-black">
<span className="text-slate-400">目前進度</span>
<span className="text-slate-800">{Math.round(progress)}%</span>
</div>
<div className="w-full h-3 bg-slate-100 rounded-full overflow-hidden">
<div className="bg-emerald-500 h-full transition-all duration-1000" style={{ width: `${progress}%` }}></div>
</div>
</div>
</div>
);
})}
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
<div className="lg:col-span-2 bg-white p-10 rounded-[3rem] shadow-sm border border-slate-100">
<h3 className="text-2xl font-black text-slate-800 flex items-center gap-4 mb-10">
<Icons.Send size={28} className="text-emerald-500" /> 快速提交任務
</h3>
<div className="space-y-6">
{assignments.map(a => {
const Icon = a.icon;
const currentCount = submissions.filter(s => s.categoryId === a.id).length;
const isComplete = currentCount >= a.total;
return (
<div key={a.id} className="group flex items-center justify-between p-6 bg-slate-50 rounded-3xl border border-transparent hover:border-emerald-200 transition-all">
<div className="flex items-center gap-6">
<div className="p-4 bg-white rounded-2xl shadow-sm text-slate-400 group-hover:bg-slate-900 group-hover:text-white transition-all"><Icon size={24} /></div>
<div>
<p className="font-black text-slate-800 text-lg">{a.title}</p>
<p className="text-xs font-bold text-slate-400">累計繳交: {currentCount} / {a.total}</p>
</div>
</div>
<button
onClick={() => handleSubmission(a)}
disabled={isComplete || submittingId === a.id}
className={`px-8 py-4 rounded-2xl font-black text-sm flex items-center gap-2 transition-all ${isComplete ? 'bg-emerald-50 text-emerald-400 cursor-not-allowed border border-emerald-100' : 'bg-emerald-600 text-white hover:bg-emerald-700 active:scale-95 shadow-lg shadow-emerald-100'}`}
>
{submittingId === a.id ? <div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"></div> : null}
{isComplete ? <><Icons.CheckCircle2 size={16}/> 已完成</> : '立即繳交'}
</button>
</div>
);
})}
</div>
</div>
<div className="bg-slate-900 text-white p-10 rounded-[3.5rem] shadow-2xl flex flex-col justify-center text-center relative overflow-hidden">
<div className="absolute top-0 right-0 p-10 opacity-5 pointer-events-none"><Icons.Rocket size={150} /></div>
<p className="text-slate-500 font-black uppercase tracking-[0.3em] text-xs mb-6">預估目前總積分</p>
<h2 className="text-[10rem] font-black text-emerald-400 leading-none drop-shadow-2xl">{Math.round(totalScore)}</h2>
<p className="text-slate-400 font-bold mt-6">依據已繳交報告權重累計</p>
<div className="mt-12 p-6 bg-white/5 rounded-3xl border border-white/10 text-left">
<div className="flex gap-4">
<Icons.AlertCircle className="text-amber-400 flex-shrink-0 mt-1" size={20} />
<div className="text-xs text-slate-400 leading-relaxed font-medium">
<p className="font-black text-white mb-1">謝老師提醒</p>
W15 成果展為最終檢核,請務必於期限內將所有作品與證據完成上傳。
</div>
</div>
</div>
</div>
</div>
</div>
) : (
<div className="bg-white rounded-[3rem] shadow-sm border border-slate-100 overflow-hidden">
<div className="p-10 border-b border-slate-100 flex justify-between items-center bg-slate-50/50">
<h3 className="text-2xl font-black text-slate-800 flex items-center gap-4"><Icons.History className="text-emerald-500" size={24} /> 職能成長紀錄庫</h3>
</div>
<div className="overflow-x-auto">
<table className="w-full text-left">
<thead>
<tr className="bg-slate-50 text-slate-400 text-[10px] font-black uppercase tracking-widest border-b">
<th className="px-10 py-6">作業項目</th>
<th className="px-10 py-6">提交時間</th>
<th className="px-10 py-6 text-center">狀態</th>
<th className="px-10 py-6">評核回饋</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{submissions.length === 0 ? (
<tr><td colSpan="4" className="p-32 text-center text-slate-300 font-black italic text-xl">尚無任何歷史紀錄</td></tr>
) : (
submissions.sort((a,b) => (b.submittedAt?.seconds || 0) - (a.submittedAt?.seconds || 0)).map(s => (
<tr key={s.id} className="hover:bg-blue-50/30 transition-colors group">
<td className="px-10 py-8">
<div className="flex items-center gap-5">
<div className="w-12 h-12 bg-white border border-slate-100 text-emerald-500 rounded-2xl flex items-center justify-center shadow-sm group-hover:scale-110 transition-transform"><Icons.CheckCircle2 size={24} /></div>
<span className="font-black text-slate-800 text-lg">{s.title}</span>
</div>
</td>
<td className="px-10 py-8 text-slate-400 text-xs font-mono">{s.submittedAt ? new Date(s.submittedAt.seconds * 1000).toLocaleString() : '處理中...'}</td>
<td className="px-10 py-8 text-center"><span className="bg-emerald-100 text-emerald-700 px-4 py-2 rounded-xl text-[10px] font-black uppercase border border-emerald-200">已存檔</span></td>
<td className="px-10 py-8"><div className="text-slate-500 text-sm italic bg-white p-4 rounded-2xl border border-slate-100 shadow-sm leading-relaxed">“ {s.feedback} ”</div></td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
)}
</main>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>