Initial Commit

This commit is contained in:
Hosteroid
2025-10-08 14:23:07 +03:00
commit b3b3ac66ff
78 changed files with 14248 additions and 0 deletions

102
app/Views/groups/create.php Normal file
View File

@@ -0,0 +1,102 @@
<?php
$title = 'Create Notification Group';
$pageTitle = 'Create Notification Group';
$pageDescription = 'Set up a new notification group for your domains';
$pageIcon = 'fas fa-plus-circle';
ob_start();
?>
<!-- Main Form -->
<div class="max-w-3xl mx-auto">
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900 flex items-center">
<i class="fas fa-bell text-gray-400 mr-2 text-sm"></i>
Group Information
</h2>
</div>
<div class="p-6">
<form method="POST" action="/groups/store" class="space-y-5">
<!-- Group Name -->
<div>
<label for="name" class="block text-sm font-medium text-gray-700 mb-1.5">
Group Name *
</label>
<input type="text"
id="name"
name="name"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
placeholder="e.g., Production Alerts, Team Notifications"
required
autofocus>
<p class="mt-1.5 text-xs text-gray-500">
Choose a descriptive name for this notification group
</p>
</div>
<!-- Description -->
<div>
<label for="description" class="block text-sm font-medium text-gray-700 mb-1.5">
Description (Optional)
</label>
<textarea id="description"
name="description"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
rows="4"
placeholder="Add details about this notification group, its purpose, or who should be notified..."></textarea>
<p class="mt-1.5 text-xs text-gray-500">
Optional: Add notes to help identify this group's purpose
</p>
</div>
<!-- Action Buttons -->
<div class="flex flex-col sm:flex-row gap-3 pt-3">
<button type="submit"
class="inline-flex items-center justify-center px-5 py-2.5 bg-primary hover:bg-primary-dark text-white rounded-lg font-medium transition-colors text-sm">
<i class="fas fa-plus-circle mr-2"></i>
Create Group
</button>
<a href="/groups"
class="inline-flex items-center justify-center px-5 py-2.5 border border-gray-300 text-gray-700 rounded-lg font-medium hover:bg-gray-50 transition-colors text-sm">
<i class="fas fa-times mr-2"></i>
Cancel
</a>
</div>
</form>
</div>
</div>
<!-- Info Section -->
<div class="mt-4 bg-blue-50 border border-blue-200 rounded-lg p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<div class="w-10 h-10 bg-blue-500 rounded-lg flex items-center justify-center">
<i class="fas fa-info-circle text-white"></i>
</div>
</div>
<div class="ml-3">
<h3 class="text-sm font-semibold text-gray-900 mb-1">Next Steps</h3>
<ul class="text-xs text-gray-600 space-y-1">
<li class="flex items-center">
<i class="fas fa-circle text-blue-500" style="font-size: 6px;"></i>
<span class="ml-2">After creating the group, you'll be able to add notification channels (Email, Telegram, Discord, Slack)</span>
</li>
<li class="flex items-center">
<i class="fas fa-circle text-blue-500" style="font-size: 6px;"></i>
<span class="ml-2">Configure each channel with the necessary credentials and settings</span>
</li>
<li class="flex items-center">
<i class="fas fa-circle text-blue-500" style="font-size: 6px;"></i>
<span class="ml-2">Assign domains to this group to start receiving notifications</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<?php
$content = ob_get_clean();
include __DIR__ . '/../layout/base.php';
?>

304
app/Views/groups/edit.php Normal file
View File

