(function () {
"use strict";
const data = window.GPT_SUSI_2028_WORKBENCH || { meta: {}, changes: [], focusPrograms: { rows: [] }, universities: [] };
const resultData = window.GPT_SUSI_RESULTS_2026 || { rows: [] };
const PAGE_SIZE = 28;
const SIGNALS = ["교과", "학종", "논술", "수능최저", "면접", "정시", "반영과목", "5등급"];
const FIVE_GRADE_REFERENCE_POINTS = [
[1, 1.39], [1.083, 1.53], [1.167, 1.73], [1.25, 1.87], [1.333, 2.03],
[1.417, 2.18], [1.5, 2.31], [1.583, 2.45], [1.667, 2.61], [1.75, 2.73],
[1.833, 2.88], [1.917, 3], [2, 3.16], [2.083, 3.28], [2.167, 3.41],
[2.25, 3.54], [2.333, 3.68], [2.417, 3.8], [2.5, 3.95], [2.583, 4.08],
[2.667, 4.21], [2.75, 4.34], [2.833, 4.48], [2.917, 4.61], [3, 4.75],
];
const params = new URLSearchParams(window.location.search);
const state = {
mode: localStorage.getItem("gpt_susi_2028_mode") || "work",
view: ["university", "department", "admission"].includes(params.get("view")) ? params.get("view") : "university",
universityQuery: params.get("university") || "",
query: params.get("q") || "",
group: params.get("group") || "all",
region: "전체",
signal: "전체",
visible: PAGE_SIZE,
changeQuery: "",
changeCategory: "전체",
};
let basket = readStorage("gpt_susi_2028_basket", []);
let student = readStorage("gpt_susi_2028_student", { name: "", school: "", grade: "", interest: "" });
const universityById = new Map(data.universities.map((university) => [university.id, university]));
const elements = {
heroMetrics: document.getElementById("heroMetrics"),
consultStrip: document.getElementById("consultStrip"),
studentName: document.getElementById("studentName"),
studentSchool: document.getElementById("studentSchool"),
studentGrade: document.getElementById("studentGrade"),
studentInterest: document.getElementById("studentInterest"),
universitySearch: document.getElementById("universitySearch"),
keywordSearch: document.getElementById("keywordSearch"),
priorityFilter: document.getElementById("priorityFilter"),
regionFilter: document.getElementById("regionFilter"),
signalFilter: document.getElementById("signalFilter"),
activeFilter: document.getElementById("activeFilter"),
resultTitle: document.getElementById("resultTitle"),
resultSummary: document.getElementById("resultSummary"),
resultList: document.getElementById("resultList"),
emptyState: document.getElementById("emptyState"),
loadMore: document.getElementById("loadMore"),
changeModal: document.getElementById("changeModal"),
changeSearch: document.getElementById("changeSearch"),
changeCategories: document.getElementById("changeCategories"),
changeList: document.getElementById("changeList"),
detailModal: document.getElementById("detailModal"),
detailModalKicker: document.getElementById("detailModalKicker"),
detailModalTitle: document.getElementById("detailModalTitle"),
detailModalSubtitle: document.getElementById("detailModalSubtitle"),
detailModalBody: document.getElementById("detailModalBody"),
focusModal: document.getElementById("focusModal"),
focusModalTitle: document.getElementById("focusModalTitle"),
focusModalBody: document.getElementById("focusModalBody"),
basket: document.getElementById("basket"),
basketDim: document.getElementById("basketDim"),
basketCount: document.getElementById("basketCount"),
basketStudent: document.getElementById("basketStudent"),
basketList: document.getElementById("basketList"),
};
function readStorage(key, fallback) {
try { return JSON.parse(localStorage.getItem(key)) || fallback; } catch (_error) { return fallback; }
}
function saveStorage(key, value) {
localStorage.setItem(key, JSON.stringify(value));
}
function escapeHtml(value) {
return String(value ?? "").replace(/[&<>"']/g, (character) => ({
"&": "&", "<": "<", ">": ">", "\"": """, "'": "'",
}[character]));
}
function normalise(value) {
return String(value || "").normalize("NFKC").toLowerCase().replace(/\s+/g, "").replace(/[()[\]{}·ㆍ,._\-/]/g, "");
}
function queryTerms() {
return state.query.trim().toLowerCase().split(/\s+/).filter(Boolean);
}
function groupMatches(university) {
if (state.group === "seoulGyeonggi") return university.priorityTags.seoulGyeonggi;
if (state.group === "medical") return university.priorityTags.medical;
if (state.group === "flagship") return university.priorityTags.flagship;
if (state.group === "structured") return university.detailReady;
return true;
}
function universityMatches(university, includeKeyword = true) {
const universityQuery = state.universityQuery.trim().toLowerCase();
if (universityQuery && ![university.name, university.short, university.regions.join(" ")].join(" ").toLowerCase().includes(universityQuery)) return false;
if (!groupMatches(university)) return false;
if (state.region !== "전체" && !university.regions.includes(state.region)) return false;
if (state.signal !== "전체" && !university.signals[state.signal]) return false;
if (includeKeyword && state.query.trim()) {
const terms = queryTerms();
const haystack = [
university.name,
university.short,
university.regions.join(" "),
...university.admissions.flatMap((admission) => [admission.name, admission.category, admission.selection, admission.ratio, admission.suneung, admission.naesin, admission.interview, admission.note]),
...university.departments.flatMap((department) => [department.college, department.name, department.track, department.note]),
].join(" ").toLowerCase();
if (!terms.every((term) => haystack.includes(term))) return false;
}
return true;
}
function structuredDepartmentMatches(university, department) {
const terms = queryTerms();
if (!terms.length) return true;
const admissionsById = Object.fromEntries(university.admissions.map((admission) => [admission.id, admission]));
const text = [department.college, department.name, department.track, department.note,
...Object.keys(department.quota || {}).flatMap((id) => {
const admission = admissionsById[id] || {};
return [admission.name, admission.category, admission.ratio, admission.suneung, admission.naesin, admission.interview];
})].join(" ").toLowerCase();
return terms.every((term) => text.includes(term));
}
function updateUrl() {
const url = new URL(window.location.href);
[["view", state.view], ["university", state.universityQuery], ["q", state.query], ["group", state.group === "all" ? "" : state.group]].forEach(([key, value]) => {
if (value) url.searchParams.set(key, value); else url.searchParams.delete(key);
});
window.history.replaceState({}, "", url);
}
function renderHero() {
const meta = data.meta;
elements.heroMetrics.innerHTML = [
[meta.universityCount, "전체 대학"],
[meta.groupCounts.seoulGyeonggi, "서울·경기"],
[meta.groupCounts.medical, "의·치·약·수·한"],
[meta.groupCounts.flagship, "지거국"],
].map(([value, label]) => `
${Number(value).toLocaleString("ko-KR")}${label}
`).join("");
const changeCount = document.querySelector('[data-action="changes"] b');
if (changeCount) changeCount.textContent = `${Number(meta.changeCount || 0).toLocaleString("ko-KR")}건`;
}
function renderFilters() {
const groups = [
["all", "전체 대학", data.meta.universityCount],
["seoulGyeonggi", "서울·경기", data.meta.groupCounts.seoulGyeonggi],
["medical", "메디컬", data.meta.groupCounts.medical],
["flagship", "지거국", data.meta.groupCounts.flagship],
["structured", "전형 입력 완료", data.meta.detailUniversityCount],
];
elements.priorityFilter.innerHTML = groups.map(([id, label, count]) => ``).join("");
const regions = ["전체", ...new Set(data.universities.flatMap((university) => university.regions))];
elements.regionFilter.innerHTML = regions.map((region) => ``).join("");
elements.signalFilter.innerHTML = ["전체", ...SIGNALS].map((signal) => ``).join("");
const active = [];
if (state.group !== "all") active.push(groups.find(([id]) => id === state.group)?.[1]);
if (state.region !== "전체") active.push(state.region);
if (state.signal !== "전체") active.push(state.signal);
if (state.universityQuery) active.push(`대학: ${state.universityQuery}`);
if (state.query) active.push(`검색: ${state.query}`);
elements.activeFilter.textContent = active.length ? `현재 조건 · ${active.filter(Boolean).join(" · ")}` : data.meta.notice;
}
function renderResults() {
updateUrl();
renderFilters();
document.querySelectorAll("[data-view]").forEach((button) => button.classList.toggle("active", button.dataset.view === state.view));
const renderers = { university: renderUniversityView, department: renderDepartmentView, admission: renderAdmissionView };
renderers[state.view]();
}
function sortedUniversities(includeKeyword = true) {
return data.universities.filter((university) => universityMatches(university, includeKeyword)).sort((left, right) => left.priorityRank - right.priorityRank || left.name.localeCompare(right.name, "ko"));
}
function renderUniversityView() {
const rows = sortedUniversities(true);
elements.resultTitle.textContent = "대학별 보기";
elements.resultSummary.textContent = `${rows.length}개 대학 · 클릭하면 2028 전형 상세 확인`;
elements.resultList.innerHTML = rows.slice(0, state.visible).map(renderUniversityCard).join("");
finishList(rows.length);
}
function renderUniversityCard(university) {
const detailReady = university.detailReady && university.admissions.length > 0;
const statusLabel = detailReady ? (university.structureStatus.includes("1차") ? "전형 1차 입력" : "전형 입력 완료") : "전형 입력 중";
const admissionLinks = university.departments.reduce((sum, department) => sum + Object.keys(department.quota || {}).length, 0);
const detailText = detailReady
? `${university.admissions.length}개 전형 · ${university.departments.length ? `${university.departments.length}개 학과` : "대학 전형표"}`
: "전형 상세 입력 중";
return `
`;
}
function renderDepartmentGrid(university, departments) {
if (!departments.length) return `입력 완료된 학과 중 검색 조건에 맞는 결과가 없습니다.
`;
const admissionsById = Object.fromEntries(university.admissions.map((admission) => [admission.id, admission]));
return `${departments.map((department) => {
const admissions = Object.keys(department.quota || {}).map((id) => admissionsById[id]).filter(Boolean);
const total = Object.values(department.quota || {}).reduce((sum, value) => sum + (Number(value) || 0), 0);
return `
${escapeHtml(department.college)} · ${escapeHtml(department.track)}
${escapeHtml(department.name)}
${total}명${admissions.map((admission) => `${escapeHtml(admission.name)} ${department.quota[admission.id]}명`).join("")}
`;
}).join("")}
`;
}
function departmentRows() {
return sortedUniversities(false).flatMap((university) => university.departments.filter((department) => structuredDepartmentMatches(university, department)).map((department) => ({ university, department })));
}
function renderDepartmentView() {
const rows = departmentRows();
elements.resultTitle.textContent = "학과별 보기";
elements.resultSummary.textContent = `${rows.length.toLocaleString("ko-KR")}개 학과 · 상세 입력 완료 대학 기준`;
elements.resultList.innerHTML = rows.slice(0, state.visible).map(({ university, department }) => {
const total = Object.values(department.quota || {}).reduce((sum, value) => sum + (Number(value) || 0), 0);
return `${escapeHtml(university.name)} · ${escapeHtml(department.college)}
${escapeHtml(department.name)}
${escapeHtml(department.track)} · ${Object.keys(department.quota || {}).length}개 전형
2028 수시 선발인원 ${total}명
`;
}).join("");
finishList(rows.length);
}
function admissionRows() {
return sortedUniversities(false).flatMap((university) => {
if (university.departments.length) {
return university.departments.filter((department) => structuredDepartmentMatches(university, department)).flatMap((department) => Object.keys(department.quota || {}).map((admissionId) => ({ university, department, admission: university.admissions.find((row) => row.id === admissionId), quota: department.quota[admissionId] })).filter((row) => row.admission));
}
return university.admissions.filter(admissionMatches).map((admission) => ({ university, department: null, admission, quota: admissionQuota(university, admission) }));
});
}
function admissionMatches(admission) {
const terms = queryTerms();
if (!terms.length) return true;
const text = [admission.name, admission.category, admission.selection, admission.ratio, admission.eligibility, admission.suneung, admission.naesin, admission.interview, admission.note].join(" ").toLowerCase();
return terms.every((term) => text.includes(term));
}
function admissionQuota(university, admission) {
if (Number.isFinite(Number(admission.quota))) return Number(admission.quota);
return university.departments.reduce((sum, department) => sum + (Number(department.quota?.[admission.id]) || 0), 0);
}
function renderAdmissionView() {
const rows = admissionRows();
elements.resultTitle.textContent = "전형별 보기";
elements.resultSummary.textContent = `${rows.length.toLocaleString("ko-KR")}개 학과·전형 연결`;
elements.resultList.innerHTML = rows.slice(0, state.visible).map(({ university, department, admission, quota }) => `${escapeHtml(university.name)}${department ? ` · ${escapeHtml(department.name)}` : ""}
${escapeHtml(admission.name)} · ${quota}명
${escapeHtml(admission.category)} · ${escapeHtml(admission.selection)}
${escapeHtml(admission.ratio)}
수능최저 ${escapeHtml(admission.suneung)}
`).join("");
finishList(rows.length);
}
function finishList(total) {
elements.emptyState.hidden = total !== 0;
elements.loadMore.hidden = state.visible >= total;
}
function showUniversityDetail(universityId) {
const university = universityById.get(universityId);
if (!university?.admissions?.length) return;
elements.detailModalKicker.textContent = "2028 대학별 전형";
elements.detailModalTitle.textContent = `${university.name} 2028 전형`;
elements.detailModalSubtitle.textContent = `${university.admissions.length}개 전형 · 전형방법·인원·수능최저·평가내용`;
const introduction = university.detailSummary || (university.briefing ? `${university.briefing.difference} ${university.briefing.minimum}` : "");
const changes = university.changes || [];
const changeSummary = changes.length ? `${changes.map((change) => `
${escapeHtml(change.category)}${escapeHtml(change.type)}${escapeHtml(change.scope)}
${escapeHtml(change.title)}
2027${escapeHtml(String(change.before || "").replace(/^2027:\s*/, ""))}
2028${escapeHtml(String(change.after || "").replace(/^2028:\s*/, ""))}
상담 포인트${escapeHtml(change.impact)}
`).join("")}
` : `2027 대비 변경사항
${escapeHtml(university.changeReview || "2028 시행계획에 별도 변경 요약이 없거나 큰 변경을 확인하지 못했습니다.")}
`;
elements.detailModalBody.innerHTML = `${changeSummary}${introduction ? `2028 상담 한 줄${escapeHtml(introduction)}
` : ""}${university.admissions.map((admission, index) => {
const quota = admissionQuota(university, admission);
return `
${escapeHtml(admission.name)}
${escapeHtml(admission.category)}${escapeHtml(admission.selection)}${quota ? `${quota.toLocaleString("ko-KR")}명` : "인원 확인 중"}
- 전형방법
- ${escapeHtml(admission.ratio)}
- 지원자격
- ${escapeHtml(admission.eligibility)}
- 수능최저
- ${escapeHtml(admission.suneung)}
- 학생부·평가
- ${escapeHtml(admission.naesin)}
- 면접
- ${escapeHtml(admission.interview)}
- 확인사항
- ${escapeHtml(admission.note)}
`;
}).join("")}
`;
openModal(elements.detailModal);
}
function showDepartmentDetail(universityId, departmentName) {
const university = universityById.get(universityId);
const department = university?.departments.find((row) => row.name === departmentName);
if (!university || !department) return;
const admissions = Object.keys(department.quota || {}).map((id) => university.admissions.find((row) => row.id === id)).filter(Boolean);
elements.detailModalKicker.textContent = "2028 학과별 전형";
elements.detailModalTitle.textContent = `${university.name} · ${department.name}`;
elements.detailModalSubtitle.textContent = `${department.college} · ${department.track} · 2028 시행계획 기준`;
elements.detailModalBody.innerHTML = admissions.map((admission) => {
const result = findResult(university, department, admission);
return `${escapeHtml(admission.name)} ${escapeHtml(admission.category)} ${department.quota[admission.id]}명
- 전형방법
- ${escapeHtml(admission.ratio)}
- 지원자격
- ${escapeHtml(admission.eligibility)}
- 수능최저
- ${escapeHtml(admission.suneung)}
- 학생부·평가
- ${escapeHtml(admission.naesin)}
- 면접
- ${escapeHtml(admission.interview)}
- 확인사항
- ${escapeHtml(admission.note)}
${renderResult(result)}`;
}).join("");
openModal(elements.detailModal);
}
function findResult(university, department, admission) {
const universityKey = normalise(university.short || university.name).replace(/^국립/, "").replace(/대학교/g, "대");
const departmentKey = normalise(department.name);
const admissionKey = normalise(admission.name);
return (resultData.rows || []).filter((row) => normalise(row.university).replace(/^국립/, "").replace(/대학교/g, "대") === universityKey)
.filter((row) => {
const rowDepartment = normalise(row.department);
return rowDepartment === departmentKey || rowDepartment.includes(departmentKey) || departmentKey.includes(rowDepartment);
}).filter((row) => !row.category || row.category === admission.category)
.map((row) => {
const rowAdmission = normalise(row.admission);
const keywords = (admission.resultKeywords || [admission.name]).map(normalise);
const nameScore = keywords.some((keyword) => rowAdmission.includes(keyword) || keyword.includes(rowAdmission)) ? 100 : (rowAdmission.includes(admissionKey) || admissionKey.includes(rowAdmission) ? 70 : -1);
return { row, score: nameScore + (numericGrade(row.grade9_2026) !== null ? 200 : 0) + (row.gradeBasis2026 ? 20 : 0) };
}).filter((item) => item.score >= 0).sort((left, right) => right.score - left.score)[0]?.row || null;
}
function renderResult(result) {
if (!result) return `2026 입결학과·전형명이 정확히 연결되는 등급 자료가 없습니다.
`;
const grade9 = numericGrade(result.grade9_2026);
const grade5 = grade9 === null ? null : grade9To5Reference(grade9);
return `2026 · 9등급 원자료${grade9 === null ? "자료 없음" : grade9.toFixed(2)}${escapeHtml(result.gradeBasis2026 || "기준 미표기")}
→2028 · 5등급 참고${grade5 === null ? "변환 불가" : `≈ ${grade5.toFixed(2)}`}공식 합격선 아님
`;
}
function numericGrade(value) {
const match = String(value || "").match(/([1-9](?:\.\d+)?)/);
const number = match ? Number(match[1]) : NaN;
return Number.isFinite(number) && number >= 1 && number <= 9 ? number : null;
}
function grade9To5Reference(grade9) {
if (grade9 <= FIVE_GRADE_REFERENCE_POINTS[0][1]) return 1;
const last = FIVE_GRADE_REFERENCE_POINTS.at(-1);
if (grade9 >= last[1]) return Math.min(5, last[0] + (grade9 - last[1]) / 2.125);
for (let index = 0; index < FIVE_GRADE_REFERENCE_POINTS.length - 1; index += 1) {
const left = FIVE_GRADE_REFERENCE_POINTS[index];
const right = FIVE_GRADE_REFERENCE_POINTS[index + 1];
if (left[1] <= grade9 && grade9 <= right[1]) return left[0] + ((grade9 - left[1]) / (right[1] - left[1])) * (right[0] - left[0]);
}
return null;
}
function openChanges() {
renderChangeFilters();
renderChanges();
openModal(elements.changeModal);
}
function renderChangeFilters() {
const categories = ["전체", ...new Set(data.changes.map((change) => change.category))];
elements.changeCategories.innerHTML = categories.map((category) => ``).join("");
}
function renderChanges() {
const query = state.changeQuery.trim().toLowerCase();
const rows = data.changes.filter((change) => state.changeCategory === "전체" || change.category === state.changeCategory).filter((change) => !query || Object.values(change).join(" ").toLowerCase().includes(query));
elements.changeList.innerHTML = rows.map((change) => `${escapeHtml(change.university)}${escapeHtml(change.category)}${escapeHtml(change.type)}
${escapeHtml(change.scope)}${escapeHtml(change.title)}
2027
${escapeHtml(String(change.before).replace(/^2027:\s*/, ""))}
→2028
${escapeHtml(String(change.after).replace(/^2028:\s*/, ""))}
상담 포인트 ${escapeHtml(change.impact)}
`).join("");
}
function openFocus(focus) {
const rows = data.focusPrograms.rows.filter((row) => row.focus === focus);
const groups = new Map();
rows.forEach((row) => {
if (!groups.has(row.university)) groups.set(row.university, []);
groups.get(row.university).push(row);
});
elements.focusModalTitle.textContent = focus === "contract" ? "대기업 계약학과" : "핵심계열 상세";
elements.focusModalBody.innerHTML = [...groups.entries()].map(([university, universityRows]) => `${escapeHtml(university)} · ${universityRows.reduce((sum, row) => sum + row.quota, 0)}명
${universityRows.map((row) => `
${escapeHtml(row.partner)}${escapeHtml(row.department)} · ${row.quota}명
${escapeHtml(row.admission)} · ${escapeHtml(row.admissionCategory)}
${escapeHtml(row.method)}
수능최저 ${escapeHtml(row.minimum)}
`).join("")}
`).join("");
openModal(elements.focusModal);
}
function openModal(modal) {
modal.classList.add("on");
modal.setAttribute("aria-hidden", "false");
document.body.classList.add("modal-open");
}
function closeModal(modal) {
modal.classList.remove("on");
modal.setAttribute("aria-hidden", "true");
if (!document.querySelector(".modal.on") && !elements.basket.classList.contains("on")) document.body.classList.remove("modal-open");
}
function addCandidate(universityId, departmentName, admissionId = "") {
const university = universityById.get(universityId);
const department = departmentName ? university?.departments.find((row) => row.name === departmentName) : null;
if (!university || (departmentName && !department)) return;
const admissions = admissionId ? university.admissions.filter((row) => row.id === admissionId) : department ? Object.keys(department.quota || {}).map((id) => university.admissions.find((row) => row.id === id)).filter(Boolean) : university.admissions;
admissions.forEach((admission) => {
const departmentLabel = department?.name || "대학 전체";
const key = [university.id, departmentLabel, admission.id].join("|");
if (!basket.some((item) => item.key === key)) basket.push({ key, university: university.name, department: departmentLabel, admission: admission.name, quota: department ? department.quota[admission.id] : admissionQuota(university, admission), minimum: admission.suneung, method: admission.ratio });
});
saveStorage("gpt_susi_2028_basket", basket);
renderBasket();
}
function renderBasket() {
elements.basketCount.textContent = basket.length;
elements.basketStudent.textContent = [student.name, student.school, student.grade ? `5등급 내신 ${student.grade}` : "", student.interest].filter(Boolean).join(" · ") || "학생 정보를 입력하지 않았습니다.";
elements.basketList.innerHTML = basket.length ? basket.map((item, index) => `${escapeHtml(item.university)} · ${escapeHtml(item.department)}${escapeHtml(item.admission)} · ${item.quota}명수능최저 ${escapeHtml(item.minimum)}`).join("") : `학과 상세에서 상담 후보를 담아주세요.
`;
}
function toggleBasket(show) {
elements.basket.classList.toggle("on", show);
elements.basket.setAttribute("aria-hidden", String(!show));
elements.basketDim.hidden = !show;
document.body.classList.toggle("modal-open", show);
}
function setMode(mode) {
state.mode = mode;
localStorage.setItem("gpt_susi_2028_mode", mode);
document.querySelectorAll("[data-mode]").forEach((button) => button.classList.toggle("active", button.dataset.mode === mode));
const consulting = mode === "consult";
elements.consultStrip.hidden = !consulting;
document.body.classList.toggle("consulting", consulting);
}
function saveStudent() {
student = { name: elements.studentName.value.trim(), school: elements.studentSchool.value.trim(), grade: elements.studentGrade.value.trim(), interest: elements.studentInterest.value.trim() };
saveStorage("gpt_susi_2028_student", student);
renderBasket();
}
document.querySelectorAll("[data-mode]").forEach((button) => button.addEventListener("click", () => setMode(button.dataset.mode)));
document.querySelectorAll(".tool-card[data-action]").forEach((card) => card.addEventListener("click", () => {
if (card.dataset.action === "changes") openChanges();
if (card.dataset.action === "focus") openFocus(card.dataset.focus);
if (card.dataset.action === "group") {
state.group = card.dataset.group;
state.visible = PAGE_SIZE;
renderResults();
document.getElementById("searchArea").scrollIntoView({ behavior: "smooth" });
}
}));
document.getElementById("viewTabs").addEventListener("click", (event) => {
const button = event.target.closest("[data-view]");
if (!button) return;
state.view = button.dataset.view;
state.visible = PAGE_SIZE;
renderResults();
});
elements.universitySearch.addEventListener("input", (event) => { state.universityQuery = event.target.value; state.visible = PAGE_SIZE; renderResults(); });
elements.keywordSearch.addEventListener("input", (event) => { state.query = event.target.value; state.visible = PAGE_SIZE; renderResults(); });
document.getElementById("resetButton").addEventListener("click", () => {
Object.assign(state, { universityQuery: "", query: "", group: "all", region: "전체", signal: "전체", visible: PAGE_SIZE });
elements.universitySearch.value = "";
elements.keywordSearch.value = "";
renderResults();
});
elements.priorityFilter.addEventListener("click", (event) => { const button = event.target.closest("[data-group]"); if (button) { state.group = button.dataset.group; state.visible = PAGE_SIZE; renderResults(); } });
elements.regionFilter.addEventListener("click", (event) => { const button = event.target.closest("[data-region]"); if (button) { state.region = button.dataset.region; state.visible = PAGE_SIZE; renderResults(); } });
elements.signalFilter.addEventListener("click", (event) => { const button = event.target.closest("[data-signal]"); if (button) { state.signal = button.dataset.signal; state.visible = PAGE_SIZE; renderResults(); } });
elements.loadMore.addEventListener("click", () => { state.visible += PAGE_SIZE; renderResults(); });
elements.resultList.addEventListener("click", (event) => {
const universityDetail = event.target.closest("[data-university-detail]");
if (universityDetail) showUniversityDetail(universityDetail.dataset.universityDetail);
const detail = event.target.closest("[data-detail-university]");
if (detail) showDepartmentDetail(detail.dataset.detailUniversity, detail.dataset.detailDepartment);
const candidate = event.target.closest("[data-candidate-university]");
if (candidate) addCandidate(candidate.dataset.candidateUniversity, candidate.dataset.candidateDepartment, candidate.dataset.candidateAdmission || "");
});
elements.detailModalBody.addEventListener("click", (event) => {
const candidate = event.target.closest("[data-candidate-university]");
if (candidate) addCandidate(candidate.dataset.candidateUniversity, candidate.dataset.candidateDepartment, candidate.dataset.candidateAdmission || "");
});
document.querySelectorAll("[data-close]").forEach((button) => button.addEventListener("click", () => closeModal(document.getElementById(button.dataset.close))));
document.querySelectorAll(".modal").forEach((modal) => modal.addEventListener("click", (event) => { if (event.target === modal) closeModal(modal); }));
elements.changeSearch.addEventListener("input", (event) => { state.changeQuery = event.target.value; renderChanges(); });
elements.changeCategories.addEventListener("click", (event) => { const button = event.target.closest("[data-change-category]"); if (button) { state.changeCategory = button.dataset.changeCategory; renderChangeFilters(); renderChanges(); } });
[elements.studentName, elements.studentSchool, elements.studentGrade, elements.studentInterest].forEach((input) => input.addEventListener("input", saveStudent));
document.getElementById("basketOpen").addEventListener("click", () => toggleBasket(true));
document.getElementById("basketClose").addEventListener("click", () => toggleBasket(false));
elements.basketDim.addEventListener("click", () => toggleBasket(false));
elements.basketList.addEventListener("click", (event) => { const button = event.target.closest("[data-remove-candidate]"); if (button) { basket.splice(Number(button.dataset.removeCandidate), 1); saveStorage("gpt_susi_2028_basket", basket); renderBasket(); } });
document.getElementById("basketClear").addEventListener("click", () => { basket = []; saveStorage("gpt_susi_2028_basket", basket); renderBasket(); });
document.getElementById("basketCopy").addEventListener("click", async (event) => {
const text = [`[2028 수시 상담]`, elements.basketStudent.textContent, ...basket.map((item, index) => `${index + 1}. ${item.university} ${item.department} / ${item.admission} ${item.quota}명 / 최저 ${item.minimum}`)].join("\n");
try { await navigator.clipboard.writeText(text); event.target.textContent = "복사 완료"; setTimeout(() => { event.target.textContent = "상담 메모 복사"; }, 1200); } catch (_error) { window.prompt("아래 내용을 복사하세요.", text); }
});
document.addEventListener("keydown", (event) => { if (event.key === "Escape") { document.querySelectorAll(".modal.on").forEach(closeModal); toggleBasket(false); } });
elements.universitySearch.value = state.universityQuery;
elements.keywordSearch.value = state.query;
elements.studentName.value = student.name;
elements.studentSchool.value = student.school;
elements.studentGrade.value = student.grade;
elements.studentInterest.value = student.interest;
renderHero();
renderBasket();
setMode(state.mode);
renderResults();
})();