PageSourceSearch

https://ddok.it/assets/alimtalk-Ce7T4-jW.js

js ddok.it collected 2026-10-06 14:03:54 UTC 52,240 bytes, 14 lines download raw bytes

1import{c8 as t,cF as ce,q as de,ce as ze,cr as He,t as we,cV as ke,c9 as e,cH as Ue,cI as qe,cJ as Ke,cK as Xe,ci as Ge,cL as Qe,cM as F,cQ as Le,cy as $,de as Te,cz as Ve,cP as Se,cb as M,cs as se,cN as Ye,ck as We,cl as Ze,cm as Je,cE as es,cn as ss,cZ as ts,di as as,dj as ns,dk as re,dl as Q,dm as ls,dn as H,co as ie,d8 as rs,d9 as is,da as cs,cG as ds,db as os,cj as ms,cp as us,c7 as Ds,cd as _s,cT as $s,ca as Is,cc as Ms}from"./index-B9fS4Gmf.js";import{S as oe,a as me,b as ue,c as xe,d as C}from"./select-G6e_BQkf.js";import{A as xs,a as hs,b as ps,c as gs,d as js,e as fs,f as Ns,g as vs}from"./alert-dialog-BYMrL4Ug.js";import{S as bs}from"./scroll-area-DV4BiEqc.js";import{u as Ae}from"./useMutation-C_rfsYCG.js";import{d as Cs}from"./date-utils-BhVms15i.js";import{U as ys}from"./users-DTvyKjqV.js";import{S as Ne}from"./send-DvVDNRZH.js";import{T as Rs,a as Bs,b as $e,c as Ie}from"./tabs-DdWmXYjZ.js";import{T as Os}from"./textarea-BzjYJ_Ae.js";import{S as Ss}from"./smartphone-CeK1XvxH.js";import{B as zs}from"./bell-5W13VsJ5.js";import{C as ws}from"./circle-check-big-CmS1prDt.js";import{C as ks}from"./circle-x-CL4-DUTq.js";import{f as Ts}from"./format-CWJRN84p.js";import{k as As}from"./ko-BEh1PYC-.js";import"./chevron-down-BR81Mjc9.js";const Hs=20;function Us(s={}){const{enabled:a=!0,campaignId:o}=s,[m,g]=t.useState(1),[c,N]=t.useState(""),[f,S]=t.useState("all"),[u,w]=t.useState("all"),{data:x,isLoading:v,refetch:j}=ce({queryKey:["alimtalk-logs",m,c,f,u,o??""],queryFn:async()=>{const r=new URLSearchParams;return r.set("page",m.toString()),r.set("limit",String(Hs)),c&&r.set("search",c),f!=="all"&&r.set("sendType",f),u!=="all"&&r.set("success",u),o&&r.set("campaignId",o),de(`/admin/alimtalk/logs?${r}`)},enabled:a}),h=t.useCallback(r=>{N(r),g(1)},[]),b=t.useCallback(r=>{S(r),g(1)},[]),E=t.useCallback(r=>{w(r),g(1)},[]),p=t.useCallback(r=>{g(r)},[]),T=t.useCallback(()=>{j()},[j]);return{data:x,isLoading:v,page:m,search:c,sendTypeFilter:f,successFilter:u,handleSearchChange:h,handleSendTypeFilterChange:b,handleSuccessFilterChange:E,handlePageChange:p,handleRefresh:T}}function ne(s){return s.visitInfo?.companyName||s.visitCompanyName||""}function qs(s){return s.visitInfo?.experienceBenefits||s.visitExperienceBenefits||""}const je=128,Ks=/^01[016789]\d{7,8}$/,Xs={blog:"블로그",instagram:"인스타그램",youtube:"유튜브",naverClip:"네이버 클립",threads:"스레드",tiktok:"틱톡"},Gs="상시모집";function Qs(s){if(s.isAlwaysAnnouncement)return Gs;const a=s.announcementDate||s.deadline||s.recruitDeadline||"";if(!a)return"";const o=new Date(a);return Number.isNaN(o.getTime())?a:Cs(a)}function Vs(s){return`ddok.it/campaigns/${s.dealNumber||s.id}`}function Ys(s={}){const{enabled:a=!0,onSuccess:o}=s,{toast:m}=ze(),g=He(),[c,N]=t.useState(""),[f,S]=t.useState(""),[u,w]=t.useState({note1:"",note2:"",note3:""}),[x,v]=t.useState(""),[j,h]=t.useState(""),[b,E]=t.useState(!1),[p,T]=t.useState(""),{data:r}=ce({queryKey:["sendable-campaigns-for-alimtalk"],queryFn:async()=>{const l=await de("/admin/campaigns/sendable");return Array.isArray(l)?l:[]},staleTime:3e4,enabled:a}),{data:k,isFetching:P}=ce({queryKey:["alimtalk-target-count",x,j,c],queryFn:async()=>{const l=new URLSearchParams;x&&l.set("region",x),j&&l.set("channel",j),c&&l.set("campaignId",c);const R=l.toString();return de(`/admin/alimtalk/target-count${R?`?${R}`:""}`)},enabled:a}),A=t.useMemo(()=>r?.find(l=>l.id===c),[r,c]),L=t.useRef(null);t.useEffect(()=>{A&&L.current!==A.id&&(L.current=A.id,w({note1:(ne(A)||A.title||"").slice(0,je),note2:qs(A).slice(0,je),note3:Qs(A).slice(0,je)}))},[A]);const q=Ae({mutationFn:async()=>{const l={};return x.trim()&&(l.region=x.trim()),j&&(l.channel=j),we("/admin/alimtalk/send",{campaignId:c,targetFilter:l,notes:K(),resendToAlreadySent:b})},onSuccess:l=>{m({title:"발송 요청 완료",description:l?.message||"알림톡 발송을 시작했습니다."}),g.invalidateQueries({queryKey:["alimtalk-logs"]}),g.invalidateQueries({queryKey:["alimtalk-target-count"]}),o?.()},onError:l=>{m({title:"발송 실패",description:l.message,variant:"destructive"})}}),U=Ae({mutationFn:async l=>we("/admin/alimtalk/test-send",{campaignId:c,notes:K(),phone:l}),onSuccess:l=>{if(l&&l.success===!1){m({title:"테스트 발송 실패",description:l.message,variant:"destructive"});return}
1m({title:"테스트 발송 완료",description:l?.message||"입력하신 번호로 발송했습니다."}),g.invalidateQueries({queryKey:["alimtalk-logs"]})},onError:l=>{m({title:"테스트 발송 실패",description:l.message,variant:"destructive"})}});function K(){const l={note1:u.note1.trim(),note2:u.note2.trim(),note3:u.note3.trim()};return A&&(l.note4=Vs(A)),l}const D=t.useCallback(l=>{N(l)},[]),O=t.useCallback((l,R)=>{w(Y=>({...Y,[l]:R.slice(0,je)}))},[]),I=t.useCallback(()=>{N(""),S(""),w({note1:"",note2:"",note3:""}),v(""),h(""),E(!1),T(""),L.current=null},[]),z=t.useMemo(()=>{if(!r)return[];const l=f.trim().toLowerCase();return l?r.filter(R=>{const Y=R.title?.toLowerCase()||"",he=ne(R).toLowerCase(),Z=R.dealNumber?.toLowerCase()||"";return Y.includes(l)||he.includes(l)||Z.includes(l)}):r},[r,f]),y=t.useCallback(()=>c?!u.note1.trim()||!u.note2.trim()||!u.note3.trim()?(m({title:"변수 입력 필요",description:"상품명·체험혜택·마감일을 모두 입력해주세요.",variant:"destructive"}),!1):!0:(m({title:"캠페인 선택 필요",description:"발송할 캠페인을 선택해주세요.",variant:"destructive"}),!1),[c,u,m]),X=t.useCallback(()=>{y()&&q.mutate()},[y,q]),B=t.useCallback(()=>{if(!y())return;const l=p.replace(/[^0-9]/g,"");if(!Ks.test(l)){m({title:"전화번호를 확인해주세요.",description:"휴대폰 번호를 하이픈 없이 입력해주세요. (예: 01012345678)",variant:"destructive"});return}U.mutate(l)},[y,p,U,m]),d=t.useCallback(()=>{const l=[];return x.trim()&&l.push(`지역: ${x.trim()}`),j&&l.push(`채널: ${Xs[j]||j}`),l.length>0?l.join(" + "):"전체 마케팅 동의자"},[x,j]);return{campaigns:r,filteredCampaigns:z,campaignSearch:f,setCampaignSearch:S,selectedCampaignId:c,selectedCampaign:A,selectCampaign:D,notes:u,setNote:O,targetRegion:x,setTargetRegion:v,targetChannel:j,setTargetChannel:h,resendToAlreadySent:b,setResendToAlreadySent:E,targetCount:k?.count??0,alreadySentCount:k?.alreadySentCount??0,effectiveTargetCount:b?k?.dedupedCount??k?.count??0:k?.effectiveCount??k?.count??0,excludedTargetCount:Math.max(0,(k?.dedupedCount??k?.count??0)-(k?.effectiveCount??k?.count??0)),isTargetCountLoading:P,testPhone:p,setTestPhone:T,handleTestSend:B,isTestSending:U.isPending,handleSend:X,isSending:q.isPending,resetState:I,getTargetLabel:d,NOTE_MAX_LENGTH:je}}const Ws=ke.memo(function({open:a,onOpenChange:o}){const[m,g]=t.useState(!1),c=ke.useRef(null),{filteredCampaigns:N,campaignSearch:f,setCampaignSearch:S,selectedCampaignId:u,selectedCampaign:w,selectCampaign:x,notes:v,setNote:j,targetRegion:h,setTargetRegion:b,targetChannel:E,setTargetChannel:p,resendToAlreadySent:T,setResendToAlreadySent:r,effectiveTargetCount:k,excludedTargetCount:P,alreadySentCount:A,testPhone:L,setTestPhone:q,handleTestSend:U,isTestSending:K,handleSend:D,isSending:O,resetState:I,getTargetLabel:z,NOTE_MAX_LENGTH:y}=Ys({enabled:a,onSuccess:()=>{g(!1),o(!1),c.current?.()}});c.current=I;const X=()=>{o(!1),I()};if(!a)return null;const B=w?ne(w)?`${ne(w)} (${w.title})`:w.title:"";return e.jsx(Ue,{open:a,onOpenChange:d=>{d||X()},children:e.jsxs(qe,{className:"max-w-3xl max-h-[92vh] flex flex-col overflow-hidden","data-testid":"dialog-alimtalk-send",children:[e.jsxs(Ke,{className:"pb-1",children:[e.jsxs(Xe,{className:"flex items-center gap-2",children:[e.jsx(Ge,{className:"h-5 w-5 text-yellow-500"}),"알림톡 발송 (신규 캠페인 알림)"]}),e.jsx(Qe,{children:"모집중이거나 상시 모집 중인 캠페인을 선택하고 변수를 확인한 뒤, 마케팅 수신 동의자에게 알림톡을 발송합니다. 앱 푸시는 함께 나가지 않습니다 — 필요하면 '앱 푸쉬 발송'에서 따로 보내세요."})]}),e.jsxs("div",{className:"flex-1 min-h-0 overflow-y-auto pr-1 space-y-4",children:[e.jsxs("section",{className:"space-y-2",children:[e.jsx(F,{className:"text-xs font-semibold",children:"① 캠페인 선택 (모집중 · 상시)"}),e.jsxs("div",{className:"relative",children:[e.jsx(Le,{className:"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground"}),e.jsx($,{placeholder:"캠페인명·업체명·딜번호 검색",value:f,onChange:d=>S(d.target.value),className:"pl-8 h-9 text-sm","data-testid":"input-campaign-search"})]}),e.jsx(bs,{className:"h-44 border rounded-lg p-1.5","data-testid":"list-campaigns",children:N.length===0?e.jsx("div",{className:"text-center py-10 text-muted-foreground text-sm",children:f?"조건에 맞는 캠페인이 없습니다.":"발송할 수 있는 캠페인이 없습니다."}):e.jsx("div",{className:"space-y-1",children:N.map(d=>e.jsxs("label",{className:`flex items-center gap-2.5 px-2.5 py-2 border rounded-md cursor-pointer hover:bg-muted/50 transition-colors ${u===d.id?"border-primary bg-primary/5":""}`,"data-testid":`campaign-option-${d.id}`,children:[e.jsx("input",{type:"radio",name:"alimtalk-campaign",className:"h-4 w-4 accent-primary flex-shrink-0",checked:u===d.id,onChange:()=>x(d.id)}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0",children:[e.jsx("span",{className:"font-medium text-sm truncate",children:ne(d)?`${ne(d)} (${d.title})`:d.title}
1),d.mainChannel&&e.jsx("span",{className:`px-1.5 py-0.5 rounded text-[10px] font-medium shrink-0 ${Te(d.mainChannel).colorClass}`,"data-testid":`campaign-channel-badge-${d.id}`,children:Te(d.mainChannel).label})]}),e.jsxs("div",{className:"flex items-center gap-1.5 text-xs text-muted-foreground mt-0.5",children:[d.isAlwaysAnnouncement&&e.jsx("span",{className:"px-1.5 py-0.5 rounded bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-200 text-[10px] font-medium shrink-0","data-testid":`campaign-always-badge-${d.id}`,children:"상시"}),d.dealNumber&&e.jsxs("span",{className:"text-primary font-mono",children:["#",d.dealNumber]}),d.region&&e.jsx("span",{children:d.region})]})]})]},d.id))})})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsx(F,{className:"text-xs font-semibold",children:"② 알림톡 변수 (자동 채움 · 수정 가능)"}),e.jsxs("div",{className:"grid gap-2 sm:grid-cols-3",children:[e.jsxs("div",{children:[e.jsx(F,{htmlFor:"alimtalk-note1",className:"text-[11px] text-muted-foreground",children:"상품명"}),e.jsx($,{id:"alimtalk-note1",value:v.note1,maxLength:y,onChange:d=>j("note1",d.target.value),placeholder:"예: 똑똑카페 강남점",className:"h-9 text-sm","data-testid":"input-note1"})]}),e.jsxs("div",{children:[e.jsx(F,{htmlFor:"alimtalk-note2",className:"text-[11px] text-muted-foreground",children:"체험혜택"}),e.jsx($,{id:"alimtalk-note2",value:v.note2,maxLength:y,onChange:d=>j("note2",d.target.value),placeholder:"예: 2인 코스요리 무료 제공",className:"h-9 text-sm","data-testid":"input-note2"})]}),e.jsxs("div",{children:[e.jsx(F,{htmlFor:"alimtalk-note3",className:"text-[11px] text-muted-foreground",children:"마감일"}),e.jsx($,{id:"alimtalk-note3",value:v.note3,maxLength:y,onChange:d=>j("note3",d.target.value),placeholder:"예: 2026. 08. 01.",className:"h-9 text-sm","data-testid":"input-note3"})]})]})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsx(F,{className:"text-xs font-semibold",children:"③ 발송 대상 (마케팅 수신 동의 · 휴대폰 등록 리뷰어)"}),e.jsxs("div",{className:"grid gap-2 sm:grid-cols-3",children:[e.jsx($,{placeholder:"지역 (예: 서울)",value:h,onChange:d=>b(d.target.value),className:"h-9 text-sm","data-testid":"input-target-region"}),e.jsxs(oe,{value:E||"_all",onValueChange:d=>p(d==="_all"?"":d),children:[e.jsx(me,{className:"h-9 text-sm","data-testid":"select-target-channel",children:e.jsx(ue,{placeholder:"대상 채널"})}),e.jsxs(xe,{children:[e.jsx(C,{value:"_all",children:"전체 채널"}),e.jsx(C,{value:"blog",children:"블로그"}),e.jsx(C,{value:"instagram",children:"인스타그램"}),e.jsx(C,{value:"youtube",children:"유튜브"}),e.jsx(C,{value:"naverClip",children:"네이버 클립"}),e.jsx(C,{value:"threads",children:"스레드"}),e.jsx(C,{value:"tiktok",children:"틱톡"})]})]}),e.jsxs("div",{className:"flex items-center gap-1.5 px-3 h-9 bg-muted rounded-md text-sm","data-testid":"badge-target-count",children:[e.jsx(ys,{className:"h-3.5 w-3.5 text-muted-foreground flex-shrink-0"}),e.jsxs("span",{className:"truncate",children:["대상 ",e.jsxs("strong",{className:"text-primary",children:[k,"명"]}),u&&P>0&&!T&&e.jsxs("span",{className:"text-xs text-muted-foreground",children:[" (이미 발송 ",P,"명 제외)"]})]})]})]}),u&&A>0&&e.jsxs("p",{className:"text-[11px] text-muted-foreground","data-testid":"text-campaign-send-history",children:["필터와 무관하게 이 캠페인 누적 발송 ",e.jsxs("strong",{children:[A.toLocaleString(),"명"]})]}),e.jsxs("label",{className:"flex items-start gap-2 text-sm cursor-pointer","data-testid":"label-resend",children:[e.jsx(Ve,{checked:T,onCheckedChange:d=>r(d===!0),className:"mt-0.5","data-testid":"checkbox-resend"}),e.jsxs("span",{children:["이미 발송된 대상 포함 재발송",T&&e.jsxs("span",{className:"text-xs text-destructive mt-0.5 flex items-center gap-1","data-testid":"text-resend-warning",children:[e.jsx(Se,{className:"h-3 w-3"}),"같은 캠페인 알림을 이미 받은 리뷰어에게 중복 발송됩니다. 스팸 신고 위험이 있으니 꼭 필요한 경우에만 사용하세요."]})]})]})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsx(F,{className:"text-xs font-semibold",children:"④ 미리보기"}
1),e.jsxs("div",{className:"rounded-lg p-3 bg-[#B2C7D9] dark:bg-slate-800","data-testid":"preview-alimtalk",children:[e.jsxs("div",{className:"max-w-sm rounded-lg overflow-hidden shadow-sm",children:[e.jsx("div",{className:"bg-[#FEE500] text-[#3C1E1E] text-xs font-bold px-3 py-2",children:"알림톡 도착"}),e.jsxs("div",{className:"bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 px-3 py-3 text-[13px] leading-relaxed whitespace-pre-line",children:[`[똑똑리워드] 신규 캠페인 알림
2
3`,e.jsx("span",{className:"font-medium",children:"회원"}),`님의 관심사에 맞는 새로운 캠페인이 오픈되어 알려드립니다.
4
5■ 캠페인 정보
6- 상품명: `,e.jsx("span",{className:"font-semibold text-primary","data-testid":"preview-note1",children:v.note1||"(상품명)"}),`
7- 체험혜택: `,e.jsx("span",{className:"font-semibold text-primary","data-testid":"preview-note2",children:v.note2||"(체험혜택)"}),`
8- 마감일: `,e.jsx("span",{className:"font-semibold text-primary","data-testid":"preview-note3",children:v.note3||"(마감일)"}),`
9
10원활한 선정을 위해 마감 전 신청을 권장드립니다.
11
12[수신 안내]
13해당 메시지는 고객님께서 요청하신 관심 캠페인 알림이 있을 경우 발송됩니다.
14(알림 해제: 대시보드 > 나의 채널관리 > 알림 설정)`,e.jsx("div",{className:"mt-3 border border-zinc-200 dark:border-zinc-700 rounded-md text-center py-2 text-[13px] font-medium text-zinc-700 dark:text-zinc-200",children:"캠페인 확인하기"})]})]}),e.jsx("p",{className:"text-[11px] text-zinc-700 dark:text-zinc-300 mt-2",children:"* 실제 발송 시 이름은 수신자별로 개인화되며, 버튼은 선택한 캠페인 상세 페이지로 연결됩니다."})]})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsx(F,{className:"text-xs font-semibold",children:"⑤ 테스트 발송 (1건)"}),e.jsxs("div",{className:"flex flex-col sm:flex-row gap-2",children:[e.jsx($,{placeholder:"테스트 수신 번호 (하이픈 없이)",value:L,onChange:d=>q(d.target.value),className:"h-9 text-sm sm:max-w-xs","data-testid":"input-test-phone"}),e.jsxs(M,{variant:"outline",onClick:U,disabled:K||!u,"data-testid":"button-test-send",children:[K?e.jsx(se,{className:"h-4 w-4 mr-2 animate-spin"}):e.jsx(Ne,{className:"h-4 w-4 mr-2"}),"테스트 발송"]})]})]})]}),e.jsxs(Ye,{className:"pt-2 flex-shrink-0",children:[e.jsx(M,{variant:"outline",onClick:X,"data-testid":"button-cancel-send",children:"취소"}),e.jsx(M,{onClick:()=>g(!0),disabled:O||!u||k===0,"data-testid":"button-open-confirm",children:O?e.jsxs(e.Fragment,{children:[e.jsx(se,{className:"h-4 w-4 mr-2 animate-spin"}),"발송 중..."]}):e.jsxs(e.Fragment,{children:[e.jsx(Ne,{className:"h-4 w-4 mr-2"}),k,"명에게 발송"]})})]}),e.jsx(xs,{open:m,onOpenChange:g,children:e.jsxs(hs,{"data-testid":"dialog-confirm-send",children:[e.jsxs(ps,{children:[e.jsx(gs,{children:"알림톡을 발송할까요?"}),e.jsx(js,{asChild:!0,children:e.jsxs("div",{className:"space-y-1.5 text-sm",children:[e.jsxs("div",{children:[e.jsx("strong",{children:"캠페인:"})," ",B||"-"]}),e.jsxs("div",{children:[e.jsx("strong",{children:"대상:"})," ",z()," · ",e.jsxs("span",{className:"text-primary font-semibold",children:[k,"명"]})]}),u&&(T||P>0)&&e.jsx("div",{className:"text-xs text-muted-foreground",children:T?"이미 발송된 대상도 포함해 재발송합니다.":`이미 발송된 ${P}명은 제외됩니다.`}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"광고성 메시지는 야간(21시~08시)에는 발송할 수 없습니다."})]})})]}),e.jsxs(fs,{children:[e.jsx(Ns,{"data-testid":"button-cancel-confirm",children:"취소"}),e.jsx(vs,{onClick:d=>{d.preventDefault(),D()},disabled:O,"data-testid":"button-confirm-send",children:O?e.jsxs(e.Fragment,{children:[e.jsx(se,{className:"h-4 w-4 mr-2 animate-spin"}),"발송 중..."]}):"발송하기"})]})]})})]})})});function le(s){return s.visitInfo?.companyName||s.visitCompanyName||""}function Zs(s){return s.visitInfo?.experienceBenefits||s.visitExperienceBenefits||""}const fe=128,Ps="[똑똑리워드]",Fe=" · 수신거부: 앱 >
14 나의 채널관리 > 알림 설정",Js=120,Pe=Js-Fe.length,Me=30,Re="",et=/^\/(?![/\\])[^\s\\]*$/;function Ls(s){return et.test(s)}const Fs="앱 내부 경로만 넣을 수 있습니다. 슬래시로 시작하고 공백·역슬래시가 없어야 합니다. (예: /campaigns/D-2608-0212)",Be=/^(?:https?:\/\/)?(?:www\.)?ddok\.it(?=[/?#]|$)/i;function st(s){const a=String(s||"").trim();if(!a||!Be.test(a))return a;const o=a.replace(Be,"");return o.startsWith("/")?o:`/${o}`}const tt={blog:"블로그",instagram:"인스타그램",youtube:"유튜브",naverClip:"네이버 클립",threads:"스레드",tiktok:"틱톡"};function ae(s,a){if(s.length<=a)return s;const o=s.slice(0,a);return/[\uD800-\uDBFF]$/.test(o)?o.slice(0,-1):o}const at="상시모집";function nt(s){if(s.isAlwaysAnnouncement)return at;const a=s.announcementDate||s.deadline||s.recruitDeadline||"";if(!a)return"";const o=new Date(a);return Number.isNaN(o.getTime())?a:Cs(a)}function lt(s){return`/campaigns/${s.dealNumber||s.id}`}function Es(s){if(s.length<=Pe)return s;let a=s.slice(0,Pe-1);return/[\uD800-\uDBFF]$/.test(a)&&(a=a.slice(0,-1)),`${a}…`}function rt(s,a={}){const o=[a.note1,a.note2].map(c=>String(c||"").trim()).filter(Boolean),m=String(a.note3||"").trim();let g;return o.length>0?(g=o.join(" · "),m&&(g+=s.isAlwaysAnnouncement?` — ${m}`:` — 마감 ${m}`),g+=". 지금 신청해 보세요."):g=`${s.title} 체험단이 새로 열렸어요. 지금 신청해 보세요.`,{title:`${Ps} 새 체험단이 올라왔어요`,body:`${Es(g)}${Fe}`,url:lt(s)}}function it(s){const a=String(s.url||"").trim(),o={title:`${Ps} ${s.title.trim()}`,body:`${Es(s.body.trim())}${Fe}`};return a&&(o.url=a),o}function ct(s){if(s.mode==="custom"){if(!s.customTitle.trim()||!s.customBody.trim())return{title:"내용 입력 필요",description:"제목과 본문을 모두 입력해주세요.",blockLabel:"① 제목과 ② 본문을 입력해야 발송할 수 있습니다."};const a=s.customUrl.trim();return a&&!Ls(a)?{title:"링크를 확인해주세요.",description:Fs,blockLabel:"③ 딥링크 경로가 앱 내부 경로가 아니라 발송할 수 없습니다."}:null}return s.selectedCampaignId?!s.notes.note1.trim()||!s.notes.note2.trim()||!s.notes.note3.trim()?{title:"변수 입력 필요",description:"상품명·체험혜택·마감일을 모두 입력해주세요.",blockLabel:"② 문구 변수(상품명·체험혜택·마감일)를 모두 채워야 합니다."}:null:{title:"캠페인 선택 필요",description:"발송할 캠페인을 선택해주세요.",blockLabel:"① 발송할 캠페인을 선택해야 합니다."}}function dt(s={}){const{enabled:a=!0,onSuccess:o}=s,{toast:m}=ze(),g=He(),[c,N]=t.useState("campaign"),[f,S]=t.useState(""),[u,w]=t.useState(""),[x,v]=t.useState({note1:"",note2:"",note3:""}),[j,h]=t.useState(!1),[b,E]=t.useState(""),[p,T]=t.useState(""),[r,k]=t.useState(""),[P,A]=t.useState(""),[L,q]=t.useState(""),[U,K]=t.useState(Re),{data:D}=ce({queryKey:["sendable-campaigns-for-push"],queryFn:async()=>{const n=await de("/admin/campaigns/sendable");return Array.isArray(n)?n:[]},staleTime:3e4,enabled:a}),O=c==="campaign"?f:"",{data:I,isFetching:z}=ce({queryKey:["push-target-count",P,L,O],queryFn:async()=>{const n=new URLSearchParams;P&&n.set("region",P),L&&n.set("channel",L),O&&n.set("campaignId",O);const _=n.toString();return de(`/admin/push/target-count${_?`?${_}`:""}`)},enabled:a}),y=t.useMemo(()=>D?.find(n=>n.id===f),[D,f]),X=t.useRef(null);t.useEffect(()=>{y&&X.current!==y.id&&(X.current=y.id,v({note1:ae(le(y)||y.title||"",fe),note2:ae(Zs(y),fe),note3:ae(nt(y),fe)}))},[y]);const B=t.useCallback(()=>{if(c==="custom"){const n=r.trim(),_={mode:"custom",title:b.trim(),body:p.trim()};return n?{..._,url:n}:_}return{mode:"campaign",campaignId:f,notes:{note1:x.note1.trim(),note2:x.note2.trim(),note3:x.note3.trim()}}},[c,b,p,r,f,x]),d=t.useCallback(()=>{const n={};return P.trim()&&(n.region=P.trim()),L&&(n.channel=L),n},[P,L]),l=Ae({mutationFn:async()=>{const n=B(),_=d(),ee=n.mode==="campaign"?{...n,targetFilter:_,resendToAlreadySent:j}:{...n,targetFilter:_};return we("/admin/push/send",ee)},onSuccess:n=>{m({title:"발송 요청 완료",description:n?.message||"앱 푸시 발송을 시작했습니다."}),g.invalidateQueries({queryKey:["push-logs"]}),g.invalidateQueries({queryKey:["push-target-count"]}),o?.()},onError:n=>{m({title:"발송 실패",description:n.message,variant:"destructive"})}}),R=Ae({mutationFn:async n=>we("/admin/push/test-send",{...B(),targetEmail:n}),onSuccess:n=>{if(n&&n.success===!1){m({title:"테스트 발송 실패",description:n.message,variant:"destructive"});return}m({title:"테스트 발송 완료",description:n?.message||"해당 계정의 기기로 발송했습니다."}),g.invalidateQueries({queryKey:["push-logs"]})},onError:n=>{m({title:"테스트 발송 실패",description:n.message,variant:"destructive"})}}),Y=t.useCallback(n=>{S(n)},[]),he=t.useCallback((n,_)=>{v(ee=>({...ee,[n]:ae(_,fe)}))},[]),Z=t.useCallback(n=>{E(ae(n,Me))},[]),Ee=t.useCallback(n=>{T(ae(n,Pe))},[]),ve=t.useCallback(()=>{N("campaign"),S(""),w(""),v({note1:"",note2:"",note3:""}),h(!1),E(""),T(""),k(""),A(""),q(""),K(Re),X.current=null},[]),De=t.useMemo(()=>
14{if(!D)return[];const n=u.trim().toLowerCase();return n?D.filter(_=>{const ee=_.title?.toLowerCase()||"",ge=le(_).toLowerCase(),i=_.dealNumber?.toLowerCase()||"";return ee.includes(n)||ge.includes(n)||i.includes(n)}):D},[D,u]),be=t.useMemo(()=>c==="custom"?!b.trim()&&!p.trim()?null:it({title:b,body:p,url:r}):y?rt(y,x):null,[c,b,p,r,y,x]),G=t.useMemo(()=>ct({mode:c,selectedCampaignId:f,notes:x,customTitle:b,customBody:p,customUrl:r}),[c,f,x,b,p,r]),W=t.useCallback(()=>G?(m({title:G.title,description:G.description,variant:"destructive"}),!1):!0,[G,m]),Ce=t.useCallback(()=>{W()&&l.mutate()},[W,l]),ye=t.useCallback(()=>{if(!W())return;const n=U.trim();if(!n||!n.includes("@")){m({title:"이메일을 확인해주세요.",description:"테스트 푸시를 받을 계정 이메일을 입력해주세요.",variant:"destructive"});return}R.mutate(n)},[W,U,R,m]),_e=t.useCallback(()=>{const n=[];return P.trim()&&n.push(`지역: ${P.trim()}`),L&&n.push(`채널: ${tt[L]||L}`),n.length>0?n.join(" + "):"전체 마케팅 동의자"},[P,L]),J=I?.dedupedCount??I?.count??0,V=I?.effectiveCount??I?.count??0,te=c==="custom"||j?J:V,pe=t.useMemo(()=>l.isPending?"발송 요청을 처리하는 중입니다.":G?G.blockLabel:z?"발송 대상 수를 확인하는 중입니다.":te===0?c==="campaign"&&!j&&J>0?"이 조건의 대상이 모두 이 캠페인 푸시를 이미 받았습니다. 다시 보내려면 재발송을 체크하세요.":"조건에 해당하는 발송 대상이 없습니다.":null,[l.isPending,G,z,te,c,j,J]);return{sendBlockReason:pe,isSendDisabled:pe!==null,mode:c,setMode:N,campaigns:D,filteredCampaigns:De,campaignSearch:u,setCampaignSearch:w,selectedCampaignId:f,selectedCampaign:y,selectCampaign:Y,notes:x,setNote:he,customTitle:b,setCustomTitle:Z,customBody:p,setCustomBody:Ee,customUrl:r,setCustomUrl:k,normalizeCustomUrl:t.useCallback(()=>{k(n=>st(n))},[]),targetRegion:P,setTargetRegion:A,targetChannel:L,setTargetChannel:q,resendToAlreadySent:j,setResendToAlreadySent:h,targetCount:I?.count??0,alreadySentCount:I?.alreadySentCount??0,effectiveTargetCount:te,excludedTargetCount:c==="custom"?0:Math.max(0,J-V),isTargetCountLoading:z,preview:be,testEmail:U,setTestEmail:K,handleTestSend:ye,isTestSending:R.isPending,handleSend:Ce,isSending:l.isPending,isContentReady:G===null,resetState:ve,getTargetLabel:_e,NOTE_MAX_LENGTH:fe,PUSH_TITLE_MAX_INPUT:Me,PUSH_BODY_MAX_INPUT:Pe,PUSH_OPT_OUT_NOTICE:Fe}}const ot=ke.memo(function({open:a,onOpenChange:o}){const[m,g]=t.useState(!1),c=ke.useRef(null),{mode:N,setMode:f,filteredCampaigns:S,campaignSearch:u,setCampaignSearch:w,selectedCampaignId:x,selectedCampaign:v,selectCampaign:j,notes:h,setNote:b,customTitle:E,setCustomTitle:p,customBody:T,setCustomBody:r,customUrl:k,setCustomUrl:P,normalizeCustomUrl:A,targetRegion:L,setTargetRegion:q,targetChannel:U,setTargetChannel:K,resendToAlreadySent:D,setResendToAlreadySent:O,effectiveTargetCount:I,excludedTargetCount:z,alreadySentCount:y,isTargetCountLoading:X,preview:B,testEmail:d,setTestEmail:l,handleTestSend:R,isTestSending:Y,handleSend:he,isSending:Z,isContentReady:Ee,sendBlockReason:ve,isSendDisabled:De,resetState:be,getTargetLabel:G,NOTE_MAX_LENGTH:W,PUSH_TITLE_MAX_INPUT:Ce,PUSH_BODY_MAX_INPUT:ye,PUSH_OPT_OUT_NOTICE:_e}=dt({enabled:a,onSuccess:()=>{g(!1),o(!1),c.current?.()}});c.current=be;const J=()=>{o(!1),be()};if(!a)return null;const V=N==="custom",te=k.trim(),pe=te&&!Ls(te)?Fs:"",n=Y||!Ee,_=V?E.trim():v?le(v)?`${le(v)} (${v.title})`:v.title:"",ee=_e.trim(),ge=N==="custom"?{target:"④",preview:"⑤",test:"⑥"}:{target:"③",preview:"④",test:"⑤"};return e.jsx(Ue,{open:a,onOpenChange:i=>{i||J()},children:e.jsxs(qe,{className:"max-w-3xl max-h-[92vh] flex flex-col overflow-hidden","data-testid":"dialog-push-send",children:[e.jsxs(Ke,{className:"pb-1",children:[e.jsxs(Xe,{className:"flex items-center gap-2",children:[e.jsx(Ss,{className:"h-5 w-5 text-blue-500"}),"앱 푸쉬 발송"]}
14),e.jsx(Qe,{children:"앱을 설치하고 마케팅 수신에 동의한 회원의 기기로 알림을 보냅니다. 캠페인 알림과 직접 입력 중 하나를 고르세요."})]}),e.jsxs("div",{className:"flex-1 min-h-0 overflow-y-auto pr-1 space-y-4",children:[e.jsxs(Rs,{value:N,onValueChange:i=>f(i==="custom"?"custom":"campaign"),children:[e.jsxs(Bs,{className:"grid w-full grid-cols-2","data-testid":"tabs-push-mode",children:[e.jsx($e,{value:"campaign","data-testid":"tab-push-campaign",children:"캠페인 알림"}),e.jsx($e,{value:"custom","data-testid":"tab-push-custom",children:"직접 입력"})]}),e.jsxs(Ie,{value:"campaign",className:"space-y-4 mt-3",children:[e.jsxs("section",{className:"space-y-2",children:[e.jsx(F,{className:"text-xs font-semibold",children:"① 캠페인 선택 (모집중 · 상시)"}),e.jsxs("div",{className:"relative",children:[e.jsx(Le,{className:"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground"}),e.jsx($,{placeholder:"캠페인명·업체명·딜번호 검색",value:u,onChange:i=>w(i.target.value),className:"pl-8 h-9 text-sm","data-testid":"input-push-campaign-search"})]}),e.jsx(bs,{className:"h-44 border rounded-lg p-1.5","data-testid":"list-push-campaigns",children:S.length===0?e.jsx("div",{className:"text-center py-10 text-muted-foreground text-sm",children:u?"조건에 맞는 캠페인이 없습니다.":"발송할 수 있는 캠페인이 없습니다."}):e.jsx("div",{className:"space-y-1",children:S.map(i=>e.jsxs("label",{className:`flex items-center gap-2.5 px-2.5 py-2 border rounded-md cursor-pointer hover:bg-muted/50 transition-colors ${x===i.id?"border-primary bg-primary/5":""}`,"data-testid":`push-campaign-option-${i.id}`,children:[e.jsx("input",{type:"radio",name:"push-campaign",className:"h-4 w-4 accent-primary flex-shrink-0",checked:x===i.id,onChange:()=>j(i.id)}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0",children:[e.jsx("span",{className:"font-medium text-sm truncate",children:le(i)?`${le(i)} (${i.title})`:i.title}),i.mainChannel&&e.jsx("span",{className:`px-1.5 py-0.5 rounded text-[10px] font-medium shrink-0 ${Te(i.mainChannel).colorClass}`,"data-testid":`campaign-channel-badge-${i.id}`,children:Te(i.mainChannel).label})]}),e.jsxs("div",{className:"flex items-center gap-1.5 text-xs text-muted-foreground mt-0.5",children:[i.isAlwaysAnnouncement&&e.jsx("span",{className:"px-1.5 py-0.5 rounded bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-200 text-[10px] font-medium shrink-0","data-testid":`campaign-always-badge-${i.id}`,children:"상시"}),i.dealNumber&&e.jsxs("span",{className:"text-primary font-mono",children:["#",i.dealNumber]}),i.region&&e.jsx("span",{children:i.region})]})]})]},i.id))})})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsx(F,{className:"text-xs font-semibold",children:"② 문구 변수 (자동 채움 · 수정 가능)"}),e.jsxs("div",{className:"grid gap-2 sm:grid-cols-3",children:[e.jsxs("div",{children:[e.jsx(F,{htmlFor:"push-note1",className:"text-[11px] text-muted-foreground",children:"상품명"}),e.jsx($,{id:"push-note1",value:h.note1,maxLength:W,onChange:i=>b("note1",i.target.value),placeholder:"예: 똑똑카페 강남점",className:"h-9 text-sm","data-testid":"input-push-note1"})]}),e.jsxs("div",{children:[e.jsx(F,{htmlFor:"push-note2",className:"text-[11px] text-muted-foreground",children:"체험혜택"}),e.jsx($,{id:"push-note2",value:h.note2,maxLength:W,onChange:i=>b("note2",i.target.value),placeholder:"예: 2인 코스요리 무료 제공",className:"h-9 text-sm","data-testid":"input-push-note2"})]}),e.jsxs("div",{children:[e.jsx(F,{htmlFor:"push-note3",className:"text-[11px] text-muted-foreground",children:"마감일"}),e.jsx($,{id:"push-note3",value:h.note3,maxLength:W,onChange:i=>b("note3",i.target.value),placeholder:"예: 2026. 08. 01.",className:"h-9 text-sm","data-testid":"input-push-note3"})]})]}),e.jsx("p",{className:"text-[11px] text-muted-foreground",children:"알림을 탭하면 선택한 캠페인 상세로 이동합니다. 세 변수는 모두 채워야 발송할 수 있습니다."})]})]}
14),e.jsxs(Ie,{value:"custom",className:"space-y-4 mt-3",children:[e.jsxs("div",{className:"flex items-start gap-2 text-xs text-destructive border border-destructive/40 rounded-md px-3 py-2","data-testid":"text-push-custom-warning",children:[e.jsx(Se,{className:"h-3.5 w-3.5 mt-0.5 flex-shrink-0"}),e.jsx("span",{children:"직접 입력 푸쉬는 중복 발송 방지 기능이 없습니다. 중단 후 다시 보내면 이미 받은 사람도 다시 받습니다."})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(F,{htmlFor:"push-custom-title",className:"text-xs font-semibold",children:"① 제목"}),e.jsxs("span",{className:"text-[11px] text-muted-foreground",children:[E.length,"/",Ce]})]}),e.jsx($,{id:"push-custom-title",value:E,maxLength:Ce,onChange:i=>p(i.target.value),placeholder:"예: 오늘 마감! 놓치면 후회할 체험단",className:"h-9 text-sm","data-testid":"input-push-title"}),e.jsxs("p",{className:"text-[11px] text-muted-foreground",children:["앞에 ",e.jsx("span",{className:"font-mono",children:"[똑똑리워드]"})," 가 자동으로 붙습니다."]})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(F,{htmlFor:"push-custom-body",className:"text-xs font-semibold",children:"② 본문"}),e.jsxs("span",{className:"text-[11px] text-muted-foreground",children:[T.length,"/",ye]})]}),e.jsx(Os,{id:"push-custom-body",value:T,maxLength:ye,onChange:i=>r(i.target.value),placeholder:"알림에 보일 문구를 적어주세요.",className:"text-sm min-h-[84px]","data-testid":"input-push-body"}),e.jsxs("p",{className:"text-[11px] text-muted-foreground",children:["뒤에 수신거부 안내(",e.jsx("span",{className:"font-mono",children:ee}),")가 자동으로 덧붙습니다. 지울 수 없습니다."]})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsx(F,{htmlFor:"push-custom-url",className:"text-xs font-semibold",children:"③ 딥링크 경로 (선택)"}),e.jsx($,{id:"push-custom-url",value:k,onChange:i=>P(i.target.value),onBlur:A,onPaste:()=>window.setTimeout(A,0),placeholder:"/campaigns/D-2608-0212",className:"h-9 text-sm font-mono","data-testid":"input-push-url"}),pe?e.jsx("p",{className:"text-[11px] text-destructive","data-testid":"text-push-url-error",children:pe}):e.jsx("p",{className:"text-[11px] text-muted-foreground",children:"앱 내부 경로만 넣습니다. 우리 사이트 주소(https://ddok.it/…)를 붙여넣으면 내부 경로로 자동 변환됩니다. 비우면 앱 첫 화면으로 갑니다."})]})]})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsxs(F,{className:"text-xs font-semibold",children:[ge.target," 발송 대상 (마케팅 수신 동의 · 앱 알림 토큰 보유)"]}),e.jsxs("div",{className:"grid gap-2 sm:grid-cols-3",children:[e.jsx($,{placeholder:"지역 (예: 서울)",value:L,onChange:i=>q(i.target.value),className:"h-9 text-sm","data-testid":"input-push-target-region"}),e.jsxs(oe,{value:U||"_all",onValueChange:i=>K(i==="_all"?"":i),children:[e.jsx(me,{className:"h-9 text-sm","data-testid":"select-push-target-channel",children:e.jsx(ue,{placeholder:"대상 채널"})}),e.jsxs(xe,{children:[e.jsx(C,{value:"_all",children:"전체 채널"}),e.jsx(C,{value:"blog",children:"블로그"}),e.jsx(C,{value:"instagram",children:"인스타그램"}),e.jsx(C,{value:"youtube",children:"유튜브"}),e.jsx(C,{value:"naverClip",children:"네이버 클립"}),e.jsx(C,{value:"threads",children:"스레드"}),e.jsx(C,{value:"tiktok",children:"틱톡"})]})]}),e.jsxs("div",{className:"flex items-center gap-1.5 px-3 h-9 bg-muted rounded-md text-sm","data-testid":"badge-push-target-count",children:[X?e.jsx(se,{className:"h-3.5 w-3.5 text-muted-foreground flex-shrink-0 animate-spin"}):e.jsx(ys,{className:"h-3.5 w-3.5 text-muted-foreground flex-shrink-0"}),e.jsxs("span",{className:"truncate",children:["대상 ",e.jsxs("strong",{className:"text-primary",children:[I,"명"]}),!V&&x&&z>0&&!D&&e.jsxs("span",{className:"text-xs text-muted-foreground",children:[" (이미 발송 ",z,"명 제외)"]})]})]})]}),!V&&x&&y>0&&e.jsxs("p",{className:"text-[11px] text-muted-foreground","data-testid":"text-push-campaign-send-history",children:["필터와 무관하게 이 캠페인 누적 발송 ",e.jsxs("strong",{children:[y.toLocaleStr
14ing(),"명"]})]}),!V&&e.jsxs("label",{className:"flex items-start gap-2 text-sm cursor-pointer","data-testid":"label-push-resend",children:[e.jsx(Ve,{checked:D,onCheckedChange:i=>O(i===!0),className:"mt-0.5","data-testid":"checkbox-push-resend"}),e.jsxs("span",{children:["이미 발송된 대상 포함 재발송",D&&e.jsxs("span",{className:"text-xs text-destructive mt-0.5 flex items-center gap-1","data-testid":"text-push-resend-warning",children:[e.jsx(Se,{className:"h-3 w-3"}),"같은 캠페인 알림을 이미 받은 리뷰어에게 중복 발송됩니다. 앱 알림 차단·삭제 위험이 있으니 꼭 필요한 경우에만 사용하세요."]})]})]})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsxs(F,{className:"text-xs font-semibold",children:[ge.preview," 미리보기 (잠금화면 알림)"]}),e.jsxs("div",{className:"rounded-lg p-3 bg-zinc-200 dark:bg-zinc-800","data-testid":"preview-push",children:[e.jsxs("div",{className:"max-w-sm rounded-xl border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-900 shadow-sm px-3 py-2.5",children:[e.jsxs("div",{className:"flex items-center gap-1.5 text-[11px] text-zinc-500 dark:text-zinc-400 mb-1",children:[e.jsx(zs,{className:"h-3 w-3 flex-shrink-0"}),e.jsx("span",{className:"font-medium",children:"똑똑리워드"}),e.jsx("span",{children:"· 지금"})]}),e.jsx("div",{className:"text-[13px] font-semibold text-zinc-900 dark:text-zinc-100 break-words","data-testid":"push-preview-title",children:B?B.title:"(제목이 여기에 표시됩니다)"}),e.jsx("div",{className:"text-[12px] text-zinc-700 dark:text-zinc-300 leading-relaxed break-words mt-0.5","data-testid":"push-preview-body",children:B?B.body:"(본문이 여기에 표시됩니다)"})]}),B?.url&&e.jsxs("p",{className:"text-[11px] text-zinc-700 dark:text-zinc-300 mt-2","data-testid":"push-preview-link",children:["탭하면 이동: ",e.jsx("span",{className:"font-mono",children:B.url})]}),e.jsx("p",{className:"text-[11px] text-zinc-700 dark:text-zinc-300 mt-1",children:"* 실제 표시는 기기·OS·잠금화면 설정에 따라 달라질 수 있습니다(줄 수가 줄거나 접힐 수 있음)."})]})]}),e.jsxs("section",{className:"space-y-2",children:[e.jsxs(F,{className:"text-xs font-semibold",children:[ge.test," 테스트 발송 (1건)"]}),e.jsxs("div",{className:"flex flex-col sm:flex-row gap-2",children:[e.jsx($,{placeholder:"테스트 수신 계정 이메일",value:d,onChange:i=>l(i.target.value),className:"h-9 text-sm sm:max-w-xs","data-testid":"input-push-test-email"}),e.jsxs(M,{variant:"outline",onClick:R,disabled:n,"data-testid":"button-push-test-send",children:[Y?e.jsx(se,{className:"h-4 w-4 mr-2 animate-spin"}):e.jsx(Ne,{className:"h-4 w-4 mr-2"}),"테스트 발송"]})]}),e.jsx("p",{className:"text-[11px] text-muted-foreground",children:"해당 계정으로 앱에 로그인된 기기로 1건 발송됩니다. 앱 미설치·미로그인이면 실패합니다."})]})]}),e.jsx(Ye,{className:"pt-2 flex-shrink-0",children:e.jsxs("div",{className:"flex w-full flex-col gap-2 sm:flex-row sm:items-center sm:justify-between",children:[ve?e.jsxs("p",{className:"flex items-start gap-1.5 text-xs text-destructive min-w-0","data-testid":"text-push-send-blocked",children:[e.jsx(Se,{className:"h-3.5 w-3.5 flex-shrink-0 mt-0.5"}),e.jsx("span",{children:ve})]}):e.jsx("span",{}),e.jsxs("div",{className:"flex gap-2 sm:flex-shrink-0",children:[e.jsx(M,{variant:"outline",onClick:J,"data-testid":"button-push-cancel-send",children:"취소"}),e.jsx(M,{onClick:()=>g(!0),disabled:De,"data-testid":"button-open-push-confirm",children:Z?e.jsxs(e.Fragment,{children:[e.jsx(se,{className:"h-4 w-4 mr-2 animate-spin"}),"발송 중..."]}):e.jsxs(e.Fragment,{children:[e.jsx(Ne,{className:"h-4 w-4 mr-2"}),I,"명에게 발송"]})})]})]})}),e.jsx(xs,{open:m,onOpenChange:g,children:e.jsxs(hs,{"data-testid":"dialog-push-confirm-send",children:[e.jsxs(ps,{children:[e.jsx(gs,{children:"앱 푸쉬를 발송할까요?"}),e.jsx(js,{asChild:!0,children:e.jsxs("div",{className:"space-y-1.5 text-sm",children:[e.jsxs("div",{children:[e.jsxs("strong",{children:[V?"제목":"캠페인",":"]})," ",_||"-"]}),e.jsxs("div",{children:[e.jsx("strong",{children:"대상:"})," ",G()," · ",e.jsxs("span",{className:"text-primary font-semibold",children:[I,"명"]})]}),V?e.jsx("div",{className:"text-xs text-destructive",children:"직접 입력 푸쉬는 중복 발송 방지 기능이 없습니다. 중단 후 다시 보내면 이미 받은 사람도 다시 받습니다."}):x&&(D||z>0)&&e.jsx("div",{className:"text-xs text-muted-foreground",children:D?"이미 발송된 대상도 포함해 재발송합니다.":`이미 발송된 ${z}명은 제외됩니다.`}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"광고성 메시지는 야간(21시~08시)에는 발송할 수 없습니다."})]})})]}),e.jsxs(fs,{children:[e.jsx(Ns,{"data-testid":"button-push-cancel-confirm",children:"취소"}),e.jsx(vs,{onClick:i=>{i.preventDefault(),he()},disabled:Z,"data-testid":"button-push-confirm-send",children:Z?e.jsxs(e.Fragment,{children:[e.jsx(se,{className:"h-4 w-4 mr-2 animate-spin"}),"발송 중..."]}):"발송하기"})]})]})})]})})}),Oe=20;function mt(s={}){const{enabled:a=!0,campaignId:o}=s,[m,g]=t.useState(1),[c,N]=t.useState(""),[f,S]=t.useState("all"),[u,w]=t.useState("all"),{data:x,isLoading:v,refetch:j}=ce({queryKey:["push-logs",m,c,f,u,o??""],queryFn:async()=>
14{const r=new URLSearchParams;return r.set("page",m.toString()),r.set("limit",String(Oe)),c&&r.set("search",c),f!=="all"&&r.set("sendType",f),u!=="all"&&r.set("success",u),o&&r.set("campaignId",o),de(`/admin/push/logs?${r}`)},enabled:a}),h=t.useCallback(r=>{N(r),g(1)},[]),b=t.useCallback(r=>{S(r),g(1)},[]),E=t.useCallback(r=>{w(r),g(1)},[]),p=t.useCallback(r=>{g(r)},[]),T=t.useCallback(()=>{j()},[j]);return{data:x,isLoading:v,page:m,search:c,sendTypeFilter:f,successFilter:u,handleSearchChange:h,handleSendTypeFilterChange:b,handleSuccessFilterChange:E,handlePageChange:p,handleRefresh:T,PAGE_SIZE:Oe}}function ut(s){if(!s)return"-";const a=new Date(s);return Number.isNaN(a.getTime())?"-":Ts(a,"M/d HH:mm",{locale:As})}function xt(){const{data:s,isLoading:a,page:o,search:m,sendTypeFilter:g,successFilter:c,handleSearchChange:N,handleSendTypeFilterChange:f,handleSuccessFilterChange:S,handlePageChange:u,handleRefresh:w}=mt(),x=Array.isArray(s?.logs)?s.logs:[],v=typeof s?.total=="number"?s.total:0,j=typeof s?.totalPages=="number"?s.totalPages:0;return e.jsxs(We,{children:[e.jsx(Ze,{children:e.jsxs("div",{className:"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4",children:[e.jsx(Je,{className:"text-lg",children:"푸쉬 발송 내역"}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative",children:[e.jsx(Le,{className:"absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground"}),e.jsx($,{placeholder:"수신자 이름 검색",value:m,onChange:h=>N(h.target.value),className:"pl-9 w-48","data-testid":"input-push-search"})]}),e.jsxs(oe,{value:g,onValueChange:f,children:[e.jsx(me,{className:"w-28","data-testid":"filter-push-send-type",children:e.jsx(ue,{placeholder:"유형"})}),e.jsxs(xe,{children:[e.jsx(C,{value:"all",children:"전체"}),e.jsx(C,{value:"manual",children:"수동"}),e.jsx(C,{value:"test",children:"테스트"})]})]}),e.jsxs(oe,{value:c,onValueChange:S,children:[e.jsx(me,{className:"w-28","data-testid":"filter-push-success",children:e.jsx(ue,{placeholder:"결과"})}),e.jsxs(xe,{children:[e.jsx(C,{value:"all",children:"전체"}),e.jsx(C,{value:"true",children:"성공"}),e.jsx(C,{value:"false",children:"실패"})]})]}),e.jsx(M,{variant:"outline",size:"icon",onClick:w,"data-testid":"button-push-refresh",children:e.jsx(es,{className:"h-4 w-4"})})]})]})}),e.jsx(ss,{children:a?e.jsx("div",{className:"space-y-2",children:[...Array(5)].map((h,b)=>e.jsx(ts,{className:"h-12 w-full"},b))}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(as,{"data-testid":"table-push-logs",children:[e.jsx(ns,{children:e.jsxs(re,{children:[e.jsx(Q,{className:"w-36",children:"발송일시"}),e.jsx(Q,{children:"수신자"}),e.jsx(Q,{className:"w-24",children:"유형"}),e.jsx(Q,{children:"내용"}),e.jsx(Q,{className:"w-20",children:"결과"})]})}),e.jsx(ls,{children:x.length===0?e.jsx(re,{children:e.jsx(H,{colSpan:5,className:"text-center py-12 text-muted-foreground","data-testid":"text-empty-push-logs",children:"발송 내역이 없습니다."})}):x.map(h=>e.jsxs(re,{"data-testid":`push-log-row-${h.id}`,children:[e.jsx(H,{className:"text-sm whitespace-nowrap",children:ut(h.sentAt)}),e.jsx(H,{children:e.jsx("div",{className:"text-sm font-medium",children:h.userName||"-"})}
14),e.jsxs(H,{children:[e.jsx(ie,{variant:h.sendType==="test"?"secondary":"outline",children:h.sendType==="test"?"테스트":"수동"}),e.jsx("div",{className:"text-[11px] text-muted-foreground mt-0.5",children:h.mode==="custom"?"직접 입력":"캠페인"})]}),e.jsxs(H,{children:[e.jsx("div",{className:"max-w-xs text-sm font-medium truncate",children:h.title||"-"}),e.jsx("div",{className:"max-w-xs text-xs text-muted-foreground truncate",children:h.body||"-"}),h.campaignTitle&&e.jsx("div",{className:"max-w-xs text-[11px] text-muted-foreground truncate",children:h.campaignTitle})]}),e.jsx(H,{children:h.success?e.jsxs(ie,{variant:"default",className:"bg-green-500",children:[e.jsx(ws,{className:"h-3 w-3 mr-1"}),"성공"]}):e.jsx(rs,{children:e.jsxs(is,{children:[e.jsx(cs,{asChild:!0,children:e.jsxs(ie,{variant:"destructive",className:"cursor-help",children:[e.jsx(ks,{className:"h-3 w-3 mr-1"}),"실패",h.errorMessage&&e.jsx(ds,{className:"h-3 w-3 ml-1"})]})}),h.errorMessage&&e.jsxs(os,{side:"left",className:"max-w-xs",children:[e.jsx("p",{className:"text-xs font-medium mb-1",children:"실패 사유"}),e.jsx("p",{className:"text-xs",children:h.errorMessage})]})]})})})]},h.id))})]})}),j>1&&e.jsxs("div",{className:"flex items-center justify-between mt-4 pt-4 border-t",children:[e.jsxs("div",{className:"text-sm text-muted-foreground","data-testid":"text-push-total-count",children:["총 ",v,"건"]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(M,{variant:"outline",size:"sm",disabled:o<=1,onClick:()=>u(o-1),"data-testid":"button-push-prev-page",children:e.jsx(ms,{className:"h-4 w-4"})}),e.jsxs("span",{className:"text-sm",children:[o," / ",j]}),e.jsx(M,{variant:"outline",size:"sm",disabled:o>=j,onClick:()=>u(o+1),"data-testid":"button-push-next-page",children:e.jsx(us,{className:"h-4 w-4"})})]})]})]})})]})}function Ft(){const[,s]=Ds(),{user:a}=_s(),[o,m]=t.useState(!1),[g,c]=t.useState(!1),{data:N,isLoading:f,page:S,search:u,sendTypeFilter:w,successFilter:x,handleSearchChange:v,handleSendTypeFilterChange:j,handleSuccessFilterChange:h,handlePageChange:b,handleRefresh:E}=Us({enabled:!!a&&a.role==="admin"});return!a||a.role!=="admin"?null:e.jsxs(e.Fragment,{children:[e.jsx($s,{children:e.jsx("title",{children:"알림톡 발송 - 관리자"})}),e.jsxs("div",{className:"min-h-screen bg-background",children:[e.jsx(Is,{}),e.jsxs("main",{className:"container mx-auto px-4 py-6 max-w-7xl",children:[e.jsx("div",{className:"flex items-center gap-4 mb-6",children:e.jsxs(M,{variant:"ghost",size:"sm",onClick:()=>s("/admin/dashboard"),"data-testid":"button-back",children:[e.jsx(Ms,{className:"h-4 w-4 mr-1"}),"돌아가기"]})}),e.jsxs("div",{className:"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-6",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"text-2xl font-bold flex items-center gap-2",children:[e.jsx(Ge,{className:"h-6 w-6 text-yellow-500"}),"알림톡 발송"]}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"신규 캠페인 알림(카카오 템플릿 103)을 마케팅 수신 동의 리뷰어에게 수동 발송합니다."}),e.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:"※ 광고성 메시지라 야간(21시~08시)에는 발송할 수 없으며, 같은 캠페인을 이미 받은 대상은 기본 제외됩니다."})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs(M,{onClick:()=>c(!0),"data-testid":"button-open-push-dialog",children:[e.jsx(Ss,{className:"h-4 w-4 mr-2"}),"푸쉬 발송"]}
14),e.jsxs(M,{variant:"outline",onClick:()=>m(!0),"data-testid":"button-open-send-dialog",children:[e.jsx(Ne,{className:"h-4 w-4 mr-2"}),"알림톡 발송"]})]})]}),e.jsx(Ws,{open:o,onOpenChange:m}),e.jsx(ot,{open:g,onOpenChange:c}),e.jsxs(We,{children:[e.jsx(Ze,{children:e.jsxs("div",{className:"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4",children:[e.jsx(Je,{className:"text-lg",children:"발송 내역"}),e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsxs("div",{className:"relative",children:[e.jsx(Le,{className:"absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground"}),e.jsx($,{placeholder:"수신자 이름 검색",value:u,onChange:p=>v(p.target.value),className:"pl-9 w-48","data-testid":"input-search"})]}),e.jsxs(oe,{value:w,onValueChange:j,children:[e.jsx(me,{className:"w-28","data-testid":"filter-send-type",children:e.jsx(ue,{placeholder:"유형"})}),e.jsxs(xe,{children:[e.jsx(C,{value:"all",children:"전체"}),e.jsx(C,{value:"manual",children:"수동"}),e.jsx(C,{value:"test",children:"테스트"})]})]}),e.jsxs(oe,{value:x,onValueChange:h,children:[e.jsx(me,{className:"w-28","data-testid":"filter-success",children:e.jsx(ue,{placeholder:"결과"})}),e.jsxs(xe,{children:[e.jsx(C,{value:"all",children:"전체"}),e.jsx(C,{value:"true",children:"성공"}),e.jsx(C,{value:"false",children:"실패"})]})]}),e.jsx(M,{variant:"outline",size:"icon",onClick:E,"data-testid":"button-refresh",children:e.jsx(es,{className:"h-4 w-4"})})]})]})}),e.jsx(ss,{children:f?e.jsx("div",{className:"space-y-2",children:[...Array(5)].map((p,T)=>e.jsx(ts,{className:"h-12 w-full"},T))}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"overflow-x-auto",children:e.jsxs(as,{"data-testid":"table-alimtalk-logs",children:[e.jsx(ns,{children:e.jsxs(re,{children:[e.jsx(Q,{className:"w-36",children:"발송일시"}),e.jsx(Q,{children:"수신자"}),e.jsx(Q,{className:"w-20",children:"유형"}),e.jsx(Q,{children:"캠페인"}),e.jsx(Q,{className:"w-20",children:"결과"})]})}),e.jsx(ls,{children:!N?.logs||N.logs.length===0?e.jsx(re,{children:e.jsx(H,{colSpan:5,className:"text-center py-12 text-muted-foreground","data-testid":"text-empty-logs",children:"발송 내역이 없습니다."})}):N.logs.map(p=>e.jsxs(re,{"data-testid":`log-row-${p.id}`,children:[e.jsx(H,{className:"text-sm whitespace-nowrap",children:Ts(new Date(p.sentAt),"M/d HH:mm",{locale:As})}
14),e.jsxs(H,{children:[e.jsx("div",{className:"text-sm font-medium",children:p.userName||"-"}),e.jsx("div",{className:"text-xs text-muted-foreground",children:p.userPhoneMasked||"-"})]}),e.jsx(H,{children:e.jsx(ie,{variant:p.sendType==="test"?"secondary":"outline",children:p.sendType==="test"?"테스트":"수동"})}),e.jsx(H,{children:e.jsx("div",{className:"max-w-xs text-xs truncate",children:p.campaignTitle||"-"})}),e.jsx(H,{children:p.success?e.jsxs(ie,{variant:"default",className:"bg-green-500",children:[e.jsx(ws,{className:"h-3 w-3 mr-1"}),"성공"]}):e.jsx(rs,{children:e.jsxs(is,{children:[e.jsx(cs,{asChild:!0,children:e.jsxs(ie,{variant:"destructive",className:"cursor-help",children:[e.jsx(ks,{className:"h-3 w-3 mr-1"}),"실패",p.errorMessage&&e.jsx(ds,{className:"h-3 w-3 ml-1"})]})}),p.errorMessage&&e.jsxs(os,{side:"left",className:"max-w-xs",children:[e.jsx("p",{className:"text-xs font-medium mb-1",children:"실패 사유"}),e.jsx("p",{className:"text-xs",children:p.errorMessage})]})]})})})]},p.id))})]})}),N&&N.totalPages>1&&e.jsxs("div",{className:"flex items-center justify-between mt-4 pt-4 border-t",children:[e.jsxs("div",{className:"text-sm text-muted-foreground","data-testid":"text-total-count",children:["총 ",N.total,"건"]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(M,{variant:"outline",size:"sm",disabled:S<=1,onClick:()=>b(S-1),"data-testid":"button-prev-page",children:e.jsx(ms,{className:"h-4 w-4"})}),e.jsxs("span",{className:"text-sm",children:[S," / ",N.totalPages]}),e.jsx(M,{variant:"outline",size:"sm",disabled:S>=N.totalPages,onClick:()=>b(S+1),"data-testid":"button-next-page",children:e.jsx(us,{className:"h-4 w-4"})})]})]})]})})]}),e.jsx("div",{className:"mt-6",children:e.jsx(xt,{})})]})]})]})}export{Ft as default};

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.