PageSourceSearch

https://seodoon-church-admin.web.app/assets/LostSheepDashboard-CBB0yJsv.js

js seodoon-church-admin.web.app collected 2026-10-03 12:36:26 UTC 15,407 bytes, 1 lines download raw bytes

1import{j as e}from"./vendor-data-Z5doMMu-.js";import{r}from"./vendor-react-zNM0AU50.js";import{m as _}from"./index-D1K3lVhu.js";import{M as B}from"./MemberCardModal-DsOGCHII.js";import{R as S,e as D,Q as T,U as H,ar as K,a1 as W,F as Y,bb as F,g as Q,f as U,a as q}from"./vendor-ui-CW5bJ11U.js";import"./vendor-firebase-Da9KY1q_.js";import"./vendor-charts-08G1rM8P.js";import"./visitService-DcwGr2oo.js";import"./prayerService-DgtIrt49.js";import"./faithJourneyService-B56_rhGw.js";const G={장기결석:"장기결석",미확인:"미확인(4주+)"},L=["별세","이사","이명"];function P(s){return s?`${s.getFullYear()}.${String(s.getMonth()+1).padStart(2,"0")}.${String(s.getDate()).padStart(2,"0")}`:"-"}function b({icon:s,label:i,value:d,sub:l,color:c}){return e.jsxs("div",{className:"bg-white rounded-xl border border-gray-100 p-4 flex items-start gap-3 shadow-sm",children:[e.jsx("div",{className:`p-2 rounded-lg ${c}`,children:e.jsx(s,{className:"w-5 h-5 text-white"})}),e.jsxs("div",{children:[e.jsx("p",{className:"text-xs text-gray-500",children:i}),e.jsx("p",{className:"text-2xl font-bold text-gray-900",children:d.toLocaleString()}),l&&e.jsx("p",{className:"text-xs text-gray-400",children:l})]})]})}function J({member:s,onSave:i,onMemberClick:d}){const[l,c]=r.useState(s.worship_status),[n,x]=r.useState(s.worship_status_note??""),[y,o]=r.useState(!1),[f,m]=r.useState(!1),[g,p]=r.useState(!1),N=l!==s.worship_status||n!==(s.worship_status_note??"");async function u(){m(!0);try{await i(s.id,l,n),p(!0),setTimeout(()=>p(!1),2e3)}finally{m(!1)}}const C=[{label:"출석",value:"출석",base:"text-emerald-600 border-emerald-300 hover:bg-emerald-50",active:"bg-emerald-500 text-white border-emerald-500"},{label:"장기결석",value:"장기결석",base:"text-red-600 border-red-300 hover:bg-red-50",active:"bg-red-500 text-white border-red-500"},{label:"미확인",value:"미확인",base:"text-amber-600 border-amber-300 hover:bg-amber-50",active:"bg-amber-400 text-white border-amber-400"}],v=L.includes(l);return e.jsxs("div",{className:"flex items-center gap-2 p-2.5 bg-white rounded-lg border border-gray-100 hover:border-gray-200",children:[e.jsxs("div",{className:"w-28 shrink-0",children:[e.jsxs("button",{className:"text-sm font-medium text-blue-600 hover:underline text-left",onClick:()=>d(s.id),children:[s.name,s.district_role==="구역장"&&e.jsx("span",{className:"ml-1 text-xs text-blue-400",children:"(구역장)"})]}),e.jsx("p",{className:"text-xs text-gray-400",children:s.position??"-"})]}),e.jsxs("div",{className:"flex gap-1 shrink-0",children:[C.map(a=>e.jsx("button",{onClick:()=>c(a.value),className:`px-2 py-1 text-xs rounded border font-medium transition-all ${l===a.value?a.active:a.base}`,children:a.label},a.value)),e.jsxs("div",{className:"relative",children:[e.jsxs("button",{onClick:()=>o(a=>!a),className:`px-2 py-1 text-xs rounded border font-medium transition-all flex items-center gap-0.5 ${v?"bg-slate-500 text-white border-slate-500":"text-slate-600 border-slate-300 hover:bg-slate-50"}`,children:[v?l:"사유"," ",e.jsx(U,{className:"w-3 h-3"})]}),y&&e.jsx("div",{className:"absolute z-10 top-full left-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg py-1 min-w-[80px]",children:L.map(a=>e.jsx("button",{onClick:()=>{c(a),o(!1)},className:"block w-full text-left px-3 py-1.5 text-xs text-gray-700 hover:bg-gray-50",children:a},a))})]})]}),e.jsx("input",{value:n,onChange:a=>x(a.target.value),placeholder:"현 상황 직접 입력 (예: 입원 중, 다음주 퇴원 예정)",className:"flex-1 min-w-0 px-3 py-1.5 text-sm border border-gray-200 rounded-md focus:outline-none focus:ring-1 focus:ring-blue-300"}),e.jsx("button",{onClick:u,disabled:f||!N&&!g,className:`shrink-0 px-3 py-1.5 text-sm font-medium rounded-md transition-all ${g?"bg-emerald-100 text-emerald-700 border border-emerald-200":N?"bg-green-600 text-white hover:bg-green-700":"bg-gray-100 text-gray-400 cursor-not-allowed"}`,children:f?e.jsx(S,{className:"w-3.5 h-3.5 animate-spin"}):g?"저장됨 ✓":"저장"})]})}function V({stat:s,onMemberClick:i,onSaveMember:d}){const[l,c]=r.useState(!1);return e.jsxs(e.Fragment,{children:[e.jsxs("tr",{className:"border-b border-gray-100 hover:bg-gray-50 cursor-pointer",onClick:()=>c(n=>!n),children:[e.jsx("td",{className:"px-4 py-2.5 text-sm text-gray-600",children:s.parish}),e.jsx("td",{className:"px-4 py-2.5 text-sm font-medium text-gray-800",children:e.jsxs("div",{className:"flex items-center gap-1",children:[l?e.jsx(U,{className:"w-4 h-4 text-gray-400"}):e.jsx(q,{className:"w-4 h-4 text-gray-400"}),s.district]})}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-gray-600",children:s.leaderName}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-center",children:s.total}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-center text-emerald-600 font-medium",children:s.attending}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-center",children:s.longAbsent>0?e.jsx("span",{className:"font-semibold text-red-600",children:s.longAbsent}):e.jsx("span",{className:"text-gray-400",children:"0"})}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-center",children:s.unconfirmed>0?e.jsx("span",{className:"font-medium text-amber-600",children:s.unconfirmed}):e.jsx("span",{className:"text-gray-400",children:"0"})}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-center text-gray-500",children:s.reasonal}),e.jsx("td",{className:"px-4 py-2.5 text-xs text-gray-400",children:P(s.lastUpdated)})]}),l&&e.jsx("tr",{className:"border-b border-blue-100",children:e.jsxs("td",{colSpan:9,className:"px-4 py-3 bg-blue-50/40",children:[e.jsxs("p",{className:"text-xs font-semibold text-gray-600 mb-2",children:[s.district," 구역원 ",s.members.length,"명 — 상태 입력"]}),e.jsx("div",{className:"space-y-1.5",children:s.members.map(n=>e.jsx(J,{member:n,onSave:d,onMemberClick:i},n.id))})]})})]})}function ie(){const[s,i]=r.useState(null),[d,l]=r.useState(!0),[c,n]=r.useState(null),[x,y]=r.useState("none"),[o,f]=r.useState(""),[m,g]=r.useState(""),[p,N]=r.useState(!1),[u,C]=r.useState(""),[v,a]=r.useState(null),[M,A]=r.useState(null);r.useEffect(()=>{k()},[]);async function k(){try{l(!0),n(null);const t=await _.getLostSheepStats();i(t)}catch{n("데이터를 불러오는 중 오류가 발생했습니다.")}finally{l(!1)}}async function $(t){a(t);const h=await _.getMember(t);A(h)}async function R(t,h,j){await _.updateWorshipStatus(t,h,j)}const E=r.useMemo(()=>
1{if(!s)return[];const t=[...s.districts];return x==="unconfirmed"?t.sort((h,j)=>j.unconfirmed-h.unconfirmed):x==="longAbsent"&&t.sort((h,j)=>j.longAbsent-h.longAbsent),t},[s,x]),w=r.useMemo(()=>s?s.lostSheep.filter(t=>!(o&&t.parish!==o||m==="장기결석"&&t.worship_status!=="장기결석"||m==="미확인"&&t.worship_status!=="미확인"||p&&!t.worship_status_note||u&&!t.name.includes(u))):[],[s,o,m,p,u]),I=r.useMemo(()=>s?Array.from(new Set(s.districts.map(t=>t.parish))).sort():[],[s]);if(d)return e.jsxs("div",{className:"flex items-center justify-center h-64",children:[e.jsx(S,{className:"w-6 h-6 text-gray-400 animate-spin"}),e.jsx("span",{className:"ml-2 text-gray-500",children:"데이터 집계 중..."})]});if(c)return e.jsxs("div",{className:"flex flex-col items-center justify-center h-64 gap-3",children:[e.jsx(D,{className:"w-8 h-8 text-red-400"}),e.jsx("p",{className:"text-red-600",children:c}),e.jsx("button",{onClick:k,className:"px-4 py-2 bg-blue-600 text-white rounded-lg text-sm",children:"다시 시도"})]});if(!s)return null;const O=s.totalMembers>0?Math.round(s.attending/s.totalMembers*100):0,z=s.totalMembers>0?Math.round(s.longAbsent/s.totalMembers*100):0;return e.jsxs("div",{className:"p-6 space-y-6",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("div",{className:"p-2 bg-red-100 rounded-lg",children:e.jsx(T,{className:"w-6 h-6 text-red-600"})}),e.jsxs("div",{children:[e.jsx("h1",{className:"text-xl font-bold text-gray-900",children:"교적부 재정비 — 잃은 양 찾기"}),e.jsx("p",{className:"text-sm text-gray-500",children:"구역을 클릭하면 출석 상태를 직접 입력할 수 있습니다"})]})]}),e.jsxs("button",{onClick:k,className:"flex items-center gap-1.5 px-3 py-1.5 text-sm text-gray-600 border border-gray-200 rounded-lg hover:bg-gray-50",children:[e.jsx(S,{className:"w-4 h-4"}),"새로고침"]})]}),e.jsxs("div",{className:"grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-6 gap-3",children:[e.jsx(b,{icon:H,label:"전체 대상",value:s.totalMembers,sub:"구역 배정 성도",color:"bg-gray-400"}),e.jsx(b,{icon:K,label:"출석",value:s.attending,sub:`${O}%`,color:"bg-emerald-500"}),e.jsx(b,{icon:D,label:"잃은 양",value:s.longAbsent,sub:`${z}%`,color:"bg-red-500"}),e.jsx(b,{icon:W,label:"미확인",value:s.unconfirmed,color:"bg-amber-400"}),e.jsx(b,{icon:Y,label:"사유(별세·이사·이명)",value:s.reasonal,color:"bg-slate-400"}),e.jsx(b,{icon:T,label:"미입력 구역",value:s.districtsWithNoInput,sub:`/ 전체 ${s.totalDistricts}구역`,color:"bg-orange-400"})]}),e.jsxs("div",{className:"bg-white rounded-xl border border-gray-100 shadow-sm overflow-hidden",children:[e.jsxs("div",{className:"px-4 py-3 border-b border-gray-100 flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"font-semibold text-gray-800 text-sm",children:"교구·구역별 집계"}),e.jsx("p",{className:"text-xs text-gray-400 mt-0.5",children:"구역 행을 클릭하면 전체 교인 상태를 입력할 수 있습니다"})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{onClick:()=>y(t=>t==="unconfirmed"?"none":"unconfirmed"),className:`flex items-center gap-1 px-2.5 py-1 text-xs rounded-md border ${x==="unconfirmed"?"bg-amber-50 border-amber-300 text-amber-700":"border-gray-200 text-gray-500"}`,children:[e.jsx(F,{className:"w-3 h-3"})," 미확인↑"]}),e.jsxs("button",{onClick:()=>y(t=>t==="longAbsent"?"none":"longAbsent"),className:`flex items-center gap-1 px-2.5 py-1 text-xs rounded-md border ${x==="longAbsent"?"bg-red-50 border-red-300 text-red-700":"border-gray-200 text-gray-500"}`,children:[e.jsx(F,{className:"w-3 h-3"})," 장기결석↑"]})]})]}),e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-left",children:[e.jsx("thead",{className:"bg-gray-50 text-xs text-gray-500 uppercase",children:e.jsxs("tr",{children:[e.jsx("th",{className:"px-4 py-2",children:"교구"}),e.jsx("th",{className:"px-4 py-2",children:"구역"}),e.jsx("th",{className:"px-4 py-2",children:"구역장"}),e.jsx("th",{className:"px-4 py-2 text-center",children:"총원"}),e.jsx("th",{className:"px-4 py-2 text-center text-emerald-600",children:"출석"}),e.jsx("th",{className:"px-4 py-2 text-center text-red-600",children:"장기결석"}),e.jsx("th",{className:"px-4 py-2 text-center text-amber-600",children:"미확인"}),e.jsx("th",{className:"px-4 py-2 text-center",children:"사유"}),e.jsx("th",{className:"px-4 py-2",children:"마지막 업데이트"})]})}),e.jsx("tbody",{children:E.map(t=>e.jsx(V,{stat:t,onMemberClick:$,onSaveMember:R},`${t.parish}||${t.district}`))})]})})]}),e.jsxs("div",{className:"bg-white rounded-xl border border-gray-100 shadow-sm overflow-hidden",children:[e.jsxs("div",{className:"px-4 py-3 border-b border-gray-100",children:[e.jsx("div",{className:"flex items-center justify-between mb-2",children:e.jsxs("h2",{className:"font-semibold text-gray-800 text-sm",children:["잃은 양 목록",e.jsxs("span",{className:"ml-2 text-red-600 font-bold",children:[w.length,"명"]}),w.length!==s.lostSheep.length&&e.jsxs("span",{className:"text-gray-400 font-normal text-xs ml-1",children:["/ 전체 ",s.lostSheep.length,"명"]})]})}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsxs("div",{className:"relative",children:[e.jsx(Q,{className:"absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-400"}),e.jsx("input",{className:"pl-7 pr-3 py-1.5 text-sm border border-gray-200 rounded-md focus:outline-none focus:ring-1 focus:ring-blue-300 w-32",placeholder:"이름 검색",value:u,onChange:t=>C(t.target.value)})]}),e.jsxs("select",{className:"px-2.5 py-1.5 text-sm border border-gray-200 rounded-md focus:outline-none",value:o,onChange:t=>f(t.target.value),children:[e.jsx("option",{value:"",children:"전체 교구"}),I.map(t=>e.jsx("option",{value:t,children:t},t))]}),e.jsxs("select",{className:"px-2.5 py-1.5 text-sm border border-gray-200 rounded-md focus:outline-none",value:m,onChange:t=>g(t.target.value),children:[e.jsx("option",{value:"",children:"전체 상태"}),e.jsx("option",{value:"장기결석",children:"장기결석만"}),e.jsx("option",{value:"미확인",children:"미확인(4주+)만"})]}),e.jsxs("label",{className:"flex items-center gap-1.5 text-sm text-gray-600 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:p,onChange:t=>N(t.target.checked),className:"rounded"}),"메모 있는 것만"]})]})]}),e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-left",children:[e.jsx("thead",{className:"bg-gray-50 text-xs text-gray-500 uppercase",children:e.jsxs("tr",{children:[e.jsx("th",{className:"px-4 py-2",children:"이름"}),e.jsx("th",{className:"px-4 py-2",children:"교구"}),e.jsx("th",{className:"px-4 py-2",children:"구역"}),e.jsx("th",{className:"px-4 py-2",children:"직분"}),e.jsx("th",{className:"px-4 py-2",children:"상태"}),e.jsx("th",{className:"px-4 py-2",children:"마지막 업데이트"}),e.jsx("th",{className:"px-4 py-2",children:"상황 메모"}),e.jsx("th",{className:"px-4 py-2",children:"담당 구역장"})]})}),e.jsx("tbody",{children:w.length===0?e.jsx("tr",{children:e.jsx("td",{colSpan:8,className:"px-4 py-8 text-center text-sm text-gray-400",children:"조건에 맞는 잃은 양이 없습니다."})}):w.map(t=>e.jsxs("tr",{className:"border-b border-gray-100 hover:bg-gray-50",children:[e.jsx("td",{className:"px-4 py-2.5",children:e.jsx("button",{className:"text-sm font-medium text-blue-600 hover:underline",onClick:()=>$(t.id),children:t.name})}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-gray-600",children:t.parish}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-gray-600",children:t.district}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-gray-500",children:t.position??"-"}),e.jsx("td",{className:"px-4 py-2.5",children:e.jsx("span",{className:`text-xs px-2 py-0.5 rounded-full font-medium ${t.worship_status==="장기결석"?"bg-red-100 text-red-700":"bg-amber-100 text-amber-700"}`,children:G[t.worship_status]??t.worship_status})}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-gray-400",children:P(t.worship_status_at)}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-gray-500 max-w-xs truncate",children:t.worship_status_note??e.jsx("span",{className:"text-gray-300",children:"-"})}),e.jsx("td",{className:"px-4 py-2.5 text-sm text-gray-600",children:t.leaderName})]},t.id))})]})})]}),M&&e.jsx(B,{member:M,onClose:()=>{A(null),a(null)}}),v&&!M&&e.jsx("div",{className:"fixed inset-0 bg-black/30 flex items-center justify-center z-50",children:e.jsxs("div",{className:"bg-white rounded-xl p-6 flex items-center gap-3",children:[e.jsx(S,{className:"w-5 h-5 animate-spin text-blue-500"}),e.jsx("span",{className:"text-sm text-gray-600",children:"교인 정보 불러오는 중..."})]})})]})}export{ie as LostSheepDashboard};

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.