- AdminFcmController, AdminFcmService 추가 - FormRequest 검증 (AdminFcmSendRequest 등) - Swagger 문서 추가 (AdminFcmApi.php) - ApiKeyMiddleware: admin/fcm/* 화이트리스트 추가 - FCM 에러 메시지 i18n 추가
169 lines
8.7 KiB
PHP
169 lines
8.7 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('title', 'FCM 테스트 발송')
|
|
|
|
@section('content')
|
|
<div>
|
|
<!-- 페이지 헤더 -->
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h1 class="text-2xl font-bold text-gray-800">FCM 테스트 발송</h1>
|
|
<div class="flex gap-2">
|
|
<a href="{{ route('fcm.tokens') }}" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition">
|
|
토큰 관리
|
|
</a>
|
|
<a href="{{ route('fcm.history') }}" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition">
|
|
발송 이력
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<!-- 발송 폼 -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<h2 class="text-lg font-semibold text-gray-800 mb-4">발송 설정</h2>
|
|
|
|
<form id="send-form"
|
|
hx-post="{{ route('fcm.send.push') }}"
|
|
hx-target="#send-result"
|
|
hx-indicator="#send-indicator">
|
|
@csrf
|
|
<!-- 대상 선택 -->
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">테넌트</label>
|
|
<select name="tenant_id"
|
|
hx-get="{{ route('fcm.preview-count') }}"
|
|
hx-target="#preview-count"
|
|
hx-trigger="change"
|
|
hx-include="#send-form"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
<option value="">전체</option>
|
|
@foreach($tenants as $tenant)
|
|
<option value="{{ $tenant->id }}">{{ $tenant->company_name }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">플랫폼</label>
|
|
<select name="platform"
|
|
hx-get="{{ route('fcm.preview-count') }}"
|
|
hx-target="#preview-count"
|
|
hx-trigger="change"
|
|
hx-include="#send-form"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
<option value="">전체</option>
|
|
<option value="android">Android</option>
|
|
<option value="ios">iOS</option>
|
|
<option value="web">Web</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">사용자 ID (선택)</label>
|
|
<input type="number"
|
|
name="user_id"
|
|
hx-get="{{ route('fcm.preview-count') }}"
|
|
hx-target="#preview-count"
|
|
hx-trigger="change"
|
|
hx-include="#send-form"
|
|
placeholder="특정 사용자에게만 발송"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="my-6">
|
|
|
|
<!-- 메시지 설정 -->
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">제목 *</label>
|
|
<input type="text"
|
|
name="title"
|
|
required
|
|
placeholder="알림 제목"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">내용 *</label>
|
|
<textarea name="body"
|
|
required
|
|
rows="3"
|
|
placeholder="알림 내용"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="my-6">
|
|
|
|
<!-- 고급 설정 -->
|
|
<details class="mb-4">
|
|
<summary class="cursor-pointer text-sm font-medium text-gray-700">고급 설정</summary>
|
|
<div class="mt-4 space-y-4 pl-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">채널 ID</label>
|
|
<input type="text"
|
|
name="channel_id"
|
|
value="push_default"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">알림 타입</label>
|
|
<input type="text"
|
|
name="type"
|
|
placeholder="예: notice, event"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">딥링크 URL</label>
|
|
<input type="text"
|
|
name="url"
|
|
placeholder="예: /notices/123"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">사운드 키</label>
|
|
<input type="text"
|
|
name="sound_key"
|
|
value="default"
|
|
placeholder="예: default, alarm"
|
|
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<!-- 발송 버튼 -->
|
|
<div class="flex items-center justify-between">
|
|
<div class="text-sm text-gray-500">
|
|
대상: <span id="preview-count" hx-get="{{ route('fcm.preview-count') }}" hx-trigger="load">
|
|
<span class="htmx-indicator" id="count-indicator">계산 중...</span>
|
|
</span>
|
|
</div>
|
|
<button type="submit"
|
|
class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg transition flex items-center gap-2">
|
|
<span id="send-indicator" class="htmx-indicator">
|
|
<svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
</svg>
|
|
</span>
|
|
발송
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- 발송 결과 -->
|
|
<div class="bg-white rounded-lg shadow-sm p-6">
|
|
<h2 class="text-lg font-semibold text-gray-800 mb-4">발송 결과</h2>
|
|
<div id="send-result">
|
|
<div class="text-center text-gray-500 py-12">
|
|
발송 버튼을 클릭하면 결과가 여기에 표시됩니다.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|