From ca5c9a34381a34ada4b81df0cb9551dba0b74472 Mon Sep 17 00:00:00 2001 From: chym1217 Date: Thu, 23 Jul 2026 15:11:47 +0900 Subject: [PATCH] =?UTF-8?q?fix(message=5Fbizppurio):=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=ED=86=A1=20=EC=97=B0=EA=B2=B0=20=EB=AA=A8=EB=8B=AC=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=A1=B0=ED=9A=8C=20=EC=8B=A4=ED=8C=A8=EC=99=80=20?= =?UTF-8?q?'=EC=8A=B9=EC=9D=B8=20=ED=85=9C=ED=94=8C=EB=A6=BF=200=EA=B1=B4'?= =?UTF-8?q?=EC=9D=84=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 자격증명 오류 등으로 카카오 템플릿 조회가 실패하면 드롭다운이 비면서 '승인된 템플릿이 없습니다'로 떠서 실제 0건과 구분되지 않던 문제를 수정한다. 데이터소스 fallback 에 load_failed 마커를 실어, 드롭다운이 비었을 때 조회 실패(설정 확인 안내)와 진짜 0건(승인 템플릿 없음)을 문구로 분기한다. 에러 토스트 재도입 없이 모달 내 인라인 문구로만 구분한다. --- .../extensions/notification_tab_core.json | 18 ++++++++-- .../layouts/notification_tab_binding.test.tsx | 35 +++++++++++++++++++ .../resources/lang/en.json | 1 + .../resources/lang/ko.json | 1 + 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/plugins/_bundled/sirsoft-message_bizppurio/resources/extensions/notification_tab_core.json b/plugins/_bundled/sirsoft-message_bizppurio/resources/extensions/notification_tab_core.json index e5d0b3d0..9aa02141 100644 --- a/plugins/_bundled/sirsoft-message_bizppurio/resources/extensions/notification_tab_core.json +++ b/plugins/_bundled/sirsoft-message_bizppurio/resources/extensions/notification_tab_core.json @@ -18,7 +18,7 @@ }, { "id": "bizppurioApprovedTemplates", - "comment": "연결 모달 드롭다운 옵션(카카오 관리 API 위임). auto_fetch:false — 자격증명 미설정 시 카카오 API 422 가 전 탭에서 발화하던 문제 차단(결정 C). 연결 모달 열 때만 조회. errorHandling.default:suppress — 자격증명 미설정 시 422(카카오 API 요청 실패)를 조용히 무시한다(fallback 빈 목록 사용, 안내는 배너·'승인 템플릿 없음' 문구가 담당). 이 토스트를 막지 않으면 모달 열 때마다 에러 토스트가 뜬다.", + "comment": "연결 모달 드롭다운 옵션(카카오 관리 API 위임). auto_fetch:false — 자격증명 미설정 시 카카오 API 422 가 전 탭에서 발화하던 문제 차단(결정 C). 연결 모달 열 때만 조회. errorHandling.default:suppress — 자격증명 미설정 시 422(카카오 API 요청 실패)를 조용히 무시한다(fallback 사용, 토스트 재도입 금지 — 모달 열 때마다 에러 토스트가 뜨던 회귀). 조회 실패와 '진짜 0건'을 화면에서 구분하기 위해 fallback.data.load_failed:true 마커를 싣는다 — 정상 응답에는 이 필드가 없으므로, 드롭다운이 비었을 때 load_failed 유무로 '불러오지 못함(자격증명 확인)' vs '승인 템플릿 없음'을 분기한다.", "type": "api", "endpoint": "/api/plugins/sirsoft-message_bizppurio/admin/notification-bindings/approved-templates", "method": "GET", @@ -31,7 +31,8 @@ }, "fallback": { "data": { - "templates": [] + "templates": [], + "load_failed": true } } } @@ -249,9 +250,20 @@ ] }, { + "comment": "드롭다운이 비었고 조회 실패(fallback 마커)일 때 — 키 오류 등을 '0건'과 구분해 설정 확인을 안내.", "type": "basic", "name": "P", - "if": "{{(bizppurioApprovedTemplates?.data?.templates ?? []).length === 0}}", + "if": "{{(bizppurioApprovedTemplates?.data?.templates ?? []).length === 0 && bizppurioApprovedTemplates?.data?.load_failed === true}}", + "props": { + "className": "text-xs text-red-600 dark:text-red-400 mt-1" + }, + "text": "$t:sirsoft-message_bizppurio.binding.templates_load_failed" + }, + { + "comment": "드롭다운이 비었고 조회는 정상(load_failed 없음)일 때 — 실제 승인 템플릿 0건.", + "type": "basic", + "name": "P", + "if": "{{(bizppurioApprovedTemplates?.data?.templates ?? []).length === 0 && !(bizppurioApprovedTemplates?.data?.load_failed)}}", "props": { "className": "text-xs text-amber-600 dark:text-amber-400 mt-1" }, diff --git a/plugins/_bundled/sirsoft-message_bizppurio/resources/js/__tests__/layouts/notification_tab_binding.test.tsx b/plugins/_bundled/sirsoft-message_bizppurio/resources/js/__tests__/layouts/notification_tab_binding.test.tsx index d179f33a..fc420452 100644 --- a/plugins/_bundled/sirsoft-message_bizppurio/resources/js/__tests__/layouts/notification_tab_binding.test.tsx +++ b/plugins/_bundled/sirsoft-message_bizppurio/resources/js/__tests__/layouts/notification_tab_binding.test.tsx @@ -156,6 +156,41 @@ describe('binding UI — 연결 모달(우리 소유, 코어 편집 모달과 }); }); +describe('binding UI — 드롭다운 조회 실패 vs 0건 구분(결함 3)', () => { + const modal = findById(overlayRoot, 'modal_bizppurio_binding'); + + it('승인 템플릿 데이터소스 fallback 에 load_failed:true 마커가 있다', () => { + const ds = ((overlay as { data_sources?: Array> }).data_sources ?? []) + .find((d) => d.id === 'bizppurioApprovedTemplates'); + const fallback = (ds?.fallback as { data?: Record })?.data ?? {}; + // 조회 실패 시 이 마커가 상태에 실려 '0건'과 구분된다. 정상 응답에는 이 필드가 없다. + expect(fallback.load_failed).toBe(true); + expect(Array.isArray(fallback.templates)).toBe(true); + expect((fallback.templates as unknown[]).length).toBe(0); + }); + + it('드롭다운이 비었을 때 조회 실패(load_failed)면 설정 확인 문구를 노출한다', () => { + const raw = JSON.stringify(modal); + // 조회 실패 분기: length===0 && load_failed===true → templates_load_failed + expect(raw).toContain('binding.templates_load_failed'); + expect(raw).toContain('load_failed === true'); + }); + + it('드롭다운이 비었을 때 조회 정상(0건)이면 승인 템플릿 없음 문구를 노출한다', () => { + const raw = JSON.stringify(modal); + // 0건 분기: length===0 && !load_failed → no_approved_templates + expect(raw).toContain('binding.no_approved_templates'); + expect(raw).toContain('!(bizppurioApprovedTemplates?.data?.load_failed)'); + }); + + it('두 문구는 상호배타 조건이라 동시에 뜨지 않는다(조회실패=빨강 / 0건=amber)', () => { + const raw = JSON.stringify(modal); + // 조회 실패 문구는 red, 0건 문구는 amber 로 시각 구분 + expect(raw).toContain('text-red-600'); + expect(raw).toContain('text-amber-600'); + }); +}); + describe('binding UI — 코어 무오염 + i18n', () => { it('overlay·footer 어디에도 코어 편집 모달 저장 body(notification-templates PUT)를 건드리지 않는다', () => { const all = JSON.stringify(overlay) + JSON.stringify(footer); diff --git a/plugins/_bundled/sirsoft-message_bizppurio/resources/lang/en.json b/plugins/_bundled/sirsoft-message_bizppurio/resources/lang/en.json index 23f7a401..2ac93845 100644 --- a/plugins/_bundled/sirsoft-message_bizppurio/resources/lang/en.json +++ b/plugins/_bundled/sirsoft-message_bizppurio/resources/lang/en.json @@ -80,6 +80,7 @@ "connected_template": "Connected Template", "none": "Not connected", "no_approved_templates": "No approved (sendable) alimtalk templates. Register and inspect a template first.", + "templates_load_failed": "Failed to load the template list. Check the plugin settings (credentials).", "fallback_sms": "Fall back to SMS on failure", "fallback_hint": "If the alimtalk message fails, this notification's body is sent as an SMS instead.", "variables_hint": "Available variables (auto-substituted on send)", diff --git a/plugins/_bundled/sirsoft-message_bizppurio/resources/lang/ko.json b/plugins/_bundled/sirsoft-message_bizppurio/resources/lang/ko.json index d177a645..fab7e0fb 100644 --- a/plugins/_bundled/sirsoft-message_bizppurio/resources/lang/ko.json +++ b/plugins/_bundled/sirsoft-message_bizppurio/resources/lang/ko.json @@ -80,6 +80,7 @@ "connected_template": "연결 템플릿", "none": "연결 안 함", "no_approved_templates": "발송 가능한(승인된) 알림톡 템플릿이 없습니다. 먼저 템플릿을 등록·검수하세요.", + "templates_load_failed": "템플릿 목록을 불러오지 못했습니다. 플러그인 설정(자격증명)을 확인하세요.", "fallback_sms": "실패 시 SMS로 대체발송", "fallback_hint": "알림톡 발송이 실패하면 이 알림의 본문 내용이 문자(SMS)로 대신 발송됩니다.", "variables_hint": "제공 변수 (발송 시 자동 치환)",