비즈뿌리오 알림톡 템플릿을 콘솔 위임(발송 전 실시간 조회) 방식에서 시스템 내 작성(draft) → 검수 신청(requested) → 승인(approved) 라이프사이클로 개편한다. 발송 판정의 SSoT 를 DB(bizppurio_templates)로 옮기고 발송 전 실시간 조회를 폐지. - 알림↔템플릿 바인딩 모델(notification_bindings) 제거, 알림 1건당 템플릿 1행으로 대체 - 검수 신청: template_code 자체 채번(codeCheck 재시도) → kapi add/update → request - 상태 동기화: 30분 스케줄러 + 수동 새로고침, 승인 전이 시 content 를 approved_content 로 동결 - SMS 본문 언어별 입력 복원(수신자 로케일 발송), SMS 단독 선택 시 알림톡 미발송 - 3면(코어/게시판/이커머스) '비즈뿌리오' 통합 탭 + 플러그인 '알림 템플릿 관리' 화면 - 중복 검수 신청은 서버 원자 선점(claimForInspection 조건부 UPDATE)으로 차단 - admin_basic 1.0.6 / board 1.0.5 / ecommerce 1.1.2: 채널 서브탭 통합 탭 선언(tab_channels) 지원 - 검증: 계획서 대비 5라운드 전수검증, §6.3 실측 매트릭스 Playwright 19 고정, PHPUnit 421 / Vitest 228 green, audit 0 error
2 lines
5.0 KiB
JavaScript
2 lines
5.0 KiB
JavaScript
var SirsoftMessageBizppurio=function($){"use strict";const I=window.G7Core?.createLogger?.("MessageBizppurio:InsertVariable")??{log:(...e)=>console.log("[MessageBizppurio:InsertVariable]",...e),warn:(...e)=>console.warn("[MessageBizppurio:InsertVariable]",...e),error:(...e)=>console.error("[MessageBizppurio:InsertVariable]",...e)},D="bz_template_content",B="bz_tpl_modal.content.templateContent";function E(e,n){const t=window.G7Core,r=e.params??{},o=String(r.variable??"").trim(),i=String(r.target??D),l=String(r.stateTarget??"global"),a=String(r.statePath??B);if(o===""){I.warn("삽입할 변수명이 지정되지 않았습니다.");return}const c=l==="local"?t?.state?.setLocal:t?.state?.setGlobal??t?.state?.set,S=l==="local"?t?.state?.getLocal:t?.state?.getGlobal??t?.state?.get;if(!c||!S){I.error("G7Core.state API 를 사용할 수 없습니다.");return}const m=l==="local"?n?.state??S()??{}:S()??{},s=String(C(m,a)??""),d=`#{${o}}`,u=document.querySelector(`textarea[name="${i}"], input[name="${i}"]`),g=(u?.value??"")===s,h=g?u?.selectionStart??s.length:s.length,_=g?u?.selectionEnd??s.length:s.length,N=s.slice(0,h)+d+s.slice(_);if(c({[a]:N}),u){const M=h+d.length;requestAnimationFrame(()=>{try{u.focus(),u.setSelectionRange(M,M)}catch{}})}I.log(`변수 삽입: ${d} → ${l}:${a}`)}function C(e,n){return n.split(".").reduce((t,r)=>t?.[r],e)}const f=window.G7Core?.createLogger?.("MessageBizppurio:UploadImage")??{log:(...e)=>console.log("[MessageBizppurio:UploadImage]",...e),warn:(...e)=>console.warn("[MessageBizppurio:UploadImage]",...e),error:(...e)=>console.error("[MessageBizppurio:UploadImage]",...e)},O="/api/plugins/sirsoft-message_bizppurio/admin/templates/image",L="sirsoft-message_bizppurio.template.form.image_upload_failed";function j(){if(typeof window>"u")return{};const e=localStorage.getItem("g7_locale");return e?{"Accept-Language":e}:{}}function z(e){const n=e?.t?.(L);return typeof n=="string"&&n!==L?n:"Failed to upload the image."}function k(e,n){return n==="local"?e?.state?.setLocal??null:e?.state?.setGlobal??e?.state?.set??null}function F(e,n){return n==="local"?e?.state?.getLocal??null:e?.state?.getGlobal??e?.state?.get??null}function V(e,n){return n.split(".").reduce((t,r)=>t?.[r],e)}function w(e,n,t){const r=F(e,n);if(!r)return!0;try{return V(r()??{},`${t}.uploading`)===!0}catch{return!0}}let P=!1;async function H(e,n){const t=window.G7Core,r=e.params??{},o=String(r.stateTarget??"global"),i=String(r.statePathUrl??"bz_tpl_modal.content.templateImageUrl"),l=String(r.statePathName??"bz_tpl_modal.content.templateImageName"),a=String(r.statePathStatus??"bz_tpl_upload"),c=k(t,o);if(!c){f.error("G7Core.state setter 를 사용할 수 없습니다.");return}const m=(n?.event??e?.event)?.target,s=m?.files?.[0];if(s){if(P){f.warn("이미 업로드가 진행 중입니다 — 새 파일 선택을 무시합니다."),m&&(m.value="");return}P=!0,c({[`${a}.uploading`]:!0,[`${a}.error`]:null});try{const d=localStorage.getItem("auth_token")??"",u=new FormData;u.append("image",s,s.name);const v=await fetch(O,{method:"POST",headers:{Accept:"application/json",...d?{Authorization:`Bearer ${d}`}:{},...j()},body:u}),g=await v.json().catch(()=>({}));if(!v.ok||g?.success===!1){const _=g?.errors?.bizppurio_message||g?.errors?.image?.[0]||g?.message||z(t);w(t,o,a)&&y(c,a,i,l,_),f.warn("이미지 업로드 실패:",_);return}const h=String(g?.data?.url??"").trim();if(h===""){w(t,o,a)&&y(c,a,i,l,z(t)),f.warn("이미지 업로드 응답에 url 이 없습니다:",g);return}if(!w(t,o,a)){f.warn("모달이 다시 시딩되어 업로드 결과를 버립니다:",h);return}c({[`${a}.uploading`]:!1,[`${a}.error`]:null,[i]:h,[l]:s.name}),f.log("이미지 업로드 완료:",h)}catch(d){w(t,o,a)&&y(c,a,i,l,z(t)),f.error("이미지 업로드 예외:",d)}finally{P=!1,m&&(m.value="")}}}function y(e,n,t,r,o){e({[`${n}.uploading`]:!1,[`${n}.error`]:o,[t]:"",[r]:""})}const b={insertVariable:E,uploadTemplateImage:H},p="sirsoft-message_bizppurio",T=window.G7Core?.createLogger?.(`Plugin:${p}`)??{log:(...e)=>console.log(`[Plugin:${p}]`,...e),warn:(...e)=>console.warn(`[Plugin:${p}]`,...e),error:(...e)=>console.error(`[Plugin:${p}]`,...e)};function G(e){Object.entries(b).forEach(([n,t])=>{e.registerHandler(`${p}.${n}`,t,{category:"plugin",source:p})}),T.log(`${Object.keys(b).length} handler(s) registered:`,Object.keys(b).map(n=>`${p}.${n}`))}function U(e){const n=window.G7Core?.getActionDispatcher?.();if(n){G(n);return}if(!e){T.warn("ActionDispatcher 를 찾지 못해 핸들러를 등록하지 못했습니다.");return}let t=0;const r=50,o=()=>{const i=window.G7Core?.getActionDispatcher?.();if(i){G(i);return}++t<=r?setTimeout(o,100):T.error("ActionDispatcher 를 찾지 못해 핸들러 등록에 실패했습니다.")};o()}function A(){if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",()=>U(!0));else{const e=!!window.G7Core?.getActionDispatcher?.();U(!e)}}return A(),typeof window<"u"&&(window.__SirsoftMessageBizppurio={identifier:p,handlers:Object.keys(b),initPlugin:A}),$.initPlugin=A,Object.defineProperty($,Symbol.toStringTag,{value:"Module"}),$}({});
|