PageSourceSearch

https://naemo.ai/_next/static/chunks/809-eb8296d393289c8b.js

js naemo.ai collected 2026-09-24 14:29:55 UTC 160,825 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[809],{39994:function(e,t,n){n.d(t,{C:function(){return i}});var r=n(57437),a=n(34137);function i(e){let{isOpen:t,step:n,onSaveAndReset:i,onResetOnly:s,onSaveFile:l,onConfirmSavedAndReset:o,onCancel:d,isProcessing:c,saveNeedsShareHint:u=!1,filename:m=null}=e;return((0,a.j)(d,t&&!c),t)?(0,r.jsx)("div",{className:"modal-viewport z-50 flex items-center justify-center bg-black/50 px-4",children:(0,r.jsxs)("div",{role:"dialog","aria-modal":"true","aria-labelledby":"reset-archive-title",className:"w-full max-w-sm rounded-xl bg-white dark:bg-[#18181b] p-5 shadow-xl",children:["ask"===n&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("h2",{id:"reset-archive-title",className:"text-[15px] font-semibold text-zinc-900 dark:text-white mb-2",children:"초기화하면 학생 기록이 삭제됩니다"}),(0,r.jsx)("p",{className:"text-[13px] text-zinc-500 dark:text-zinc-400 mb-5 leading-relaxed",children:"먼저 교무수첩 파일로 받아 두세요. 파일은 선생님 기기에만 남고, 서버에서는 지워집니다."}),(0,r.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,r.jsx)("button",{type:"button",onClick:i,disabled:c,className:"w-full h-10 rounded-lg bg-[#2883DE] text-white text-[13px] font-medium disabled:opacity-50",children:c?"교무수첩 만드는 중…":"교무수첩 저장 후 초기화"}),(0,r.jsx)("button",{type:"button",onClick:s,disabled:c,className:"w-full h-10 rounded-lg border border-zinc-300 dark:border-zinc-600 text-zinc-700 dark:text-zinc-300 text-[13px] font-medium disabled:opacity-50",children:"저장하지 않고 초기화"}),(0,r.jsx)("button",{type:"button",onClick:d,disabled:c,className:"w-full h-10 rounded-lg text-zinc-500 text-[13px] font-medium disabled:opacity-50",children:"취소"})]})]}),"save-file"===n&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("h2",{id:"reset-archive-title",className:"text-[15px] font-semibold text-zinc-900 dark:text-white mb-2",children:"교무수첩이 준비되었습니다"}),(0,r.jsxs)("p",{className:"text-[13px] text-zinc-500 dark:text-zinc-400 mb-5 leading-relaxed",children:[m?"".concat(m," 파일을 "):"파일을 ",u?"받은 뒤, 공유 화면에서 ‘파일에 저장’을 고르세요. 저장이 끝난 다음에 초기화합니다.":"받은 뒤에 초기화합니다."]}),(0,r.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,r.jsx)("button",{type:"button",onClick:l,disabled:c,className:"w-full h-10 rounded-lg bg-[#2883DE] text-white text-[13px] font-medium disabled:opacity-50",children:c?"저장하는 중…":"기기에 저장"}),(0,r.jsx)("button",{type:"button",onClick:d,disabled:c,className:"w-full h-10 rounded-lg text-zinc-500 text-[13px] font-medium disabled:opacity-50",children:"취소"})]})]}),"confirm-saved"===n&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("h2",{id:"reset-archive-title",className:"text-[15px] font-semibold text-zinc-900 dark:text-white mb-2",children:"파일을 받은 뒤에만 지우세요"}),(0,r.jsxs)("p",{className:"text-[13px] text-zinc-500 dark:text-zinc-400 mb-5 leading-relaxed",children:[m?"".concat(m," 이 "):"교무수첩 파일이 ","기기에 있는지 확인하세요. 지우면 서버의 학생 기록은 되돌릴 수 없습니다."]}),(0,r.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,r.jsx)("button",{type:"button",onClick:o,disabled:c,className:"w-full h-10 rounded-lg bg-red-600 text-white text-[13px] font-medium disabled:opacity-50",children:c?"처리 중…":"기록 지우기"}),(0,r.jsx)("button",{type:"button",onClick:d,disabled:c,className:"w-full h-10 rounded-lg text-zinc-500 text-[13px] font-medium disabled:opacity-50",children:"취소"})]})]})]})}):null}},70287:function(e,t,n){n.d(t,{J:function(){return c}});var r=n(57437),a=n(98319),i=n(2265),s=n(67920),l=n(54345),o=n(90241),d=n(34137);function c(e){var t;let{variant:n="desktop"}=e,d=(0,s.XC)(),[c,m]=(0,i.useState)(!1),[p,x]=(0,i.useState)(null),{showToast:h}=(0,a.p)(),f=async(e,t)=>{try{await (0,o.pM)(e,t),h("교무수첩을 저장했습니다. 서버에는 남지 않고 선생님 기기에만 보관됩니다.","success"),x(null)}catch(e){if(e instanceof o.f_){h("저장을 취소했습니다.","error");return}throw e}},b=async()=>{m(!0);try{let e=await (0,l.F)(d);if(!e.success){h(e.error||"교무수첩을 만들지 못했습니다.","error");return}if((0,o.NI)()){x({filename:e.filename,html:e.html});return}await f(e.filename,e.html)}catch(e){console.error(e),h("교무수첩을 만드는 중 오류가 발생했습니다.","error")}finally{m(!1)}},g=async()=>
1{if(p){m(!0);try{await f(p.filename,p.html)}catch(e){console.error(e),h("교무수첩을 저장하지 못했습니다.","error")}finally{m(!1)}}},v="mobile"===n?(0,r.jsxs)("button",{type:"button",onClick:b,disabled:c,className:"w-full px-4 py-3.5 flex items-center justify-between active:bg-zinc-50 dark:active:bg-zinc-800 transition-colors disabled:opacity-50",children:[(0,r.jsx)("span",{className:"text-[14px] text-zinc-800 dark:text-zinc-200",children:"교무수첩으로 보관"}),(0,r.jsx)("span",{className:"text-[13px] text-zinc-400 dark:text-zinc-500",children:c?"만드는 중…":"보관하기"})]}):(0,r.jsxs)("div",{className:"flex items-center justify-between h-8",children:[(0,r.jsx)("span",{className:"text-[14px] text-[#E0E0E0] font-medium",children:"교무수첩으로 보관"}),(0,r.jsx)("button",{type:"button",onClick:b,disabled:c,className:"text-[13px] text-zinc-500 hover:text-zinc-300 disabled:opacity-50 font-medium transition-colors",children:c?"만드는 중…":"보관하기"})]});return(0,r.jsxs)(r.Fragment,{children:[v,(0,r.jsx)(u,{isOpen:!!p,isSaving:c,filename:null!==(t=null==p?void 0:p.filename)&&void 0!==t?t:null,onSave:g,onCancel:()=>x(null)})]})}function u(e){let{isOpen:t,isSaving:n,filename:a,onSave:i,onCancel:s}=e;return((0,d.j)(s,t&&!n),t)?(0,r.jsx)("div",{className:"modal-viewport z-[120] flex items-center justify-center bg-black/50 px-4",children:(0,r.jsxs)("div",{role:"dialog","aria-modal":"true","aria-labelledby":"yearbook-ready-title",className:"w-full max-w-sm rounded-xl bg-white dark:bg-[#18181b] p-5 shadow-xl",children:[(0,r.jsx)("h2",{id:"yearbook-ready-title",className:"text-[15px] font-semibold text-zinc-900 dark:text-white mb-2",children:"교무수첩이 준비되었습니다"}),(0,r.jsxs)("p",{className:"text-[13px] text-zinc-500 dark:text-zinc-400 mb-5 leading-relaxed",children:[a?"".concat(a," 파일을 기기에 저장하세요. "):"","공유 화면에서는 ‘파일에 저장’을 고르면 됩니다. 서버에는 남지 않습니다."]}),(0,r.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,r.jsx)("button",{type:"button",onClick:i,disabled:n,className:"w-full h-10 rounded-lg bg-[#2883DE] text-white text-[13px] font-medium disabled:opacity-50",children:n?"저장하는 중…":"기기에 저장"}),(0,r.jsx)("button",{type:"button",onClick:s,disabled:n,className:"w-full h-10 rounded-lg text-zinc-500 text-[13px] font-medium disabled:opacity-50",children:"취소"})]})]})}):null}},54345:function(e,t,n){n.d(t,{F:function(){return a}});var r=n(83008);async function a(e){var t,n;let a=await (0,r.X)("generateYearbook",{},e,{timeout:12e4});return a.success&&(null===(t=a.data)||void 0===t?void 0:t.yearbook)?{success:!0,filename:a.data.filename,html:'<!DOCTYPE html>\n<html lang="ko">\n<head>\n<meta charset="utf-8">\n<meta name="viewport" content="width=device-width, initial-scale=1">\n<title>'+((n=a.data.yearbook).meta.academicYearLabel+" 교무수첩").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")+'</title>\n<style>* { box-sizing: border-box; }\nhtml, body { height: 100%; }\nbody { margin: 0; font-family: "Apple SD Gothic Neo", "Malgun Gothic", sans-serif; color: #1c1917; background: #fafaf9; line-height: 1.55; word-break: keep-all; overflow-wrap: anywhere; }\n:focus-visible { outline: 2px solid #1c1917; outline-offset: 2px; }\n.yb-skip { position: absolute; left: -999px; top: 8px; background: #fff; padding: 6px 10px; font-size: 13px; }\n.yb-skip:focus { left: 8px; z-index: 20; }\n#app { display: flex; height: 100vh; overflow: hidden; }\n#sidebar { width: 260px; flex-shrink: 0; background: #f4f3f1; border-right: 1px solid #e7e5e4; padding: 20px 16px; overflow-y: auto; }\n#main { flex: 1; padding: 32px 40px 48px; overflow-y: auto; min-width: 0; }\n.yb-search-label { display: block; font-size: 12px; font-weight: 600; color: #44403c; margin-bottom: 6px; }\n#search-input { width: 100%; padding: 8px 10px; margin-bottom: 14px; border: 1px solid #d6d3d1; border-radius: 4px; font-size: 13px; background: #fff; }\n.yb-group { margin-bottom: 8px; }\n.yb-group summary { cursor: pointer; font-weight: 600; font-size: 13px; padding: 6px 4px; color: #44403c; }\n.yb-student-btn { display: block; width: 100%; text-align: left; padding: 6px 8px; border: none; background: none; font-size: 13px; border-radius: 4px; cursor: pointer; color: inherit; }\n.yb-student-btn:hover { background: #e7e5e4; }\n.yb-student-btn.active { background: #d6d3d1; font-weight: 600; }\n.yb-hidden { display: none !important; }\n#search-empty { font-size: 13px; color: #44403c; padding: 8px 4px; }\nh1 { font-size: 22px; font-weight: 700; margin: 0 0 20px; letter-spacing: -0.02em; line-height: 1.3; }\n.yb-running { display: none; }\n.yb-section { margin: 0 0 8px; }\n.yb-section h2 { font-size: 13px; font-weight: 700; color: #44403c; margin: 28px 0 10px; padding: 0; border: 0; }\n.yb-empty { font-size: 14px; color: #44403c; }\n.yb-kv { display: grid; grid-template-columns: 6.5em 1fr; gap: 4px 12px; font-size: 14px; }\n.yb-kv dt { color: #44403c; font-weight: 600; }\n.yb-kv dd { margin: 0; }\n.yb-counsel { display: grid; grid-template-columns: 7em 1fr; gap: 2px 16px; margin-bottom: 14px; }\n.yb-counsel-date { font-size: 12px; color: #44403c; padding-top: 2px; }\n.yb-counsel-action { font-size: 13px; color: #44403c; margin-top: 4px; }\ntable.yb-table { border-collapse: collapse; width: 100%; font-size: 13px; margin: 0 0 8px; font-variant-numeric: tabular-nums; }\ntable.yb-table th { text-align: left; font-weight: 600; color: #44403c; border-bottom: 1px solid #1c1917; padding: 8px 12px; }\ntable.yb-table td { text-align: left; border-bottom: 1px solid #e7e5e4; padding: 8px 12px; vertical-align: middle; }\ntable.yb-table .yb-student-btn { display: inline; width: auto; padding: 0; border-radius: 0; }\n.yb-table-details summary { cursor: pointer; font-size: 12px; color: #44403c; padding: 4px 0; user-select: none; }\nmark { background: #fde68a; padding: 0 1px; }\n.yb-chart { margin: 0 0 14px; }\n.yb-chart svg { max-width: 560px; display: block; }\n.yb-legend { display: flex; flex-wrap: wrap; gap: 10px 14px; margin-top: 8px; }\n.yb-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; cursor: pointer; background: none; border: 0; padding: 0; color: #1c1917; }\n.yb-legend-item.is-off { color: #a8a29e; }\n.yb-legend-swatch { display: inline-block; width: 8px; height: 8px; flex-shrink: 0; }\n.yb-tooltip { position: fixed; display: none; background: #1c1917; color: #fff; font-size: 11px; line-height: 1.4; padding: 4px 8px; pointer-events: none; z-index: 1000; white-space: nowrap; }\n.yb-print-cover { display: none; }\n.yb-sidebar-cover { margin-bottom: 16px; }\n.yb-home h1 { font-size: 26px; margin-bottom: 8px; }\n.yb-home h2 { font-size: 13px; font-weight: 700; color: #44403c; margin: 0 0 10px; }\n.yb-home-meta { font-size: 14px; color: #44403c; margin-bottom: 28px; }\n.yb-roster-h { font-size: 13px; font-weight: 700; color: #44403c; margin: 18px 0 8px; }\n.yb-snippet { font-size: 12px; color: #44403c; padding: 0 8px 10px 8px; }\n.yb-home-btn { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }\n.yb-home-btn svg { width: 15px; height: 15px; flex-shrink: 0; }\n.yb-nav-home { font-weight: 600; }\n.yb-folder > summary { color: #57534e; }\n.yb-folder .yb-group { margin-left: 8px; margin-bottom: 2px; }\n.yb-folder .yb-group summary { font-weight: 500; }\n.yb-prose { white-space: pre-wrap; }\n.yb-note { margin: 0 0 28px; }\n.yb-note-title { font-size: 16px; font-weight: 700; margin: 0 0 4px; }\n.yb-note-date { font-size: 12px; color: #44403c; margin-bottom: 8px; }\n.yb-month { font-size: 13px; font-weight: 700; color: #44403c; margin: 22px 0 8px; }\n.yb-event { display: grid; grid-template-columns: 12.5em 1fr; gap: 2px 16px; margin-bottom: 8px; }\n.yb-event-date { font-size: 12px; color: #44403c; padding-top: 2px; white-space: nowrap; }\nh4.yb-roster-h { font-size: 12px; font-weight: 600; margin: 10px 0 6px; }\n@media (max-width: 720px) {\n  html, body { height: auto; }\n  #app { display: block; height: auto; overflow: visible; }\n  #sidebar { width: auto; border-right: 0; border-bottom: 1px solid #e7e5e4; }\n  .yb-sidebar-cover { display: none; }\n  #main { padding: 20px 16px 40px; }\n  h1 { font-size: 20px; }\n  .yb-counsel, .yb-kv, .yb-event { grid-template-columns: 1fr; gap: 2px; }\n}\n@media print {\n  html, body, #app { height: auto; overflow: visible; display: block; }\n  .yb-skip, #sidebar, #search-input, #search-results, #search-empty, .yb-search-label { display: none !important; }\n  #main { padding: 0; overflow: visible; }\n  .yb-print-cover { display: block; page-break-after: always; padding: 24mm 0; }\n  .yb-print-cover h1 { font-size: 28px; margin-bottom: 12px; }\n  .yb-student-page { page-break-after: always; }\n  .yb-student-page:last-child { page-break-after: auto; }\n  .yb-running { display: block; font-size: 11px; color: #44403c; margin-bottom: 16px; padding-bottom: 6px; border-bottom: 1px solid #e7e5e4; }\n  .yb-table-details summary { display: none; }\n  .yb-table-details table { display: table !important; }\n  .yb-chart, .yb-no-print { display: none !important; }\n}</style>\n</head>\n<body>\n<div id="app"></div>\n<script id="yearbook-data" type="application/json">'+JSON.stringify(n).replace(/</g,"\\u003c").replace(/>/g,"\\u003e")+"</script>\n<script>(function () {\n  var raw = document.getElementById('yearbook-data').textContent;\n  var data = JSON.parse(raw);\n  var app = document.getElementById('app');\n  function ymd(value) {\n    if (!value) return '';\n    var s = String(value);\n    if (s.length >= 10 && s.charAt(4) === '-') return s.slice(0, 10).replace(/-/g, '.');\n    var d = new Date(s);\n    if (isNaN(d.getTime())) return s;\n    var m = d.getMonth() + 1;\n    var day = d.getDate();\n    return d.getFullYear() + '.' + (m < 10 ? '0' : '') + m + '.' + (d
1ay < 10 ? '0' : '') + day;\n  }\n  function runningLabel() {\n    var parts = [];\n    if (data.meta.schoolName) parts.push(data.meta.schoolName);\n    parts.push(data.meta.academicYearLabel);\n    if (data.meta.homeroomLabel) parts.push(data.meta.homeroomLabel);\n    parts.push(data.meta.teacherName + ' 선생님');\n    return parts.join(' \xb7 ');\n  }\n  var skip = document.createElement('a');\n  skip.className = 'yb-skip';\n  skip.href = '#main';\n  skip.textContent = '본문으로';\n  document.body.insertBefore(skip, app);\n  var printCover = document.createElement('div');\n  printCover.className = 'yb-print-cover';\n  printCover.setAttribute('aria-hidden', 'true');\n  document.body.insertBefore(printCover, app);\n  var sidebar = document.createElement('nav');\n  sidebar.id = 'sidebar';\n  sidebar.setAttribute('aria-label', '교무수첩');\n  var main = document.createElement('main');\n  main.id = 'main';\n  app.appendChild(sidebar);\n  app.appendChild(main);\n\n  var cover = document.createElement('div');\n  cover.className = 'yb-sidebar-cover';\n  if (data.meta.schoolName) {\n    var schoolLine = document.createElement('div');\n    schoolLine.style.fontSize = '15px';\n    schoolLine.style.fontWeight = '700';\n    schoolLine.textContent = data.meta.schoolName;\n    cover.appendChild(schoolLine);\n  }\n  var homeroomLine = document.createElement('div');\n  homeroomLine.style.fontSize = '13px';\n  homeroomLine.style.fontWeight = '600';\n  homeroomLine.style.color = '#44403c';\n  homeroomLine.textContent = data.meta.academicYearLabel +\n    (data.meta.homeroomLabel ? ' ' + data.meta.homeroomLabel : '') +\n    ' ' + data.meta.teacherName + ' 선생님';\n  cover.appendChild(homeroomLine);\n  var countLine = document.createElement('div');\n  countLine.style.fontSize = '12px';\n  countLine.style.color = '#44403c';\n  countLine.textContent = '학생 ' + data.meta.studentCount + '명';\n  cover.appendChild(countLine);\n  if (data.meta.generatedAt) {\n    var dateLine = document.createElement('div');\n    dateLine.style.fontSize = '12px';\n    dateLine.style.color = '#44403c';\n    dateLine.textContent = '만든 날 ' + ymd(data.meta.generatedAt);\n    cover.appendChild(dateLine);\n  }\n  sidebar.appendChild(cover);\n\n  var searchLabel = document.createElement('label');\n  searchLabel.className = 'yb-search-label';\n  searchLabel.setAttribute('for', 'search-input');\n  searchLabel.textContent = '검색';\n  sidebar.appendChild(searchLabel);\n  var searchInput = document.createElement('input');\n  searchInput.id = 'search-input';\n  searchInput.type = 'search';\n  searchInput.placeholder = '이름, 상담, 세특, 출결…';\n  sidebar.appendChild(searchInput);\n  var searchEmpty = document.createElement('div');\n  searchEmpty.id = 'search-empty';\n  searchEmpty.hidden = true;\n  searchEmpty.textContent = '해당하는 학생이 없습니다.';\n  sidebar.appendChild(searchEmpty);\n\n  var studentsById = {};\n  data.students.forEach(function (s) { studentsById[s.id] = s; });\n\n  function searchBlob(student) {\n    var parts = [student.name];\n    if (student.memo) parts.push(student.memo);\n    if (student.overallOpinion) parts.push(student.overallOpinion);\n    if (student.careerCategory) parts.push(student.careerCategory);\n    if (student.targetUniversityName) parts.push(student.targetUniversityName);\n    if (student.targetDepartment) parts.push(student.targetDepartment);\n    student.counseling.forEach(function (c) { parts.push(c.content); if (c.actionItems) parts.push(c.actionItems); });\n    student.seteuk.forEach(function (r) { if (r.draft) parts.push(r.draft); });\n    student.performance.forEach(function (r) { if (r.draft) parts.push(r.draft); });\n    if (student.behaviorDraft) parts.push(student.behaviorDraft);\n    if (student.lifeRecord && student.lifeRecord.summary) parts.push(student.lifeRecord.summary);\n    student.activities.forEach(function (a) { parts.push(a.activityName); if (a.details) parts.push(a.details); });\n    (student.attendanceRecords || []).forEach(function (r) {\n      if (r.reason) parts.push(r.reason);\n      if (r.type) parts.push(r.type);\n    });\n    student.surveys.forEach(function (sv) {\n      parts.push(sv.surveyTitle);
1\n      sv.answers.forEach(function (qa) { parts.push(qa.question); parts.push(qa.answer); });\n    });\n    student.admissionPlans.forEach(function (p) {\n      parts.push(p.universityName); parts.push(p.department);\n      if (p.memo) parts.push(p.memo);\n    });\n    return parts.join(' \\n ').toLowerCase();\n  }\n\n  var rosterButtons = [];\n  var allSearchButtons = [];\n  var sidebarGroups = [];\n  function allButtons() { return rosterButtons.concat(allSearchButtons); }\n\n  function makeStudentButton(student) {\n    var btn = document.createElement('button');\n    btn.type = 'button';\n    btn.className = 'yb-student-btn';\n    btn.textContent = student.number + '번 ' + student.name;\n    btn.dataset.studentId = student.id;\n    btn.dataset.blob = searchBlob(student);\n    var query = searchInput.value.trim().toLowerCase();\n    if (query && btn.dataset.blob.indexOf(query) === -1) btn.classList.add('yb-hidden');\n    btn.addEventListener('click', function () {\n      renderStudent(student);\n      setActive(btn);\n      var mark = main.querySelector('mark');\n      if (mark && mark.scrollIntoView) mark.scrollIntoView({ block: 'center' });\n    });\n    return btn;\n  }\n\n  var workNotes = data.workNotes || [];\n  var scheduleEvents = data.scheduleEvents || [];\n  var homeroomGroup = null;\n  var folderGroups = [];\n  function isHomeroomLabel(label) {\n    return label === '담임 학급' || label === '담임학급';\n  }\n  data.groups.forEach(function (group) {\n    if (isHomeroomLabel(group.label) && !homeroomGroup) homeroomGroup = group;\n    else folderGroups.push(group);\n  });\n\n  function appendStudentGroup(parent, group, open) {\n    var details = document.createElement('details');\n    details.className = 'yb-group';\n    if (open) details.open = true;\n    var summary = document.createElement('summary');\n    summary.textContent = group.label + ' (' + group.studentIds.length + ')';\n    details.appendChild(summary);\n    group.studentIds.forEach(function (id) {\n      var student = studentsById[id];\n      if (!student) return;\n      var btn = makeStudentButton(student);\n      details.appendChild(btn);\n      rosterButtons.push(btn);\n    });\n    parent.appendChild(details);\n    return details;\n  }\n\n  if (homeroomGroup) {\n    sidebarGroups.push(appendStudentGroup(sidebar, homeroomGroup, true));\n  }\n  if (folderGroups.length) {\n    var folder = document.createElement('details');\n    folder.className = 'yb-group yb-folder';\n    folder.open = !homeroomGroup;\n    var folderSummary = document.createElement('summary');\n    folderSummary.textContent = '교과 학급';\n    folder.appendChild(folderSummary);\n    folderGroups.forEach(function (group) {\n      appendStudentGroup(folder, group, false);\n    });\n    sidebar.appendChild(folder);\n    sidebarGroups.push(folder);\n  }\n\n  function countType(student, type) {\n    var list = student.attendanceSummary || [];\n    for (var i = 0; i < list.length; i++) if (list[i].type === type) return list[i].count;\n    return 0;\n  }\n  function hasSeteuk(student) {\n    var i;\n    for (i = 0; i < student.seteuk.length; i++) if (student.seteuk[i].draft) return true;\n    for (i = 0; i < student.performance.length; i++) if (student.performance[i].draft) return true;\n    return false;\n  }\n  function sortForRoster(list) {\n    return list.slice().sort(function (a, b) { return a.number - b.number; });\n  }\n  function attendanceLabel(student) {\n    var parts = [];\n    ['결석', '지각', '조퇴', '결과'].forEach(function (type) {\n      var n = countType(student, type);\n      if (n) parts.push(type + ' ' + n);\n    });\n    return parts.length ? parts.join(' \xb7 ') : '—';\n  }\n  function hasPrintBody(student) {\n    if (student.counseling.length) return true;\n    if (hasSeteuk(student)) return true;\n    if (student.behaviorDraft) return true;\n    if (student.overallOpinion) return true;\n    if (student.memo) return true;\n    if (student.careerCategory) return true;\n    if (student.targetUniversityName) return true;\n    if ((student.attendanceSummary || []).length) return true;\n    var lr = student.lifeRecord;\n    if (lr && (lr.summary || (lr.strengths && lr.strengths.length) || (lr.weaknesses && lr.weaknesses.length) || (lr.behaviorKeywords && lr.behaviorKeywords.length))) return true;\n    return false;\n  }\n  function chartWorthDrawing(chart) {\n    if (!chart || !chart.series || !chart.series.length || !c
1hart.xLabels || chart.xLabels.length < 2) return false;\n    var n = 0;\n    chart.series.forEach(function (s) {\n      (s.values || []).forEach(function (v) { if (v != null) n += 1; });\n    });\n    return n >= 2;\n  }\n  function snippetFor(student, query) {\n    var texts = [];\n    student.counseling.forEach(function (c) { texts.push(c.content); if (c.actionItems) texts.push(c.actionItems); });\n    student.seteuk.forEach(function (r) { if (r.draft) texts.push(r.draft); });\n    if (student.behaviorDraft) texts.push(student.behaviorDraft);\n    if (student.memo) texts.push(student.memo);\n    if (student.lifeRecord && student.lifeRecord.summary) texts.push(student.lifeRecord.summary);\n    for (var t = 0; t < texts.length; t++) {\n      var lower = texts[t].toLowerCase();\n      var at = lower.indexOf(query);\n      if (at === -1) continue;\n      var from = Math.max(0, at - 12);\n      var to = Math.min(texts[t].length, at + query.length + 20);\n      var cut = texts[t].slice(from, to);\n      if (from > 0) cut = '…' + cut;\n      if (to < texts[t].length) cut += '…';\n      return cut;\n    }\n    return '';\n  }\n  function studentsForIds(ids) {\n    var list = [];\n    ids.forEach(function (id) {\n      var s = studentsById[id];\n      if (s) list.push(s);\n    });\n    return list;\n  }\n  function buildRosterTable(students, asButtons) {\n    var table = document.createElement('table');\n    table.className = 'yb-table';\n    var head = document.createElement('thead');\n    var hr = document.createElement('tr');\n    ['번호', '이름', '상담', '출결', '세특'].forEach(function (h) {\n      var th = document.createElement('th');\n      th.textContent = h;\n      hr.appendChild(th);\n    });\n    head.appendChild(hr);\n    table.appendChild(head);\n    var body = document.createElement('tbody');\n    sortForRoster(students).forEach(function (student) {\n      var tr = document.createElement('tr');\n      function cell(text) {\n        var td = document.createElement('td');\n        td.textContent = text;\n        tr.appendChild(td);\n        return td;\n      }\n      cell(String(student.number));\n      var nameTd = cell('');\n      if (asButtons) {\n        var nameBtn = document.createElement('button');\n        nameBtn.type = 'button';\n        nameBtn.className = 'yb-student-btn';\n        nameBtn.textContent = student.name;\n        nameBtn.addEventListener('click', function () {\n          renderStudent(student);\n          var match = rosterButtons.filter(function (b) { return b.dataset.studentId === student.id; })[0];\n          if (match) setActive(match);\n        });\n        nameTd.appendChild(nameBtn);\n      } else {\n        nameTd.textContent = student.name;\n      }\n      cell(String(student.counseling.length));\n      cell(attendanceLabel(student));\n      cell(hasSeteuk(student) ? '있음' : '—');\n      body.appendChild(tr);\n    });\n    table.appendChild(body);\n    return table;\n  }\n  function buildRoster(asButtons) {\n    var wrap = document.createElement('div');\n    function addGroup(label, ids, headingTag) {\n      var list = studentsForIds(ids);\n      if (!list.length) return;\n      var heading = document.createElement(headingTag || 'h3');\n      heading.className = 'yb-roster-h';\n      heading.textContent = label;\n      wrap.appendChild(heading);\n      wrap.appendChild(buildRosterTable(list, asButtons));\n    }\n    if (homeroomGroup) addGroup(data.meta.homeroomLabel || homeroomGroup.label, homeroomGroup.studentIds, 'h3');\n    if (folderGroups.length) {\n      var subHead = document.createElement('h3');\n      subHead.className = 'yb-roster-h';\n      subHead.textContent = '교과 학급';\n      wrap.appendChild(subHead);\n      folderGroups.forEach(function (group) { addGroup(group.label, group.studentIds, 'h4'); });\n    }\n    var seen = {};\n    data.groups.forEach(function (g) {\n      g.studentIds.forEach(function (id) { seen[id] = true; });\n    });\n    var orphanIds = [];\n    data.students.forEach(function (s) { if (!seen[s.id]) orphanIds.push(s.id); });\n    if (orphanIds.length) addGroup('그 외', orphanIds);\n    return wrap;\n  }\n  function fillCover(container, asButtons) {\n    while (container.firstChild) container.removeChild(container.firstChild);\n    var title = document.createElement('h1');\n    title.textContent = data.meta.academicYearLabel + ' 교무수첩';\n    container.appendChild(title);\n    var metaLine = document.createElement('div');\n    metaLine.className = 'yb-home-meta';\n    metaLine.textContent = runningLabel() + ' \xb7 학생 ' + data.meta.studentCount + '명';\n    if (data.meta.generatedAt) metaLine.textContent += ' \xb7 만든 날 ' + ymd(data.meta.generatedAt);\n    container.appendChild(metaLine);\n    var rosterHeading = document.createElement('h2');\n    rosterHeading.textContent = '학급 총람';\n    container.appendChild(rosterHeading);\n    container.appendChild(buildRoster(asButtons));\n  }\n  fillCover(printCover, false);\n  var currentView = 'home';\n  function rosterIcon() {\n    var ns = 'http://www.w3.org/2000/svg';\n    var svg = document.createElementNS(ns, 'svg');\n    svg.setAttribute('viewBox', '0 0 16 16');\n    svg.setAttribute('aria-hidden', 'true');\n    svg.setAttribute('focusable', 'false');\n    var g = document.createElementNS(ns, 'g');\n    g.setAttribute('fill', 'none');\n    g.setAttribute('stroke', 'currentColor');\n    g.setAttribute('stroke-width', '1.25');\n    g.setAttribute('stroke-linejoin', 'round');\n    var box = document.createElementNS(ns, 'rect');\n    box.setAttribute('x', '2');\n    box.setAttribute('y', '2');\n    box.setAttribute('width', '12');\n    box.setAttribute('height', '12');\n    box.setAttribute('rx', '1.5');\n    g.appendChild(box);\n    ['M2 6h12', 'M2 10h12', 'M6 6v8'].forEach(function (d) {\n      var p = document.createElementNS(ns, 'path');\n      p.setAttribute('d', d);\n      g.appendChild(p);\n    });\n    svg.appendChild(g);\n    return svg;\n  }\n  var homeBtn = document.createElement('button');\n  homeBtn.type = 'button';\n  homeBtn.className = 'yb-student-btn yb-home-btn yb-nav-home';\n  homeBtn.appendChild(rosterIcon());\n  var homeLabel = document.createElement('span');\n  homeLabel.textContent = '학급 총람';\n  homeBtn.appendChild(homeLabel);\n  var notesBtn = document.createElement('button');\n  notesBtn.type = 'button';\n  notesBtn.className = 'yb-student-btn yb-home-btn';\n  notesBtn.textContent = '업무 노트';\n  var scheduleBtn = document.createElement('button');\n  scheduleBtn.type = 'button';\n  scheduleBtn.className = 'yb-student-btn yb-home-btn';\n  scheduleBtn.textContent = '일정';
1\n  function navButtons() { return [homeBtn, notesBtn, scheduleBtn]; }\n  function showHome() {\n    currentStudent = null;\n    currentView = 'home';\n    main.innerHTML = '';\n    var home = document.createElement('div');\n    home.className = 'yb-home';\n    fillCover(home, true);\n    main.appendChild(home);\n    setActive(homeBtn);\n  }\n  function renderNotes(append) {\n    if (!append) {\n      main.innerHTML = '';\n      currentStudent = null;\n      currentView = 'notes';\n    }\n    var page = document.createElement('div');\n    page.className = 'yb-student-page';\n    var running = document.createElement('div');\n    running.className = 'yb-running';\n    running.textContent = runningLabel();\n    page.appendChild(running);\n    var header = document.createElement('h1');\n    header.textContent = '업무 노트';\n    page.appendChild(header);\n    if (!workNotes.length) {\n      var notesEmpty = document.createElement('p');\n      notesEmpty.className = 'yb-empty';\n      notesEmpty.textContent = '작성한 업무 노트가 없습니다.';\n      page.appendChild(notesEmpty);\n    }\n    workNotes.forEach(function (note) {\n      var block = document.createElement('div');\n      block.className = 'yb-note';\n      var title = document.createElement('h2');\n      title.className = 'yb-note-title';\n      title.textContent = note.title || '제목 없는 노트';\n      block.appendChild(title);\n      var dateLine = document.createElement('div');\n      dateLine.className = 'yb-note-date';\n      dateLine.textContent = (note.isPinned ? '고정 \xb7 ' : '') + '고친 날 ' + ymd(note.updatedAt || note.createdAt);\n      block.appendChild(dateLine);\n      if (note.content) {\n        var body = document.createElement('div');\n        body.className = 'yb-prose';\n        renderTextWithHighlight(body, note.content);\n        block.appendChild(body);\n      }\n      page.appendChild(block);\n    });\n    main.appendChild(page);\n    if (!append) setActive(notesBtn);\n  }\n  function monthLabel(dateStr) {\n    if (!dateStr || String(dateStr).length < 7) return '메모';\n    var y = String(dateStr).slice(0, 4);\n    var m = parseInt(String(dateStr).slice(5, 7), 10);\n    if (!y || !m) return '메모';\n    return y + '년 ' + m + '월';\n  }
1\n  function renderSchedule(append) {\n    if (!append) {\n      main.innerHTML = '';\n      currentStudent = null;\n      currentView = 'schedule';\n    }\n    var page = document.createElement('div');\n    page.className = 'yb-student-page';\n    var running = document.createElement('div');\n    running.className = 'yb-running';\n    running.textContent = runningLabel();\n    page.appendChild(running);\n    var header = document.createElement('h1');\n    header.textContent = '일정';\n    page.appendChild(header);\n    if (!scheduleEvents.length) {\n      var scheduleEmpty = document.createElement('p');\n      scheduleEmpty.className = 'yb-empty';\n      scheduleEmpty.textContent = '기록된 일정이 없습니다.';\n      page.appendChild(scheduleEmpty);\n    }\n    var dated = [];\n    var undated = [];\n    scheduleEvents.forEach(function (ev) {\n      if (ev.date) dated.push(ev);\n      else undated.push(ev);\n    });\n    dated.sort(function (a, b) { return a.date < b.date ? -1 : a.date > b.date ? 1 : 0; });\n    var lastMonth = null;\n    dated.concat(undated).forEach(function (ev) {\n      var month = monthLabel(ev.date);\n      if (month !== lastMonth) {\n        lastMonth = month;\n        var mh = document.createElement('h2');\n        mh.className = 'yb-month';\n        mh.textContent = month;\n        page.appendChild(mh);\n      }\n      var titleEl = document.createElement('div');\n      renderTextWithHighlight(titleEl, ev.title);\n      if (!ev.date) {\n        titleEl.style.marginBottom = '8px';\n        page.appendChild(titleEl);\n        return;\n      }\n      var row = document.createElement('div');\n      row.className = 'yb-event';\n      var dateEl = document.createElement('div');\n      dateEl.className = 'yb-event-date';\n      var dateText = ymd(ev.date);\n      if (ev.endDate && ev.endDate !== ev.date) dateText += ' ~ ' + ymd(ev.endDate);\n      dateEl.textContent = dateText;\n      row.appendChild(dateEl);\n      row.appendChild(titleEl);\n      page.appendChild(row);\n    });\n    main.appendChild(page);\n    if (!append) setActive(scheduleBtn);\n  }\n  homeBtn.addEventListener('click', showHome);\n  notesBtn.addEventListener('click', function () { renderNotes(); });\n  scheduleBtn.addEventListener('click', function () { renderSchedule(); });\n  sidebar.insertBefore(homeBtn, searchLabel);\n  sidebar.insertBefore(notesBtn, searchLabel);\n  sidebar.insertBefore(scheduleBtn, searchLabel);\n\n  var searchResults = document.createElement('div');\n  searchResults.id = 'search-results';\n  searchResults.style.display = 'none';\n  sidebar.appendChild(searchResults);\n\n  function setActive(activeBtn) {\n    allButtons().concat(navButtons()).forEach(function (b) { b.classList.toggle('active', b === activeBtn); });\n  }\n\n  searchInput.addEventListener('input', function () {\n    var query = searchInput.value.trim().toLowerCase();\n    if (!query) {\n      searchResults.style.display = 'none';\n      searchEmpty.hidden = true;\n      sidebarGroups.forEach(function (el) { el.style.display = ''; });\n      navButtons().forEach(function (b) { b.style.display = ''; });\n      return;\n    }\n    sidebarGroups.forEach(function (el) { el.style.display = 'none'; });\n    navButtons().forEach(function (b) { b.style.display = 'none'; });\n    searchResults.style.display = 'block';\n    while (searchResults.firstChild) searchResults.removeChild(searchResults.firstChild);\n    allSearchButtons = [];\n    var shown = 0;\n    data.students.forEach(function (student) {\n      if (searchBlob(student).indexOf(query) === -1) return;\n      shown += 1;\n      var hitBtn = makeStudentButton(student);\n      searchResults.appendChild(hitBtn);\n      allSearchButtons.push(hitBtn);\n      var sn = snippetFor(student, query);\n      if (sn) {\n        var snLine = document.createElement('div');\n        snLine.className = 'yb-snippet';\n        snLine.textContent = sn;\n        searchResults.appendChild(snLine);\n      }\n    });\n    workNotes.forEach(function (note) {\n      var noteBlob = ((note.title || '') + ' \\n ' + (note.content || '')).toLowerCase();\n      if (noteBlob.indexOf(query) === -1) return;\n      shown += 1;\n      var noteBtn = document.createElement('button');\n      noteBtn.type = 'button';\n      noteBtn.className = 'yb-student-btn';\n      noteBtn.textContent = '업무 노트 \xb7 ' + (note.title || '제목 없는 노트');\n      noteBtn.addEventListener('click', function () {\n        renderNotes();\n        var mark = main.querySelector('mark');\n        if (mark && mark.scrollIntoView) mark.scrollIntoView({ block: 'center' });\n      });\n      searchResults.appendChild(noteBtn);\n      allSearchButtons.push(noteBtn);\n    });
1\n    scheduleEvents.forEach(function (ev) {\n      var evBlob = ((ev.title || '') + ' \\n ' + (ev.date || '') + ' \\n ' + (ev.endDate || '')).toLowerCase();\n      if (evBlob.indexOf(query) === -1) return;\n      shown += 1;\n      var evBtn = document.createElement('button');\n      evBtn.type = 'button';\n      evBtn.className = 'yb-student-btn';\n      evBtn.textContent = (ev.date ? ymd(ev.date) + ' \xb7 ' : '') + ev.title;\n      evBtn.addEventListener('click', function () {\n        renderSchedule();\n        var mark = main.querySelector('mark');\n        if (mark && mark.scrollIntoView) mark.scrollIntoView({ block: 'center' });\n      });\n      searchResults.appendChild(evBtn);\n      allSearchButtons.push(evBtn);\n    });\n    searchEmpty.hidden = shown !== 0;\n  });\n\n  function renderTextWithHighlight(container, text) {\n    var query = searchInput.value.trim().toLowerCase();\n    if (!query || text.toLowerCase().indexOf(query) === -1) {\n      container.appendChild(document.createTextNode(text));\n      return;\n    }\n    var lowerText = text.toLowerCase();\n    var start = 0;\n    var idx;\n    while ((idx = lowerText.indexOf(query, start)) !== -1) {\n      container.appendChild(document.createTextNode(text.slice(start, idx)));\n      var mark = document.createElement('mark');\n      mark.textContent = text.slice(idx, idx + query.length);\n      container.appendChild(mark);\n      start = idx + query.length;\n    }\n    container.appendChild(document.createTextNode(text.slice(start)));\n  }\n\n  function section(title) {\n    var wrap = document.createElement('div');\n    wrap.className = 'yb-section';\n    var h2 = document.createElement('h2');\n    h2.textContent = title;\n    wrap.appendChild(h2);\n    return wrap;\n  }\n\n  function table(headers, rows) {\n    var t = document.createElement('table');\n    t.className = 'yb-table';\n    var thead = document.createElement('thead');\n    var headRow = document.createElement('tr');\n    headers.forEach(function (h) {\n      var th = document.createElement('th');\n      th.textContent = h;\n      headRow.appendChild(th);\n    });\n    thead.appendChild(headRow);\n    t.appendChild(thead);\n    var tbody = document.createElement('tbody');\n    rows.forEach(function (cells) {\n      var tr = document.createElement('tr');\n      cells.forEach(function (cell) {\n        var td = document.createElement('td');\n        td.textContent = cell === null || cell === undefined ? '' : String(cell);\n        tr.appendChild(td);\n      });\n      tbody.appendChild(tr);\n    });\n    t.appendChild(tbody);\n    return t;\n  }\n\n  function collapsibleTable(headers, rows) {\n    var details = document.createElement('details');\n    details.className = 'yb-table-details';\n    var summary = document.createElement('summary');\n    function updateSummaryText() {\n      summary.textContent = (details.open ? '표 접기' : '표 펼치기') + ' (' + rows.length + '건)';\n    }\n    updateSummaryText();\n    details.addEventListener('toggle', updateSummaryText);\n    details.appendChild(summary);\n    details.appendChild(table(headers, rows));\n    return details;\n  }\n\n  function maybeTable(headers, rows) {\n    if (rows.length <= 8) return table(headers, rows);\n    return collapsibleTable(headers, rows);\n  }\n\n  var CHART_COLORS = ['#1c1917', '#9a3412', '#1e3a5f', '#57534e', '#3f6212'];\n  var SVG_NS = 'http://www.w3.org/2000/svg';\n\n  var sharedTooltip = null;\n  function getTooltip() {\n    if (!sharedTooltip) {\n      sharedTooltip = document.createElement('div');\n      sharedTooltip.className = 'yb-tooltip';\n      document.body.appendChild(sharedTooltip);\n    }\n    return sharedTooltip;\n  }\n\n  function smoothPath(points) {\n    if (points.length < 2) return '';\n    if (points.length === 2) {\n      return 'M' + points[0][0] + ',' + points[0][1] + ' L' + points[1][0] + ',' + points[1][1];\n    }\n    var d = 'M' + points[0][0] + ',' + points[0][1];\n    for (var i = 0; i < points.length - 1; i++) {\n      var p0 = points[i - 1] || points[i];\n      var p1 = points[i];\n      var p2 = points[i + 1];\n      var p3 = points[i + 2] || p2;\n      var cp1x = p1[0] + (p2[0] - p0[0]) / 6;\n      var cp1y = p1[1] + (p2[1] - p0[1]) / 6;\n      var cp2x = p2[0] - (p3[0] - p1[0]) / 6;\n      var cp2y = p2[1] - (p3[1] - p1[1]) / 6;\n      d += ' C' + cp1x + ',' + cp1y + ' ' + cp2x + ',' + cp2y + ' ' + p2[0] + ',' + p2[1];\n    }\n    return d;\n  }\n\n  function lineChart(chart) {\n    var wrap = document.createElement('div');\n    wrap.className = 'yb-chart';\n    if (!chartWorthDrawing(chart)) return wrap;\n\n    var width = 560, height = 220;\n    var padLeft = 26, padRight = 14, padTop = 12, padBottom = 30;\n    var plotW = width - padLeft - padRight;\n    var plotH = height - padTop - padBottom;\n    var n = chart.xLabels.length;\n\n    function xPos(i) { return padLeft + (n <= 1 ? plotW / 2 : (i * plotW) / (n - 1)); }\n    function yPos(grade) { return padTop + ((grade - 1) * plotH) / 8; }\n\n    var svg = document.createElementNS(SVG_NS, 'svg');\n    svg.setAttribute('viewBox', '0 0 ' + width + ' ' + height);\n    svg.setAttribute('width', '100%');\n\n    [1, 3, 5, 7, 9].forEach(function (g) {\n      var y = yPos(g);\n      var gridLine = document.createElementNS(SVG_NS, 'line');\n      gridLine.setAttribute('x1', String(padLeft));\n      gridLine.setAttribute('x2', String(width - padRight));\n      gridLine.setAttribute('y1', String(y));
1\n      gridLine.setAttribute('y2', String(y));\n      gridLine.setAttribute('stroke', g === 9 ? '#d6d3d1' : '#eeedeb');\n      svg.appendChild(gridLine);\n      var gridLabel = document.createElementNS(SVG_NS, 'text');\n      gridLabel.setAttribute('x', String(padLeft - 8));\n      gridLabel.setAttribute('y', String(y + 3));\n      gridLabel.setAttribute('text-anchor', 'end');\n      gridLabel.setAttribute('font-size', '10');\n      gridLabel.setAttribute('fill', '#a8a29e');\n      gridLabel.textContent = String(g);\n      svg.appendChild(gridLabel);\n    });\n\n    var axisLine = document.createElementNS(SVG_NS, 'line');\n    axisLine.setAttribute('x1', String(padLeft));\n    axisLine.setAttribute('x2', String(padLeft));\n    axisLine.setAttribute('y1', String(padTop));\n    axisLine.setAttribute('y2', String(height - padBottom));\n    axisLine.setAttribute('stroke', '#d6d3d1');\n    svg.appendChild(axisLine);\n\n    chart.xLabels.forEach(function (label, i) {\n      var xLabel = document.createElementNS(SVG_NS, 'text');\n      xLabel.setAttribute('x', String(xPos(i)));\n      xLabel.setAttribute('y', String(height - padBottom + 16));\n      xLabel.setAttribute('text-anchor', 'middle');\n      xLabel.setAttribute('font-size', '10');\n      xLabel.setAttribute('fill', '#a8a29e');\n      xLabel.textContent = label;\n      svg.appendChild(xLabel);\n    });\n\n    var legend = document.createElement('div');\n    legend.className = 'yb-legend';\n    var groups = [];\n\n    chart.series.forEach(function (s, idx) {\n      var color = CHART_COLORS[idx % CHART_COLORS.length];\n      var group = document.createElementNS(SVG_NS, 'g');\n      group.setAttribute('class', 'yb-chart-series');\n      groups.push(group);\n\n      var runs = [];\n      var current = [];\n      s.values.forEach(function (v, i) {\n        if (v == null) {\n          if (current.length) { runs.push(current); current = []; }\n          return;\n        }\n        current.push([xPos(i), yPos(v)]);\n      });\n      if (current.length) runs.push(current);\n      runs.forEach(function (run) {\n        var path = document.createElementNS(SVG_NS, 'path');\n        path.setAttribute('d', smoothPath(run));\n        path.setAttribute('fill', 'none');\n        path.setAttribute('stroke', color);\n        path.setAttribute('stroke-width', '2.5');\n        path.setAttribute('stroke-linecap', 'round');\n        path.setAttribute('stroke-linejoin', 'round');\n        group.appendChild(path);\n      });\n      s.values.forEach(function (v, i) {\n        if (v == null) return;\n        var circle = document.createElementNS(SVG_NS, 'circle');\n        circle.setAttribute('cx', String(xPos(i)));\n        circle.setAttribute('cy', String(yPos(v)));\n        circle.setAttribute('r', '3');\n        circle.setAttribute('fill', '#fff');\n        circle.setAttribute('stroke', color);\n        circle.setAttribute('stroke-width', '2');\n        circle.addEventListener('mouseenter', function (e) {\n          circle.setAttribute('r', '5.5');\n          var tip = getTooltip();\n          tip.textContent = s.label + ' \xb7 ' + chart.xLabels[i] + ' \xb7 ' + v + '등급';\n          tip.style.left = (e.clientX + 12) + 'px';\n          tip.style.top = (e.clientY - 10) + 'px';\n          tip.style.display = 'block';\n        });\n        circle.addEventListener('mousemove', function (e) {\n          var tip = getTooltip();\n          tip.style.left = (e.clientX + 12) + 'px';\n          tip.style.top = (e.clientY - 10) + 'px';\n        });\n        circle.addEventListener('mouseleave', function () {\n          circle.setAttribute('r', '3');\n          getTooltip().style.display = 'none';\n        });\n        group.appendChild(circle);\n      });\n      svg.appendChild(group);\n\n      var item = document.createElement('button');\n      item.type = 'button';\n      item.className = 'yb-legend-item';\n      var swatch = document.createElement('span');\n      swatch.className = 'yb-legend-swatch';\n      swatch.style.background = color;\n      item.appendChild(swatch);\n      var labelText = document.createElement('span');\n      labelText.textContent = s.label;\n      item.appendChild(labelText);\n      item.addEventListener('click', function () {\n        var off = group.style.display === 'none';\n        group.style.display = off ? '' : 'none';\n        item.classList.toggle('is-off', !off);\n      });\n      item.addEventListener('mouseenter', function () {\n        groups.forEach(function (g, gi) { g.style.opacity = gi === idx ? '1' : '0.15'; });\n      });\n      item.addEventListener('mouseleave', function () {\n        groups.forEach(function (g) { g.style.opacity = ''; });\n      });\n      legend.appendChild(item);\n    });\n\n    wrap.appendChild(svg);\n    wrap.appendChild(legend);\n    return wrap;\n  }\n\n  var currentStudent = null;
1\n  function renderStudent(student, append) {\n    if (!append) {\n      main.innerHTML = '';\n      currentStudent = student;\n      currentView = 'student';\n    }\n    var page = document.createElement('div');\n    page.className = 'yb-student-page';\n    var running = document.createElement('div');\n    running.className = 'yb-running';\n    running.textContent = runningLabel();\n    page.appendChild(running);\n\n    var header = document.createElement('h1');\n    var classInfo = (student.grade != null && student.classNum != null)\n      ? student.grade + '학년 ' + student.classNum + '반 ' : '';\n    header.textContent = classInfo + student.number + '번 ' + student.name;\n    page.appendChild(header);\n\n    var infoRows = [];\n    if (student.careerCategory) infoRows.push(['진로', student.careerCategory]);\n    if (student.targetUniversityName) {\n      var extras = [];\n      if (student.targetAdmissionType) extras.push(student.targetAdmissionType);\n      if (student.targetDetailType) extras.push(student.targetDetailType);\n      var target = student.targetUniversityName + ' ' + (student.targetDepartment || '');\n      if (extras.length) target += ' \xb7 ' + extras.join(' ');\n      infoRows.push(['진학', target]);\n    }\n    if (student.memo) infoRows.push(['메모', student.memo]);\n    if (infoRows.length) {\n      var info = section('기본정보');\n      var dl = document.createElement('dl');\n      dl.className = 'yb-kv';\n      infoRows.forEach(function (row) {\n        var dt = document.createElement('dt');\n        dt.textContent = row[0];\n        var dd = document.createElement('dd');\n        dd.className = 'yb-prose';\n        dd.textContent = row[1];\n        dl.appendChild(dt);\n        dl.appendChild(dd);\n      });\n      info.appendChild(dl);\n      page.appendChild(info);\n    }\n\n    if (student.counseling.length) {\n      var counselingSection = section('상담 기록');\n      student.counseling.forEach(function (c) {\n        var entry = document.createElement('div');\n        entry.className = 'yb-counsel';\n        var dateLine = document.createElement('div');\n        dateLine.className = 'yb-counsel-date';\n        dateLine.textContent = ymd(c.date) + (c.category ? ' \xb7 ' + c.category : '');\n        entry.appendChild(dateLine);\n        var body = document.createElement('div');\n        var contentLine = document.createElement('div');\n        contentLine.className = 'yb-prose';\n        renderTextWithHighlight(contentLine, c.content);\n        body.appendChild(contentLine);\n        if (c.actionItems) {\n          var actionLine = document.createElement('div');\n          actionLine.className = 'yb-counsel-action';\n          renderTextWithHighlight(actionLine, '후속: ' + c.actionItems);\n          body.appendChild(actionLine);\n        }\n        entry.appendChild(body);\n        counselingSection.appendChild(entry);\n      });\n      page.appendChild(counselingSection);\n    }\n\n    if (student.grades.length) {\n      var gradeSection = section('내신');\n      gradeSection.classList.add('yb-no-print');\n      var gradeTable = maybeTable(\n        ['학년', '학기', '구분', '과목', '원점수', '평균', '성취도', '등급', '백분위'],\n        student.grades.map(function (g) {\n          return [g.year, g.semester, g.examType, g.subjectName, g.rawScore, g.subjectAverage, g.achievement, g.gradeLevel, g.percentile];\n        })\n      );\n      gradeTable.classList.add('yb-no-print');\n      gradeSection.appendChild(gradeTable);\n      if (chartWorthDrawing(student.gradeChart)) gradeSection.appendChild(lineChart(student.gradeChart));\n      page.appendChild(gradeSection);\n    }\n\n    if (student.mockExams.length) {\n      var mockSection = section('모의고사');\n      mockSection.classList.add('yb-no-print');\n      var mockTable = maybeTable(\n        ['연도', '월', '과목', '표준점수', '등급', '백분위'],\n        student.mockExams.map(function (m) {\n          return [m.year, m.month, m.subject, m.standardScore, m.gradeLevel, m.percentile];\n        })\n      );\n      mockTable.classList.add('yb-no-print');\n      mockSection.appendChild(mockTable);\n      if (chartWorthDrawing(student.mockExamChart)) mockSection.appendChild(lineChart(student.mockExamChart));\n      page.appendChild(mockSection);\n    }\n\n    var seteukEntries = student.seteuk.map(function (r) {\n      return { kind: '세특', className: r.className, draft: r.draft, attitude: r.attitude, sources: r.sources };\n    }).concat(student.performance.map(function (r) {\n      return { kind: '수행', className: r.className, draft: r.draft, attitude: r.attitude, sources: r.sources };\n    })).filter(function (e) { return e.draft; });\n    if (seteukEntries.length) {\n      var seteukSection = section('세특 \xb7 수행');
1\n      seteukEntries.forEach(function (e) {\n        var block = document.createElement('div');\n        block.style.marginBottom = '10px';\n        var label = document.createElement('div');\n        label.style.fontSize = '12px';\n        label.style.fontWeight = '600';\n        label.style.color = '#78716c';\n        var extra = [];\n        if (e.attitude) extra.push(e.attitude);\n        if (e.sources) extra.push(e.sources);\n        label.textContent = e.className + ' \xb7 ' + e.kind + (extra.length ? ' \xb7 ' + extra.join(' \xb7 ') : '');\n        block.appendChild(label);\n        var p = document.createElement('div');\n        p.className = 'yb-prose';\n        renderTextWithHighlight(p, e.draft);\n        block.appendChild(p);\n        seteukSection.appendChild(block);\n      });\n      page.appendChild(seteukSection);\n    }\n\n    if (student.activities.length) {\n      var activitySection = section('활동');\n      activitySection.classList.add('yb-no-print');\n      student.activities.forEach(function (a) {\n        var block = document.createElement('div');\n        block.style.marginBottom = '10px';\n        var title = document.createElement('div');\n        title.style.fontWeight = '600';\n        title.textContent = '[' + a.category + '] ' + a.activityName;\n        block.appendChild(title);\n        if (a.details) {\n          var details = document.createElement('div');\n          renderTextWithHighlight(details, a.details);\n          block.appendChild(details);\n        }\n        a.attachments.forEach(function (att) {\n          var attLine = document.createElement('div');\n          attLine.style.fontSize = '12px';\n          attLine.style.color = '#78716c';\n          attLine.textContent = '첨부: ' + att.fileName + (att.extractedText ? ' — ' + att.extractedText : '');\n          block.appendChild(attLine);\n        });\n        activitySection.appendChild(block);\n      });\n      page.appendChild(activitySection);\n    }\n\n    var attendanceRecords = student.attendanceRecords || [];\n    if (student.attendanceSummary.length || attendanceRecords.length) {\n      var attendanceSection = section('출결');\n      if (student.attendanceSummary.length) {\n        var summaryLine = document.createElement('div');\n        summaryLine.textContent = student.attendanceSummary\n          .map(function (a) { return a.type + ' ' + a.count; })\n          .join(' \xb7 ');\n        attendanceSection.appendChild(summaryLine);\n      }\n      if (attendanceRecords.length) {\n        var attTable = maybeTable(\n          ['날짜', '구분', '사유', '교시'],\n          attendanceRecords.map(function (r) {\n            var date = ymd(r.date);\n            if (r.endDate && r.endDate !== r.date) date += ' ~ ' + ymd(r.endDate);\n            return [date, r.type, r.reason, r.period];\n          })\n        );\n        attTable.classList.add('yb-no-print');\n        attendanceSection.appendChild(attTable);\n      }\n      page.appendChild(attendanceSection);\n    }\n\n    if (student.surveys.length) {\n      var surveySection = section('설문');\n      surveySection.classList.add('yb-no-print');\n      student.surveys.forEach(function (sv) {\n        var svTitle = document.createElement('div');\n        svTitle.style.fontWeight = '600';\n        svTitle.textContent = sv.surveyTitle;\n        surveySection.appendChild(svTitle);\n        sv.answers.forEach(function (qa) {\n          var qaLine = document.createElement('div');\n          qaLine.textContent = qa.question + ': ' + qa.answer;\n          surveySection.appendChild(qaLine);\n        });\n      });\n      page.appendChild(surveySection);\n    }\n\n    if (student.admissionPlans.length) {\n      var planSection = section('진학계획');\n      planSection.classList.add('yb-no-print');\n      student.admissionPlans.forEach(function (p) {\n        var line = document.createElement('div');\n        var bits = [];\n        if (p.priority != null) bits.push(p.priority + '순위');\n        bits.push(p.universityName);\n        bits.push(p.department);\n        bits.push(p.admissionType);
1\n        line.textContent = bits.filter(Boolean).join(' \xb7 ');\n        planSection.appendChild(line);\n        if (p.memo) {\n          var planMemo = document.createElement('div');\n          planMemo.style.fontSize = '12px';\n          planMemo.style.color = '#78716c';\n          planMemo.textContent = p.memo;\n          planSection.appendChild(planMemo);\n        }\n      });\n      page.appendChild(planSection);\n    }\n\n    if (student.lifeRecord) {\n      var lifeSection = section('생기부 요약');\n      if (student.lifeRecord.summary) {\n        var lifeSummary = document.createElement('div');\n        renderTextWithHighlight(lifeSummary, student.lifeRecord.summary);\n        lifeSection.appendChild(lifeSummary);\n      }\n      if (student.lifeRecord.strengths.length) {\n        var st = document.createElement('div');\n        st.textContent = '강점: ' + student.lifeRecord.strengths.join(' \xb7 ');\n        lifeSection.appendChild(st);\n      }\n      if (student.lifeRecord.weaknesses.length) {\n        var wk = document.createElement('div');\n        wk.textContent = '보완: ' + student.lifeRecord.weaknesses.join(' \xb7 ');\n        lifeSection.appendChild(wk);\n      }\n      if (student.lifeRecord.behaviorKeywords.length) {\n        var kw = document.createElement('div');\n        kw.textContent = '키워드: ' + student.lifeRecord.behaviorKeywords.join(' \xb7 ');\n        lifeSection.appendChild(kw);\n      }\n      page.appendChild(lifeSection);\n    }\n\n    if (student.behaviorDraft) {\n      var behaviorSection = section('행동특성');\n      var behavior = document.createElement('div');\n      behavior.className = 'yb-prose';\n      renderTextWithHighlight(behavior, student.behaviorDraft);\n      behaviorSection.appendChild(behavior);\n      page.appendChild(behaviorSection);\n    }\n\n    if (student.overallOpinion) {\n      var opinionSection = section('종합의견');\n      var opinion = document.createElement('div');\n      opinion.className = 'yb-prose';\n      renderTextWithHighlight(opinion, student.overallOpinion);\n      opinionSection.appendChild(opinion);\n      page.appendChild(opinionSection);\n    }\n\n    if (page.querySelectorAll('.yb-section').length === 0) {\n      var empty = document.createElement('p');\n      empty.className = 'yb-empty';\n      empty.textContent = '이 학생의 기록이 없습니다.';\n      page.appendChild(empty);\n    }\n\n    main.appendChild(page);\n  }\n\n  window.addEventListener('beforeprint', function () {\n    main.innerHTML = '';\n    data.students.forEach(function (s) { if (hasPrintBody(s)) renderStudent(s, true); });\n    if (workNotes.length) renderNotes(true);\n    if (scheduleEvents.length) renderSchedule(true);\n  });\n  window.addEventListener('afterprint', function () {\n    if (currentStudent) renderStudent(currentStudent);\n    else if (currentView === 'notes') renderNotes();\n    else if (currentView === 'schedule') renderSchedule();\n    else if (data.students.length) showHome();\n    else main.textContent = '보관된 학생이 없습니다.';\n  });\n\n  if (data.students.length) showHome();\n  else main.textContent = '보관된 학생이 없습니다.';\n})();</script>\n</body>\n</html>\n"}:{success:!1,error:a.error||"교무수첩을 만들지 못했습니다."}}n(90241)},76283:function(e,t,n){n.d(t,{v:function(){return m}});var r=n(2265),a=n(98319),i=n(10136),s=n(67920),l=n(4908),o=n(83008),d=n(54345),c=n(90241),u=n(15563);function m(){var e;let t=(0,s.XC)(),{showToast:n}=(0,a.p)(),m=(0,i.N)(),{reloadStudents:p}=(0,l.AF)(),[x,h]=(0,r.useState)(!1),[f,b]=(0,r.useState)(!1),[g,v]=(0,r.useState)("ask"),[y,k]=(0,r.useState)(null),w=async()=>{h(!0);try{let e=await (0,o.X)("resetAllStudentData",{confirmText:"초기화"},t);e.success?(n("모든 학생 데이터를 초기화했습니다.","success"),localStorage.removeItem("rosterWizardCompleted"),(0,u.X9)(),await p()):n(e.error||"초기화 실패","error")}catch(e){console.error(e),n("오류가 발생했습니다.","error")}finally{h(!1),b(!1),v("ask"),k(null)}},j=async()=>{h(!0);try{let e=await (0,d.F)(t);if(!e.success){n(e.error||"교무수첩을 만들지 못했습니다. 초기화를 진행하지 않았습니다.","error"),h(!1);return}if((0,c.NI)()){k({filename:e.filename,html:e.html}),v("save-file"),h(!1);return}await (0,c.pM)(e.filename,e.html),v("confirm-saved"),h(!1)}catch(e){if(console.error(e),e instanceof c.f_){n("저장을 취소했습니다. 초기화를 진행하지 않았습니다.","error"),h(!1);return}n("교무수첩을 만드는 중 오류가 발생했습니다. 초기화를 진행하지 않았습니다.","error"),h(!1)}},N=async()=>
1{if(y){h(!0);try{await (0,c.pM)(y.filename,y.html),v("confirm-saved")}catch(e){if(e instanceof c.f_){n("저장을 취소했습니다. 초기화를 진행하지 않았습니다.","error");return}console.error(e),n("교무수첩을 저장하지 못했습니다. 초기화를 진행하지 않았습니다.","error")}finally{h(!1)}}};return{isResetting:x,isResetModalOpen:f,resetStep:g,saveNeedsShareHint:(0,c.NI)(),openResetModal:()=>{v("ask"),k(null),b(!0)},closeResetModal:()=>{x||(b(!1),v("ask"),k(null))},performReset:w,handleSaveAndReset:j,handleSaveFile:N,handleConfirmSavedAndReset:w,handleResetOnly:async()=>{await m({title:"저장하지 않고 학생 기록을 지울까요?",description:"교무수첩 파일을 만들지 않습니다. 서버의 학생 기록은 되돌릴 수 없습니다.",consequences:["학생 명단","성적\xb7출결 기록","상담 일지","세특\xb7행동특성","설문과 응답"],confirmLabel:"초기화"})&&await w()},filename:null!==(e=null==y?void 0:y.filename)&&void 0!==e?e:null}}},73348:function(e,t,n){n.d(t,{Z:function(){return l}});var r=n(57437),a=n(2265),i=n(76865),s=n(34137);function l(e){let{open:t,title:n,description:l,consequences:o,confirmLabel:d="삭제",cancelLabel:c="취소",tone:u="danger",busy:m=!1,onConfirm:p,onCancel:x}=e,h="danger"===u,f=(0,a.useRef)(null),b=(0,a.useRef)(null),g=(0,a.useRef)(null);return((0,s.j)(x,t&&!m),(0,a.useEffect)(()=>{var e;if(t)return g.current=document.activeElement,null===(e=f.current)||void 0===e||e.focus(),()=>{var e,t;null===(t=g.current)||void 0===t||null===(e=t.focus)||void 0===e||e.call(t)}},[t]),t)?(0,r.jsx)("div",{className:"modal-viewport z-[200] flex items-center justify-center p-4 bg-black/50",onKeyDown:e=>{if("Tab"!==e.key||!b.current)return;let t=b.current.querySelectorAll('button:not([disabled]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(0===t.length)return;let n=t[0],r=t[t.length-1];e.shiftKey&&document.activeElement===n?(e.preventDefault(),r.focus()):e.shiftKey||document.activeElement!==r||(e.preventDefault(),n.focus())},children:(0,r.jsxs)("div",{ref:b,role:"dialog","aria-modal":"true","aria-labelledby":"danger-confirm-title",className:"w-full max-w-md rounded-2xl bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 shadow-xl p-5",children:[(0,r.jsxs)("div",{className:"flex items-start gap-2.5",children:[h&&(0,r.jsx)(i.Z,{size:18,className:"shrink-0 mt-[3px] text-red-600 dark:text-red-400","aria-hidden":"true"}),(0,r.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,r.jsx)("h2",{id:"danger-confirm-title",className:"text-[15px] font-bold text-zinc-900 dark:text-zinc-100 leading-snug break-keep",children:n}),l&&(0,r.jsx)("p",{className:"mt-1 text-[13px] text-zinc-600 dark:text-zinc-400 leading-relaxed break-keep",children:l})]})]}),o&&o.length>0&&(0,r.jsxs)("div",{className:"mt-4 rounded-xl bg-red-50 dark:bg-red-950/20 border border-red-200 dark:border-red-900/40 px-4 py-3",children:[(0,r.jsx)("p",{className:"text-[12px] font-semibold text-red-700 dark:text-red-300 mb-1.5",children:"함께 사라지는 기록"}),(0,r.jsx)("ul",{className:"space-y-0.5",children:o.map(e=>(0,r.jsxs)("li",{className:"text-[13px] text-red-800 dark:text-red-200 break-keep",children:["\xb7 ",e]},e))}),(0,r.jsx)("p",{className:"mt-2 text-[12px] font-medium text-red-700 dark:text-red-300",children:"한 번 삭제하면 되돌릴 수 없습니다."})]}),(0,r.jsxs)("div",{className:"mt-5 flex gap-2 justify-end",children:[(0,r.jsx)("button",{ref:f,type:"button",onClick:x,disabled:m,className:"px-4 py-2 rounded-xl text-[13px] font-semibold text-zinc-700 dark:text-zinc-300 bg-zinc-100 dark:bg-zinc-800 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition disabled:opacity-50",children:c}),(0,r.jsx)("button",{type:"button",onClick:p,disabled:m,className:h?"px-4 py-2 rounded-xl text-[13px] font-semibold text-white bg-red-600 hover:bg-red-700 transition disabled:opacity-50":"btn-primary-solid px-4 py-2 rounded-xl text-[13px] font-semibold transition",children:m?"처리하는 중…":d})]})]})}):null}},80664:function(e,t,n){n.d(t,{g:function(){return x}});var r=n(57437),a=n(2265),i=n(32489),s=n(54001),l=n(49322),o=n(33245),d=n(51817),c=n(30401),u=n(82023),m=n(83008),p=n(34137);function x(e){let{isOpen:t,onClose:n,onSuccess:x}=e,[h,f]=(0,a.useState)(""),[b,g]=(0,a.useState)(!1),[v,y]=(0,a.useState)(null),[k,w]=(0,a.useState)("input"),[j,N]=(0,a.useState)(null),[z,C]=(0,a.useState)(0);if((0,a.useEffect)(()=>
1{if(!t){let e=setTimeout(()=>{f(""),y(null),w("input"),N(null),C(0),g(!1)},200);return()=>clearTimeout(e)}},[t]),(0,p.j)(n,t),!t)return null;let E=async e=>{if(e.preventDefault(),h.replace(/-/g,"")){g(!0),y(null);try{let e=await (0,m.X)("redeemLicenseCode",{code:h},{});if(e.success){var t,n,r,a;N(null!==(r=null===(t=e.data)||void 0===t?void 0:t.paidUntil)&&void 0!==r?r:null),C(null!==(a=null===(n=e.data)||void 0===n?void 0:n.bonusCredits)&&void 0!==a?a:0),w("success")}else y(e.error||"등록할 수 없는 코드입니다. 다시 확인해 주세요.")}catch(e){y("코드 등록 중 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.")}finally{g(!1)}}},S=h.replace(/-/g,"").length>0;return(0,r.jsx)("div",{className:"modal-viewport z-[120000] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 animate-in fade-in duration-200",onClick:e=>e.target===e.currentTarget&&n(),children:(0,r.jsxs)("div",{className:"relative w-full max-w-[400px] bg-white dark:bg-[#121212] border border-zinc-200 dark:border-white/10 rounded-2xl shadow-2xl overflow-hidden animate-in zoom-in-95 duration-150 flex flex-col",children:[(0,r.jsx)("button",{type:"button",onClick:n,"aria-label":"닫기",className:"absolute top-4 right-4 p-1.5 text-zinc-400 hover:text-zinc-600 dark:hover:text-white rounded-lg hover:bg-zinc-100 dark:hover:bg-white/5 transition-colors",children:(0,r.jsx)(i.Z,{size:18})}),(0,r.jsx)("div",{className:"p-7",children:"input"===k?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("div",{className:"w-11 h-11 rounded-xl flex items-center justify-center bg-zinc-100 dark:bg-white/5 border border-zinc-200 dark:border-white/10 text-zinc-800 dark:text-zinc-100 mb-4",children:(0,r.jsx)(s.Z,{size:20})}),(0,r.jsx)("h2",{className:"text-lg font-bold tracking-tight text-zinc-900 dark:text-white mb-1.5",children:"이용권 코드 등록"}),(0,r.jsx)("p",{className:"text-sm text-zinc-500 dark:text-zinc-400 leading-relaxed mb-6",children:"전달받으신 이용권 코드를 입력하시면 유료 기능이 즉시 활성화됩니다."}),(0,r.jsxs)("form",{onSubmit:E,className:"space-y-4",children:[(0,r.jsxs)("div",{children:[(0,r.jsx)("label",{className:"block text-xs font-semibold text-zinc-700 dark:text-zinc-300 mb-2",children:"이용권 코드 (12자리)"}),(0,r.jsx)("div",{className:"relative",children:(0,r.jsx)("input",{type:"text",value:h,onChange:e=>{let t=e.target.value;/[ㄱ-ㅎㅏ-ㅣ가-힣]/.test(t)?y("이용권 코드는 영문과 숫자만 입력 가능합니다. (한/영 키를 확인해 주세요)"):v&&v.includes("한/영 키")?y(null):v&&y(null),f(function(e){let t=e.replace(/[^a-zA-Z0-9]/g,"").toUpperCase().slice(0,12),n=[];for(let e=0;e<t.length;e+=4)n.push(t.slice(e,e+4));return n.join("-")}(t))},placeholder:"XXXX-XXXX-XXXX",autoFocus:!0,autoComplete:"off",spellCheck:!1,className:["w-full bg-zinc-50 dark:bg-white/5 rounded-xl px-4 py-3.5","text-base text-zinc-900 dark:text-white font-mono tracking-widest text-center uppercase font-medium","placeholder:text-zinc-300 dark:placeholder:text-zinc-600 placeholder:tracking-widest placeholder:font-normal","border outline-none transition duration-150",v?"border-red-500/80 dark:border-red-500/80 ring-2 ring-red-500/20 bg-red-50/20 dark:bg-red-950/10":"border-zinc-200 dark:border-white/10 focus:border-zinc-900 dark:focus:border-white focus:ring-2 focus:ring-zinc-900/10 dark:focus:ring-white/10 focus:bg-white dark:focus:bg-[#181818]"].join(" ")})}),(0,r.jsx)("div",{className:"mt-2.5 min-h-[20px] flex items-start gap-1.5",children:v?(0,r.jsxs)("div",{className:"flex items-center gap-1.5 text-xs font-medium text-red-600 dark:text-red-400 animate-in fade-in duration-150",children:[(0,r.jsx)(l.Z,{size:14,className:"shrink-0 mt-0.5"}),(0,r.jsx)("span",{children:v})]}):(0,r.jsxs)("div",{className:"flex items-center gap-1.5 text-xs text-zinc-400 dark:text-zinc-500",children:[(0,r.jsx)(o.Z,{size:13,className:"shrink-0"}),(0,r.jsx)("span",{children:"학교 또는 단체에서 구매 후 발급된 코드입니다."})]})})]}),(0,r.jsx)("div",{className:"pt-2",children:(0,r.jsx)("button",{type:"submit",disabled:b||!S,className:["license-btn-primary w-full py-3.5 rounded-xl text-sm font-semibold transition duration-150 flex items-center justify-center gap-2",b||!S?"opacity-40 cursor-not-allowed":"hover:opacity-90 active:scale-[0.99] cursor-pointer"].join(" "),children:b?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(d.Z,{size:16,className:"animate-spin"}),"코드 검증 및 등록 중..."]}):"이용권 등록하기"})})]})]}):(0,r.jsxs)("div",{className:"pt-1 text-center sm:text-left",children:[(0,r.jsx)("div",{className:"w-12 h-12 rounded-xl flex items-center justify-center bg-emerald-500/10 border border-emerald-500/20 text-emerald-600 dark:text-emerald-400 mb-4",children:(0,r.jsx)(c.Z,{size:24,strokeWidth:2.5})}),(0,r.jsx)("h2",{className:"text-lg font-bold tracking-tight text-zinc-900 dark:text-white mb-1.5",children:"이용권이 등록되었습니다!"}),(0,r.jsx)("p",{className:"text-sm text-zinc-500 dark:text-zinc-400 leading-relaxed mb-6",children:"유료 플랜이 적용되어 모든 기능을 제한 없이 이용하실 수 있습니다."}),(j||z>0)&&(0,r.jsxs)("div",{className:"bg-zinc-50 dark:bg-white/5 border border-zinc-200 dark:border-white/10 rounded-xl p-4 mb-6 space-y-3",children:[j&&(0,r.jsxs)("div",{className:"flex items-center justify-between text-sm",children:[(0,r.jsx)("span",{className:"text-zinc-500 dark:text-zinc-400 font-medium",children:"이용 만료일"}),(0,r.jsx)("span",{className:"font-semibold text-zinc-900 dark:text-white",children:new Date(j).toLocaleDateString("ko-KR",{year:"numeric",month:"long",day:"numeric"})+"까지"})]}),z>0&&(0,r.jsxs)("div",{className:"flex items-center justify-between text-sm pt-2.5 border-t border-zinc-200/60 dark:border-white/5",children:[(0,r.jsxs)("span",{className:"flex items-center gap-1.5 text-zinc-500 dark:text-zinc-400 font-medium",children:[(0,r.jsx)(u.Z,{size:14,className:"text-amber-500"}),"보너스 크레딧"]}
1),(0,r.jsxs)("span",{className:"font-bold text-blue-600 dark:text-blue-400",children:["+",z,"개 지급 완료"]})]})]}),(0,r.jsx)("button",{type:"button",onClick:()=>{null==x||x(),window.location.reload()},className:"license-btn-primary w-full py-3.5 rounded-xl text-sm font-semibold hover:opacity-90 active:scale-[0.99] transition duration-150",children:"바로 시작하기"})]})})]})})}},285:function(e,t,n){n.d(t,{k:function(){return f}});var r=n(57437),a=n(2265),i=n(98319),s=n(99397),l=n(69076),o=n(30401),d=n(18930),c=n(93448),u=n(83008),m=n(67920),p=n(4908),x=n(54696),h=n(96214);function f(){let e=(0,m.XC)(),{showToast:t}=(0,i.p)(),n=(0,x.Dv)(),{students:f}=(0,p.AF)(),b=(0,a.useMemo)(()=>f.filter(e=>"담임 학급"===e.homeClassId).sort((e,t)=>e.number-t.number),[f]),[g,v]=(0,a.useState)({columns:[],assignments:{}}),[y,k]=(0,a.useState)({columns:[],assignments:{}}),[w,j]=(0,a.useState)(!1),[N,z]=(0,a.useState)(!1),[C,E]=(0,a.useState)(!1),S=a.useRef([]),L=a.useCallback(()=>{S.current.push(JSON.parse(JSON.stringify(g))),S.current.length>20&&S.current.shift()},[g]);(0,a.useEffect)(()=>{let e=e=>{if((e.metaKey||e.ctrlKey)&&"z"===e.key){e.preventDefault();let t=S.current.pop();t&&v(t)}};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[]);let[A,D]=(0,a.useState)(null),[T,R]=(0,a.useState)(!1),[B,O]=(0,a.useState)(null),M=(e,t,n)=>{0===n.button&&(R(!0),O({row:e,col:t}),D({start:{row:e,col:t},end:{row:e,col:t}}))},I=(e,t)=>{T&&B&&D({start:B,end:{row:e,col:t}})},F=()=>{R(!1)},P=(e,t)=>{if(!A)return!1;let n=Math.min(A.start.row,A.end.row),r=Math.max(A.start.row,A.end.row),a=Math.min(A.start.col,A.end.col),i=Math.max(A.start.col,A.end.col);return e>=n&&e<=r&&t>=a&&t<=i};(0,a.useEffect)(()=>{let e=()=>R(!1);return window.addEventListener("mouseup",e),()=>window.removeEventListener("mouseup",e)},[]),(0,a.useEffect)(()=>{(async()=>{if(n){let e=(await (0,h.getGuestData)()).studentRoles;if(e&&e.columns&&e.columns.length>0)v(e),k(e);else{let e={columns:[{id:"col-".concat(Date.now()),name:"역할1"}],assignments:{}};v(e),k(e)}return}let t=await (0,u.X)("getTimetable",{type:"student-roles"},e);if(t.success&&t.data&&Object.keys(t.data).length>0){let e=t.data;if(e.columns&&e.columns.length>0)v(e),k(e);else{let e={columns:[{id:"col-".concat(Date.now()),name:"역할1"}],assignments:{}};v(e),k(e)}}else{let e={columns:[{id:"col-".concat(Date.now()),name:"역할1"}],assignments:{}};v(e),k(e)}})()},[e,n]),(0,a.useEffect)(()=>{j(JSON.stringify(g)!==JSON.stringify(y))},[g,y]);let Z=async()=>{E(!0);try{if(n){await (0,h.saveGuestData)("studentRoles",g);let e=[];Object.entries(g.assignments).forEach(t=>{let[n,r]=t;
1Object.entries(r).forEach(t=>{let[r,a]=t;if(a&&a.trim()){let t=b.find(e=>e.id===n);e.push({id:"".concat(n,"-").concat(r),studentId:n,studentName:(null==t?void 0:t.name)||"",role:a})}})}),await (0,h.saveGuestData)("roleAssignments",e)}else{let n=await (0,u.X)("saveTimetable",{type:"student-roles",data:g},e);if(!n.success){t(n.error||"저장에 실패했습니다. 다시 시도해주세요.","error");return}}k(g),j(!1),z(!0),setTimeout(()=>z(!1),2e3)}finally{E(!1)}},G=e=>{L(),v(t=>{let n={...t.assignments};return Object.keys(n).forEach(t=>{delete n[t][e]}),{columns:t.columns.filter(t=>t.id!==e),assignments:n}})},X=(e,t)=>{v(n=>({...n,columns:n.columns.map(n=>n.id===e?{...n,name:t}:n)}))},K=(e,t,n)=>{v(r=>({...r,assignments:{...r.assignments,[e]:{...r.assignments[e],[t]:n}}}))},q=(e,t,n)=>{let r=e.clipboardData.getData("text");if(!r||(L(),!r.includes("	")&&!r.includes("\n")))return;e.preventDefault();let a=r.replace(/\r\n/g,"\n").split("\n").map(e=>e.split("	")),i=b.map(e=>e.id),s=g.columns.map(e=>e.id),l=i.indexOf(t),o=s.indexOf(n);-1!==l&&-1!==o&&v(e=>{let t={...e.assignments};return a.forEach((e,n)=>{if(1===e.length&&""===e[0])return;let r=l+n;if(r>=i.length)return;let a=i[r];t[a]||(t[a]={}),e.forEach((e,n)=>{let r=o+n;if(r>=s.length)return;let i=s[r];t[a][i]=e.trim()})}),{...e,assignments:t}})},H=(0,a.useCallback)(()=>{A&&(L(),v(e=>{let t={...e.assignments},n=Math.min(A.start.row,A.end.row),r=Math.max(A.start.row,A.end.row),a=Math.min(A.start.col,A.end.col),i=Math.max(A.start.col,A.end.col);for(let s=n;s<=r;s++){if(s>=b.length)continue;let n=b[s].id;if(t[n])for(let r=a;r<=i;r++){if(r>=e.columns.length)continue;let a=e.columns[r].id;t[n][a]&&delete t[n][a]}}return{...e,assignments:t}}))},[A,L,b]),W=(0,a.useCallback)(async()=>{if(!A)return;let e=Math.min(A.start.row,A.end.row),t=Math.max(A.start.row,A.end.row),n=Math.min(A.start.col,A.end.col),r=Math.max(A.start.col,A.end.col),a="";for(let s=e;s<=t;s++){let e=[],l=b[s];if(l){for(let t=n;t<=r;t++){var i;let n=g.columns[t];if(!n)continue;let r=(null===(i=g.assignments[l.id])||void 0===i?void 0:i[n.id])||"";e.push(r)}a+=e.join("	")+(s<t?"\n":"")}}try{await navigator.clipboard.writeText(a)}catch(e){console.error("Failed to copy:",e)}},[A,b,g]),U=(0,a.useCallback)(e=>{var t;if(!A)return;L(),e.preventDefault(),document.activeElement instanceof HTMLElement&&document.activeElement.blur();let n=null===(t=e.clipboardData)||void 0===t?void 0:t.getData("text");if(!n)return;let r=Math.min(A.start.row,A.end.row),a=Math.min(A.start.col,A.end.col),i=n.replace(/\r\n/g,"\n").split("\n").map(e=>e.split("	"));v(e=>{let t={...e.assignments};return i.forEach((n,i)=>{let s=r+i;if(s>=b.length)return;let l=b[s].id;t[l]||(t[l]={}),n.forEach((n,r)=>{let i=a+r;if(i>=e.columns.length)return;let s=e.columns[i].id;t[l][s]=n.trim()})}),{...e,assignments:t}})},[A,L,b]);(0,a.useEffect)(()=>{let e=async e=>{if(("Delete"===e.key||"Backspace"===e.key)&&A){let t=e.target;if("INPUT"===t.tagName&&t.value)return;e.preventDefault(),H()}(e.metaKey||e.ctrlKey)&&"c"===e.key&&A&&(e.preventDefault(),W())},t=e=>{A&&(e.target,U(e))};return window.addEventListener("keydown",e),window.addEventListener("paste",t),()=>{window.removeEventListener("keydown",e),window.removeEventListener("paste",t)}},[A,g,b,W,H,U]);let V=(e,t,n)=>{let r=e.currentTarget,a=r.value.length,i=r.selectionStart,s=r.selectionEnd,l="";if("ArrowUp"===e.key?(e.preventDefault(),l="role-input-".concat(t-1,"-").concat(n)):"ArrowDown"===e.key?(e.preventDefault(),l="role-input-".concat(t+1,"-").concat(n)):"ArrowLeft"===e.key?0===i&&0===s&&(e.preventDefault(),l="role-input-".concat(t,"-").concat(n-1)):"ArrowRight"===e.key&&i===a&&s===a&&(e.preventDefault(),l="role-input-".concat(t,"-").concat(n+1)),l){let e=document.getElementById(l);e&&e.focus()}};return(0,r.jsxs)("div",{className:"space-y-6 animate-in fade-in slide-in-from-right-4 duration-300",children:[(0,r.jsxs)("div",{className:"border-b border-[#27272a] pb-2 mb-6",children:[(0,r.jsx)("h3",{className:"text-[17px] font-medium text-zinc-900 dark:text-white",children:"학생 역할"}),(0,r.jsxs)("div",{className:"flex items-center justify-between mt-2",children:[(0,r.jsx)("p",{className:"hidden md:block text-[13px] text-[#71717a]",children:"학생들의 역할을 관리합니다. 열을 추가하여 다양한 역할을 부여할 수 있습니다."}),(0,r.jsxs)("div",{className:"flex items-center gap-2 ml-auto",children:[(0,r.jsxs)("button",{onClick:()=>{L();let e="col-".concat(Date.now());v(t=>{let n=t.columns.length+1;return{...t,columns:[...t.columns,{id:e,name:"역할".concat(n)}]}})},className:"flex items-center gap-2 text-[13px] text-[#2883DE] hover:text-white px-3 py-1.5 hover:bg-[#2883DE]
1/10 rounded-lg transition-colors",children:[(0,r.jsx)(s.Z,{size:14}),"열 추가"]}),(0,r.jsxs)("button",{onClick:()=>v({columns:[],assignments:{}}),className:"flex items-center gap-2 text-[13px] text-[#a1a1aa] hover:text-white px-3 py-1.5 hover:bg-[#27272a] rounded-lg transition-colors",children:[(0,r.jsx)(l.Z,{size:14}),"초기화"]}),(0,r.jsx)("button",{onClick:Z,disabled:!w||C,className:(0,c.cn)("flex items-center gap-2 text-[13px] px-3 py-1.5 rounded-lg transition-colors font-medium",w&&!C?"bg-[#2883DE] text-white hover:opacity-90":N?"text-[#2883DE]":"text-[#52525b] cursor-not-allowed"),children:C?"저장 중...":N?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(o.Z,{size:14}),"저장됨"]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(o.Z,{size:14}),"저장"]})})]})]})]}),(0,r.jsxs)("div",{className:"overflow-x-auto custom-scrollbar border border-zinc-200 dark:border-[#27272a] rounded-sm bg-white dark:bg-[#18181b]",children:[(0,r.jsxs)("table",{className:"w-full border-collapse",children:[(0,r.jsx)("thead",{children:(0,r.jsxs)("tr",{children:[(0,r.jsx)("th",{className:"sticky left-0 z-20 w-12 text-center text-xs text-zinc-500 font-medium py-3 px-2 border-b border-r border-zinc-200 dark:border-[#27272a] bg-zinc-50 dark:bg-[#1f1f1f]",children:"번호"}),(0,r.jsx)("th",{className:"sticky left-12 z-20 w-24 text-center text-xs text-zinc-400 font-medium py-3 px-2 border-b border-r border-zinc-200 dark:border-[#27272a] bg-zinc-50 dark:bg-[#1f1f1f]",children:"이름"}),g.columns.map(e=>(0,r.jsx)("th",{className:"min-w-[120px] text-center text-xs font-medium py-2 px-2 border-b border-r border-zinc-200 dark:border-[#27272a] bg-zinc-50 dark:bg-[#1f1f1f]",children:(0,r.jsxs)("div",{className:"relative group flex items-center justify-center h-full",children:[(0,r.jsx)("input",{type:"text",value:e.name,onChange:t=>X(e.id,t.target.value),onFocus:()=>L(),className:"bg-transparent border-none focus:ring-1 focus:ring-[#2883DE] rounded px-1 w-full text-center text-zinc-900 dark:text-white"}),(0,r.jsx)("button",{"aria-label":"열 삭제",onClick:()=>G(e.id),className:"absolute right-0 opacity-0 group-hover:opacity-100 p-1 text-zinc-500 hover:text-rose-500 transition",children:(0,r.jsx)(d.Z,{size:12})})]})},e.id)),(0,r.jsx)("th",{className:"border-b border-zinc-200 dark:border-[#27272a] bg-zinc-50 dark:bg-[#1f1f1f]"})]})}),(0,r.jsx)("tbody",{children:b.map((e,t)=>(0,r.jsxs)("tr",{className:"dark:hover:bg-[#27272a]/30 transition-colors",children:[(0,r.jsx)("td",{className:"sticky left-0 z-10 text-center text-[13px] text-zinc-500 font-mono py-2 bg-zinc-50 dark:bg-[#1f1f1f] border-r border-b border-zinc-200 dark:border-[#27272a]",children:e.number}),(0,r.jsx)("td",{className:"sticky left-12 z-10 text-center text-[13px] text-zinc-700 dark:text-zinc-300 py-2 bg-zinc-50 dark:bg-[#1f1f1f] border-r border-b border-zinc-200 dark:border-[#27272a]",children:e.name}),g.columns.map((n,a)=>{var i;return(0,r.jsx)("td",{className:(0,c.cn)("p-0 border-r border-b border-zinc-200 dark:border-[#27272a] relative select-none",P(t,a)&&"bg-blue-500/10 ring-1 ring-inset ring-blue-500 z-10"),onMouseDown:e=>M(t,a,e),onMouseEnter:()=>I(t,a),onMouseUp:F,children:(0,r.jsx)("input",{id:"role-input-".concat(t,"-").concat(a),type:"text",value:(null===(i=g.assignments[e.id])||void 0===i?void 0:i[n.id])||"",onChange:t=>K(e.id,n.id,t.target.value),onFocus:()=>{},onPaste:t=>{A&&(A.start.row!==A.end.row||A.start.col!==A.end.col)||q(t,e.id,n.id)},onKeyDown:e=>V(e,t,a),className:"w-full h-9 bg-transparent text-[13px] text-center text-zinc-900 dark:text-white px-2 dark:focus:bg-[#27272a] focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 transition-colors border-none selection:bg-blue-200 dark:selection:bg-blue-800"})},n.id)}),(0,r.jsx)("td",{className:"border-b border-zinc-200 dark:border-[#27272a]"})]},e.id))})]}),0===b.length&&(0,r.jsx)("div",{className:"py-20 text-center text-zinc-500 text-sm",children:"등록된 학생이 없습니다. '학생 명단' 탭에서 학생을 먼저 등록해 주세요."})]})]})}},93627:function(e,t,n){n.d(t,{Q:function(){return L}});var r=n(57437),a=n(2265),i=n(98319),s=n(51817),l=n(50733),o=n(69076),d=n(30401),c=n(32489),u=n(93448),m=n(53834),p=n(83008),x=n(67920),h=n(54696),f=n(96214);let b=["mon","tue","wed","thu","fri"],g=b.length;function v(e,t){return"".concat(b[t],"-").concat(e+1)}function y(e){return{r1:Math.min(e.start.row,e.end.row),r2:Math.max(e.start.row,e.end.row),c1:Math.min(e.start.col,e.end.col),c2:Math.max(e.start.col,e.end.col)}}function k(e,t){return{row:Math.max(0,Math.min(6,e)),col:Math.max(0,Math.min(g-1,t))}}function w(e,t){let{r1:n,r2:r,c1:a,c2:i}=y(t),s={...e};for(let e=n;e<=r;e++)for(let t=a;t<=i;t++)delete s[v(e,t)];return s}function j(e,t,n,r){let a={...e};return t.forEach((e,t)=>{let i=n+t;i<0||i>=7||e.forEach((e,t)=>{let n=r+t;n<0||n>=g||(a[v(i,n)]=e.trim())})}),a}var N=n(69813);let z=["image/png","image/jpeg","image/webp","image/heic","image/heif","application/pdf"];function C(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>
1t(r.result),r.onerror=n,r.readAsDataURL(e)})}async function E(e){let t=e=>e.split(",")[1]||"";if("application/pdf"===e.type)return{base64:t(await C(e)),mediaType:"application/pdf"};let n=await C(e);try{let e=await new Promise((e,t)=>{let r=new Image;r.onload=()=>e(r),r.onerror=t,r.src=n}),r=Math.min(1,2e3/Math.max(e.width,e.height)),a=Math.round(e.width*r),i=Math.round(e.height*r),s=document.createElement("canvas");s.width=a,s.height=i;let l=s.getContext("2d");if(!l)throw Error("canvas unavailable");return l.drawImage(e,0,0,a,i),{base64:t(s.toDataURL("image/jpeg",.9)),mediaType:"image/jpeg"}}catch(r){return{base64:t(n),mediaType:e.type}}}function S(e){let{value:t,onChange:n,onPaste:i,className:s}=e,l=(0,a.useRef)(null),o=(0,a.useCallback)(()=>{let e=l.current;e&&(e.style.height="auto",e.style.height="".concat(e.scrollHeight,"px"))},[]);return(0,a.useLayoutEffect)(o,[o,t]),(0,a.useEffect)(()=>{let e=l.current;if(!e||"undefined"==typeof ResizeObserver)return;let t=e.clientWidth,n=new ResizeObserver(()=>{let e=l.current;e&&e.clientWidth!==t&&(t=e.clientWidth,o())});return n.observe(e),()=>n.disconnect()},[o]),(0,r.jsx)("textarea",{ref:l,value:t,onChange:e=>n(e.target.value),onPaste:i,className:s,rows:1,placeholder:"-"})}function L(e){var t,n;let{gridSelection:C=!0}=e,L=(0,x.XC)(),{showToast:A}=(0,i.p)(),D=(0,h.Dv)(),[T,R]=(0,a.useState)("my"),[B,O]=(0,a.useState)({}),[M,I]=(0,a.useState)({}),[F,P]=(0,a.useState)({}),[Z,G]=(0,a.useState)({}),[X,K]=(0,a.useState)(!1),[q,H]=(0,a.useState)(!1),[W,U]=(0,a.useState)(!0),[V,_]=(0,a.useState)(!1),J=(0,a.useRef)(null),[Y,Q]=(0,a.useState)(!1),[$,ee]=(0,a.useState)(null),[et,en]=(0,a.useState)(null),[er,ea]=(0,a.useState)(null),[ei,es]=(0,a.useState)(!1),[el,eo]=(0,a.useState)(null),ed=(0,a.useRef)(null),ec=(0,a.useCallback)(e=>{ed.current=e,eo(e)},[]),eu=(0,a.useRef)(null),em=(0,a.useRef)(null),ep=(0,a.useRef)(!1),ex=C&&null!==(t=null==er?void 0:er.end.row)&&void 0!==t?t:null,eh=C&&null!==(n=null==er?void 0:er.end.col)&&void 0!==n?n:null;(0,a.useEffect)(()=>{ea(null),ec(null)},[T,ec]),(0,a.useLayoutEffect)(()=>{var e;null!==ex&&null!==eh&&(null===(e=em.current)||void 0===e||e.focus())},[ex,eh]);let ef="my"===T?B:M,eb="my"===T?O:I,eg=a.useRef([]),ev=a.useCallback(()=>{eg.current.push({teacher:JSON.parse(JSON.stringify(B)),class:JSON.parse(JSON.stringify(M))}),eg.current.length>20&&eg.current.shift()},[B,M]);(0,a.useEffect)(()=>{let e=e=>{if((e.metaKey||e.ctrlKey)&&"z"===e.key&&!ed.current){e.preventDefault();let t=eg.current.pop();t&&(O(t.teacher),I(t.class))}};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[]),(0,a.useEffect)(()=>{if(null===ex||null===eh||el)return;let e=ef[v(ex,eh)]||"";em.current&&em.current.value!==e&&(em.current.value=e)},[ef,ex,eh,el]),(0,a.useEffect)(()=>{(async()=>{U(!0);try{if(D){let e=await (0,f.getGuestData)(),t=e.teacherTimetable||{},n=e.classTimetable||{};O(t),P(t),I(n),G(n);return}let[e,t]=await Promise.all([(0,p.X)("getTimetable",{type:"teacher"},L),(0,p.X)("getTimetable",{type:"class"},L)]);if(e.success&&e.data&&Object.keys(e.data).length>0){let t=e.data;O(t),P(t)}if(t.success&&t.data&&Object.keys(t.data).length>0){let e=t.data;I(e),G(e)}}finally{U(!1)}})()},[L,D]),(0,a.useEffect)(()=>{let e=JSON.stringify(B)!==JSON.stringify(F),t=JSON.stringify(M)!==JSON.stringify(Z);K(e||t)},[B,M,F,Z]);let ey=async()=>{_(!0);try{if(D)await (0,f.saveGuestData)("teacherTimetable",B),await (0,f.saveGuestData)("classTimetable",M);else{let e=await (0,p.X)("saveTimetable",{type:"teacher",data:B},L);if(!e.success){A(e.error||"저장에 실패했습니다. 다시 시도해주세요.","error");return}let t=await (0,p.X)("saveTimetable",{type:"class",data:M},L);if(!t.success){A(t.error||"저장에 실패했습니다. 다시 시도해주세요.","error");return}}P(B),G(M),K(!1),H(!0),en(null),setTimeout(()=>H(!1),2e3)}finally{_(!1)}},ek=async e=>{if(ee(null),D){ee("체험 모드에서는 사진 인식을 사용할 수 없습니다. 로그인 후 이용해 주세요.");return}if(!z.includes(e.type)){ee("JPG, PNG, HEIC 사진이나 PDF 파일만 인식할 수 있습니다.");return}Q(!0);try{let{base64:t,mediaType:n}=await E(e),r=await (0,p.X)("analyzeTimetableImage",{fileBase64:t,mediaType:n},L,{timeout:12e4});if(!(null==r?void 0:r.success)||!r.data){ee((null==r?void 0:r.error)||"시간표를 인식하지 못했습니다. 다시 시도해 주세요.");return}let a=r.data;ev(),"my"===T?O(a):I(a),en(T)}catch(e){console.error("[SettingsTabTimetable] 시간표 인식 실패:",e),ee("시간표를 분석하는 중 문제가 생겼습니다. 잠시 후 다시 시도해 주세요.")}finally{Q(!1)}},ew=(e,t)=>{ev(),em.current&&(em.current.value=""),eb(n=>({...n,[v(e,t)]:""})),ec({row:e,col:t})},ej=(e,t,n)=>{if(C&&(!el||el.row!==t||el.col!==n)){if(ep.current){var r;null===(r=em.current)||void 0===r||r.blur(),ep.current=!1}if(e.shiftKey&&er){e.preventDefault(),ea({start:er.start,end:{row:t,col:n}});return}e.preventDefault(),ec(null),ea({start:{row:t,col:n},end:{row:t,col:n}}),es(!0)}},eN=(e,t)=>{C&&ei&&er&&ea({start:er.start,end:{row:e,col:t}})};(0,a.useEffect)(()=>
1{if(!ei)return;let e=()=>es(!1);return window.addEventListener("mouseup",e),()=>window.removeEventListener("mouseup",e)},[ei]);let ez=e=>!!e&&new DOMParser().parseFromString(e,"text/html").querySelectorAll("tr").length>0,eC=(e,t)=>{if(e){let t=Array.from(new DOMParser().parseFromString(e,"text/html").querySelectorAll("tr"));if(t.length>0)return t.map(e=>Array.from(e.querySelectorAll("td, th")).map(e=>(function(e){let t=e.innerHTML.replace(/<br\s*\/?>/gi,"\n").replace(/<\/p>\s*<p[^>]*>/gi,"\n").replace(/<\/div>\s*<div[^>]*>/gi,"\n"),n=document.createElement("div");return n.innerHTML=t,(n.textContent||"").replace(/\n{2,}/g,"\n").trim()})(e)))}if(!t)return null;let n=(0,u.R3)(t);return n.length>0?n:null},eE=(e,t,n)=>{let r=e.clipboardData.getData("text/html"),a=e.clipboardData.getData("text");if(!ez(r)&&!/[\t\n]/.test(a))return;let i=eC(r,a);if(!i)return;e.preventDefault(),ev();let s=j("my"===T?B:M,i,n-1,b.indexOf(t));"my"===T?O(s):I(s)};return((0,a.useEffect)(()=>{if(!C)return;let e=async e=>{if(ed.current||!er)return;let t=e.target;if(t&&t!==em.current&&("INPUT"===t.tagName||"TEXTAREA"===t.tagName))return;let n=e.metaKey||e.ctrlKey,{row:r,col:a}=er.end,i="my"===T?B:M;if(n&&"a"===e.key){e.preventDefault(),ea({start:{row:0,col:0},end:{row:6,col:g-1}});return}if(n&&("c"===e.key||"x"===e.key)){e.preventDefault();let t=function(e,t){let{r1:n,r2:r,c1:a,c2:i}=y(t),s=[];for(let t=n;t<=r;t++){let n=[];for(let r=a;r<=i;r++)n.push((0,u.vB)(e[v(t,r)]||""));s.push(n.join("	"))}return s.join("\r\n")}(i,er);try{await navigator.clipboard.writeText(t)}catch(e){console.error("[SettingsTabTimetable] 복사 실패:",e);return}"x"===e.key&&(ev(),"my"===T?O(e=>w(e,er)):I(e=>w(e,er)));return}if("Delete"===e.key||"Backspace"===e.key){var s;e.preventDefault(),s=w(i,er),ev(),"my"===T?O(s):I(s);return}if("Enter"===e.key||"F2"===e.key){e.preventDefault(),ec({row:r,col:a});return}let l={ArrowUp:[-1,0],ArrowDown:[1,0],ArrowLeft:[0,-1],ArrowRight:[0,1]};if(l[e.key]){e.preventDefault();let[t,n]=l[e.key],i=k(r+t,a+n);ea(e.shiftKey?{start:er.start,end:i}:{start:i,end:i});return}if("Tab"===e.key){e.preventDefault();let t=k(r,a+(e.shiftKey?-1:1));ea({start:t,end:t});return}};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[C,er,T,B,M,ev,ec]),W)?(0,r.jsxs)("div",{className:"flex flex-col items-center justify-center h-[400px] animate-in fade-in duration-500",children:[(0,r.jsx)(m.W,{size:60}),(0,r.jsx)("p",{className:"text-sm text-zinc-500 mt-4 font-medium",children:"시간표를 불러오고 있습니다..."})]}):(0,r.jsxs)("div",{className:"space-y-6 animate-in fade-in slide-in-from-right-4 duration-300",children:[(0,r.jsxs)("div",{className:"border-b border-zinc-200 dark:border-[#27272a] pb-2 mb-6",children:[(0,r.jsx)("h3",{className:"text-[17px] font-medium text-zinc-900 dark:text-white",children:"시간표"}),(0,r.jsxs)("div",{className:"flex items-center justify-between mt-2",children:[(0,r.jsxs)("div",{className:"relative flex items-center bg-[#fafaf9] dark:bg-[#27272a] border border-zinc-200/70 dark:border-white/5 p-[2px] rounded-lg w-[180px] h-[32px]",children:[(0,r.jsx)("div",{className:(0,u.cn)("absolute top-[2px] bottom-[2px] w-[calc(50%-2px)] bg-[#eeeee8] dark:bg-[#3f3f46] border border-black/5 dark:border-white/10 rounded-md shadow-sm transition-all duration-300 ease-in-out pointer-events-none","my"===T?"left-[2px]":"left-[calc(50%)]")}),(0,r.jsxs)("div",{className:"absolute inset-[2px] flex pointer-events-none z-10",children:[(0,r.jsx)("div",{className:(0,u.cn)("flex-1 flex items-center justify-center text-[13px] font-medium transition-colors","my"===T?"text-black dark:text-white":"text-zinc-500 dark:text-[#a1a1aa]"),children:"개인 시간표"}),(0,r.jsx)("div",{className:(0,u.cn)("flex-1 flex items-center justify-center text-[13px] font-medium transition-colors","class"===T?"text-black dark:text-white":"text-zinc-500 dark:text-[#a1a1aa]"),children:"학급 시간표"})]}),(0,r.jsxs)("div",{className:"absolute inset-[2px] flex z-20",children:[(0,r.jsx)("button",{onClick:()=>R("my"),className:"flex-1 h-full cursor-pointer bg-transparent opacity-0","aria-label":"개인 시간표 선택"}),(0,r.jsx)("button",{onClick:()=>R("class"),className:"flex-1 h-full cursor-pointer bg-transparent opacity-0","aria-label":"학급 시간표 선택"})]})]}),(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)("input",{ref:J,type:"file",accept:"image/png,image/jpeg,image/webp,image/heic,image/heif,application/pdf",className:"hidden",onChange:e=>{var t;let n=null===(t=e.target.files)||void 0===t?void 0:t[0];e.target.value="",n&&ek(n)}}),(0,r.jsx)("button",{onClick:()=>{var e;if(D){ee("체험 모드에서는 사진 인식을 사용할 수 없습니다. 로그인 후 이용해 주세요.");return}ee(null),null===(e=J.current)||void 0===e||e.click()},disabled:Y,className:(0,u.cn)("flex items-center gap-2 text-[13px] px-3 py-1.5 rounded-lg transition-colors font-medium",Y?"text-[#52525b] cursor-not-allowed":"text-[#2883DE] hover:bg-[#2883DE]
1/10"),children:Y?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(s.Z,{size:14,className:"animate-spin"}),"인식하는 중..."]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(l.Z,{size:14}),"사진으로 불러오기"]})}),(0,r.jsxs)("button",{onClick:()=>{eb({}),en(null)},className:"flex items-center gap-2 text-[13px] text-[#a1a1aa] hover:text-white px-3 py-1.5 hover:bg-[#27272a] rounded-lg transition-colors",children:[(0,r.jsx)(o.Z,{size:14}),"초기화"]}),(0,r.jsx)("button",{onClick:ey,disabled:!X||V,className:(0,u.cn)("flex items-center gap-2 text-[13px] px-3 py-1.5 rounded-lg transition-colors font-medium",X&&!V?"bg-[#007AFF] dark:bg-[#2883DE] text-white hover:opacity-90":q?"text-[#2883DE]":"text-[#52525b] cursor-not-allowed"),children:V?"저장 중...":q?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(d.Z,{size:14}),"저장됨"]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(d.Z,{size:14}),"저장"]})})]})]})]}),$&&(0,r.jsxs)("div",{className:"-mt-4 flex items-start justify-between gap-3 rounded-lg border border-red-200 dark:border-red-500/30 bg-red-50 dark:bg-red-500/10 px-3 py-2.5",children:[(0,r.jsx)("p",{className:"text-[13px] text-red-700 dark:text-red-300 leading-relaxed",children:$}),(0,r.jsx)("button",{onClick:()=>ee(null),className:"shrink-0 text-red-500 hover:text-red-700 dark:hover:text-red-200 transition-colors","aria-label":"알림 닫기",children:(0,r.jsx)(c.Z,{size:14})})]}),et===T&&(0,r.jsxs)("div",{className:"-mt-4 flex items-center justify-between gap-3 rounded-lg border border-[#2883DE]/30 bg-[#2883DE]/5 dark:bg-[#2883DE]/10 px-3 py-2.5",children:[(0,r.jsx)("p",{className:"text-[13px] text-zinc-700 dark:text-zinc-200 leading-relaxed",children:"아직 저장되지 않았으니 내용을 확인하고 저장해주세요."}),(0,r.jsx)("button",{onClick:()=>en(null),className:"shrink-0 text-zinc-400 hover:text-zinc-600 dark:hover:text-white transition-colors","aria-label":"알림 닫기",children:(0,r.jsx)(c.Z,{size:14})})]}),(0,r.jsx)("div",{ref:eu,tabIndex:C?0:void 0,onPaste:e=>{if(!C||ed.current||!er)return;let t=eC(e.clipboardData.getData("text/html"),e.clipboardData.getData("text"));if(!t)return;e.preventDefault(),ev();let{start:n,end:r}=er,a=j("my"===T?B:M,t,Math.min(n.row,r.row),Math.min(n.col,r.col));"my"===T?O(a):I(a)},className:"focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500",children:(0,r.jsx)("div",{className:"-mt-4 rounded-md overflow-hidden",children:(0,r.jsxs)("table",{className:(0,u.cn)("w-full border-collapse",C&&"table-fixed"),children:[(0,r.jsx)("thead",{children:(0,r.jsxs)("tr",{children:[(0,r.jsx)("th",{className:"text-center text-xs text-zinc-500 dark:text-[#a1a1aa] font-medium py-2 px-2 border border-zinc-200 dark:border-[#27272a] bg-zinc-50 dark:bg-[#1f1f1f] w-12",children:"교시"}),["월","화","수","목","금"].map(e=>(0,r.jsx)("th",{className:"text-center text-xs text-zinc-400 dark:text-[#a1a1aa] font-medium py-2 border border-zinc-200 dark:border-[#27272a] bg-zinc-50 dark:bg-[#1f1f1f]",children:e},e))]})}),(0,r.jsx)("tbody",{children:Array.from({length:7},(e,t)=>t+1).map(e=>(0,r.jsxs)("tr",{children:[(0,r.jsx)("td",{className:"text-center text-xs text-zinc-500 dark:text-[#a1a1aa] font-mono py-0 border border-zinc-200 dark:border-[#27272a] bg-[#FAFAFA] dark:bg-[#1f1f1f]",children:e}),b.map((t,n)=>{let a=e-1,i=v(a,n),s=ef[i]||"",l=(null==el?void 0:el.row)===a&&(null==el?void 0:el.col)===n,o=!!C&&!!er&&function(e,t,n){let{r1:r,r2:a,c1:i,c2:s}=y(n);return e>=r&&e<=a&&t>=i&&t<=s}(a,n,er),d=C&&(null==er?void 0:er.end.row)===a&&(null==er?void 0:er.end.col)===n;return(0,r.jsx)("td",{className:(0,u.cn)("p-0 border border-zinc-200 dark:border-[#27272a]",d&&"ring-2 ring-inset ring-[#2883DE]"),onMouseDown:e=>ej(e,a,n),onMouseEnter:()=>eN(a,n),onDoubleClick:()=>C&&ec({row:a,col:n}),children:(0,r.jsx)("div",{className:(0,u.cn)("h-[60px] flex items-center justify-center transition-colors",o?"bg-[#2883DE]/10 dark:bg-[#2883DE]/20":"bg-[#fafaf9] dark:bg-[#18181b]"),children:C?d?(0,r.jsx)("textarea",{ref:em,"data-grid-editor":"true",defaultValue:s,onCompositionStart:()=>{ep.current=!0,l||ew(a,n)},onCompositionEnd:()=>{ep.current=!1},onMouseUp:e=>{if(l)return;if(e.detail>
1=2){ec({row:a,col:n});return}let t=e.currentTarget;t.setSelectionRange(t.value.length,t.value.length)},onInput:e=>{let t=e.currentTarget.value;l||ec({row:a,col:n}),eb(e=>({...e,[i]:t}))},onKeyDown:e=>{var t,r;if(e.nativeEvent.isComposing||229===e.keyCode)return;if(!l&&!e.metaKey&&!e.ctrlKey&&!e.altKey&&1===e.key.length){ew(a,n);return}if("Escape"===e.key){e.preventDefault(),ec(null),ea({start:{row:a,col:n},end:{row:a,col:n}});return}if("Tab"===e.key){e.preventDefault(),e.nativeEvent.stopImmediatePropagation(),ec(null);let t=k(a,n+(e.shiftKey?-1:1));ea({start:t,end:t});return}if(!l)return;if("Enter"===e.key){if(e.preventDefault(),e.nativeEvent.stopImmediatePropagation(),(0,N.xQ)({key:e.key,altKey:e.altKey,shiftKey:e.shiftKey,multiline:!0})){(0,N.yl)(e.currentTarget);return}ec(null);let t=k(a+1,n);ea({start:t,end:t});return}let i=(0,N.Ud)({key:e.key,value:e.currentTarget.value,selectionStart:null!==(t=e.currentTarget.selectionStart)&&void 0!==t?t:0,selectionEnd:null!==(r=e.currentTarget.selectionEnd)&&void 0!==r?r:0,shiftKey:e.shiftKey});if(i){e.preventDefault(),e.nativeEvent.stopImmediatePropagation(),ec(null);let t=k(a+i.rowDelta,n+i.colDelta);ea({start:t,end:t})}},onBlur:()=>{ec(null)},onPaste:n=>eE(n,t,e),className:(0,u.cn)("w-full bg-transparent text-sm text-center text-zinc-900 dark:text-white px-2 focus:outline-none transition-colors border-0 resize-none leading-tight overflow-hidden block",!l&&"caret-transparent"),rows:s.includes("\n")?s.split("\n").length:1,placeholder:"-"},i):(0,r.jsx)("div",{className:"w-full text-sm text-center text-zinc-900 dark:text-white px-2 leading-tight whitespace-pre-line select-none cursor-default",children:s||(0,r.jsx)("span",{className:"text-zinc-400 dark:text-[#52525b]",children:"-"})}):(0,r.jsx)(S,{value:s,onChange:e=>{eb(t=>({...t,[i]:e}))},onPaste:n=>eE(n,t,e),className:"w-full bg-transparent text-sm text-center text-zinc-900 dark:text-white px-2 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 transition-colors border-0 resize-none leading-tight overflow-hidden block"})})},i)})]},e))})]})})})]})}},99098:function(e,t,n){n.d(t,{l:function(){return l}});var r=n(57437),a=n(2265),i=n(83008),s=n(67920);function l(e){let{variant:t="desktop"}=e,n=(0,s.XC)(),[l,o]=(0,a.useState)(!0),[d,c]=(0,a.useState)(!0);(0,a.useEffect)(()=>{let e=!0;return(0,i.X)("getSuggestionSettings",{},n).then(t=>{e&&(null==t?void 0:t.success)&&o(t.data.enabled)}).catch(()=>{}).finally(()=>{e&&c(!1)}),()=>{e=!1}},[n]);let u=async()=>{let e=!l;o(e);try{let t=await (0,i.X)("setSuggestionEnabled",{enabled:e},n);(null==t?void 0:t.success)||o(!e)}catch(t){o(!e)}},m="mobile"===t;return(0,r.jsxs)("div",{className:m?"flex items-center justify-between px-4 py-3.5":"flex items-center justify-between h-8",children:[(0,r.jsx)("span",{className:m?"text-[14px] font-medium text-zinc-800 dark:text-zinc-200":"text-[14px] text-[#E0E0E0] font-medium",children:"내모 알람"}),(0,r.jsx)("button",{role:"switch","aria-checked":l,"aria-label":"내모 알람",disabled:d,onClick:u,className:["relative w-11 h-6 rounded-full transition-colors shrink-0 ml-4",l?"bg-[#007AFF] dark:bg-[#2883DE]":"bg-zinc-300 dark:bg-zinc-700",d?"opacity-50":""].join(" "),children:(0,r.jsx)("span",{className:["absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white transition-transform",l?"translate-x-5":"translate-x-0"].join(" ")})})]})}},98381:function(e,t,n){n.d(t,{F:function(){return o},f:function(){return l}});var r=n(57437),a=n(2265),i=n(99376);let s=(0,a.createContext)(void 0);function l(e){var t;let{children:n}=e,[l,o]=(0,a.useState)("system"),[d,c]=(0,a.useState)(!1),u=(0,i.usePathname)(),m=null!==(t=null==u?void 0:u.startsWith("/admin"))&&void 0!==t&&t;(0,a.useEffect)(()=>{let e=localStorage.getItem("theme");e?o(e):o("system"),c(!0)},[]),(0,a.useEffect)(()=>{if(!d)return;let e=document.documentElement,t=t=>{e.setAttribute("data-theme",t),"dark"===t?e.classList.add("dark"):e.classList.remove("dark");let n=document.querySelector('meta[name="theme-color"]');n&&n.setAttribute("content","dark"===t?"#09090b":"#FBFBF9")};if(m){t("dark");return}let n=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>
1{"system"===l&&t(n.matches?"dark":"light")};return"system"===l?(localStorage.setItem("theme","system"),t(n.matches?"dark":"light"),n.addEventListener("change",r)):(localStorage.setItem("theme",l),t(l)),()=>n.removeEventListener("change",r)},[l,d,m]);let p=e=>{o(e)};return(0,r.jsx)(s.Provider,{value:{theme:l,toggleTheme:()=>{p("dark"===l?"light":"dark")},setTheme:p},children:n})}function o(){let e=(0,a.useContext)(s);if(void 0===e)throw Error("useTheme must be used within a ThemeProvider");return e}},43232:function(e,t,n){n.d(t,{O:function(){return a}});var r=n(57437);let a=e=>{let{size:t=32,className:n="",strokeWidth:a=1.9}=e;return(0,r.jsx)("svg",{width:t,height:t,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:a,strokeLinecap:"square",strokeLinejoin:"miter",className:n,children:(0,r.jsx)("path",{d:"M12 7 L17 12 L12 17 L7 12 Z"})})}},53834:function(e,t,n){n.d(t,{W:function(){return o}});var r=n(57437),a=n(29),i=n.n(a),s=n(93448),l=n(43232);let o=e=>{let{size:t=32,speed:n=1.5,className:a="",withPulse:o=!1}=e;return(0,r.jsxs)("div",{style:{animation:"spin ".concat(n,"s linear infinite").concat(o?", pulse 2s ease-in-out infinite":"")},className:"jsx-ba9b38e61bef3f8b "+((0,s.cn)("inline-flex items-center justify-center",a)||""),children:[(0,r.jsx)(l.O,{size:t}),(0,r.jsx)(i(),{id:"ba9b38e61bef3f8b",children:"@-webkit-keyframes spin{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes spin{from{-moz-transform:rotate(0deg);transform:rotate(0deg)}to{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes spin{from{-o-transform:rotate(0deg);transform:rotate(0deg)}to{-o-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spin{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}@-moz-keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}@-o-keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}"})]})}},10136:function(e,t,n){n.d(t,{N:function(){return l},W:function(){return o}});var r=n(57437),a=n(2265),i=n(73348);let s=(0,a.createContext)(null);function l(){let e=(0,a.useContext)(s);if(!e)throw Error("useConfirm은 ConfirmProvider 안에서만 쓸 수 있습니다.");return e}function o(e){var t,n;let{children:l}=e,[o,d]=(0,a.useState)(null),c=(0,a.useRef)(null),u=(0,a.useCallback)(e=>{var t;return null===(t=c.current)||void 0===t||t.call(c,!1),d(e),new Promise(e=>{c.current=e})},[]),m=(0,a.useCallback)(e=>{let t=c.current;c.current=null,d(null),null==t||t(e)},[]),p=null!==(t=null==o?void 0:o.tone)&&void 0!==t?t:"danger";return(0,r.jsxs)(s.Provider,{value:u,children:[l,o&&(0,r.jsx)(i.Z,{open:!0,title:o.title,description:o.description,consequences:"danger"===p?o.consequences:void 0,confirmLabel:null!==(n=o.confirmLabel)&&void 0!==n?n:"danger"===p?"삭제":"확인",cancelLabel:o.cancelLabel,tone:p,onConfirm:()=>m(!0),onCancel:()=>m(!1)})]})}},70809:function(e,t,n){n.d(t,{x:function(){return ef},Y:function(){return eb}});var r=n(57437),a=n(2265),i=n(98319),s=n(54887),l=n(66337),o=n(41905),d=n(32489),c=n(48736),u=n(55900),m=n(88501),p=n(28842),x=n(69076),h=n(30401),f=n(93448),b=n(30166),g=n(53834),v=n(86315),y=n(83008),k=n(67920),w=n(34137),j=n(93627),N=n(10136),z=n(27526),C=n(98381),E=n(85929),S=n(97226),L=n(99897),A=n(2665),D=n(80664),T=n(70287),R=n(39994),B=n(76283),O=n(99098);function M(){var e,t,n,s;let{data:l,update:o}=(0,z.kP)(),{theme:d,setTheme:c}=(0,C.F)(),u=(0,k.XC)(),m=(0,N.N)(),{showToast:p}=(0,i.p)(),{isResetting:x,isResetModalOpen:h,resetStep:b,saveNeedsShareHint:g,openResetModal:v,closeResetModal:w,handleSaveAndReset:j,handleSaveFile:M,handleConfirmSavedAndReset:I,handleResetOnly:F,filename:P}=(0,B.v)(),[Z,G]=(0,a.useState)(!1),[X,K]=(0,a.useState)(!1),q=async()=>{if(await m({title:"계정을 삭제할까요?",description:"탈퇴하면 이 계정으로 만든 모든 기록이 영구적으로 사라집니다. 되돌릴 수 없습니다.",consequences:["학생 명단","성적\xb7출결 기록","상담 일지","세특\xb7행동특성","설문과 응답","교무수첩 보관함"],confirmLabel:"계정 삭제"})){G(!0);try{let e=await (0,y.X)("deleteAccount",{confirmText:"탈퇴"},u);e.success?(p("계정을 삭제했습니다. 그동안 이용해 주셔서 감사합니다.","success"),await (0,z.w7)({callbackUrl:"/"})):(p(e.error||"계정 삭제 실패","error"),G(!1))}catch(e){console.error(e),p("오류가 발생했습니다.","error"),G(!1)}}},H=null==l?void 0:null===(e=l.user)||void 0===e?void 0:e.paidUntil,W=null==l?void 0:null===(t=l.user)||void 0===t?void 0:t.recurringStatus,U=e=>new Date(e).toLocaleDateString("ko-KR",{year:"numeric",month:"long",day:"numeric"});return(0,r.jsxs)("div",{className:"space-y-[19px] pt-6 animate-in fade-in slide-in-from-right-4 duration-300",children:[(0,r.jsxs)("div",{className:"flex items-center justify-between h-8",children:[(0,r.jsx)("span",{className:"text-[14px] text-[#E0E0E0] font-medium",children:"계정"}),(0,r.jsx)("div",{className:"flex flex-col items-end gap-1",children:(0,r.jsx)("span",{className:"text-[14px] text-zinc-500",children:(null==l?void 0:null===(n=l.user)||void 0===n?void 0:n.email)||"[email protected]"})})]}),(0,r.jsxs)("div",{className:"flex items-center justify-between h-8",children:[(0,r.jsx)("span",{className:"text-[14px] text-[#E0E0E0] font-medium",children:"테마"}),(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsxs)("button",{onClick:()=>c("light"),className:(0,f.cn)("flex items-center gap-1.5 text-[13px] transition-colors","light"===d?"text-black dark:text-white font-bold":"text-zinc-500 font-medium hover:text-black dark:hover:text-zinc-200"),children:[(0,r.jsx)(E.Z,{size:14}),"라이트"]}),(0,r.jsx)("div",{className:"w-[1px] h-3 bg-zinc-300 dark:bg-zinc-700"}),(0,r.jsxs)("button",{onClick:()=>c("dark"),className:(0,f.cn)("flex items-center gap-1.5 text-[13px] transition-colors","dark"===d?"text-black dark:text-white font-bold":"text-zinc-500 font-medium hover:text-black dark:hover:text-zinc-200"),children:[(0,r.jsx)(S.Z,{size:14}),"다크"]}),(0,r.jsx)("div",{className:"w-[1px] h-3 bg-zinc-300 dark:bg-zinc-700"}),(0,r.jsxs)("button",{onClick:()=>c("system"),className:(0,f.cn)("flex items-center gap-1.5 text-[13px] transition-colors","system"===d?"text-black dark:text-white font-bold":"text-zinc-500 font-medium hover:text-black dark:hover:text-zinc-200"),children:[(0,r.jsx)(L.Z,{size:14}),"시스템"]})]})]}),(0,r.jsxs)("div",{className:"flex items-center justify-between h-8",children:[(0,r.jsx)("span",{className:"text-[14px] text-[#E0E0E0] font-medium",children:"플랜 안내"}),"ACTIVE"===W?(0,r.jsxs)("div",{className:"flex flex-col items-end gap-0.5",children:[(0,r.jsx)("span",{className:"text-[13px] text-zinc-300 font-medium",children:"정기구독 중"}),H&&(0,r.jsxs)("span",{className:"text-[12px] text-zinc-500",children:["다음 결제 예정: ",U(H)]})]}):(0,A.O)(H)?(0,r.jsxs)("div",{className:"flex flex-col items-end gap-0.5",children:[(0,r.jsx)("span",{className:"text-[13px] text-zinc-300 font-medium",children:"유료 이용 중"}),H&&(0,r.jsxs)("span",{className:"text-[12px] text-zinc-500",children:[U(H)," 까지"]})]}):(0,r.jsx)("span",{className:"text-[13px] text-zinc-500 font-medium",children:"무료 플랜"})]}),(0,r.jsxs)("div",{className:"flex items-center justify-between h-8",children:[(0,r.jsx)("span",{className:"text-[14px] text-[#E0E0E0] font-medium",children:"이용권 코드 등록"}),(0,r.jsx)("button",{onClick:()=>K(!0),className:"text-[13px] text-zinc-500 font-medium hover:text-zinc-300 transition-colors",children:"코드 입력"})]}),(null==l?void 0:null===(s=l.user)||void 0===s?void 0:s.schoolId)&&(0,r.jsxs)("div",{className:"flex items-center justify-between h-8",children:[(0,r.jsx)("div",{className:"flex flex-col gap-0.5",children:(0,r.jsx)("span",{className:"text-[14px] text-[#E0E0E0] font-medium",children:"학교 연동 정보"})}),(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)("span",{className:"text-[13px] text-white font-medium px-2",children:l.user.schoolName||"학교 정보 없음"}),(0,r.jsx)("button",{onClick:async()=>
1{if(await m({title:"학교 연동을 해제할까요?",description:"내가 만든 기록은 그대로 남고, 학교와의 연결만 끊어집니다.",confirmLabel:"연동 해제"}))try{let e=await (0,y.X)("leaveSchool",{confirm:!0},u);e.success?(p(e.message,"success"),await o()):p(e.error||"오류가 발생했습니다.","error")}catch(e){console.error(e),p("오류가 발생했습니다.","error")}},className:"h-[26px] px-3 rounded-md bg-red-500/10 border border-red-500/20 text-[13px] text-red-500 hover:bg-red-500/20 transition font-medium whitespace-nowrap",children:"해지"})]})]}),(0,r.jsx)(T.J,{}),(0,r.jsxs)("div",{className:"flex items-center justify-between h-8",children:[(0,r.jsx)("span",{className:"text-[14px] text-[#E0E0E0] font-medium",children:"학생 초기화"}),(0,r.jsx)("button",{onClick:v,disabled:x||Z,className:"text-[13px] text-zinc-500 hover:text-zinc-300 disabled:opacity-50 font-medium transition-colors",children:x?"처리 중…":"초기화"})]}),(0,r.jsx)(R.C,{isOpen:h,step:b,onSaveAndReset:j,onResetOnly:F,onSaveFile:M,onConfirmSavedAndReset:I,onCancel:w,isProcessing:x,saveNeedsShareHint:g,filename:P}),(0,r.jsxs)("div",{className:"flex items-center justify-between h-8",children:[(0,r.jsx)("span",{className:"text-[14px] text-[#E0E0E0] font-medium",children:"계정 탈퇴"}),(0,r.jsx)("button",{onClick:q,disabled:Z||x,className:"text-[13px] text-zinc-500 disabled:opacity-50 font-medium underline-offset-4",children:Z?"처리중...":"탈퇴하기"})]}),(0,r.jsx)(D.g,{isOpen:X,onClose:()=>K(!1)}),(0,r.jsx)(O.l,{})]})}var I=n(285),F=n(78867),P=n(87769),Z=n(42208),G=n(91723),X=n(53470),K=n(80221),q=n(40875),H=n(75368),W=n(51817),U=n(92934),V=n(99388),_=n(42488),J=n(18930),Y=n(99397),Q=n(71947);function $(e){var t;let{size:n=16,className:a="",...i}=e;return(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:n,height:n,viewBox:"0 0 430 430",fill:"none",stroke:"currentColor",className:a,...i,children:(0,r.jsxs)("g",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:null!==(t=i.strokeWidth)&&void 0!==t?t:24,children:[(0,r.jsx)("path",{d:"M315.08 174.97v67.37c0 36.49-29.06 66.06-64.91 66.06l-70.34-.15c-35.85 0-64.91-29.58-64.91-66.06v-67.22m100.081 173.46-.13 53.38"}),(0,r.jsx)("path",{d:"M255.031 134.94V54.88c0-7.37 5.97-13.34 13.34-13.34s13.34 5.97 13.34 13.34v80.06m-133.43 0V54.88c0-7.37 5.97-13.34 13.34-13.34s13.34 5.97 13.34 13.34v80.06"}),(0,r.jsx)("path",{d:"M241.691 308.4v13.34c0 14.74-11.95 26.69-26.69 26.69s-26.69-11.95-26.69-26.69V308.4M328.43 134.94H101.58c-11.05 0-20.01 8.959-20.01 20.01v.01c0 11.051 8.96 20.01 20.01 20.01h226.85c11.052 0 20.01-8.959 20.01-20.01v-.01c0-11.051-8.958-20.01-20.01-20.01"})]})})}function ee(e){var t;let{size:n=16,className:a="",...i}=e;return(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:n,height:n,viewBox:"0 0 430 430",fill:"none",stroke:"currentColor",className:a,...i,children:(0,r.jsxs)("g",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:null!==(t=i.strokeWidth)&&void 0!==t?t:24,children:[(0,r.jsx)("path",{d:"m202.7 250.121 104.56-104.99 20.53 20.53 22.21-22.21-12.76-12.76 22.22-22.22 12.76 12.76 22.22-22.22-20.53-20.53 9.12-9.22c4.34-4.34 4.34-11.37 0-15.71l-10.3-10.3c-4.34-4.34-11.37-4.34-15.71 0l-180.33 180.86"}),(0,r.jsx)("path",{d:"m58.45 368.212-.09-.09m128.871-76.12 19.94-19.68c4.9-4.9 4.89-12.83 0-17.73l-34.96-34.96c-4.9-4.9-12.83-4.9-17.73 0l-19.94 19.68m31.14 21.34c29.67 29.67 29.67 77.76 0 107.43s-77.76 29.66-107.43 0-29.67-77.76 0-107.43c29.66-29.66 77.76-29.66 107.43 0m-21.73 21.72c17.67 17.67 17.67 46.31 0 63.98s-46.32 17.67-63.98 0-17.67-46.31 0-63.98 46.32-17.66 63.98 0"})]})})}function et(){var e,t,n;let{showToast:l}=(0,i.p)(),o=(0,N.N)(),d=(0,k.XC)(),[c,u]=(0,a.useState)([]),[m,p]=(0,a.useState)(!0),[x,b]=(0,a.useState)(!1),[g,v]=(0,a.useState)(""),[w,j]=(0,a.useState)(!0),[z,C]=(0,a.useState)(!1),[E,S]=(0,a.useState)(null),[L,A]=(0,a.useState)(!1),[D,T]=(0,a.useState)(!1),[R,B]=(0,a.useState)([]),[O,M]=(0,a.useState)(!1),[I,$]=(0,a.useState)("claude"),[et,en]=(0,a.useState)(!1),[er,ea]=(0,a.useState)(!1),[ei,es]=(0,a.useState)("claude"),[el,eo]=(0,a.useState)(!1),[ed,ec]=(0,a.useState)(!1),[eu,em]=(0,a.useState)({}),ep=async()=>{p(!0);try{let e=await (0,y.X)("listMcpApiKeys",{},d);e.success&&e.data?(u(e.data),e.data.length>0&&M(!0)):l(e.error||"키 목록을 불러오지 못했습니다.","error")}catch(e){console.error(e),l("키 목록 조회 중 오류가 발생했습니다.","error")}finally{p(!1)}},ex=async()=>{try{let e=await (0,y.X)("listOAuthConnections",{},d);e.success&&e.data&&B(e.data)}catch(e){console.error(e)}};(0,a.useEffect)(()=>{ep(),ex()},[]);let eh=async(e,t)=>{if(await o({title:"연결 끊기",description:"'".concat(t,"'의 내모 접근을 지금 끊습니다. 다시 쓰시려면 그 앱에서 한 번 더 연결을 허용해야 합니다."),confirmLabel:"연결 끊기",cancelLabel:"취소",tone:"danger"}))try{let t=await (0,y.X)("revokeOAuthConnection",{clientId:e},d);t.success?(l("연결을 끊었습니다.","success"),ex()):l(t.error||"연결을 끊지 못했습니다.","error")}catch(e){console.error(e),l("연결을 끊는 중 오류가 발생했습니다.","error")}},ef=async e=>{if(e.preventDefault(),!g.trim()){l("키의 이름 또는 용도를 입력해주세요.","error");return}b(!0);try{let e=await (0,y.X)("createMcpApiKey",{name:g.trim(),maskPii:w},d);e.success&&e.data?(S(e.data.apiKey),v(""),M(!0),l("새 연결 키가 발급되었습니다.","success"),ep()):l(e.error||"키 발급에 실패했습니다.","error")}catch(e){console.error(e),l("키 발급 중 오류가 발생했습니다.","error")}finally{b(!1)}},eb=async(e,t)=>{let n=!t;try{let t=await (0,y.X)("toggleMcpApiKeyMaskPii",{id:e,maskPii:n},d);t.success?(u(t=>t.map(t=>
1t.id===e?{...t,maskPii:n}:t)),l(t.message||"개인정보 보호 설정이 변경되었습니다.","success")):l(t.error||"설정 변경에 실패했습니다.","error")}catch(e){console.error(e),l("설정 변경 중 오류가 발생했습니다.","error")}finally{p(!1)}},eg=async(e,t)=>{if(await o({title:"연결 키 삭제",description:"'".concat(t,"' 키를 정말 삭제하시겠습니까? 이 키를 사용하는 외부 AI 도구와의 연결이 즉시 차단됩니다."),confirmLabel:"삭제",cancelLabel:"취소",tone:"danger"}))try{let t=await (0,y.X)("deleteMcpApiKey",{id:e},d);t.success?(l("키가 삭제되었습니다.","success"),ep()):l(t.error||"키 삭제에 실패했습니다.","error")}catch(e){console.error(e),l("키 삭제 중 오류가 발생했습니다.","error")}},ev=async e=>{em(e=>({...e,new:{testing:!0}}));let t=performance.now();try{let n=await fetch("/api/mcp",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify({jsonrpc:"2.0",id:999,method:"ping"})}),r=Math.round(performance.now()-t);if(n.ok)em(e=>({...e,new:{testing:!1,success:!0,latency:r}})),l("이 키로 내모에 정상 연결됩니다. (응답 ".concat(r,"ms)"),"success");else{let e=401===n.status?"키가 거부되었습니다":"서버 응답 ".concat(n.status);em(t=>({...t,new:{testing:!1,success:!1,error:e}})),l("연결 확인 실패: ".concat(e),"error")}}catch(e){em(t=>({...t,new:{testing:!1,success:!1,error:e.message}})),l("연결 확인 실패: ".concat(e.message),"error")}},ey=async e=>{em(t=>({...t,[e]:{testing:!0}}));try{let t=await (0,y.X)("testMcpApiKey",{id:e},d);if(!t.success||!t.data){em(n=>({...n,[e]:{testing:!1,success:!1,error:t.error||"확인 실패"}})),l(t.error||"키 상태를 확인하지 못했습니다.","error");return}let{state:n,message:r}=t.data;em(t=>({...t,[e]:{testing:!1,success:"active"===n,error:"active"===n?void 0:r}})),l(r,"expired"===n?"error":"active"===n?"success":"info"),"expired"!==n&&ep()}catch(t){em(n=>({...n,[e]:{testing:!1,success:!1,error:t.message}})),l("키 상태 확인 중 오류가 발생했습니다: ".concat(t.message),"error")}},ek=e=>{navigator.clipboard.writeText(e),A(!0),setTimeout(()=>A(!1),2e3),l("클립보드에 복사되었습니다.","success")},ew=window.location.origin,ej="".concat(ew,"/api/mcp"),eN=function(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n='    "naemo": {\n      "command": "npx",\n      "args": [\n        "-y",\n        "mcp-remote",\n        "'.concat(ej,'",\n        "--header",\n        "Authorization: Bearer ').concat(e,'"\n      ]\n    }');return t?'{\n  "mcpServers": {\n'.concat(n,"\n  }\n}"):"  ".concat(n.trim())},ez={claude:{mac:"~/Library/Application Support/Claude/claude_desktop_config.json",win:"%APPDATA%\\Claude\\claude_desktop_config.json",menu:"Claude 메뉴 → Settings(설정) → Developer(개발자) → Edit Config 클릭"}},eC="mt-6 pt-6 border-t border-zinc-200 dark:border-[#27272a]",eE="text-[14px] font-medium text-zinc-900 dark:text-white",eS="text-[13px] text-zinc-500 dark:text-[#a1a1aa] leading-relaxed";return(0,r.jsxs)("div",{className:"animate-in fade-in slide-in-from-right-4 duration-300 pb-8",children:[(0,r.jsxs)("div",{className:"border-b border-zinc-200 dark:border-[#27272a] pb-4",children:[(0,r.jsx)("h3",{className:"text-[17px] font-medium text-zinc-900 dark:text-white",children:"MCP"}),(0,r.jsx)("p",{className:"text-[13px] text-zinc-500 dark:text-[#71717a] mt-1",children:"MCP 서버를 통해 자주 사용하는 AI와 내모를 연결하세요."})]}),(0,r.jsxs)("section",{className:"pt-6",children:[(0,r.jsx)("h4",{className:eE,children:"주소만으로 연결하기"}),(0,r.jsx)("p",{className:(0,f.cn)(eS,"mt-1"),children:"쓰시는 AI 앱에 이 주소를 넣으면 내모 로그인 화면이 뜹니다. 허용을 누르면 바로 연결됩니다."}),(0,r.jsxs)("div",{className:"mt-3 flex items-center gap-2 rounded-lg border border-zinc-200 dark:border-[#27272a] bg-white dark:bg-[#18181b] py-2 pl-3 pr-1.5",children:[(0,r.jsx)("code",{className:"flex-1 min-w-0 font-mono text-[12.5px] text-zinc-700 dark:text-zinc-300 truncate select-all",children:ej}),(0,r.jsx)("button",{type:"button",onClick:()=>{navigator.clipboard.writeText(ej),T(!0),setTimeout(()=>T(!1),2e3),l("MCP 주소가 복사되었습니다.","success")},className:"shrink-0 p-1.5 rounded-md text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-[#27272a] transition-colors",title:"주소 복사",children:D?(0,r.jsx)(h.Z,{size:14,className:"text-emerald-500"}):(0,r.jsx)(F.Z,{size:14})})]}),(0,r.jsx)("dl",{className:"mt-3 space-y-1.5",children:[{app:"Claude (앱\xb7웹)",where:"설정 → 커넥터 → 사용자 지정 커넥터 추가"},{app:"ChatGPT",where:"설정 → 커넥터 → 추가"},{app:"그 밖의 AI 앱",where:"MCP 또는 커넥터 설정에서 주소를 넣는 칸을 찾으세요"}].map(e=>{let{app:t,where:n}=e;return(0,r.jsxs)("div",{className:"flex items-baseline gap-2 text-[12.5px] leading-relaxed",children:[(0,r.jsx)("dt",{className:"shrink-0 w-[108px] text-zinc-700 dark:text-zinc-300",children:t}),(0,r.jsx)("dd",{className:"min-w-0 text-zinc-500 dark:text-[#a1a1aa]",children:n})]},t)})}),(0,r.jsxs)("p",{className:(0,f.cn)(eS,"mt-3"),children:["주소를 넣으면 내모 로그인 화면이 뜹니다. 이때 ",(0,r.jsx)("strong",{className:"font-medium text-zinc-700 dark:text-zinc-200",children:"학생 실명을 가릴지"}),"와",(0,r.jsx)("strong",{className:"font-medium text-zinc-700 dark:text-zinc-200",children:" 저장까지 맡길지"}),"를 고르실 수 있습니다. 읽기만 허용하면 그 앱은 출결이나 세특 초안을 건드리지 못합니다."]}),R.length>0&&(0,r.jsxs)("div",{className:"mt-5",children:[(0,r.jsxs)("p",{className:"text-[12px] text-zinc-400 dark:text-[#71717a]",children:["연결된 앱 ",R.length,"개"]}),(0,r.jsx)("ul",{className:"mt-1 divide-y divide-zinc-100 dark:divide-[#27272a]",children:R.map(e=>(0,r.jsxs)("li",{className:"py-3 flex items-start justify-between gap-3",children:[(0,r.jsxs)("div",{className:"min-w-0",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2 flex-wrap",children:[(0,r.jsx)("span",{className:"text-[13px] font-medium text-zinc-900 dark:text-white truncate",children:e.clientName}),(0,r.jsxs)("span",{className:(0,f.cn)("inline-flex items-center gap-1 text-[11px]",e.maskPii?"text-zinc-400 dark:text-[#71717a]":"text-amber-600 dar
1k:text-amber-500"),children:[e.maskPii?(0,r.jsx)(P.Z,{size:11}):(0,r.jsx)(Z.Z,{size:11}),e.maskPii?"실명 가림":"실명 그대로"]})]}),(0,r.jsxs)("div",{className:"mt-1 flex items-center gap-2 flex-wrap text-[12px] text-zinc-400 dark:text-[#71717a]",children:[(0,r.jsxs)("span",{children:[new Date(e.connectedAt).toLocaleDateString("ko-KR")," 연결"]}),(0,r.jsx)("span",{children:"\xb7"}),(0,r.jsxs)("span",{className:"flex items-center gap-1",children:[(0,r.jsx)(G.Z,{size:11}),e.lastUsedAt?"최근 사용 ".concat(new Date(e.lastUsedAt).toLocaleString("ko-KR")):"사용 이력 없음"]})]})]}),(0,r.jsxs)("button",{type:"button",onClick:()=>eh(e.clientId,e.clientName),className:"flex items-center gap-1.5 shrink-0 text-[12px] px-2 py-1 -mr-2 rounded-md text-zinc-500 dark:text-[#a1a1aa] hover:text-red-600 dark:hover:text-red-400 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors",children:[(0,r.jsx)(X.Z,{size:13}),"연결 끊기"]})]},e.clientId))})]})]}),(0,r.jsxs)("section",{className:eC,children:[(0,r.jsx)("h4",{className:eE,children:"AI에게 이렇게 질문해보세요"}),(0,r.jsx)("ul",{className:"mt-2 space-y-1.5",children:["오늘 3반 15번 김철수 감기로 병결 처리해줘. 진단서는 내일 제출 예정","어제 지각으로 등록된 5번 이영희 기록, 담임 인정지각으로 변경해줘","성적 골고루 섞이게 6개 모둠으로 자리 배치해줘","우리 반 애들 출결이랑 성적 상관관계 분석해서 그래프로 보여줘","상담일지랑 메모 바탕으로 학부모 상담 때 이야기할 학생별 특징 정리해줘","OO이 이번 학기 활동 기록 바탕으로 기재요령에 맞게 세특 초안 써서 내모에 저장해줘"].map((e,t)=>(0,r.jsxs)("li",{className:"text-[13px] text-zinc-600 dark:text-zinc-400 leading-relaxed",children:["“",e,"”"]},t))})]}),(0,r.jsxs)("section",{className:eC,children:[(0,r.jsxs)("button",{type:"button",onClick:()=>ea(e=>!e),"aria-expanded":er,className:"w-full flex items-start justify-between gap-3 text-left group",children:[(0,r.jsxs)("div",{className:"min-w-0",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)(K.Z,{size:16,className:"text-zinc-500 dark:text-[#a1a1aa]"}),(0,r.jsx)("h4",{className:eE,children:"설정 파일을 직접 고치는 방법"})]}),(0,r.jsx)("p",{className:(0,f.cn)(eS,"mt-1"),children:"위의 주소 방식이 먼저입니다. 쓰시는 도구에 주소를 넣을 칸이 없을 때만 설정 파일에 아래 코드를 붙여넣으세요. 이 방법은 아래 ‘연결 키’가 함께 필요합니다."})]}),(0,r.jsx)(q.Z,{size:16,className:(0,f.cn)("shrink-0 mt-0.5 text-zinc-400 transition-transform group-hover:text-zinc-600 dark:group-hover:text-zinc-200",er&&"rotate-180")})]}),er&&(0,r.jsxs)("div",{className:"mt-4 space-y-4",children:[(0,r.jsx)("div",{className:"flex gap-1.5 p-1 bg-zinc-100 dark:bg-[#1f1f23] rounded-lg w-fit",children:["claude","cursor","antigravity"].map(e=>(0,r.jsx)("button",{type:"button",onClick:()=>es(e),className:(0,f.cn)("px-3 py-1.5 rounded-md text-[12.5px] font-medium transition-all",ei===e?"bg-white dark:bg-[#27272a] text-zinc-900 dark:text-white shadow-xs":"text-zinc-500 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-white"),children:"claude"===e?"Claude Desktop":"cursor"===e?"Cursor / VS Code":"Antigravity"},e))}),(0,r.jsxs)("div",{className:"rounded-xl border border-zinc-200 dark:border-[#27272a] bg-zinc-50/60 dark:bg-[#141416] p-4 space-y-3",children:[(0,r.jsxs)("div",{className:"space-y-1",children:[(0,r.jsxs)("h5",{className:"text-[13px] font-medium text-zinc-900 dark:text-white flex items-center gap-1.5",children:[(0,r.jsx)(H.Z,{size:14,className:"text-zinc-400"}),"설정 파일 위치 및 여는 방법"]}
1),"claude"===ei&&(0,r.jsxs)("div",{className:"text-[12px] text-zinc-600 dark:text-zinc-400 space-y-1.5 leading-relaxed",children:[(0,r.jsxs)("p",{className:"text-zinc-500 dark:text-zinc-500",children:["Claude는 ",(0,r.jsx)("strong",{children:"설정 → 커넥터 → 사용자 지정 커넥터 추가"}),"에 위의 주소만 넣으면 끝입니다. 아래는 그 방법이 막혔을 때 쓰는 우회로입니다."]}),(0,r.jsxs)("p",{children:["Claude 상단 메뉴에서 ",(0,r.jsx)("strong",{children:"Settings(설정) → Developer(개발자) → Edit Config"})," 버튼을 누르면 설정 파일이 열립니다."]}),(0,r.jsxs)("div",{className:"mt-2 space-y-1 font-mono text-[11.5px] text-zinc-600 dark:text-zinc-300",children:[(0,r.jsxs)("div",{className:"flex items-center justify-between gap-2 p-1.5 bg-white dark:bg-zinc-900 rounded border border-zinc-200 dark:border-zinc-800",children:[(0,r.jsxs)("span",{className:"truncate",children:["macOS: ",ez.claude.mac]}),(0,r.jsx)("button",{type:"button",onClick:()=>ek(ez.claude.mac),className:"shrink-0 text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 p-1",title:"macOS 경로 복사",children:(0,r.jsx)(F.Z,{size:12})})]}),(0,r.jsxs)("div",{className:"flex items-center justify-between gap-2 p-1.5 bg-white dark:bg-zinc-900 rounded border border-zinc-200 dark:border-zinc-800",children:[(0,r.jsxs)("span",{className:"truncate",children:["Windows: ",ez.claude.win]}),(0,r.jsx)("button",{type:"button",onClick:()=>ek(ez.claude.win),className:"shrink-0 text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 p-1",title:"Windows 경로 복사",children:(0,r.jsx)(F.Z,{size:12})})]})]})]}),"cursor"===ei&&(0,r.jsxs)("div",{className:"text-[12px] text-zinc-600 dark:text-zinc-400 space-y-1 leading-relaxed",children:[(0,r.jsxs)("p",{children:[(0,r.jsx)("strong",{children:"Cursor Settings → Features → MCP Servers → Edit config"}),"를 클릭하거나 프로젝트 루트의 ",(0,r.jsx)("code",{className:"font-mono text-[11.5px] bg-white dark:bg-zinc-900 px-1 py-0.5 rounded border border-zinc-200 dark:border-zinc-800",children:".cursor/mcp.json"})," 파일을 엽니다."]}),(0,r.jsx)("p",{className:"text-zinc-400 dark:text-zinc-500",children:"VS Code(Roo Code, Cline 등)를 쓰실 경우에도 동일한 형식으로 확장 프로그램 MCP 설정에 등록할 수 있습니다."})]}),"antigravity"===ei&&(0,r.jsx)("div",{className:"text-[12px] text-zinc-600 dark:text-zinc-400 space-y-1 leading-relaxed",children:(0,r.jsxs)("p",{children:["Antigravity 작업 영역 또는 전역 MCP 설정 파일(",(0,r.jsx)("code",{className:"font-mono text-[11.5px] bg-white dark:bg-zinc-900 px-1 py-0.5 rounded border border-zinc-200 dark:border-zinc-800",children:"mcp.json"}),")에 내모 MCP 서버를 추가합니다."]})})]}),(0,r.jsxs)("div",{className:"space-y-1.5 pt-1",children:[(0,r.jsxs)("div",{className:"flex items-center justify-between",children:[(0,r.jsx)("label",{className:"text-[12px] font-medium text-zinc-700 dark:text-zinc-300",children:"설정 파일에 추가할 JSON 코드"}),(0,r.jsx)("span",{className:"text-[11px] text-zinc-400",children:c.length>0?"가장 최근 발급 키가 반영됨":"키 발급 후 비밀 키를 넣어주세요"})]}),(0,r.jsxs)("div",{className:"relative",children:[(0,r.jsx)("pre",{className:"text-[11.5px] font-mono p-3 rounded-lg bg-white dark:bg-[#18181b] border border-zinc-200 dark:border-zinc-800 text-zinc-800 dark:text-zinc-200 overflow-x-auto select-all max-h-[160px] custom-scrollbar leading-normal",children:eN(c[0]?"<".concat(c[0].name,"의_비밀키>"):"<발급받은_연결키>",!0)}),(0,r.jsxs)("button",{type:"button",onClick:()=>{let e=eN(c[0]?"<".concat(c[0].name,"의_비밀키>"):"<발급받은_연결키>",!0);navigator.clipboard.writeText(e),eo(!0),setTimeout(()=>eo(!1),2e3),l("전체 설정 코드가 복사되었습니다.","success")},className:"absolute top-2.5 right-2.5 flex items-center gap-1.5 text-[11.5px] px-2.5 py-1 rounded-md bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors shadow-xs font-medium",title:"전체 설정 코드 복사",children:[el?(0,r.jsx)(h.Z,{size:13,className:"text-emerald-500"}):(0,r.jsx)(F.Z,{size:13}),(0,r.jsx)("span",{children:el?"복사 완료":"코드 복사"})]})]}),(0,r.jsxs)("p",{className:"text-[11.5px] text-zinc-400 dark:text-[#71717a] pt-1",children:["\uD83D\uDCA1 키 발급 시 생성되는 팝업에서 ",(0,r.jsx)("strong",{children:"내 비밀키가 자동으로 채워진 완성 코드"}),"를 원클릭 복사할 수도 있습니다."]})]})]})]})]}),(0,r.jsxs)("section",{className:eC,children:[(0,r.jsxs)("button",{type:"button",onClick:()=>M(e=>!e),"aria-expanded":O,className:"w-full flex items-start justify-between gap-3 text-left group",children:[(0,r.jsxs)("div",{className:"min-w-0",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)(ee,{size:16,className:"text-zinc-500 dark:text-[#a1a1aa]"}),(0,r.jsx)("h4",{className:eE,children:"연결 키로 연결하기"}),c.length>0&&(0,r.jsxs)("span",{className:"text-[12px] text-zinc-400 dark:text-[#71717a]",children:[c.length,"개"]})]}),(0,r.jsx)("p",{className:(0,f.cn)(eS,"mt-1"),children:"주소 입력을 지원하지 않는 도구이거나, 로그인 화면을 띄울 수 없는 곳에서 쓰는 방법입니다."})]}),(0,r.jsx)(q.Z,{size:16,className:(0,f.cn)("shrink-0 mt-0.5 text-zinc-400 transition-transform group-hover:text-zinc-600 dark:group-hover:text-zinc-200",O&&"rotate-180")}
1)]}),O&&(0,r.jsxs)("div",{className:"mt-4",children:[m?(0,r.jsx)("p",{className:"py-6 text-[13px] text-zinc-400",children:"연결 키 목록을 불러오는 중..."}):0===c.length?(0,r.jsx)("p",{className:(0,f.cn)(eS,"max-w-lg"),children:"아직 발급한 연결 키가 없습니다. 대부분의 AI 앱은 위의 주소만으로 연결되니, 주소를 넣을 칸이 없는 도구를 쓰실 때만 키를 만드세요."}):(0,r.jsx)("ul",{className:"divide-y divide-zinc-100 dark:divide-[#27272a]",children:c.map(e=>{let t=eu[e.id];return(0,r.jsxs)("li",{className:"py-3.5",children:[(0,r.jsxs)("div",{className:"flex items-center justify-between gap-3",children:[(0,r.jsxs)("div",{className:"flex items-center gap-2 flex-wrap min-w-0",children:[(0,r.jsx)("span",{className:"text-[13px] font-medium text-zinc-900 dark:text-white",children:e.name}),(0,r.jsx)("code",{className:"font-mono text-[11px] text-zinc-400 dark:text-[#71717a]",children:e.keyPrefix}),(0,r.jsxs)("button",{onClick:()=>eb(e.id,e.maskPii),className:(0,f.cn)("flex items-center gap-1 text-[11px] px-1.5 py-0.5 -mx-1 rounded-md transition-colors hover:bg-zinc-100 dark:hover:bg-[#27272a]",e.maskPii?"text-zinc-400 dark:text-[#71717a]":"text-amber-600 dark:text-amber-500"),title:"눌러서 실명 마스킹을 켜고 끕니다",children:[e.maskPii?(0,r.jsx)(P.Z,{size:11}):(0,r.jsx)(Z.Z,{size:11}),e.maskPii?"실명 가림":"실명 그대로"]})]}),(0,r.jsxs)("div",{className:"flex items-center gap-1 shrink-0",children:[(0,r.jsx)("button",{onClick:()=>ey(e.id),disabled:null==t?void 0:t.testing,className:"flex items-center gap-1 text-[12px] px-2 py-1 rounded-md text-zinc-500 dark:text-[#a1a1aa] hover:bg-zinc-100 dark:hover:bg-[#27272a] transition-colors",children:(null==t?void 0:t.testing)?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(W.Z,{size:12,className:"animate-spin text-[#007AFF]"}),(0,r.jsx)("span",{children:"확인 중"})]}):(null==t?void 0:t.success)?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(U.Z,{size:12,className:"text-emerald-500"}),(0,r.jsx)("span",{className:"text-emerald-600 dark:text-emerald-400",children:"사용 중"})]}):(null==t?void 0:t.error)?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(V.Z,{size:12,className:"text-amber-500"}),(0,r.jsx)("span",{className:"text-amber-600 dark:text-amber-500 max-w-[160px] truncate",title:t.error,children:"확인 필요"})]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(_.Z,{size:12}),(0,r.jsx)("span",{children:"상태 확인"})]})}),(0,r.jsx)("button",{onClick:()=>eg(e.id,e.name),title:"키 삭제",className:"p-1.5 rounded-md text-zinc-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors",children:(0,r.jsx)(J.Z,{size:14})})]})]}),(0,r.jsxs)("div",{className:"mt-1 flex items-center gap-2 flex-wrap text-[12px] text-zinc-400 dark:text-[#71717a]",children:[(0,r.jsxs)("span",{children:[new Date(e.createdAt).toLocaleDateString("ko-KR")," 발급"]}),(0,r.jsx)("span",{children:"\xb7"}),(0,r.jsxs)("span",{children:[e.requestCount.toLocaleString(),"회 사용"]}),e.lastToolName&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("span",{children:"\xb7"}),(0,r.jsxs)("span",{className:"font-mono text-[11px]",children:["최근 도구 ",e.lastToolName]})]}),(0,r.jsx)("span",{children:"\xb7"}),(0,r.jsxs)("span",{className:"flex items-center gap-1",children:[(0,r.jsx)(G.Z,{size:11}),e.lastUsedAt?"최근 사용 ".concat(new Date(e.lastUsedAt).toLocaleString("ko-KR")):"사용 이력 없음"]})]})]},e.id)})}),(0,r.jsxs)("button",{onClick:()=>{S(null),C(!0)},className:"mt-3 flex items-center gap-1.5 text-[13px] px-2 py-1 -ml-2 rounded-md font-medium text-[#007AFF] dark:text-[#2883DE] hover:bg-[#007AFF]/10 dark:hover:bg-[#2883DE]/10 transition-colors",children:[(0,r.jsx)(Y.Z,{size:15}),"새 연결 키 발급"]})]})]}),z&&"undefined"!=typeof document&&(0,s.createPortal)((0,r.jsx)("div",{className:"modal-viewport z-[110] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm animate-in fade-in duration-150",children:(0,r.jsxs)("div",{className:"bg-white dark:bg-[#18181b] border border-zinc-200 dark:border-[#27272a] rounded-2xl w-full max-w-lg p-6 shadow-2xl space-y-5 animate-in zoom-in-95 duration-200",children:[(0,r.jsxs)("div",{children:[(0,r.jsx)("h4",{className:"text-[16px] font-semibold text-zinc-900 dark:text-white",children:E?"연결 키 발급 완료":"새 연결 키 발급"}),(0,r.jsx)("p",{className:"text-[13px] text-zinc-500 dark:text-zinc-400 mt-1",children:E?"아래 비밀 키는 지금 단 한 번만 표시됩니다. 지금 복사하여 안전한 곳에 보관하세요.":"외부 AI 도구(Cursor, Claude Desktop 등)에서 구분할 수 있는 이름을 지정해주세요."})]}),E?(0,r.jsxs)("div",{className:"space-y-4",children:[(0,r.jsxs)("div",{className:"space-y-1.5",children:[(0,r.jsx)("label",{className:"text-[12px] font-medium text-zinc-500",children:"발급된 비밀 키"}),(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)("input",{type:"text",readOnly:!0,value:E,className:"w-full font-mono text-[13px] p-2.5 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-900 text-zinc-900 dark:text-white select-all"}),(0,r.jsx)("button",{type:"button",onClick:()=>ek(E),className:"shrink-0 p-2.5 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white hover:bg-zinc-100 dark:hover:bg-[#27272a] transition-colors",title:"비밀 키 복사","aria-label":"비밀 키 복사",children:L?(0,r.jsx)(h.Z,{size:16,className:"text-emerald-500"}):(0,r.jsx)(F.Z,{size:16})})]})]}),(0,r.jsxs)("div",{className:"rounded-xl border border-zinc-200 dark:border-[#27272a] bg-zinc-50/70 dark:bg-[#121214] p-3.5 space-y-2.5",children:[(0,r.jsxs)("div",{className:"flex items-center justify-between gap-2 flex-wrap",children:[(0,r.jsxs)("span",{className:"text-[12.5px] font-medium text-zinc-800 dark:text-zinc-200 flex items-center gap-1.5",children:[(0,r.jsx)(Q.Z,{size:14,className:"text-zinc-500"}),"앱 설정에 바로 붙여넣기"]}),(0,r.jsx)("div",{className:"flex gap-1 bg-zinc-200/70 dark:bg-zinc-800 p-0.5 rounded-lg text-[11px]",children:["claude","cursor","antigravity"].map(e=>(0,r.jsx)("button",{type:"button",onClick:()=>$(e),className:(0,f.cn)("px-2 py-0.5 rounded-md font-medium transition-colors",I===e?"bg-white dark:bg-zinc-700 text-zinc-900 dark:text-white shadow-xs":"text-zinc-500 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-white"),children:"claude"===e?"Claude Desktop":"cursor"===e?"Cursor / VS Code":"Antigravity"},e))})]}),(0,r.jsxs)("p",{className:"text-[11.5px] text-zinc-500 dark:text-[#a1a1aa] leading-relaxed",children:["claude"===I&&(0,r.jsxs)(r.Fragment,{children:["Claude Desktop 메뉴 → ",(0,r.jsx)("strong",{className:"text-zinc-700 dark:text-zinc-300",children:"Settings(설정) → Developer(개발자) → Edit Config"}),"를 누른 후 아래 내용을 붙여넣으세요."]}),"cursor"===I&&(0,r.jsxs)(r.Fragment,{children:["Cursor 설정 → ",(0,r.jsx)("strong",{className:"text-zinc-700 dark:text-zinc-300",children:"Features → MCP Servers"}),"(또는 VS Code의 Cline/Roo 확장)에 아래 내용을 추가하세요."]}),"antigravity"===I&&(0,r.jsxs)(r.Fragment,{children:["Google Antigravity의 ",(0,r.jsx)("strong",{className:"text-zinc-700 dark:text-zinc-300",children:"mcp.json"})," 설정 파일에 아래 내용을 추가하세요."]})]}),(0,r.jsxs)("div",{className:"relative",children:[(0,r.jsx)("pre",{className:"text-[11px] font-mono p-2.5 rounded-lg bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 text-zinc-800 dark:text-zinc-200 overflow-x-auto select-all max-h-[130px] custom-scrollbar",children:eN(E,!1)}),(0,r.jsxs)("button",{type:"button",onClick:()=>{navigator.clipboard.writeText(eN(E,!1)),en(!0),setTimeout(()=>en(!1),2e3),l("설정 코드가 클립보드에 복사되었습니다.","success")},className:"absolute top-2 right-2 flex items-center gap-1 text-[11px] px-2 py-1 rounded bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors shadow-xs",title:"설정 코드 복사",children:[et?(0,r.jsx)(h.Z,{size:12,className:"text-emerald-500"}):(0,r.jsx)(F.Z,{size:12}
1),(0,r.jsx)("span",{children:et?"복사됨":"설정 코드 복사"})]})]})]}),(0,r.jsxs)("div",{className:"flex items-center justify-between pt-1",children:[(0,r.jsx)("span",{className:"text-[12px] text-zinc-500 dark:text-[#a1a1aa]",children:"발급된 키로 바로 확인해 보세요"}),(0,r.jsx)("button",{type:"button",onClick:()=>ev(E),disabled:null===(e=eu.new)||void 0===e?void 0:e.testing,className:"flex items-center gap-1.5 text-[12px] px-2 py-1 -mr-2 rounded-md font-medium text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-[#27272a] transition-colors",children:(null===(t=eu.new)||void 0===t?void 0:t.testing)?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(W.Z,{size:13,className:"animate-spin text-[#007AFF]"}),(0,r.jsx)("span",{children:"확인 중"})]}):(null===(n=eu.new)||void 0===n?void 0:n.success)?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(U.Z,{size:13,className:"text-emerald-500"}),(0,r.jsxs)("span",{className:"text-emerald-600 dark:text-emerald-400",children:[eu.new.latency,"ms 정상"]})]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(_.Z,{size:13}),(0,r.jsx)("span",{children:"연결 확인"})]})})]}),(0,r.jsx)("button",{type:"button",onClick:()=>{C(!1),S(null)},className:"btn-primary-solid w-full py-2.5 rounded-lg font-medium text-[14px] transition-colors",children:"확인하고 닫기"})]}):(0,r.jsxs)("form",{onSubmit:ef,className:"space-y-4",children:[(0,r.jsxs)("div",{className:"space-y-1.5",children:[(0,r.jsx)("label",{className:"text-[13px] font-medium text-zinc-700 dark:text-zinc-300",children:"키 이름 (용도)"}),(0,r.jsx)("input",{type:"text",placeholder:"예: 내 맥북 Cursor, Claude Desktop, 연구실 PC",value:g,onChange:e=>v(e.target.value),autoFocus:!0,className:"w-full text-[14px] p-2.5 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-white placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-[#007AFF]"})]}),(0,r.jsxs)("div",{className:"flex items-start gap-2.5",children:[(0,r.jsx)("input",{type:"checkbox",id:"maskPiiCheckbox",checked:w,onChange:e=>j(e.target.checked),className:"mt-1 rounded border-zinc-300 text-[#007AFF] focus:ring-[#007AFF]"}),(0,r.jsxs)("label",{htmlFor:"maskPiiCheckbox",className:"text-[12px] leading-relaxed text-zinc-600 dark:text-zinc-300 cursor-pointer",children:[(0,r.jsx)("strong",{className:"text-zinc-900 dark:text-white",children:"개인정보 보호(실명 가명화) 활성화 (권장)"}),(0,r.jsx)("br",{}),"외부 AI로 전송될 때 학생 실명이 ",(0,r.jsx)("code",{className:"bg-zinc-200 dark:bg-zinc-800 px-1 rounded text-[11px]",children:"김*수"})," 형태로 가명화되어 교육청 개인정보 가이드라인을 준수합니다."]})]}),(0,r.jsxs)("div",{className:"flex items-center justify-end gap-2 pt-2",children:[(0,r.jsx)("button",{type:"button",onClick:()=>C(!1),className:"px-4 py-2 rounded-lg text-[13px] text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors",children:"취소"}),(0,r.jsx)("button",{type:"submit",disabled:x||!g.trim(),className:"btn-primary-solid px-4 py-2 rounded-lg text-[13px] font-medium transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:x?"발급 중...":"발급하기"})]})]})]})}),document.body)]})}var en=n(98728),er=n(49724),ea=n(25466);let ei=()=>(0,r.jsxs)("div",{className:"flex flex-col items-center justify-center p-8 gap-3",children:[(0,r.jsx)(g.W,{size:40}),(0,r.jsx)("div",{className:"text-[13px] text-zinc-500",children:"불러오는 중..."})]}),es=(0,b.default)(()=>Promise.all([n.e(3100),n.e(6494),n.e(3202),n.e(6518)]).then(n.bind(n,76518)),{loadableGenerated:{webpack:()=>[76518]},loading:()=>(0,r.jsx)(ei,{})}),el=(0,b.default)(()=>Promise.all([n.e(4065),n.e(3100),n.e(3145),n.e(3500),n.e(8726),n.e(7380),n.e(9717),n.e(7459)]).then(n.bind(n,59112)),{loadableGenerated:{webpack:()=>[59112]},loading:()=>(0,r.jsx)(ei,{})}),eo=[{key:"recordToolsOrder",label:v.U8},{key:"classToolsOrder",label:v.zN}],ed="dashboard",ec=["topLevelOrder","recordToolsOrder","classToolsOrder"],eu=(e,t)=>e.recordToolsOrder.includes(t)?"recordToolsOrder":e.classToolsOrder.includes(t)?"classToolsOrder":"topLevelOrder";function em(e,t,n,r){let a=eu(e,t),i=e[a].indexOf(t);if(-1===i)return e;let s={...e,topLevelOrder:e.topLevelOrder.filter(e=>e!==t),recordToolsOrder:e.recordToolsOrder.filter(e=>
1e!==t),classToolsOrder:e.classToolsOrder.filter(e=>e!==t)},l=[...s[n]];return l.splice(Math.min(Math.max(a===n&&i<r?r-1:r,0),l.length),0,t),s[n]=l,s}let ep={dropLine:"bg-[#007AFF] dark:bg-[#2883DE]",dropRing:"border-[#007AFF] dark:border-[#2883DE]",focusRing:"focus-visible:border-[#007AFF] dark:focus-visible:border-[#2883DE]"};function ex(e){let{isOpen:t,onClose:n,defaultTab:b="general",studentId:g,autoAddClass:N}=e,{config:z,setConfig:C,resetToDefault:E}=(0,v.Nv)(),[S,L]=(0,a.useState)(b),{showToast:A}=(0,i.p)();(0,a.useEffect)(()=>{t&&L(b)},[t,b]);let D=(0,k.XC)(),[T,R]=(0,a.useState)(!1),[B,O]=(0,a.useState)(z),[F,P]=(0,a.useState)(!1),[Z,G]=(0,a.useState)(null),[X,K]=(0,a.useState)(null),q=(0,a.useRef)(null),H=(0,a.useRef)({}),W=(0,a.useRef)(null),[U,V]=(0,a.useState)(!1),[_,J]=(0,a.useState)(!1);if((0,a.useEffect)(()=>{let e=JSON.stringify(B.topLevelOrder)!==JSON.stringify(z.topLevelOrder),t=JSON.stringify(B.recordToolsOrder)!==JSON.stringify(z.recordToolsOrder),n=JSON.stringify(B.classToolsOrder)!==JSON.str
1ingify(z.classToolsOrder);P(e||t||n)},[B,z]),(0,a.useEffect)(()=>{R(!0)},[]),(0,a.useEffect)(()=>{O(z),P(!1)},[z]),(0,a.useEffect)(()=>{var e;let t=W.current;t&&(W.current=null,null===(e=H.current[t])||void 0===e||e.focus())},[B]),(0,w.j)(n,t),!t||!T)return null;let Y=async()=>{J(!0);try{let e=await (0,y.X)("saveSidebarConfig",{config:B},D);if(!e.success){A(e.error||"저장에 실패했습니다. 다시 시도해주세요.","error");return}C(B),P(!1),V(!0),setTimeout(()=>V(!1),2e3)}finally{J(!1)}},Q=e=>eu(B,e),ee=(e,t)=>{q.current=t,e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",t);let n=e.currentTarget.getBoundingClientRect();e.dataTransfer.setDragImage(e.currentTarget,Math.max(0,e.clientX-n.left),Math.max(0,e.clientY-n.top)),G(t)},ei=()=>{q.current=null,G(null),K(null)},ex=(e,t,n)=>{if(!q.current)return;e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="move";let r=e.currentTarget.getBoundingClientRect();K({group:t,index:e.clientY>r.top+r.height/2?n+1:n})},eh=(e,t)=>{q.current&&(e.preventDefault(),e.dataTransfer.dropEffect="move",K({group:t,index:B[t].length}))},ef=(e,t)=>{e.currentTarget.contains(e.relatedTarget)||K(e=>(null==e?void 0:e.group)===t?null:e)},eb=e=>{e.preventDefault();let t=q.current;t&&X&&O(e=>em(e,t,X.group,X.index)),ei()},eg=(e,t)=>{if(!Z||(null==X?void 0:X.group)!==e||X.index!==t)return!1;if(Q(Z)!==e)return!0;let n=B[e].indexOf(Z);return t!==n&&t!==n+1},ev=(e,t)=>{t!==ed&&["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(e.key)&&(e.preventDefault(),O(n=>(function(e,t,n){let r=eu(e,t),a=e[r].indexOf(t),i=ec.indexOf(r);if("ArrowUp"===n)return a>0?em(e,t,r,a-1):e;if("ArrowDown"===n)return a<e[r].length-1?em(e,t,r,a+2):e;let s=ec[i+("ArrowLeft"===n?-1:1)];return s?em(e,t,s,e[s].length):e})(n,t,e.key)),W.current=t)},ey=(e,t,n)=>{let a=(0,v.m3)(e),i=(0,v.Kg)(e),s=e===ed,d=Z===e;return(0,r.jsxs)("div",{ref:t=>{H.current[e]=t},draggable:!s,onDragStart:t=>ee(t,e),onDragEnd:ei,onKeyDown:t=>ev(t,e),tabIndex:s?void 0:0,role:s?void 0:"button","aria-label":s?void 0:"".concat(i,". 끌어서 옮기거나 방향키로 옮길 수 있습니다."),className:(0,f.cn)("flex items-center gap-2.5 pl-2 pr-3 py-2.5 rounded border transition-colors select-none outline-none",s?"bg-[#27272a]/50 border-[#3f3f46]/50 opacity-60":(0,f.cn)("bg-white dark:bg-[#27272a] border-zinc-200 dark:border-[#3f3f46]","cursor-grab active:cursor-grabbing hover:border-zinc-300 dark:hover:border-white/20",ep.focusRing),d&&"opacity-40 border-dashed"),children:[s?(0,r.jsx)(l.Z,{size:14,className:"shrink-0 text-[#52525b]"}):(0,r.jsx)(o.Z,{size:16,className:"shrink-0 text-[#52525b]"}),(0,r.jsx)(a,{size:16,className:"shrink-0 text-zinc-500 dark:text-[#a1a1aa]"}),(0,r.jsx)("span",{className:"text-[14px] text-zinc-600 dark:text-[#d4d4d8]",children:i}),s&&(0,r.jsx)("span",{className:"ml-auto text-[11px] text-[#71717a]",children:"고정"})]})},ek=e=>(0,r.jsx)("div",{className:(0,f.cn)("absolute left-0 right-0 h-0.5 rounded-full pointer-events-none z-10",ep.dropLine,"before"===e?"-top-[5px]":"bottom-[3px]")}),ew=e=>{let t=B[e];return(0,r.jsx)("div",{className:"flex flex-col -mb-2",children:t.map((n,a)=>(0,r.jsxs)("div",{className:"relative pb-2",onDragOver:t=>ex(t,e,a),onDrop:eb,children:[eg(e,a)&&ek("before"),ey(n,e,a),a===t.length-1&&eg(e,t.length)&&ek("after")]},n))})},ej=(e,t)=>(0,f.cn)("bg-zinc-50 dark:bg-[#18181b] rounded-xl border p-2 transition-colors",t,Z?(null==X?void 0:X.group)===e?ep.dropRing:"border-zinc-300 dark:border-[#3f3f46] border-dashed":"border-zinc-200 dark:border-[#27272a]");return(0,s.createPortal)((0,r.jsxs)("div",{className:"modal-viewport z-[100] flex items-center justify-center",children:[(0,r.jsx)("div",{className:"absolute inset-0 bg-black/50 backdrop-blur-sm",onClick:n}),(0,r.jsxs)("div",{className:(0,f.cn)("relative bg-[#18181b] border border-zinc-200 dark:border-[#27272a] rounded-2xl shadow-lg flex overflow-hidden animate-in zoom-in-95 duration-200","dashboard-layout"===S?"w-[95vw] max-w-[1400px] h-[90vh]":"w-[1000px] h-[600px]"),children:[(0,r.jsx)("button",{"aria-label":"닫기",onClick:n,className:"absolute top-4 right-4 text-[#a1a1aa] hover:text-white transition-colors z-50",children:(0,r.jsx)(d.Z,{size:20})}),(0,r.jsx)("div",{className:"w-[240px] border-r border-zinc-200 dark:border-[#27272a] p-3 flex flex-col gap-1 bg-[#18181b]",children:[{id:"general",label:"일반",icon:en.Z},{id:"student-list",label:"학생 명단",icon:c.Z},{id:"grade-file",label:"성적 파일",icon:u.Z},{id:"divider-1",type:"divider"},{id:"timetable",label:"시간표",icon:m.Z},{id:"personal",label:"메뉴 순서",icon:p.Z},{id:"student-roles",label:"학생 역할",icon:er.Z},{id:"dashboard-layout",label:"대시보드 편집",icon:ea.Z},{id:"divider-2",type:"divider"},{id:"mcp",label:"MCP",icon:$}].map(e=>{if("divider"===e.type)return(0,r.jsx)("div",{className:"h-px bg-zinc-200 dark:bg-[#27272a] my-2 mx-2"},e.id);let t=e.icon;return(0,r.jsxs)("button",{onClick:()=>L(e.id),className:(0,f.cn)("flex items-center gap-3 w-full px-3 py-2.5 rounded-lg text-[14px] font-normal transition-colors text-left",S===e.id||"grade-file"===e.id&&("mock-grade"===S||"record-grade"===S)?"bg-[#EAEAEA] dark:bg-[#27272a] text-zinc-900 dark:text-white":"text-[#a1a1aa] hover:bg-[#
1EAEAEA]/50 dark:hover:bg-[#27272a]/50 hover:text-zinc-900 dark:hover:text-white"),children:[t&&(0,r.jsx)(t,{className:"w-[18px] h-[18px]"}),e.label]},e.id)})}),(0,r.jsxs)("div",{className:(0,f.cn)("flex-1 flex flex-col","student-list"===S||"grade-file"===S||"mock-grade"===S||"record-grade"===S?"overflow-hidden":"overflow-y-auto custom-scrollbar p-8"),children:[("student-list"===S||"grade-file"===S||"mock-grade"===S||"record-grade"===S)&&(0,r.jsx)(es,{minimal:!1,activeTabOverride:"mock-grade"===S||"record-grade"===S||"grade-file"===S?"grade-file":S,initialGradeFileType:"mock-grade"===S?"mock":"record-grade"===S?"record":"school",hidePrimarySidebar:!0,studentId:g,autoAddClass:"student-list"===S&&N}),"general"===S&&(0,r.jsx)(M,{}),"personal"===S&&(0,r.jsxs)("div",{className:"space-y-6 animate-in fade-in slide-in-from-right-4 duration-300",children:[(0,r.jsxs)("div",{className:"border-b border-zinc-200 dark:border-[#27272a] pb-2 mb-6",children:[(0,r.jsx)("h3",{className:"text-[17px] font-medium text-zinc-900 dark:text-white",children:"메뉴 순서"}),(0,r.jsxs)("div",{className:"flex items-center justify-between mt-2",children:[(0,r.jsx)("p",{className:"text-[13px] text-zinc-500 dark:text-[#71717a]",children:"메뉴를 끌어서 순서를 바꾸고, 다른 상자로 끌어다 놓아 그룹을 옮기세요."}),(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsxs)("button",{onClick:()=>{O(v.g5)},className:"flex items-center gap-2 text-[13px] text-zinc-500 dark:text-[#a1a1aa] hover:text-zinc-900 dark:hover:text-white px-3 py-1.5 hover:bg-zinc-100 dark:hover:bg-[#27272a] rounded-lg transition-colors",children:[(0,r.jsx)(x.Z,{size:14}),"초기화"]}),(0,r.jsx)("button",{onClick:Y,disabled:!F||_,className:(0,f.cn)("flex items-center gap-2 text-[13px] px-3 py-1.5 rounded-lg transition-colors font-medium",F&&!_?"bg-[#007AFF] dark:bg-[#2883DE] text-white hover:opacity-90":U?"text-[#2883DE]":"text-[#52525b] cursor-not-allowed"),children:_?"저장 중...":U?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(h.Z,{size:14}),"저장됨"]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(h.Z,{size:14}),"저장"]})})]})]})]}),(0,r.jsxs)("div",{className:"grid grid-cols-2 gap-6 items-start",children:[(0,r.jsxs)("div",{className:"space-y-3",children:[(0,r.jsx)("h4",{className:"text-[14px] font-medium text-zinc-900 dark:text-white px-1",children:"메인 메뉴"}),(0,r.jsx)("div",{className:ej("topLevelOrder","min-h-[300px]"),onDragOver:e=>eh(e,"topLevelOrder"),onDragLeave:e=>ef(e,"topLevelOrder"),onDrop:eb,children:ew("topLevelOrder")})]}),(0,r.jsx)("div",{className:"space-y-4",children:eo.map(e=>{let t=B[e.key];return(0,r.jsxs)("div",{className:"space-y-3",children:[(0,r.jsx)("h4",{className:"text-[14px] font-medium text-zinc-900 dark:text-white px-1",children:e.label}),(0,r.jsx)("div",{className:ej(e.key,"min-h-[140px]"),onDragOver:t=>eh(t,e.key),onDragLeave:t=>ef(t,e.key),onDrop:eb,children:0===t.length?(0,r.jsxs)("div",{className:"h-[124px] flex items-center justify-center px-4 text-center text-[13px] text-zinc-400 dark:text-[#71717a]",children:["메뉴를 여기로",(0,r.jsx)("br",{}),"끌어다 놓으세요"]}):ew(e.key)})]},e.key)})})]})]}),"timetable"===S&&(0,r.jsx)(j.Q,{}),"student-roles"===S&&(0,r.jsx)(I.k,{}),"dashboard-layout"===S&&(0,r.jsx)(el,{}),"mcp"===S&&(0,r.jsx)(et,{})]})]})]}),document.body)}let eh=(0,a.createContext)(void 0);function ef(e){let{children:t}=e,[n,i]=(0,a.useState)(!1),[s,l]=(0,a.useState)("general"),[o,d]=(0,a.useState)(void 0),[c,u]=(0,a.useState)({}),m=(0,a.useRef)(void 0),p=()=>{i(!1),u({}),m.current&&(m.current(),m.current=void 0)};return(0,r.jsxs)(eh.Provider,{value:{openSettings:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"general",t=arguments.length>1?arguments[1]:void 0,n=arguments.length>2?arguments[2]:void 0;l(e),d(t),u(n||{}),m.current=null==n?void 0:n.onClose,i(!0)},closeSettings:p,isOpen:n},children:[t,(0,r.jsx)(ex,{isOpen:n,onClose:p,defaultTab:s,studentId:o,autoAddClass:c.autoAddClass})]})}function eb(){let e=(0,a.useContext)(eh);if(void 0===e)throw Error("useSettingsModal must be used within a SettingsModalProvider");return e}},34137:function(e,t,n){n.d(t,{j:function(){return s}});var r=n(2265);let a=[],i=!1;function s(e){let t=!(arguments.length>1)||void 0===arguments[1]||arguments[1],n=(0,r.useRef)(e);(0,r.useEffect)(()=>{n.current=e},[e]),(0,r.useEffect)(()=>
1{if(!t)return;i||(i=!0,window.addEventListener("keydown",e=>{if("Escape"!==e.key)return;let t=a[a.length-1];t&&t.call()}));let e={call:()=>n.current()};return a.push(e),()=>{let t=a.lastIndexOf(e);-1!==t&&a.splice(t,1)}},[t])}},2665:function(e,t,n){n.d(t,{O:function(){return r}});function r(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:new Date,n=e?e instanceof Date?e:new Date(e):null;return!!n&&n.getTime()>t.getTime()}},69813:function(e,t,n){n.d(t,{E$:function(){return l},Mo:function(){return i},Ud:function(){return a},xQ:function(){return s},yl:function(){return o}});let r=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"];function a(e){let{key:t,value:n,selectionStart:a,selectionEnd:i,shiftKey:s}=e;if(s||!r.includes(t)||a!==i)return null;switch(t){case"ArrowUp":return n.slice(0,a).includes("\n")?null:{rowDelta:-1,colDelta:0};case"ArrowDown":return n.slice(a).includes("\n")?null:{rowDelta:1,colDelta:0};case"ArrowLeft":return 0===a?{rowDelta:0,colDelta:-1}:null;case"ArrowRight":return a===n.length?{rowDelta:0,colDelta:1}:null;default:return null}}function i(e){switch(e){case"ArrowUp":return{rowDelta:-1,colDelta:0};case"ArrowDown":return{rowDelta:1,colDelta:0};case"ArrowLeft":return{rowDelta:0,colDelta:-1};case"ArrowRight":return{rowDelta:0,colDelta:1};default:return null}}function s(e){let{key:t,altKey:n,shiftKey:r,multiline:a}=e;return"Enter"===t&&a&&!!(n||r)}function l(e){return 1===e.key.length&&!e.ctrlKey&&!e.metaKey&&!e.altKey}function o(e){var t,n;if(document.execCommand("insertText",!1,"\n"))return;let r=null!==(t=e.selectionStart)&&void 0!==t?t:e.value.length,a=null!==(n=e.selectionEnd)&&void 0!==n?n:r;e.value="".concat(e.value.slice(0,r),"\n").concat(e.value.slice(a)),e.setSelectionRange(r+1,r+1),e.dispatchEvent(new Event("input",{bubbles:!0}))}},15563:function(e,t,n){function r(e){return!e.nativeShell&&!e.isGuest&&!!e.hasSession&&!e.rosterWizardCompleted&&(0===e.studentCount||!0===e.wizardInProgress)}function a(e){return e.nativeShell&&!e.isGuest&&0===e.studentCount&&!e.rosterWizardCompleted}function i(e){return!e.nativeShell&&e.previewQuery}function s(e){return!e}n.d(t,{Nx:function(){return d},QY:function(){return u},X9:function(){return c},Ys:function(){return o},a8:function(){return i},fL:function(){return s},sg:function(){return r},vh:function(){return a}});let l="rosterWizardProgress";function o(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Date.now();try{let t=window.localStorage.getItem(l);if(!t)return null;let n=JSON.parse(t),r=n.step;if("number"!=typeof r||r<1||r>5||"number"!=typeof n.updatedAt||e-n.updatedAt>864e5)return null;return{step:r,savedCount:"number"==typeof n.savedCount?n.savedCount:0,isHomeroom:!1!==n.isHomeroom,hasSubjectAdded:!0===n.hasSubjectAdded,gradeInput:"string"==typeof n.gradeInput?n.gradeInput:"",classInput:"string"==typeof n.classInput?n.classInput:"",updatedAt:n.updatedAt}}catch(e){return null}}function d(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Date.now();try{window.localStorage.setItem(l,JSON.stringify({...e,updatedAt:t}))}catch(e){}}function c(){try{window.localStorage.removeItem(l)}catch(e){}}function u(e){return 4===e.step?3:e.step}},90241:function(e,t,n){n.d(t,{f_:function(){return a},pM:function(){return d},NI:function(){return l}});var r=n(9244);class a extends Error{constructor(){super("cancelled"),this.cancelled=!0,this.name="YearbookSaveCancelled"}}function i(e){return e.isNative||/iPhone|iPad|iPod/i.test(e.userAgent)||"MacIntel"===e.platform&&e.maxTouchPoints>1?e.canShareFiles?"share":"open":"download"}function s(){var e,t,n;let a="undefined"==typeof navigator?void 0:navigator;return{userAgent:null!==(e=null==a?void 0:a.userAgent)&&void 0!==e?e:"",platform:null!==(t=null==a?void 0:a.platform)&&void 0!==t?t:"",maxTouchPoints:null!==(n=null==a?void 0:a.maxTouchPoints)&&void 0!==n?n:0,isNative:(0,r.c)(),canShareFiles:function(){if("undefined"==typeof File||"undefined"==typeof navigator||"function"!=typeof navigator.canShare||"function"!=typeof navigator.share)return!1;try{let e=new File(["x"],"t.html",{type:"text/html"});return navigator.canShare({files:[e]})}catch(e){return!1}}()}}function l(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:s();return"download"!==i(e)}function o(e){let t=URL.createObjectURL(e);window.open(t,"_blank")||(window.location.href=t),window.setTimeout(()=>URL.revokeObjectURL(t),6e4)}async function d(e,t){let n=new Blob([t],{type:"text/html;charset=utf-8"}),r=i(s());if("share"===r){try{let t=new File([n],e,{type:"text/html"});await navigator.share({files:[t],title:e});return}catch(e){if(e&&"object"==typeof e&&"name"in e&&"AbortError"===e.name)throw new a}o(n);return}if("download"===r){!function(e,t){let n=URL.createObjectURL(t),r=document.createElement("a");r.href=n,r.download=e,document.body.appendChild(r),r.click(),document.body.removeChild(r),window.setTimeout(()=>URL.revokeObjectURL(n),6e4)}(e,n);return}o(n)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.