PageSourceSearch

https://seodoon-church-admin.web.app/assets/PushManagementPage-DqOccZGR.js

js seodoon-church-admin.web.app collected 2026-10-03 12:35:56 UTC 37,951 bytes, 7 lines download raw bytes

1import{j as e}from"./vendor-data-Z5doMMu-.js";import{r as p}from"./vendor-react-zNM0AU50.js";import{p as _}from"./pushService-ggivUk2Y.js";import{s as Y}from"./storageService-HxVmCucr.js";import{U as $,_ as J,ab as W,a5 as Z,a7 as y,B as L,aa as ee,af as P,i as K,ar as q,ak as T,g as te,ad as E,f as R,ax as F,a4 as G,T as Q,X as V,a2 as se,aU as ae,a8 as le}from"./vendor-ui-CW5bJ11U.js";import{m as z}from"./index-D1K3lVhu.js";import"./vendor-firebase-Da9KY1q_.js";import"./vendor-charts-08G1rM8P.js";function ne({value:t,onChange:x}){const[u,h]=p.useState(0),[g,b]=p.useState(!1);function o(l){x({mode:l,organization:l==="organization"?{}:void 0,condition:l==="condition"?{}:void 0,individual:l==="individual"?{member_ids:[]}:void 0})}p.useEffect(()=>{r()},[t]);async function r(){try{if(b(!0),t.mode==="all"){const l=await z.getMembers();h(l.filter(c=>c.status==="활동").length)}else if(t.mode==="organization"&&t.organization){const c=(await z.getMembers()).filter(m=>{if(m.status!=="활동")return!1;const d=t.organization;return!(d.positions&&d.positions.length>0&&!d.positions.includes(m.position||"")||d.detailed_positions&&d.detailed_positions.length>0&&!d.detailed_positions.includes(m.detailed_position||"")||d.parishes&&d.parishes.length>0&&!d.parishes.includes(m.parish||"")||d.districts&&d.districts.length>0&&!d.districts.includes(m.district||"")||d.evangelism_groups&&d.evangelism_groups.length>0&&!d.evangelism_groups.includes(m.evangelism_group||""))});h(c.length)}else if(t.mode==="condition"&&t.condition){const c=(await z.getMembers()).filter(m=>{const d=t.condition;if(d.classifications&&d.classifications.length>0&&!d.classifications.includes(m.member_classification||"")||d.statuses&&d.statuses.length>0&&!d.statuses.includes(m.status||"")||d.gender&&m.gender!==d.gender)return!1;if(d.age_range){const n=m.age||0;if(d.age_range.min!==void 0&&n<d.age_range.min||d.age_range.max!==void 0&&n>d.age_range.max)return!1}return!0});h(c.length)}else t.mode==="individual"&&t.individual&&h(t.individual.member_ids.length)}catch(l){console.error("대상 인원 계산 실패:",l),h(0)}finally{b(!1)}}return e.jsxs("div",{className:"space-y-4 p-4 bg-gray-50 rounded border border-gray-200",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("h3",{className:"text-[14px] font-bold text-gray-900",children:"발송 대상 선택"}),e.jsxs("div",{className:"flex items-center gap-2 text-[12px]",children:[e.jsx($,{className:"w-4 h-4 text-naver-green"}),e.jsx("span",{className:"font-bold text-naver-green",children:g?"계산 중...":`${u.toLocaleString()}명`})]})]}),e.jsxs("div",{className:"grid grid-cols-4 gap-2",children:[e.jsx(M,{icon:e.jsx($,{className:"w-4 h-4"}),label:"전체",description:"모든 교인",active:t.mode==="all",onClick:()=>o("all")}),e.jsx(M,{icon:e.jsx(J,{className:"w-4 h-4"}),label:"조직",description:"직분·교구·구역",active:t.mode==="organization",onClick:()=>o("organization")}),e.jsx(M,{icon:e.jsx(W,{className:"w-4 h-4"}),label:"조건",description:"분류·상태·성별·연령",active:t.mode==="condition",onClick:()=>o("condition")}),e.jsx(M,{icon:e.jsx(Z,{className:"w-4 h-4"}),label:"개별",description:"직접 선택",active:t.mode==="individual",onClick:()=>o("individual")})]}),t.mode==="all"&&e.jsx("div",{className:"p-3 bg-blue-50 border border-blue-200 rounded text-[12px] text-blue-800",children:"ℹ️ 모든 활동 중인 교인에게 발송됩니다."}),t.mode==="organization"&&e.jsx(ie,{value:t.organization||{},onChange:l=>x({...t,organization:l})}),t.mode==="condition"&&e.jsx(re,{value:t.condition||{},onChange:l=>x({...t,condition:l})}),t.mode==="individual"&&e.jsx(ce,{value:t.individual||{member_ids:[]},onChange:l=>x({...t,individual:l})})]})}function M({icon:t,label:x,description:u,active:h,onClick:g}){return e.jsxs("button",{type:"button",onClick:g,className:`
2        p-3 rounded border-2 transition-all text-left
3        ${h?"border-naver-green bg-green-50":"border-gray-200 bg-white hover:border-gray-300"}
4      `,children:[e.jsxs("div",{className:`flex items-center gap-2 mb-1 ${h?"text-naver-green":"text-gray-700"}`,children:[t,e.jsx("span",{className:"text-[13px] font-bold",children:x})]}),e.jsx("div",{className:"text-[10px] text-gray-500",children:u})]})}function ie({value:t,onChange:x}){const[u,h]=p.useState([]),[g,b]=p.useState([]),[o,r]=p.useState([]),[l,c]=p.useState([]),[m,d]=p.useState([]);p.useEffect(()=>{n()},[]);async function n(){try{const a=await z.getMembers();h([...new Set(a.map(s=>
4s.position).filter(Boolean))]),b([...new Set(a.map(s=>s.detailed_position).filter(Boolean))]),r([...new Set(a.map(s=>s.parish).filter(Boolean))]),c([...new Set(a.map(s=>s.district).filter(Boolean))]),d([...new Set(a.map(s=>s.evangelism_group).filter(Boolean))])}catch(a){console.error("조직 옵션 로딩 실패:",a)}}return e.jsxs("div",{className:"space-y-3 p-3 bg-white rounded border border-gray-200",children:[e.jsx("p",{className:"text-[11px] text-gray-600",children:"여러 조건을 선택하면 AND 조건으로 필터링됩니다."}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"직분"}),e.jsx("select",{multiple:!0,value:t.positions||[],onChange:a=>{const s=Array.from(a.target.selectedOptions,f=>f.value);x({...t,positions:s.length>0?s:void 0})},className:"compact-input",size:4,children:u.map(a=>e.jsx("option",{value:a,children:a},a))}),e.jsx("p",{className:"text-[10px] text-gray-500 mt-0.5",children:"Ctrl/Cmd 키를 누르고 클릭하여 여러 개 선택"})]}),g.length>0&&e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"세부직분"}),e.jsx("select",{multiple:!0,value:t.detailed_positions||[],onChange:a=>{const s=Array.from(a.target.selectedOptions,f=>f.value);x({...t,detailed_positions:s.length>0?s:void 0})},className:"compact-input",size:4,children:g.map(a=>e.jsx("option",{value:a,children:a},a))})]}),o.length>0&&e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"교구"}),e.jsx("select",{multiple:!0,value:t.parishes||[],onChange:a=>{const s=Array.from(a.target.selectedOptions,f=>f.value);x({...t,parishes:s.length>0?s:void 0})},className:"compact-input",size:3,children:o.map(a=>e.jsx("option",{value:a,children:a},a))})]}),l.length>0&&e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"구역"}),e.jsx("select",{multiple:!0,value:t.districts||[],onChange:a=>{const s=Array.from(a.target.selectedOptions,f=>f.value);x({...t,districts:s.length>0?s:void 0})},className:"compact-input",size:4,children:l.map(a=>e.jsx("option",{value:a,children:a},a))})]}),m.length>0&&e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"전도회"}),e.jsx("select",{multiple:!0,value:t.evangelism_groups||[],onChange:a=>{const s=Array.from(a.target.selectedOptions,f=>f.value);x({...t,evangelism_groups:s.length>0?s:void 0})},className:"compact-input",size:3,children:m.map(a=>e.jsx("option",{value:a,children:a},a))})]})]})}function re({value:t,onChange:x}){var g,b;const u=["서둔출석성도","새가족","별세","유고","군입대","이민","제적"],h=["활동","휴면","이탈","전도"];return e.jsxs("div",{className:"space-y-3 p-3 bg-white rounded border border-gray-200",children:[e.jsx("p",{className:"text-[11px] text-gray-600",children:"여러 조건을 선택하면 AND 조건으로 필터링됩니다."}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"교적분류"}),e.jsx("select",{multiple:!0,value:t.classifications||[],onChange:o=>{const r=Array.from(o.target.selectedOptions,l=>l.value);x({...t,classifications:r.length>0?r:void 0})},className:"compact-input",size:4,children:u.map(o=>e.jsx("option",{value:o,children:o},o))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"상태"}),e.jsx("select",{multiple:!0,value:t.statuses||[],onChange:o=>{const r=Array.from(o.target.selectedOptions,l=>l.value);x({...t,statuses:r.length>0?r:void 0})},className:"compact-input",size:4,children:h.map(o=>e.jsx("option",{value:o,children:o},o))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"성별"}),e.jsxs("select",{value:t.gender||"",onChange:o=>x({...t,gender:o.target.value||void 0}),className:"compact-input",children:[e.jsx("option",{value:"",children:"전체"}),e.jsx("option",{value:"남",children:"남"}),e.jsx("option",{value:"여",children:"여"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"연령대"}),e.jsxs("div",{className:"grid grid-cols-2 gap-2",children:[e.jsx("input",{type:"number",placeholder:"최소 (예: 20)",value:((g=t.age_range)==null?void 0:g.min)||"",onChange:o=>{var l;const r=o.target.value?parseInt(o.target.value):void 0;x({...t,age_range:r!==void 0||((l=t.age_range)==null?void 0:l.max)!==void 0?{...t.age_range,min:r}:void 0})},className:"compact-input",min:"0",max:"120"}),e.jsx("input",{type:"number",placeholder:"최대 (예: 30)",value:((b=t.age_range)==null?void 0:b.max)||"",onChange:o=>{var l;const r=o.target.value?parseInt(o.target.value):void 0;x({...t,age_range:r!==void 0||((l=t.age_range)==null?void 0:l.min)!==void 0?{...t.age_range,max:r}:void 0})},className:"compact-input",min:"0",max:"120"})]})]})]})}function ce({value:t,onChange:x}){const[u,h]=p.useState([]),[g,b]=p.useState(""),[o,r]=p.useState(!1);p.useEffect(()=>{l()},[]);async function l(){try{r(!0);const n=await z.getMembers();h(n.filter(a=>a.status==="활동").map(a=>({id:a.id,name:a.name,position:a.position,district:a.district})))}catch(n){console.error("교인 목록 로딩 실패:",n)}finally{r(!1)}}const c=u.filter(n=>{var a,s;return n.name.includes(g)||((a=n.position)==null?void 0:a.includes(g))||((s=n.district)==null?void 0:s.includes(g))}),m=u.filter(n=>
4t.member_ids.includes(n.id));function d(n){const a=t.member_ids.includes(n)?t.member_ids.filter(s=>s!==n):[...t.member_ids,n];x({member_ids:a})}return e.jsxs("div",{className:"space-y-3 p-3 bg-white rounded border border-gray-200",children:[m.length>0&&e.jsxs("div",{className:"p-2 bg-green-50 border border-green-200 rounded",children:[e.jsxs("div",{className:"text-[11px] font-medium text-green-800 mb-2",children:["선택된 교인 (",m.length,"명)"]}),e.jsx("div",{className:"flex flex-wrap gap-1",children:m.map(n=>e.jsxs("button",{type:"button",onClick:()=>d(n.id),className:"px-2 py-1 bg-white border border-green-300 rounded text-[11px] hover:bg-red-50 hover:border-red-300 transition-colors",children:[n.name," ",n.position&&`(${n.position})`,e.jsx("span",{className:"ml-1 text-red-500",children:"×"})]},n.id))})]}),e.jsx("div",{children:e.jsx("input",{type:"text",placeholder:"이름, 직분, 구역으로 검색...",value:g,onChange:n=>b(n.target.value),className:"compact-input"})}),e.jsx("div",{className:"max-h-60 overflow-y-auto border border-gray-200 rounded",children:o?e.jsx("div",{className:"p-4 text-center text-[12px] text-gray-500",children:"로딩 중..."}):c.length===0?e.jsx("div",{className:"p-4 text-center text-[12px] text-gray-500",children:"검색 결과가 없습니다"}):e.jsx("div",{children:c.map(n=>e.jsx("button",{type:"button",onClick:()=>d(n.id),className:`
5                  w-full text-left px-3 py-2 border-b border-gray-100 hover:bg-gray-50 transition-colors text-[12px]
6                  ${t.member_ids.includes(n.id)?"bg-green-50":""}
7                `,children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("span",{className:"font-medium",children:n.name}),n.position&&e.jsxs("span",{className:"ml-2 text-gray-500",children:["(",n.position,")"]}),n.district&&e.jsx("span",{className:"ml-2 text-gray-400 text-[11px]",children:n.district})]}),t.member_ids.includes(n.id)&&e.jsx("span",{className:"text-naver-green font-bold",children:"✓"})]})},n.id))})})]})}const oe={qt_daily:{url:"/qt.html",label:"QT"},bulletin:{url:"/bulletin.html",label:"주보"},bulletin_published:{url:"/bulletin.html",label:"주보"},communication:{url:"/communication.html",label:"소통"},worship:{url:"/index.html",label:"예배"},event:{url:"/index.html",label:"행사"},prayer:{url:"/index.html",label:"기도"},occasion:{url:"/index.html",label:"경조사"},birthday:{url:"/index.html",label:"생일"},youth_announcement:{url:"/youth/index.html",label:"청년부"},attendance_reminder:{url:"/attendance.html",label:"출석"},content:{url:"/index.html",label:"일반"},urgent:{url:"/index.html",label:"긴급"}},de=[{value:"",label:"선택 안 함"},{value:"content",label:"일반 소식"},{value:"worship",label:"예배 안내"},{value:"qt_daily",label:"QT 알림"},{value:"bulletin",label:"주보"},{value:"communication",label:"소통"},{value:"event",label:"행사 안내"},{value:"prayer",label:"기도"},{value:"occasion",label:"경조사"},{value:"birthday",label:"생일 축하"},{value:"youth_announcement",label:"청년부"},{value:"attendance_reminder",label:"출석 알림"},{value:"urgent",label:"긴급 알림"}];function we(){const[t,x]=p.useState(!0),[u,h]=p.useState([]),[g,b]=p.useState(""),[o,r]=p.useState("all"),[l,c]=p.useState(!1),[m,d]=p.useState(null),[n,a]=p.useState(null),[s,f]=p.useState("created_at"),[N,S]=p.useState("desc");p.useEffect(()=>{k()},[]);async function k(){try{x(!0);const i=await _.getAllPushes();h(i)}catch(i){console.error("푸시 로딩 실패:",i),y.error("푸시 알림을 불러올 수 없습니다")}finally{x(!1)}}const O=[...u].sort((i,j)=>{let C,D;return s==="created_at"?(C=new Date(i.created_at||0).getTime(),D=new Date(j.created_at||0).getTime()):s==="sent_at"?(C=i.sent_at?new Date(i.sent_at).getTime():0,D=j.sent_at?new Date(j.sent_at).getTime():0):(C=i.title.toLowerCase(),D=j.title.toLowerCase()),N==="asc"?C>D?1:-1:C<D?1:-1}).filter(i=>!(g&&!i.title.toLowerCase().includes(g.toLowerCase())&&!i.body.toLowerCase().includes(g.toLowerCase())||o!=="all"&&i.status!==o)),v={total:u.length,draft:u.filter(i=>i.status==="draft").length,scheduled:u.filter(i=>i.status==="scheduled").length,sent:u.filter(i=>i.status==="sent").length,totalSent:u.reduce((i,j)=>i+(j.sent_count||0),0),totalRead:u.reduce((i,j)=>i+(j.read_count||0),0)};async function U(i){if(confirm("이 푸시 알림을 삭제하시겠습니까?"))try{await _.deletePush(i),y.success("삭제되었습니다"),a(null),k()}catch{y.error("삭제에 실패했습니다")}}async function B(i){if(confirm("이 푸시를 즉시 발송하시겠습니까?"))try{await _.sendNow(i),y.success("발송되었습니다"),a(null),k()}catch{y.error("발송에 실패했습니다")}}async function I(i){if(confirm("예약을 취소하시겠습니까?"))try{await _.cancelScheduled(i),y.success("예약이 취소되었습니다"),k()}catch{y.error("취소에 실패했습니다")}}function A(i){s===i?S(N==="asc"?"desc":"asc"):(f(i),S("desc"))}return t?e.jsx("div",{className:"flex items-center justify-center h-[calc(100vh-10rem)]",children:e.jsxs("div",{className:"flex flex-col items-center gap-3",children:[e.jsx("div",{className:"w-10 h-10 border-4 border-blue-500 border-t-transparent roun
7ded-full animate-spin"}),e.jsx("span",{className:"text-gray-500 text-sm",children:"푸시 알림을 불러오는 중..."})]})}):e.jsxs("div",{className:"space-y-6 animate-fade-in",children:[e.jsx("div",{className:"relative overflow-hidden bg-white border border-gray-200 rounded-2xl p-6 shadow-sm",children:e.jsxs("div",{className:"relative flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-3 mb-2",children:[e.jsx("div",{className:"w-10 h-10 bg-blue-50 rounded-xl flex items-center justify-center",children:e.jsx(L,{className:"w-5 h-5 text-blue-600"})}),e.jsx("h1",{className:"text-2xl font-bold text-gray-900",children:"푸시 알림 관리"})]}),e.jsx("p",{className:"text-gray-500 text-sm",children:"앱 사용자에게 푸시 알림을 발송합니다"})]}),e.jsxs("button",{onClick:()=>{d(null),c(!0)},className:"flex items-center gap-2 px-5 py-2.5 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all text-sm font-semibold shadow-sm",children:[e.jsx(ee,{className:"w-4 h-4"}),"푸시 작성"]})]})}),e.jsxs("div",{className:"grid grid-cols-6 gap-4",children:[e.jsx(w,{label:"전체",value:v.total,color:"blue",icon:L}),e.jsx(w,{label:"임시저장",value:v.draft,color:"gray",icon:P}),e.jsx(w,{label:"예약",value:v.scheduled,color:"orange",icon:K}),e.jsx(w,{label:"발송완료",value:v.sent,color:"emerald",icon:q}),e.jsx(w,{label:"총 발송",value:v.totalSent,color:"blue",icon:$}),e.jsx(w,{label:"총 읽음",value:v.totalRead,color:"blue",icon:T})]}),e.jsx("div",{className:"bg-white rounded-2xl shadow-sm border border-gray-200 p-4",children:e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsx("div",{className:"flex-1 max-w-xs",children:e.jsxs("div",{className:"relative",children:[e.jsx(te,{className:"absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400"}),e.jsx("input",{type:"text",placeholder:"제목/내용 검색...",value:g,onChange:i=>b(i.target.value),className:"w-full pl-10 pr-4 py-2.5 border border-gray-200 rounded-xl text-sm bg-gray-50 focus:bg-white focus:outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 transition-all"})]})}),e.jsx("div",{className:"flex items-center gap-2 bg-gray-100 p-1 rounded-xl",children:["all","draft","scheduled","sent"].map(i=>e.jsx("button",{onClick:()=>r(i),className:`px-4 py-2 rounded-lg text-sm font-medium transition-all ${o===i?"bg-white text-blue-600 shadow-sm":"text-gray-600 hover:text-gray-900"}`,children:i==="all"?"전체":i==="draft"?"임시저장":i==="scheduled"?"예약":"발송완료"},i))})]})}),e.jsxs("div",{className:"bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden",children:[e.jsx("div",{className:"px-6 py-4 border-b border-gray-100 bg-slate-50",children:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"w-8 h-8 bg-blue-50 rounded-lg flex items-center justify-center",children:e.jsx(L,{className:"w-4 h-4 text-blue-600"})}),e.jsxs("div",{children:[e.jsx("h2",{className:"text-base font-bold text-gray-900",children:"푸시 알림 목록"}),e.jsxs("p",{className:"text-xs text-gray-500",children:[O.length,"개의 알림"]})]})]})}),O.length===0?e.jsxs("div",{className:"p-16 text-center",children:[e.jsx("div",{className:"w-16 h-16 bg-gray-100 rounded-2xl flex items-center justify-center mx-auto mb-4",children:e.jsx(L,{className:"w-8 h-8 text-gray-400"})}),e.jsx("p",{className:"text-gray-600 font-medium",children:"등록된 푸시 알림이 없습니다"}),e.jsx("p",{className:"text-sm text-gray-400 mt-1",children:"상단의 '푸시 작성' 버튼을 클릭하여 새 알림을 작성하세요"})]}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-[13px]",children:[e.jsx("thead",{className:"bg-blue-600 text-white",children:e.jsxs("tr",{children:[e.jsx("th",{className:"text-left px-3 py-2.5 font-medium w-10",children:"#"}),e.jsx("th",{className:"text-left px-3 py-2.5 font-medium cursor-pointer hover:bg-blue-700 transition-colors",onClick:()=>A("title"),children:e.jsxs("div",{className:"flex items-center gap-1",children:["제목",s==="title"&&(N==="asc"?e.jsx(E,{className:"w-3 h-3"}):e.jsx(R,{className:"w-3 h-3"}))]})}),e.jsx("th",{className:"text-left px-3 py-2.5 font-medium w-20",children:"상태"}),e.jsx("th",{className:"text-left px-3 py-2.5 font-medium w-32",children:"대상"}),e.jsx("th",{className:"text-left px-3 py-2.5 font-medium w-36 curs
7or-pointer hover:bg-blue-700 transition-colors",onClick:()=>A("created_at"),children:e.jsxs("div",{className:"flex items-center gap-1",children:["작성일",s==="created_at"&&(N==="asc"?e.jsx(E,{className:"w-3 h-3"}):e.jsx(R,{className:"w-3 h-3"}))]})}),e.jsx("th",{className:"text-left px-3 py-2.5 font-medium w-36 cursor-pointer hover:bg-blue-700 transition-colors",onClick:()=>A("sent_at"),children:e.jsxs("div",{className:"flex items-center gap-1",children:["발송일",s==="sent_at"&&(N==="asc"?e.jsx(E,{className:"w-3 h-3"}):e.jsx(R,{className:"w-3 h-3"}))]})}),e.jsx("th",{className:"text-center px-3 py-2.5 font-medium w-16",children:"발송"}),e.jsx("th",{className:"text-center px-3 py-2.5 font-medium w-16",children:"읽음"}),e.jsx("th",{className:"text-center px-3 py-2.5 font-medium w-28",children:"액션"})]})}),e.jsx("tbody",{className:"divide-y divide-gray-100",children:O.map((i,j)=>e.jsx(xe,{push:i,index:j+1,isSelected:(n==null?void 0:n.id)===i.id,onClick:()=>a((n==null?void 0:n.id)===i.id?null:i),onEdit:()=>{d(i),c(!0)},onDelete:()=>U(i.id),onSendNow:()=>B(i.id),onCancelScheduled:()=>I(i.id)},i.id))})]})})]}),n&&e.jsx(me,{push:n,onClose:()=>a(null),onEdit:()=>{d(n),c(!0)},onDelete:()=>U(n.id),onSendNow:()=>B(n.id),onCancelScheduled:()=>I(n.id)}),l&&e.jsx(ue,{push:m,onClose:()=>{c(!1),d(null)},onSuccess:()=>{c(!1),d(null),a(null),k()}})]})}function X(t){var x,u,h,g,b,o;if(t.target_config){const r=t.target_config;if(r.mode==="all")return"전체 교인";if(r.mode==="organization"&&r.organization){const l=[];return(x=r.organization.positions)!=null&&x.length&&l.push(`직분: ${r.organization.positions.join(", ")}`),(u=r.organization.parishes)!=null&&u.length&&l.push(`교구: ${r.organization.parishes.join(", ")}`),(h=r.organization.districts)!=null&&h.length&&l.push(`구역: ${r.organization.districts.join(", ")}`),l.length>0?l.join(" · "):"조직 기반"}if(r.mode==="condition"&&r.condition){const l=[];return(g=r.condition.classifications)!=null&&g.length&&l.push(`분류: ${r.condition.classifications.join(", ")}`),(b=r.condition.statuses)!=null&&b.length&&l.push(`상태: ${r.condition.statuses.join(", ")}`),r.condition.gender&&l.push(`성별: ${r.condition.gender}`),l.length>0?l.join(" · "):"조건 기반"}if(r.mode==="individual"&&r.individual)return`개별 선택 (${r.individual.member_ids.length}명)`}return t.target_type==="all"?"전체 사용자":t.target_type==="specific"?`특정 사용자 (${((o=t.target_users)==null?void 0:o.length)||0}명)`:t.target_type==="group"?`그룹: ${t.target_group}`:"알 수 없음"}function w({label:t,value:x,color:u,icon:h}){const b={blue:{icon:"text-blue-600"},gray:{icon:"text-gray-500"},orange:{icon:"text-orange-500"},emerald:{icon:"text-emerald-500"},violet:{icon:"text-blue-600"},cyan:{icon:"text-blue-600"}}[u];return e.jsxs("div",{className:"bg-white rounded-xl p-4 border border-gray-200 transition-all duration-300 hover:shadow-sm",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx(h,{className:`w-4 h-4 ${b.icon}`}),e.jsx("span",{className:"text-sm font-medium text-gray-500",children:t})]}),e.jsx("div",{className:"text-2xl font-bold text-gray-900 tabular-nums",children:x.toLocaleString()})]})}const H={draft:{label:"임시저장",color:"bg-gray-100 text-gray-700",icon:P},scheduled:{label:"예약",color:"bg-orange-100 text-orange-700",icon:K},sent:{label:"발송완료",color:"bg-green-100 text-green-700",icon:q},failed:{label:"실패",color:"bg-red-100 text-red-700",icon:F}};function xe({push:t,index:x,isSelected:u,onClick:h,onEdit:g,onDelete:b,onSendNow:o,onCancelScheduled:r}){const l=H[t.status],c=l.icon;return e.jsxs("tr",{className:`hover:bg-gray-50 cursor-pointer transition-colors ${u?"bg-blue-50":""}`,onClick:h,children:[e.jsx("td",{className:"px-3 py-2 text-gray-500",children:x}),e.jsx("td",{className:"px-3 py-2",children:e.jsxs("div",{className:"flex items-center gap-2",children:[t.image_url&&e.jsx("img",{src:t.image_url,alt:"",className:"w-8 h-8 object-cover rounded"}),e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"font-medium text-gray-900 truncate",children:t.title}),e.jsx("div",{className:"text-gray-500 truncate max-w-[200px]",children:t.body})]})]})}),e.jsx("td",{className:"px-3 py-2",children:e.jsxs("span",{className:`text-[10px] px-2 py-0.5 rounded font-medium inline-flex items-center gap-1 ${l.color}`,children:[e.jsx(c,{className:"w-3 h-3"}),l.label]})}),e.jsx("td",{className:"px-3 py-2 text-gray-600 truncate max-w-[120px]",children:X(t)}),e.jsx("td",{className:"px-3 py-2 text-gray-600",children:t.created_at?new Date(t.created_at).toLocaleDateString("ko-KR",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"}):"-"}),e.jsx("td",{className:"px-3 py-2 text-gray-600",children:t.sent_at?new Date(t.sent_at).toLocaleDateString("ko-KR",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"}):t.scheduled_at?e.jsxs("span",{className:"text-orange-600",children:[new Date(t.scheduled_at).toLocaleDateString("ko-KR",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})," 예약"]}):"-"}),e.jsx("td",{className:"px-3 py-2 text-center",children:e.jsx("span",{className:"font-medium",children:t.sent_count||0})}),e.jsxs("td",{className:"px-3 py-2 text-center",children:[e.jsx("span",{className:"font-medium",children:t.read_count||0}),t.sent_count&&t.read_count?e.jsxs("span",{className:"text-[10px] text-gray-500 ml-1",children:["(",(t.read_count/t.sent_count*100).toFixed(0),"%)"]}):null]}),e.jsx("td",{className:"px-3 py-2",onClick:m=>m.stopPropagation(),children:e.jsxs("div",{className:"flex items-center justify-center gap-1",children:[t.status==="draft"&&e.jsx("button",{onClick:o,className:"p-1.5 text-green-600 hover:bg-green-100 rounded transition-colors",title:"즉시 발송",children:e.jsx(G,{className:"w-4 h-4"})}),t.status==="sent"&&e.jsx("button",{onClick:g,className:"p-1.5 text-gray-600 hover:bg-gray-100 rounded transition-colors",title:"상세보기",children:e.jsx(T,{className:"w-4 h-4"})}),e.jsx("button",{onClick:g,className:"p-1.5 text-blue-600 hover:bg-blue-100 rounded transition-colors",title:"수정",children:e.jsx(P,{className:"w-4 h-4"})}),e.jsx("button",{onClick:b,className:"p-1.5 text-red-600 hover:bg-red-100 rounded transition-colors",title:"삭제",children:e.jsx(Q,{className:"w-4 h-4"})})]})})]})}function me({push:t,onClose:x,onEdit:u,onDelete:h,onSendNow:g,onCancelScheduled:b}){const o=H[t.status],r=o.icon;return e.jsxs("div",{className:"bg-white border border-gray-200 rounded shadow-sm p-4",children:[e.jsxs("div",{className:"flex items-start justify-between mb-4",children:[e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx("h3",{className:"text-[16px] font-bold text-gray-900",children:t.title}),e.jsxs("span",{className:`text-[10px] px-2 py-0.5 rounded font-medium flex items-center gap-1 ${o.color}`,children:[e.jsx(r,{className:"w-3 h-3"}),o.label]})]}),e.jsx("p",{className:"text-[13px] text-gray-600 whitespace-pre-wrap",children:t.body})]}),e.jsx("button",{onClick:x,className:"p-1 text-gray-400 hover:text-gray-600 transition-colors",children:e.jsx(V,{className:"w-5 h-5"})})]}),t.image_url&&e.jsx("div",{className:"mb-4",children:e.jsx("img",{src:t.image_url,alt:"푸시 이미지",className:"w-full max-w-md h-40 object-cover rounded border border-gray-200"})}),e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4 mb-4 p-3 bg-gray-50 rounded",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[11px] text-gray-500 mb-1",children:"발송 대상"}),e.jsx("div",{className:"text-[13px] font-medium text-gray-900",children:X(t)})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-[11px] text-gray-500 mb-1",children:"작성일시"}),e.jsx("div",{className:"text-[13px] font-medium text-gray-900",children:t.created_at?new Date(t.created_at).toLocaleStr
7ing("ko-KR"):"-"})]}),t.scheduled_at&&e.jsxs("div",{children:[e.jsx("div",{className:"text-[11px] text-gray-500 mb-1",children:"예약 시간"}),e.jsx("div",{className:"text-[13px] font-medium text-orange-600",children:new Date(t.scheduled_at).toLocaleString("ko-KR")})]}),t.sent_at&&e.jsxs("div",{children:[e.jsx("div",{className:"text-[11px] text-gray-500 mb-1",children:"발송 시간"}),e.jsx("div",{className:"text-[13px] font-medium text-green-600",children:new Date(t.sent_at).toLocaleString("ko-KR")})]}),t.link_url&&e.jsxs("div",{children:[e.jsx("div",{className:"text-[11px] text-gray-500 mb-1",children:"링크"}),e.jsxs("div",{className:"text-[13px] font-medium text-blue-600 flex items-center gap-1",children:[e.jsx(se,{className:"w-3 h-3"}),t.link_type==="internal"?"내부":"외부",": ",t.link_url]})]})]}),t.status==="sent"&&e.jsxs("div",{className:"grid grid-cols-4 gap-3 mb-4",children:[e.jsxs("div",{className:"p-3 bg-blue-50 rounded text-center",children:[e.jsx("div",{className:"text-[20px] font-bold text-blue-600",children:t.sent_count||0}),e.jsxs("div",{className:"text-[11px] text-blue-600 flex items-center justify-center gap-1",children:[e.jsx($,{className:"w-3 h-3"})," 발송"]}
7)]}),e.jsxs("div",{className:"p-3 bg-green-50 rounded text-center",children:[e.jsx("div",{className:"text-[20px] font-bold text-green-600",children:t.read_count||0}),e.jsxs("div",{className:"text-[11px] text-green-600 flex items-center justify-center gap-1",children:[e.jsx(T,{className:"w-3 h-3"})," 읽음"]})]}),e.jsxs("div",{className:"p-3 bg-blue-50 rounded text-center",children:[e.jsx("div",{className:"text-[20px] font-bold text-blue-600 tabular-nums",children:t.click_count||0}),e.jsxs("div",{className:"text-[11px] text-blue-600 flex items-center justify-center gap-1",children:[e.jsx(ae,{className:"w-3 h-3"})," 클릭"]})]}),e.jsxs("div",{className:"p-3 bg-orange-50 rounded text-center",children:[e.jsx("div",{className:"text-[20px] font-bold text-orange-600",children:t.sent_count&&t.read_count?`${(t.read_count/t.sent_count*100).toFixed(1)}%`:"-"}),e.jsx("div",{className:"text-[11px] text-orange-600",children:"읽음률"})]})]}),e.jsxs("div",{className:"flex gap-2 pt-2 border-t border-gray-200",children:[t.status==="draft"&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{onClick:g,className:"flex items-center justify-center gap-1 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors text-[12px] font-medium",children:[e.jsx(G,{className:"w-4 h-4"}),"즉시 발송"]}),e.jsxs("button",{onClick:u,className:"flex items-center justify-center gap-1 px-4 py-2 bg-blue-100 hover:bg-blue-200 text-blue-700 rounded transition-colors text-[12px]",children:[e.jsx(P,{className:"w-4 h-4"}),"수정"]})]}),t.status==="scheduled"&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{onClick:b,className:"flex items-center justify-center gap-1 px-4 py-2 bg-orange-100 hover:bg-orange-200 text-orange-700 rounded transition-colors text-[12px]",children:[e.jsx(F,{className:"w-4 h-4"}),"예약 취소"]}),e.jsxs("button",{onClick:u,className:"flex items-center justify-center gap-1 px-4 py-2 bg-blue-100 hover:bg-blue-200 text-blue-700 rounded transition-colors text-[12px]",children:[e.jsx(P,{className:"w-4 h-4"}),"수정"]})]}),t.status==="sent"&&e.jsxs("button",{onClick:u,className:"flex items-center justify-center gap-1 px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded transition-colors text-[12px]",children:[e.jsx(T,{className:"w-4 h-4"}),"상세보기/복제"]}),(t.status==="draft"||t.status==="scheduled")&&e.jsxs("button",{onClick:h,className:"ml-auto flex items-center justify-center gap-1 px-4 py-2 bg-red-100 hover:bg-red-200 text-red-700 rounded transition-colors text-[12px]",children:[e.jsx(Q,{className:"w-4 h-4"}),"삭제"]})]})]})}function ue({push:t,onClose:x,onSuccess:u}){const h=!!t,[g,b]=p.useState(!1),[o,r]=p.useState(!1),l=(t==null?void 0:t.target_config)||{mode:"all"},[c,m]=p.useState({title:(t==null?void 0:t.title)||"",body:(t==null?void 0:t.body)||"",image_url:(t==null?void 0:t.image_url)||"",target_config:l,category:(t==null?void 0:t.category)||"",link_url:(t==null?void 0:t.link_url)||"",link_type:(t==null?void 0:t.link_type)||"none",scheduled_at:t!=null&&t.scheduled_at?t.scheduled_at.slice(0,16):"",status:(t==null?void 0:t.status)||"draft"});function d(s){const f=oe[s];m(f?N=>({...N,category:s,link_type:"internal",link_url:f.url}):N=>({...N,category:s,link_type:"none",link_url:""}))}async function n(s){var N;const f=(N=s.target.files)==null?void 0:N[0];if(f)try{r(!0);const S=await Y.uploadPushImage(f);m({...c,image_url:S}),y.success("이미지가 업로드되었습니다")}catch{y.error("이미지 업로드에 실패했습니다")}finally{r(!1)}}async function a(s){if(s.preventDefault(),!c.title||!c.body){y.error("제목과 내용을 입력하세요");return}try{b(!0);const f={...c,scheduled_at:c.scheduled_at?new Date(c.scheduled_at).toISOString():void 0,status:c.scheduled_at?"scheduled":"draft",created_by:"admin"};h?(await _.updatePush(t.id,f),y.success("푸시가 수정되었습니다")):(await _.createPush(f),y.success("푸시가 작성되었습니다")),u()}catch{y.error(h?"수정에 실패했습니다":"작성에 실패했습니다")}finally{b(!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.jsxs("div",{className:"p-4 border-b border-gray-200 flex items-center justify-between",children:[e.jsx("h2",{className:"text-[16px] font-bold text-gray-900",children:h?"푸시 수정":"푸시 작성"}),e.jsx("button",{onClick:x,className:"p-1 text-gray-400 hover:text-gray-600",children:e.jsx(V,{className:"w-5 h-5"})})]}),e.jsxs("form",{onSubmit:a,className:"p-4 space-y-4",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.title,onChange:s=>m({...c,title:s.target.value}),className:"compact-input",placeholder:"예: 주일 예배 안내",maxLength:50,required:!0}),e.jsxs("div",{className:"text-[10px] text-gray-500 mt-0.5 text-right",children:[c.title.length,"/50"]})]}),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.body,onChange:s=>m({...c,body:s.target.value}),className:"compact-input",rows:4,placeholder:"푸시 알림 내용을 입력하세요",maxLength:200,required:!0}),e.jsxs("div",{className:"text-[10px] text-gray-500 mt-0.5 text-right",children:[c.body.length,"/200"]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"이미지 (선택)"}),e.jsxs("div",{className:"space-y-2",children:[c.image_url&&e.jsxs("div",{className:"relative h-40 bg-gray-100 rounded overflow-hidden",children:[e.jsx("img",{src:c.image_url,alt:"미리보기",className:"w-full h-full object-cover"}),e.jsx("button",{type:"button",onClick:()=>m({...c,image_url:""}),className:"absolute top-2 right-2 p-1 bg-red-500 text-white rounded hover:bg-red-600",children:e.jsx(F,{className:"w-4 h-4"})})]}),e.jsxs("label",{className:"flex items-center justify-center gap-2 px-4 py-2 border-2 border-dashed border-gray-300 rounded hover:border-blue-500 cursor-pointer transition-colors",children:[e.jsx(le,{className:"w-4 h-4 text-gray-400"}),e.jsx("span",{className:"text-[13px] text-gray-600",children:o?"업로드 ì
7¤‘...":"이미지 선택"}),e.jsx("input",{type:"file",accept:"image/*",onChange:n,className:"hidden",disabled:o})]})]})]}),e.jsx(ne,{value:c.target_config,onChange:s=>m({...c,target_config:s})}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"알림 카테고리"}),e.jsx("select",{value:c.category,onChange:s=>d(s.target.value),className:"compact-input",children:de.map(s=>e.jsx("option",{value:s.value,children:s.label},s.value))}),e.jsx("p",{className:"text-[10px] text-gray-500 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.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"링크 타입"}),e.jsxs("select",{value:c.link_type,onChange:s=>m({...c,link_type:s.target.value}),className:"compact-input",children:[e.jsx("option",{value:"none",children:"링크 없음"}),e.jsx("option",{value:"internal",children:"내부 링크"}),e.jsx("option",{value:"external",children:"외부 링크"})]})]}),c.link_type!=="none"&&e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"링크 URL"}),e.jsx("input",{type:"text",value:c.link_url,onChange:s=>m({...c,link_url:s.target.value}),className:"compact-input",placeholder:c.link_type==="internal"?"/bulletin":"https://example.com"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[12px] font-medium text-gray-700 mb-1",children:"예약 발송 (선택)"}),e.jsx("input",{type:"datetime-local",value:c.scheduled_at,onChange:s=>m({...c,scheduled_at:s.target.value}),className:"compact-input"}),e.jsx("p",{className:"text-[10px] text-gray-500 mt-1",children:"비워두면 임시저장됩니다"})]}),e.jsxs("div",{className:"flex gap-2 pt-2",children:[e.jsx("button",{type:"button",onClick:x,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:g||o,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:g?"처리 중...":h?"수정":c.scheduled_at?"예약":"저장"})]})]})]})})}export{we as PushManagementPage};

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.