feat: add resources and view components
This commit is contained in:
@@ -0,0 +1,677 @@
|
||||
<x-app-layout>
|
||||
@push('styles')
|
||||
<script src="https://cdn.jsdelivr.net/npm/apexcharts" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
.sparkline-container {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
border-bottom-left-radius: 20px;
|
||||
border-bottom-right-radius: 20px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Skeleton Loading */
|
||||
.skeleton {
|
||||
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-loading 1.5s infinite;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@keyframes skeleton-loading {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton-text {
|
||||
height: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.skeleton-title {
|
||||
height: 2.5rem;
|
||||
width: 60%;
|
||||
margin: 10px auto;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
<div class="container-fluid mt-3 pb-5" id="monitoring-master">
|
||||
|
||||
{{-- Welcome Header --}}
|
||||
<div
|
||||
class="card adminuiux-card bg-dark text-white mb-4 border-0 shadow-lg overflow-hidden animate__animated animate__fadeIn">
|
||||
<div class="card-body p-4 position-relative">
|
||||
<div class="row align-items-center position-relative z-1">
|
||||
<div class="col">
|
||||
<h1 class="display-5 fw-bold text-white mb-1 tracking-tight">{{ __('Operational Dashboard') }}
|
||||
</h1>
|
||||
<p class="small text-white-50 mb-0 d-flex align-items-center gap-2">
|
||||
<span class="status-indicator"></span>
|
||||
System operational since <span class="badge text-bg-theme-1 rounded-pill px-3 shadow-sm"
|
||||
id="stat-uptime-badge">{{ $stats['uptime'] }}</span>
|
||||
at <span class="fw-bold">{{ $stats['hostname'] }}</span> ({{ $stats['ip'] }})
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-theme-1 btn-square rounded-circle shadow-sm" id="refresh-all-stats"
|
||||
title="Refresh Live Data">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-decoration"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Main Stats Row --}}
|
||||
<div class="row g-3 g-lg-4 mb-4">
|
||||
{{-- CPU --}}
|
||||
<div class="col-6 col-md-6 col-lg-3">
|
||||
<div class="card adminuiux-card border-0 shadow-sm h-100 hover-lift animate__animated animate__fadeIn"
|
||||
style="animation-delay: 0.1s">
|
||||
<div class="card-body p-4 text-center">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h6 class="fw-bold text-dark small mb-0">CPU LOAD</h6>
|
||||
<i class="bi bi-speedometer2 text-theme-1"></i>
|
||||
</div>
|
||||
<h1 class="display-3 fw-black text-theme-1 mb-0 counter-value" id="stat-cpu-percent">
|
||||
{{ $stats['cpu'] }}%
|
||||
</h1>
|
||||
<div class="mini-progress mt-3">
|
||||
<div class="bar" id="cpu-bar" style="width:{{$stats['cpu']}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chart-cpu-sparkline" class="sparkline-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- RAM --}}
|
||||
<div class="col-6 col-md-6 col-lg-3">
|
||||
<div class="card adminuiux-card border-0 shadow-sm h-100 hover-lift animate__animated animate__fadeIn"
|
||||
style="animation-delay: 0.2s">
|
||||
<div class="card-body p-4 text-center">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h6 class="fw-bold text-dark small mb-0">MEMORY</h6>
|
||||
<i class="bi bi-memory text-info"></i>
|
||||
</div>
|
||||
<h1 class="display-3 fw-black text-info mb-0 counter-value" id="stat-ram-percent">
|
||||
{{ $stats['ram']['percentage'] }}%
|
||||
</h1>
|
||||
<div class="d-flex justify-content-between extra-small text-muted mt-2">
|
||||
<span id="stat-ram-used">{{ $stats['ram']['used'] }} used</span>
|
||||
<span id="stat-swap-info" class="text-warning">Swap:
|
||||
{{ $stats['ram']['swap']['percentage'] ?? 0 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chart-ram-sparkline" class="sparkline-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- DISK --}}
|
||||
<div class="col-6 col-md-6 col-lg-3">
|
||||
<div class="card adminuiux-card border-0 shadow-sm h-100 hover-lift animate__animated animate__fadeIn"
|
||||
style="animation-delay: 0.3s">
|
||||
<div class="card-body p-4 text-center">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h6 class="fw-bold text-dark small mb-0">STORAGE</h6>
|
||||
<i class="bi bi-hdd-network text-warning"></i>
|
||||
</div>
|
||||
<h1 class="display-3 fw-black text-warning mb-0 counter-value" id="stat-disk-percent">
|
||||
{{ $stats['disk']['percentage'] }}%
|
||||
</h1>
|
||||
<p class="extra-small text-muted mb-0 mt-2" id="stat-disk-total">
|
||||
{{ $stats['disk']['free'] }} available
|
||||
</p>
|
||||
</div>
|
||||
<div id="chart-disk-sparkline" class="sparkline-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- SESSIONS --}}
|
||||
<div class="col-6 col-md-6 col-lg-3">
|
||||
<div class="card adminuiux-card border-0 shadow-sm h-100 hover-lift bg-theme-1 text-white card-glow-theme animate__animated animate__fadeIn"
|
||||
style="animation-delay: 0.4s">
|
||||
<div class="card-body p-4 text-center">
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<h6 class="fw-bold text-white small mb-0">LIVE USERS</h6>
|
||||
<i class="bi bi-people text-white-50"></i>
|
||||
</div>
|
||||
<h1 class="display-3 fw-black text-white mb-0 counter-value" id="stat-users-count">
|
||||
{{ $stats['users']['total'] }}
|
||||
</h1>
|
||||
<p class="extra-small text-white-50 mb-0 mt-2">
|
||||
<span id="stat-users-auth">{{ $stats['users']['authenticated'] }}</span> Authenticated
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 g-lg-6">
|
||||
@if(auth()->user()->can('view health and logs'))
|
||||
|
||||
{{-- Right Column: Live Console (Logs) --}}
|
||||
<div class="col-lg-7">
|
||||
<div
|
||||
class="card adminuiux-card border-0 shadow-sm h-100 mb-4 mb-lg-0 animate__animated animate__fadeIn">
|
||||
<div
|
||||
class="card-header bg-white border-bottom p-4 d-flex justify-content-between align-items-center">
|
||||
<h6 class="fw-bold text-dark mb-0 d-flex align-items-center gap-2">
|
||||
<i class="bi bi-terminal text-theme-1"></i>
|
||||
{{ __('Runtime Activity Feed') }}
|
||||
</h6>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ route('system-monitoring') }}"
|
||||
class="btn btn-sm btn-light rounded-pill px-3 fw-bold small">
|
||||
<i class="bi bi-gear me-1"></i> FULL MONITOR
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<style>
|
||||
#logs-datatable tbody td:nth-child(3) {
|
||||
white-space: normal !important;
|
||||
min-width: 250px;
|
||||
max-width: 400px;
|
||||
word-break: break-word;
|
||||
}
|
||||
#logs-datatable thead th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
<table id="logs-datatable"
|
||||
class="table table-hover align-middle mb-0 w-100 small compact-table">
|
||||
<thead>
|
||||
<tr class="bg-white">
|
||||
<th>INCIDENT TIME</th>
|
||||
<th>LVL</th>
|
||||
<th>MANIFEST</th>
|
||||
<th class="text-end pe-4">INTEL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@can('view ai log analysis')
|
||||
|
||||
{{-- AI Analysis Card --}}
|
||||
<div class="col-lg-5">
|
||||
<div class="card adminuiux-card border-0 shadow-sm h-100 mb-4 mb-lg-0 animate__animated animate__fadeIn"
|
||||
style="animation-delay: 0.5s">
|
||||
<div
|
||||
class="card-header bg-transparent border-0 pt-4 px-4 d-flex justify-content-between align-items-center">
|
||||
<h6 class="fw-bold text-dark mb-0 d-flex align-items-center gap-2">
|
||||
<i class="bi bi-robot text-theme-1"></i>
|
||||
{{ __('AI Security Insight') }}
|
||||
</h6>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button class="btn btn-sm btn-outline-danger rounded-pill px-3 extra-small"
|
||||
id="btn-ai-clear">
|
||||
<i class="bi bi-trash me-1"></i>Clear
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-dark rounded-pill px-3 extra-small"
|
||||
id="btn-ai-analyze">
|
||||
<i class="bi bi-cpu me-1"></i>Analyze
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body px-4">
|
||||
<div id="ai-analysis-container" class="small text-muted"
|
||||
style="min-height: 200px; line-height: 1.6;">
|
||||
<div class="placeholder-glow" id="ai-placeholder" style="display:none;">
|
||||
<div class="d-flex flex-column gap-3">
|
||||
<span class="placeholder col-12 rounded-pill py-2"></span>
|
||||
<span class="placeholder col-10 rounded-pill py-1"></span>
|
||||
<span class="placeholder col-11 rounded-pill py-1"></span>
|
||||
<span class="placeholder col-8 rounded-pill py-1"></span>
|
||||
<span class="placeholder col-9 rounded-pill py-1"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ai-content-display" class="animate__animated animate__fadeIn">
|
||||
<div class="text-center py-5 opacity-50">
|
||||
<i class="bi bi-robot display-4 d-block mb-3"></i>
|
||||
<p class="fst-italic">Click analyze to get security insights from your recent
|
||||
activity logs.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Log Detail Modal --}}
|
||||
<div class="modal fade animate__animated animate__fadeIn" id="logDetailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl animate__animated animate__fadeIn">
|
||||
<div class="modal-content adminuiux-card border-0 shadow-2xl terminal-box">
|
||||
<div class="modal-header terminal-header">
|
||||
<div class="window-controls me-3">
|
||||
<span class="dot red"></span><span class="dot yellow"></span><span class="dot green"></span>
|
||||
</div>
|
||||
<h6 class="modal-title fw-bold extra-small text-white-50 letter-spacing-1">TELEMETRY_DUMP @node_01
|
||||
</h6>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body p-0">
|
||||
<div class="code-container p-4">
|
||||
<pre id="detail-message" class="mb-0 small text-success font-monospace scroll-custom"
|
||||
style="max-height: 700px; overflow-y: auto; overflow-x: hidden; line-height: 1.6; white-space: pre-wrap; word-break: break-all;"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
@if(auth()->user()->can('view health and logs'))
|
||||
const REFRESH_RATE = 10000; // 10s for dashboard to be less aggressive
|
||||
|
||||
// Logs Init
|
||||
const logsTable = $('#logs-datatable').DataTable({
|
||||
processing: true, serverSide: true, ajax: '{{ route("system-monitoring.logs.datatable") }}',
|
||||
order: [[0, 'desc']], pageLength: 5, autoWidth: false, dom: 'tr<"p-3 border-top d-flex justify-content-end"p>',
|
||||
columns: [{ data: 0, className: 'ps-4 datetime-col fw-bold' }, { data: 1 }, { data: 2 }, { data: 3, className: 'pe-4 text-end', orderable: false }],
|
||||
drawCallback: function () {
|
||||
$('.view-log-detail').off('click').on('click', function () {
|
||||
const log = $(this).data('log');
|
||||
$('#detail-message').text(log.message);
|
||||
new bootstrap.Modal('#logDetailModal').show();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function refreshStats() {
|
||||
const btn = $('#refresh-all-stats');
|
||||
btn.find('i').addClass('bi-spin');
|
||||
|
||||
$.get('{{ route("system-monitoring.stats") }}', function (d) {
|
||||
updateVal('#stat-cpu-percent', d.cpu + '%');
|
||||
updateVal('#stat-ram-percent', d.ram.percentage + '%');
|
||||
$('#stat-ram-used').text(d.ram.used + ' used');
|
||||
if (d.ram.swap) {
|
||||
$('#stat-swap-info').text('Swap: ' + d.ram.swap.percentage + '%');
|
||||
}
|
||||
|
||||
updateVal('#stat-disk-percent', d.disk.percentage + '%');
|
||||
$('#stat-disk-total').text(d.disk.free + ' available');
|
||||
|
||||
// Refined Users
|
||||
updateVal('#stat-users-count', d.users.total);
|
||||
$('#stat-users-auth').text(d.users.authenticated);
|
||||
|
||||
updateVal('#stat-queues-pending', d.queues.pending);
|
||||
updateVal('#stat-queues-failed', d.queues.failed);
|
||||
|
||||
$('#stat-uptime-badge').text(d.uptime);
|
||||
$('#cpu-bar').css('width', d.cpu + '%');
|
||||
|
||||
// Update Charts
|
||||
updateSparkline(cpuChart, d.cpu);
|
||||
updateSparkline(ramChart, d.ram.percentage);
|
||||
updateSparkline(diskChart, d.disk.percentage);
|
||||
|
||||
if (d.has_reverb) {
|
||||
$('#reverb-icon').addClass('active');
|
||||
$('#reverb-status-text').text('ACTIVE').removeClass('text-muted').addClass('text-success');
|
||||
} else {
|
||||
$('#reverb-icon').removeClass('active');
|
||||
$('#reverb-status-text').text('IDLE').addClass('text-muted').removeClass('text-success');
|
||||
}
|
||||
|
||||
logsTable.ajax.reload(null, false);
|
||||
setTimeout(() => btn.find('i').removeClass('bi-spin'), 1000);
|
||||
});
|
||||
}
|
||||
|
||||
// --- Sparkline Helpers ---
|
||||
const sparklineOptions = (color) => ({
|
||||
series: [{ data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }],
|
||||
chart: { type: 'area', height: 60, sparkline: { enabled: true }, animations: { enabled: true, easing: 'linear', dynamicAnimation: { speed: 1000 } } },
|
||||
stroke: { curve: 'smooth', width: 2 },
|
||||
fill: { opacity: 0.3, type: 'gradient', gradient: { shadeIntensity: 1, opacityFrom: 0.4, opacityTo: 0.1 } },
|
||||
colors: [color],
|
||||
tooltip: { enabled: false }
|
||||
});
|
||||
|
||||
const cpuChart = new ApexCharts(document.querySelector("#chart-cpu-sparkline"), sparklineOptions('var(--adminuiux-theme-1)'));
|
||||
const ramChart = new ApexCharts(document.querySelector("#chart-ram-sparkline"), sparklineOptions('#0dcaf0'));
|
||||
const diskChart = new ApexCharts(document.querySelector("#chart-disk-sparkline"), sparklineOptions('#ffc107'));
|
||||
|
||||
cpuChart.render();
|
||||
ramChart.render();
|
||||
diskChart.render();
|
||||
|
||||
function updateSparkline(chart, val) {
|
||||
let newData = chart.w.globals.series[0].slice();
|
||||
newData.push(val);
|
||||
if (newData.length > 10) newData.shift();
|
||||
chart.updateSeries([{ data: newData }]);
|
||||
}
|
||||
|
||||
function updateVal(selector, newVal) {
|
||||
const el = $(selector);
|
||||
if (el.text() !== newVal.toString()) {
|
||||
el.text(newVal).addClass('animate__animated animate__pulse');
|
||||
setTimeout(() => el.removeClass('animate__animated animate__pulse'), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
$('#refresh-all-stats').on('click', refreshStats);
|
||||
setInterval(refreshStats, REFRESH_RATE);
|
||||
|
||||
// 📡 Real-time Activity Feed (Broadcasting)
|
||||
if (window.Echo) {
|
||||
window.Echo.private('admin.monitoring')
|
||||
.listen('.activity.created', (e) => {
|
||||
console.log('Real-time Log received:', e.log);
|
||||
|
||||
// Prepend to DataTable
|
||||
const rowNode = logsTable.row.add([
|
||||
e.log.datetime,
|
||||
e.log.level,
|
||||
e.log.manifest,
|
||||
`<button class="btn btn-square btn-light btn-sm rounded-circle view-log-detail" data-log='${JSON.stringify({ message: e.log.description }).replace(/'/g, "'")}'>
|
||||
<i class="bi bi-info-circle"></i>
|
||||
</button>`
|
||||
]).draw(false).node();
|
||||
|
||||
$(rowNode).addClass('animate__animated animate__highlight').css('background-color', '#fff9c4');
|
||||
setTimeout(() => $(rowNode).css('background-color', ''), 3000);
|
||||
|
||||
// Re-bind modal click
|
||||
$(rowNode).find('.view-log-detail').on('click', function () {
|
||||
const log = $(this).data('log');
|
||||
$('#detail-message').text(log.message);
|
||||
new bootstrap.Modal('#logDetailModal').show();
|
||||
});
|
||||
});
|
||||
}
|
||||
@endif
|
||||
|
||||
// 🤖 AI Log Analysis JS
|
||||
function fetchAiAnalysis() {
|
||||
$.get('{{ route("ai.log-analysis.index") }}', function (d) {
|
||||
if (d.analysis && !d.analysis.includes('Analysis not generated yet')) {
|
||||
renderAiContent(d.analysis);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderAiContent(text) {
|
||||
// Simple markdown-ish bold headers conversion
|
||||
let formatted = text.replace(/### (.*)/g, '<h6 class="fw-bold text-dark mt-3 mb-2">$1</h6>')
|
||||
.replace(/## (.*)/g, '<h6 class="fw-bold text-dark mt-3 mb-2">$1</h6>')
|
||||
.replace(/\*\*(.*)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/\n/g, '<br>');
|
||||
$('#ai-content-display').html(formatted);
|
||||
}
|
||||
|
||||
$('#btn-ai-analyze').on('click', function () {
|
||||
const btn = $(this);
|
||||
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span> Analyzing...');
|
||||
|
||||
$('#ai-content-display').fadeOut(200, function () {
|
||||
$('#ai-placeholder').show();
|
||||
|
||||
$.post('{{ route("ai.log-analysis.analyze") }}', { _token: '{{ csrf_token() }}' }, function (d) {
|
||||
renderAiContent(d.analysis);
|
||||
$('#ai-placeholder').hide();
|
||||
$('#ai-content-display').fadeIn();
|
||||
btn.prop('disabled', false).html('<i class="bi bi-cpu me-1"></i>Analyze');
|
||||
}).fail(function () {
|
||||
$('#ai-content-display').html('<div class="alert alert-danger p-2 small"><i class="bi bi-exclamation-triangle me-2"></i> Error connecting to AI service.</div>').fadeIn();
|
||||
$('#ai-placeholder').hide();
|
||||
btn.prop('disabled', false).html('<i class="bi bi-cpu me-1"></i>Analyze');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('#btn-ai-clear').on('click', function () {
|
||||
const btn = $(this);
|
||||
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm"></span>');
|
||||
|
||||
$.post('{{ route("ai.log-analysis.clear") }}', { _token: '{{ csrf_token() }}' }, function (d) {
|
||||
$('#ai-content-display').fadeOut(200, function () {
|
||||
$(this).html(`
|
||||
<div class="text-center py-5 opacity-50">
|
||||
<i class="bi bi-robot display-4 d-block mb-3"></i>
|
||||
<p class="fst-italic">Click analyze to get security insights from your recent activity logs.</p>
|
||||
</div>
|
||||
`).fadeIn();
|
||||
});
|
||||
btn.prop('disabled', false).html('<i class="bi bi-trash me-1"></i>Clear');
|
||||
}).fail(function () {
|
||||
btn.prop('disabled', false).html('<i class="bi bi-trash me-1"></i>Clear');
|
||||
});
|
||||
});
|
||||
|
||||
fetchAiAnalysis();
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.fw-black {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.ls-1 {
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.ls-2 {
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.tracking-tight {
|
||||
letter-spacing: -2px;
|
||||
}
|
||||
|
||||
.display-3 {
|
||||
font-size: 3.5rem;
|
||||
letter-spacing: -2px;
|
||||
}
|
||||
|
||||
.extra-small {
|
||||
font-size: 0.85rem !important;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.hover-lift {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.hover-lift:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08) !important;
|
||||
}
|
||||
|
||||
.bg-decoration {
|
||||
position: absolute;
|
||||
right: -50px;
|
||||
bottom: -50px;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 50%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.icon-shape {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bg-theme-1-subtle {
|
||||
background-color: rgba(var(--adminuiux-theme-1-rgb), 0.1);
|
||||
}
|
||||
|
||||
.bg-info-subtle {
|
||||
background-color: rgba(13, 202, 240, 0.1);
|
||||
}
|
||||
|
||||
.bg-warning-subtle {
|
||||
background-color: rgba(255, 193, 7, 0.1);
|
||||
}
|
||||
|
||||
.bg-success-subtle {
|
||||
background-color: rgba(25, 135, 84, 0.1);
|
||||
}
|
||||
|
||||
.bg-danger-subtle {
|
||||
background-color: rgba(220, 53, 69, 0.1);
|
||||
}
|
||||
|
||||
.icon-box-modern {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.hover-bg-light:hover {
|
||||
background-color: #f8fafc;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pulse-tiny {
|
||||
animation: pulse-glow-tiny 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-glow-tiny {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.bi-spin {
|
||||
animation: spin 1.5s infinite linear;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.compact-table thead th {
|
||||
font-size: 9px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: #64748b;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.compact-table tbody td {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.scroll-custom::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.scroll-custom::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.scroll-custom::-webkit-scrollbar-thumb {
|
||||
background: #334155;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
font-size: 0.7rem !important;
|
||||
line-height: 1.4 !important;
|
||||
}
|
||||
|
||||
.markdown-body p {
|
||||
margin-bottom: 0.4rem !important;
|
||||
}
|
||||
|
||||
/* Terminal Styling */
|
||||
.terminal-box {
|
||||
background: #0c121e !important;
|
||||
border: 1px solid #1e293b !important;
|
||||
color: #10b981 !important;
|
||||
min-height: 500px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.terminal-header {
|
||||
background: #1e293b !important;
|
||||
border-bottom: 1px solid #334155 !important;
|
||||
padding: 12px 20px !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.window-controls .dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.dot.red {
|
||||
background: #ef4444;
|
||||
}
|
||||
|
||||
.dot.yellow {
|
||||
background: #f59e0b;
|
||||
}
|
||||
|
||||
.dot.green {
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
.code-container pre {
|
||||
font-family: 'Fira Code', 'JetBrains Mono', monospace !important;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
Reference in New Issue
Block a user