@@ -0,0 +1,304 @@
<?php
$title = 'Edit Notification Group';
$pageTitle = 'Edit Notification Group';
$pageDescription = htmlspecialchars($group['name']);
$pageIcon = 'fas fa-edit';
ob_start();
?>
<div class="max-w-7xl mx-auto space-y-4">
<!-- Group Details Form -->
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900 flex items-center">
<i class="fas fa-info-circle text-gray-400 mr-2 text-sm"></i>
Group Details
</h2>
</div>
<div class="p-6">
<form method="POST" action="/groups/update" class="space-y-5">
<input type="hidden" name="id" value="<?= $group['id'] ?>">
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
<!-- Group Name -->
<div>
<label for="name" class="block text-sm font-medium text-gray-700 mb-1.5">
Group Name *
</label>
<input type="text"
id="name"
name="name"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
value="<?= htmlspecialchars($group['name']) ?>"
required>
</div>
<!-- Description -->
<div>
<label for="description" class="block text-sm font-medium text-gray-700 mb-1.5">
Description (Optional)
</label>
<textarea id="description"
name="description"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
rows="3"><?= htmlspecialchars($group['description'] ?? '') ?></textarea>
</div>
</div>
<div class="flex gap-3">
<button type="submit"
class="inline-flex items-center px-5 py-2.5 bg-primary hover:bg-primary-dark text-white rounded-lg font-medium transition-colors text-sm">
<i class="fas fa-save mr-2"></i>
Update Group
</button>
</div>
</form>
</div>
</div>
<!-- Notification Channels -->
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900 flex items-center">
<i class="fas fa-plug text-gray-400 mr-2 text-sm"></i>
Notification Channels
</h2>
</div>
<div class="p-6">
<?php if (empty($group['channels'])): ?>
<div class="text-center py-10">
<i class="fas fa-plug text-gray-300 text-5xl mb-3"></i>
<p class="text-gray-500">No channels configured yet</p>
<p class="text-sm text-gray-400 mt-1">Add your first channel below to start receiving notifications</p>
</div>
<?php else: ?>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
<?php foreach ($group['channels'] as $channel):
$config = json_decode($channel['channel_config'], true);
$icons = ['email' => 'fa-envelope', 'telegram' => 'fa-telegram', 'discord' => 'fa-discord', 'slack' => 'fa-slack'];
$colors = ['email' => 'blue', 'telegram' => 'blue', 'discord' => 'indigo', 'slack' => 'purple'];
$icon = $icons[$channel['channel_type']] ?? 'fa-bell';
$color = $colors[$channel['channel_type']] ?? 'gray';
?>
<div class="bg-gray-50 border border-gray-200 rounded-lg p-6 hover:shadow-md transition-shadow duration-200">
<div class="flex items-start justify-between mb-4">
<div class="w-12 h-12 bg-<?= $color ?>-100 rounded-lg flex items-center justify-center">
<i class="fab <?= $icon ?> text-<?= $color ?>-600 text-xl"></i>
</div>
<span class="px-3 py-1 rounded-full text-xs font-semibold <?= $channel['is_active'] ? 'bg-green-100 text-green-800' : 'bg-gray-200 text-gray-600' ?>">
<?= $channel['is_active'] ? 'Active' : 'Disabled' ?>
</span>
</div>
<h3 class="font-semibold text-gray-800 mb-2"><?= ucfirst($channel['channel_type']) ?></h3>
<p class="text-sm text-gray-600 mb-4 truncate">
<?php
if ($channel['channel_type'] === 'email') {
echo htmlspecialchars($config['email'] ?? 'No email');
} elseif ($channel['channel_type'] === 'telegram') {
echo "Chat: " . htmlspecialchars($config['chat_id'] ?? 'N/A');
} else {
echo "Webhook configured";
}
?>
</p>
<div class="flex gap-2">
<a href="/channels/toggle?id=<?= $channel['id'] ?>&group_id=<?= $group['id'] ?>"
class="flex-1 px-3 py-2 bg-yellow-50 text-yellow-700 rounded text-center text-sm hover:bg-yellow-100 transition-colors duration-150">
<i class="fas fa-<?= $channel['is_active'] ? 'pause' : 'play' ?> mr-1"></i>
<?= $channel['is_active'] ? 'Disable' : 'Enable' ?>
</a>
<a href="/channels/delete?id=<?= $channel['id'] ?>&group_id=<?= $group['id'] ?>"
class="flex-1 px-3 py-2 bg-red-50 text-red-700 rounded text-center text-sm hover:bg-red-100 transition-colors duration-150"
onclick="return confirm('Delete this channel?')">
<i class="fas fa-trash mr-1"></i>
Delete
</a>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- Add Channel Form -->
<div class="bg-gray-50 rounded-lg p-5 border border-gray-200">
<h3 class="text-sm font-semibold text-gray-900 mb-4 flex items-center">
<i class="fas fa-plus-circle text-gray-400 mr-2 text-sm"></i>
Add New Channel
</h3>
<form method="POST" action="/channels/add" id="channelForm" class="space-y-5">
<input type="hidden" name="group_id" value="<?= $group['id'] ?>">
<!-- Channel Type -->
<div>
<label for="channel_type" class="block text-sm font-medium text-gray-700 mb-1.5">Channel Type</label>
<select id="channel_type"
name="channel_type"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
onchange="toggleChannelFields()">
<option value="">-- Select Channel Type --</option>
<option value="email">Email</option>
<option value="telegram">Telegram</option>
<option value="discord">Discord</option>
<option value="slack">Slack</option>
</select>
</div>
<!-- Email Fields -->
<div id="email_fields" class="hidden space-y-4">
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-1.5">
Email Address
</label>
<input type="email"
id="email"
name="email"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
placeholder="user@example.com">
</div>
</div>
<!-- Telegram Fields -->
<div id="telegram_fields" class="hidden space-y-4">
<div>
<label for="bot_token" class="block text-sm font-medium text-gray-700 mb-1.5">
Bot Token
</label>
<input type="text"
id="bot_token"
name="bot_token"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11">
<p class="mt-1.5 text-xs text-gray-500">
Get from @BotFather on Telegram
</p>
</div>
<div>
<label for="chat_id" class="block text-sm font-medium text-gray-700 mb-1.5">
Chat ID
</label>
<input type="text"
id="chat_id"
name="chat_id"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
placeholder="123456789">
<p class="mt-1.5 text-xs text-gray-500">
Use @userinfobot to get your chat ID
</p>
</div>
</div>
<!-- Discord Fields -->
<div id="discord_fields" class="hidden space-y-4">
<div>
<label for="discord_webhook" class="block text-sm font-medium text-gray-700 mb-1.5">
Webhook URL
</label>
<input type="url"
id="discord_webhook"
name="webhook_url"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
placeholder="https://discord.com/api/webhooks/...">
<p class="mt-1.5 text-xs text-gray-500">
Create in Discord Server Settings → Integrations → Webhooks
</p>
</div>
</div>
<!-- Slack Fields -->
<div id="slack_fields" class="hidden space-y-4">
<div>
<label for="slack_webhook" class="block text-sm font-medium text-gray-700 mb-1.5">
Webhook URL
</label>
<input type="url"
id="slack_webhook"
name="webhook_url"
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm"
placeholder="https://hooks.slack.com/services/...">
<p class="mt-1.5 text-xs text-gray-500">
Create in Slack App Settings → Incoming Webhooks
</p>
</div>
</div>
<button type="submit"
class="inline-flex items-center px-5 py-2.5 bg-primary hover:bg-primary-dark text-white rounded-lg font-medium transition-colors text-sm">
<i class="fas fa-plus mr-2"></i>
Add Channel
</button>
</form>
</div>
</div>
</div>
<!-- Assigned Domains -->
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900 flex items-center">
<i class="fas fa-globe text-gray-400 mr-2 text-sm"></i>
Assigned Domains (<?= count($group['domains']) ?>)
</h2>
</div>
<div class="p-6">
<?php if (empty($group['domains'])): ?>
<div class="text-center py-10">
<i class="fas fa-globe text-gray-300 text-5xl mb-3"></i>
<p class="text-gray-500">No domains assigned to this group yet</p>
<a href="/domains/create" class="mt-3 inline-flex items-center px-5 py-2.5 bg-primary text-white text-sm rounded-lg hover:bg-primary-dark transition-colors font-medium">
<i class="fas fa-plus mr-2"></i>
Add a Domain
</a>
</div>
<?php else: ?>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<?php foreach ($group['domains'] as $domain): ?>
<a href="/domains/<?= $domain['id'] ?>" class="block bg-gray-50 border border-gray-200 rounded-lg p-6 hover:shadow-md hover:border-primary transition-all duration-200">
<div class="flex items-start justify-between mb-3">
<div class="w-12 h-12 bg-primary bg-opacity-10 rounded-lg flex items-center justify-center">
<i class="fas fa-globe text-primary text-xl"></i>
</div>
<?php
$statusClass = $domain['status'] === 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-200 text-gray-600';
?>
<span class="px-3 py-1 rounded-full text-xs font-semibold <?= $statusClass ?>">
<?= ucfirst($domain['status']) ?>
</span>
</div>
<h3 class="font-semibold text-gray-800 mb-2 truncate"><?= htmlspecialchars($domain['domain_name']) ?></h3>
<p class="text-sm text-gray-600 flex items-center">
<i class="far fa-calendar mr-2"></i>
Expires: <?= date('M j, Y', strtotime($domain['expiration_date'])) ?>
</p>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<script>
function toggleChannelFields() {
const channelType = document.getElementById('channel_type').value;
// Hide all fields
document.getElementById('email_fields').classList.add('hidden');
document.getElementById('telegram_fields').classList.add('hidden');
document.getElementById('discord_fields').classList.add('hidden');
document.getElementById('slack_fields').classList.add('hidden');
// Show selected field
if (channelType) {
document.getElementById(channelType + '_fields').classList.remove('hidden');
}
}
</script>
<?php
$content = ob_get_clean();
include __DIR__ . '/../layout/base.php';
?>

