PageSourceSearch

https://seodoon-church-admin.web.app/assets/CultureManagementPage-DsZocwz2.js

js seodoon-church-admin.web.app collected 2026-10-03 12:36:14 UTC 76,603 bytes, 99 lines download raw bytes

1import{j as e}from"./vendor-data-Z5doMMu-.js";import{r as h}from"./vendor-react-zNM0AU50.js";import{A as De}from"./index.esm-NBNS-BUx.js";/* empty css                        */import{W as Z,K as Te,c as ee,U as le,y as Pe,a7 as f,ar as xe,d as ce,b8 as Ae,A as ue,aa as ie,af as pe,T as $e,D as ge,ax as Ie,aA as fe,X as te,n as be,a8 as ye,ah as Re,ai as Le,aW as Oe,Y as Me}from"./vendor-ui-CW5bJ11U.js";import{H as je,q,l as O,w as B,m as z,k as T,u as U,z as oe,j as V,x as K,t as Ue,y as Fe,D as Ne,G as ve,E as we}from"./vendor-firebase-Da9KY1q_.js";import{d as S,s as Se}from"./index-D1K3lVhu.js";import"./vendor-charts-08G1rM8P.js";const de="seodoon-church",Y=`churches/${de}/cultureSemesters`,G=`churches/${de}/cultureCourses`,L=`churches/${de}/cultureEnrollments`,H={async getAllSemesters(){try{const t=q(O(S,Y),K("year","desc"),K("semester","desc"));return(await z(t)).docs.map(s=>({id:s.id,...s.data()}))}catch(t){throw console.error("학기 목록 조회 실패:",t),t}},async getSemester(t){try{const a=T(S,Y,t),s=await V(a);return s.exists()?{id:s.id,...s.data()}:null}catch(a){throw console.error("학기 조회 실패:",a),a}},async getActiveSemester(){try{let t=q(O(S,Y),B("status","==","recruiting")),a=await z(t);if(!a.empty){const s=a.docs[0];return{id:s.id,...s.data()}}if(t=q(O(S,Y),B("status","==","active")),a=await z(t),!a.empty){const s=a.docs[0];return{id:s.id,...s.data()}}return null}catch(t){throw console.error("활성 학기 조회 실패:",t),t}},async createSemester(t){try{const a={...t,createdAt:new Date,updatedAt:new Date};return(await oe(O(S,Y),a)).id}catch(a){throw console.error("학기 생성 실패:",a),a}},async updateSemester(t,a){try{const s=T(S,Y,t);await U(s,{...a,updatedAt:new Date})}catch(s){throw console.error("학기 수정 실패:",s),s}},async updateSemesterStatus(t,a){try{const s=T(S,Y,t);await U(s,{status:a,updatedAt:new Date})}catch(s){throw console.error("학기 상태 변경 실패:",s),s}},async deleteSemester(t){try{const a=je(S),s=q(O(S,G),B("semesterId","==",t));(await z(s)).docs.forEach(x=>{a.delete(x.ref)});const d=q(O(S,L),B("semesterId","==",t));(await z(d)).docs.forEach(x=>{a.delete(x.ref)}),a.delete(T(S,Y,t)),await a.commit()}catch(a){throw console.error("학기 삭제 실패:",a),a}}},$={async getCoursesBySemester(t){try{const a=q(O(S,G),B("semesterId","==",t),K("sortOrder","asc"));return(await z(a)).docs.map(u=>({id:u.id,...u.data()}))}catch(a){throw console.error("강좌 목록 조회 실패:",a),a}},async getCourse(t){try{const a=T(S,G,t),s=await V(a);return s.exists()?{id:s.id,...s.data()}:null}catch(a){throw console.error("강좌 조회 실패:",a),a}},async getOpenCourses(t){try{const a=q(O(S,G),B("semesterId","==",t),B("status","==","open"),K("sortOrder","asc"));return(await z(a)).docs.map(u=>({id:u.id,...u.data()}))}catch(a){throw console.error("공개 강좌 조회 실패:",a),a}},async createCourse(t){try{const a={...t,currentEnrollment:0,isMinEnrollmentMet:!1,createdAt:new Date,updatedAt:new Date};return(await oe(O(S,G),a)).id}catch(a){throw console.error("강좌 생성 실패:",a),a}},async updateCourse(t,a){try{const s=T(S,G,t);await U(s,{...a,updatedAt:new Date})}catch(s){throw console.error("강좌 수정 실패:",s),s}},async updateCourseStatus(t,a){try{const s=T(S,G,t);await U(s,{status:a,updatedAt:new Date})}catch(s){throw console.error("강좌 상태 변경 실패:",s),s}},async deleteCourse(t){try{const a=je(S),s=q(O(S,L),B("courseId","==",t));(await z(s)).docs.forEach(d=>{a.delete(d.ref)}),a.delete(T(S,G,t)),await a.commit()}catch(a){throw console.error("강좌 삭제 실패:",a),a}},async copyCoursesBetweenSemesters(t,a,s){try{const u=await this.getCoursesBySemester(t),d=s?u.filter(x=>s.includes(x.id)):u;let v=0;for(const x of d){const c={semesterId:a,category:x.category,name:x.name,description:x.description||"",instructor:x.instructor,instructorTitle:x.instructorTitle||"",instructorPhone:x.instructorPhone||"",instructorProfile:x.instructorProfile||{photoUrl:"",bio:"",careers:[]},schedule:x.schedule,scheduleText:x.scheduleText,durationText:x.durationText,location:x.location,imageUrl:x.imageUrl||"",tagline:x.tagline||"",targetAudience:x.targetAudience||"",suppliesNeeded:x.suppliesNeeded||"",curriculum:[],maxEnrollment:x.maxEnrollment,fee:x.fee,feeType:x.feeType,status:"draft",sortOrder:x.sortOrder,createdBy:"admin"};await this.createCourse(c),v++}return v}catch(u){throw console.error("강좌 복사 실패:",u),u}},async updateEnrollmentCount(t,a){try{const s=T(S,G,t);await U(s,{currentEnrollment:Ue(a),updatedAt:new Date});const u=await V(s);if(u.exists()){const d=u.data(),v=await V(T(S,Y,d.semesterId));if(v.exists()){const x=v.data(),c=d.currentEnrollment>=x.minEnrollment;await U(s,{isMinEnrollmentMet:c})}}}catch(s){throw console.error("수강 인원 업데이트 실패:",s),s}}},J={async getEnrollmentsBySemester(t){try{const a=q(O(S,L),B("semesterId","==",t),K("createdAt","desc"));return(await z(a)).docs.map(u=>({id:u.id,...u.data()}))}catch(a){throw console.error("수강신청 목록 조회 실패:",a),a}},async getEnrollmentsByCourse(t){try{const a=q(O(S,L),B("courseId","==",t),K("createdAt","desc"));return(await z(a)).docs.map(u=>({id:u.id,...u.data()}))}catch(a){throw console.error("강좌별 수강신청 조회 실패:",a),a}},async getEnrollment(t){try{const a=T(S,L,t),s=await V(a);return s.exists()?{id:s.id,...s.data()}:null}catch(a){throw console.error("수강신청 조회 실패:",a),a}},async createEnrollment(t){try{const a={...t,enrolledAt:new Date,createdAt:new Date,updatedAt:new Date},s=await oe(O(S,L),a);return await $.updateEnrollmentCount(t.courseId,1),s.id}catch(a){throw console.error("수강신청 생성 실패:",a),a}},async updateEnrollment(t,a){try{const s=T(S,L,t);await U(s,{...a,updatedAt:new Date})}catch(s){throw console.error("수강신청 수정 실패:",s),s}},async confirmEnrollment(t,a){try{const s=T(S,L,t);await U(s,{status:"confirmed",confirmedAt:new Date,confirmedBy:a,updatedAt:new Date})}catch(s){throw console.error("수강신청 확정 실패:",s),s}},async confirmPayment(t,a){try{const s=T(S,L,t);await U(s,{paymentStatus:"paid",paymentDate:new Date().toISOString().split("T")[0],...a,updatedAt:new Date})}catch(s){throw console.error("납부 확인 실패:",s),s}},async refundPayment(t){try{const a=T(S,L,t);await U(a,{paymentStatus:"refunded",updatedAt:new Date})}catch(a){throw console.error("환불 처리 실패:",a),a}},async cancelEnrollment(t,a){try{const s=await V(T(S,L,t));if(!s.exists())throw new Error("수강신청을 찾을 수 없습니다.");const u=s.data(),d=T(S,L,t);await U(d,{status:"cancelled",cancelledAt:new Date,cancelReason:a,updatedAt:new Date}),await $.updateEnrollmentCount(u.courseId,-1)}catch(s){throw console.error("수강신청 취소 실패:",s),s}},async deleteEnrollment(t){try{const a=await V(T(S,L,t));if(a.exists()){const s=a.data();
1s.status!=="cancelled"&&await $.updateEnrollmentCount(s.courseId,-1)}await Fe(T(S,L,t))}catch(a){throw console.error("수강신청 삭제 실패:",a),a}}},qe={async getSemesterStats(t){try{const a=await $.getCoursesBySemester(t),s=await J.getEnrollmentsBySemester(t),u=a.map(d=>{const v=s.filter(x=>x.courseId===d.id);return{courseId:d.id,courseName:d.name,category:d.category,enrollmentCount:v.length,confirmedCount:v.filter(x=>x.status==="confirmed").length,paidCount:v.filter(x=>x.paymentStatus==="paid").length,isMinEnrollmentMet:d.isMinEnrollmentMet}});return{totalCourses:a.length,openCourses:a.filter(d=>d.status==="open").length,totalEnrollments:s.length,confirmedEnrollments:s.filter(d=>d.status==="confirmed").length,paidEnrollments:s.filter(d=>d.paymentStatus==="paid").length,courseStats:u}}catch(a){throw console.error("학기 통계 조회 실패:",a),a}}},se={music:"음악",literature:"문학",craft:"공예",other:"기타"},re={planning:"준비중",recruiting:"모집중",active:"진행중",closed:"종료"},ze={draft:"준비중",open:"모집중",closed:"마감",cancelled:"취소"},me={pending:"미납",paid:"납부완료",refunded:"환불"},he={pending:"대기",confirmed:"확정",cancelled:"취소",withdrawn:"철회"},Ce={once:"1회(일시납)",monthly:"월납",semester:"학기납"};function ne(t,a="monthly"){const s=`${t.toLocaleString()}원`;return a==="once"?`${s} (1회)`:a==="semester"?`학기 ${s}`:`월 ${s}`}const Be=["일","월","화","수","목","금","토"],_e=[{category:"music",name:"발성교실"},{category:"music",name:"우쿨렐레"},{category:"music",name:"오카리나"},{category:"music",name:"플루트"},{category:"music",name:"하모니카"},{category:"literature",name:"영어교실"}];function ae(t){const a=t.dayOfWeek.map(u=>Be[u]).join(", "),s=`${t.startTime} ~ ${t.endTime}`;return t.note?`${a} ${s} (${t.note})`:`${a} ${s}`}function We(t,a){return`${t}년 ${a==="first"?"상반기":"하반기"}`}function it(){const[t,a]=h.useState("dashboard"),s=[{id:"dashboard",label:"대시보드",icon:Te},{id:"semesters",label:"학기 관리",icon:ee},{id:"courses",label:"강좌 관리",icon:Z},{id:"enrollments",label:"수강신청",icon:le},{id:"instructors",label:"강사 관리",icon:Pe}];return e.jsxs("div",{className:"space-y-4",children:[e.jsx("div",{className:"flex items-center justify-between",children:e.jsxs("div",{children:[e.jsxs("h1",{className:"text-[18px] font-bold text-gray-900 flex items-center gap-2",children:[e.jsx(Z,{className:"w-5 h-5 text-blue-600"}),"서둔문화강좌"]}),e.jsx("p",{className:"text-[12px] text-gray-500 mt-1",children:"문화강좌 학기, 강좌, 수강신청을 관리합니다"})]})}),e.jsx("div",{className:"flex gap-1 border-b border-gray-200",children:s.map(u=>e.jsxs("button",{onClick:()=>a(u.id),className:`flex items-center gap-1.5 px-4 py-2 text-[13px] font-medium border-b-2 -mb-[1px] transition-colors ${t===u.id?"border-blue-600 text-blue-600":"border-transparent text-gray-500 hover:text-gray-700"}`,children:[e.jsx(u.icon,{className:"w-4 h-4"}),u.label]},u.id))}),t==="dashboard"&&e.jsx(Ye,{}),t==="semesters"&&e.jsx(Ge,{}),t==="courses"&&e.jsx(Ve,{}),t==="enrollments"&&e.jsx(Qe,{}),t==="instructors"&&e.jsx(Ze,{})]})}function Ye(){const[t,a]=h.useState(!0),[s,u]=h.useState(null),[d,v]=h.useState(null);h.useEffect(()=>{x()},[]);async function x(){try{a(!0);const n=await H.getActiveSemester();if(u(n),n){const N=await qe.getSemesterStats(n.id);v(N)}}catch(n){console.error("대시보드 로딩 실패:",n),f.error("데이터를 불러올 수 없습니다")}finally{a(!1)}}if(t)return e.jsx("div",{className:"flex items-center justify-center h-64",children:e.jsx("div",{className:"text-gray-500 text-[13px]",children:"로딩 중..."})});if(!s)return e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-8 text-center",children:[e.jsx(ee,{className:"w-12 h-12 text-gray-300 mx-auto mb-3"}),e.jsx("p",{className:"text-[14px] text-gray-600 mb-2",children:"진행 중인 학기가 없습니다"}),e.jsx("p",{className:"text-[12px] text-gray-500",children:"학기 관리 탭에서 새 학기를 추가하세요"})]});const c=(d==null?void 0:d.courseStats.filter(n=>!n.isMinEnrollmentMet))||[];return e.jsxs("div",{className:"space-y-4",children:[e.jsx("div",{className:"bg-white border border-gray-200 rounded-lg p-4",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[12px] text-gray-500 mb-1",children:"현재 학기"}),e.jsx("h2",{className:"text-[20px] font-bold text-gray-900",children:s.name}),e.jsxs("p",{className:"text-[12px] text-gray-500 mt-1",children:[s.startDate," ~ ",s.endDate]})]}),e.jsxs("div",{className:"text-right",children:[e.jsx("span",{className:`px-3 py-1 rounded-full text-[11px] font-medium text-white ${s.status==="recruiting"?"bg-green-500":s.status==="active"?"bg-blue-500":"bg-gray-500"}`,children:re[s.status]}),e.jsxs("p",{className:"text-[11px] text-gray-500 mt-2",children:["담당: ",s.managerName," (",s.managerPhone,")"]})]})]})}
1),d&&e.jsxs("div",{className:"grid grid-cols-5 gap-3",children:[e.jsx(X,{label:"전체 강좌",value:d.totalCourses,icon:e.jsx(Z,{className:"w-4 h-4"})}),e.jsx(X,{label:"모집중 강좌",value:d.openCourses,icon:e.jsx(xe,{className:"w-4 h-4"}),color:"green"}),e.jsx(X,{label:"총 신청",value:d.totalEnrollments,icon:e.jsx(le,{className:"w-4 h-4"})}),e.jsx(X,{label:"확정",value:d.confirmedEnrollments,icon:e.jsx(ce,{className:"w-4 h-4"}),color:"blue"}),e.jsx(X,{label:"납부완료",value:d.paidEnrollments,icon:e.jsx(Ae,{className:"w-4 h-4"}),color:"blue"})]}),c.length>0&&e.jsx("div",{className:"bg-orange-50 border border-orange-200 rounded-lg p-4",children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx(ue,{className:"w-5 h-5 text-orange-500 mt-0.5"}),e.jsxs("div",{children:[e.jsxs("h3",{className:"text-[13px] font-bold text-orange-800",children:["최소 인원 미충족 강좌 (",c.length,"개)"]}),e.jsxs("p",{className:"text-[12px] text-orange-700 mt-1",children:["아래 강좌는 최소 개설 인원(",s.minEnrollment,"명)을 충족하지 못했습니다"]}),e.jsx("div",{className:"flex flex-wrap gap-2 mt-2",children:c.map(n=>e.jsxs("span",{className:"px-2 py-1 bg-orange-100 text-orange-800 rounded text-[11px]",children:[n.courseName," (",n.enrollmentCount,"명)"]},n.courseId))})]})]})}),d&&d.courseStats.length>0&&e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm",children:[e.jsx("div",{className:"p-3 border-b border-gray-200",children:e.jsx("h3",{className:"text-[14px] font-bold text-gray-900",children:"강좌별 신청 현황"})}),e.jsx("div",{className:"divide-y divide-gray-100",children:d.courseStats.map(n=>e.jsxs("div",{className:"p-3 flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] font-medium ${n.category==="music"?"bg-slate-100 text-slate-700":n.category==="literature"?"bg-blue-100 text-blue-700":"bg-gray-100 text-gray-700"}`,children:se[n.category]}),e.jsx("span",{className:"text-[13px] font-medium text-gray-900",children:n.courseName})]}),e.jsxs("div",{className:"flex items-center gap-4 text-[12px]",children:[e.jsxs("span",{className:"text-gray-600",children:["신청 ",e.jsx("strong",{children:n.enrollmentCount}),"명"]}),e.jsxs("span",{className:"text-blue-600",children:["확정 ",e.jsx("strong",{children:n.confirmedCount}),"명"]}),e.jsxs("span",{className:"text-blue-600",children:["납부 ",e.jsx("strong",{children:n.paidCount}),"명"]}),n.isMinEnrollmentMet?e.jsx(xe,{className:"w-4 h-4 text-green-500"}):e.jsx(ue,{className:"w-4 h-4 text-orange-500"})]})]},n.courseId))})]})]})}function Ge(){const[t,a]=h.useState(!0),[s,u]=h.useState([]),[d,v]=h.useState(!1),[x,c]=h.useState(null);h.useEffect(()=>{n()},[]);async function n(){try{a(!0);const p=await H.getAllSemesters();u(p)}catch(p){console.error("학기 목록 조회 실패:",p),f.error("학기 목록을 불러올 수 없습니다")}finally{a(!1)}}async function N(p){if(confirm(`이 학기를 삭제하시겠습니까?
2연관된 모든 강좌와 수강신청도 함께 삭제됩니다.`))try{await H.deleteSemester(p),f.success("삭제되었습니다"),n()}catch{f.error("삭제에 실패했습니다")}}async function w(p,g){try{await H.updateSemesterStatus(p,g),f.success("상태가 변경되었습니다"),n()}catch{f.error("상태 변경에 실패했습니다")}}return t?e.jsx("div",{className:"flex items-center justify-center h-64",children:e.jsx("div",{className:"text-gray-500 text-[13px]",children:"로딩 중..."})}):e.jsxs("div",{className:"space-y-4",children:[e.jsx("div",{className:"flex justify-end",children:e.jsxs("button",{onClick:()=>{c(null),v(!0)},className:"flex items-center gap-1.5 px-3 py-1.5 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors text-[13px] font-medium",children:[e.jsx(ie,{className:"w-4 h-4"}),"학기 추가"]})}),s.length===0?e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-8 text-center",children:[e.jsx(ee,{className:"w-12 h-12 text-gray-300 mx-auto mb-3"}),e.jsx("p",{className:"text-[13px] text-gray-500",children:"등록된 학기가 없습니다"})]}):e.jsx("div",{className:"space-y-3",children:s.map(p=>e.jsx("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-4",children:e.jsxs("div",{className:"flex items-start justify-between",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx("h3",{className:"text-[15px] font-bold text-gray-900",children:p.name}),e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] font-medium ${p.status==="planning"?"bg-gray-100 text-gray-700":p.status==="recruiting"?"bg-green-100 text-green-700":p.status==="active"?"bg-blue-100 text-blue-700":"bg-gray-100 text-gray-500"}`,children:re[p.status]})]}),e.jsxs("div",{className:"text-[12px] text-gray-600 space-y-1",children:[e.jsxs("p",{children:["기간: ",p.startDate," ~ ",p.endDate]}),e.jsxs("p",{children:["모집: ",p.registrationStartDate," ~ ",p.registrationEndDate]}),e.jsxs("p",{children:["담당: ",p.managerName," (",p.managerPhone,")"]}),e.jsxs("p",{children:["수강료: 월 ",p.monthlyFee.toLocaleStr
2ing(),"원 / 최소 인원: ",p.minEnrollment,"명"]}),p.instructorAccessCode&&e.jsxs("p",{className:"flex items-center gap-1",children:["강사 접근코드: ",e.jsx("code",{className:"bg-gray-100 px-1.5 py-0.5 rounded text-[11px] font-mono font-bold",children:p.instructorAccessCode})]})]})]}),e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsxs("select",{value:p.status,onChange:g=>w(p.id,g.target.value),className:"text-[11px] px-2 py-1 border border-gray-200 rounded",children:[e.jsx("option",{value:"planning",children:"준비중"}),e.jsx("option",{value:"recruiting",children:"모집중"}),e.jsx("option",{value:"active",children:"진행중"}),e.jsx("option",{value:"closed",children:"종료"})]}),e.jsxs("div",{className:"flex gap-1",children:[e.jsx("button",{onClick:()=>{c(p),v(!0)},className:"p-1.5 bg-blue-100 hover:bg-blue-200 text-blue-700 rounded",children:e.jsx(pe,{className:"w-3.5 h-3.5"})}),e.jsx("button",{onClick:()=>N(p.id),className:"p-1.5 bg-red-100 hover:bg-red-200 text-red-700 rounded",children:e.jsx($e,{className:"w-3.5 h-3.5"})})]})]})]})},p.id))}),d&&e.jsx(Xe,{semester:x,onClose:()=>{v(!1),c(null)},onSuccess:()=>{v(!1),c(null),n()}})]})}function He(t){const a=t.data;return a?e.jsxs("div",{className:"flex gap-1 items-center h-full",children:[e.jsx("button",{className:"px-2 py-1 text-[11px] bg-blue-500 text-white rounded hover:bg-blue-600",onClick:s=>{var u;s.stopPropagation(),(u=t.onEdit)==null||u.call(t,a)},title:"상세편집",children:"수정"}),e.jsx("button",{className:"px-2 py-1 text-[11px] bg-blue-600 text-white rounded hover:bg-blue-700",onClick:s=>{var u;s.stopPropagation(),(u=t.onCopyLink)==null||u.call(t,a.id)},title:"강사 입력 링크 복사",children:"링크"}),e.jsx("button",{className:"px-2 py-1 text-[11px] bg-red-500 text-white rounded hover:bg-red-600",onClick:s=>{var u;s.stopPropagation(),(u=t.onDelete)==null||u.call(t,a.id)},title:"삭제",children:"삭제"})]}):null}function Ve(){const[t,a]=h.useState(!0),[s,u]=h.useState([]),[d,v]=h.useState(""),[x,c]=h.useState([]),[n,N]=h.useState(!1),[w,p]=h.useState(null),[g,k]=h.useState(!1);h.useEffect(()=>{M()},[]),h.useEffect(()=>{d&&P()},[d]);async function M(){try{const l=await H.getAllSemesters();if(u(l),l.length>0){const m=l.find(b=>b.status==="recruiting"||b.status==="active");
2v((m==null?void 0:m.id)||l[0].id)}}catch(l){console.error("학기 목록 조회 실패:",l)}finally{a(!1)}}async function P(){try{const l=await $.getCoursesBySemester(d);c(l)}catch(l){console.error("강좌 목록 조회 실패:",l),f.error("강좌 목록을 불러올 수 없습니다")}}function F(l){const m=`https://culture.seodoon.org/instructor-form.html?course=${l}`;navigator.clipboard.writeText(m).then(()=>{f.success("강사 입력 링크가 복사되었습니다")}).catch(()=>{f.error("복사에 실패했습니다")})}async function R(l){if(confirm(`이 강좌를 삭제하시겠습니까?
3연관된 수강신청도 함께 삭제됩니다.`))try{await $.deleteCourse(l),f.success("삭제되었습니다"),P()}catch{f.error("삭제에 실패했습니다")}}async function _(){const l=x.filter(m=>m.status==="open");if(l.length===0){f("모집중인 강좌가 없습니다");return}if(confirm(`${l.length}개 강좌의 수강신청을 전체 마감하시겠습니까?`))try{await Promise.all(l.map(m=>$.updateCourseStatus(m.id,"closed"))),f.success(`${l.length}개 강좌 수강신청 마감 완료`),P()}catch(m){console.error("전체 마감 실패:",m),f.error("마감 처리에 실패했습니다")}}function W(){const l=s.find(E=>E.id===d);if(!l){f.error("학기를 선택하세요");return}const m=x.filter(E=>E.status!=="cancelled").sort((E,Q)=>(E.sortOrder||0)-(Q.sortOrder||0));if(m.length===0){f.error("인쇄할 강좌가 없습니다");return}const b=E=>E.fee!=null?ne(E.fee,E.feeType||"monthly"):ne(l.monthlyFee,"monthly"),C=E=>String(E??"").replace(/[&<>"]/g,Q=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"})[Q]),A=m.map((E,Q)=>{const Ee=C(E.scheduleText||(E.schedule?ae(E.schedule):""))||"-",ke=E.durationText?` <span style="color:#666;">(${C(E.durationText)})</span>`:"";return`
4      <tr class="course-row">
5        <td class="c" rowspan="2">□</td>
6        <td class="c" rowspan="2">${Q+1}</td>
7        <td class="cname"><b>${C(E.name)}</b></td>
8        <td>${C(E.instructor)}${E.instructorTitle?" "+C(E.instructorTitle):""}</td>
9        <td class="r">${C(b(E))}${ke}</td>
10      </tr>
11      <tr class="course-sub">
12        <td colspan="3"><b>시간</b> ${Ee} | <b>장소</b> ${C(E.location)||"-"}</td>
13      </tr>`}).join(""),D=`<!DOCTYPE html>
14<html lang="ko">
15<head>
16<meta charset="utf-8" />
17<title>서둔문화강좌 수강신청서 - ${C(l.name)}</title>
18<style>
19  * { box-sizing: border-box; }
20  body { font-family: 'Malgun Gothic', '맑은 고딕', sans-serif; color: #111; margin: 24px; font-size: 13px; }
21  h1 { text-align: center; font-size: 22px; margin: 0 0 4px; letter-spacing: 4px; }
22  .sub { text-align: center; color: #555; font-size: 13px; margin-bottom: 18px; }
23  table { width: 100%; border-collapse: collapse; margin-bottom: 16px; }
24  th, td { border: 1px solid #333; padding: 6px 8px; }
25  th { background: #f0f0f0; font-weight: 700; }
26  .c { text-align: center; }
27  .r { text-align: right; }
28  .applicant th { width: 80px; background: #f0f0f0; text-align: center; }
29  .applicant td { height: 38px; }
30  .applicant td.wide { width: 260px; }
31  .course-row td { border-top: 1.6px solid #333; }
32  .course-row .cname { font-size: 13px; }
33  .course-sub td { font-size: 12px; color: #333; }
34  .section-title { font-weight: 700; font-size: 14px; margin: 16px 0 6px; }
35  .notice { border: 1px solid #999; padding: 10px 12px; font-size: 12px; line-height: 1.7; color: #333; }
36  .notice b { color: #111; }
37  .sign { margin-top: 24px; text-align: right; font-size: 13px; line-height: 2.2; }
38  .sign .line { display: inline-block; border-bottom: 1px solid #333; width: 120px; }
39  @media print { body { margin: 12mm; } .no-print { display: none; } }
40</style>
41</head>
42<body>
43  <h1>수 강 신 청 서</h1>
44  <div class="sub">서둔문화강좌 · ${C(l.name)} (모집: ${C(l.registrationStartDate)} ~ ${C(l.registrationEndDate)})<br />주관: 서둔교회 문화체육선교부</div>
45
46  <div class="section-title">■ 신청자 정보</div>
47  <table class="applicant">
48    <tr><th>성명</th><td class="wide"></td><th>교인 여부</th><td>□ 교인  □ 비교인</td></tr>
49    <tr><th>연락처</th><td colspan="3"></td></tr>
50  </table>
51
52  <div class="section-title">■ 신청 강좌 (신청할 강좌의 □에 표시하세요 · 수강료는 강좌별로 다릅니다)</div>
53  <table>
54    <thead>
55      <tr>
56        <th class="c" style="width:36px;">신청</th>
57        <th class="c" style="width:34px;">No</th>
58        <th>강좌명</th>
59        <th style="width:130px;">강사</th>
60        <th class="r" style="width:130px;">수강료</th>
61      </tr>
62    </thead>
63    <tbody>${A}</tbody>
64  </table>
65
66  <div class="section-title">■ 납부 안내</div>
67  <div class="notice">
68    · 수강료는 <b>강좌별로 상이</b>하며, 위 표에 기재된 금액을 납부합니다.<br />
69    · 입금 계좌: <b>카카오뱅크 3333333111536</b> (예금주: 신효필 / 서둔문화강좌팀장)<br />
70    · 주관: 서둔교회 문화체육선교부 · 담당: ${C(l.managerName)} (${C(l.managerPhone)})
71  </div>
72
73  <div class="sign">
74    위와 같이 수강을 신청합니다.<br />
75    ${new Date().getFullYear()}년   월   일<br />
76    신청인 : <span class="line"></span> (서명 또는 인)
77  </div>
78
79  <div class="no-print" style="text-align:center; margin-top:20px;">
80    <button onclick="window.print()" style="padding:8px 20px; font-size:14px; cursor:pointer;">인쇄하기</button>
81  </div>
82</body>
83</html>`,I=window.open("","_blank","width=900,height=700");if(!I){f.error("팝업이 차단되었습니다. 팝업을 허용해주세요.");return}I.document.write(D),I.document.close()}const y=h.useCallback(async l=>{const m=l.data,b=l.column.colId;let C=l.newValue;(b==="sortOrder"||b==="maxEnrollment"||b==="fee")&&(C=C?Number(C):b==="sortOrder"?0:null);try{await $.updateCourse(m.id,{[b]:C}),f.success("저장되었습니다")}catch(A){console.error("저장 실패:",A),f.error("저장에 실패했습니다"),l.node.setDataValue(b,l.oldValue)}},[]),i=h.useCallback(l=>{p(l.data),N(!0)},[]),j=h.useMemo(()=>[{headerName:"순서",field:"sortOrder",width:70,editable:!0,cellDataType:"number"},{headerName:"카테고리",field:"category",width:90,editable:!0,cellEditor:"agSelectCellEditor",cellEditorParams:{values:["music","literature","craft","other"]},valueFormatter:l=>se[l.value]||l.value,cellStyle:l=>{const m={music:{bg:"#f3e8ff",color:"#7c3aed"},literature:{bg:"#dbeafe",color:"#2563eb"},craft:{bg:"#ffedd5",color:"#ea580c"},other:{bg:"#f3f4f6",color:"#4b5563"}},b=m[l.value]||m.other;return{backgroundColor:b.bg,color:b.color,fontWeight:500}}},{headerName:"강좌명",field:"name",width:150,editable:!0,cellStyle:{fontWeight:"bold"}},{headerName:"강사",field:"instructor",width:100,editable:!0},{headerName:"직책",field:"instructorTitle",width:80,editable:!0},{headerName:"강의시간",field:"scheduleText",width:160,editable:!0,valueGetter:l=>{const m=l.data;return(m==null?void 0:m.scheduleText)||(m!=null&&m.schedule?ae(m.schedule):"")}},{headerName:"장소",field:"location",width:120,editable:!0},{headerName:"정원",field:"maxEnrollment",width:70,editable:!0,cellDataType:"number",valueFormatter:l=>l.value?`${l.value}명`:"-"},{headerName:"수강료",field:"fee",width:110,editable:!0,cellDataType:"number",valueFormatter:l=>{if(l.value==null)return"학기 기본";const m=l.data;return ne(Number(l.value),(m==null?void 0:m.feeType)||"monthly")}},{headerName:"납부주기",field:"feeType",width:90,editable:!0,cellEditor:"agSelectCellEditor",cellEditorParams:{values:["once","monthly","semester"]},valueFormatter:l=>Ce[l.value||"monthly"]},{headerName:"기간/횟수",field:"durationText",width:110,editable:!0,valueFormatter:l=>l.value||"-"},{headerName:"현재인원",field:"currentEnrollment",width:80,editable:!1,valueFormatter:l=>`${l.value||0}명`},{headerName:"상태",field:"status",width:90,editable:!0,cellEditor:"agSelectCellEditor",cellEditorParams:{values:["draft","open","closed","cancelled"]},valueFormatter:l=>ze[l.value]||l.value,cellStyle:l=>{const m={draft:{bg:"#f3f4f6",color:"#4b5563"},open:{bg:"#dcfce7",color:"#16a34a"},closed:{bg:"#dbeafe",color:"#2563eb"},cancelled:{bg:"#fee2e2",color:"#dc2626"}},b=m[l.value]||m.draft;return{backgroundColor:b.bg,color:b.color,fontWeight:500}}},{headerName:"홍보문구",field:"tagline",width:150,editable:!0},{headerName:"대상",field:"targetAudience",width:100,editable:!0},{headerName:"준비물",field:"suppliesNeeded",width:100,editable:!0},{headerName:"액션",field:"id",width:160,editable:!1,sortable:!1,filter:!1,cellRenderer:He,cellRendererParams:{onEdit:l=>{p(l),N(!0)},onCopyLink:F,onDelete:R}}],[]),o=h.useMemo(()=>
83({sortable:!0,filter:!0,resizable:!0,minWidth:60,editable:!1,cellStyle:{display:"flex",alignItems:"center",fontSize:"12px",lineHeight:"normal"}}),[]);if(t)return e.jsx("div",{className:"flex items-center justify-center h-64",children:e.jsx("div",{className:"text-gray-500 text-[13px]",children:"로딩 중..."})});if(s.length===0)return e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-8 text-center",children:[e.jsx(ee,{className:"w-12 h-12 text-gray-300 mx-auto mb-3"}),e.jsx("p",{className:"text-[13px] text-gray-500",children:"먼저 학기를 추가하세요"})]});const r=s.find(l=>l.id===d);return e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("select",{value:d,onChange:l=>v(l.target.value),className:"text-[13px] px-3 py-1.5 border border-gray-200 rounded",children:s.map(l=>e.jsxs("option",{value:l.id,children:[l.name," (",re[l.status],")"]},l.id))}),e.jsxs("span",{className:"text-[12px] text-gray-500",children:[x.length,"개 강좌"]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{onClick:W,className:"flex items-center gap-1.5 px-3 py-1.5 border border-gray-300 text-gray-700 rounded hover:bg-gray-50 transition-colors text-[13px] font-medium",children:[e.jsx(ge,{className:"w-4 h-4"}),"종이 신청서 인쇄"]}),e.jsxs("button",{onClick:_,className:"flex items-center gap-1.5 px-3 py-1.5 bg-red-500 text-white rounded hover:bg-red-600 transition-colors text-[13px] font-medium",children:[e.jsx(Ie,{className:"w-4 h-4"}),"전체 수강신청 마감"]}),s.length>=2&&e.jsxs("button",{onClick:()=>k(!0),className:"flex items-center gap-1.5 px-3 py-1.5 border border-blue-300 text-blue-700 rounded hover:bg-blue-50 transition-colors text-[13px] font-medium",children:[e.jsx(fe,{className:"w-4 h-4"}),"이전 학기에서 복사"]}),e.jsxs("button",{onClick:()=>{p(null),N(!0)},className:"flex items-center gap-1.5 px-3 py-1.5 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors text-[13px] font-medium",children:[e.jsx(ie,{className:"w-4 h-4"}),"강좌 추가"]})]})]}),e.jsxs("div",{className:"ag-theme-alpine",style:{height:Math.max(300,Math.min(x.length*42+50,600)),width:"100%"},children:[e.jsx("style",{children:`
84          .ag-theme-alpine .ag-cell {
85            font-size: 12px !important;
86          }
87          .ag-theme-alpine .ag-header-cell-label {
88            font-size: 12px !important;
89          }
90        `}),e.jsx(De,{rowData:x,columnDefs:j,defaultColDef:o,onCellValueChanged:y,onRowDoubleClicked:i,undoRedoCellEditing:!0,undoRedoCellEditingLimit:20,animateRows:!0,rowHeight:40,enableCellTextSelection:!0,ensureDomOrder:!0,getRowId:l=>l.data.id,noRowsOverlayComponent:()=>e.jsxs("div",{className:"flex flex-col items-center justify-center py-8",children:[e.jsx(Z,{className:"w-12 h-12 text-gray-300 mb-3"}),e.jsx("p",{className:"text-[13px] text-gray-500",children:"등록된 강좌가 없습니다"})]})})]}),n&&r&&e.jsx(Je,{course:w,semesterId:d,onClose:()=>{N(!1),p(null)},onSuccess:()=>{N(!1),p(null),P()}}),g&&e.jsx(Ke,{semesters:s,targetSemesterId:d,onClose:()=>k(!1),onSuccess:()=>{k(!1),P()}})]})}function Ke({semesters:t,targetSemesterId:a,onClose:s,onSuccess:u}){const[d,v]=h.useState(!1),[x,c]=h.useState(""),[n,N]=h.useState([]),[w,p]=h.useState(new Set),[g,k]=h.useState(!1),M=t.filter(y=>y.id!==a),P=t.find(y=>y.id===a);h.useEffect(()=>{x&&F()},[x]);async function F(){try{k(!0);const y=await $.getCoursesBySemester(x);N(y),p(new Set(y.map(i=>i.id)))}catch{f.error("강좌 목록을 불러올 수 없습니다")}finally{k(!1)}}function R(y){p(i=>{const j=new Set(i);return j.has(y)?j.delete(y):j.add(y),j})}function _(){w.size===n.length?p(new Set):p(new Set(n.map(y=>y.id)))}async function W(){if(w.size===0){f.error("복사할 강좌를 선택하세요");return}if(confirm(`선택한 ${w.size}개 강좌를 복사하시겠습니까?
91강좌 기본 정보가 복사되고, 수강 인원은 0으로 초기화됩니다.`))try{v(!0);const y=await $.copyCoursesBetweenSemesters(x,a,Array.from(w));f.success(`${y}개 강좌가 복사되었습니다`),u()}catch{f.error("복사에 실패했습니다")}finally{v(!1)}}return e.jsx("div",{className:"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4",children:e.jsxs("div",{className:"bg-white rounded-lg shadow-xl max-w-lg w-full max-h-[80vh] overflow-y-auto",children:[e.jsxs("div",{className:"p-4 border-b border-gray-200",children:[e.jsxs("h2",{className:"text-[16px] font-bold text-gray-900 flex items-center gap-2",children:[e.jsx(fe,{className:"w-5 h-5 text-blue-600"}),"이전 학기에서 강좌 복사"]}),e.jsxs("p",{className:"text-[12px] text-gray-500 mt-1",children:["복사 대상: ",e.jsx("strong",{children:P==null?void 0:P.name})]})]}),e.jsxs("div",{className:"p-4 space-y-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"복사할 학기 선택"}),e.jsxs("select",{value:x,onChange:y=>c(y.target.value),className:"compact-input",children:[e.jsx("option",{value:"",children:"학기를 선택하세요"}),M.map(y=>
91e.jsxs("option",{value:y.id,children:[y.name," (",re[y.status],")"]},y.id))]})]}),g&&e.jsx("div",{className:"text-center py-4 text-[13px] text-gray-500",children:"불러오는 중..."}),x&&!g&&n.length===0&&e.jsx("div",{className:"text-center py-4 text-[13px] text-gray-500",children:"해당 학기에 강좌가 없습니다"}),n.length>0&&e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700",children:["복사할 강좌 (",w.size,"/",n.length,")"]}),e.jsx("button",{type:"button",onClick:_,className:"text-[11px] text-blue-600 hover:underline",children:w.size===n.length?"전체 해제":"전체 선택"})]}),e.jsx("div",{className:"space-y-1 max-h-60 overflow-y-auto border border-gray-200 rounded p-2",children:n.map(y=>e.jsxs("label",{className:`flex items-center gap-2 p-2 rounded cursor-pointer transition-colors ${w.has(y.id)?"bg-blue-50":"hover:bg-gray-50"}`,children:[e.jsx("input",{type:"checkbox",checked:w.has(y.id),onChange:()=>R(y.id),className:"rounded border-gray-300 text-blue-600 focus:ring-blue-500"}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:`px-1.5 py-0.5 rounded text-[9px] font-medium ${y.category==="music"?"bg-slate-100 text-slate-700":y.category==="literature"?"bg-blue-100 text-blue-700":y.category==="craft"?"bg-orange-100 text-orange-700":"bg-gray-100 text-gray-700"}`,children:se[y.category]}),e.jsx("span",{className:"text-[13px] font-medium text-gray-900 truncate",children:y.name})]}),e.jsxs("div",{className:"text-[11px] text-gray-500 mt-0.5",children:[y.instructor,y.instructorTitle?` (${y.instructorTitle})`:""," · ",ae(y.schedule)]})]})]},y.id))}),e.jsx("p",{className:"text-[10px] text-gray-500 mt-2",children:'* 강좌명, 강사, 시간, 장소 등 기본 정보가 복사됩니다. 수강 인원은 0으로, 상태는 "준비중"으로 설정됩니다.'})]})]}),e.jsxs("div",{className:"p-4 border-t border-gray-200 flex gap-2",children:[e.jsx("button",{type:"button",onClick:s,className:"flex-1 px-3 py-2 border border-gray-300 rounded hover:bg-gray-50 transition-colors text-[13px]",children:"취소"}),e.jsx("button",{onClick:W,disabled:d||w.size===0,className:"flex-1 px-3 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors text-[13px] font-medium disabled:opacity-50",children:d?"복사 중...":`${w.size}개 강좌 복사`})]})]})})}function Qe(){const[t,a]=h.useState(!0),[s,u]=h.useState([]),[d,v]=h.useState(""),[x,c]=h.useState([]),[n,N]=h.useState("all"),[w,p]=h.useState([]),[g,k]=h.useState("all"),[M,P]=h.useState("all");h.useEffect(()=>{F()},[]),h.useEffect(()=>{d&&R()},[d]);async function F(){try{const r=await H.getAllSemesters();if(u(r),r.length>0){const l=r.find(m=>m.status==="recruiting"||m.status==="active");
91v((l==null?void 0:l.id)||r[0].id)}}catch(r){console.error("학기 목록 조회 실패:",r)}finally{a(!1)}}async function R(){try{const[r,l]=await Promise.all([$.getCoursesBySemester(d),J.getEnrollmentsBySemester(d)]);c(r),p(l)}catch(r){console.error("데이터 로딩 실패:",r),f.error("데이터를 불러올 수 없습니다")}}async function _(r){const l=prompt(`납부 방법을 선택하세요:
921. 현금
932. 계좌이체
943. 카드`,"2");if(!l)return;const b={1:"cash",2:"transfer",3:"card"}[l]||"transfer",C=s.find(I=>I.id===d),A=x.find(I=>I.id===r.courseId),D=(A==null?void 0:A.fee)??(C==null?void 0:C.monthlyFee)??3e4;try{await J.confirmPayment(r.id,{paymentMethod:b,paymentAmount:D}),f.success("납부가 확인되었습니다"),R()}catch{f.error("납부 확인에 실패했습니다")}}async function W(r){try{await J.confirmEnrollment(r,"admin"),f.success("수강이 확정되었습니다"),R()}catch{f.error("확정에 실패했습니다")}}async function y(r){const l=prompt("취소 사유를 입력하세요:");if(l!==null)try{await J.cancelEnrollment(r,l),f.success("수강이 취소되었습니다"),R()}catch{f.error("취소에 실패했습니다")}}function i(){const r=j();if(r.length===0){f.error("내보낼 데이터가 없습니다");return}const l=["강좌","이름","연락처","교인여부","신청일","납부상태","신청상태"],m=r.map(D=>[D.courseName,D.applicantName,D.applicantPhone,D.isChurchMember?"O":"X",D.enrolledAt?new Date(D.enrolledAt).toLocaleDateString("ko-KR"):"",me[D.paymentStatus],he[D.status]]),b=[l,...m].map(D=>D.map(I=>`"${I}"`).join(",")).join(`
95`),C=new Blob(["\uFEFF"+b],{type:"text/csv;charset=utf-8;"}),A=document.createElement("a");A.href=URL.createObjectURL(C),A.download=`수강신청_${new Date().toISOString().split("T")[0]}.csv`,A.click(),f.success("엑셀 파일이 다운로드되었습니다")}function j(){return w.filter(r=>!(n!=="all"&&r.courseId!==n||g!=="all"&&r.paymentStatus!==g||M!=="all"&&r.status!==M))}if(t)return e.jsx("div",{className:"flex items-center justify-center h-64",children:e.jsx("div",{className:"text-gray-500 text-[13px]",children:"로딩 중..."})});if(s.length===0)return e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-8 text-center",children:[e.jsx(ee,{className:"w-12 h-12 text-gray-300 mx-auto mb-3"}),e.jsx("p",{className:"text-[13px] text-gray-500",children:"먼저 학기를 추가하세요"})]});const o=j();return e.jsxs("div",{className:"space-y-4",children:[e.jsx("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-3",children:e.jsxs("div",{className:"grid grid-cols-5 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[11px] font-medium text-gray-600 mb-1",children:"학기"}),e.jsx("select",{value:d,onChange:r=>v(r.target.value),className:"compact-input",children:s.map(r=>e.jsx("option",{value:r.id,children:r.name},r.id))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[11px] font-medium text-gray-600 mb-1",children:"강좌"}),e.jsxs("select",{value:n,onChange:r=>N(r.target.value),className:"compact-input",children:[e.jsx("option",{value:"all",children:"전체 강좌"}),x.map(r=>e.jsx("option",{value:r.id,children:r.name},r.id))]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[11px] font-medium text-gray-600 mb-1",children:"납부 상태"}),e.jsxs("select",{value:g,onChange:r=>k(r.target.value),className:"compact-input",children:[e.jsx("option",{value:"all",children:"전체"}),e.jsx("option",{value:"pending",children:"미납"}),e.jsx("option",{value:"paid",children:"납부완료"}),e.jsx("option",{value:"refunded",children:"환불"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[11px] font-medium text-gray-600 mb-1",children:"신청 상태"}),e.jsxs("select",{value:M,onChange:r=>P(r.target.value),className:"compact-input",children:[e.jsx("option",{value:"all",children:"전체"}),e.jsx("option",{value:"pending",children:"대기"}),e.jsx("option",{value:"confirmed",children:"확정"}),e.jsx("option",{value:"cancelled",children:"취소"})]})]}),e.jsx("div",{className:"flex items-end",children:e.jsxs("button",{onClick:i,className:"flex items-center gap-1.5 px-3 py-1.5 bg-green-600 text-white rounded hover:bg-green-700 transition-colors text-[12px] font-medium w-full justify-center",children:[e.jsx(ge,{className:"w-3.5 h-3.5"}),"엑셀 내보내기"]})})]})}),e.jsxs("div",{className:"grid grid-cols-4 gap-3",children:[e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-3",children:[e.jsx("div",{className:"text-[20px] font-bold text-gray-900",children:w.length}),e.jsx("div",{className:"text-[11px] text-gray-500",children:"전체 신청"})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-3",children:[e.jsx("div",{className:"text-[20px] font-bold text-blue-600",children:w.filter(r=>r.status==="confirmed").length}),e.jsx("div",{className:"text-[11px] text-gray-500",children:"확정"})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-3",children:[e.jsx("div",{className:"text-[20px] font-bold text-blue-600",children:w.filter(r=>r.paymentStatus==="paid").length}),e.jsx("div",{className:"text-[11px] text-gray-500",children:"납부완료"})]}),e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-3",children:[e.jsx("div",{className:"text-[20px] font-bold text-orange-600",children:w.filter(r=>r.paymentStatus==="pending").length}),e.jsx("div",{className:"text-[11px] text-gray-500",children:"미납"})]})]}),o.length===0?e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-8 text-center",children:[e.jsx(le,{className:"w-12 h-12 text-gray-300 mx-auto mb-3"}),e.jsx("p",{className:"text-[13px] text-gray-500",children:"수강신청 내역이 없습니다"})]}):e.jsx("div",{className:"bg-white border border-gray-200 rounded shadow-sm overflow-hidden",children:e.jsxs("table",{className:"w-full",children:[e.jsx("thead",{className:"bg-gray-50",children:e.jsxs("tr",{children:[e.jsx("th",{className:"px-3 py-2 text-left text-[11px] font-medium text-gray-600",children:"강좌"}),e.jsx("th",{className:"px-3 py-2 text-left text-[11px] font-medium text-gray-600",children:"이름"}),e.jsx("th",{className:"px-3 py-2 text-left text-[11px] font-medium text-gray-600",children:"연락처"}),e.jsx("th",{className:"px-3 py-2 text-center text-[11px] font-medium text-gray-600",children:"교인"}),e.jsx("th",{className:"px-3 py-2 text-center text-[11px] font-medium text-gray-600",children:"납부"}),e.jsx("th",{className:"px-3 py-2 text-center text-[11px] font-medium text-gray-600",children:"상태"}),e.jsx("th",{className:"px-3 py-2 text-center text-[11px] font-medium text-gray-600",children:"액션"})]})}),e.jsx("tbody",{className:"divide-y divide-gray-100",children:o.map(r=>e.jsxs("tr",{className:"hover:bg-gray-50",children:[e.jsx("td",{className:"px-3 py-2 text-[12px] text-gray-900",children:r.courseName}),e.jsx("td",{className:"px-3 py-2 text-[12px] font-medium text-gray-900",children:r.applicantName}),e.jsx("td",{className:"px-3 py-2 text-[12px] text-gray-600",children:r.applicantPhone}),e.jsx("td",{className:"px-3 py-2 text-center",children:r.isChurch
95Member?e.jsx(ce,{className:"w-4 h-4 text-green-500 mx-auto"}):e.jsx(te,{className:"w-4 h-4 text-gray-300 mx-auto"})}),e.jsx("td",{className:"px-3 py-2 text-center",children:e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] font-medium ${r.paymentStatus==="pending"?"bg-orange-100 text-orange-700":r.paymentStatus==="paid"?"bg-green-100 text-green-700":"bg-gray-100 text-gray-700"}`,children:me[r.paymentStatus]})}),e.jsx("td",{className:"px-3 py-2 text-center",children:e.jsx("span",{className:`px-2 py-0.5 rounded text-[10px] font-medium ${r.status==="pending"?"bg-yellow-100 text-yellow-700":r.status==="confirmed"?"bg-blue-100 text-blue-700":r.status==="cancelled"?"bg-red-100 text-red-700":"bg-gray-100 text-gray-700"}`,children:he[r.status]})}),e.jsx("td",{className:"px-3 py-2",children:e.jsxs("div",{className:"flex items-center justify-center gap-1",children:[r.paymentStatus==="pending"&&e.jsx("button",{onClick:()=>_(r),className:"px-2 py-1 bg-green-100 hover:bg-green-200 text-green-700 rounded text-[10px]",title:"납부 확인",children:"납부"}),r.status==="pending"&&e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>W(r.id),className:"px-2 py-1 bg-blue-100 hover:bg-blue-200 text-blue-700 rounded text-[10px]",title:"수강 확정",children:"확정"}),e.jsx("button",{onClick:()=>y(r.id),className:"px-2 py-1 bg-red-100 hover:bg-red-200 text-red-700 rounded text-[10px]",title:"수강 취소",children:"취소"})]})]})})]},r.id))})]})})]})}function X({label:t,value:a,icon:s,color:u}){const d={green:"text-green-600",blue:"text-blue-600",purple:"text-blue-600",orange:"text-orange-600"}[u||"gray"]||"text-gray-900";return e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-3",children:[e.jsxs("div",{className:`text-[22px] font-bold flex items-center gap-2 ${d}`,children:[a,s]}),e.jsx("div",{className:"text-[11px] text-gray-500",children:t})]})}function Xe({semester:t,onClose:a,onSuccess:s}){const u=!!t,[d,v]=h.useState(!1),x=new Date().getFullYear(),c=new Date().getMonth()+1,[n,N]=h.useState({year:(t==null?void 0:t.year)||x,semester:(t==null?void 0:t.semester)||(c<=6?"first":"second"),startDate:(t==null?void 0:t.startDate)||"",endDate:(t==null?void 0:t.endDate)||"",registrationStartDate:(t==null?void 0:t.registrationStartDate)||"",registrationEndDate:(t==null?void 0:t.registrationEndDate)||"",status:(t==null?void 0:t.status)||"planning",minEnrollment:(t==null?void 0:t.minEnrollment)||5,monthlyFee:(t==null?void 0:t.monthlyFee)||3e4,managerName:(t==null?void 0:t.managerName)||"",managerPhone:(t==null?void 0:t.managerPhone)||"",managerPosition:(t==null?void 0:t.managerPosition)||"",orientationDate:(t==null?void 0:t.orientationDate)||"",openingDate:(t==null?void 0:t.openingDate)||"",description:(t==null?void 0:t.description)||"",instructorAccessCode:(t==null?void 0:t.instructorAccessCode)||""}),w=We(n.year,n.semester);async function p(g){if(g.preventDefault(),!n.startDate||!n.endDate||!n.managerName||!n.managerPhone){f.error("필수 항목을 입력하세요");return}try{v(!0);const k={...n,name:w,createdBy:"admin"};
95u?(await H.updateSemester(t.id,k),f.success("학기가 수정되었습니다")):(await H.createSemester(k),f.success("학기가 추가되었습니다")),s()}catch{f.error(u?"수정에 실패했습니다":"추가에 실패했습니다")}finally{v(!1)}}return e.jsx("div",{className:"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4",children:e.jsxs("div",{className:"bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto",children:[e.jsx("div",{className:"p-4 border-b border-gray-200",children:e.jsx("h2",{className:"text-[16px] font-bold text-gray-900",children:u?"학기 수정":"학기 추가"})}),e.jsxs("form",{onSubmit:p,className:"p-4 space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["연도 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("select",{value:n.year,onChange:g=>N({...n,year:parseInt(g.target.value)}),className:"compact-input",children:[x-1,x,x+1].map(g=>e.jsxs("option",{value:g,children:[g,"년"]},g))})]}),e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["학기 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsxs("select",{value:n.semester,onChange:g=>N({...n,semester:g.target.value}),className:"compact-input",children:[e.jsx("option",{value:"first",children:"상반기"}),e.jsx("option",{value:"second",children:"하반기"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"학기명"}),e.jsx("input",{type:"text",value:w,disabled:!0,className:"compact-input bg-gray-50"})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["시작일 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"date",value:n.startDate,onChange:g=>N({...n,startDate:g.target.value}),className:"compact-input",required:!0})]}),e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["종료일 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"date",value:n.endDate,onChange:g=>N({...n,endDate:g.target.value}),className:"compact-input",required:!0})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"모집 시작일"}),e.jsx("input",{type:"date",value:n.registrationStartDate,onChange:g=>N({...n,registrationStartDate:g.target.value}),className:"compact-input"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"모집 종료일"}),e.jsx("input",{type:"date",value:n.registrationEndDate,onChange:g=>N({...n,registrationEndDate:g.target.value}),className:"compact-input"})]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["담당자 이름 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"text",value:n.managerName,onChange:g=>N({...n,managerName:g.target.value}),className:"compact-input",placeholder:"홍길동",required:!0})]}),e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["담당자 연락처 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"tel",value:n.managerPhone,onChange:g=>N({...n,managerPhone:g.target.value}),className:"compact-input",placeholder:"010-1234-5678",required:!0})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"담당자 직분"}),e.jsx("input",{type:"text",value:n.managerPosition,onChange:g=>N({...n,managerPosition:g.target.value}),className:"compact-input",placeholder:"권사"})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"기본 월 수강료"}),e.jsx("input",{type:"number",value:n.monthlyFee,onChange:g=>N({...n,monthlyFee:parseInt(g.target.value)}),className:"compact-input",min:"0",step:"1000"}),e.jsx("p",{className:"text-[11px] text-gray-400 mt-1",children:"강좌에 수강료 미입력 시 적용되는 기본값"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"최소 개설 인원"}),e.jsx("input",{type:"number",value:n.minEnrollment,onChange:g=>N({...n,minEnrollment:parseInt(g.target.value)}),className:"compact-input",min:"1"})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"오리엔테이션 날짜"}),e.jsx("input",{type:"date",value:n.orientationDate,onChange:g=>N({...n,orientationDate:g.target.value}),className:"compact-input"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"개강일"}),e.jsx("input",{type:"date",value:n.openingDate,onChange:g=>N({...n,openingDate:g.target.value}),className:"compact-input"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"강사 정보 입력 접근코드"}),e.jsx("input",{type:"text",value:n.instructorAccessCode,onChange:g=>N({...n,instructorAccessCode:g.target.value}),className:"compact-input",placeholder:"강사들이 정보 입력 시 사용할 코드"}),e.jsx("p",{className:"text-[10px] text-gray-500 mt-0.5",children:"강사 입력 페이지(culture.seodoon.org/instructor-form.html)에서 사용"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"안내 문구"}),e.jsx("textarea",{value:n.description,onChange:g=>N({...n,description:g.target.value}),className:"compact-input",rows:2,placeholder:"학기에 대한 추가 안내 문구"})]}),e.jsxs("div",{className:"flex gap-2 pt-2",children:[e.jsx("button",{type:"button",onClick:a,className:"flex-1 px-3 py-2 border border-gray-300 rounded hover:bg-gray-50 transition-colors text-[13px]",children:"취소"}),e.jsx("button",{type:"submit",disabled:d,className:"flex-1 px-3 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors text-[13px] font-medium disabled:opacity-50",children:d?"처리 중...":u?"수정":"추가"})]})]})]})})}function Je({course:t,semesterId:a,onClose:s,onSuccess:u}){const d=!!t,[v,x]=h.useState(!1),[c,n]=h.useState({category:(t==null?void 0:t.category)||"music",name:(t==null?void 0:t.name)||"",description:(t==null?void 0:t.description)||"",instructor:(t==null?void 0:t.instructor)||"",instructorTitle:(t==null?void 0:t.instructorTitle)||"",instructorPhone:(t==null?void 0:t.instructorPhone)||"",instructorProfile:(t==null?void 0:t.instructorProfile)||{photoUrl:"",bio:"",careers:[]},schedule:(t==null?void 0:t.schedule)||{dayOfWeek:[],startTime:"10:00",endTime:"11:00",note:""},scheduleText:(t==null?void 0:t.scheduleText)||"",durationText:(t==null?void 0:t.durationText)||"",weeklyOverrides:(t==null?void 0:t.weeklyOverrides)||[],location:(t==null?void 0:t.location)||"",maxEnrollment:(t==null?void 0:t.maxEnrollment)||void 0,fee:(t==null?void 0:t.fee)??void 0,feeType:(t==null?void 0:t.feeType)||"monthly",status:(t==null?void 0:t.status)||"draft",sortOrder:(t==null?void 0:t.sortOrder)||1,imageUrl:(t==null?void 0:t.imageUrl)||"",tagline:(t==null?void 0:t.tagline)||"",targetAudience:(t==null?void 0:t.targetAudience)||"",suppliesNeeded:(t==null?void 0:t.suppliesNeeded)||"",curriculum:(t==null?void 0:t.curriculum)||[]}),[N,w]=h.useState(!1),[p,g]=h.useState(!1),k=h.useCallback(async(i,j)=>{var l;const o=(l=i.target.files)==null?void 0:l[0];if(!o)return;if(o.size>5*1024*1024){f.error("5MB 이하만 가능합니다");return}const r=j==="course"?w:g;try{r(!0);const m=Date.now(),b=o.name.split(".").pop()||"jpg",C=j==="course"?`culture/courses/${m}.${b}
95`:`culture/instructors/${m}.${b}`,A=Ne(Se,C);await ve(A,o,{contentType:o.type});const D=await we(A);n(j==="course"?I=>({...I,imageUrl:D}):I=>({...I,instructorProfile:{...I.instructorProfile,photoUrl:D}})),f.success("이미지가 업로드되었습니다")}catch{f.error("업로드에 실패했습니다")}finally{r(!1)}},[]);function M(){const i=c.curriculum.length+1;n({...c,curriculum:[...c.curriculum,{week:i,title:"",description:""}]})}function P(i){const j=c.curriculum.filter((o,r)=>r!==i).map((o,r)=>({...o,week:r+1}));n({...c,curriculum:j})}function F(i,j,o){const r=[...c.curriculum];r[i]={...r[i],[j]:o},n({...c,curriculum:r})}function R(){n({...c,instructorProfile:{...c.instructorProfile,careers:[...c.instructorProfile.careers||[],""]}})}function _(i){const j=(c.instructorProfile.careers||[]).filter((o,r)=>r!==i);n({...c,instructorProfile:{...c.instructorProfile,careers:j}})}function W(i,j){const o=[...c.instructorProfile.careers||[]];o[i]=j,n({...c,instructorProfile:{...c.instructorProfile,careers:o}})}async function y(i){var j;if(i.preventDefault(),!c.name||!c.instructor||!((j=c.scheduleText)!=null&&j.trim())){f.error("필수 항목을 입력하세요");return}try{x(!0);const o={...c,semesterId:a,createdBy:"admin"};d?(await $.updateCourse(t.id,o),f.success("강좌가 수정되었습니다")):(await $.createCourse(o),f.success("강좌가 추가되었습니다")),u()}catch{f.error(d?"수정에 실패했습니다":"추가에 실패했습니다")}finally{x(!1)}}return e.jsx("div",{className:"fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4",children:e.jsxs("div",{className:"bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto",children:[e.jsx("div",{className:"p-4 border-b border-gray-200",children:e.jsx("h2",{className:"text-[16px] font-bold text-gray-900",children:d?"강좌 수정":"강좌 추가"})}),e.jsxs("form",{onSubmit:y,className:"p-4 space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["카테고리 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("select",{value:c.category,onChange:i=>n({...c,category:i.target.value}),className:"compact-input",children:Object.entries(se).map(([i,j])=>e.jsx("option",{value:i,children:j},i))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"정렬 순서"}),e.jsx("input",{type:"number",value:c.sortOrder,onChange:i=>n({...c,sortOrder:parseInt(i.target.value)}),className:"compact-input",min:"1"})]})]}),e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["강좌명 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"text",value:c.name,onChange:i=>n({...c,name:i.target.value}),className:"compact-input",placeholder:"예: 발성교실",required:!0}),!d&&e.jsx("div",{className:"flex flex-wrap gap-1 mt-1",children:_e.filter(i=>i.category===c.category).map(i=>e.jsx("button",{type:"button",onClick:()=>n({...c,name:i.name}),className:"px-2 py-0.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded text-[10px]",children:i.name},i.name))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"설명"}),e.jsx("textarea",{value:c.description,onChange:i=>n({...c,description:i.target.value}),className:"compact-input",rows:2,placeholder:"강좌에 대한 간단한 설명"})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-3",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["강사 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("input",{type:"text",value:c.instructor,onChange:i=>n({...c,instructor:i.target.value}),className:"compact-input",placeholder:"홍길동",required:!0})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"강사 직책"}),e.jsx("input",{type:"text",value:c.instructorTitle,onChange:i=>n({...c,instructorTitle:i.target.value}),className:"compact-input",placeholder:"권사"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"강사 연락처"}),e.jsx("input",{type:"tel",value:c.instructorPhone,onChange:i=>n({...c,instructorPhone:i.target.value}),className:"compact-input",placeholder:"010-1234-5678"})]})]}),e.jsxs("div",{className:"space-y-3 p-3 bg-blue-50 rounded",children:[e.jsx("h3",{className:"text-[13px] font-bold text-gray-900",children:"강사 프로필 (홍보 사이트용)"}),e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("div",{className:"flex-shrink-0",children:c.instructorProfile.photoUrl?e.jsx("img",{src:c.instructorProfile.photoUrl,alt:"강사 사진",className:"w-16 h-16 rounded-full object-cover border-2 border-white shadow"}):e.jsx("div",{className:"w-16 h-16 rounded-full bg-gray-200 flex items-center justify-center",children:e.jsx(be,{className:"w-6 h-6 text-gray-400"})})}),e.jsx("div",{className:"flex-1",children:e.jsxs("label",{className:"flex items-center gap-1.5 px-3 py-1.5 bg-white border border-gray-200 rounded hover:bg-gray-50 cursor-pointer w-fit text-[12px]",children:[e.jsx(ye,{className:"w-3.5 h-3.5 text-gray-500"}
95),p?"업로드 중...":"강사 사진 업로드",e.jsx("input",{type:"file",accept:"image/*",onChange:i=>k(i,"instructor"),className:"hidden",disabled:p})]})})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"강사 소개"}),e.jsx("textarea",{value:c.instructorProfile.bio||"",onChange:i=>n({...c,instructorProfile:{...c.instructorProfile,bio:i.target.value}}),className:"compact-input",rows:2,placeholder:"강사 소개 (경력, 전문분야 등)"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"경력/자격"}),(c.instructorProfile.careers||[]).map((i,j)=>e.jsxs("div",{className:"flex gap-1 mb-1",children:[e.jsx("input",{type:"text",value:i,onChange:o=>W(j,o.target.value),className:"compact-input flex-1",placeholder:`경력 ${j+1}`}),e.jsx("button",{type:"button",onClick:()=>_(j),className:"p-1 text-red-500 hover:bg-red-50 rounded",children:e.jsx(te,{className:"w-3.5 h-3.5"})})]},j)),e.jsx("button",{type:"button",onClick:R,className:"text-[11px] text-blue-600 hover:underline mt-1",children:"+ 경력 추가"})]})]}),e.jsxs("div",{className:"space-y-3 p-3 bg-gray-50 rounded",children:[e.jsx("h3",{className:"text-[13px] font-bold text-gray-900",children:"수업 시간"}),e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["강의시간 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("textarea",{value:c.scheduleText,onChange:i=>n({...c,scheduleText:i.target.value}),className:"compact-input",rows:3,placeholder:`예:
96매주 토요일 오후 2:00~3:00
973,4주차 해당 / 1,2주차는 비전센터2층`,required:!0}),e.jsx("p",{className:"text-[11px] text-gray-400 mt-1",children:"홈페이지 수강안내에 그대로 표시됩니다. 줄바꿈 가능."})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"수업 기간/횟수"}),e.jsx("input",{type:"text",value:c.durationText,onChange:i=>n({...c,durationText:i.target.value}),className:"compact-input",placeholder:"예: 16주 / 학기당 8회 (비워두면 미표시)"}),e.jsx("p",{className:"text-[11px] text-gray-400 mt-1",children:"강좌별로 수업 기간·횟수가 다를 경우 입력하세요. 수강료 옆에 표시됩니다."})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:["장소 ",e.jsx("span",{className:"text-red-500",children:"*"})]}),e.jsx("textarea",{value:c.location,onChange:i=>n({...c,location:i.target.value}),className:"compact-input",rows:2,placeholder:`예: 1,2주 비전센터
983,4주 교육관`,required:!0})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"최대 정원 (0=무제한)"}),e.jsx("input",{type:"number",value:c.maxEnrollment||"",onChange:i=>n({...c,maxEnrollment:i.target.value?parseInt(i.target.value):void 0}),className:"compact-input",placeholder:"무제한",min:"0"})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"수강료 (원)"}),e.jsx("input",{type:"number",value:c.fee??"",onChange:i=>n({...c,fee:i.target.value?parseInt(i.target.value):void 0}),className:"compact-input",placeholder:"비워두면 학기 기본 수강료 적용",min:"0",step:"1000"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"납부주기"}),e.jsx("select",{value:c.feeType,onChange:i=>n({...c,feeType:i.target.value}),className:"compact-input",children:Object.entries(Ce).map(([i,j])=>e.jsx("option",{value:i,children:j},i))})]})]}),e.jsx("p",{className:"text-[11px] text-gray-400 -mt-2",children:"강좌별로 수강료가 다를 경우 입력하세요. 비워두면 학기 기본 수강료(월납)가 적용됩니다. 예) 성악반 80,000원 · 1회(일시납) / 하모니카 20,000원 · 월납"}),e.jsxs("div",{className:"space-y-3 p-3 bg-amber-50 rounded",children:[e.jsx("h3",{className:"text-[13px] font-bold text-gray-900",children:"홍보 정보 (홍보 사이트용)"}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"강좌 대표 이미지"}),e.jsxs("div",{className:"flex items-center gap-3",children:[c.imageUrl&&e.jsx("img",{src:c.imageUrl,alt:"강좌 이미지",className:"w-20 h-14 roun
98ded object-cover border"}),e.jsxs("label",{className:"flex items-center gap-1.5 px-3 py-1.5 bg-white border border-gray-200 rounded hover:bg-gray-50 cursor-pointer text-[12px]",children:[e.jsx(Me,{className:"w-3.5 h-3.5 text-gray-500"}),N?"업로드 중...":"이미지 선택",e.jsx("input",{type:"file",accept:"image/*",onChange:i=>k(i,"course"),className:"hidden",disabled:N})]})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"한줄 홍보 문구"}),e.jsx("input",{type:"text",value:c.tagline,onChange:i=>n({...c,tagline:i.target.value}),className:"compact-input",placeholder:"예: 음악의 즐거움을 함께 나눠요!",maxLength:50})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"대상"}),e.jsx("input",{type:"text",value:c.targetAudience,onChange:i=>n({...c,targetAudience:i.target.value}),className:"compact-input",placeholder:"예: 누구나 참여 가능"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"준비물"}),e.jsx("input",{type:"text",value:c.suppliesNeeded,onChange:i=>n({...c,suppliesNeeded:i.target.value}),className:"compact-input",placeholder:"예: 개인 악기 지참"})]})]})]}),e.jsxs("div",{className:"space-y-3 p-3 bg-green-50 rounded",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("h3",{className:"text-[13px] font-bold text-gray-900",children:"주차별 커리큘럼"}),e.jsxs("button",{type:"button",onClick:M,className:"flex items-center gap-1 text-[11px] text-green-700 hover:underline",children:[e.jsx(ie,{className:"w-3 h-3"})," 주차 추가"]})]}),c.curriculum.length===0?e.jsx("p",{className:"text-[11px] text-gray-500 text-center py-2",children:"커리큘럼을 추가하면 홍보 사이트에 표시됩니다"}):e.jsx("div",{className:"space-y-2 max-h-48 overflow-y-auto",children:c.curriculum.map((i,j)=>e.jsxs("div",{className:"flex items-start gap-2 bg-white p-2 rounded border border-green-100",children:[e.jsx("span",{className:"flex-shrink-0 w-6 h-6 bg-green-600 text-white rounded-full flex items-center justify-center text-[10px] font-bold mt-0.5",children:i.week}),e.jsxs("div",{className:"flex-1 space-y-1",children:[e.jsx("input",{type:"text",value:i.title,onChange:o=>F(j,"title",o.target.value),className:"compact-input text-[12px]",placeholder:`${i.week}주차 제목`}),e.jsx("input",{type:"text",value:i.description||"",onChange:o=>F(j,"description",o.target.value),className:"compact-input text-[11px]",placeholder:"상세 설명 (선택)"})]}),e.jsx("button",{type:"button",onClick:()=>P(j),className:"p-1 text-red-500 hover:bg-red-50 rounded flex-shrink-0",children:e.jsx(te,{className:"w-3.5 h-3.5"})})]},j))})]}),e.jsxs("div",{className:"flex gap-2 pt-2",children:[e.jsx("button",{type:"button",onClick:s,className:"flex-1 px-3 py-2 border border-gray-300 rounded hover:bg-gray-50 transition-colors text-[13px]",children:"취소"}),e.jsx("button",{type:"submit",disabled:v||N||p,className:"flex-1 px-3 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors text-[13px] font-medium disabled:opacity-50",children:v?"처리 중...":d?"수정":"추가"})]})]})]})})}function Ze(){const[t,a]=h.useState(!0),[s,u]=h.useState([]),[d,v]=h.useState(""),[x,c]=h.useState([]),[n,N]=h.useState(null),[w,p]=h.useState({name:"",bio:"",careers:"",phone:""}),[g,k]=h.useState(!1),[M,P]=h.useState(!1);h.useEffect(()=>{F()},[]),h.useEffect(()=>{d&&R()},[d]);async function F(){try{const o=await H.getAllSemesters();if(u(o),o.length>0){const r=o.find(l=>l.status==="recruiting"||l.status==="active");
98v((r==null?void 0:r.id)||o[0].id)}}catch(o){console.error("학기 목록 조회 실패:",o)}finally{a(!1)}}async function R(){try{a(!0);const o=await $.getCoursesBySemester(d);c(o.sort((r,l)=>(r.sortOrder||0)-(l.sortOrder||0)))}catch(o){console.error("데이터 로딩 실패:",o),f.error("데이터를 불러올 수 없습니다")}finally{a(!1)}}function _(o){const r=`https://culture.seodoon.org/instructor-form.html?course=${o}`;navigator.clipboard.writeText(r),f.success("강사 입력 링크가 복사되었습니다")}function W(o){const r=o.instructorProfile||{};N(o.id),p({name:o.instructor||"",bio:r.bio||"",careers:(r.careers||[]).join(`
99`),phone:o.instructorPhone||""})}function y(){N(null),p({name:"",bio:"",careers:"",phone:""})}async function i(o,r){if(r.size>5*1024*1024){f.error("파일 크기가 5MB를 초과합니다");return}try{P(!0);const l=r.name.split(".").pop()||"jpg",m=Ne(Se,`culture/instructors/${o}/photo.${l}`);await ve(m,r);const b=await we(m),C=x.find(D=>D.id===o),A=(C==null?void 0:C.instructorProfile)||{};await $.updateCourse(o,{instructorProfile:{...A,photoUrl:b}}),f.success("강사 사진이 업데이트되었습니다"),R()}catch(l){console.error("사진 업로드 실패:",l),f.error("사진 업로드에 실패했습니다")}finally{P(!1)}}async function j(o){try{k(!0);const r=x.find(b=>b.id===o),l=(r==null?void 0:r.instructorProfile)||{},m=w.careers.split(/\r?\n/).map(b=>b.trim()).filter(b=>b);await $.updateCourse(o,{instructor:w.name.trim(),instructorPhone:w.phone.trim()||void 0,instructorProfile:{...l,bio:w.bio.trim(),careers:m}}),f.success("강사 정보가 저장되었습니다"),N(null),R()}catch(r){console.error("저장 실패:",r),f.error("저장에 실패했습니다")}finally{k(!1)}}return t&&x.length===0?e.jsx("div",{className:"flex items-center justify-center h-64",children:e.jsx("div",{className:"text-gray-500 text-[13px]",children:"로딩 중..."})}):e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("label",{className:"text-[13px] font-medium text-gray-700",children:"학기"}),e.jsx("select",{value:d,onChange:o=>v(o.target.value),className:"px-3 py-1.5 border border-gray-300 rounded text-[13px]",children:s.map(o=>e.jsx("option",{value:o.id,children:o.name},o.id))})]}),x.length===0?e.jsx("div",{className:"text-center py-16 text-gray-500 text-[13px]",children:"등록된 강좌가 없습니다"}):e.jsx("div",{className:"space-y-4",children:x.map(o=>{const r=o.instructorProfile||{},l=n===o.id;return e.jsx("div",{className:"bg-white rounded-lg border border-gray-200 overflow-hidden",children:e.jsxs("div",{className:"p-4",children:[e.jsxs("div",{className:"flex items-start gap-4",children:[e.jsxs("div",{className:"flex flex-col items-center gap-1.5 flex-shrink-0",children:[e.jsxs("label",{className:"cursor-pointer group relative",children:[e.jsx("div",{className:"w-16 h-16 rounded-full bg-gray-100 flex items-center justify-center overflow-hidden ring-2 ring-gray-200 group-hover:ring-blue-300 transition-all",children:r.photoUrl?e.jsx("img",{src:r.photoUrl,alt:o.instructor,className:"w-full h-full object-cover"}):e.jsx(be,{className:"w-8 h-8 text-gray-300"})}),e.jsx("div",{className:"absolute inset-0 rounded-full bg-black/30 opacity-0 group-hover:opacity-100 flex items-center justify-center transition-opacity",children:e.jsx(ye,{className:"w-4 h-4 text-white"})}),e.jsx("input",{type:"file",accept:"image/*",className:"hidden",onChange:m=>{var C;const b=(C=m.target.files)==null?void 0:C[0];b&&i(o.id,b),m.target.value=""},disabled:M})]}),M&&e.jsx("span",{className:"text-[10px] text-blue-600",children:"업로드 중..."})]}),e.jsx("div",{className:"flex-1 min-w-0",children:l?e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[e.jsxs("div",{children:[e.jsx("label",{className:"text-[11px] font-medium text-gray-500 mb-1 block",children:"강사명"}),e.jsx("input",{type:"text",value:w.name,onChange:m=>p(b=>({...b,name:m.target.value})),className:"w-full px-2.5 py-1.5 border border-gray-300 rounded text-[13px] focus:outline-none focus:border-blue-500"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"text-[11px] font-medium text-gray-500 mb-1 block",children:"연락처"}),e.jsx("input",{type:"tel",value:w.phone,onChange:m=>p(b=>({...b,phone:m.target.value})),placeholder:"010-0000-0000",className:"w-full px-2.5 py-1.5 border border-gray-300 rounded text-[13px] focus:outline-none focus:border-blue-500"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"text-[11px] font-medium text-gray-500 mb-1 block",children:"자기소개"}),e.jsx("textarea",{value:w.bio,onChange:m=>p(b=>({...b,bio:m.target.value})),rows:2,placeholder:"강사 소개",className:"w-full px-2.5 py-1.5 border border-gray-300 rounded text-[13px] focus:outline-none focus:border-blue-500 resize-none"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"text-[11px] font-medium text-gray-500 mb-1 block",children:"경력/자격 (줄바꿈 구분)"}),e.jsx("textarea",{value:w.careers,onChange:m=>p(b=>({...b,careers:m.target.value})),rows:3,placeholder:"한 줄에 하나씩 입력",className:"w-full px-2.5 py-1.5 border border-gray-300 rounded text-[13px] focus:outline-none focus:border-blue-500 resize-none"})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{onClick:()=>j(o.id),disabled:g,className:"px-3 py-1.5 bg-blue-600 text-white rounded text-[12px] font-medium hover:bg-blue-700 disabled:opacity-50 flex items-center gap-1",children:[e.jsx(ce,{className:"w-3 h-3"}),g?"저장 중...":"저장"]}),e.jsxs("button",{onClick:y,className:"px-3 py-1.5 border border-gray-300 rounded text-[12px] font-medium hover:bg-gray-50 flex items-center gap-1",children:[e.jsx(te,{className:"w-3 h-3"}),"취소"]})]})]}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-bold text-[15px] text-gray-900",children:o.instructor||"강사 미정"}),o.instructorPhone&&e.jsxs("span",{className:"text-[11px] text-gray-400 flex items-center gap-0.5",children:[e.jsx(Re,{className:"w-3 h-3"})," ",o.instructorPhone]}),e.jsxs("button",{onClick:()=>W(o),className:"ml-auto flex items-center gap-1 px-2 py-1 text-[11px] font-medium text-gray-500 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors",children:[e.jsx(pe,{className:"w-3 h-3"}),"수정"]})]}),e.jsxs("div",{className:"text-[13px] text-gray-600 mt-1 flex items-center gap-1.5",children:[e.jsx(Z,{className:"w-3.5 h-3.5 text-blue-500"}),e.jsx("span",{className:"font-medium",children:o.name}),o.schedule&&e.jsxs("span",{className:"text-gray-400",children:["| ",ae(o.schedule)]}),o.location&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-gray-300",children:"|"}),e.jsx(Le,{className:"w-3 h-3 text-gray-400"}),e.jsx("span",{className:"text-gray-400",children:o.location})]})]}),r.bio&&e.jsx("p",{className:"text-[12px] text-gray-500 mt-2 line-clamp-2",children:r.bio}),r.careers&&r.careers.length>0&&e.jsx("div",{className:"flex flex-wrap gap-1.5 mt-2",children:r.careers.map((m,b)=>e.jsx("span",{className:"inline-block px-2 py-0.5 bg-gray-50 border border-gray-200 rounded text-[11px] text-gray-600",children:m},b))})]})})]}),!l&&e.jsx("div",{className:"flex items-center mt-3 pt-3 border-t border-gray-100",children:e.jsxs("button",{onClick:()=>_(o.id),className:"flex items-center gap-1 px-2.5 py-1 text-[11px] font-medium text-blue-600 bg-blue-50 rounded hover:bg-blue-100 transition-colors",title:"강사에게 이 링크를 보내면 자기 프로필/커리큘럼을 직접 입력할 수 있습니다",children:[e.jsx(Oe,{className:"w-3 h-3"}),"강사 입력 링크 복사"]})})]})},o.id)})})]})}export{it as CultureManagementPage};

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.