PageSourceSearch

https://app.backstagebaton.com/app/assets/AttendanceReportPage-ClbPXEdf.js

js backstagebaton.com collected 2026-10-07 08:49:21 UTC 16,735 bytes, 15 lines download raw bytes

1import{h as W,c as Z,r as l,z as ee,j as e,B as $,S as U,A as te,d as k,C as re,l as se,o as _,t as B}from"./index-_aijvlUq.js";import{P as ae}from"./PageHeader-DCofr6qo.js";import{I as H}from"./Input-ysTyZFq5.js";import{E as ne}from"./EmptyState-CPGKooqj.js";import{e as le}from"./ensembles-WGlB365j.js";const de=[{key:"",label:"All events"},{key:"rehearsal",label:"Rehearsals"},{key:"performance",label:"Performances"}],P={excluded:"excused absences don't count either way",counts_against:"excused absences count as a miss",counts_for:"excused absences count as attended"};function ce(r){return re(new Date(Date.now()-r*864e5))}function oe(){return se()}function ie(r,n){return r==null?"bg-gray-100 text-gray-500":r>=n?"bg-green-100 text-green-700":r>=n-15?"bg-amber-100 text-amber-700":"bg-red-100 text-red-700"}function C(r){let n=String(r??"");return/^[=+\-@]/.test(n)&&(n=`'${n}`),/[",\r\n]/.test(n)?`"${n.replace(/"/g,'""')}"`:n}function ge(){const r=W(t=>t.currentOrg),{showError:n}=Z(),[c,o]=l.useState(ce(90)),[i,y]=l.useState(oe()),[m,s]=l.useState(""),[j,G]=l.useState(""),[S,V]=l.useState("active"),[T,Y]=l.useState([]),[a,K]=l.useState(null),[F,I]=l.useState(!1),[x,X]=l.useState({key:"lastName",dir:1}),[L,E]=l.useState(null),[M,O]=l.useState({});l.useEffect(()=>{r?.id&&le.list(r.id).then(t=>Y(Array.isArray(t)?t:[])).catch(ee("ensemble filter options; the report still runs"))},[r?.id]);const R=l.useRef("");async function D(){I(!0),E(null),O({}),R.current=`${c}|${i}|${m}|${j}|${S}`;try{const t=await _.getReport({start:c,end:i,ensembleId:m||void 0,eventType:j||void 0,memberStatus:S});K(t)}catch(t){n(t?.message||B("attendance report"))}finally{I(!1)}}l.useEffect(()=>{r?.id&&D()},[r?.id]);async function J(t){if(L===t){E(null);return}if(E(t),!M[t]){const p=R.current;try{const g=await _.getReportMemberDetail(t,{start:c,end:i,ensembleId:m||void 0,eventType:j||void 0});if(R.current!==p)return;O(u=>({...u,[t]:{events:g.events||[],reportedAbsences:g.reportedAbsences||[]}}))}catch{n(B("member detail"))}}}const f=l.useMemo(()=>{const t=[...a?.members||[]],{key:p,dir:g}=x;return t.sort((u,d)=>{const N=u[p],w=d[p];return N==null?1:w==null?-1:typeof N=="string"?N.localeCompare(w)*g:(N-w)*g}),t},[a,x]);function h(t){X(p=>p.key===t?{key:t,dir:-p.dir}:{key:t,dir:t==="lastName"?1:-1})}function Q(){if(!a)return;const p=[["Last name","First name","Ensembles","Events held","Events marked","Present","Excused","Absent","Reported out","Attendance %","Performance %","Rehearsal %"].join(",")];for(const d of f)p.push([C(d.lastName),C(d.firstName),C(d.ensembles.join("; ")),d.held,d.marked,d.present,d.excused,d.absent,d.reportedOut??0,d.rate??"",d.performance?.rate??"",d.rehearsal?.rate??""].join(","));const g=new Blob([p.join(`
2`)],{type:"text/csv;charset=utf-8"}),u=document.createElement("a");u.href=URL.createObjectURL(g),u.download=`attendance-report_${c}_${i}.csv`,u.click(),URL.revokeObjectURL(u.href)}const A=a?.policy?.targetPercent??50,v=a?.tracksAttendance!==!1,z=!!a?.collectsRsvps;return e.jsxs("div",{className:"px-4 md:px-0",children:[e.jsxs("div",{className:"print:hidden",children:[e.jsx(ae,{title:"Attendance Report",subtitle:"Per-member attendance over any date range — for board packets, standing reviews, and stipend math.",breadcrumbs:[{label:"Reports",to:"/reports"},{label:"Attendance Report"}],actions:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx($,{variant:"secondary",size:"md",onClick:Q,disabled:!a||f.length===0,children:"Export CSV"}),e.jsx($,{variant:"secondary",size:"md",onClick:()=>window.print(),disabled:!a,children:"Print"})]})}),e.jsxs("div",{className:"bg-white rounded-xl border border-gray-200 p-4 mb-4 flex flex-wrap items-end gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1",htmlFor:"rep-start",children:"From"}),e.jsx(H,{id:"rep-start",type:"date",value:c,onChange:t=>o(t.target.value)})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1",htmlFor:"rep-end",children:"To"}),e.jsx(H,{id:"rep-end",type:"date",value:i,onChange:t=>y(t.target.value)})]}),T.length>1&&e.jsxs("div",{className:"w-48",children:[e.jsx("label",{className:"block text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1",htmlFor:"rep-ens",children:"Ensemble"}),e.jsx(U,{id:"rep-ens",value:m,onChange:t=>s(t.target.value),placeholder:"All ensembles",options:T.map(t=>({value:t.id,label:t.name}))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1",children:"Events"}),e.jsx("div",{className:"flex items-center gap-1",role:"group","aria-label":"Filter by event type",children:de.map(t=>e.jsx("button",{type:"button",onClick:()=>G(t.key),"aria-pressed":j===t.key,className:`px-3 py-2 rounded-full text-sm font-medium border transition-colors ${j===t.key?"bg-band-600 border-band-600 text-white":"bg-white border-gray-200 text-gray-600 hover:border-band-400 hover:text-band-700"}`,children:t.label},t.key))})]}),e.jsxs("div",{className:"w-36",children:[e.jsx("label",{className:"block text-xs font-semibold text-gray-500 uppercase tracking-wide mb-1",htmlFor:"rep-status",children:"Members"}),e.jsx(U,{id:"rep-status",value:S,onChange:t=>V(t.target.value),options:[{value:"active",label:"Active"},{value:"all",label:"All statuses"}]})]}),e.jsx($,{variant:"primary",size:"md",onClick:D,isLoading:F,children:"Run report"})]})]}),a&&te.createPortal(e.jsxs("div",{id:"attendance-report-print","aria-hidden":"true",children:[e.jsxs("h1",{children:[r?.name," — Attendance Report"]}),e.jsxs("p",{className:"arp-sub",children:[k(a.start)," to ",k(a.end)," · target ",A,"% 
2· ",P[a.policy.excusedPolicy]||a.policy.excusedPolicy]}),e.jsxs("table",{children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Member"}),e.jsx("th",{children:"Ensembles"}),e.jsx("th",{children:"Held"}),e.jsx("th",{children:"Marked"}),e.jsx("th",{children:"Present"}),e.jsx("th",{children:"Excused"}),e.jsx("th",{children:"Absent"}),e.jsx("th",{children:"%"}),e.jsx("th",{children:"Perf %"}),e.jsx("th",{children:"Reh %"})]})}),e.jsx("tbody",{children:f.map(t=>e.jsxs("tr",{children:[e.jsxs("td",{children:[t.lastName,", ",t.firstName]}),e.jsx("td",{children:t.ensembles.join(", ")}),e.jsx("td",{children:t.held}),e.jsx("td",{children:t.marked}),e.jsx("td",{children:t.present}),e.jsx("td",{children:t.excused}),e.jsx("td",{children:t.absent}),e.jsx("td",{children:t.rate!==null?`${t.rate}%`:"—"}),e.jsxs("td",{children:[t.performance?.rate??"—",t.performance?.rate!==null&&t.performance?.rate!==void 0?"%":""]}),e.jsxs("td",{children:[t.rehearsal?.rate??"—",t.rehearsal?.rate!==null&&t.rehearsal?.rate!==void 0?"%":""]})]},t.memberId))})]}),e.jsx("p",{className:"arp-foot",children:"Held = scheduled events in range for the member’s ensembles (plus any event they were recorded at). Marked = events with a recorded status. Membership is as of the report date."})]}),document.body),e.jsx("style",{children:`
3        #attendance-report-print { display: none; }
4        @media print {
5          body > *:not(#attendance-report-print) { display: none !important; }
6          #attendance-report-print { display: block; font: 11px/1.4 -apple-system, Segoe UI, sans-serif; color: #111; }
7          #attendance-report-print h1 { font-size: 16px; margin: 0 0 2px; }
8          #attendance-report-print .arp-sub { margin: 0 0 10px; color: #444; }
9          #attendance-report-print table { width: 100%; border-collapse: collapse; }
10          #attendance-report-print th, #attendance-report-print td { border: 1px solid #ccc; padding: 3px 6px; text-align: left; }
11          #attendance-report-print th { background: #f2f2f2; }
12          #attendance-report-print tr { break-inside: avoid; }
13          #attendance-report-print .arp-foot { margin-top: 8px; color: #555; font-size: 9px; }
14        }
15      `}),a?e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"text-sm text-gray-600 mb-3",children:[v?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"font-semibold text-gray-800",children:a.totals.eventsHeld})," events held"," ","(",a.totals.performances," performance",a.totals.performances===1?"":"s",", ",a.totals.rehearsals," rehearsal",a.totals.rehearsals===1?"":"s",")",e.jsx("span",{className:"text-gray-300",children:" · "}),"target ",e.jsxs("span",{className:"font-semibold text-gray-800",children:[A,"%"]}),e.jsx("span",{className:"text-gray-300",children:" · "}),P[a.policy.excusedPolicy]||a.policy.excusedPolicy]}):e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"font-semibold text-gray-800",children:a.totals.reportedOut})," reported absence",a.totals.reportedOut===1?"":"s"]}),a.requestedEnd>a.end&&e.jsxs("span",{className:"text-gray-400",children:[" · future dates excluded (through ",k(a.end),")"]})]}),!v&&e.jsx("div",{className:"mb-3 rounded-lg border border-amber-200 bg-amber-50 p-3 text-sm text-amber-900",children:"Your band doesn’t track attendance, so there are no present/absent records to report. Below is what members told you in advance — the nights they said they’d miss. To record who actually attended, turn on attendance tracking in Settings → Organization."}),f.length===0?e.jsx(ne,{title:"No members match",description:"Adjust the filters and run the report again."}):e.jsx("div",{className:"bg-white rounded-xl border border-gray-200 overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"text-left text-gray-400 uppercase tracking-wide text-xs border-b border-gray-200",children:[e.jsx(b,{className:"px-4 py-2.5",label:"Member",k:"lastName",sort:x,onSort:h,align:"left"}),e.jsx("th",{className:"px-2 py-2.5 font-semibold",children:"Ensembles"}),v&&e.jsxs(e.Fragment,{children:[e.jsx(b,{className:"px-2 py-2.5",label:"Held",k:"held",
15sort:x,onSort:h,title:"Valid events scheduled in the range for the member's ensembles"}),e.jsx(b,{className:"px-2 py-2.5",label:"Marked",k:"marked",sort:x,onSort:h,title:"Events where this member's attendance was recorded"}),e.jsx(b,{className:"px-2 py-2.5",label:"Present",k:"present",sort:x,onSort:h}),e.jsx(b,{className:"px-2 py-2.5",label:"Excused",k:"excused",sort:x,onSort:h}),e.jsx(b,{className:"px-2 py-2.5",label:"Absent",k:"absent",sort:x,onSort:h})]}),z&&e.jsx(b,{className:"px-2 py-2.5",label:"Reported out",k:"reportedOut",sort:x,onSort:h,title:"Events the member told you in advance they would miss. A count, not a rate — it has no denominator."}),v&&e.jsxs(e.Fragment,{children:[e.jsx(b,{className:"px-2 py-2.5",label:"%",k:"rate",sort:x,onSort:h}),e.jsx("th",{className:"px-2 py-2.5 font-semibold text-center",children:"Perf %"}),e.jsx("th",{className:"px-4 py-2.5 font-semibold text-center",children:"Reh %"})]})]})}),e.jsx("tbody",{className:"divide-y divide-gray-100",children:f.map(t=>e.jsx(xe,{m:t,target:A,tracksAttendance:v,showReportedOut:z,open:L===t.memberId,events:M[t.memberId],onToggle:()=>J(t.memberId)},t.memberId))})]})}),e.jsxs("p",{className:"mt-2 text-[11px] text-gray-400",children:["Held = valid scheduled events in the range for the member’s ensembles, plus any event they were recorded at (records outlast ensemble moves). Marked = events with a recorded status for the member. Reported out = events the member told you in advance they’d miss (their own reply, a leader recording a phone call, or an SMS) — a count, never folded into the percentage. Ensemble membership is as of today. Percentages use your organization’s attendance rules (",P[a.policy.excusedPolicy],"); unmarked events are shown, never assumed."]})]}):F?e.jsx("p",{className:"text-sm text-gray-400",children:"Loading…"}):null]})}function b({className:r="",label:n,k:c,sort:o,onSort:i,title:y,align:m="center"}){const s=o.key===c;return e.jsx("th",{className:`${r} font-semibold ${m==="left"?"":"text-center"}`,"aria-sort":s?o.dir===1?"ascending":"descending":void 0,title:y,children:e.jsxs("button",{type:"button",onClick:()=>i(c),className:"uppercase tracking-wide font-semibold hover:text-band-700 focus:outline-none focus:ring-2 focus:ring-band-500 rounded px-0.5",children:[n,s?o.dir===1?" ▲":" ▼":""]})})}const q={present:"bg-green-100 text-green-700",absent:"bg-red-100 text-red-700",excused:"bg-amber-100 text-amber-700",unmarked:"bg-gray-100 text-gray-400"};function xe({m:r,target:n,open:c,events:o,onToggle:i,tracksAttendance:y=!0,showReportedOut:m=!1}){return e.jsxs(e.Fragment,{children:[e.jsxs("tr",{className:"hover:bg-gray-50 cursor-pointer",onClick:i,children:[e.jsx("td",{className:"px-4 py-2.5 font-medium text-gray-800 whitespace-nowrap",children:e.jsxs("button",{type:"button","aria-expanded":c,onClick:s=>{s.stopPropagation(),i()},className:"text-left font-medium text-gray-800 hover:text-band-700 focus:outline-none focus:ring-2 focus:ring-band-500 rounded",children:[e.jsx("span",{className:`inline-block mr-1.5 text-gray-400 transition-transform ${c?"rotate-90":""}`,"aria-hidden":"true",children:"▸"}),r.lastName,", ",r.firstName]})}),e.jsx("td",{className:"px-2 py-2.5 text-gray-500 text-xs",children:r.ensembles.join(", ")}),y&&e.jsxs(e.Fragment,{children:[e.jsx("td",{className:"px-2 py-2.5 text-center text-gray-700",children:r.held}),e.jsxs("td",{className:"px-2 py-2.5 text-center text-gray-700",children:[r.marked,r.marked<r.held&&e.jsxs("span",{className:"text-gray-300",children:[" / ",r.held]})]}),e.jsx("td",{className:"px-2 py-2.5 text-center text-gray-700",children:r.present}),e.jsx("td",{className:"px-2 py-2.5 text-center text-gray-700",children:r.excused}),e.jsx("td",{className:"px-2 py-2.5 text-center text-gray-700",children:r.absent})]}),m&&e.jsx("td",{className:"px-2 py-2.5 text-center text-gray-700",children:r.reportedOut??0}),y&&e.jsxs(e.Fragment,{children:[e.jsx("td",{className:"px-2 py-2.5 text-center",children:e.jsx("span",{className:`inline-flex items-center px-1.5 py-0.5 rounded text-xs font-semibold ${ie(r.rate,n)}`,children:r.rate!==null?`${r.rate}%`:"—"})}),e.jsx("td",{className:"px-2 py-2.5 text-center text-gray-500",children:r.performance?.rate!==null&&r.performance?.rate!==void 0?`${r.performance.rate}%`:"—"}),e.jsx("td",{className:"px-4 py-2.5 text-center text-gray-500",children:r.rehearsal?.rate!==null&&r.rehearsal?.rate!==void 0?`${r.rehearsal.rate}%`:"—"})]})]}),c&&e.jsx("tr",{className:"print:hidden",children:e.jsx("td",{colSpan:12,className:"px-8 py-3 bg-gray-50",children:o?(o.events||[]).length===0&&(o.reportedAbsences||[]).length===0?e.jsx("p",{className:"text-xs text-gray-500",children:"No events in this range."}):e.jsxs("ul",{className:"space-y-1",children:[(o.events||[]).map(s=>e.jsxs("li",{className:"flex items-center gap-3 text-xs",children:[e.jsx("span",{className:"w-24 text-gray-600 whitespace-nowrap",children:k(s.date)}),e.jsx("span",{className:`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium border ${s.eventType==="performance"?"bg-blue-100 text-blue-700 border-blue-200":"bg-gray-100 text-gray-600 border-gray-200"}`,children:s.eventType==="performance"?"Concert":"Rehearsal"}),e.jsx("span",{className:"text-gray-700 truncate",children:s.title}),e.jsx("span",{className:`ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold ${q[s.status]||q.unmarked}`,children:s.status==="unmarked"?"not marked":s.status}),s.note&&e.jsxs("span",{className:"text-gray-400 italic truncate max-w-[200px]",children:["“",s.note,"”"]})]},`${s.eventType}:${s.eventId}`)),(o.reportedAbsences||[]).length>0&&e.jsx("li",{className:"pt-2 mt-1 border-t border-gray-200 text-[10px] font-semibold uppercase tracking-wide text-gray-400",children:"Reported out in advance"}),(o.reportedAbsences||[]).map(s=>e.jsxs("li",{className:"flex items-center gap-3 text-xs",children:[e.jsx("span",{className:"w-24 text-gray-600 whitespace-nowrap",children:k(s.date)}),e.jsx("span",{className:`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium border ${s.eventType==="performance"?"bg-blue-100 text-blue-700 border-blue-200":"bg-gray-100 text-gray-600 border-gray-200"}`,children:s.eventType==="performance"?"Concert":"Rehearsal"}),e.jsx("span",{className:"text-gray-700 truncate",children:s.title}),e.jsx("span",{className:"ml-auto inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold bg-red-100 text-red-700",children:"said out"}),s.reason&&e.jsxs("span",{className:"text-gray-400 italic truncate max-w-[200px]",children:["“",s.reason,"”"]}
15)]},`out:${s.eventType}:${s.eventId}`))]}):e.jsx("p",{className:"text-xs text-gray-400",children:"Loading…"})})})]})}export{ge as AttendanceReportPage};

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.