PageSourceSearch

https://seodoon-church-admin.web.app/assets/DonorStatsPage-BA0i7PIe.js

js seodoon-church-admin.web.app collected 2026-10-03 12:37:32 UTC 16,843 bytes, 1 lines download raw bytes

1import{j as e,u as j,w as R}from"./vendor-data-Z5doMMu-.js";import{r as i}from"./vendor-react-zNM0AU50.js";import{u as te,m as P}from"./index-D1K3lVhu.js";import{d as ae}from"./donationReceiptService-CQSRXCTG.js";import{z as F,R as M,n as re,H as Y,K as ne,g as le,D as H,A as U,ad as ce,f as ie,bN as oe,c as me,s as de}from"./vendor-ui-CW5bJ11U.js";import"./vendor-firebase-Da9KY1q_.js";import"./vendor-charts-08G1rM8P.js";function m(_){return new Intl.NumberFormat("ko-KR").format(_)}const w=["#3B82F6","#10B981","#F59E0B","#EF4444","#8B5CF6","#EC4899","#06B6D4","#84CC16","#F97316","#6366F1"];function Ne({embedded:_=!1}){const{user:C}=te(),z=(C==null?void 0:C.churchId)||"seodoon-church",[h,K]=i.useState(new Date().getFullYear().toString()),[E,T]=i.useState(!1),[d,W]=i.useState([]),[O,A]=i.useState([]),[N,q]=i.useState(""),[S,G]=i.useState(null),[b,k]=i.useState("individual"),[y,J]=i.useState([]),[u,B]=i.useState("amount"),[g,$]=i.useState("desc"),Q=Array.from({length:5},(s,a)=>(new Date().getFullYear()-a).toString());i.useEffect(()=>{L()},[h]),i.useEffect(()=>{if(!N.trim())A(d);else{const s=N.toLowerCase();A(d.filter(a=>a.memberName.toLowerCase().includes(s)))}},[N,d]);const L=async()=>{try{T(!0);const s=await ae.getDonorListByYear(z,h);W(s),A(s),await V(s)}catch(s){console.error("데이터 로드 실패:",s),F.error("데이터를 불러올 수 없습니다")}finally{T(!1)}},V=async s=>{try{const a=new Map;for(const t of s){if(!t.memberId)continue;const n=(await P.searchMembers(t.memberName)).find(f=>f.id===t.memberId);let o=t.memberId,v=`${t.memberName} 가정`;if(n!=null&&n.spouse){const p=(await P.searchMembers(n.spouse)).find(se=>se.name===n.spouse);p&&p.id<t.memberId&&(o=p.id,v=`${p.name} 가정`)}const x=a.get(o);x?(x.members.push({memberId:t.memberId,memberName:t.memberName,totalAmount:t.totalAmount,donationCount:t.donationCount}),x.totalAmount+=t.totalAmount,x.totalDonationCount+=t.donationCount):a.set(o,{familyId:o,familyName:v,members:[{memberId:t.memberId,memberName:t.memberName,totalAmount:t.totalAmount,donationCount:t.donationCount}],totalAmount:t.totalAmount,totalDonationCount:t.donationCount})}const r=Array.from(a.values()).sort((t,l)=>l.totalAmount-t.totalAmount);J(r)}catch(a){console.error("가정별 통계 계산 실패:",a)}},I=i.useMemo(()=>{const s=[...O];return s.sort((a,r)=>{let t=0;switch(u){case"amount":t=a.totalAmount-r.totalAmount;break;case"name":t=a.memberName.localeCompare(r.memberName);break;case"count":t=a.donationCount-r.donationCount;break}return g==="desc"?-t:t}),s},[O,u,g]),c=i.useMemo(()=>{const s={},a={};let r=0,t=0;return d.forEach(l=>{r+=l.totalAmount,t+=l.donationCount,Object.entries(l.byType).forEach(([n,o])=>{s[n]=(s[n]||0)+o}),Object.entries(l.byMonth).forEach(([n,o])=>{a[n]=(a[n]||0)+o})}),{byType:s,byMonth:a,totalAmount:r,totalCount:t,donorCount:d.length}},[d]),X=s=>{G(S===s?null:s)},D=s=>{u===s?$(g==="desc"?"asc":"desc"):(B(s),$("desc"))},Z=()=>{const s=I.map((t,l)=>({순번:l+1,성명:t.memberName,연간헌금액:t.totalAmount,헌금횟수:t.donationCount,...Object.fromEntries(Object.entries(t.byType).map(([n,o])=>[n,o]))})),a=j.json_to_sheet(s),r=j.book_new();j.book_append_sheet(r,a,"개인별헌금"),R(r,`개인별헌금통계_${h}.xlsx`),F.success("엑셀 내보내기 완료")},ee=()=>{const s=y.map((t,l)=>({순번:l+1,가정명:t.familyName,구성원:t.members.map(n=>n.memberName).join(", "),연간헌금액:t.totalAmount,헌금횟수:t.totalDonationCount})),a=j.json_to_sheet(s),r=j.book_new();j.book_append_sheet(r,a,"가정별헌금"),R(r,`가정별헌금통계_${h}.xlsx`),F.success("엑셀 내보내기 완료")};return e.jsxs("div",{className:"p-6",children:[e.jsxs("div",{className:"mb-6",children:[e.jsx("h1",{className:"text-2xl font-bold text-gray-900",children:"헌금 통계"}),e.jsx("p",{className:"text-sm text-gray-500 mt-1",children:"개인별/가정별 헌금 현황을 조회합니다"})]}),e.jsx("div",{className:"bg-white rounded-lg shadow-sm border p-4 mb-6",children:e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-4",children:[e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-gray-500 mb-1",children:"연도"}),e.jsx("select",{value:h,onChange:s=>K(s.target.value),className:"px-3 py-2 border rounded-lg text-sm focus:ring-2 focus:ring-blue-500",children:Q.map(s=>e.jsxs("option",{value:s,children:[s,"년"]},s))})]}),e.jsxs("button",{onClick:L,className:"px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 rounded-lg flex items-center gap-1",children:[e.jsx(M,{className:"w-4 h-4"}),"새로고침"]})]}),e.jsxs("div",{className:"flex items-center gap-6 text-sm",children:[e.jsxs("div",{className:"text-center",children:[e.jsx("div",{className:"text-gray-500",children:"총 헌금자"}),e.jsxs("div",{className:"text-xl font-bold text-blue-600",children:[c.donorCount,"명"]})]}),e.jsxs("div",{className:"text-center",children:[e.jsx("div",{className:"text-gray-500",children:"총 헌금액"}),e.jsxs("div",{className:"text-xl font-bold text-green-600",children:[m(c.totalAmount),"원"]})]}),e.jsxs("div",{className:"text-center",children:[e.jsx("div",{className:"text-gray-500",children:"총 헌금 횟수"}),e.jsxs("div",{className:"text-xl font-bold text-purple-600",children:[c.totalCount,"회"]})]})]})]})}),e.jsxs("div",{className:"flex border-b mb-4",children:[e.jsxs("button",{onClick:()=>k("individual"),className:`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${b==="individual"?"border-blue-500 text-blue-600":"border-transparent text-gray-500 hover:text-gray-700"}`,children:[e.jsx(re,{className:"w-4 h-4 inline mr-1"}),"개인별 통계"]}),e.jsxs("button",{onClick:()=>k("family"),className:`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${b==="family"?"border-blue-500 text-blue-600":"border-transparent text-gray-500 hover:text-gray-700"}`,children:[e.jsx(Y,{className:"w-4 h-4 inline mr-1"}),"가정별 통계"]}),e.jsxs("button",{onClick:()=>k("stats"),className:`px-4 py-2 text-sm font-medium border-b-2 -mb-px ${b==="stats"?"border-blue-500 text-blue-600":"border-transparent text-gray-500 hover:text-gray-700"}`,children:[e.jsx(ne,{className:"w-4 h-4 inline mr-1"}),"종합 통계"]})]}),b==="individual"&&e.jsxs("div",{className:"bg-white rounded-lg shadow-sm border",children:[e.jsxs("div",{className:"p-4 border-b flex flex-wrap items-center justify-between gap-4",children:[e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("div",{className:"relative",children:[e.jsx(le,{className:"absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-4 h-4"}),e.jsx("input",{type:"text",placeholder:"이름 검색...",value:N,onChange:s=>q(s.target.value),className:"w-64 pl-9 pr-4 py-2 border rounded-lg text-sm focus:ring-2 focus:ring-blue-500"})]}),e.jsxs("select",{value:`${u}-${g}`,onChange:s=>{const[a,r]=s.target.value.split("-");B(a),$(r)},className:"px-3 py-2 border rounded-lg text-sm",children:[e.jsx("option",{value:"amount-desc",children:"금액 높은순"}),e.jsx("option",{value:"amount-asc",children:"금액 낮은순"}),e.jsx("option",{value:"name-asc",children:"이름순"}),e.jsx("option",{value:"count-desc",children:"횟수 많은순"})]})]}),e.jsxs("button",{onClick:Z,className:"px-4 py-2 bg-green-600 text-white text-sm rounded-lg hover:bg-green-700 flex items-center gap-2",children:[e.jsx(H,{className:"w-4 h-4"}),"엑셀 다운로드"]})]}),E?e.jsxs("div",{className:"p-8 text-center text-gray-500",children:[e.jsx(M,{className:"w-6 h-6 animate-spin mx-auto mb-2"}),"로딩 중..."]}):I.length===0?e.jsxs("div",{className:"p-8 text-center text-gray-500",children:[e.jsx(U,{className:"w-8 h-8 mx-auto mb-2 text-gray-400"}
1),N?"검색 결과가 없습니다":"헌금 내역이 없습니다"]}):e.jsxs("div",{className:"divide-y",children:[e.jsxs("div",{className:"grid grid-cols-12 gap-4 p-4 bg-gray-50 text-xs font-medium text-gray-500",children:[e.jsx("div",{className:"col-span-1",children:"순번"}),e.jsxs("div",{className:"col-span-3 cursor-pointer",onClick:()=>D("name"),children:["성명 ",u==="name"&&(g==="desc"?"▼":"▲")]}),e.jsxs("div",{className:"col-span-3 text-right cursor-pointer",onClick:()=>D("amount"),children:["연간 헌금액 ",u==="amount"&&(g==="desc"?"▼":"▲")]}),e.jsxs("div",{className:"col-span-2 text-right cursor-pointer",onClick:()=>D("count"),children:["횟수 ",u==="count"&&(g==="desc"?"▼":"▲")]}),e.jsx("div",{className:"col-span-2 text-right",children:"헌금 유형"}),e.jsx("div",{className:"col-span-1"})]}),I.map((s,a)=>e.jsxs("div",{className:"hover:bg-gray-50",children:[e.jsxs("div",{className:"grid grid-cols-12 gap-4 p-4 items-center",children:[e.jsx("div",{className:"col-span-1 text-sm text-gray-500",children:a+1}),e.jsx("div",{className:"col-span-3 font-medium text-gray-900",children:s.memberName}),e.jsxs("div",{className:"col-span-3 text-right font-bold text-gray-900",children:[m(s.totalAmount),"원"]}),e.jsxs("div",{className:"col-span-2 text-right text-gray-600",children:[s.donationCount,"회"]}),e.jsxs("div",{className:"col-span-2 text-right text-xs text-gray-500",children:[Object.keys(s.byType).length,"종"]}),e.jsx("div",{className:"col-span-1 text-right",children:e.jsx("button",{onClick:()=>X(s.memberId),className:"p-2 text-gray-400 hover:text-gray-600 rounded",children:S===s.memberId?e.jsx(ce,{className:"w-5 h-5"}):e.jsx(ie,{className:"w-5 h-5"})})})]}),S===s.memberId&&e.jsx("div",{className:"px-4 pb-4",children:e.jsxs("div",{className:"bg-gray-50 rounded-lg p-4",children:[e.jsxs("div",{className:"mb-4",children:[e.jsx("div",{className:"text-xs font-medium text-gray-500 mb-2",children:"헌금 유형별 합계"}),e.jsx("div",{className:"flex flex-wrap gap-2",children:Object.entries(s.byType).sort(([,r],[,t])=>t-r).map(([r,t])=>e.jsxs("span",{className:"px-3 py-1.5 bg-white rounded-lg text-sm border",children:[e.jsxs("span",{className:"text-gray-600",children:[r,":"]})," ",e.jsxs("span",{className:"font-medium",children:[m(t),"원"]})]},r))})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-xs font-medium text-gray-500 mb-2",children:"월별 내역"}),e.jsx("div",{className:"grid grid-cols-6 md:grid-cols-12 gap-2",children:Array.from({length:12},(r,t)=>{const l=`${h}-${String(t+1).padStart(2,"0")}`,n=s.byMonth[l]||0;return e.jsxs("div",{className:`rounded p-2 text-center text-xs ${n>0?"bg-blue-50 border border-blue-200":"bg-gray-100"}`,children:[e.jsxs("div",{className:"text-gray-500",children:[t+1,"월"]}),e.jsx("div",{className:`font-medium ${n>0?"text-blue-600":"text-gray-400"}`,children:n>0?m(n):"-"})]},l)})})]})]})})]},s.memberId))]})]}),b==="family"&&e.jsxs("div",{className:"bg-white rounded-lg shadow-sm border",children:[e.jsxs("div",{className:"p-4 border-b flex items-center justify-between",children:[e.jsxs("div",{className:"text-sm text-gray-500",children:["총 ",y.length,"가정"]}),e.jsxs("button",{onClick:ee,className:"px-4 py-2 bg-green-600 text-white text-sm rounded-lg hover:bg-green-700 flex items-center gap-2",children:[e.jsx(H,{className:"w-4 h-4"}),"엑셀 다운로드"]})]}),E?e.jsxs("div",{className:"p-8 text-center text-gray-500",children:[e.jsx(M,{className:"w-6 h-6 animate-spin mx-auto mb-2"}),"로딩 중..."]}):y.length===0?e.jsxs("div",{className:"p-8 text-center text-gray-500",children:[e.jsx(U,{className:"w-8 h-8 mx-auto mb-2 text-gray-400"}),"데이터가 없습니다"]}):e.jsxs("div",{className:"divide-y",children:[e.jsxs("div",{className:"grid grid-cols-12 gap-4 p-4 bg-gray-50 text-xs font-medium text-gray-500",children:[e.jsx("div",{className:"col-span-1",children:"순번"}),e.jsx("div",{className:"col-span-3",children:"가정명"}),e.jsx("div",{className:"col-span-3",children:"구성원"}),e.jsx("div",{className:"col-span-3 text-right",children:"연간 헌금액"}),e.jsx("div",{className:"col-span-2 text-right",children:"횟수"})]}),y.map((s,a)=>e.jsxs("div",{className:"grid grid-cols-12 gap-4 p-4 items-center hover:bg-gray-50",children:[e.jsx("div",{className:"col-span-1 text-sm text-gray-500",children:a+1}),e.jsx("div",{className:"col-span-3",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Y,{className:"w-4 h-4 text-gray-400"}),e.jsx("span",{className:"font-medium text-gray-900",children:s.familyName})]})}),e.jsx("div",{className:"col-span-3 text-sm text-gray-600",children:s.members.map((r,t)=>e.jsxs("span",{children:[r.memberName,t<s.members.length-1&&", "]},r.memberId))}),e.jsxs("div",{className:"col-span-3 text-right font-bold text-gray-900",children:[m(s.totalAmount),"원"]}),e.jsxs("div",{className:"col-span-2 text-right text-gray-600",children:[s.totalDonationCount,"회"]})]},s.familyId))]})]}),b==="stats"&&e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"bg-white rounded-lg shadow-sm border p-6",children:[e.jsxs("h3",{className:"font-bold text-gray-900 mb-4 flex items-center gap-2",children:[e.jsx(oe,{className:"w-5 h-5 text-blue-500"}),"헌금 유형별 통계"]}),e.jsxs("div",{className:"grid md:grid-cols-2 gap-6",children:[e.jsx("div",{className:"flex items-center justify-center",children:e.jsxs("div",{className:"relative w-48 h-48",children:[e.jsx("svg",{viewBox:"0 0 100 100",className:"w-full h-full -rotate-90",children:(()=>{const s=Object.entries(c.byType).sort(([,r],[,t])=>t-r);let a=0;return s.map(([r,t],l)=>{const n=c.totalAmount>0?t/c.totalAmount*100:0,o=a;a+=n;const v=40,x=2*Math.PI*v,f=`${n/100*x} ${x}`,p=-(o/100*x);return e.jsx("circle",{cx:"50",cy:"50",r:v,fill:"none",stroke:w[l%w.length],strokeWidth:"20",strokeDasharray:f,strokeDashoffset:p},r)})})()}),e.jsx("div",{className:"absolute inset-0 flex items-center justify-center",children:e.jsxs("div",{className:"text-center",children:[e.jsx("div",{className:"text-xs text-gray-500",children:"총액"}),e.jsx("div",{className:"text-sm font-bold",children:m(c.totalAmount)})]})})]})}),e.jsx("div",{className:"space-y-2",children:Object.entries(c.byType).sort(([,
1s],[,a])=>a-s).map(([s,a],r)=>{const t=c.totalAmount>0?(a/c.totalAmount*100).toFixed(1):"0";return e.jsxs("div",{className:"flex items-center justify-between p-2 rounded hover:bg-gray-50",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-3 h-3 rounded-full",style:{backgroundColor:w[r%w.length]}}),e.jsx("span",{className:"text-sm text-gray-700",children:s})]}),e.jsxs("div",{className:"text-right",children:[e.jsxs("div",{className:"text-sm font-medium",children:[m(a),"원"]}),e.jsxs("div",{className:"text-xs text-gray-500",children:[t,"%"]})]})]},s)})})]})]}),e.jsxs("div",{className:"bg-white rounded-lg shadow-sm border p-6",children:[e.jsxs("h3",{className:"font-bold text-gray-900 mb-4 flex items-center gap-2",children:[e.jsx(me,{className:"w-5 h-5 text-green-500"}),"월별 헌금 추이"]}),e.jsx("div",{className:"h-64",children:e.jsx("div",{className:"flex items-end justify-between h-full gap-2",children:Array.from({length:12},(s,a)=>{const r=`${h}-${String(a+1).padStart(2,"0")}`,t=c.byMonth[r]||0,l=Math.max(...Object.values(c.byMonth),1),n=t/l*100;return e.jsxs("div",{className:"flex-1 flex flex-col items-center",children:[e.jsx("div",{className:"w-full h-48 flex items-end",children:e.jsx("div",{className:"w-full bg-blue-500 rounded-t transition-all duration-300 hover:bg-blue-600",style:{height:`${n}%`,minHeight:t>0?"4px":"0"},title:`${a+1}월: ${m(t)}원`})}),e.jsxs("div",{className:"text-xs text-gray-500 mt-2",children:[a+1,"월"]}),e.jsx("div",{className:"text-xs font-medium text-gray-700",children:t>0?`${Math.round(t/1e4)}만`:"-"})]},r)})})})]}),e.jsxs("div",{className:"bg-white rounded-lg shadow-sm border p-6",children:[e.jsxs("h3",{className:"font-bold text-gray-900 mb-4 flex items-center gap-2",children:[e.jsx(de,{className:"w-5 h-5 text-purple-500"}),"상위 헌금자 (Top 10)"]}),e.jsx("div",{className:"space-y-2",children:d.slice(0,10).map((s,a)=>{var t;const r=c.totalAmount>0?(s.totalAmount/c.totalAmount*100).toFixed(1):"0";return e.jsxs("div",{className:"flex items-center gap-4 p-2 rounded hover:bg-gray-50",children:[e.jsx("div",{className:"w-6 h-6 rounded-full bg-gray-100 flex items-center justify-center text-xs font-medium text-gray-600",children:a+1}),e.jsxs("div",{className:"flex-1",children:[e.jsx("div",{className:"text-sm font-medium text-gray-900",children:s.memberName}),e.jsx("div",{className:"h-2 bg-gray-100 rounded-full mt-1",children:e.jsx("div",{className:"h-full bg-purple-500 rounded-full",style:{width:`${s.totalAmount/(((t=d[0])==null?void 0:t.totalAmount)||1)*100}%`}})})]}),e.jsxs("div",{className:"text-right",children:[e.jsxs("div",{className:"text-sm font-bold text-gray-900",children:[m(s.totalAmount),"원"]}),e.jsxs("div",{className:"text-xs text-gray-500",children:[r,"%"]})]})]},s.memberId)})})]})]})]})}export{Ne as DonorStatsPage};

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.