PageSourceSearch

https://naemo.ai/_next/static/chunks/6245-59c850eca6fc5c17.js

js naemo.ai collected 2026-10-02 01:12:09 UTC 152,093 bytes, 1 lines download raw bytes

1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6245],{76245:function(e,t,n){"use strict";n.d(t,{T:function(){return eg}});var r=n(57437),a=n(90982),i=n(2265),s=n(98319),l=n(54887),o=n(66337),c=n(41905),d=n(32489),u=n(48736),p=n(55900),m=n(88501),h=n(28842),x=n(69076),f=n(30401),b=n(93448),g=n(30166),v=n(86315),y=n(83008),w=n(67920),k=n(46977),j=n(95539),N=n(93627),E=n(10136),z=n(27526),C=n(98381),S=n(85929),L=n(97226),A=n(99897),T=n(2665),D=n(80664),R=n(70287),B=n(39994),O=n(76283),I=n(99098),F=n(17790),M=n(39561),P=n(45701);function _(){var e,t,n,a;let l=(0,M.g)(),{data:o,update:c}=(0,z.kP)(),{theme:d,setTheme:u}=(0,C.F)(),p=(0,w.XC)(),m=(0,E.N)(),{showToast:h}=(0,s.pm)(),{isResetting:x,isResetModalOpen:f,resetStep:g,saveNeedsShareHint:v,openResetModal:k,closeResetModal:j,handleSaveAndReset:N,handleSaveFile:_,handleConfirmSavedAndReset:G,handleResetOnly:Z,filename:q}=(0,O.v)(),[K,H]=(0,i.useState)(!1),[U,W]=(0,i.useState)(!1),X=async()=>{if(await m({title:"계정을 삭제할까요?",description:"탈퇴하면 이 계정으로 만든 모든 기록이 영구적으로 사라집니다. 되돌릴 수 없습니다.",consequences:["학생 명단","성적\xb7출결 기록","상담 일지","세특\xb7행동특성","설문과 응답","교무수첩 보관함"],confirmLabel:"계정 삭제"})){H(!0);try{let e=await (0,y.X)("deleteAccount",{confirmText:"탈퇴"},p);e.success?(h("계정을 삭제했습니다. 그동안 이용해 주셔서 감사합니다.","success"),await (0,z.w7)({callbackUrl:"/"})):(h(e.error||"계정 삭제 실패","error"),H(!1))}catch(e){console.error(e),h("오류가 발생했습니다.","error"),H(!1)}}},J=null==o?void 0:null===(e=o.user)||void 0===e?void 0:e.paidUntil,V=null==o?void 0:null===(t=o.user)||void 0===t?void 0:t.recurringStatus,Y=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==o?void 0:null===(n=o.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:()=>u("light"),className:(0,b.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)(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:()=>u("dark"),className:(0,b.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)(L.Z,{size:14}),"다크"]}),(0,r.jsx)("div",{className:"w-[1px] h-3 bg-zinc-300 dark:bg-zinc-700"}),(0,r.jsxs)("button",{onClick:()=>u("system"),className:(0,b.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)(A.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"===V?(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:"정기구독 중"}),J&&(0,r.jsxs)("span",{className:"text-[12px] text-zinc-500",children:["다음 결제 예정: ",Y(J)]})]}):(0,T.Om)(J)?(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:"유료 이용 중"}),J&&(0,r.jsxs)("span",{className:"text-[12px] text-zinc-500",children:[Y(J)," 까지"]})]}):(0,r.jsx)("span",{className:"text-[13px] text-zinc-500 font-medium",children:"무료 플랜"})]}),(0,r.jsx)(P.D,{}),(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:l?"학교 초대":"이용권\xb7초대 코드"}),(0,r.jsx)("button",{onClick:()=>W(!0),className:"text-[13px] text-zinc-500 font-medium hover:text-zinc-300 transition-colors",children:l?"학교 참여":"코드 입력"})]}),(0,r.jsx)(F.GoogleCalendarButton,{settingsRow:"desktop"}),(null==o?void 0:null===(a=o.user)||void 0===a?void 0:a.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 min-w-0",children:[(0,r.jsx)("span",{className:"text-[13px] text-white font-medium px-2 truncate max-w-[240px]",title:o.user.schoolName||void 0,children:o.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},p);e.success?(h(e.message,"success"),await c()):h(e.error||"오류가 발생했습니다.","error")}catch(e){console.error(e),h("오류가 발생했습니다.","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 shrink-0",children:"학교 연동 해제"})]})]}),(0,r.jsx)(I.l,{}),(0,r.jsx)(R.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:k,disabled:x||K,className:"text-[13px] text-zinc-500 hover:text-zinc-300 disabled:opacity-50 font-medium transition-colors",children:x?"처리 중…":"초기화"})]}),(0,r.jsx)(B.C,{isOpen:f,step:g,onSaveAndReset:N,onResetOnly:Z,onSaveFile:_,onConfirmSavedAndReset:G,onCancel:j,isProcessing:x,saveNeedsShareHint:v,filename:q}),(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:X,disabled:K||x,className:"text-[13px] text-zinc-500 disabled:opacity-50 font-medium underline-offset-4",children:K?"처리중...":"탈퇴하기"})]}),(0,r.jsx)(D.g,{isOpen:U,onClose:()=>W(!1)})]})}var G=n(285),Z=n(87769),q=n(42208),K=n(91723),H=n(53470),U=n(40875),W=n(51817),X=n(92934),J=n(99388),V=n(42488),Y=n(18930),Q=n(99397),$=n(78867),ee=n(71947),et=n(33145);let en=[{id:"claude",name:"Claude",icon:"/icons/brands/claude.svg"},{id:"chatgpt",name:"ChatGPT",icon:"/icons/brands/openai.svg"}];function er(e){let{endpointUrl:t,defaultApp:n}=e,[a,s]=(0,i.useState)(n||null),[l,o]=(0,i.useState)("idle"),c=(0,i.useRef)(null),d=(0,i.useId)();(0,i.useEffect)(()=>()=>{c.current&&clearTimeout(c.current)},[]);let u=async()=>{c.current&&clearTimeout(c.current);try{await navigator.clipboard.writeText(t),o("copied"),c.current=setTimeout(()=>o("idle"),2500)}catch(e){o("error")}};return(0,r.jsxs)("div",{children:[(0,r.jsx)("p",{className:"text-[13px] leading-relaxed text-zinc-600 dark:text-zinc-400",children:"앱을 선택하면 연결 방법을 안내해 드려요."}),(0,r.jsx)("div",{className:"mt-3 flex gap-3",children:en.map(e=>{let t=a===e.id;return(0,r.jsxs)("button",{type:"button","aria-expanded":t,"aria-controls":"".concat(d,"-").concat(e.id),"aria-label":"".concat(e.name," 연결 안내"),onClick:()=>{s(t=>t===e.id?null:e.id),o("idle")},className:(0,b.cn)("flex min-h-20 min-w-0 w-28 flex-col items-center justify-center gap-2 rounded-xl border px-3 py-3","transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#007AFF] focus-visible:ring-offset-2 dark:focus-visible:ring-offset-zinc-900",t?"border-zinc-900 bg-zinc-100 text-zinc-900 dark:border-zinc-300 dark:bg-zinc-800 dark:text-white":"border-zinc-200 bg-transparent text-zinc-700 hover:border-zinc-400 hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-200 dark:hover:border-zinc-500 dark:hover:bg-zinc-800"),children:[(0,r.jsx)(et.default,{src:e.icon,alt:"",width:26,height:26,className:"shrink-0 dark:invert"}),(0,r.jsx)("span",{className:"text-[13px] font-medium",children:e.name})]},e.id)})}),en.map(e=>(0,r.jsx)("div",{id:"".concat(d,"-").concat(e.id),role:"region","aria-labelledby":"".concat(d,"-").concat(e.id,"-title"),hidden:a!==e.id,className:"mt-6",children:a===e.id&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("h5",{id:"".concat(d,"-").concat(e.id,"-title"),className:"sr-only",children:[e.name," 연결하기"]}),(0,r.jsxs)("ol",{className:"space-y-4 text-[13px] leading-relaxed text-zinc-600 dark:text-zinc-400",children:[(0,r.jsxs)("li",{className:"flex gap-3",children:[(0,r.jsx)("span",{"aria-hidden":"true",className:"w-5 shrink-0 pt-0.5 text-center font-medium text-zinc-500 dark:text-zinc-400",children:"1"}),(0,r.jsxs)("div",{children:[(0,r.jsx)("p",{className:"font-medium text-zinc-900 dark:text-zinc-100",children:"claude"===e.id?"커넥터 추가 열기":"플러그인 화면 열기"}),(0,r.jsx)("p",{className:"mt-0.5",children:"claude"===e.id?"왼쪽 사용자 지정 → 커넥터 → + → 커스텀 커넥터 추가":"ChatGPT 웹 왼쪽 메뉴에서 플러그인을 여세요."})]})]}),"chatgpt"===e.id&&(0,r.jsxs)("li",{className:"flex gap-3",children:[(0,r.jsx)("span",{"aria-hidden":"true",className:"w-5 shrink-0 pt-0.5 text-center font-medium text-zinc-500 dark:text-zinc-400",children:"2"}),(0,r.jsxs)("div",{children:[(0,r.jsx)("p",{className:"font-medium text-zinc-900 dark:text-zinc-100",children:"MCP 서버 추가하기"}),(0,r.jsx)("p",{className:"mt-0.5",children:"화면 오른쪽 위 추가(+) 메뉴에서 ‘MCP 서버 추가’를 선택하세요."})]})]}),(0,r.jsxs)("li",{className:"flex gap-3",children:[(0,r.jsx)("span",{"aria-hidden":"true",className:"w-5 shrink-0 pt-0.5 text-center font-medium text-zinc-500 dark:text-zinc-400",children:"claude"===e.id?2:3}),(0,r.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,r.jsx)("p",{className:"font-medium text-zinc-900 dark:text-zinc-100",children:"내모 주소 붙여넣기"}),(0,r.jsx)("p",{className:"mt-0.5",children:"claude"===e.id?"이름에 ‘내모’, 서버 URL에 아래 주소를 입력하고 추가하세요.":"이름은 ‘내모’, 연결은 ‘서버 URL’을 선택해 아래 주소를 입력하세요. 인증은 OAuth로 두세요."}),(0,r.jsxs)("div",{className:"mt-2 flex items-center gap-2 rounded-lg bg-zinc-100 p-1 pl-3 dark:bg-zinc-800",children:[(0,r.jsx)("code",{title:t,className:"min-w-0 flex-1 select-all truncate text-[12px] text-zinc-700 selection:bg-blue-100 selection:text-zinc-900 dark:text-zinc-200 dark:selection:bg-blue-900 dark:selection:text-white",children:t}),(0,r.jsxs)("button",{type:"button",onClick:u,"aria-label":"copied"===l?"주소 복사됨":"주소 복사",className:"inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded-md px-2 text-[12px] font-medium text-zinc-700 transition-colors hover:bg-zinc-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#007AFF] dark:text-zinc-200 dark:hover:bg-zinc-700",children:["copied"===l?(0,r.jsx)(f.Z,{size:14,"aria-hidden":"true"}):(0,r.jsx)($.Z,{size:14,"aria-hidden":"true"}),"copied"===l?"복사됨":"복사"]})]}),(0,r.jsxs)("div",{role:"status","aria-live":"polite",children:["copied"===l&&(0,r.jsx)("span",{className:"sr-only",children:"내모 연결 주소를 복사했습니다."}),"error"===l&&(0,r.jsx)("p",{className:"mt-1.5 text-red-600 dark:text-red-400",children:"복사하지 못했어요. 위 주소를 선택해 직접 복사해 주세요."})]})]})]}),(0,r.jsxs)("li",{className:"flex gap-3",children:[(0,r.jsx)("span",{"aria-hidden":"true",className:"w-5 shrink-0 pt-0.5 text-center font-medium text-zinc-500 dark:text-zinc-400",children:"claude"===e.id?3:4}),(0,r.jsxs)("div",{children:[(0,r.jsx)("p",{className:"font-medium text-zinc-900 dark:text-zinc-100",children:"로그인하고 연결 허용하기"}),(0,r.jsx)("p",{className:"mt-0.5",children:"claude"===e.id?"추가한 내모에서 연결을 누르고 로그인한 뒤 연결 허용을 선택하세요. 대화의 + → 커넥터에서 내모를 켜면 됩니다.":"안내에 따라 연결을 진행하고 ‘내모(으)로 계속’을 선택하세요. 내모에 로그인한 뒤 계정과 권한을 확인하고 ‘연결 허용’을 누르세요."})]})]})]}),(0,r.jsxs)("div",{className:"mt-4 flex gap-3 text-[12px] leading-relaxed text-zinc-600 dark:text-zinc-400",children:[(0,r.jsx)("span",{"aria-hidden":"true",className:"w-5 shrink-0 pt-2.5",children:(0,r.jsx)(et.default,{src:"/icons/wired-outline-2815-ghost.svg",alt:"",width:20,height:20,className:"dark:brightness-0 dark:invert"})}),(0,r.jsx)("p",{className:"relative min-w-0 rounded-xl bg-zinc-100 px-3 py-2 before:absolute before:-left-1 before:top-4 before:h-2 before:w-2 before:rotate-45 before:bg-zinc-100 before:content-[''] dark:bg-zinc-800 dark:before:bg-zinc-800",children:"설정하다 막히면 AI에게 물어보세요. MCP 연결법 정도는 쉽게 도와줄 거에요. 어쩌면 직접 해줄 수도?"})]})]})},e.id))]})}function ea(){var e,t,n;let{previewEndpointUrl:o}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{showToast:c}=(0,s.pm)(),d=(0,E.N)(),u=(0,w.XC)(),[p,m]=(0,i.useState)([]),[h,x]=(0,i.useState)(!0),[g,v]=(0,i.useState)(!1),[k,j]=(0,i.useState)(""),[N,z]=(0,i.useState)(!0),[C,S]=(0,i.useState)(!1),[L,A]=(0,i.useState)(null),[T,D]=(0,i.useState)(!1),[R,B]=(0,i.useState)([]),[O,I]=(0,i.useState)(!1),[F,M]=(0,i.useState)(!1),[P,_]=(0,i.useState)({}),G=(0,i.useCallback)(async()=>{x(!0);try{let e=await (0,y.X)("listMcpApiKeys",{},u);e.success&&e.data?(m(e.data),e.data.length>0&&I(!0)):c(e.error||"키 목록을 불러오지 못했습니다.","error")}catch(e){console.error(e),c("키 목록 조회 중 오류가 발생했습니다.","error")}finally{x(!1)}},[u,c]),et=(0,i.useCallback)(async()=>{try{let e=await (0,y.X)("listOAuthConnections",{},u);e.success&&e.data&&B(e.data)}catch(e){console.error(e)}},[u]);(0,i.useEffect)(()=>
1{if(o){x(!1);return}G(),et()},[o,G,et]);let en=async(e,t)=>{if(await d({title:"연결 끊기",description:"'".concat(t,"'의 내모 접근을 지금 끊습니다. 다시 쓰시려면 그 앱에서 한 번 더 연결을 허용해야 합니다."),confirmLabel:"연결 끊기",cancelLabel:"취소",tone:"danger"}))try{let t=await (0,y.X)("revokeOAuthConnection",{clientId:e},u);t.success?(c("연결을 끊었습니다.","success"),et()):c(t.error||"연결을 끊지 못했습니다.","error")}catch(e){console.error(e),c("연결을 끊는 중 오류가 발생했습니다.","error")}},ea=async e=>{if(e.preventDefault(),!k.trim()){c("키의 이름 또는 용도를 입력해주세요.","error");return}v(!0);try{let e=await (0,y.X)("createMcpApiKey",{name:k.trim(),maskPii:N},u);e.success&&e.data?(A(e.data.apiKey),j(""),I(!0),c("새 연결 키가 발급되었습니다.","success"),G()):c(e.error||"키 발급에 실패했습니다.","error")}catch(e){console.error(e),c("키 발급 중 오류가 발생했습니다.","error")}finally{v(!1)}},ei=async(e,t)=>{let n=!t;try{let t=await (0,y.X)("toggleMcpApiKeyMaskPii",{id:e,maskPii:n},u);t.success?(m(t=>t.map(t=>t.id===e?{...t,maskPii:n}:t)),c(t.message||"개인정보 보호 설정이 변경되었습니다.","success")):c(t.error||"설정 변경에 실패했습니다.","error")}catch(e){console.error(e),c("설정 변경 중 오류가 발생했습니다.","error")}finally{x(!1)}},es=async(e,t)=>{if(await d({title:"연결 키 삭제",description:"'".concat(t,"' 키를 정말 삭제하시겠습니까? 이 키를 사용하는 외부 AI 도구와의 연결이 즉시 차단됩니다."),confirmLabel:"삭제",cancelLabel:"취소",tone:"danger"}))try{let t=await (0,y.X)("deleteMcpApiKey",{id:e},u);t.success?(c("키가 삭제되었습니다.","success"),G()):c(t.error||"키 삭제에 실패했습니다.","error")}
1catch(e){console.error(e),c("키 삭제 중 오류가 발생했습니다.","error")}},el=async e=>{_(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)_(e=>({...e,new:{testing:!1,success:!0,latency:r}})),c("이 키로 내모에 정상 연결됩니다. (응답 ".concat(r,"ms)"),"success");else{let e=401===n.status?"키가 거부되었습니다":"서버 응답 ".concat(n.status);_(t=>({...t,new:{testing:!1,success:!1,error:e}})),c("연결 확인 실패: ".concat(e),"error")}}catch(e){_(t=>({...t,new:{testing:!1,success:!1,error:e.message}})),c("연결 확인 실패: ".concat(e.message),"error")}},eo=async e=>{_(t=>({...t,[e]:{testing:!0}}));try{let t=await (0,y.X)("testMcpApiKey",{id:e},u);if(!t.success||!t.data){_(n=>({...n,[e]:{testing:!1,success:!1,error:t.error||"확인 실패"}})),c(t.error||"키 상태를 확인하지 못했습니다.","error");return}let{state:n,message:r}=t.data;_(t=>({...t,[e]:{testing:!1,success:"active"===n,error:"active"===n?void 0:r}})),c(r,"expired"===n?"error":"active"===n?"success":"info"),"expired"!==n&&G()}catch(t){_(n=>({...n,[e]:{testing:!1,success:!1,error:t.message}})),c("키 상태 확인 중 오류가 발생했습니다: ".concat(t.message),"error")}},ec=e=>{navigator.clipboard.writeText(e),D(!0),setTimeout(()=>D(!1),2e3),c("클립보드에 복사되었습니다.","success")},ed=window.location.origin,eu=o||"".concat(ed,"/api/mcp"),ep=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(eu,'",\n        "--header",\n        "Authorization: Bearer ').concat(e,'"\n      ]\n    }');return t?'{\n  "mcpServers": {\n'.concat(n,"\n  }\n}"):"  ".concat(n.trim())};return(0,r.jsxs)("div",{className:"animate-in fade-in slide-in-from-right-4 duration-300 pb-8",children:[(0,r.jsxs)("div",{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-600 dark:text-zinc-400 mt-1",children:"사용하는 AI 앱에서 내모의 자료를 활용하세요."})]}),(0,r.jsxs)("section",{className:"pt-6",children:[(0,r.jsx)(er,{endpointUrl:eu,defaultApp:o?"chatgpt":void 0}),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,b.cn)("inline-flex items-center gap-1 text-[11px]",e.maskPii?"text-zinc-400 dark:text-[#71717a]":"text-amber-600 dark:text-amber-500"),children:[e.maskPii?(0,r.jsx)(Z.Z,{size:11}):(0,r.jsx)(q.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)(K.Z,{size:11}),e.lastUsedAt?"최근 사용 ".concat(new Date(e.lastUsedAt).toLocaleString("ko-KR")):"사용 이력 없음"]})]})]}),(0,r.jsxs)("button",{type:"button",onClick:()=>en(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)(H.Z,{size:13}),"연결 끊기"]})]},e.clientId))})]})]}),(0,r.jsxs)("details",{className:"group mt-7 border-t border-zinc-200 pt-2 dark:border-zinc-800",children:[(0,r.jsxs)("summary",{className:"flex min-h-11 cursor-pointer list-none items-center justify-between gap-3 rounded text-[13px] text-zinc-600 hover:text-zinc-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#007AFF] dark:text-zinc-400 dark:hover:text-white [&::-webkit-details-marker]:hidden",children:["활용 예시",(0,r.jsx)(U.Z,{size:16,"aria-hidden":"true",className:"text-zinc-500 group-open:rotate-180"})]}),(0,r.jsx)("ul",{className:"mt-2 space-y-1.5",children:["오늘 3반 15번 김철수 감기로 병결 처리해줘. 진단서는 내일 제출 예정","어제 지각으로 등록된 5번 이영희 기록, 담임 인정지각으로 변경해줘","성적 골고루 섞이게 6개 모둠으로 자리 배치해줘","우리 반 애들 출결이랑 성적 상관관계 분석해서 ê
1·¸ëž˜í”„로 보여줘","상담일지랑 메모 바탕으로 학부모 상담 때 이야기할 학생별 특징 정리해줘","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",{children:[(0,r.jsxs)("button",{type:"button",onClick:()=>I(e=>!e),disabled:!!o,"aria-expanded":O,className:"group flex min-h-11 w-full items-center justify-between gap-3 rounded text-left text-[13px] text-zinc-600 hover:text-zinc-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#007AFF] dark:text-zinc-400 dark:hover:text-white",children:[(0,r.jsx)("div",{className:"min-w-0",children:(0,r.jsxs)("div",{className:"flex items-center gap-2",children:[(0,r.jsx)("span",{children:"연결 키 관리"}),p.length>0&&(0,r.jsxs)("span",{className:"text-[12px] text-zinc-400 dark:text-[#71717a]",children:[p.length,"개"]})]})}),(0,r.jsx)(U.Z,{size:16,className:(0,b.cn)("shrink-0 text-zinc-500 transition-transform group-hover:text-zinc-600 dark:group-hover:text-zinc-200",O&&"rotate-180")})]}),O&&(0,r.jsxs)("div",{className:"mt-4",children:[h?(0,r.jsx)(a.ContentLoadingState,{variant:"list",rows:3,label:"연결 키 목록을 불러오고 있어요"}):0===p.length?(0,r.jsx)("p",{className:(0,b.cn)("text-[13px] text-zinc-500 dark:text-[#a1a1aa] leading-relaxed","max-w-lg"),children:"아직 발급한 연결 키가 없습니다. 연결 키를 요구하는 도구에서 사용할 수 있습니다."}):(0,r.jsx)("ul",{className:"divide-y divide-zinc-100 dark:divide-[#27272a]",children:p.map(e=>{let t=P[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:()=>ei(e.id,e.maskPii),className:(0,b.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)(Z.Z,{size:11}):(0,r.jsx)(q.Z,{size:11}),e.maskPii?"실명 가림":"실명 그대로"]})]}),(0,r.jsxs)("div",{className:"flex items-center gap-1 shrink-0",children:[(0,r.jsx)("button",{onClick:()=>eo(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)(X.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)(J.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)(V.Z,{size:12}),(0,r.jsx)("span",{children:"상태 확인"})]})}),(0,r.jsx)("button",{onClick:()=>es(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)(Y.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")," 발급"]}
1),(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)(K.Z,{size:11}),e.lastUsedAt?"최근 사용 ".concat(new Date(e.lastUsedAt).toLocaleString("ko-KR")):"사용 이력 없음"]})]})]},e.id)})}),(0,r.jsxs)("button",{onClick:()=>{A(null),S(!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)(Q.Z,{size:15}),"새 연결 키 발급"]})]})]}),C&&"undefined"!=typeof document&&(0,l.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:L?"연결 키 발급 완료":"새 연결 키 발급"}),(0,r.jsx)("p",{className:"text-[13px] text-zinc-500 dark:text-zinc-400 mt-1",children:L?"아래 비밀 키는 지금 단 한 번만 표시됩니다. 지금 복사하여 안전한 곳에 보관하세요.":"연결 키를 구분할 수 있는 이름을 지정해주세요."})]}),L?(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:L,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:()=>ec(L),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:T?(0,r.jsx)(f.Z,{size:16,className:"text-emerald-500"}):(0,r.jsx)($.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)("span",{className:"text-[13px] font-medium text-zinc-800 dark:text-zinc-200 flex items-center gap-1.5",children:[(0,r.jsx)(ee.Z,{size:14,className:"text-zinc-500"}),"고급 설정용 코드"]}),(0,r.jsx)("p",{className:"text-[12px] text-zinc-600 dark:text-zinc-400 leading-relaxed",children:"연결 키가 포함된 설정입니다. 직접 설정할 도구의 mcpServers 항목에 추가하세요."}),(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:ep(L,!1)}),(0,r.jsxs)("button",{type:"button",onClick:()=>{navigator.clipboard.writeText(ep(L,!1)),M(!0),setTimeout(()=>M(!1),2e3),c("설정 코드가 클립보드에 복사되었습니다.","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:[F?(0,r.jsx)(f.Z,{size:12,className:"text-emerald-500"}):(0,r.jsx)($.Z,{size:12}),(0,r.jsx)("span",{children:F?"복사됨":"설정 코드 복사"})]})]})]}),(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:()=>el(L),disabled:null===(e=P.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=P.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=P.new)||void 0===n?void 0:n.success)?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(X.Z,{size:13,className:"text-emerald-500"}),(0,r.jsxs)("span",{className:"text-emerald-600 dark:text-emerald-400",children:[P.new.latency,"ms 정상"]})]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(V.Z,{size:13}),(0,r.jsx)("span",{children:"연결 확인"})]})})]}),(0,r.jsx)("button",{type:"button",onClick:()=>{S(!1),A(null)},className:"btn-primary-solid w-full py-2.5 rounded-lg font-medium text-[14px] transition-colors",children:"확인하고 닫기"})]}):(0,r.jsxs)("form",{onSubmit:ea,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:"예: 개인 컴퓨터, 업무용 도구",value:k,onChange:e=>j(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:N,onChange:e=>z(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:()=>S(!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:g||!k.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:g?"발급 중...":"발급하기"})]})]})]})}),document.body)]})}var ei=n(98728),es=n(49724),el=n(25466);function eo(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.01
1h226.85c11.052 0 20.01-8.959 20.01-20.01v-.01c0-11.051-8.958-20.01-20.01-20.01"})]})})}let ec=()=>(0,r.jsx)(a.ContentLoadingState,{variant:"table",label:"설정을 불러오고 있어요"}),ed=(0,g.default)(()=>Promise.all([n.e(3100),n.e(3549),n.e(3202),n.e(3119)]).then(n.bind(n,83119)),{loadableGenerated:{webpack:()=>[83119]},loading:()=>(0,r.jsx)(ec,{})}),eu=(0,g.default)(()=>Promise.all([n.e(4168),n.e(3100),n.e(3500),n.e(8726),n.e(2095),n.e(6078),n.e(6707)]).then(n.bind(n,59112)),{loadableGenerated:{webpack:()=>[59112]},loading:()=>(0,r.jsx)(ec,{})}),ep=[{key:"recordToolsOrder",label:v.U8},{key:"classToolsOrder",label:v.zN}],em="dashboard",eh=["topLevelOrder","recordToolsOrder","classToolsOrder"],ex=(e,t)=>e.recordToolsOrder.includes(t)?"recordToolsOrder":e.classToolsOrder.includes(t)?"classToolsOrder":"topLevelOrder";function ef(e,t,n,r){let a=ex(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=>e!==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 eb={dropLine:"bg-[#007AFF] dark:bg-[#2883DE]",dropRing:"border-[#007AFF] dark:border-[#2883DE]",focusRing:"focus-visible:border-[#007AFF] dark:focus-visible:border-[#2883DE]"};function eg(e){let{isOpen:t,onClose:n,defaultTab:a="general",studentId:g,autoAddClass:E,mcpPreviewEndpointUrl:z}=e,{config:C,setConfig:S,resetToDefault:L}=(0,v.Nv)(),[A,T]=(0,i.useState)(a),{showToast:D}=(0,s.pm)();(0,i.useEffect)(()=>{t&&T(a)},[t,a]);let R=(0,w.XC)(),[B,O]=(0,i.useState)(!1),[I,M]=(0,i.useState)(C),[P,Z]=(0,i.useState)(!1),[q,K]=(0,i.useState)(null),[H,U]=(0,i.useState)(null),W=(0,i.useRef)(null),X=(0,i.useRef)({}),J=(0,i.useRef)(null),[V,Y]=(0,i.useState)(!1),[Q,$]=(0,i.useState)(!1);(0,i.useEffect)(()=>{let e=JSON.stringify(I.topLevelOrder)!==JSON.stringify(C.topLevelOrder),t=JSON.stringify(I.recordToolsOrder)!==JSON.stringify(C.recordToolsOrder),n=JSON.stringify(I.classToolsOrder)!==JSON.stringify(C.classToolsOrder);Z(e||t||n)},[I,C]),(0,i.useEffect)(()=>{O(!0)},[]),(0,i.useEffect)(()=>{M(C),Z(!1)},[C]),(0,i.useEffect)(()=>{var e;let t=J.current;t&&(J.current=null,null===(e=X.current[t])||void 0===e||e.focus())},[I]);let[ee,et]=(0,i.useState)(!1),en=()=>{Q||(P?et(!0):n())},er=(0,k.a)(t&&B,en);if(!t||!B)return null;let ec=async()=>{$(!0);try{let e=await (0,y.X)("saveSidebarConfig",{config:I},R);if(!e.success)return D(e.error||"저장에 실패했습니다. 다시 시도해주세요.","error"),!1;return S(I),Z(!1),Y(!0),setTimeout(()=>Y(!1),2e3),!0}catch(e){return D("메뉴 순서를 저장하지 못했어요. 다시 시도해 주세요.","error"),!1}finally{$(!1)}},eg=e=>ex(I,e),ev=(e,t)=>
1{W.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)),K(t)},ey=()=>{W.current=null,K(null),U(null)},ew=(e,t,n)=>{if(!W.current)return;e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="move";let r=e.currentTarget.getBoundingClientRect();U({group:t,index:e.clientY>r.top+r.height/2?n+1:n})},ek=(e,t)=>{W.current&&(e.preventDefault(),e.dataTransfer.dropEffect="move",U({group:t,index:I[t].length}))},ej=(e,t)=>{e.currentTarget.contains(e.relatedTarget)||U(e=>(null==e?void 0:e.group)===t?null:e)},eN=e=>{e.preventDefault();let t=W.current;t&&H&&M(e=>ef(e,t,H.group,H.index)),ey()},eE=(e,t)=>{if(!q||(null==H?void 0:H.group)!==e||H.index!==t)return!1;if(eg(q)!==e)return!0;let n=I[e].indexOf(q);return t!==n&&t!==n+1},ez=(e,t)=>{t!==em&&["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(e.key)&&(e.preventDefault(),M(n=>(function(e,t,n){let r=ex(e,t),a=e[r].indexOf(t),i=eh.indexOf(r);if("ArrowUp"===n)return a>0?ef(e,t,r,a-1):e;if("ArrowDown"===n)return a<e[r].length-1?ef(e,t,r,a+2):e;let s=eh[i+("ArrowLeft"===n?-1:1)];return s?ef(e,t,s,e[s].length):e})(n,t,e.key)),J.current=t)},eC=(e,t,n)=>{let a=(0,v.m3)(e),i=(0,v.Kg)(e),s=e===em,l=q===e;return(0,r.jsxs)("div",{ref:t=>{X.current[e]=t},draggable:!s,onDragStart:t=>ev(t,e),onDragEnd:ey,onKeyDown:t=>ez(t,e),tabIndex:s?void 0:0,role:s?void 0:"button","aria-label":s?void 0:"".concat(i,". 끌어서 옮기거나 방향키로 옮길 수 있습니다."),className:(0,b.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,b.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",eb.focusRing),l&&"opacity-40 border-dashed"),children:[s?(0,r.jsx)(o.Z,{size:14,className:"shrink-0 text-[#52525b]"}):(0,r.jsx)(c.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:"고정"})]})},eS=e=>(0,r.jsx)("div",{className:(0,b.cn)("absolute left-0 right-0 h-0.5 rounded-full pointer-events-none z-10",eb.dropLine,"before"===e?"-top-[5px]":"bottom-[3px]")}),eL=e=>{let t=I[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=>ew(t,e,a),onDrop:eN,children:[eE(e,a)&&eS("before"),eC(n,e,a),a===t.length-1&&eE(e,t.length)&&eS("after")]},n))})},eA=(e,t)=>(0,b.cn)("bg-zinc-50 dark:bg-[#18181b] rounded-xl border p-2 transition-colors",t,q?(null==H?void 0:H.group)===e?eb.dropRing:"border-zinc-300 dark:border-[#3f3f46] border-dashed":"border-zinc-200 dark:border-[#27272a]");return(0,l.createPortal)((0,r.jsxs)("div",{className:"modal-viewport z-[100] flex items-center justify-center",children:[(0,r.jsx)(j.z,{open:ee,busy:Q,onCancel:()=>et(!1),onDiscard:()=>{M(C),Z(!1),et(!1),n()},onSave:async()=>{await ec()&&(et(!1),n())}}),(0,r.jsx)("div",{className:"absolute inset-0 bg-black/50 backdrop-blur-sm",onClick:en}),(0,r.jsxs)("div",{ref:er,role:"dialog","aria-modal":"true","aria-label":"설정",tabIndex:-1,className:(0,b.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"===A?"w-[95vw] max-w-[1400px] h-[90vh]":"w-[1000px] h-[600px]"),children:[(0,r.jsx)("button",{"aria-label":"닫기",onClick:en,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.jsxs)("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:ei.Z},{id:"student-list",label:"학생 명단",icon:u.Z},{id:"grade-file",label:"성적 파일",icon:p.Z},{id:"divider-1",type:"divider"},{id:"timetable",label:"시간표",icon:m.Z},{id:"personal",label:"메뉴 순서",icon:h.Z},{id:"student-roles",label:"학생 역할",icon:es.Z},{id:"dashboard-layout",label:"대시보드 편집",icon:el.Z},{id:"divider-2",type:"divider"},{id:"mcp",label:"MCP",icon:eo}].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:()=>T(e.id),disabled:!!z&&"mcp"!==e.id,className:(0,b.cn)("flex items-center gap-3 w-full px-3 py-2.5 rounded-lg text-[14px] font-normal transition-colors text-left",A===e.id||"grade-file"===e.id&&("mock-grade"===A||"record-grade"===A)?"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)}),!z&&(0,r.jsx)(F.GoogleCalendarButton,{menu:!0})]}),(0,r.jsxs)("div",{className:(0,b.cn)("flex-1 flex flex-col","student-list"===A||"grade-file"===A||"mock-grade"===A||"record-grade"===A?"overflow-hidden":"overflow-y-auto custom-scrollbar p-8"),children:[("student-list"===A||"grade-file"===A||"mock-grade"===A||"record-grade"===A)&&(0,r.jsx)(ed,{minimal:!1,activeTabOverride:"mock-grade"===A||"record-grade"===A||"grade-file"===A?"grade-file":A,initialGradeFileType:"mock-grade"===A?"mock":"record-grade"===A?"record":"school",hidePrimarySidebar:!0,studentId:g,autoAddClass:"student-list"===A&&E}),"general"===A&&(0,r.jsx)(_,{}),"personal"===A&&(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:()=>{M(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:ec,disabled:!P||Q,className:(0,b.cn)("flex items-center gap-2 text-[13px] px-3 py-1.5 rounded-lg transition-colors font-medium",P&&!Q?"bg-[#007AFF] dark:bg-[#2883DE] text-white hover:opacity-90":V?"text-[#2883DE]":"text-[#52525b] cursor-not-allowed"),children:Q?"저장 중...":V?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(f.Z,{size:14}),"저장됨"]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(f.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:eA("topLevelOrder","min-h-[300px]"),onDragOver:e=>ek(e,"topLevelOrder"),onDragLeave:e=>ej(e,"topLevelOrder"),onDrop:eN,children:eL("topLevelOrder")})]}),(0,r.jsx)("div",{className:"space-y-4",children:ep.map(e=>{let t=I[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:eA(e.key,"min-h-[140px]"),onDragOver:t=>ek(t,e.key),onDragLeave:t=>ej(t,e.key),onDrop:eN,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",{}),"끌어다 놓으세요"]}):eL(e.key)})]},e.key)})})]})]}),"timetable"===A&&(0,r.jsx)(N.Q,{}),"student-roles"===A&&(0,r.jsx)(G.k,{}),"dashboard-layout"===A&&(0,r.jsx)(eu,{}),"mcp"===A&&(0,r.jsx)(ea,{previewEndpointUrl:z})]})]})]}),document.body)}},39994:function(e,t,n){"use strict";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:c,isProcessing:d,saveNeedsShareHint:u=!1,filename:p=null}=e;return((0,a.j)(c,t&&!d),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:"먼저 교무수첩 파일로 받아 두세요. 학교 협업에 공유한 학생 명단\xb7자료는 유지됩니다."}),(0,r.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,r.jsx)("button",{type:"button",onClick:i,disabled:d,className:"w-full h-10 rounded-lg bg-[#2883DE] text-white text-[13px] font-medium disabled:opacity-50",children:d?"교무수첩 만드는 중…":"교무수첩 저장 후 초기화"}),(0,r.jsx)("button",{type:"button",onClick:s,disabled:d,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:c,disabled:d,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:[p?"".concat(p," 파일ì
1„ "):"파일을 ",u?"받은 뒤, 공유 화면에서 ‘파일에 저장’을 고르세요. 저장이 끝난 다음에 초기화합니다.":"받은 뒤에 초기화합니다."]}),(0,r.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,r.jsx)("button",{type:"button",onClick:l,disabled:d,className:"w-full h-10 rounded-lg bg-[#2883DE] text-white text-[13px] font-medium disabled:opacity-50",children:d?"저장하는 중…":"기기에 저장"}),(0,r.jsx)("button",{type:"button",onClick:c,disabled:d,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:[p?"".concat(p," 이 "):"교무수첩 파일이 ","기기에 있는지 확인하세요. 삭제한 개인 학생 기록은 되돌릴 수 없습니다. 학교 협업의 공동 자료는 유지됩니다."]}),(0,r.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,r.jsx)("button",{type:"button",onClick:o,disabled:d,className:"w-full h-10 rounded-lg bg-red-600 text-white text-[13px] font-medium disabled:opacity-50",children:d?"처리 중…":"기록 지우기"}),(0,r.jsx)("button",{type:"button",onClick:c,disabled:d,className:"w-full h-10 rounded-lg text-zinc-500 text-[13px] font-medium disabled:opacity-50",children:"취소"})]})]})]})}):null}},70287:function(e,t,n){"use strict";n.d(t,{J:function(){return d}});var r=n(57437),a=n(98319),i=n(2265),s=n(67920),l=n(54345),o=n(90241),c=n(34137);function d(e){var t;let{variant:n="desktop"}=e,c=(0,s.XC)(),[d,p]=(0,i.useState)(!1),[m,h]=(0,i.useState)(null),{showToast:x}=(0,a.pm)(),f=async(e,t)=>{try{await (0,o.pM)(e,t),x("교무수첩을 저장했습니다. 서버에는 남지 않고 선생님 기기에만 보관됩니다.","success"),h(null)}catch(e){if(e instanceof o.f_){x("저장을 취소했습니다.","error");return}throw e}},b=async()=>{p(!0);try{let e=await (0,l.F)(c);if(!e.success){x(e.error||"교무수첩을 만들지 못했습니다.","error");return}if((0,o.NI)()){h({filename:e.filename,html:e.html});return}await f(e.filename,e.html)}catch(e){console.error(e),x("교무수첩을 만드는 중 오류가 발생했습니다.","error")}finally{p(!1)}},g=async()=>{if(m){p(!0);try{await f(m.filename,m.html)}catch(e){console.error(e),x("교무수첩을 저장하지 못했습니다.","error")}finally{p(!1)}}},v="mobile"===n?(0,r.jsxs)("button",{type:"button",onClick:b,disabled:d,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:d?"만드는 중…":"저장하기"})]}):(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:d,className:"text-[13px] text-zinc-500 hover:text-zinc-300 disabled:opacity-50 font-medium transition-colors",children:d?"만드는 중…":"저장하기"})]});return(0,r.jsxs)(r.Fragment,{children:[v,(0,r.jsx)(u,{isOpen:!!m,isSaving:d,filename:null!==(t=null==m?void 0:m.filename)&&void 0!==t?t:null,onSave:g,onCancel:()=>h(null)})]})}function u(e){let{isOpen:t,isSaving:n,filename:a,onSave:i,onCancel:s}=e;return((0,c.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," 파일ì
1„ 기기에 저장하세요. "):"","공유 화면에서는 ‘파일에 저장’을 고르면 됩니다. 서버에는 남지 않습니다."]}),(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){"use strict";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 hasExamLabels = student.mockExams.some(function (m) { return !!m.examLabel; });\n      var mockTable = maybeTable(\n        ['연도', '월'].concat(hasExamLabels ? ['시험'] : []).concat(['과목', '표준점수', '등급', '백분위']),\n        student.mockExams.map(function (m) {\n          return [m.year, m.month].concat(hasExamLabels ? [m.examLabel || '기존 월별 모의고사'] : []).c
1oncat([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 수행');\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);
1\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);\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){"use strict";n.d(t,{v:function(){return p}});var r=n(2265),a=n(98319),i=n(10136),s=n(67920),l=n(4908),o=n(83008),c=n(54345),d=n(90241),u=n(15563);function p(){var e;let t=(0,s.XC)(),{showToast:n}=(0,a.pm)(),p=(0,i.N)(),{reloadStudents:m}=(0,l.AF)(),[h,x]=(0,r.useState)(!1),[f,b]=(0,r.useState)(!1),[g,v]=(0,r.useState)("ask"),[y,w]=(0,r.useState)(null),k=async()=>{x(!0);try{let e=await (0,o.X)("resetAllStudentData",{confirmText:"초기화"},t);e.success?(n("개인 학생 데이터를 초기화했습니다.","success"),localStorage.removeItem("rosterWizardCompleted"),(0,u.X9)(),(0,u.D1)(),(0,u.Yq)(),await m()):n(e.error||"초기화 실패","error")}catch(e){console.error(e),n("오류가 발생했습니다.","error")}finally{x(!1),b(!1),v("ask"),w(null)}},j=async()=>{x(!0);try{let e=await (0,c.F)(t);if(!e.success){n(e.error||"교무수첩을 만들지 못했습니다. 초기화를 진행하지 않았습니다.","error"),x(!1);return}if((0,d.NI)()){w({filename:e.filename,html:e.html}),v("save-file"),x(!1);return}await (0,d.pM)(e.filename,e.html),v("confirm-saved"),x(!1)}catch(e){if(console.error(e),e instanceof d.f_){n("저장을 취소했습니다. 초기화를 진행하지 않았습니다.","error"),x(!1);return}n("교무수첩을 만드는 중 오류가 발생했습니다. 초기화를 진행하지 않았습니다.","error"),x(!1)}},N=async()=>
1{if(y){x(!0);try{await (0,d.pM)(y.filename,y.html),v("confirm-saved")}catch(e){if(e instanceof d.f_){n("저장을 취소했습니다. 초기화를 진행하지 않았습니다.","error");return}console.error(e),n("교무수첩을 저장하지 못했습니다. 초기화를 진행하지 않았습니다.","error")}finally{x(!1)}}};return{isResetting:h,isResetModalOpen:f,resetStep:g,saveNeedsShareHint:(0,d.NI)(),openResetModal:()=>{v("ask"),w(null),b(!0)},closeResetModal:()=>{h||(b(!1),v("ask"),w(null))},performReset:k,handleSaveAndReset:j,handleSaveFile:N,handleConfirmSavedAndReset:k,handleResetOnly:async()=>{await p({title:"저장하지 않고 개인 학생 기록을 지울까요?",description:"교무수첩 파일을 만들지 않습니다. 삭제한 개인 기록은 되돌릴 수 없습니다. 학교 협업에 공유한 학생 명단\xb7자료는 유지됩니다.",consequences:["학생 명단","성적\xb7출결 기록","상담 일지","세특\xb7행동특성","설문과 응답"],confirmLabel:"초기화"})&&await k()},filename:null!==(e=null==y?void 0:y.filename)&&void 0!==e?e:null}}},45701:function(e,t,n){"use strict";n.d(t,{D:function(){return h}});var r=n(57437),a=n(2265),i=n(27526),s=n(10407),l=n(32489),o=n(48313),c=n(9244),d=n(2665),u=n(56205),p=n(14714),m=n.n(p);function h(){var e;let[t,n]=(0,a.useState)(!1),[l,o]=(0,a.useState)(!1),{data:d,update:u}=(0,i.kP)();return((0,a.useEffect)(()=>n((0,c.Ec)()),[]),t&&(null==d?void 0:null===(e=d.user)||void 0===e?void 0:e.id)&&!d.user.impersonatedBy)?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("button",{type:"button",className:m().row,onClick:()=>o(!0),children:[(0,r.jsx)("span",{children:"플랜 및 구매"}),(0,r.jsx)(s.Z,{size:15,"aria-hidden":!0})]}),l&&(0,r.jsx)(x,{onClose:()=>o(!1),onUpdated:async()=>{await u()}})]}):null}function x(e){let{onClose:t,onUpdated:n}=e,[i,s]=(0,a.useState)(null),[c,p]=(0,a.useState)(null),[h,x]=(0,a.useState)("load"),[f,b]=(0,a.useState)(""),[g,v]=(0,a.useState)(""),y=(0,a.useRef)(null),w=(0,a.useRef)(!1),k=(0,a.useRef)(!0),j=(0,a.useCallback)(async()=>{v(""),x("load");try{let[e,t]=await Promise.all([u.NaemoStore.products(),(0,u.appleRequest)()]);if(!k.current)return;p(t);let n=e.products.find(e=>"com.naemo.app.personal.monthly"===e.id);s(null!=n?n:null),n||v("구독 상품을 불러오지 못했습니다. 잠시 후 다시 시도해 주세요.")}catch(e){k.current&&v(e instanceof Error?e.message:"구매 화면을 불러오지 못했습니다. 앱을 최신 버전으로 업데이트한 뒤 다시 시도해 주세요.")}finally{k.current&&x(null)}},[]);(0,a.useEffect)(()=>{var e;k.current=!0;let t=document.activeElement,n=document.body.style.overflow;return document.body.style.overflow="hidden",null===(e=y.current)||void 0===e||e.focus(),j(),()=>{k.current=!1,document.body.style.overflow=n,null==t||t.focus()}},[j]);let N=async e=>{if(!w.current&&!h){w.current=!0,x(e),v(""),b("");try{if("purchase"===e){let e=await (0,u.appleRequest)("prepare"),t=await u.NaemoStore.purchase(e);if("cancelled"===t.status)return;if("pending"===t.status){b("구매 승인을 기다리고 있습니다. 승인되면 플랜이 자동으로 적용됩니다.");return}let n=await (0,u.deliverApplePurchase)(t);p(n),b("구독이 적용되었습니다.")}else if("restore"===e){let e=await (0,u.restoreApplePurchases)(!0);p(e),b(e.hasAppleSubscription?"구매를 복원했습니다.":"이 계정에 복원할 활성 구독이 없습니다.")}else await u.NaemoStore.manage(),p(await (0,u.appleRequest)("refresh"));await n()}catch(e){v(e instanceof Error?e.message:"요청을 완료하지 못했습니다. 다시 시도해 주세요.")}finally{w.current=!1,x(null)}}},E=!!c&&(c.hasWebSubscription||(0,d.Om)(c.webPaidUntil)),z=()=>{h||t()};return(0,r.jsx)(o.Z,{children:(0,r.jsx)("div",{className:m().backdrop,onClick:z,children:(0,r.jsxs)("div",{ref:y,className:m().dialog,role:"dialog","aria-modal":"true","aria-labelledby":"apple-plan-title",tabIndex:-1,onClick:e=>e.stopPropagation(),onKeyDown:e=>{if("Escape"===e.key&&(e.preventDefault(),z()),"Tab"===e.key){var t,n;let r=Array.from(null!==(n=null===(t=y.current)||void 0===t?void 0:t.querySelectorAll("button:not(:disabled), a[href]"))&&void 0!==n?n:[]),a=r[0],i=r[r.length-1];if(!a){e.preventDefault();return}e.shiftKey&&(document.activeElement===a||document.activeElement===y.current)?(e.preventDefault(),i.focus()):e.shiftKey||document.activeElement!==i&&document.activeElement!==y.current||(e.preventDefault(),a.focus())}},children:[(0,r.jsxs)("header",{className:m().header,children:[(0,r.jsx)("h2",{id:"apple-plan-title",children:"플랜 및 구매"}),(0,r.jsx)("button",{type:"button","aria-label":"닫기",disabled:!!h,onClick:z,children:(0,r.jsx)(l.Z,{size:20})})]}),(0,r.jsxs)("p",{className:m().current,children:["현재 플랜 ",(0,r.jsx)("strong",{children:(0,d.Om)(null==c?void 0:c.paidUntil)?"개인 유료 플랜":c?"무료 플랜":"확인 중"})]}),(0,d.Om)(null==c?void 0:c.paidUntil)&&(0,r.jsxs)("p",{className:m().note,children:[new Date(c.paidUntil).toLocaleDateString("ko-KR"),"까지 이용 가능"]}),(0,r.jsxs)("section",{className:m().product,children:[(0,r.jsx)("h3",{children:"개인 월 구독"}),(0,r.jsx)("p",{children:"파일 저장 공간과 첨부 한도를 늘리고, 내모 유료 플랜의 기능을 이용합니다."}),(0,r.jsx)("p",{className:m().price,children:i?"".concat(i.displayPrice," / 월"):"가격 확인 중"}),(null==c?void 0:c.hasAppleSubscription)?(0,r.jsx)("p",{className:m().note,children:c.autoRenewEnabled?"자동 갱신 중입니다.":"자동 갱신이 해제되었습니다. 남은 기간 동안 이용할 수 있습니다."}):E?(0,r.jsx)("p",{className:m().note,children:"이미 이용 중인 플랜이 있습니다. 중복 결제를 막기 위해 현재 이용 기간이 끝난 뒤 구독할 수 있습니다."}):(0,r.jsx)("button",{type:"button",className:m().purchase,disabled:!!h||!i||!c,onClick:()=>void N("purchase"),children:"purchase"===h?"구매 확인 중…":"월 구독 시작"})]}),(0,r.jsx)("p",{className:m().note,children:"구독은 매월 자동 갱신되며 Apple 계정에 요금이 청구됩니다. 다음 갱신을 원하지 않으면 현재 이용 기간이 끝나기 최소 24시간 전에 Apple 구독 관리에서 취소해 주세요."}),g&&(0,r.jsxs)("div",{role:"alert",className:m().error,children:[g,(0,r.jsx)("button",{type:"button",disabled:!!h,onClick:()=>void j(),children:"다시 불러오기"})]}),f&&(0,r.jsx)("p",{role:"status",className:m().note,children:f}),(0,r.jsxs)("div",{className:m().actions,children:[(0,r.jsx)("button",{type:"button",disabled:!!h,onClick:()=>void N("restore"),children:"restore"===h?"복원 중…":"구매 복원"}),(0,r.jsx)("button",{type:"button",disabled:!!h,onClick:()=>void N("manage"),children:"구독 관리"})]}),(0,r.jsxs)("footer",{className:m().legal,children:[(0,r.jsx)("a",{href:"https://www.apple.com/legal/internet-services/itunes/dev/stdeula/",target:"_blank",rel:"noreferrer",children:"이용약관"}),(0,r.jsx)("a",{href:"/privacy",target:"_blank",rel:"noreferrer",children:"개인정보 처리방침"})]})]})})})}},285:function(e,t,n){"use strict";n.d(t,{k:function(){return v}});var r=n(57437),a=n(90982),i=n(78529),s=n(28691),l=n(2265),o=n(98319),c=n(99397),d=n(69076),u=n(30401),p=n(18930),m=n(93448),h=n(83008),x=n(67920),f=n(4908),b=n(54696),g=n(96214);function v(){let e=(0,x.XC)(),{showToast:t}=(0,o.pm)(),n=(0,b.Dv)(),{students:v}=(0,f.AF)(),y=(0,l.useMemo)(()=>v.filter(e=>"담임 학급"===e.homeClassId).sort((e,t)=>
1e.number-t.number),[v]),[w,k]=(0,l.useState)({columns:[],assignments:{}}),[j,N]=(0,l.useState)({columns:[],assignments:{}}),[E,z]=(0,l.useState)(!1),[C,S]=(0,l.useState)(!1),[L,A]=(0,l.useState)(!1),T=l.useRef([]),D=l.useCallback(()=>{T.current.push(JSON.parse(JSON.stringify(w))),T.current.length>20&&T.current.shift()},[w]);(0,l.useEffect)(()=>{let e=e=>{if((e.metaKey||e.ctrlKey)&&"z"===e.key){e.preventDefault();let t=T.current.pop();t&&k(t)}};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[]);let[R,B]=(0,l.useState)(null),[O,I]=(0,l.useState)(!1),[F,M]=(0,l.useState)(null),P=(e,t,n)=>{0===n.button&&(I(!0),M({row:e,col:t}),B({start:{row:e,col:t},end:{row:e,col:t}}))},_=(e,t)=>{O&&F&&B({start:F,end:{row:e,col:t}})},G=()=>{I(!1)},Z=(e,t)=>{if(!R)return!1;let n=Math.min(R.start.row,R.end.row),r=Math.max(R.start.row,R.end.row),a=Math.min(R.start.col,R.end.col),i=Math.max(R.start.col,R.end.col);return e>=n&&e<=r&&t>=a&&t<=i};(0,l.useEffect)(()=>{let e=()=>I(!1);return window.addEventListener("mouseup",e),()=>window.removeEventListener("mouseup",e)},[]);let[q,K]=(0,l.useState)(!0),[H,U]=(0,l.useState)(!1),[W,X]=(0,l.useState)(0);(0,l.useEffect)(()=>{let t=!1;return K(!0),U(!1),(async()=>{if(n){let e=await (0,g.getGuestData)();if(t)return;let n=e.studentRoles;
1if(n&&n.columns&&n.columns.length>0)k(n),N(n);else{let e={columns:[{id:"col-".concat(Date.now()),name:"역할1"}],assignments:{}};k(e),N(e)}return}let r=await (0,s.W)(()=>(0,h.X)("getTimetable",{type:"student-roles"},e));if(!t){if(!r.success)throw Error(r.error);if(r.success&&r.data&&Object.keys(r.data).length>0){let e=r.data;if(e.columns&&e.columns.length>0)k(e),N(e);else{let e={columns:[{id:"col-".concat(Date.now()),name:"역할1"}],assignments:{}};k(e),N(e)}}else{let e={columns:[{id:"col-".concat(Date.now()),name:"역할1"}],assignments:{}};k(e),N(e)}}})().catch(()=>{t||U(!0)}).finally(()=>{t||K(!1)}),()=>{t=!0}},[e,n,W]),(0,l.useEffect)(()=>{z(JSON.stringify(w)!==JSON.stringify(j))},[w,j]);let J=async()=>{A(!0);try{if(n){await (0,g.saveGuestData)("studentRoles",w);let e=[];Object.entries(w.assignments).forEach(t=>{let[n,r]=t;Object.entries(r).forEach(t=>{let[r,a]=t;if(a&&a.trim()){let t=y.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,g.saveGuestData)("roleAssignments",e)}else{let n=await (0,h.X)("saveTimetable",{type:"student-roles",data:w},e);if(!n.success){t(n.error||"저장에 실패했습니다. 다시 시도해주세요.","error");return}}N(w),z(!1),S(!0),setTimeout(()=>S(!1),2e3)}finally{A(!1)}},V=e=>{D(),k(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}})},Y=(e,t)=>{k(n=>({...n,columns:n.columns.map(n=>n.id===e?{...n,name:t}:n)}))},Q=(e,t,n)=>{k(r=>({...r,assignments:{...r.assignments,[e]:{...r.assignments[e],[t]:n}}}))},$=(e,t,n)=>{let r=e.clipboardData.getData("text");if(!r||(D(),!r.includes("	")&&!r.includes("\n")))return;e.preventDefault();let a=r.replace(/\r\n/g,"\n").split("\n").map(e=>e.split("	")),i=y.map(e=>e.id),s=w.columns.map(e=>e.id),l=i.indexOf(t),o=s.indexOf(n);-1!==l&&-1!==o&&k(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}})},ee=(0,l.useCallback)(()=>{R&&(D(),k(e=>{let t={...e.assignments},n=Math.min(R.start.row,R.end.row),r=Math.max(R.start.row,R.end.row),a=Math.min(R.start.col,R.end.col),i=Math.max(R.start.col,R.end.col);for(let s=n;s<=r;s++){if(s>=y.length)continue;let n=y[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}}))},[R,D,y]),et=(0,l.useCallback)(async()=>{if(!R)return;let e=Math.min(R.start.row,R.end.row),t=Math.max(R.start.row,R.end.row),n=Math.min(R.start.col,R.end.col),r=Math.max(R.start.col,R.end.col),a="";for(let s=e;s<=t;s++){let e=[],l=y[s];if(l){for(let t=n;t<=r;t++){var i;let n=w.columns[t];if(!n)continue;let r=(null===(i=w.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)}},[R,y,w]),en=(0,l.useCallback)(e=>{var t;if(!R)return;D(),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(R.start.row,R.end.row),a=Math.min(R.start.col,R.end.col),i=n.replace(/\r\n/g,"\n").split("\n").map(e=>e.split("	"));k(e=>{let t={...e.assignments};return i.forEach((n,i)=>{let s=r+i;if(s>=y.length)return;let l=y[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}})},[R,D,y]);(0,l.useEffect)(()=>{let e=async e=>{if(("Delete"===e.key||"Backspace"===e.key)&&R){let t=e.target;if("INPUT"===t.tagName&&t.value)return;e.preventDefault(),ee()}(e.metaKey||e.ctrlKey)&&"c"===e.key&&R&&(e.preventDefault(),et())},t=e=>{R&&(e.target,en(e))};return window.addEventListener("keydown",e),window.addEventListener("paste",t),()=>{window.removeEventListener("keydown",e),window.removeEventListener("paste",t)}},[R,w,y,et,ee,en]);let er=(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 q?(0,r.jsx)(a.ContentLoadingState,{variant:"table",label:"학생 역할을 불러오고 있어요"}):H?(0,r.jsx)(i.A,{message:"학생 역할을 불러오지 못했어요.",onRetry:()=>X(e=>e+1)}):(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:()=>{D();let e="col-".concat(Date.now());k(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)(c.Z,{size:14}),"열 추가"]}),(0,r.jsxs)("button",{onClick:()=>k({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)(d.Z,{size:14}),"초기화"]}),(0,r.jsx)("button",{onClick:J,disabled:!E||L,className:(0,m.cn)("flex items-center gap-2 text-[13px] px-3 py-1.5 rounded-lg transition-colors font-medium",E&&!L?"bg-[#2883DE] text-white hover:opacity-90":C?"text-[#2883DE]":"text-[#52525b] cursor-not-allowed"),children:L?"저장 중...":C?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(u.Z,{size:14}),"저장됨"]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(u.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:"이름"}),w.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=>Y(e.id,t.target.value),onFocus:()=>D(),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:()=>V(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)(p.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:y.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}),w.columns.map((n,a)=>{var i;return(0,r.jsx)("td",{className:(0,m.cn)("p-0 border-r border-b border-zinc-200 dark:border-[#27272a] relative select-none",Z(t,a)&&"bg-blue-500/10 ring-1 ring-inset ring-blue-500 z-10"),onMouseDown:e=>P(t,a,e),onMouseEnter:()=>_(t,a),onMouseUp:G,children:(0,r.jsx)("input",{id:"role-input-".concat(t,"-").concat(a),type:"text",value:(null===(i=w.assignments[e.id])||void 0===i?void 0:i[n.id])||"",onChange:t=>Q(e.id,n.id,t.target.value),onFocus:()=>{},onPaste:t=>{R&&(R.start.row!==R.end.row||R.start.col!==R.end.col)||$(t,e.id,n.id)},onKeyDown:e=>er(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===y.length&&(0,r.jsx)("div",{className:"py-20 text-center text-zinc-500 text-sm",children:"등록된 학생이 없습니다. '학생 명단' 탭에서 학생을 먼저 등록해 주세요."})]})]})}},93627:function(e,t,n){"use strict";n.d(t,{Q:function(){return L}});var r=n(57437),a=n(90982),i=n(2265),s=n(98319),l=n(51817),o=n(50733),c=n(69076),d=n(30401),u=n(32489),p=n(93448),m=n(83008),h=n(67920),x=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 w(e,t){return{row:Math.max(0,Math.min(6,e)),col:Math.max(0,Math.min(g-1,t))}}function k(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 E=["image/png","image/jpeg","image/webp","image/heic","image/heif","application/pdf"];function z(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>
1t(r.result),r.onerror=n,r.readAsDataURL(e)})}async function C(e){let t=e=>e.split(",")[1]||"";if("application/pdf"===e.type)return{base64:t(await z(e)),mediaType:"application/pdf"};let n=await z(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:a,className:s}=e,l=(0,i.useRef)(null),o=(0,i.useCallback)(()=>{let e=l.current;e&&(e.style.height="auto",e.style.height="".concat(e.scrollHeight,"px"))},[]);return(0,i.useLayoutEffect)(o,[o,t]),(0,i.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:a,className:s,rows:1,placeholder:"-"})}function L(e){var t,n;let{gridSelection:z=!0}=e,L=(0,h.XC)(),{showToast:A}=(0,s.pm)(),T=(0,x.Dv)(),[D,R]=(0,i.useState)("my"),[B,O]=(0,i.useState)({}),[I,F]=(0,i.useState)({}),[M,P]=(0,i.useState)({}),[_,G]=(0,i.useState)({}),[Z,q]=(0,i.useState)(!1),[K,H]=(0,i.useState)(!1),[U,W]=(0,i.useState)(!0),[X,J]=(0,i.useState)(!1),V=(0,i.useRef)(null),[Y,Q]=(0,i.useState)(!1),[$,ee]=(0,i.useState)(null),[et,en]=(0,i.useState)(null),[er,ea]=(0,i.useState)(null),[ei,es]=(0,i.useState)(!1),[el,eo]=(0,i.useState)(null),ec=(0,i.useRef)(null),ed=(0,i.useCallback)(e=>{ec.current=e,eo(e)},[]),eu=(0,i.useRef)(null),ep=(0,i.useRef)(null),em=(0,i.useRef)(!1),eh=z&&null!==(t=null==er?void 0:er.end.row)&&void 0!==t?t:null,ex=z&&null!==(n=null==er?void 0:er.end.col)&&void 0!==n?n:null;(0,i.useEffect)(()=>{ea(null),ed(null)},[D,ed]),(0,i.useLayoutEffect)(()=>{var e;null!==eh&&null!==ex&&(null===(e=ep.current)||void 0===e||e.focus())},[eh,ex]);let ef="my"===D?B:I,eb="my"===D?O:F,eg=i.useRef([]),ev=i.useCallback(()=>{eg.current.push({teacher:JSON.parse(JSON.stringify(B)),class:JSON.parse(JSON.stringify(I))}),eg.current.length>20&&eg.current.shift()},[B,I]);(0,i.useEffect)(()=>{let e=e=>{if((e.metaKey||e.ctrlKey)&&"z"===e.key&&!ec.current){e.preventDefault();let t=eg.current.pop();t&&(O(t.teacher),F(t.class))}};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[]),(0,i.useEffect)(()=>{if(null===eh||null===ex||el)return;let e=ef[v(eh,ex)]||"";ep.current&&ep.current.value!==e&&(ep.current.value=e)},[ef,eh,ex,el]),(0,i.useEffect)(()=>{(async()=>{W(!0);try{if(T){let e=await (0,f.getGuestData)(),t=e.teacherTimetable||{},n=e.classTimetable||{};O(t),P(t),F(n),G(n);return}let[e,t]=await Promise.all([(0,m.X)("getTimetable",{type:"teacher"},L),(0,m.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;F(e),G(e)}}finally{W(!1)}})()},[L,T]),(0,i.useEffect)(()=>{let e=JSON.stringify(B)!==JSON.stringify(M),t=JSON.stringify(I)!==JSON.stringify(_);q(e||t)},[B,I,M,_]);let ey=async()=>{J(!0);try{if(T)await (0,f.saveGuestData)("teacherTimetable",B),await (0,f.saveGuestData)("classTimetable",I);else{let e=await (0,m.X)("saveTimetable",{type:"teacher",data:B},L);if(!e.success){A(e.error||"저장에 실패했습니다. 다시 시도해주세요.","error");return}let t=await (0,m.X)("saveTimetable",{type:"class",data:I},L);if(!t.success){A(t.error||"저장에 실패했습니다. 다시 시도해주세요.","error");return}}P(B),G(I),q(!1),H(!0),en(null),setTimeout(()=>H(!1),2e3)}finally{J(!1)}},ew=async e=>{if(ee(null),T){ee("체험 모드에서는 사진 인식을 사용할 수 없습니다. 로그인 후 이용해 주세요.");return}if(!E.includes(e.type)){ee("JPG, PNG, HEIC 사진이나 PDF 파일만 인식할 수 있습니다.");return}Q(!0);try{let{base64:t,mediaType:n}=await C(e),r=await (0,m.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"===D?O(a):F(a),en(D)}catch(e){console.error("[SettingsTabTimetable] 시간표 인식 실패:",e),ee("시간표를 분석하는 중 문제가 생겼습니다. 잠시 후 다시 시도해 주세요.")}finally{Q(!1)}},ek=(e,t)=>{ev(),ep.current&&(ep.current.value=""),eb(n=>({...n,[v(e,t)]:""})),ed({row:e,col:t})},ej=(e,t,n)=>{if(z&&(!el||el.row!==t||el.col!==n)){if(em.current){var r;null===(r=ep.current)||void 0===r||r.blur(),em.current=!1}if(e.shiftKey&&er){e.preventDefault(),ea({start:er.start,end:{row:t,col:n}});return}e.preventDefault(),ed(null),ea({start:{row:t,col:n},end:{row:t,col:n}}),es(!0)}},eN=(e,t)=>{z&&ei&&er&&ea({start:er.start,end:{row:e,col:t}})};(0,i.useEffect)(()=>
1{if(!ei)return;let e=()=>es(!1);return window.addEventListener("mouseup",e),()=>window.removeEventListener("mouseup",e)},[ei]);let eE=e=>!!e&&new DOMParser().parseFromString(e,"text/html").querySelectorAll("tr").length>0,ez=(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,p.R3)(t);return n.length>0?n:null},eC=(e,t,n)=>{let r=e.clipboardData.getData("text/html"),a=e.clipboardData.getData("text");if(!eE(r)&&!/[\t\n]/.test(a))return;let i=ez(r,a);if(!i)return;e.preventDefault(),ev();let s=j("my"===D?B:I,i,n-1,b.indexOf(t));"my"===D?O(s):F(s)};return((0,i.useEffect)(()=>{if(!z)return;let e=async e=>{if(ec.current||!er)return;let t=e.target;if(t&&t!==ep.current&&("INPUT"===t.tagName||"TEXTAREA"===t.tagName))return;let n=e.metaKey||e.ctrlKey,{row:r,col:a}=er.end,i="my"===D?B:I;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,p.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"===D?O(e=>k(e,er)):F(e=>k(e,er)));return}if("Delete"===e.key||"Backspace"===e.key){var s;e.preventDefault(),s=k(i,er),ev(),"my"===D?O(s):F(s);return}if("Enter"===e.key||"F2"===e.key){e.preventDefault(),ed({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=w(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=w(r,a+(e.shiftKey?-1:1));ea({start:t,end:t});return}};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[z,er,D,B,I,ev,ed]),U)?(0,r.jsx)(a.ContentLoadingState,{variant:"table",label:"시간표를 불러오고 있어요"}):(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,p.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"===D?"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,p.cn)("flex-1 flex items-center justify-center text-[13px] font-medium transition-colors","my"===D?"text-black dark:text-white":"text-zinc-500 dark:text-[#a1a1aa]"),children:"개인 시간표"}),(0,r.jsx)("div",{className:(0,p.cn)("flex-1 flex items-center justify-center text-[13px] font-medium transition-colors","class"===D?"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:V,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&&ew(n)}}),(0,r.jsx)("button",{onClick:()=>{var e;if(T){ee("체험 모드에서는 사진 인식을 사용할 수 없습니다. 로그인 후 이용해 주세요.");return}ee(null),null===(e=V.current)||void 0===e||e.click()},disabled:Y,className:(0,p.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)(l.Z,{size:14,className:"animate-spin"}),"인식하는 중..."]}):(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(o.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)(c.Z,{size:14}),"초기화"]}),(0,r.jsx)("button",{onClick:ey,disabled:!Z||X,className:(0,p.cn)("flex items-center gap-2 text-[13px] px-3 py-1.5 rounded-lg transition-colors font-medium",Z&&!X?"bg-[#007AFF] dark:bg-[#2883DE] text-white hover:opacity-90":K?"text-[#2883DE]":"text-[#52525b] cursor-not-allowed"),children:X?"저장 중...":K?(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)(u.Z,{size:14})})]}),et===D&&(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)(u.Z,{size:14})})]}),(0,r.jsx)("div",{ref:eu,tabIndex:z?0:void 0,onPaste:e=>{if(!z||ec.current||!er)return;let t=ez(e.clipboardData.getData("text/html"),e.clipboardData.getData("text"));if(!t)return;e.preventDefault(),ev();let{start:n,end:r}=er,a=j("my"===D?B:I,t,Math.min(n.row,r.row),Math.min(n.col,r.col));"my"===D?O(a):F(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,p.cn)("w-full border-collapse",z&&"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=!!z&&!!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),c=z&&(null==er?void 0:er.end.row)===a&&(null==er?void 0:er.end.col)===n;return(0,r.jsx)("td",{className:(0,p.cn)("p-0 border border-zinc-200 dark:border-[#27272a]",c&&"ring-2 ring-inset ring-[#2883DE]"),onMouseDown:e=>ej(e,a,n),onMouseEnter:()=>eN(a,n),onDoubleClick:()=>z&&ed({row:a,col:n}),children:(0,r.jsx)("div",{className:(0,p.cn)("h-[60px] flex items-center justify-center transition-colors",o?"bg-[#2883DE]/10 dark:bg-[#2883DE]/20":"bg-[#fafaf9] dark:bg-[#18181b]"),children:z?c?(0,r.jsx)("textarea",{ref:ep,"data-grid-editor":"true",defaultValue:s,onCompositionStart:()=>{em.current=!0,l||ek(a,n)},onCompositionEnd:()=>{em.current=!1},onMouseUp:e=>{if(l)return;if(e.detail>=2){ed({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||ed({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){ek(a,n);return}if("Escape"===e.key){e.preventDefault(),ed(null),ea({start:{row:a,col:n},end:{row:a,col:n}});return}if("Tab"===e.key){e.preventDefault(),e.nativeEvent.stopImmediatePropagation(),ed(null);let t=w(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)({ke
1y:e.key,altKey:e.altKey,shiftKey:e.shiftKey,multiline:!0})){(0,N.yl)(e.currentTarget);return}ed(null);let t=w(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(),ed(null);let t=w(a+i.rowDelta,n+i.colDelta);ea({start:t,end:t})}},onBlur:()=>{ed(null)},onPaste:n=>eC(n,t,e),className:(0,p.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=>eC(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){"use strict";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),[c,d]=(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&&d(!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)}},p="mobile"===t;return(0,r.jsxs)("div",{className:p?"flex items-center justify-between px-4 py-3.5":"flex items-center justify-between h-8",children:[(0,r.jsx)("span",{className:p?"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:c,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",c?"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){"use strict";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"),[c,d]=(0,a.useState)(!1),u=(0,i.usePathname)(),p=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"),d(!0)},[]),(0,a.useEffect)(()=>{if(!c)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(p){t("dark");return}let n=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{"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,c,p]);let m=e=>{o(e)};return(0,r.jsx)(s.Provider,{value:{theme:l,toggleTheme:()=>{m("dark"===l?"light":"dark")},setTheme:m},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}},56205:function(e,t,n){"use strict";n.r(t),n.d(t,{NaemoStore:function(){return r},appleRequest:function(){return a},deliverApplePurchase:function(){return i},restoreApplePurchases:function(){return s}});let r=(0,n(56689).fo)("NaemoStore");async function a(e,t){let n=await fetch("/api/apple/subscription",{...e?{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:e,...t})}:{},cache:"no-store",signal:AbortSignal.timeout(3e4)}),r=await n.json();if(!n.ok)throw Error(r.error||"구매 정보를 확인하지 못했습니다. 다시 시도해 주세요.");return r}async function i(e){let t=await a("sync",{signedTransaction:e.signedTransaction});return await r.finish({transactionId:e.transactionId}),t}async function s(){let e,t=arguments.length>0&&void 0!==arguments[0]&&arguments[0],{transactions:n}
1=await (t?r.restore():r.entitlements());for(let t of n)try{await i(t)}catch(t){e=t}let s=await a("refresh");if(e)throw e;return s}},69813:function(e,t,n){"use strict";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){"use strict";function r(e){return!e.nativeShell&&!e.isGuest&&!!e.hasSession&&!e.rosterWizardCompleted&&!e.snoozed&&!e.deferredOnPhone&&(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.isGuest&&!!e.hasSession&&!!e.loaded&&!e.wizardOpen&&0===e.studentCount}function s(e){return!e.nativeShell&&e.previewQuery}function l(e){return!e}n.d(t,{CX:function(){return h},D1:function(){return f},D5:function(){return i},Li:function(){return v},Nx:function(){return d},QY:function(){return p},X9:function(){return u},Yq:function(){return y},Ys:function(){return c},a8:function(){return s},f9:function(){return x},fL:function(){return l},qr:function(){return g},sg:function(){return r},vh:function(){return a}});let o="rosterWizardProgress";function c(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Date.now();try{let t=window.localStorage.getItem(o);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(o,JSON.stringify({...e,updatedAt:t}))}catch(e){}}function u(){try{window.localStorage.removeItem(o)}catch(e){}}function p(e){return 4===e.step?3:e.step}let m="rosterWizardSnoozed";function h(){try{window.sessionStorage.setItem(m,"1")}catch(e){}}function x(){try{return"1"===window.sessionStorage.getItem(m)}catch(e){return!1}}function f(){try{window.sessionStorage.removeItem(m)}catch(e){}}let b="rosterWizardDeferredToPc";function g(){try{window.localStorage.setItem(b,"1")}catch(e){}}function v(){try{return"1"===window.localStorage.getItem(b)}catch(e){return!1}}function y(){try{window.localStorage.removeItem(b)}catch(e){}}},90241:function(e,t,n){"use strict";n.d(t,{f_:function(){return a},pM:function(){return c},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.cz)(),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 c(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)}},14714:function(e){e.exports={row:"apple-plan-settings_row__tp4SU",backdrop:"apple-plan-settings_backdrop__LY4yw",dialog:"apple-plan-settings_dialog__cLYIb",header:"apple-plan-settings_header__PTuB4",current:"apple-plan-settings_current__Hqmj1",product:"apple-plan-settings_product__E5ngJ",price:"apple-plan-settings_price___8ac2",purchase:"apple-plan-settings_purchase__HQF0p",note:"apple-plan-settings_note__fMasS",error:"apple-plan-settings_error__TnHt5",actions:"apple-plan-settings_actions__V5ea2",legal:"apple-plan-settings_legal__LkPiF"}}}]);

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.