DNSBlock
This commit is contained in:
74
resources/views/admin/clients/form.php
Normal file
74
resources/views/admin/clients/form.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<div class="max-w-2xl mx-auto bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-6"><?= isset($client) ? 'Editar Cliente' : 'Novo Cliente' ?></h3>
|
||||
|
||||
<form action="<?= isset($client) ? '/admin/clients/update/' . $client['id'] : '/admin/clients/store' ?>"
|
||||
method="POST">
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Razão Social / Nome</label>
|
||||
<input type="text" name="name" value="<?= $client['name'] ?? '' ?>"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">ASN (ex: AS12345)</label>
|
||||
<input type="text" name="asn" value="<?= $client['asn'] ?? '' ?>"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Telegram ID (Opcional)</label>
|
||||
<input type="text" name="telegram_id" value="<?= $client['telegram_id'] ?? '' ?>"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Email Principal (Login)</label>
|
||||
<input type="email" name="email" value="<?= $client['email'] ?? '' ?>"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Email Financeiro</label>
|
||||
<input type="email" name="financial_email" value="<?= $client['financial_email'] ?? '' ?>"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Senha de Acesso
|
||||
<?= isset($client) ? '<span class="text-gray-400 font-normal">(Deixe em branco para manter)</span>' : '' ?></label>
|
||||
<input type="password" name="password"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
<?= isset($client) ? '' : 'required' ?>>
|
||||
</div>
|
||||
|
||||
<?php if (isset($client)): ?>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Status</label>
|
||||
<select name="status"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
||||
<option value="active" <?= $client['status'] == 'active' ? 'selected' : '' ?>>Ativo</option>
|
||||
<option value="inactive" <?= $client['status'] == 'inactive' ? 'selected' : '' ?>>Inativo</option>
|
||||
</select>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<input type="hidden" name="status" value="active">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end space-x-3">
|
||||
<a href="/admin/clients"
|
||||
class="px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg transition-colors">Cancelar</a>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors">
|
||||
<?= isset($client) ? 'Salvar Alterações' : 'Criar Cliente' ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
57
resources/views/admin/clients/index.php
Normal file
57
resources/views/admin/clients/index.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 flex justify-between items-center bg-gray-50">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Clientes Cadastrados</h3>
|
||||
<a href="/admin/clients/create"
|
||||
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
||||
</svg>
|
||||
Novo Cliente
|
||||
</a>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Nome
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">ASN</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Email
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Status
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Ações
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<?php if (!empty($clients)): ?>
|
||||
<?php foreach ($clients as $client): ?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900"><?= htmlspecialchars($client['name']) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= htmlspecialchars($client['asn']) ?></td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= htmlspecialchars($client['email']) ?></td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $client['status'] == 'active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' ?>">
|
||||
<?= $client['status'] == 'active' ? 'Ativo' : 'Inativo' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm font-medium">
|
||||
<a href="/admin/clients/edit/<?= $client['id'] ?>"
|
||||
class="text-[#1e3a8a] hover:text-blue-900 mr-3">Editar</a>
|
||||
<a href="/admin/clients/delete/<?= $client['id'] ?>" onclick="return confirm('Tem certeza?')"
|
||||
class="text-red-600 hover:text-red-900">Excluir</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500">Nenhum cliente encontrado.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
121
resources/views/admin/dashboard.php
Normal file
121
resources/views/admin/dashboard.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<!-- Card 1 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-blue-50 text-blue-600">
|
||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Clientes Ativos</p>
|
||||
<p class="text-2xl font-bold text-gray-900"><?= $stats['clients'] ?? 0 ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 2 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-green-50 text-green-600">
|
||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Servidores Online</p>
|
||||
<p class="text-2xl font-bold text-gray-900"><?= $stats['servers'] ?? 0 ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 3 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-red-50 text-red-600">
|
||||
<svg class="w-8 h-8" 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">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Domínios Bloqueados</p>
|
||||
<p class="text-2xl font-bold text-gray-900"><?= $stats['blocked_domains'] ?? 0 ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 4 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-purple-50 text-purple-600">
|
||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Ordens Totais</p>
|
||||
<p class="text-2xl font-bold text-gray-900"><?= $stats['orders'] ?? 0 ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 flex justify-between items-center bg-gray-50">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Últimas Ordens Judiciais</h3>
|
||||
<a href="/admin/orders" class="text-sm text-primary-600 hover:text-primary-700 font-medium">Ver todas</a>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Título
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tipo
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Início do
|
||||
Bloqueio</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<?php if (!empty($recent_orders)): ?>
|
||||
<?php foreach ($recent_orders as $order): ?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= $order['id'] ?></td>
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900"><?= htmlspecialchars($order['title']) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $order['type'] == 'block' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' ?>">
|
||||
<?= $order['type'] == 'block' ? 'Bloqueio' : 'Desbloqueio' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500">
|
||||
<?= date('d/m/Y', strtotime($order['received_at'])) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm font-medium">
|
||||
<a href="/admin/orders/view/<?= $order['id'] ?>"
|
||||
class="text-primary-600 hover:text-primary-900 hover:underline">Detalhes</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500">Nenhuma ordem registrada.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
84
resources/views/admin/logs/index.php
Normal file
84
resources/views/admin/logs/index.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="p-6 border-b border-gray-100 flex justify-between items-center">
|
||||
<h2 class="text-lg font-semibold text-gray-800">Logs de Conexão e Erros</h2>
|
||||
<span class="text-sm text-gray-500">Mostrando últimos registros</span>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr class="bg-gray-50 text-gray-600 text-sm uppercase tracking-wider">
|
||||
<th class="p-4 font-medium border-b border-gray-100">Data/Hora</th>
|
||||
<th class="p-4 font-medium border-b border-gray-100">Servidor</th>
|
||||
<th class="p-4 font-medium border-b border-gray-100">Ação</th>
|
||||
<th class="p-4 font-medium border-b border-gray-100">Mensagem</th>
|
||||
<th class="p-4 font-medium border-b border-gray-100">IP Origem</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm divide-y divide-gray-100">
|
||||
<?php foreach ($logs as $log): ?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="p-4 text-gray-600 whitespace-nowrap">
|
||||
<?= date('d/m/Y H:i:s', strtotime($log['created_at'])) ?>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<?php if ($log['server_name']): ?>
|
||||
<a href="/admin/servers/edit/<?= $log['server_id'] ?>"
|
||||
class="font-medium text-primary-600 hover:underline">
|
||||
<?= htmlspecialchars($log['server_name']) ?>
|
||||
</a>
|
||||
<div class="text-xs text-gray-400"><?= htmlspecialchars($log['ip_v4']) ?></div>
|
||||
<?php else: ?>
|
||||
<span class="text-gray-400 italic">Desconhecido / Falha Auth</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="p-4">
|
||||
<?php
|
||||
$badgeColor = match ($log['action']) {
|
||||
'sync' => 'bg-green-100 text-green-700',
|
||||
'bind_machine' => 'bg-blue-100 text-blue-700',
|
||||
'auth_failed', 'auth_blocked', 'error' => 'bg-red-100 text-red-700',
|
||||
default => 'bg-gray-100 text-gray-700'
|
||||
};
|
||||
?>
|
||||
<span class="px-2 py-1 rounded-full text-xs font-medium <?= $badgeColor ?>">
|
||||
<?= htmlspecialchars($log['action']) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="p-4 text-gray-700">
|
||||
<?= htmlspecialchars($log['message']) ?>
|
||||
</td>
|
||||
<td class="p-4 text-gray-500 font-mono text-xs">
|
||||
<?= htmlspecialchars($log['ip_address']) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if (empty($logs)): ?>
|
||||
<tr>
|
||||
<td colspan="5" class="p-8 text-center text-gray-500">
|
||||
Nenhum log registrado ainda.
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<?php if ($totalPages > 1): ?>
|
||||
<div class="p-4 border-t border-gray-100 flex justify-center space-x-2">
|
||||
<?php if ($page > 1): ?>
|
||||
<a href="?page=<?= $page - 1 ?>"
|
||||
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-50 text-sm">Anterior</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<span class="px-3 py-1 text-sm text-gray-600">Página <?= $page ?> de <?= $totalPages ?></span>
|
||||
|
||||
<?php if ($page < $totalPages): ?>
|
||||
<a href="?page=<?= $page + 1 ?>"
|
||||
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-50 text-sm">Próxima</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
119
resources/views/admin/orders/create.php
Normal file
119
resources/views/admin/orders/create.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<div class="max-w-3xl mx-auto bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-6">Nova Ordem Judicial</h3>
|
||||
|
||||
<?php if (isset($_SESSION['flash_error'])): ?>
|
||||
<div class="bg-red-50 border-l-4 border-red-500 p-4 mb-6">
|
||||
<p class="text-sm text-red-700"><?= $_SESSION['flash_error'] ?></p>
|
||||
</div>
|
||||
<?php unset($_SESSION['flash_error']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Quill Styles -->
|
||||
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
||||
|
||||
<form action="/admin/orders/store" method="POST" enctype="multipart/form-data" class="space-y-6" id="orderForm">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Título / Identificação da Ordem</label>
|
||||
<input type="text" name="title" required placeholder="Ex: Processo nº 123456 - Bloqueio Jogos"
|
||||
oninvalid="this.setCustomValidity('Por favor, preencha o título da ordem.')"
|
||||
oninput="this.setCustomValidity('')"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 outline-none">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Tipo da Ordem</label>
|
||||
<select name="type" required oninvalid="this.setCustomValidity('Por favor, selecione o tipo da ordem.')"
|
||||
oninput="this.setCustomValidity('')"
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 outline-none">
|
||||
<option value="block">Bloqueio</option>
|
||||
<option value="unblock">Desbloqueio</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Data de Recebimento</label>
|
||||
<input type="date" name="received_at"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required oninvalid="this.setCustomValidity('Por favor, informe a data de recebimento.')"
|
||||
oninput="this.setCustomValidity('')">
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Conteúdo do Email / Descrição</label>
|
||||
<div id="editor" class="bg-white" style="height: 200px;"></div>
|
||||
<input type="hidden" name="content" id="content">
|
||||
</div>
|
||||
|
||||
<div class="col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Arquivo de Domínios (.csv, .txt)</label>
|
||||
<div x-data="{ fileName: '' }" class="mt-1">
|
||||
<label
|
||||
class="flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer hover:border-primary-500 transition-colors bg-white">
|
||||
<div class="space-y-1 text-center">
|
||||
<svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none"
|
||||
viewBox="0 0 48 48" aria-hidden="true">
|
||||
<path
|
||||
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
<div class="flex text-sm text-gray-600 justify-center">
|
||||
<span class="font-medium text-primary-600 hover:text-primary-500">Upload um
|
||||
arquivo</span>
|
||||
<p class="pl-1">ou arraste e solte</p>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500">CSV ou TXT até 10MB</p>
|
||||
<p class="text-sm font-semibold text-gray-800 mt-2"
|
||||
x-text="fileName ? 'Selecionado: ' + fileName : ''"></p>
|
||||
</div>
|
||||
<input type="file" name="csv_file" class="sr-only" accept=".csv,.txt"
|
||||
@change="fileName = $event.target.files[0].name" required
|
||||
oninvalid="this.setCustomValidity('Por favor, selecione um arquivo CSV ou TXT.')"
|
||||
oninput="this.setCustomValidity('')">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-3 pt-4 border-t border-gray-100">
|
||||
<a href="/admin/orders"
|
||||
class="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors">Cancelar</a>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 text-white bg-primary-600 rounded-lg hover:bg-primary-700 transition-colors">Salvar e
|
||||
Processar</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Quill Script -->
|
||||
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
|
||||
<script>
|
||||
var quill = new Quill('#editor', {
|
||||
theme: 'snow',
|
||||
placeholder: 'Cole o conteúdo do email ou descreva a ordem aqui...',
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline', 'strike'],
|
||||
['blockquote', 'code-block'],
|
||||
[{ 'header': 1 }, { 'header': 2 }],
|
||||
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
|
||||
[{ 'script': 'sub' }, { 'script': 'super' }],
|
||||
[{ 'indent': '-1' }, { 'indent': '+1' }],
|
||||
[{ 'direction': 'rtl' }],
|
||||
[{ 'size': ['small', false, 'large', 'huge'] }],
|
||||
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
[{ 'font': [] }],
|
||||
[{ 'align': [] }],
|
||||
['clean'],
|
||||
['link']
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
var form = document.getElementById('orderForm');
|
||||
form.onsubmit = function () {
|
||||
var content = document.querySelector('input[name=content]');
|
||||
content.value = quill.root.innerHTML;
|
||||
};
|
||||
</script>
|
||||
85
resources/views/admin/orders/index.php
Normal file
85
resources/views/admin/orders/index.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 flex justify-between items-center bg-gray-50">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Ordens Judiciais</h3>
|
||||
<a href="/admin/orders/create"
|
||||
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
||||
</svg>
|
||||
Nova Ordem
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_SESSION['flash_success'])): ?>
|
||||
<div class="bg-green-50 border-l-4 border-green-500 p-4 m-6">
|
||||
<p class="text-sm text-green-700"><?= $_SESSION['flash_success'] ?></p>
|
||||
</div>
|
||||
<?php unset($_SESSION['flash_success']); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Título
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tipo</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Início do
|
||||
Bloqueio</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<?php if (!empty($orders)): ?>
|
||||
<?php foreach ($orders as $order): ?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= $order['id'] ?></td>
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900"><?= htmlspecialchars($order['title']) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $order['type'] == 'block' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' ?>">
|
||||
<?= $order['type'] == 'block' ? 'Bloqueio' : 'Desbloqueio' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= date('d/m/Y', strtotime($order['received_at'])) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm font-medium">
|
||||
<a href="/admin/orders/view/<?= $order['id'] ?>"
|
||||
class="text-primary-600 hover:text-primary-900 hover:underline">Detalhes</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500">Nenhuma ordem encontrada.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php if (isset($pagination) && $pagination['total'] > 1): ?>
|
||||
<div class="px-6 py-4 border-t border-gray-100 flex justify-between items-center">
|
||||
<div>
|
||||
<span class="text-sm text-gray-700">
|
||||
Página <span class="font-medium"><?= $pagination['current'] ?></span> de <span
|
||||
class="font-medium"><?= $pagination['total'] ?></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
<?php if ($pagination['prev']): ?>
|
||||
<a href="?page=<?= $pagination['prev'] ?>"
|
||||
class="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50">Anterior</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($pagination['next']): ?>
|
||||
<a href="?page=<?= $pagination['next'] ?>"
|
||||
class="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50">Próxima</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
61
resources/views/admin/orders/view.php
Normal file
61
resources/views/admin/orders/view.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<h3 class="text-xl font-bold text-gray-900"><?= htmlspecialchars($order['title']) ?></h3>
|
||||
<p class="text-sm text-gray-500 mt-1">Recebido em
|
||||
<?= date('d/m/Y H:i', strtotime($order['received_at'])) ?>
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
class="px-3 py-1 rounded-full text-sm font-semibold <?= $order['type'] == 'block' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' ?>">
|
||||
<?= $order['type'] == 'block' ? 'Bloqueio' : 'Desbloqueio' ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-6 border-t border-gray-100 pt-4">
|
||||
<h4 class="text-sm font-medium text-gray-700">Conteúdo / Descrição</h4>
|
||||
<style>
|
||||
.prose a {
|
||||
color: #2563eb;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.prose a:hover {
|
||||
color: #1e40af;
|
||||
}
|
||||
</style>
|
||||
<div class="mt-2 text-gray-600 text-sm prose max-w-none"><?= $order['content'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 bg-gray-50">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Domínios Afetados (<?= count($items) ?>)</h3>
|
||||
</div>
|
||||
<div class="overflow-x-auto max-h-96">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead class="bg-gray-50 sticky top-0">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider">Domínio</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider">Ação Nesta
|
||||
Ordem</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<?php foreach ($items as $item): ?>
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-3 text-sm font-mono text-gray-900"><?= htmlspecialchars($item['name']) ?>
|
||||
</td>
|
||||
<td class="px-6 py-3 text-sm">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $item['action'] == 'block' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' ?>">
|
||||
<?= $item['action'] == 'block' ? 'Bloquear' : 'Desbloquear' ?>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
65
resources/views/admin/profile.php
Normal file
65
resources/views/admin/profile.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<div class="max-w-4xl mx-auto space-y-6">
|
||||
|
||||
<!-- Profile Info -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-6">Informações Pessoais</h3>
|
||||
|
||||
<form action="/admin/profile/update" method="POST" class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nome</label>
|
||||
<input type="text" name="name" value="<?= htmlspecialchars($user['name']) ?>" required
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 outline-none">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
||||
<input type="email" name="email" value="<?= htmlspecialchars($user['email']) ?>" required
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 outline-none">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-4">
|
||||
<button type="submit"
|
||||
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors">
|
||||
Salvar Alterações
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Change Password -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-6">Alterar Senha</h3>
|
||||
|
||||
<form action="/admin/profile/password" method="POST" class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Senha Atual</label>
|
||||
<input type="password" name="current_password" required
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 outline-none">
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nova Senha</label>
|
||||
<input type="password" name="new_password" required
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 outline-none">
|
||||
<p class="text-xs text-gray-500 mt-1">Mínimo 8 caracteres, 1 maiúscula, 1 especial.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Confirmar Nova Senha</label>
|
||||
<input type="password" name="confirm_password" required
|
||||
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 outline-none">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-4">
|
||||
<button type="submit"
|
||||
class="bg-gray-800 hover:bg-gray-900 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors">
|
||||
Alterar Senha
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
58
resources/views/admin/search/results.php
Normal file
58
resources/views/admin/search/results.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-4">Resultados para "<?= htmlspecialchars($query) ?>"</h3>
|
||||
|
||||
<?php if (empty($results['clients']) && empty($results['servers']) && empty($results['orders']) && empty($results['domains'])): ?>
|
||||
<p class="text-gray-500">Nenhum resultado encontrado.</p>
|
||||
<?php else: ?>
|
||||
|
||||
<?php if (!empty($results['clients'])): ?>
|
||||
<div class="mb-6">
|
||||
<h4 class="text-sm font-bold text-gray-500 uppercase tracking-wider mb-2">Clientes</h4>
|
||||
<ul class="divide-y divide-gray-100">
|
||||
<?php foreach ($results['clients'] as $item): ?>
|
||||
<li class="py-2"><a href="/admin/clients/edit/<?= $item['id'] ?>"
|
||||
class="text-primary-600 hover:underline"><?= htmlspecialchars($item['name']) ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($results['servers'])): ?>
|
||||
<div class="mb-6">
|
||||
<h4 class="text-sm font-bold text-gray-500 uppercase tracking-wider mb-2">Servidores</h4>
|
||||
<ul class="divide-y divide-gray-100">
|
||||
<?php foreach ($results['servers'] as $item): ?>
|
||||
<li class="py-2"><a href="/admin/servers"
|
||||
class="text-primary-600 hover:underline"><?= htmlspecialchars($item['name']) ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($results['orders'])): ?>
|
||||
<div class="mb-6">
|
||||
<h4 class="text-sm font-bold text-gray-500 uppercase tracking-wider mb-2">Ordens</h4>
|
||||
<ul class="divide-y divide-gray-100">
|
||||
<?php foreach ($results['orders'] as $item): ?>
|
||||
<li class="py-2"><a href="/admin/orders/view/<?= $item['id'] ?>"
|
||||
class="text-primary-600 hover:underline"><?= htmlspecialchars($item['name']) ?></a></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($results['domains'])): ?>
|
||||
<div class="mb-6">
|
||||
<h4 class="text-sm font-bold text-gray-500 uppercase tracking-wider mb-2">Domínios</h4>
|
||||
<ul class="divide-y divide-gray-100">
|
||||
<?php foreach ($results['domains'] as $item): ?>
|
||||
<li class="py-2"><span class="text-gray-700"><?= htmlspecialchars($item['name']) ?></span></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
112
resources/views/admin/servers/form.php
Normal file
112
resources/views/admin/servers/form.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<div class="max-w-2xl mx-auto bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-6"><?= isset($server) ? 'Editar Servidor' : 'Novo Servidor' ?>
|
||||
</h3>
|
||||
|
||||
<?php if (isset($_SESSION['flash_error'])): ?>
|
||||
<div class="bg-red-50 text-red-700 p-4 rounded-lg mb-6">
|
||||
<?= $_SESSION['flash_error'];
|
||||
unset($_SESSION['flash_error']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="<?= isset($server) ? '/admin/servers/update/' . $server['id'] : '/admin/servers/store' ?>"
|
||||
method="POST">
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Cliente</label>
|
||||
<select name="client_id"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required>
|
||||
<option value="">Selecione um cliente...</option>
|
||||
<?php foreach ($clients as $client): ?>
|
||||
<option value="<?= $client['id'] ?>" <?= (isset($server) && $server['client_id'] == $client['id']) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($client['name']) ?> (<?= htmlspecialchars($client['asn']) ?>)
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nome do Servidor</label>
|
||||
<input type="text" name="name" value="<?= $server['name'] ?? '' ?>"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">IP v4</label>
|
||||
<input type="text" name="ip_v4" value="<?= $server['ip_v4'] ?? '' ?>"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required placeholder="0.0.0.0">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">IP v6 (Opcional)</label>
|
||||
<input type="text" name="ip_v6" value="<?= $server['ip_v6'] ?? '' ?>"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
placeholder="::1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($server)): ?>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Status</label>
|
||||
<select name="status"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
||||
<option value="active" <?= $server['status'] == 'active' ? 'selected' : '' ?>>Ativo</option>
|
||||
<option value="inactive" <?= $server['status'] == 'inactive' ? 'selected' : '' ?>>Inativo</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-span-1 md:col-span-2 border-t border-gray-100 pt-4 mt-2">
|
||||
<h4 class="text-sm font-semibold text-gray-900 mb-4">Integração do Agente</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Serial Key</label>
|
||||
<div class="flex">
|
||||
<input type="text" value="<?= $server['serial_key'] ?>" readonly
|
||||
class="w-full px-4 py-2 bg-gray-50 border border-gray-300 rounded-l-lg text-gray-500 font-mono text-sm">
|
||||
<button type="button"
|
||||
onclick="navigator.clipboard.writeText('<?= $server['serial_key'] ?>')"
|
||||
class="px-3 py-2 bg-gray-100 border border-l-0 border-gray-300 rounded-r-lg hover:bg-gray-200 text-gray-600">
|
||||
<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="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Machine ID Vinculado</label>
|
||||
<?php if (!empty($server['machine_id'])): ?>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input type="text" value="<?= $server['machine_id'] ?>" readonly
|
||||
class="w-full px-4 py-2 bg-gray-50 border border-gray-300 rounded-lg text-gray-500 font-mono text-sm">
|
||||
<a href="/admin/servers/reset-machine/<?= $server['id'] ?>"
|
||||
onclick="return confirm('Tem certeza? Isso permitirá que um novo servidor utilize esta Serial Key.')"
|
||||
class="px-4 py-2 bg-red-100 text-red-700 rounded-lg hover:bg-red-200 text-sm whitespace-nowrap">
|
||||
Desvincular
|
||||
</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="px-4 py-2 bg-yellow-50 border border-yellow-100 rounded-lg text-yellow-700 text-sm">
|
||||
Nenhuma máquina vinculada. Aguardando conexão.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end space-x-3">
|
||||
<a href="/admin/servers"
|
||||
class="px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg transition-colors">Cancelar</a>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors">
|
||||
<?= isset($server) ? 'Salvar Alterações' : 'Criar Servidor' ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
87
resources/views/admin/servers/index.php
Normal file
87
resources/views/admin/servers/index.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 flex justify-between items-center bg-gray-50">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Servidores Registrados</h3>
|
||||
<a href="/admin/servers/create"
|
||||
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
||||
</svg>
|
||||
Novo Servidor
|
||||
</a>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Nome
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Cliente
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">IP (v4)
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Serial
|
||||
Key</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Status
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Agente
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Ações
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<?php if (!empty($servers)): ?>
|
||||
<?php foreach ($servers as $server): ?>
|
||||
<?php
|
||||
$lastSeen = !empty($server['last_seen']) ? strtotime($server['last_seen']) : 0;
|
||||
$isOnline = $lastSeen && (time() - $lastSeen < 300); // 5 minutes
|
||||
$lastSeenText = $lastSeen ? date('d/m/Y H:i', $lastSeen) : 'Nunca';
|
||||
?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900"><?= htmlspecialchars($server['name']) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= htmlspecialchars($server['client_name']) ?></td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= htmlspecialchars($server['ip_v4']) ?></td>
|
||||
<td class="px-6 py-4 text-sm text-gray-400 font-mono text-xs">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span><?= substr($server['serial_key'], 0, 8) ?>...</span>
|
||||
<button
|
||||
onclick="navigator.clipboard.writeText('<?= $server['serial_key'] ?>'); alert('Key copiada!');"
|
||||
class="text-primary-600 hover:text-primary-800" title="Copiar Serial Completo">
|
||||
<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="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $server['status'] == 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800' ?>">
|
||||
<?= $server['status'] == 'active' ? 'Ativo' : 'Inativo' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span title="Visto por último: <?= $lastSeenText ?>"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $isOnline ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' ?>">
|
||||
<?= $isOnline ? 'Online' : 'Offline' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm font-medium">
|
||||
<a href="/admin/servers/edit/<?= $server['id'] ?>"
|
||||
class="text-[#1e3a8a] hover:text-blue-900 mr-3">Editar</a>
|
||||
<a href="/admin/servers/delete/<?= $server['id'] ?>" class="text-red-600 hover:text-red-900"
|
||||
onclick="return confirm('Tem certeza?')">Excluir</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="6" class="px-6 py-4 text-center text-gray-500">Nenhum servidor encontrado.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
88
resources/views/auth/login.php
Normal file
88
resources/views/auth/login.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - DNSBlock</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#fef2f2',
|
||||
100: '#fee2e2',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-50 h-screen flex items-center justify-center">
|
||||
<div
|
||||
class="w-full max-w-md bg-white rounded-2xl shadow-xl overflow-hidden transform transition-all hover:scale-[1.01] duration-300">
|
||||
<div class="p-8">
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-900 tracking-tight">DNS<span
|
||||
class="text-primary-600">Block</span></h1>
|
||||
<p class="text-gray-500 mt-2 text-sm">Acesso ao Sistema de Gestão</p>
|
||||
</div>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="bg-red-50 border-l-4 border-primary-500 p-4 mb-6 rounded-r">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-5 w-5 text-primary-500" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm text-red-700"><?= htmlspecialchars($error) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/login" method="POST" class="space-y-6">
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">E-mail</label>
|
||||
<input type="email" name="email" id="email" required
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition-colors bg-gray-50 focus:bg-white outline-none"
|
||||
placeholder="seu@email.com">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">Senha</label>
|
||||
<input type="password" name="password" id="password" required
|
||||
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition-colors bg-gray-50 focus:bg-white outline-none"
|
||||
placeholder="••••••••">
|
||||
</div>
|
||||
|
||||
<button type="submit"
|
||||
class="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-all transform hover:-translate-y-0.5">
|
||||
Entrar
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="px-8 py-4 bg-gray-50 border-t border-gray-100 text-center">
|
||||
<p class="text-xs text-gray-400">© <?= date('Y') ?> DNSBlock System. Todos os direitos reservados.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
118
resources/views/client/dashboard.php
Normal file
118
resources/views/client/dashboard.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<!-- Card 1 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-blue-50 text-blue-600">
|
||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Meus Servidores</p>
|
||||
<p class="text-2xl font-bold text-gray-900"><?= $stats['my_servers'] ?? 0 ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 2 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-green-50 text-green-600">
|
||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Servidores Ativos</p>
|
||||
<p class="text-2xl font-bold text-gray-900"><?= $stats['active_servers'] ?? 0 ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 3 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-red-50 text-red-600">
|
||||
<svg class="w-8 h-8" 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">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Domínios Bloqueados</p>
|
||||
<p class="text-2xl font-bold text-gray-900"><?= $stats['total_blocked'] ?? 0 ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Card 4 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center">
|
||||
<div class="p-3 rounded-full bg-purple-50 text-purple-600">
|
||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<p class="text-sm font-medium text-gray-500">Ordens Recentes</p>
|
||||
<p class="text-2xl font-bold text-gray-900"><?= $stats['recent_orders'] ?? 0 ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 flex justify-between items-center">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Últimas ordens de bloqueio</h3>
|
||||
<a href="/client/orders" class="text-primary-600 hover:text-primary-700 text-sm font-medium">Ver todas</a>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Título
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tipo</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Início do
|
||||
Bloqueio</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<?php if (!empty($recent_orders)): ?>
|
||||
<?php foreach ($recent_orders as $order): ?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= $order['id'] ?></td>
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900"><?= htmlspecialchars($order['title']) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $order['type'] == 'block' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' ?>">
|
||||
<?= $order['type'] == 'block' ? 'Bloqueio' : 'Desbloqueio' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= date('d/m/Y', strtotime($order['received_at'])) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm font-medium">
|
||||
<a href="/client/orders/view/<?= $order['id'] ?>"
|
||||
class="text-primary-600 hover:text-primary-900 hover:underline">Ver Detalhes</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500">Nenhuma ordem registrada.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
79
resources/views/client/orders.php
Normal file
79
resources/views/client/orders.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 bg-gray-50 flex justify-between items-center">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Ordens Judiciais</h3>
|
||||
<form action="/client/orders" method="GET" class="relative">
|
||||
<input type="text" name="q" placeholder="Buscar ordem ou domínio..." value="<?= $_GET['q'] ?? '' ?>"
|
||||
class="pl-4 pr-10 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary-500">
|
||||
<button type="submit" class="absolute right-0 top-0 mt-2 mr-3 text-gray-400 hover:text-primary-600">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Título
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tipo</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Início do
|
||||
Bloqueio</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ações
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<?php if (!empty($orders)): ?>
|
||||
<?php foreach ($orders as $order): ?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= $order['id'] ?></td>
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900"><?= htmlspecialchars($order['title']) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $order['type'] == 'block' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' ?>">
|
||||
<?= $order['type'] == 'block' ? 'Bloqueio' : 'Desbloqueio' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= date('d/m/Y', strtotime($order['received_at'])) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm font-medium">
|
||||
<a href="/client/orders/view/<?= $order['id'] ?>"
|
||||
class="text-primary-600 hover:text-primary-900 hover:underline">Ver Detalhes</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500">Nenhuma ordem encontrada.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php if (isset($pagination) && $pagination['total'] > 1): ?>
|
||||
<div class="px-6 py-4 border-t border-gray-100 flex justify-between items-center">
|
||||
<div>
|
||||
<span class="text-sm text-gray-700">
|
||||
Página <span class="font-medium"><?= $pagination['current'] ?></span> de <span
|
||||
class="font-medium"><?= $pagination['total'] ?></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
<?php if ($pagination['prev']): ?>
|
||||
<a href="?page=<?= $pagination['prev'] ?><?= isset($_GET['q']) ? '&q=' . $_GET['q'] : '' ?>"
|
||||
class="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50">Anterior</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($pagination['next']): ?>
|
||||
<a href="?page=<?= $pagination['next'] ?><?= isset($_GET['q']) ? '&q=' . $_GET['q'] : '' ?>"
|
||||
class="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50">Próxima</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
78
resources/views/client/orders_view.php
Normal file
78
resources/views/client/orders_view.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<div class="space-y-6">
|
||||
<!-- Order Info -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<div class="flex justify-between items-start mb-6">
|
||||
<div>
|
||||
<h3 class="text-xl font-bold text-gray-900"><?= htmlspecialchars($order['title']) ?></h3>
|
||||
<p class="text-sm text-gray-500 mt-1">Recebido em:
|
||||
<?= date('d/m/Y H:i', strtotime($order['received_at'])) ?>
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
class="px-3 py-1 rounded-full text-sm font-semibold <?= $order['type'] == 'block' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' ?>">
|
||||
<?= $order['type'] == 'block' ? 'Bloqueio' : 'Desbloqueio' ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="prose max-w-none text-gray-700 bg-gray-50 p-4 rounded-lg border border-gray-200">
|
||||
<h4 class="text-sm font-bold text-gray-500 uppercase tracking-wider mb-2">Conteúdo da Ordem</h4>
|
||||
<style>
|
||||
.prose a {
|
||||
color: #2563eb;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.prose a:hover {
|
||||
color: #1e40af;
|
||||
}
|
||||
</style>
|
||||
<div class="prose max-w-none text-gray-700"><?= $order['content'] ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Domains List -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 bg-gray-50 flex justify-between items-center">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Domínios Afetados (<?= count($domains) ?>)</h3>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">
|
||||
Domínio</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Ação
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<?php if (!empty($domains)): ?>
|
||||
<?php foreach ($domains as $domain): ?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900"><?= htmlspecialchars($domain['name']) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $domain['action'] == 'block' ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' ?>">
|
||||
<?= $domain['action'] == 'block' ? 'Bloquear' : 'Desbloquear' ?>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="2" class="px-6 py-4 text-center text-gray-500">Nenhum domínio listado nesta ordem.
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<a href="/client/orders"
|
||||
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors">Voltar para
|
||||
Lista</a>
|
||||
</div>
|
||||
</div>
|
||||
91
resources/views/client/profile.php
Normal file
91
resources/views/client/profile.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Client Data (Read-only) -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-4">Dados da Empresa</h3>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-500">Razão Social / Nome</label>
|
||||
<p class="mt-1 text-gray-900 font-medium"><?= htmlspecialchars($client['name']) ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-500">ASN</label>
|
||||
<p class="mt-1 text-gray-900 font-medium"><?= htmlspecialchars($client['asn']) ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-500">Email de Contato</label>
|
||||
<p class="mt-1 text-gray-900 font-medium"><?= htmlspecialchars($client['email']) ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-500">Email Financeiro</label>
|
||||
<p class="mt-1 text-gray-900 font-medium"><?= htmlspecialchars($client['financial_email']) ?></p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-500">Telegram ID</label>
|
||||
<p class="mt-1 text-gray-900 font-medium"><?= htmlspecialchars($client['telegram_id'] ?? '-') ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 p-4 bg-yellow-50 rounded-lg text-sm text-yellow-700">
|
||||
Para alterar estes dados, entre em contato com o administrador.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Change Password -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-4">Alterar Senha</h3>
|
||||
|
||||
<?php if (isset($_SESSION['flash_error'])): ?>
|
||||
<div class="bg-red-50 text-red-700 p-3 rounded-lg mb-4 text-sm">
|
||||
<?= $_SESSION['flash_error'];
|
||||
unset($_SESSION['flash_error']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION['flash_success'])): ?>
|
||||
<div class="bg-green-50 text-green-700 p-3 rounded-lg mb-4 text-sm">
|
||||
<?= $_SESSION['flash_success'];
|
||||
unset($_SESSION['flash_success']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="/client/profile/password" method="POST">
|
||||
<div class="space-y-4">
|
||||
<div class="bg-blue-50 border-l-4 border-blue-400 p-4 mb-4">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-5 w-5 text-blue-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm text-blue-700">
|
||||
A senha deve ter no mínimo <strong>8 caracteres</strong>, contendo pelo menos
|
||||
<strong>uma letra maiúscula</strong> e <strong>um caractere especial</strong>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nova Senha</label>
|
||||
<input type="password" name="password"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Confirmar Nova Senha</label>
|
||||
<input type="password" name="confirm_password"
|
||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 flex justify-end">
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors">
|
||||
Atualizar Senha
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
68
resources/views/client/servers.php
Normal file
68
resources/views/client/servers.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-100 bg-gray-50">
|
||||
<h3 class="text-lg font-semibold text-gray-800">Meus Servidores</h3>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Nome
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">IP (v4)
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Serial
|
||||
Key</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Status
|
||||
</th>
|
||||
<th class="px-6 py-3 text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-50">Agente
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100">
|
||||
<?php if (!empty($servers)): ?>
|
||||
<?php foreach ($servers as $server): ?>
|
||||
<?php
|
||||
$lastSeen = !empty($server['last_seen']) ? strtotime($server['last_seen']) : 0;
|
||||
$isOnline = $lastSeen && (time() - $lastSeen < 300); // 5 minutes
|
||||
$lastSeenText = $lastSeen ? date('d/m/Y H:i', $lastSeen) : 'Nunca';
|
||||
?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-6 py-4 text-sm font-medium text-gray-900"><?= htmlspecialchars($server['name']) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-gray-500"><?= htmlspecialchars($server['ip_v4']) ?></td>
|
||||
<td class="px-6 py-4 text-sm text-gray-400 font-mono text-xs">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span><?= substr($server['serial_key'], 0, 8) ?>...</span>
|
||||
<button onclick="navigator.clipboard.writeText('<?= $server['serial_key'] ?>')"
|
||||
class="text-primary-600 hover:text-primary-800" title="Copiar Serial Completo">
|
||||
<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="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $server['status'] == 'active' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800' ?>">
|
||||
<?= $server['status'] == 'active' ? 'Ativo' : 'Inativo' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span title="Visto por último: <?= $lastSeenText ?>"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full <?= $isOnline ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' ?>">
|
||||
<?= $isOnline ? 'Online' : 'Offline' ?>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="4" class="px-6 py-4 text-center text-gray-500">Nenhum servidor cadastrado.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
202
resources/views/layouts/admin.php
Normal file
202
resources/views/layouts/admin.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?? 'Dashboard' ?> - DNSBlock Admin</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<!-- Alpine.js for interactions -->
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#fef2f2',
|
||||
100: '#fee2e2',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-50 text-gray-800 font-sans antialiased h-screen overflow-hidden" x-data="{ sidebarOpen: false }">
|
||||
|
||||
<!-- Mobile Header -->
|
||||
<div class="md:hidden flex items-center justify-between bg-white border-b border-gray-200 p-4 sticky top-0 z-30">
|
||||
<a href="/admin/dashboard" class="font-bold text-xl tracking-tight">DNS<span
|
||||
class="text-primary-600">Block</span></a>
|
||||
<button @click="sidebarOpen = !sidebarOpen" class="text-gray-500 focus:outline-none">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex h-screen overflow-hidden">
|
||||
<!-- Sidebar -->
|
||||
<aside :class="sidebarOpen ? 'translate-x-0' : '-translate-x-full'"
|
||||
class="fixed inset-y-0 left-0 z-40 w-64 bg-white border-r border-gray-200 transition-transform duration-300 md:relative md:translate-x-0 flex flex-col">
|
||||
<div class="h-16 flex items-center px-6 border-b border-gray-100">
|
||||
<a href="/admin/dashboard" class="font-bold text-2xl tracking-tight text-gray-900">DNS<span
|
||||
class="text-primary-600">Block</span></a>
|
||||
</div>
|
||||
|
||||
<nav class="flex-1 px-4 py-6 space-y-1 overflow-y-auto">
|
||||
<a href="/admin/dashboard"
|
||||
class="flex items-center px-4 py-3 text-gray-700 rounded-lg hover:bg-primary-50 hover:text-primary-700 transition-colors group <?= ($_SERVER['REQUEST_URI'] == '/admin/dashboard') ? 'bg-primary-50 text-primary-700' : '' ?>">
|
||||
<svg class="w-5 h-5 mr-3 text-gray-400 group-hover:text-primary-500" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z">
|
||||
</path>
|
||||
</svg>
|
||||
Dashboard
|
||||
</a>
|
||||
|
||||
<div class="pt-4 pb-2">
|
||||
<p class="px-4 text-xs font-semibold text-gray-400 uppercase tracking-wider">Gestão</p>
|
||||
</div>
|
||||
|
||||
<a href="/admin/clients"
|
||||
class="flex items-center px-4 py-3 text-gray-700 rounded-lg hover:bg-primary-50 hover:text-primary-700 transition-colors group <?= (strpos($_SERVER['REQUEST_URI'], '/admin/clients') !== false) ? 'bg-primary-50 text-primary-700' : '' ?>">
|
||||
<svg class="w-5 h-5 mr-3 text-gray-400 group-hover:text-primary-500" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z">
|
||||
</path>
|
||||
</svg>
|
||||
Clientes
|
||||
</a>
|
||||
|
||||
<a href="/admin/servers"
|
||||
class="flex items-center px-4 py-3 text-gray-700 rounded-lg hover:bg-primary-50 hover:text-primary-700 transition-colors group <?= (strpos($_SERVER['REQUEST_URI'], '/admin/servers') !== false) ? 'bg-primary-50 text-primary-700' : '' ?>">
|
||||
<svg class="w-5 h-5 mr-3 text-gray-400 group-hover:text-primary-500" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01">
|
||||
</path>
|
||||
</svg>
|
||||
Servidores
|
||||
</a>
|
||||
|
||||
<a href="/admin/orders"
|
||||
class="flex items-center px-4 py-3 text-gray-700 rounded-lg hover:bg-primary-50 hover:text-primary-700 transition-colors group <?= (strpos($_SERVER['REQUEST_URI'], '/admin/orders') !== false) ? 'bg-primary-50 text-primary-700' : '' ?>">
|
||||
<svg class="w-5 h-5 mr-3 text-gray-400 group-hover:text-primary-500" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
|
||||
</path>
|
||||
</svg>
|
||||
Ordens Judiciais
|
||||
</a>
|
||||
|
||||
<a href="/admin/logs"
|
||||
class="flex items-center px-4 py-3 text-gray-700 rounded-lg hover:bg-primary-50 hover:text-primary-700 transition-colors group <?= (strpos($_SERVER['REQUEST_URI'], '/admin/logs') !== false) ? 'bg-primary-50 text-primary-700' : '' ?>">
|
||||
<svg class="w-5 h-5 mr-3 text-gray-400 group-hover:text-primary-500" 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">
|
||||
</path>
|
||||
</svg>
|
||||
Logs do Sistema
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="p-4 border-t border-gray-100">
|
||||
<a href="/logout"
|
||||
class="flex items-center px-4 py-3 text-gray-600 rounded-lg hover:bg-red-50 hover:text-red-700 transition-colors">
|
||||
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1">
|
||||
</path>
|
||||
</svg>
|
||||
Sair
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content Wrapper -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<!-- Topbar (Fixed) -->
|
||||
<header class="bg-white shadow-sm z-20 flex-none">
|
||||
<div class="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8 flex justify-between items-center">
|
||||
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
||||
<?= $title ?? 'Dashboard' ?>
|
||||
</h2>
|
||||
<div class="flex items-center space-x-4">
|
||||
<form action="/admin/search" method="GET" class="relative">
|
||||
<span class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="text" name="q"
|
||||
class="pl-10 pr-4 py-2 border border-gray-300 rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
||||
placeholder="Buscar..." value="<?= $_GET['q'] ?? '' ?>">
|
||||
</form>
|
||||
<a href="/admin/profile"
|
||||
class="flex items-center hover:bg-gray-50 rounded-full pr-3 py-1 transition-colors">
|
||||
<div
|
||||
class="h-8 w-8 rounded-full bg-primary-100 flex items-center justify-center text-primary-700 font-bold">
|
||||
<?= substr($_SESSION['user_name'] ?? 'A', 0, 1) ?>
|
||||
</div>
|
||||
<span
|
||||
class="ml-2 text-sm font-medium text-gray-700 hidden md:block"><?= $_SESSION['user_name'] ?? 'Admin' ?></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Scrollable Content -->
|
||||
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-gray-50">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<?php if (isset($_SESSION['flash_success'])): ?>
|
||||
<div class="mb-4 bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative"
|
||||
role="alert">
|
||||
<span class="block sm:inline"><?= $_SESSION['flash_success'] ?></span>
|
||||
<?php unset($_SESSION['flash_success']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION['flash_error'])): ?>
|
||||
<div class="mb-4 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
|
||||
role="alert">
|
||||
<span class="block sm:inline"><?= $_SESSION['flash_error'] ?></span>
|
||||
<?php unset($_SESSION['flash_error']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($content))
|
||||
include $content; ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Overlay -->
|
||||
<div x-show="sidebarOpen" @click="sidebarOpen = false"
|
||||
class="fixed inset-0 z-30 bg-black bg-opacity-50 md:hidden" x-transition.opacity></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
167
resources/views/layouts/client.php
Normal file
167
resources/views/layouts/client.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?? 'Dashboard' ?> - DNSBlock Cliente</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#fef2f2',
|
||||
100: '#fee2e2',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-50 text-gray-800 font-sans antialiased" x-data="{ sidebarOpen: false }">
|
||||
|
||||
<!-- Mobile Header -->
|
||||
<div class="md:hidden flex items-center justify-between bg-white border-b border-gray-200 p-4 sticky top-0 z-30">
|
||||
<a href="/client/dashboard" class="font-bold text-xl tracking-tight">DNS<span
|
||||
class="text-primary-600">Block</span></a>
|
||||
<button @click="sidebarOpen = !sidebarOpen" class="text-gray-500 focus:outline-none">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex h-screen overflow-hidden">
|
||||
<!-- Sidebar -->
|
||||
<aside :class="sidebarOpen ? 'translate-x-0' : '-translate-x-full'"
|
||||
class="fixed inset-y-0 left-0 z-40 w-64 bg-white border-r border-gray-200 transition-transform duration-300 md:relative md:translate-x-0 flex flex-col">
|
||||
<div class="h-16 flex items-center px-6 border-b border-gray-100">
|
||||
<a href="/client/dashboard" class="font-bold text-2xl tracking-tight text-gray-900">DNS<span
|
||||
class="text-primary-600">Block</span></a>
|
||||
</div>
|
||||
|
||||
<nav class="flex-1 px-4 py-6 space-y-1 overflow-y-auto">
|
||||
<a href="/client/dashboard"
|
||||
class="flex items-center px-4 py-3 text-gray-700 rounded-lg hover:bg-primary-50 hover:text-primary-700 transition-colors group <?= ($_SERVER['REQUEST_URI'] == '/client/dashboard') ? 'bg-primary-50 text-primary-700' : '' ?>">
|
||||
<svg class="w-5 h-5 mr-3 text-gray-400 group-hover:text-primary-500" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z">
|
||||
</path>
|
||||
</svg>
|
||||
Visão Geral
|
||||
</a>
|
||||
|
||||
<div class="pt-4 pb-2">
|
||||
<p class="px-4 text-xs font-semibold text-gray-400 uppercase tracking-wider">Consultas</p>
|
||||
</div>
|
||||
|
||||
<a href="/client/servers"
|
||||
class="flex items-center px-4 py-3 text-gray-700 rounded-lg hover:bg-primary-50 hover:text-primary-700 transition-colors group <?= (strpos($_SERVER['REQUEST_URI'], '/client/servers') !== false) ? 'bg-primary-50 text-primary-700' : '' ?>">
|
||||
<svg class="w-5 h-5 mr-3 text-gray-400 group-hover:text-primary-500" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01">
|
||||
</path>
|
||||
</svg>
|
||||
Meus Servidores
|
||||
</a>
|
||||
|
||||
<a href="/client/orders"
|
||||
class="flex items-center px-4 py-3 text-gray-700 rounded-lg hover:bg-primary-50 hover:text-primary-700 transition-colors group <?= (strpos($_SERVER['REQUEST_URI'], '/client/orders') !== false) ? 'bg-primary-50 text-primary-700' : '' ?>">
|
||||
<svg class="w-5 h-5 mr-3 text-gray-400 group-hover:text-primary-500" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
|
||||
</path>
|
||||
</svg>
|
||||
Ordens Judiciais
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-gray-50">
|
||||
<!-- Topbar -->
|
||||
<header class="bg-white shadow-sm sticky top-0 z-20">
|
||||
<div class="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8 flex justify-between items-center">
|
||||
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
|
||||
<?= $title ?? 'Dashboard' ?>
|
||||
</h2>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="relative" x-data="{ userMenuOpen: false }">
|
||||
<button @click="userMenuOpen = !userMenuOpen" class="flex items-center focus:outline-none">
|
||||
<div
|
||||
class="h-8 w-8 rounded-full bg-primary-100 flex items-center justify-center text-primary-700 font-bold">
|
||||
<?= substr($_SESSION['user_name'] ?? 'C', 0, 1) ?>
|
||||
</div>
|
||||
<span
|
||||
class="ml-2 text-sm font-medium text-gray-700 hidden md:block"><?= $_SESSION['user_name'] ?? 'Cliente' ?></span>
|
||||
<svg class="w-4 h-4 ml-1 text-gray-500" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div x-show="userMenuOpen" @click.away="userMenuOpen = false"
|
||||
class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50 ring-1 ring-black ring-opacity-5"
|
||||
style="display: none;">
|
||||
<a href="/client/profile"
|
||||
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Meu Perfil</a>
|
||||
<a href="/logout"
|
||||
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Sair</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<?php if (isset($_SESSION['flash_success'])): ?>
|
||||
<div class="mb-4 bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative"
|
||||
role="alert">
|
||||
<span class="block sm:inline"><?= $_SESSION['flash_success'] ?></span>
|
||||
<?php unset($_SESSION['flash_success']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION['flash_error'])): ?>
|
||||
<div class="mb-4 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
||||
<span class="block sm:inline"><?= $_SESSION['flash_error'] ?></span>
|
||||
<?php unset($_SESSION['flash_error']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($content))
|
||||
include $content; ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Overlay -->
|
||||
<div x-show="sidebarOpen" @click="sidebarOpen = false"
|
||||
class="fixed inset-0 z-30 bg-black bg-opacity-50 md:hidden" x-transition.opacity></div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user