diff --git a/src/App.css b/src/App.css index af95038..376bd76 100644 --- a/src/App.css +++ b/src/App.css @@ -779,3 +779,101 @@ footer { transition-duration: 0.01ms !important; } } + +/* --------------------------------------------------------------------------- + Table cells for the 2016-only identity columns. + Ported from the 2016 frontend; hidden automatically on the 2017 datasets + because every row there is NULL. + --------------------------------------------------------------------------- */ + +.sbd-cell { + font-family: ui-monospace, monospace; + font-size: 0.85rem; + color: var(--text-muted); + white-space: nowrap; +} + +.cumthi-cell { + font-size: 0.82rem; + color: var(--text-muted); + max-width: 220px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* --------------------------------------------------------------------------- + Hub route (/thptqg/) — replaces the old standalone static index.html + --------------------------------------------------------------------------- */ + +.hub { + max-width: 42rem; + margin: 3rem auto; + padding: 0 1rem; + line-height: 1.6; +} + +.hub h1 { + font-size: 1.5rem; + margin-bottom: 0.5rem; +} + +.hub-list { + list-style: none; + padding: 0; + margin: 1.5rem 0; + display: grid; + gap: 0.75rem; +} + +.hub-link { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: 0.25rem 0.75rem; + padding: 0.9rem 1.1rem; + border: 1px solid var(--border); + border-radius: 10px; + background: var(--surface, transparent); + text-decoration: none; + color: inherit; +} + +.hub-link:hover, +.hub-link:focus-visible { + border-color: var(--border-strong); +} + +.hub-label { + font-weight: 600; +} + +.hub-blurb { + color: var(--text-muted); + font-size: 0.9rem; +} + +.hub-archive h2 { + font-size: 0.95rem; + font-weight: 600; + color: var(--text-muted); + margin-bottom: 0; +} + +.hub-list-compact { + margin-top: 0.75rem; +} + +.hub-back { + margin: 0.35rem 0 0; + font-size: 0.85rem; +} + +.hub-back a { + color: var(--text-muted); + text-decoration: none; +} + +.hub-back a:hover { + text-decoration: underline; +} diff --git a/src/App.jsx b/src/App.jsx index 94646c7..7e59bd2 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,17 +4,19 @@ import { SearchForm } from "./components/search-form"; import { ScoreTable } from "./components/score-table"; import { StudentDetail } from "./components/student-detail"; import { CustomQuery } from "./components/custom-query"; +import { Hub } from "./components/hub"; +import { dbOf } from "./datasets"; +import { resolveRoute } from "./router"; +import { isExamId, normaliseExamId } from "./lib/query-mode"; import "./App.css"; -const DB_URL = import.meta.env.BASE_URL + "thptqg2017.db.gz"; const MAX_RESULTS = 100; -const DB_SIZE_MB = 47; // Strip Vietnamese diacritics: "Nguyễn Bửu Lộc" → "nguyen buu loc" function toAscii(str) { return str .normalize("NFD") - .replace(/[\u0300-\u036f]/g, "") + .replace(/[̀-ͯ]/g, "") .replace(/đ/gi, "d") .toLowerCase(); } @@ -38,8 +40,11 @@ function readUrlQuery() { return new URL(window.location.href).searchParams.get("q") || ""; } -function App() { - const { db, loading, error, progress } = useSqlite(DB_URL); +/** The score-lookup view for one dataset. */ +function DatasetApp({ dataset }) { + const { db, loading, error, progress } = useSqlite( + dbOf(dataset, import.meta.env.BASE_URL), + ); const [results, setResults] = useState(null); const [searchError, setSearchError] = useState(null); const [activeTab, setActiveTab] = useState("search"); @@ -55,14 +60,14 @@ function App() { writeUrlQuery(q); try { - const isExamId = /^\d+$/.test(q); let stmt; - if (isExamId) { + if (isExamId(q)) { + // Letter-prefixed 2016 IDs are stored upper-case; digits are unaffected. stmt = db.prepare( "SELECT * FROM student WHERE so_bao_danh = $q LIMIT $limit", ); - stmt.bind({ $q: q, $limit: MAX_RESULTS }); + stmt.bind({ $q: normaliseExamId(q), $limit: MAX_RESULTS }); } else if (isAsciiOnly(q)) { stmt = db.prepare( "SELECT * FROM student WHERE ho_ten_ascii LIKE $q LIMIT $limit", @@ -141,9 +146,10 @@ function App() { return (
-

Tra cứu điểm thi THPT Quốc gia 2017

-

- Dữ liệu thí sinh toàn quốc · Hỗ trợ truy vấn SQL tùy chỉnh +

{dataset.title}

+

{dataset.subtitle}

+

+ ← Tất cả các kỳ thi

@@ -151,7 +157,7 @@ function App() { {loading && (

- Đang tải cơ sở dữ liệu ~{DB_SIZE_MB} MB + Đang tải cơ sở dữ liệu ~{dataset.dbSizeMb} MB {progress > 0 ? ` · ${progress}%` : ""}

@@ -188,6 +194,7 @@ function App() { onSearch={handleSearch} onClear={handleClear} disabled={loading || !!error} + examples={dataset.examples} /> {searchError && ( @@ -209,13 +216,17 @@ function App() { )} {activeTab === "sql" && ( - + )}