Files
Kol-Manager/resources/views/settings/notifications.blade.php
I-Fun d3c35ad730 feat: Add notification services for Email, Telegram, and WhatsApp
- Implement EmailNotifier for sending batch and test notifications via email.
- Implement TelegramNotifier for sending batch and test notifications via Telegram.
- Implement WahaNotifier for sending batch and test notifications via WhatsApp using WAHA API.
- Create migration for client_categories table and update clients table to include category_id.
- Create migration for notification_settings table to manage notification configurations.
- Add ClientCategorySeeder to populate default client categories.
- Create manifest.json for PWA support.
- Implement service worker (sw.js) for caching static assets and offline support.
- Add edit event view for modifying scheduled events.
- Create search results view for displaying search queries.
- Develop notification settings view for configuring notification channels and triggers.
2026-04-01 12:46:58 +07:00

264 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('layouts.app')
@section('title', 'Notification Settings')
@section('page-title', '🔔 Notifications')
@section('page-subtitle', 'Configure Email, Telegram & WhatsApp alerts')
@section('content')
<div class="max-w-3xl mx-auto space-y-6">
{{-- ── How it works ─────────────────────────────────────── --}}
<div class="cat-card p-4 flex gap-3 items-start bg-blue-50/50 border-blue-100">
<span class="text-2xl shrink-0">💡</span>
<div class="text-sm text-gray-600 space-y-1">
<p class="font-semibold text-gray-700">How notifications work</p>
<p>Add one or more channels below. Notifications fire automatically every hour via the scheduler.</p>
<p class="text-xs text-gray-400">To enable the scheduler on your server: add <code class="bg-white px-1 rounded text-xs">* * * * * php /path/to/artisan schedule:run</code> to crontab.</p>
</div>
</div>
{{-- ── Existing channels ────────────────────────────────── --}}
@if($settings->isNotEmpty())
<div class="space-y-3">
@foreach($settings as $setting)
<div class="cat-card p-4" x-data="{ editing: false }">
<div class="flex items-center gap-3">
<span class="text-2xl shrink-0">{{ $setting->channelIcon }}</span>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 flex-wrap">
<span class="font-semibold text-gray-800 text-sm">{{ $setting->name }}</span>
<span class="badge badge-ghost text-xs">{{ $setting->channelLabel }}</span>
@if(! $setting->is_enabled)
<span class="badge badge-error text-xs">Disabled</span>
@endif
</div>
<div class="text-xs text-gray-400 mt-0.5 flex flex-wrap gap-2">
@foreach($setting->triggers ?? [] as $trigger)
<span>{{ \App\Models\NotificationSetting::$triggerMeta[$trigger]['icon'] ?? '🔔' }}
{{ \App\Models\NotificationSetting::$triggerMeta[$trigger]['label'] ?? $trigger }}</span>
@endforeach
<span>⏰ {{ $setting->notify_before_hours }}h before</span>
</div>
</div>
<div class="flex items-center gap-1 shrink-0 flex-wrap justify-end">
{{-- Test --}}
<form action="{{ route('settings.notifications.test', $setting) }}" method="POST">
@csrf
<button type="submit" class="text-xs px-3 py-1.5 rounded-full border border-blue-200 text-blue-500 hover:bg-blue-50">
Test
</button>
</form>
{{-- Toggle --}}
<form action="{{ route('settings.notifications.toggle', $setting) }}" method="POST">
@csrf
<button type="submit"
class="text-xs px-3 py-1.5 rounded-full border {{ $setting->is_enabled ? 'border-amber-200 text-amber-500 hover:bg-amber-50' : 'border-green-200 text-green-500 hover:bg-green-50' }}">
{{ $setting->is_enabled ? 'Disable' : 'Enable' }}
</button>
</form>
{{-- Edit --}}
<button @click="editing = !editing"
class="text-xs px-3 py-1.5 rounded-full border border-pink-200 text-pink-500 hover:bg-pink-50">
Edit
</button>
{{-- Delete --}}
<form action="{{ route('settings.notifications.destroy', $setting) }}" method="POST"
onsubmit="return confirm('Remove this channel?')">
@csrf @method('DELETE')
<button type="submit"
class="text-xs px-3 py-1.5 rounded-full border border-red-100 text-red-400 hover:bg-red-50">
Del
</button>
</form>
</div>
</div>
{{-- Inline edit form --}}
<div x-show="editing" x-transition class="mt-4 pt-4 border-t border-pink-50">
<form action="{{ route('settings.notifications.update', $setting) }}" method="POST"
class="space-y-3">
@csrf @method('PUT')
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Name</label>
<input type="text" name="name" value="{{ $setting->name }}"
class="input-soft text-sm" required>
</div>
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Notify before (hours)</label>
<input type="number" name="notify_before_hours"
value="{{ $setting->notify_before_hours }}"
min="1" max="720" class="input-soft text-sm">
</div>
</div>
{{-- Triggers --}}
<div>
<label class="text-xs font-semibold text-gray-600 block mb-2">Triggers</label>
<div class="flex flex-wrap gap-3">
@foreach(\App\Models\NotificationSetting::$triggerMeta as $key => $meta)
<label class="flex items-center gap-1.5 cursor-pointer text-sm">
<input type="checkbox" name="triggers[]" value="{{ $key }}"
class="accent-pink-400"
{{ in_array($key, $setting->triggers ?? []) ? 'checked' : '' }}>
{{ $meta['icon'] }} {{ $meta['label'] }}
</label>
@endforeach
</div>
</div>
{{-- Channel-specific config --}}
@if($setting->channel === 'email')
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">To Email</label>
<input type="email" name="config[to_email]"
value="{{ $setting->config['to_email'] ?? '' }}"
class="input-soft text-sm" placeholder="you@example.com">
</div>
@elseif($setting->channel === 'telegram')
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Bot Token</label>
<input type="text" name="config[bot_token]"
value="{{ $setting->config['bot_token'] ?? '' }}"
class="input-soft text-sm" placeholder="123456:ABC-DEF...">
</div>
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Chat ID</label>
<input type="text" name="config[chat_id]"
value="{{ $setting->config['chat_id'] ?? '' }}"
class="input-soft text-sm" placeholder="-100123456789">
</div>
</div>
@elseif($setting->channel === 'whatsapp')
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">WAHA URL</label>
<input type="url" name="config[waha_url]"
value="{{ $setting->config['waha_url'] ?? '' }}"
class="input-soft text-sm" placeholder="http://localhost:3000">
</div>
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Session Name</label>
<input type="text" name="config[waha_session]"
value="{{ $setting->config['waha_session'] ?? 'default' }}"
class="input-soft text-sm" placeholder="default">
</div>
<div class="sm:col-span-2">
<label class="text-xs font-semibold text-gray-600 block mb-1">Phone Number</label>
<input type="text" name="config[phone]"
value="{{ $setting->config['phone'] ?? '' }}"
class="input-soft text-sm" placeholder="628123456789">
</div>
</div>
@endif
<button type="submit" class="btn-sakura text-sm px-6 py-2">💾 Save</button>
</form>
</div>
</div>
@endforeach
</div>
@else
<div class="cat-card p-8 text-center text-gray-400">
<span class="text-4xl block mb-2">🔕</span>
<p class="text-sm font-medium">No notification channels yet</p>
<p class="text-xs mt-1">Add one below to get started</p>
</div>
@endif
{{-- ── Add new channel ─────────────────────────────────── --}}
<div class="cat-card p-5" x-data="{ channel: 'email' }">
<h2 class="font-bold text-gray-700 mb-4 flex items-center gap-2">
<span>➕</span> Add Notification Channel
</h2>
<form action="{{ route('settings.notifications.store') }}" method="POST" class="space-y-4">
@csrf
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Name <span class="text-pink-400">*</span></label>
<input type="text" name="name" class="input-soft text-sm"
placeholder="My Telegram Bot" required>
</div>
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Channel <span class="text-pink-400">*</span></label>
<select name="channel" class="input-soft text-sm" x-model="channel">
<option value="email">✉️ Email</option>
<option value="telegram">✈️ Telegram</option>
<option value="whatsapp">💬 WhatsApp (WAHA)</option>
</select>
</div>
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Notify before (hours)</label>
<input type="number" name="notify_before_hours" value="24"
min="1" max="720" class="input-soft text-sm">
</div>
</div>
{{-- Triggers --}}
<div>
<label class="text-xs font-semibold text-gray-600 block mb-2">Send when…</label>
<div class="flex flex-wrap gap-4">
@foreach(\App\Models\NotificationSetting::$triggerMeta as $key => $meta)
<label class="flex items-center gap-1.5 cursor-pointer text-sm">
<input type="checkbox" name="triggers[]" value="{{ $key }}"
class="accent-pink-400" checked>
{{ $meta['icon'] }} {{ $meta['label'] }}
</label>
@endforeach
</div>
</div>
{{-- Email --}}
<div x-show="channel === 'email'" x-transition>
<label class="text-xs font-semibold text-gray-600 block mb-1">To Email <span class="text-pink-400">*</span></label>
<input type="email" name="config[to_email]" class="input-soft text-sm"
placeholder="you@example.com">
</div>
{{-- Telegram --}}
<div x-show="channel === 'telegram'" x-transition class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Bot Token <span class="text-pink-400">*</span></label>
<input type="text" name="config[bot_token]" class="input-soft text-sm"
placeholder="123456:ABC-DEF...">
<p class="text-xs text-gray-400 mt-1">Get from <a href="https://t.me/BotFather" target="_blank" class="text-pink-500">@BotFather</a></p>
</div>
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Chat ID <span class="text-pink-400">*</span></label>
<input type="text" name="config[chat_id]" class="input-soft text-sm"
placeholder="-100123456789 or @username">
<p class="text-xs text-gray-400 mt-1">Use <a href="https://t.me/userinfobot" target="_blank" class="text-pink-500">@userinfobot</a> to find your ID</p>
</div>
</div>
{{-- WhatsApp / WAHA --}}
<div x-show="channel === 'whatsapp'" x-transition class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">WAHA API URL <span class="text-pink-400">*</span></label>
<input type="url" name="config[waha_url]" class="input-soft text-sm"
placeholder="http://localhost:3000">
<p class="text-xs text-gray-400 mt-1">Your self-hosted WAHA instance</p>
</div>
<div>
<label class="text-xs font-semibold text-gray-600 block mb-1">Session Name</label>
<input type="text" name="config[waha_session]" value="default" class="input-soft text-sm">
</div>
<div class="sm:col-span-2">
<label class="text-xs font-semibold text-gray-600 block mb-1">Phone Number <span class="text-pink-400">*</span></label>
<input type="text" name="config[phone]" class="input-soft text-sm"
placeholder="628123456789 (country code, no +)">
</div>
</div>
<button type="submit" class="btn-sakura">🔔 Add Channel</button>
</form>
</div>
</div>
@endsection