/* blueyetutor — App shell + hash router.
*
* Layout: left sidebar (brand + nav + user) + main content area.
* Visual: white surfaces, navy used only on active-item indicator,
* CTAs, links, and key numeric values.
*
* Routes:
* #/login LoginPage (anonymous only)
* #/upload UploadPage (default authed landing)
* #/library LibraryPage
* #/workbook WorkbookPage
* #/history HistoryPage
* #/students StudentsPage
*/
const { useState: appUseState, useEffect: appUseEffect } = React;
// History 는 Workbook 안 sub-tab, 배정 현황은 Students 안 sub-tab 으로 통합 (2026-05-20).
const TEACHER_NAV = [
{ route: "upload", label: "Upload", icon: "Upload" },
{ route: "library", label: "Library", icon: "Library" },
{ route: "workbook", label: "Workbook", icon: "Workbook" },
{ route: "students", label: "Students", icon: "Students" },
];
const STUDENT_NAV = [
{ route: "assignments", label: "내 과제", icon: "Notebook" },
];
function navFor(role) { return role === "student" ? STUDENT_NAV : TEACHER_NAV; }
function homeFor(role) { return role === "student" ? "assignments" : "upload"; }
function useHashRoute() {
const [r, setR] = appUseState(() => parseHash());
appUseEffect(() => {
const onHash = () => setR(parseHash());
window.addEventListener("hashchange", onHash);
return () => window.removeEventListener("hashchange", onHash);
}, []);
return r;
}
function parseHash() {
const h = (location.hash || "").replace(/^#\/?/, "");
const q = h.indexOf("?");
return (q >= 0 ? h.slice(0, q) : h) || "upload";
}
// 페이지가 sub-tab 직접 링크(`#/workbook?tab=history`)를 읽을 때 사용.
function hashQueryParam(name) {
const h = (location.hash || "");
const q = h.indexOf("?");
if (q < 0) return null;
const sp = new URLSearchParams(h.slice(q + 1));
return sp.get(name);
}
window.hashQueryParam = hashQueryParam;
function initial(email) {
if (!email) return "·";
const at = email.indexOf("@");
const local = at > 0 ? email.slice(0, at) : email;
return (local[0] || "·").toUpperCase();
}
// 사이드바 footer 의 "비밀번호 변경" 진입점 — modal 로 옛/새 비번 받아서
// /api/auth/change-password 호출. 성공 시 사용자에게 통보만, 세션은 유지.
function ChangePasswordButton() {
const [open, setOpen] = appUseState(false);
const [oldPw, setOldPw] = appUseState("");
const [newPw, setNewPw] = appUseState("");
const [newPw2, setNewPw2] = appUseState("");
const [busy, setBusy] = appUseState(false);
const [msg, setMsg] = appUseState(null);
const reset = () => { setOldPw(""); setNewPw(""); setNewPw2(""); setMsg(null); };
const submit = async () => {
if (newPw.length < 8) { setMsg({ ok: false, text: "새 비밀번호는 최소 8자 이상이어야 합니다." }); return; }
if (newPw !== newPw2) { setMsg({ ok: false, text: "새 비밀번호가 일치하지 않습니다." }); return; }
setBusy(true); setMsg(null);
const r = await window.storeActions.changePassword(oldPw, newPw);
setBusy(false);
if (r && r.ok) { setMsg({ ok: true, text: "변경 완료." }); setOldPw(""); setNewPw(""); setNewPw2(""); }
else setMsg({ ok: false, text: (r && r.message) || "변경에 실패했습니다." });
};
return (
<>