- BarobillService에 KAKAOTALK SOAP 클라이언트 추가 - 채널/템플릿 관리, 알림톡/친구톡 발송, 전송조회/예약취소 API - BarobillKakaotalkController (API) 생성: 15개 엔드포인트 - KakaotalkController (페이지) 생성: 5개 페이지 - 라우트 등록 (web.php, api.php) - Blade 뷰 5개 생성: 대시보드, 채널관리, 템플릿관리, 발송, 전송내역 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
195 lines
12 KiB
PHP
195 lines
12 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', '카카오톡 서비스')
|
|
|
|
@section('content')
|
|
<!-- 현재 테넌트 정보 카드 -->
|
|
@if($currentTenant)
|
|
<div class="rounded-xl shadow-lg p-5 mb-6" style="background: linear-gradient(to right, #fee500, #f5c800); color: #3c1e1e;">
|
|
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
|
|
<div class="flex items-center gap-4">
|
|
<div class="p-3 rounded-xl" style="background: rgba(0,0,0,0.1);">
|
|
<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<span class="px-2 py-0.5 rounded-full text-xs font-bold" style="background: rgba(0,0,0,0.1);">T-ID: {{ $currentTenant->id }}</span>
|
|
</div>
|
|
<h2 class="text-xl font-bold">{{ $currentTenant->company_name }} - 카카오톡 서비스</h2>
|
|
</div>
|
|
</div>
|
|
@if($barobillMember)
|
|
<div class="grid grid-cols-2 gap-3 text-sm">
|
|
<div class="rounded-lg p-2" style="background: rgba(0,0,0,0.05);">
|
|
<p class="text-xs" style="color: rgba(60,30,30,0.6);">사업자번호</p>
|
|
<p class="font-medium">{{ $barobillMember->biz_no }}</p>
|
|
</div>
|
|
<div class="rounded-lg p-2" style="background: rgba(0,0,0,0.05);">
|
|
<p class="text-xs" style="color: rgba(60,30,30,0.6);">바로빌 ID</p>
|
|
<p class="font-medium">{{ $barobillMember->barobill_id }}</p>
|
|
</div>
|
|
</div>
|
|
@else
|
|
<div class="flex items-center gap-2" style="color: #7c2d12;">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
</svg>
|
|
<span class="text-sm font-medium">바로빌 회원사 미연동</span>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="flex flex-col h-full">
|
|
<!-- 페이지 헤더 -->
|
|
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4 mb-6 flex-shrink-0">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-gray-800">카카오톡 서비스</h1>
|
|
<p class="text-sm text-gray-500 mt-1">알림톡/친구톡 발송 및 관리</p>
|
|
</div>
|
|
</div>
|
|
|
|
@if(!$barobillMember)
|
|
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-6 text-center">
|
|
<svg class="w-12 h-12 text-yellow-400 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
</svg>
|
|
<h3 class="text-lg font-semibold text-yellow-800 mb-1">바로빌 회원사 연동 필요</h3>
|
|
<p class="text-sm text-yellow-600">카카오톡 서비스를 이용하려면 먼저 바로빌 회원사를 등록해주세요.</p>
|
|
<a href="{{ route('barobill.members.index') }}" class="inline-block mt-4 px-4 py-2 bg-yellow-600 text-white rounded-lg hover:bg-yellow-700 transition text-sm">회원사 관리로 이동</a>
|
|
</div>
|
|
@else
|
|
<!-- 빠른 메뉴 카드 -->
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
|
<!-- 채널 관리 -->
|
|
<a href="{{ route('barobill.kakaotalk.channels') }}" class="bg-white rounded-xl shadow-sm p-6 hover:shadow-md transition group border border-gray-100">
|
|
<div class="w-12 h-12 bg-blue-100 rounded-xl flex items-center justify-center mb-4 group-hover:bg-blue-200 transition">
|
|
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-semibold text-gray-800 mb-1">채널 관리</h3>
|
|
<p class="text-xs text-gray-500">카카오톡 채널 목록 조회 및 관리</p>
|
|
</a>
|
|
|
|
<!-- 템플릿 관리 -->
|
|
<a href="{{ route('barobill.kakaotalk.templates') }}" class="bg-white rounded-xl shadow-sm p-6 hover:shadow-md transition group border border-gray-100">
|
|
<div class="w-12 h-12 bg-green-100 rounded-xl flex items-center justify-center mb-4 group-hover:bg-green-200 transition">
|
|
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z" />
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-semibold text-gray-800 mb-1">템플릿 관리</h3>
|
|
<p class="text-xs text-gray-500">알림톡 템플릿 목록 조회 및 관리</p>
|
|
</a>
|
|
|
|
<!-- 발송 -->
|
|
<a href="{{ route('barobill.kakaotalk.send') }}" class="bg-white rounded-xl shadow-sm p-6 hover:shadow-md transition group border border-gray-100">
|
|
<div class="w-12 h-12 bg-purple-100 rounded-xl flex items-center justify-center mb-4 group-hover:bg-purple-200 transition">
|
|
<svg class="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-semibold text-gray-800 mb-1">메시지 발송</h3>
|
|
<p class="text-xs text-gray-500">알림톡/친구톡 메시지 발송</p>
|
|
</a>
|
|
|
|
<!-- 전송내역 -->
|
|
<a href="{{ route('barobill.kakaotalk.history') }}" class="bg-white rounded-xl shadow-sm p-6 hover:shadow-md transition group border border-gray-100">
|
|
<div class="w-12 h-12 bg-orange-100 rounded-xl flex items-center justify-center mb-4 group-hover:bg-orange-200 transition">
|
|
<svg class="w-6 h-6 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" />
|
|
</svg>
|
|
</div>
|
|
<h3 class="font-semibold text-gray-800 mb-1">전송내역</h3>
|
|
<p class="text-xs text-gray-500">발송 이력 조회 및 예약 관리</p>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- 채널 상태 -->
|
|
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 bg-yellow-100 rounded-lg flex items-center justify-center">
|
|
<svg class="w-5 h-5 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-semibold text-gray-800">채널 현황</h3>
|
|
<p class="text-xs text-gray-500">등록된 카카오톡 채널 목록</p>
|
|
</div>
|
|
</div>
|
|
<button type="button" onclick="loadChannels()" class="px-3 py-1.5 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition text-sm flex items-center gap-1">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
</svg>
|
|
새로고침
|
|
</button>
|
|
</div>
|
|
<div id="channels-container">
|
|
<div class="text-center py-8 text-gray-400">
|
|
<svg class="w-8 h-8 mx-auto mb-2 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
</svg>
|
|
<p class="text-sm">채널 정보 로딩 중...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
@endsection
|
|
|
|
@push('scripts')
|
|
<script>
|
|
function loadChannels() {
|
|
const container = document.getElementById('channels-container');
|
|
if (!container) return;
|
|
|
|
container.innerHTML = '<div class="text-center py-8 text-gray-400"><svg class="w-8 h-8 mx-auto mb-2 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /></svg><p class="text-sm">채널 정보 로딩 중...</p></div>';
|
|
|
|
fetch('/api/admin/barobill/kakaotalk/channels', {
|
|
headers: { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (!data.success) {
|
|
container.innerHTML = '<div class="text-center py-8 text-red-400"><p class="text-sm">' + (data.error || data.message || '채널 조회 실패') + '</p></div>';
|
|
return;
|
|
}
|
|
|
|
const channels = data.data;
|
|
if (!channels || (Array.isArray(channels) && channels.length === 0)) {
|
|
container.innerHTML = '<div class="text-center py-8 text-gray-400"><p class="text-sm">등록된 채널이 없습니다.</p></div>';
|
|
return;
|
|
}
|
|
|
|
let items = Array.isArray(channels) ? channels : (channels.KakaotalkChannel ? (Array.isArray(channels.KakaotalkChannel) ? channels.KakaotalkChannel : [channels.KakaotalkChannel]) : [channels]);
|
|
|
|
let html = '<div class="divide-y divide-gray-100">';
|
|
items.forEach(ch => {
|
|
const statusColor = ch.Status === '정상' || ch.Status === 'Y' ? 'green' : 'gray';
|
|
html += '<div class="flex items-center justify-between py-3">';
|
|
html += '<div class="flex items-center gap-3">';
|
|
html += '<div class="w-8 h-8 bg-yellow-100 rounded-full flex items-center justify-center"><svg class="w-4 h-4 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" /></svg></div>';
|
|
html += '<div><p class="text-sm font-medium text-gray-800">' + (ch.ChannelName || ch.ChannelId || '-') + '</p>';
|
|
html += '<p class="text-xs text-gray-500">ID: ' + (ch.ChannelId || '-') + '</p></div>';
|
|
html += '</div>';
|
|
html += '<span class="px-2 py-0.5 rounded-full text-xs font-medium bg-' + statusColor + '-100 text-' + statusColor + '-700">' + (ch.Status || '-') + '</span>';
|
|
html += '</div>';
|
|
});
|
|
html += '</div>';
|
|
container.innerHTML = html;
|
|
})
|
|
.catch(err => {
|
|
container.innerHTML = '<div class="text-center py-8 text-red-400"><p class="text-sm">채널 조회 중 오류 발생: ' + err.message + '</p></div>';
|
|
});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', loadChannels);
|
|
</script>
|
|
@endpush
|