156
app/Views/groups/index.php Normal file
View File

@@ -0,0 +1,156 @@
<?php
$title = 'Notification Groups';
$pageTitle = 'Notification Groups';
$pageDescription = 'Manage notification channels and assignments';
$pageIcon = 'fas fa-bell';
ob_start();
?>
<!-- Quick Actions -->
<div class="mb-4 flex justify-end">
<a href="/groups/create" class="inline-flex items-center px-4 py-2.5 bg-primary text-white text-sm rounded-lg hover:bg-primary-dark transition-colors font-medium">
<i class="fas fa-plus mr-2"></i>
Create New Group
</a>
</div>
<!-- Info Card -->
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<i class="fas fa-info-circle text-blue-500 text-lg"></i>
</div>
<div class="ml-3">
<h3 class="text-sm font-semibold text-gray-900 mb-1">About Notification Groups</h3>
<p class="text-xs text-gray-600 leading-relaxed">
Notification groups allow you to organize your notification channels. You can create multiple channels
(Email, Telegram, Discord, Slack) within each group, then assign domains to the group. When a domain
is about to expire, all active channels in its group will receive notifications.
</p>
</div>
</div>
</div>
<!-- Groups List -->
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
<?php if (!empty($groups)): ?>
<!-- Table View (Desktop) -->
<div class="hidden md:block overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Group Name</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Description</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Channels</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">Domains</th>
<th class="px-6 py-4 text-right text-xs font-semibold text-gray-600 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<?php foreach ($groups as $group): ?>
<tr class="hover:bg-gray-50 transition-colors duration-150">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10 bg-primary bg-opacity-10 rounded-lg flex items-center justify-center">
<i class="fas fa-bell text-primary"></i>
</div>
<div class="ml-4">
<div class="text-sm font-semibold text-gray-900"><?= htmlspecialchars($group['name']) ?></div>
</div>
</div>
</td>
<td class="px-6 py-4">
<div class="text-sm text-gray-700 max-w-xs truncate">
<?= htmlspecialchars($group['description'] ?? 'No description') ?>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-blue-100 text-blue-800">
<i class="fas fa-plug mr-1"></i>
<?= $group['channel_count'] ?> channel<?= $group['channel_count'] != 1 ? 's' : '' ?>
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">
<i class="fas fa-globe mr-1"></i>
<?= $group['domain_count'] ?> domain<?= $group['domain_count'] != 1 ? 's' : '' ?>
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div class="flex items-center justify-end space-x-2">
<a href="/groups/edit?id=<?= $group['id'] ?>" class="text-blue-600 hover:text-blue-800" title="Manage">
<i class="fas fa-cog"></i>
</a>
<a href="/groups/delete?id=<?= $group['id'] ?>"
class="text-red-600 hover:text-red-800"
title="Delete"
onclick="return confirm('Are you sure? Domains will be unassigned from this group.')">
<i class="fas fa-trash"></i>
</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Card View (Mobile) -->
<div class="md:hidden divide-y divide-gray-200">
<?php foreach ($groups as $group): ?>
<div class="p-6 hover:bg-gray-50 transition-colors duration-150">
<div class="flex items-start justify-between mb-3">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10 bg-primary bg-opacity-10 rounded-lg flex items-center justify-center">
<i class="fas fa-bell text-primary"></i>
</div>
<div class="ml-3">
<h3 class="font-semibold text-gray-900"><?= htmlspecialchars($group['name']) ?></h3>
<p class="text-sm text-gray-500"><?= htmlspecialchars($group['description'] ?? 'No description') ?></p>
</div>
</div>
</div>
<div class="flex space-x-3 mb-3">
<span class="px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
<i class="fas fa-plug mr-1"></i>
<?= $group['channel_count'] ?> channels
</span>
<span class="px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">
<i class="fas fa-globe mr-1"></i>
<?= $group['domain_count'] ?> domains
</span>
</div>
<div class="flex space-x-2">
<a href="/groups/edit?id=<?= $group['id'] ?>" class="flex-1 px-3 py-1.5 bg-blue-50 text-blue-600 rounded text-center text-sm hover:bg-blue-100 transition-colors">
<i class="fas fa-cog mr-1"></i> Manage
</a>
<a href="/groups/delete?id=<?= $group['id'] ?>"
class="flex-1 px-3 py-1.5 bg-red-50 text-red-600 rounded text-center text-sm hover:bg-red-100 transition-colors"
onclick="return confirm('Are you sure? Domains will be unassigned from this group.')">
<i class="fas fa-trash mr-1"></i> Delete
</a>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="text-center py-12 px-6">
<div class="mb-4">
<i class="fas fa-bell-slash text-gray-300 text-6xl"></i>
</div>
<h3 class="text-lg font-semibold text-gray-700 mb-1">No Notification Groups</h3>
<p class="text-sm text-gray-500 mb-4">Create your first notification group to start receiving alerts</p>
<a href="/groups/create" class="inline-flex items-center px-5 py-2.5 bg-primary text-white text-sm rounded-lg hover:bg-primary-dark transition-colors font-medium">
<i class="fas fa-plus mr-2"></i>
Create Your First Group
</a>
</div>
<?php endif; ?>
</div>
<?php
$content = ob_get_clean();
include __DIR__ . '/../layout/base.php';
?>