Files
jsahn 4e943700fc feat: add common utilities and feedback handling scripts
- Introduced common.js for shared functions including API calls, error reporting, and UI helpers.
- Implemented feedback.js for the feedback reporting page, including form handling and displaying feedback status.
- Added layout.js for managing galaxy and section layouts in the visualization.
- Created login.js for user authentication with session management.
- Developed search.js for term searching based on user input and synonyms.
- Implemented signup.js for user registration with validation and immediate login.
2026-10-06 23:14:45 +09:00

60 lines
3.4 KiB
JavaScript

// 내 정보: 로그인한 학생의 반·학습 기록 (로그인 안 했으면 /login 으로)
import { api, esc, toast } from "./common.js";
const main = document.getElementById("main");
const next = new URLSearchParams(location.search).get("next") || "/";
async function load() {
let me;
try { me = await api("/api/members/me"); } catch (e) { main.innerHTML = `<p class="muted">불러오지 못했습니다: ${esc(e.message)}</p>`; return; }
if (!me.logged_in) return renderGuest(me);
if (me.status === "approved") return renderMember(me);
main.innerHTML = `<h1>${esc(me.name)} 님</h1>
<div class="card" style="margin-top:16px">
<div class="acc-status"><span class="badge new">사용할 수 없는 계정</span>
<span class="acc-note">담당 교수자에게 문의해 주세요.</span></div>
<p style="margin-top:14px"><button class="btn" id="logout">로그아웃</button> <a class="btn ghost" href="/">개념 우주로</a></p>
</div>`;
document.getElementById("logout").onclick = logout;
}
function renderGuest(me) {
if (me.admin) {
main.innerHTML = `<h1>${esc(me.admin)} 님</h1><p class="acc-note">교수자로 로그인되어 있습니다.</p>
<p><a class="btn primary" href="/admin">관리 화면</a> <a class="btn" href="/members">회원 관리</a></p>`;
return;
}
location.replace("/login?next=" + encodeURIComponent("/account"));
}
async function renderMember(me) {
const rate = me.attempts ? Math.round((me.correct / me.attempts) * 100) : 0;
main.innerHTML = `<h1>${esc(me.name)} 님</h1>
<div class="acc-status"><span class="badge confirmed">승인됨</span><span class="acc-note">${esc(me.class_name || "")}</span>
<button class="btn small" id="logout">로그아웃</button></div>
<div class="acc-stats">
<div class="acc-stat"><small>푼 문제</small><b>${me.attempts}</b></div>
<div class="acc-stat"><small>맞힌 문제</small><b>${me.correct}</b></div>
<div class="acc-stat"><small>정답률</small><b>${rate}%</b></div>
</div>
<p style="margin-top:14px"><a class="btn primary" href="/#/quiz">문제 풀러 가기</a> <a class="btn" href="/#/quiz/wrong">틀린 문제 다시 풀기</a></p>
<div class="acc-grid" id="stats"><p class="muted">기록을 불러오는 중…</p></div>`;
document.getElementById("logout").onclick = logout;
try {
const s = await api("/api/members/my-stats");
document.getElementById("stats").innerHTML = `
<div class="card"><h2>단원별 정답률</h2>${s.by_galaxy.length ? s.by_galaxy.map((g) => {
const p = g.attempts ? Math.round((g.correct / g.attempts) * 100) : 0;
return `<div style="margin-bottom:10px"><div class="row"><span class="grow">${esc(g.name)}</span><span class="acc-note">${g.correct}/${g.attempts} · ${p}%</span></div><div class="bar"><i style="width:${p}%"></i></div></div>`;
}).join("") : `<p class="acc-note">아직 푼 문제가 없습니다.</p>`}</div>
<div class="card"><h2>자주 틀린 용어</h2>${s.wrong_terms.length ? `<div class="chips">${s.wrong_terms.map((t) => `<a href="/#/t/${esc(t.term_id)}">${esc(t.name)} · ${t.wrong}</a>`).join("")}</div>` : `<p class="acc-note">틀린 용어가 없습니다.</p>`}</div>`;
} catch (e) { document.getElementById("stats").innerHTML = `<p class="muted">${esc(e.message)}</p>`; }
}
async function logout() {
try { await api("/api/members/logout", { method: "POST" }); } catch { /* 무시 */ }
load();
}
load();