// 웹 스크린 E — 참가자 명단 (노쇼 처리 · 신고)
function WScrAdmin({S,set,go}){
  const {useState, useEffect} = React;
  const [tab, setTab] = useState(0);
  const [reports, setReports] = useState([]);
  const [inquiries, setInquiries] = useState([]);
  const [sanctions, setSanctions] = useState([]);
  const [nsEmail, setNsEmail] = useState('');
  const [nsRecords, setNsRecords] = useState(null);
  const [allMeets, setAllMeets] = useState(null);
  const [evList, setEvList] = useState(null);
  const [evSel, setEvSel] = useState(null);
  const [evApps, setEvApps] = useState(null);
  const [evRoom, setEvRoom] = useState(null);
  const [evAliases, setEvAliases] = useState(null);
  const [evAnon, setEvAnon] = useState(true);
  const [evBusy, setEvBusy] = useState('');
  const [reasonView, setReasonView] = useState(null);
  const [sancEmail, setSancEmail] = useState('');
  const [sancType, setSancType] = useState('warning');
  const [sancReason, setSancReason] = useState('');
  const [loading, setLoading] = useState(false);
  const isAdmin = isAdminUser(S);
  const load = () => {
    setLoading(true);
    Promise.all([fetchReports(), fetchInquiries(), fetchActiveSanctions()]).then(([r,i,sn])=>{ setReports(r); setInquiries(i); setSanctions(sn); setLoading(false); });
  };
  useEffect(()=>{ if(isAdmin) load(); }, [isAdmin]);
  const loadEvents = () => { const sb=getSupabaseClient(); if(!sb) return; sb.from('events').select('id,title,when_label,city,mode,cap,created_at').order('created_at',{ascending:false}).limit(50).then(({data})=>setEvList(data||[])); };
  const loadEvApps = (eid) => { adminFetchEventApplicants(eid).then(setEvApps); fetchEventChatRoom(eid).then(r=>{ setEvRoom(r); if(r&&r.anonymous) fetchEventAliases(eid).then(setEvAliases); else setEvAliases(null); }); };
  useEffect(()=>{ if(isAdmin&&tab===4) loadEvents(); }, [isAdmin, tab]);
  useEffect(()=>{ if(evSel) loadEvApps(evSel.id); }, [evSel&&evSel.id]);
  const loadMeets = () => { const sb=getSupabaseClient(); if(!sb) return; sb.from('meetups').select('id,title,host_email,host_name,event_date,when_label,created_at,attendance_state').order('created_at',{ascending:false}).limit(100).then(({data})=>setAllMeets(data||[])); };
  useEffect(()=>{ if(isAdmin) loadMeets(); const f=()=>loadMeets(); document.addEventListener('mm:contentremoved', f); return ()=>document.removeEventListener('mm:contentremoved', f); }, [isAdmin]);
  const fmt = t => { try { return new Date(t).toLocaleString('ko-KR'); } catch(e) { return t||''; } };
  if (!isAdmin) {
    return <div className="wapp"><WNav S={S} go={go} set={set}/><div className="wmain" style={{maxWidth:480, paddingTop:60, textAlign:'center'}}>
      <p style={{fontSize:15, fontWeight:700, margin:'0 0 8px'}}>운영진 전용 페이지예요</p>
      <p className="meta">운영진으로 등록된 계정으로 로그인하면 이 화면 대신 관리 기능이 나타나요.</p>
    </div></div>;
  }
  const pendingReports = reports.filter(r=>r.status!=='처리완료');
  const pendingInquiries = inquiries.filter(i=>i.status!=='처리완료');
  return <div className="wapp"><WNav S={S} go={go} set={set}/>
    <div className="wmain" style={{maxWidth:860}}>
      <div className="row" style={{justifyContent:'space-between', marginBottom:16}}>
        <h2 style={{fontSize:20, margin:0}}>운영진 페이지</h2>
        <button style={{background:'none', border:'1px solid var(--c-line)', borderRadius:8, padding:'7px 14px', fontFamily:'inherit', fontSize:13, cursor:'pointer'}} onClick={load}>새로고침</button>
      </div>
      <p className="meta" style={{marginBottom:16}}>공지사항 작성, 게시물·댓글 삭제는 커뮤니티 화면에서 바로 하실 수 있어요(운영진 계정으로 로그인 중이면 삭제·공지작성 버튼이 자동으로 보여요). 여기서는 신고·문의만 모아서 처리해요.</p>
      <div className="row" style={{gap:8, marginBottom:20}}>
        <button className={'qtab'+(tab===0?' on':'')} onClick={()=>setTab(0)}>신고 내역 {pendingReports.length>0&&`(${pendingReports.length})`}</button>
        <button className={'qtab'+(tab===1?' on':'')} onClick={()=>setTab(1)}>문의 내역 {pendingInquiries.length>0&&`(${pendingInquiries.length})`}</button>
        <button className={'qtab'+(tab===2?' on':'')} onClick={()=>setTab(2)}>제재 관리 {sanctions.filter(x=>x.active).length>0&&`(${sanctions.filter(x=>x.active).length})`}</button>
        <button className={'qtab'+(tab===3?' on':'')} onClick={()=>setTab(3)}>모임 관리</button>
        <button className={'qtab'+(tab===4?' on':'')} onClick={()=>setTab(4)}>이벤트 관리</button>
      </div>
      {loading&&<p className="meta">불러오는 중...</p>}
      {!loading&&tab===0&&(reports.length===0?<p className="meta">접수된 신고가 없어요.</p>:reports.map(r=>{
        const hasEmail = !!r.target_email;
        const sanctionLabels = {warning:'경고', suspend_3d:'3일 정지', suspend_7d:'7일 정지', permanent:'영구 정지'};
        const doSanction = (type) => {
          if (!hasEmail) return;
          const confirmMsg = `${r.target_email} 계정에 "${sanctionLabels[type]}"을(를) 적용할까요?\n신고 사유: ${r.reason}\n\n※ 계정 이메일을 다시 한 번 확인하고 진행해주세요.`;
          if (!window.confirm(confirmMsg)) return;
          applySanction(r.target_email, type, `[신고 처리] ${r.reason}${r.description?' — '+r.description:''}`, S.acctEmail).then(res=>{
            if (res&&res.error) { set({toast:'제재 적용 실패 — 다시 시도해주세요'}); return; }
            set({toast:`${r.target_email} 계정에 ${sanctionLabels[type]} 조치를 적용했어요`});
            updateReportStatus(r.id,'처리완료').then(load);
          });
        };
        return <div key={r.id} className="card" style={{marginBottom:10, padding:'14px 16px'}}>
        <div className="row" style={{justifyContent:'space-between', marginBottom:6}}>
          <b style={{fontSize:14}}>{r.report_type} · {r.target||'(대상 미기재)'}</b>
          <Chip label={r.status} tone={r.status==='처리완료'?'green':'orange'}/>
        </div>
        <div className="meta" style={{marginBottom:4}}>사유: {r.reason}</div>
        {r.description&&<p style={{fontSize:13.5, margin:'0 0 8px', color:'var(--c-sub)'}}>{r.description}</p>}
        <div className="meta" style={{fontSize:11.5, marginBottom:4}}>신고자: {r.reporter_email||'익명'} · {fmt(r.created_at)}</div>
        {r.screenshot_path&&<button style={{background:'none',border:'1px solid var(--c-line)',borderRadius:8,padding:'5px 10px',fontFamily:'inherit',fontSize:12,cursor:'pointer',color:'var(--c-primary)',marginBottom:6}} onClick={async()=>{ const sb=getSupabaseClient(); if(!sb) return; const {data,error}=await sb.storage.from('reports').createSignedUrl(r.screenshot_path,3600); if(data&&data.signedUrl) window.open(data.signedUrl,'_blank'); else alert('스크린샷을 불러오지 못했어요 — 운영진 계정으로 로그인했는지 확인해주세요'); }}>첨부 스크린샷 보기</button>}
        {hasEmail
          ?<div className="meta" style={{fontSize:11.5, marginBottom:10, color:'var(--c-primary)', fontWeight:700}}>대상 계정: {r.target_email} (자동 확인됨)</div>
          :<div className="notice orange" style={{marginBottom:10, fontSize:11.5, padding:'8px 10px'}}><Ic n="IconNormalCircleExclamation2NameCircleExclamationFillFalse" s={14}/><span>대상 계정 이메일이 자동으로 확인되지 않은 신고예요(닉네임만으로는 계정을 특정할 수 없어 자동 제재가 비활성화됐어요). 신고 내용을 보고 어떤 계정인지 직접 확인한 뒤, 필요하면 Supabase에서 수동으로 처리해주세요.</span></div>}
        {r.status!=='처리완료'&&<div style={{display:'flex', flexWrap:'wrap', gap:6}}>
          <DSBtn label="경고" size="sm" fit variant="outlined" disable={!hasEmail} onClick={()=>doSanction('warning')}/>
          <DSBtn label="3일 정지" size="sm" fit variant="outlined" disable={!hasEmail} onClick={()=>doSanction('suspend_3d')}/>
          <DSBtn label="7일 정지" size="sm" fit variant="outlined" disable={!hasEmail} onClick={()=>doSanction('suspend_7d')}/>
          <DSBtn label="영구 정지" size="sm" fit variant="outlined" color="assistive" disable={!hasEmail} onClick={()=>doSanction('permanent')}/>
          <DSBtn label="조치 없이 처리완료" size="sm" fit onClick={()=>updateReportStatus(r.id,'처리완료').then(load)}/>
        </div>}
      </div>;}))}
      {!loading&&tab===1&&(inquiries.length===0?<p className="meta">접수된 문의가 없어요.</p>:inquiries.map(i=><div key={i.id} className="card" style={{marginBottom:10, padding:'14px 16px'}}>
        <div className="row" style={{justifyContent:'space-between', marginBottom:6}}>
          <b style={{fontSize:14}}>{i.sender_email||'익명'}</b>
          <Chip label={i.status} tone={i.status==='처리완료'?'green':'orange'}/>
        </div>
        <p style={{fontSize:13.5, margin:'0 0 8px', color:'var(--c-sub)'}}>{i.message}</p>
        <div className="meta" style={{fontSize:11.5, marginBottom:8}}>{fmt(i.created_at)}</div>
        {i.status!=='처리완료'&&<DSBtn label="처리완료로 표시" size="sm" fit onClick={()=>updateInquiryStatus(i.id,'처리완료').then(load)}/>}
      </div>))}
      {!loading&&tab===2&&<div>
        <b style={{fontSize:14.5}}>직접 제재</b>
        <p className="meta" style={{margin:'6px 0 10px', lineHeight:1.5}}>신고가 없어도 계정 이메일로 바로 조치할 수 있어요. 이메일을 한 번 더 확인하고 진행해주세요.</p>
        <div className="card" style={{padding:'14px 16px', marginBottom:22}}>
          <input value={sancEmail} onChange={e=>setSancEmail(e.target.value)} placeholder="대상 계정 이메일" style={{width:'100%', boxSizing:'border-box', border:'1px solid var(--c-line)', borderRadius:8, padding:'9px 12px', fontFamily:'inherit', fontSize:14, outline:'none', marginBottom:8}}/>
          <div style={{display:'flex', flexWrap:'wrap', gap:6, marginBottom:8}}>
            {[['warning','경고'],['suspend_3d','3일 정지'],['suspend_7d','7일 정지'],['permanent','영구 정지']].map(([k,lab])=><button key={k} className={'qtab'+(sancType===k?' on':'')} onClick={()=>setSancType(k)}>{lab}</button>)}
          </div>
          <input value={sancReason} onChange={e=>setSancReason(e.target.value)} placeholder="사유 (당사자 안내·이력에 남아요)" style={{width:'100%', boxSizing:'border-box', border:'1px solid var(--c-line)', borderRadius:8, padding:'9px 12px', fontFamily:'inherit', fontSize:14, outline:'none', marginBottom:10}}/>
          <DSBtn label="제재 적용" size="md" onClick={()=>{ const em=sancEmail.trim().toLowerCase(); if(!em||!em.includes('@')){ set({toast:'이메일을 확인해주세요'}); return; } if(!sancReason.trim()){ set({toast:'사유를 입력해주세요'}); return; } if(!window.confirm(em+' 계정에 "'+({warning:'경고',suspend_3d:'3일 정지',suspend_7d:'7일 정지',permanent:'영구 정지'}[sancType])+'" 조치를 적용할까요?')) return; applySanction(em, sancType, sancReason.trim(), S.acctEmail).then(r=>{ if(r&&r.error){ set({toast:'제재 적용 실패 — 다시 시도해주세요'}); return; } set({toast:em+' 계정에 조치를 적용했어요'}); setSancEmail(''); setSancReason(''); load(); }); }}/>
        </div>
        <b style={{fontSize:14.5}}>활성 제재</b>
        <div style={{margin:'10px 0 22px'}}>
          {sanctions.filter(x=>x.active).length===0?<p className="meta">현재 활성화된 정지 계정이 없어요.</p>
          :sanctions.filter(x=>x.active).map(sn=><div key={sn.id} className="card" style={{marginBottom:8, padding:'12px 14px'}}>
            <div className="row" style={{justifyContent:'space-between'}}>
              <b style={{fontSize:13.5}}>{sn.email}</b>
              <Chip label={{warning:'경고',suspend_3d:'3일 정지',suspend_7d:'7일 정지',permanent:'영구 정지'}[sn.type]||sn.type} tone="orange"/>
            </div>
            <div className="meta" style={{fontSize:11.5, margin:'4px 0'}}>사유: {sn.reason||'-'} · {sn.expires_at?('~'+fmt(sn.expires_at)):'영구'} · 조치자 {sn.created_by||'-'}</div>
            <DSBtn label="제한 해제" size="sm" fit variant="outlined" onClick={()=>{ if(!window.confirm(sn.email+' 계정의 제한을 즉시 해제할까요?')) return; liftSanction(sn.id).then(r=>{ if(r&&r.error){ set({toast:'해제 실패'}); return;} set({toast:sn.email+' 제한을 해제했어요'}); load(); }); }}/>
          </div>)}
        </div>
        <b style={{fontSize:14.5}}>노쇼 기록 조회·정정</b>
        <p className="meta" style={{margin:'6px 0 10px', lineHeight:1.5}}>잘못 기록된 노쇼를 참석으로 정정할 수 있어요(정정 시 당사자에게 알림). 이메일로 조회하세요.</p>
        <div className="row" style={{gap:8, marginBottom:12}}>
          <input value={nsEmail} onChange={e=>setNsEmail(e.target.value)} placeholder="계정 이메일" style={{flex:1, minWidth:0, border:'1px solid var(--c-line)', borderRadius:8, padding:'9px 12px', fontFamily:'inherit', fontSize:14, outline:'none'}}/>
          <DSBtn label="조회" size="md" fit onClick={()=>{ if(!nsEmail.trim())return; fetchNoshowRecords(nsEmail.trim()).then(setNsRecords); }}/>
        </div>
        {nsRecords!=null&&(nsRecords.length===0?<p className="meta">해당 계정의 노쇼 기록이 없어요.</p>
        :nsRecords.map(r=><div key={r.id} className="card row" style={{justifyContent:'space-between', marginBottom:8, padding:'11px 14px'}}>
          <span style={{fontSize:13.5}}><b>{r.meetup_title}</b><br/><span className="meta">{fmt(r.updated_at)} 노쇼 기록</span></span>
          <DSBtn label="참석으로 정정" size="sm" fit variant="outlined" onClick={()=>{ correctNoshow(r.id).then(res=>{ if(res&&res.error){ set({toast:'정정 실패'}); return;} set({toast:'참석으로 정정했어요 — 당사자에게 알림이 갔어요'}); fetchNoshowRecords(nsEmail.trim()).then(setNsRecords); load(); }); }}/>
        </div>))}
      </div>}
      {!loading&&tab===3&&<div>
        <p className="meta" style={{marginBottom:12, lineHeight:1.5}}>서버의 모든 모임이에요(최신순, 최대 100개). 삭제하면 사유 선택 후 주최자에게 알림이 가고, 확정 참가자에게는 모임 취소가 고지돼요.</p>
        {allMeets==null?<p className="meta">불러오는 중…</p>
        :allMeets.length===0?<p className="meta">등록된 모임이 없어요.</p>
        :allMeets.map(m=><div key={m.id} className="card row" style={{justifyContent:'space-between', marginBottom:8, padding:'12px 14px', gap:10}}>
          <span style={{flex:1, minWidth:0, fontSize:13.5}}>
            <b>{m.title}</b> {m.attendance_state==='done'&&<Chip label="완료" tone="green"/>}<br/>
            <span className="meta">{m.host_name||m.host_email} · {m.when_label||m.event_date||''} · 개설 {fmt(m.created_at)}</span>
          </span>
          <DSBtn label="삭제" size="sm" fit variant="outlined" color="assistive" onClick={()=>set({adminRemove:{kind:'meetup', id:m.id, ownerEmail:m.host_email, title:m.title}})}/>
        </div>)}
      </div>}
      {!loading&&tab===4&&<div>
        {evSel==null?<>
          <p className="meta" style={{marginBottom:12, lineHeight:1.5}}>운영진이 등록한 이벤트예요. 이벤트를 선택하면 신청자 명단·선정·통보·채팅방을 관리할 수 있어요.</p>
          {evList==null?<p className="meta">불러오는 중…</p>
          :evList.length===0?<p className="meta">등록된 이벤트가 없어요 — 이벤트 탭에서 먼저 이벤트를 등록해주세요.</p>
          :evList.map(ev=><button key={ev.id} className="card row" style={{width:'100%', justifyContent:'space-between', marginBottom:8, padding:'12px 14px', cursor:'pointer', fontFamily:'inherit', textAlign:'left'}} onClick={()=>{ setEvApps(null); setEvRoom(null); setEvSel(ev); }}>
            <span style={{fontSize:13.5}}><b>{ev.title}</b><br/><span className="meta">{ev.when_label} · {ev.city} · {ev.mode==='lottery'?'추첨제':'선착순'} · 정원 {ev.cap}</span></span>
            <Ic n="IconNormalChevronRightNameChevronRightTightFalseThick" s={13} c="var(--c-faint)"/>
          </button>)}
        </>:<>
          <div className="row" style={{justifyContent:'space-between', marginBottom:12}}>
            <button style={{background:'none', border:'none', color:'var(--c-primary)', fontFamily:'inherit', fontSize:13, fontWeight:700, cursor:'pointer', padding:0}} onClick={()=>{ setEvSel(null); setEvApps(null); }}>← 이벤트 목록</button>
            <b style={{fontSize:14.5}}>{evSel.title}</b>
          </div>
          {evApps==null?<p className="meta">명단 불러오는 중…</p>:<>
            <div className="row" style={{gap:6, flexWrap:'wrap', marginBottom:12}}>
              {Object.entries(evApps.reduce((m,x)=>{ const g=(x.group_choice||'').trim()||'무그룹'; m[g]=(m[g]||0)+1; return m; },{})).map(([g,n])=><Chip key={g} label={g+' '+n+'명'} tone="line"/>)}
              <Chip label={'선정 '+evApps.filter(x=>x.status==='selected').length+'명'} tone="green"/>
              <Chip label={'탈락 '+evApps.filter(x=>x.status==='rejected').length+'명'} tone="orange"/>
            </div>
            <div className="card" style={{padding:'12px 14px', marginBottom:14}}>
              {evRoom
                ?<div className="row" style={{justifyContent:'space-between', flexWrap:'wrap', gap:8}}>
                  <span style={{fontSize:13.5}}><b>채팅방 열림</b> · {evRoom.anonymous?'익명 모드(가명 표시)':'일반 모드'}</span>
                  <div className="row" style={{gap:6}}>
                    <DSBtn label="채팅방 입장" size="sm" fit onClick={()=>{ set({detailId:evSel.id, eventChatMeta:{title:evSel.title, anonymous:evRoom.anonymous}}); go('eventchat'); }}/>
                    {evRoom.anonymous&&<DSBtn label="가명 매핑 새로고침" size="sm" fit variant="outlined" onClick={()=>fetchEventAliases(evSel.id).then(setEvAliases)}/>}
                  </div>
                  {evRoom.anonymous&&evAliases&&evAliases.length>0&&<div style={{width:'100%', marginTop:6}}>{evAliases.map(al=><div key={al.email} className="meta" style={{padding:'2px 0'}}>{al.alias} ↔ {al.email}</div>)}</div>}
                </div>
                :<div className="row" style={{justifyContent:'space-between', flexWrap:'wrap', gap:8}}>
                  <label className="row" style={{gap:8, cursor:'pointer', fontSize:13.5}}>
                    <input type="checkbox" checked={evAnon} onChange={e=>setEvAnon(e.target.checked)} style={{width:16, height:16, accentColor:'var(--c-primary)'}}/>
                    익명 모드 (소개팅 권장 — 남1·여1 가명으로만 표시)
                  </label>
                  <DSBtn label={evBusy==='open'?'여는 중…':'이벤트 채팅방 열기'} size="sm" fit disable={evBusy==='open'} onClick={()=>{ if(!window.confirm('선정자 전용 채팅방을 열까요?'+(evAnon?' (익명 모드)':' (일반 모드)')+' — 선정자 전원에게 입장 알림이 가요.')) return; setEvBusy('open'); adminOpenEventChat(evSel.id, evAnon).then(r=>{ setEvBusy(''); if(r&&r.error){ set({toast:'채팅방 열기에 실패했어요'}); return; } set({toast:'이벤트 채팅방을 열었어요 — 선정자에게 알림이 갔어요'}); loadEvApps(evSel.id); }); }}/>
                </div>}
            </div>
            {(()=>{ const pendingRejects=evApps.filter(x=>x.status==='rejected'&&!x.rejected_notified_at).length; return <div className="row" style={{justifyContent:'flex-end', marginBottom:10}}>
              <DSBtn label={evBusy==='rej'?'발송 중…':pendingRejects>0?('탈락 통보 보내기 ('+pendingRejects+'명)'):'탈락 통보 완료'} size="sm" fit variant="outlined" color="assistive" disable={pendingRejects===0||evBusy==='rej'} onClick={()=>{ if(!window.confirm('탈락 통보를 보낼까요? 아직 통보받지 않은 탈락자 '+pendingRejects+'명에게 알림이 가요.')) return; if(!window.confirm('정말 보낼까요? 선정 작업이 끝났는지 한 번 더 확인해주세요 — 발송 후엔 되돌릴 수 없어요.')) return; setEvBusy('rej'); adminNotifyRejected(evSel.id).then(r=>{ setEvBusy(''); if(r&&r.error){ set({toast:'통보 발송에 실패했어요'}); return; } set({toast:(r.count||0)+'명에게 탈락 안내를 보냈어요'}); loadEvApps(evSel.id); }); }}/>
            </div>; })()}
            {evApps.length===0?<p className="meta">아직 신청자가 없어요.</p>
            :evApps.map(ap=><div key={ap.id} className="card" style={{marginBottom:8, padding:'12px 14px'}}>
              <div className="row" style={{justifyContent:'space-between', flexWrap:'wrap', gap:6}}>
                <span style={{fontSize:13.5, minWidth:0}}><b>{ap.applicant_name||ap.applicant_email}</b> <span className="meta">{ap.applicant_email}</span><br/>
                  <span className="meta">{(ap.group_choice||'').trim()||'무그룹'} · {fmt(ap.created_at)}{ap.reason&&<> · <button style={{background:'none',border:'none',color:'var(--c-primary)',fontFamily:'inherit',fontSize:12,cursor:'pointer',padding:0,textDecoration:'underline'}} onClick={()=>setReasonView(ap)}>사연 보기</button></>}</span>
                </span>
                <Chip label={({pending:'심사 중',applied:'심사 중',confirmed:'확정',waitlisted:'대기',waitlist:'대기',selected:'선정',rejected:ap.rejected_notified_at?'탈락(통보됨)':'탈락(미통보)'})[ap.status]||ap.status} tone={ap.status==='selected'?'green':ap.status==='rejected'?'orange':'line'}/>
              </div>
              <div style={{display:'flex', gap:6, marginTop:8, flexWrap:'wrap'}}>
                {[['selected','선정'],['rejected','탈락'],['waitlist','대기']].map(([k,lab])=><DSBtn key={k} label={evBusy===ap.id+k?'처리 중…':lab} size="sm" fit variant={ap.status===k?'filled':'outlined'} disable={!!evBusy||ap.status===k} onClick={()=>{ if(!window.confirm((ap.applicant_name||ap.applicant_email)+'님을 "'+lab+'"(으)로 변경할까요?'+(k==='selected'?' 선정 알림이 즉시 발송돼요.':''))) return; setEvBusy(ap.id+k); adminSetEventAppStatus(ap.id, k).then(r=>{ setEvBusy(''); if(r&&r.error){ set({toast:'상태 변경에 실패했어요'}); return; } set({toast:'상태를 변경했어요'+(k==='selected'?' — 선정 알림이 갔어요':'')}); loadEvApps(evSel.id); }); }}/>)}
              </div>
            </div>)}
          </>}
          {reasonView&&<div className="overlay" onClick={()=>setReasonView(null)}><div className="sheet" onClick={e=>e.stopPropagation()}>
            <div className="row" style={{justifyContent:'space-between', marginBottom:10}}>
              <b style={{fontSize:15}}>{reasonView.applicant_name||reasonView.applicant_email}님의 사연</b>
              <button style={{background:'none', border:'none', cursor:'pointer', padding:4}} onClick={()=>setReasonView(null)}><Ic n="IconNormalCloseNameCloseThickFalse" s={16} c="var(--c-faint)"/></button>
            </div>
            <p style={{fontSize:14, lineHeight:1.7, whiteSpace:'pre-wrap', margin:0}}>{reasonView.reason}</p>
          </div></div>}
        </>}
      </div>}
    </div>
  </div>;
}
function WScrAttendees({S, set, go}) {
  const m = MEETUPS[S.detailId] || null;
  if (!m) return <div className="wapp"><WNav S={S} go={go} set={set}/><div className="wmain"><MeetupNotFoundNotice go={go}/></div></div>;
  const isHost = S.myHostedMeetups.some(hm=>hm.title===m.title) || S.regName===m.host.name;
  const list = ATTENDEES.slice(0, Math.max(3, m.capJoined - 30 > 0 ? 5 : 4));
  const key = n => `${m.id}:${n}`;
  const statusOf = n => S.attendeeStatus[key(n)] || 'attended';
  const toggle = n => { if(!isHost) return; set({attendeeStatus:{...S.attendeeStatus, [key(n)]: statusOf(n)==='attended'?'noshow':'attended'}}); };
  const noshowCount = list.filter(n=>statusOf(n)==='noshow').length;
  const companion = getCompanion(m.id);
  return <div className="wapp"><WNav S={S} go={go} set={set}/>
    <div className="wmain" style={{maxWidth:640}}>
      <button className="wback" onClick={()=>go('mymeet')}><Ic n="IconNormalChevronLeftNameChevronLeftTightFalseThick" s={14}/>내 모임으로</button>
      <div className="wsect"><h2>참가자 명단</h2></div>
      <div className="meta" style={{marginBottom:14}}>{m.title} · {list.length}명 신청 · 노쇼 {noshowCount}명</div>
      {!isHost&&<div className="notice" style={{marginBottom:14}}><Ic n="IconNormalCircleInfo2NameCircleInfoFillFalse" s={15}/><span>참여 여부(노쇼) 처리는 이 모임의 주최자만 할 수 있어요.</span></div>}
      {list.map(n=>{const st = statusOf(n); return <div key={n} className="card row" style={{gap:12, marginBottom:10}}>
        <Av name={n} tone={st==='noshow'?undefined:'p'}/>
        <span style={{flex:1, fontSize:14.5}}><b>{n}</b><span className="meta" style={{marginLeft:6}}>가입 회원</span><br/><span className="meta">{st==='noshow'?'노쇼':'참여 확인'}</span></span>
        {st==='noshow'?<Chip label="노쇼" tone="orange"/>:<Chip icon="IconNormalCheckNameCheckThickFalse" label="참여" tone="green"/>}
        {isHost&&<button style={{background:'none', border:'1px solid var(--c-line)', borderRadius:8, padding:'7px 12px', fontFamily:'inherit', fontSize:12.5, cursor:'pointer'}} onClick={()=>toggle(n)}>{st==='noshow'?'참여로 변경':'참여X (노쇼)'}</button>}
        <button className="iconbtn" aria-label="신고하기" onClick={()=>guardAuth(S,set,go,()=>set({reportOpen:true, reportPrefill:`${n} · ${m.title||'모임'}`}))}><Ic n="IconNormalFlagNameFlagFillFalse" s={16} c="var(--c-faint)"/></button>
      </div>;})}
      {companion&&companion.status==='approved'&&<div className="card row" style={{gap:12, marginBottom:10, marginLeft:24, background:'var(--c-fill-a)'}}>
        <Ic n="IconNormalPersonsNamePersonsFillFalse" s={22} c="var(--c-faint)"/>
        <span style={{flex:1, fontSize:14.5}}><b>동반자 {companion.name||'동반자'}</b><br/><span className="meta">{companion.count||1}명 · 요청회원 {companion.requestedBy||'회원'}</span></span>
        <Chip label="비회원 동반자" tone="violet"/>
        {isHost&&<button style={{background:'none', border:'1px solid var(--c-line)', borderRadius:8, padding:'7px 12px', fontFamily:'inherit', fontSize:12.5, cursor:'pointer'}} onClick={()=>{ setCompanion(m.id,null); set({toast:'동반자 승인을 취소했어요'}); }}>승인 취소</button>}
      </div>}
      {(() => { const wl = (S.waitlists&&S.waitlists[m.id])||[]; return wl.length>0 && <>
        <div className="wsect" style={{marginTop:20}}><h2 style={{fontSize:15}}>대기 명단 ({wl.length}명)</h2></div>
        {wl.map((w,i)=><div key={w.name+i} className="card row" style={{gap:12, marginBottom:10}}>
          <Av name={w.name} tone={w.isMe?'p':undefined}/>
          <span style={{flex:1, fontSize:14.5}}><b>{w.isMe?'나':w.name}</b><span className="meta" style={{marginLeft:6}}>대기 {i+1}번째</span></span>
        </div>)}
      </>; })()}
      <p className="meta" style={{margin:'12px 2px', lineHeight:1.6}}>※ 노쇼로 표시된 참가자는 반복 시 다음 모임 신청이 제한될 수 있어요. 신고는 운영진에게 바로 접수돼요.</p>
    </div>
  </div>;
}
Object.assign(window, {WScrAttendees});
