feat: refined stucture & bugs

This commit is contained in:
2026-01-02 17:56:07 +05:30
Unverified
parent b4f24f4fcf
commit 54cdce1dff
9 changed files with 420 additions and 347 deletions

BIN
src.zip Normal file

Binary file not shown.

View File

@@ -24,7 +24,7 @@ $user_initial = strtoupper($current_email[0]);
<div class="text-sm font-semibold text-white"> <div class="text-sm font-semibold text-white">
<?php echo htmlspecialchars($current_email); ?> <?php echo htmlspecialchars($current_email); ?>
</div> </div>
<div class="text-xs text-neutral-500 font-medium uppercase tracking-wide"> <div class="text-xs text-neutral-400 font-medium uppercase tracking-wide">
<?php echo ($is_admin ?? false) ? 'Administrator' : 'User'; ?> <?php echo ($is_admin ?? false) ? 'Administrator' : 'User'; ?>
</div> </div>
</div> </div>

View File

@@ -33,14 +33,14 @@ $inactiveClass = 'text-neutral-400 hover:text-white hover:bg-neutral-800/60 bord
<?php if ($is_admin): ?> <?php if ($is_admin): ?>
<a href="?page=status-board" <a href="?page=admin_overview"
class="group flex items-center px-4 py-3 text-sm font-medium rounded-xl <?php echo $page === 'status-board' ? $activeClass : $inactiveClass; ?>"> class="group flex items-center px-4 py-3 text-sm font-medium rounded-xl <?php echo $page === 'admin_overview' ? $activeClass : $inactiveClass; ?>">
<svg class="w-5 h-5 mr-3 <?php echo $page === 'status-board' ? 'text-blue-400' : 'text-neutral-500 group-hover:text-white'; ?> transition-colors" <svg class="w-5 h-5 mr-3 <?php echo $page === 'overview' ? 'text-blue-400' : 'text-neutral-500 group-hover:text-white'; ?> transition-colors"
fill="none" stroke="currentColor" viewBox="0 0 24 24"> fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /> 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" />
</svg> </svg>
Status Board Admin Overview
</a> </a>
<a href="?page=activity-logs" <a href="?page=activity-logs"

View File

@@ -0,0 +1,267 @@
<?php
$departments = [
'WiFi & Network Issue',
'Electrical Issue',
'Water & Plumbing Issue',
'HVAC (AC/Heating) Issue',
'Furniture & Fixtures Issue',
'Cleaning & Janitorial Issue',
'Security & Safety Issue',
'Road & Pathway Damage Issue',
'Library & Study Issue',
'Lost & Stolen Issue',
'Medical/Health Issue',
'Other Issues',
];
$services = [];
$sql = "
SELECT
REPLACE(category, '&amp;', '&') AS category,
SUM(TRIM(status) = 'Opened') AS opened_count,
SUM(TRIM(status) = 'In Progress') AS progress_count
FROM reports
GROUP BY category
";
$result = $conn->query($sql);
$deptStats = [];
while ($row = $result->fetch_assoc()) {
$deptStats[$row['category']] = $row;
}
function getStatusConfig($status) {
switch ($status) {
case 'Operational':
return [
'color' => 'green',
'hover_border' => 'hover:border-green-500/50 hover:shadow-green-500/5',
'group_hover_icon' => 'group-hover:text-green-400 group-hover:bg-green-500/10 group-hover:border-green-500/20'
];
case 'Maintenance':
return [
'color' => 'yellow',
'hover_border' => 'hover:border-yellow-500/50 hover:shadow-yellow-500/5',
'group_hover_icon' => 'group-hover:text-yellow-400 group-hover:bg-yellow-500/10 group-hover:border-yellow-500/20'
];
case 'Outage':
return [
'color' => 'red',
'hover_border' => 'hover:border-red-500/50 hover:shadow-red-500/5',
'group_hover_icon' => 'group-hover:text-red-400 group-hover:bg-red-500/10 group-hover:border-red-500/20'
];
default:
return [
'color' => 'gray',
'hover_border' => 'hover:border-neutral-500/50',
'group_hover_icon' => 'group-hover:text-white'
];
}
}
function getIconPath($name) {
if (strpos($name, 'WiFi') !== false) return 'M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0';
if (strpos($name, 'Electrical') !== false) return 'M13 10V3L4 14h7v7l9-11h-7z';
if (strpos($name, 'Water') !== false) return 'M19.428 15.428a2 2 0 00-1.022-.547l-2.384-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z';
if (strpos($name, 'HVAC') !== false) return 'M14 10l-2 1m0 0l-2-1m2 1v2.5M20 7l-2 1m2-1l-2-1m2 1v2.5M14 4l-2-1-2 1M4 7l2-1M4 7l2 1M4 7v2.5M12 21l-2-1m2 1l2-1m-2 1v-2.5M6 18l-2-1v-2.5M18 18l2-1v-2.5';
if (strpos($name, 'Furniture') !== false) return 'M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4';
if (strpos($name, 'Cleaning') !== false) return 'M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10';
if (strpos($name, 'Security') !== false) return 'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z';
if (strpos($name, 'Road') !== false) return 'M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7';
if (strpos($name, 'Library') !== false) return 'M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253';
if (strpos($name, 'Lost') !== false) return 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z';
if (strpos($name, 'Medical') !== false) return 'M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z';
return 'M5 12h.01M12 12h.01M19 12h.01M6 12a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z';
}
$activeServices = 0;
$maintenanceCount = 0;
$highPriority = 0;
foreach ($departments as $dept) {
$opened = $deptStats[$dept]['opened_count'] ?? 0;
$progress = $deptStats[$dept]['progress_count'] ?? 0;
if ($opened > 0) {
$status = 'Outage';
$msg = "$opened active issue(s)";
$highPriority++;
} elseif ($progress > 0) {
$status = 'Maintenance';
$msg = "$progress issue(s) in progress";
$maintenanceCount++;
} else {
$status = 'Operational';
$msg = 'No active issues';
$activeServices++;
}
$services[] = [
'name' => $dept,
'status' => $status,
'msg' => $msg
];
}
$totalServices = count($services);
$activeIncidents = $maintenanceCount + $highPriority;
if ($highPriority > 0) {
$sysData = [
'status' => 'System Alert',
'msg' => 'Critical issues detected',
'text' => 'text-red-400',
'icon' => 'text-red-500',
'bg' => 'bg-red-500/10 border-red-500/20',
'hover' => 'hover:border-red-500/50',
'svg' => '<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"/>'
];
} elseif ($maintenanceCount > 0) {
$sysData = [
'status' => 'Maintenance',
'msg' => 'Performance degraded',
'text' => 'text-yellow-400',
'icon' => 'text-yellow-500',
'bg' => 'bg-yellow-500/10 border-yellow-500/20',
'hover' => 'hover:border-yellow-500/50',
'svg' => '<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>'
];
} else {
$sysData = [
'status' => 'Operational',
'msg' => 'All core systems nominal',
'text' => 'text-green-400',
'icon' => 'text-green-500',
'bg' => 'bg-green-500/10 border-green-500/20',
'hover' => 'hover:border-green-500/50',
'svg' => '<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"/>'
];
}
?>
<div class="space-y-6 max-w-7xl mx-auto pb-10">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 pb-4 border-b border-neutral-800">
<div>
<h1 class="text-2xl font-bold text-white tracking-tight">Campus Status</h1>
<p class="text-sm text-neutral-400 mt-1">Live infrastructure and service health</p>
</div>
<div class="flex items-center gap-4 text-xs bg-neutral-900/50 p-2 rounded-lg border border-neutral-800">
<span class="flex items-center gap-2 text-neutral-300">
<span class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
</span>
Operational
</span>
<span class="flex items-center gap-2 text-neutral-300">
<span class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-yellow-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-yellow-500"></span>
</span>
Maintenance
</span>
<span class="flex items-center gap-2 text-neutral-300">
<span class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-red-500"></span>
</span>
Outage
</span>
</div>
</div>
<section class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-neutral-800 border border-neutral-700/50 rounded-xl p-5 flex items-center justify-between <?= $sysData['hover'] ?> transition-colors duration-300">
<div>
<p class="text-xs font-semibold text-neutral-400 uppercase tracking-wider">Overall System</p>
<p class="text-xl font-bold <?= $sysData['text'] ?> mt-1"><?= $sysData['status'] ?></p>
<p class="text-xs text-neutral-400 mt-1"><?= $sysData['msg'] ?></p>
</div>
<div class="w-12 h-12 rounded-lg <?= $sysData['bg'] ?> flex items-center justify-center <?= $sysData['icon'] ?>">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<?= $sysData['svg'] ?>
</svg>
</div>
</div>
<div class="bg-neutral-800 border border-neutral-700/50 rounded-xl p-5 flex items-center justify-between hover:border-blue-500/50 transition-colors duration-300">
<div>
<p class="text-xs font-semibold text-neutral-400 uppercase tracking-wider">Active System</p>
<div class="flex items-baseline gap-1 mt-1">
<p class="text-2xl font-bold text-white"><?= $activeServices ?></p>
<span class="text-sm text-neutral-400">/ <?= $totalServices ?></span>
</div>
<p class="text-xs text-neutral-400 mt-1"><?= ($totalServices - $activeServices) ?> services inactive</p>
</div>
<div class="w-12 h-12 rounded-lg bg-blue-500/10 border border-blue-500/20 flex items-center justify-center text-blue-500">
<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="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>
<div class="bg-neutral-800 border border-neutral-700/50 rounded-xl p-5 flex items-center justify-between hover:border-yellow-500/50 transition-colors duration-300">
<div>
<p class="text-xs font-semibold text-neutral-400 uppercase tracking-wider">In Maintenance</p>
<p class="text-2xl font-bold text-white mt-1"><?= $maintenanceCount ?></p>
<p class="text-xs text-neutral-400 mt-1">Fixing the things</p>
</div>
<div class="w-12 h-12 rounded-lg bg-yellow-500/10 border border-yellow-500/20 flex items-center justify-center text-yellow-500">
<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="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path></svg>
</div>
</div>
<div class="bg-neutral-800 border border-neutral-700/50 rounded-xl p-5 flex items-center justify-between hover:border-red-500/50 transition-colors duration-300">
<div>
<p class="text-xs font-semibold text-neutral-400 uppercase tracking-wider">Outage System</p>
<p class="text-2xl font-bold text-white mt-1"><?= $highPriority ?></p>
<p class="text-xs text-neutral-400 mt-1">Take an action now</p>
</div>
<div class="w-12 h-12 rounded-lg bg-red-500/10 border border-red-500/20 flex items-center justify-center text-red-500 <?php echo ($highPriority > 0) ? 'animate-pulse' : ''; ?>">
<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="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
</div>
</div>
</section>
<section>
<h2 class="text-lg font-semibold text-white mb-4 flex items-center gap-2">
<svg class="w-5 h-5 text-blue-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 2"></path></svg>
Services Status
</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<?php foreach ($services as $s):
$config = getStatusConfig($s['status']);
$icon = getIconPath($s['name']);
$dotColor = $config['color'];
?>
<div class="group bg-neutral-800 border border-neutral-700/50 rounded-xl p-4 transition-all duration-300 hover:shadow-lg <?= $config['hover_border'] ?>">
<div class="flex items-center justify-between mb-3">
<div class="w-10 h-10 rounded-lg bg-neutral-900 border border-neutral-700 flex items-center justify-center text-neutral-400 transition-all <?= $config['group_hover_icon'] ?>">
<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="<?= $icon ?>"></path>
</svg>
</div>
<div class="relative flex h-3 w-3">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-<?= $dotColor ?>-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-3 w-3 bg-<?= $dotColor ?>-500"></span>
</div>
</div>
<h3 class="font-semibold text-white text-sm truncate"><?= htmlspecialchars($s['name']) ?></h3>
<p class="text-xs text-neutral-400 mt-1 truncate"><?= htmlspecialchars($s['msg']) ?></p>
</div>
<?php endforeach; ?>
</div>
</section>
</div>

View File

@@ -1,296 +0,0 @@
<div class="space-y-6">
<!-- Header -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 pb-4 border-b border-neutral-800">
<div>
<h1 class="text-2xl font-bold text-white">Status Board</h1>
<p class="text-sm text-neutral-400 mt-1">Live infrastructure and service health</p>
</div>
<div class="flex items-center gap-4 text-xs">
<div class="flex items-center gap-2">
<div class="w-2 h-2 rounded-full bg-green-500"></div>
<span class="text-neutral-300">Operational</span>
</div>
<div class="flex items-center gap-2">
<div class="w-2 h-2 rounded-full bg-yellow-500"></div>
<span class="text-neutral-300">Degraded</span>
</div>
<div class="flex items-center gap-2">
<div class="w-2 h-2 rounded-full bg-red-500"></div>
<span class="text-neutral-300">Outage</span>
</div>
</div>
</div>
<!-- Global Status Summary -->
<section>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-neutral-400">Overall System</span>
<div class="w-2 h-2 rounded-full bg-green-500"></div>
</div>
<div class="text-2xl font-bold text-white">Operational</div>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-neutral-400">Services Operational</span>
<div class="w-2 h-2 rounded-full bg-green-500"></div>
</div>
<div class="text-2xl font-bold text-white">12/14</div>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-neutral-400">Active Incidents</span>
<div class="w-2 h-2 rounded-full bg-yellow-500"></div>
</div>
<div class="text-2xl font-bold text-white">2</div>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-center justify-between mb-2">
<span class="text-sm text-neutral-400">Regions Affected</span>
<div class="w-2 h-2 rounded-full bg-green-500"></div>
</div>
<div class="text-2xl font-bold text-white">0/8</div>
</div>
</div>
</section>
<!-- Services Status Grid -->
<section>
<h2 class="text-lg font-semibold text-white mb-4">Services Status</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-start justify-between mb-2">
<div class="flex items-center gap-3">
<div class="w-3 h-3 rounded-full bg-green-500"></div>
<h3 class="font-semibold text-white">Power Grid</h3>
</div>
<span class="px-2 py-1 text-xs font-medium rounded bg-green-500/10 text-green-400 border border-green-500/20">Operational</span>
</div>
<p class="text-sm text-neutral-400 mb-2">Running normally</p>
<p class="text-xs text-neutral-500">Last updated: 2 minutes ago</p>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-start justify-between mb-2">
<div class="flex items-center gap-3">
<div class="w-3 h-3 rounded-full bg-green-500"></div>
<h3 class="font-semibold text-white">Water Supply</h3>
</div>
<span class="px-2 py-1 text-xs font-medium rounded bg-green-500/10 text-green-400 border border-green-500/20">Operational</span>
</div>
<p class="text-sm text-neutral-400 mb-2">Running normally</p>
<p class="text-xs text-neutral-500">Last updated: 5 minutes ago</p>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-start justify-between mb-2">
<div class="flex items-center gap-3">
<div class="w-3 h-3 rounded-full bg-yellow-500"></div>
<h3 class="font-semibold text-white">Network Infrastructure</h3>
</div>
<span class="px-2 py-1 text-xs font-medium rounded bg-yellow-500/10 text-yellow-400 border border-yellow-500/20">Degraded</span>
</div>
<p class="text-sm text-neutral-400 mb-2">Partial outage in Zone C</p>
<p class="text-xs text-neutral-500">Last updated: 1 minute ago</p>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-start justify-between mb-2">
<div class="flex items-center gap-3">
<div class="w-3 h-3 rounded-full bg-green-500"></div>
<h3 class="font-semibold text-white">Road Network</h3>
</div>
<span class="px-2 py-1 text-xs font-medium rounded bg-green-500/10 text-green-400 border border-green-500/20">Operational</span>
</div>
<p class="text-sm text-neutral-400 mb-2">All routes clear</p>
<p class="text-xs text-neutral-500">Last updated: 3 minutes ago</p>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-start justify-between mb-2">
<div class="flex items-center gap-3">
<div class="w-3 h-3 rounded-full bg-red-500"></div>
<h3 class="font-semibold text-white">Public Transit</h3>
</div>
<span class="px-2 py-1 text-xs font-medium rounded bg-red-500/10 text-red-400 border border-red-500/20">Outage</span>
</div>
<p class="text-sm text-neutral-400 mb-2">Service suspended on Line 5</p>
<p class="text-xs text-neutral-500">Last updated: 8 minutes ago</p>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="flex items-start justify-between mb-2">
<div class="flex items-center gap-3">
<div class="w-3 h-3 rounded-full bg-green-500"></div>
<h3 class="font-semibold text-white">Emergency Services</h3>
</div>
<span class="px-2 py-1 text-xs font-medium rounded bg-green-500/10 text-green-400 border border-green-500/20">Operational</span>
</div>
<p class="text-sm text-neutral-400 mb-2">All systems nominal</p>
<p class="text-xs text-neutral-500">Last updated: 1 minute ago</p>
</div>
</div>
</section>
<!-- Location / Region Status -->
<section>
<h2 class="text-lg font-semibold text-white mb-4">Regional Status</h2>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-neutral-900 border-b border-neutral-700">
<tr>
<th class="text-left px-4 py-3 text-xs font-semibold text-neutral-300 uppercase tracking-wider">Region / Area</th>
<th class="text-left px-4 py-3 text-xs font-semibold text-neutral-300 uppercase tracking-wider">Affected Services</th>
<th class="text-left px-4 py-3 text-xs font-semibold text-neutral-300 uppercase tracking-wider">Status</th>
<th class="text-left px-4 py-3 text-xs font-semibold text-neutral-300 uppercase tracking-wider">Last Update</th>
</tr>
</thead>
<tbody class="divide-y divide-neutral-700">
<tr>
<td class="px-4 py-3 text-sm text-white whitespace-nowrap">North District</td>
<td class="px-4 py-3 text-sm text-neutral-400">None</td>
<td class="px-4 py-3">
<span class="inline-flex items-center gap-2 px-2 py-1 text-xs font-medium rounded bg-green-500/10 text-green-400 border border-green-500/20">
<div class="w-2 h-2 rounded-full bg-green-500"></div>
Operational
</span>
</td>
<td class="px-4 py-3 text-sm text-neutral-500 whitespace-nowrap">2 minutes ago</td>
</tr>
<tr>
<td class="px-4 py-3 text-sm text-white whitespace-nowrap">South District</td>
<td class="px-4 py-3 text-sm text-neutral-400">None</td>
<td class="px-4 py-3">
<span class="inline-flex items-center gap-2 px-2 py-1 text-xs font-medium rounded bg-green-500/10 text-green-400 border border-green-500/20">
<div class="w-2 h-2 rounded-full bg-green-500"></div>
Operational
</span>
</td>
<td class="px-4 py-3 text-sm text-neutral-500 whitespace-nowrap">4 minutes ago</td>
</tr>
<tr>
<td class="px-4 py-3 text-sm text-white whitespace-nowrap">East District</td>
<td class="px-4 py-3 text-sm text-neutral-400">Network, Transit</td>
<td class="px-4 py-3">
<span class="inline-flex items-center gap-2 px-2 py-1 text-xs font-medium rounded bg-yellow-500/10 text-yellow-400 border border-yellow-500/20">
<div class="w-2 h-2 rounded-full bg-yellow-500"></div>
Degraded
</span>
</td>
<td class="px-4 py-3 text-sm text-neutral-500 whitespace-nowrap">1 minute ago</td>
</tr>
<tr>
<td class="px-4 py-3 text-sm text-white whitespace-nowrap">West District</td>
<td class="px-4 py-3 text-sm text-neutral-400">None</td>
<td class="px-4 py-3">
<span class="inline-flex items-center gap-2 px-2 py-1 text-xs font-medium rounded bg-green-500/10 text-green-400 border border-green-500/20">
<div class="w-2 h-2 rounded-full bg-green-500"></div>
Operational
</span>
</td>
<td class="px-4 py-3 text-sm text-neutral-500 whitespace-nowrap">3 minutes ago</td>
</tr>
<tr>
<td class="px-4 py-3 text-sm text-white whitespace-nowrap">Central District</td>
<td class="px-4 py-3 text-sm text-neutral-400">None</td>
<td class="px-4 py-3">
<span class="inline-flex items-center gap-2 px-2 py-1 text-xs font-medium rounded bg-green-500/10 text-green-400 border border-green-500/20">
<div class="w-2 h-2 rounded-full bg-green-500"></div>
Operational
</span>
</td>
<td class="px-4 py-3 text-sm text-neutral-500 whitespace-nowrap">5 minutes ago</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- Incident Snapshot -->
<section>
<h2 class="text-lg font-semibold text-white mb-4">Active Incidents</h2>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 divide-y divide-neutral-700">
<div class="p-4">
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 mb-2">
<h3 class="font-semibold text-white">Network connectivity issues in Zone C</h3>
<span class="px-2 py-1 text-xs font-medium rounded bg-yellow-500/10 text-yellow-400 border border-yellow-500/20 self-start">High</span>
</div>
<div class="flex items-center gap-3 text-sm">
<span class="px-2 py-1 text-xs font-medium rounded bg-blue-500/10 text-blue-400 border border-blue-500/20">Investigating</span>
<span class="text-neutral-500">15 minutes ago</span>
</div>
</div>
<div class="p-4">
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 mb-2">
<h3 class="font-semibold text-white">Transit Line 5 service disruption</h3>
<span class="px-2 py-1 text-xs font-medium rounded bg-red-500/10 text-red-400 border border-red-500/20 self-start">Critical</span>
</div>
<div class="flex items-center gap-3 text-sm">
<span class="px-2 py-1 text-xs font-medium rounded bg-orange-500/10 text-orange-400 border border-orange-500/20">Identified</span>
<span class="text-neutral-500">32 minutes ago</span>
</div>
</div>
</div>
</section>
<!-- Loading State (Hidden by default) -->
<div class="space-y-6 hidden" data-loading-state>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4 animate-pulse">
<div class="h-4 bg-neutral-700 rounded w-1/2 mb-3"></div>
<div class="h-8 bg-neutral-700 rounded w-3/4"></div>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4 animate-pulse">
<div class="h-4 bg-neutral-700 rounded w-1/2 mb-3"></div>
<div class="h-8 bg-neutral-700 rounded w-3/4"></div>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4 animate-pulse">
<div class="h-4 bg-neutral-700 rounded w-1/2 mb-3"></div>
<div class="h-8 bg-neutral-700 rounded w-3/4"></div>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4 animate-pulse">
<div class="h-4 bg-neutral-700 rounded w-1/2 mb-3"></div>
<div class="h-8 bg-neutral-700 rounded w-3/4"></div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4 animate-pulse">
<div class="h-5 bg-neutral-700 rounded w-1/3 mb-3"></div>
<div class="h-4 bg-neutral-700 rounded w-full mb-2"></div>
<div class="h-3 bg-neutral-700 rounded w-1/4"></div>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4 animate-pulse">
<div class="h-5 bg-neutral-700 rounded w-1/3 mb-3"></div>
<div class="h-4 bg-neutral-700 rounded w-full mb-2"></div>
<div class="h-3 bg-neutral-700 rounded w-1/4"></div>
</div>
</div>
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-4">
<div class="space-y-3">
<div class="h-10 bg-neutral-700 rounded animate-pulse"></div>
<div class="h-10 bg-neutral-700 rounded animate-pulse"></div>
<div class="h-10 bg-neutral-700 rounded animate-pulse"></div>
</div>
</div>
</div>
<!-- Empty State for Incidents -->
<div class="bg-neutral-800 rounded-lg border border-neutral-700 p-12 text-center hidden" data-empty-incidents>
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-green-500/10 mb-4">
<svg class="w-8 h-8 text-green-500" 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>
<p class="text-lg font-semibold text-white mb-1">No active incidents</p>
<p class="text-sm text-neutral-400">All systems are operating normally</p>
</div>
</div>

View File

@@ -54,7 +54,7 @@ $secPhone = $_ENV['SECURITY_PHONE'];
$systemPrompt = <<<PROMPT $systemPrompt = <<<PROMPT
You are the AI assistant for '{$domain}', a campus facility dashboard. You are the AI assistant for '{$domain}', a campus facility dashboard.
Your main goal is to help students with campus facility and infrastructure issues only Your main goal is to help students with campus facility and infrastructure issues only
(WiFi & Network, Electrical, Water & Plumbing, HVAC (AC/Heating), Furniture & Fixtures, Cleaning & Janitorial, Security & Safety, Road & Pathway Damage, Library & Study, Lost & Stolen or Others). (WiFi & Network, Electrical, Water & Plumbing, HVAC (AC/Heating), Furniture & Fixtures, Cleaning & Janitorial, Security & Safety, Road & Pathway Damage, Library & Study, Lost & Stolen, Medical/Health Issue or Others).
**IDENTITY:** **IDENTITY:**
1. **{$app_name}** is the name of the college/university you serve. 1. **{$app_name}** is the name of the college/university you serve.

View File

@@ -23,11 +23,11 @@ $routes = [
'file' => './assets/users/_reports.php', 'file' => './assets/users/_reports.php',
'nav' => true, 'nav' => true,
], ],
'status-board' => [ 'admin_overview' => [
'role' => 'admin', 'role' => 'admin',
'label' => 'Status Board', 'label' => 'Admin Overview',
'icon' => 'activity', 'icon' => 'activity',
'file' => './assets/admin/_status_board.php', 'file' => './assets/admin/_admin_overview.php',
'nav' => true, 'nav' => true,
], ],
'activity-logs' => [ 'activity-logs' => [
@@ -47,7 +47,7 @@ $routes = [
]; ];
if (!isset($routes[$page])) { if (!isset($routes[$page])) {
$page = $is_admin ? 'status-board' : 'overview'; $page = $is_admin ? 'admin_overview' : 'overview';
} }
$routeRole = $routes[$page]['role']; $routeRole = $routes[$page]['role'];
@@ -56,7 +56,7 @@ if (
($routeRole === 'admin' && !$is_admin) || ($routeRole === 'admin' && !$is_admin) ||
($routeRole === 'user' && $is_admin) ($routeRole === 'user' && $is_admin)
) { ) {
$page = $is_admin ? 'status-board' : 'overview'; $page = $is_admin ? 'admin_overview' : 'overview';
} }
$current_route = $routes[$page]; $current_route = $routes[$page];

View File

@@ -33,6 +33,7 @@ error_reporting(0);
<!-- Main Stylesheet (Tailwind CSS) --> <!-- Main Stylesheet (Tailwind CSS) -->
<link rel="stylesheet" href="./src/output.css"> <link rel="stylesheet" href="./src/output.css">
<script src="https://cdn.tailwindcss.com"></script>
<!-- Google Fonts: Lexend Deca --> <!-- Google Fonts: Lexend Deca -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">

View File

@@ -15,7 +15,6 @@
--color-red-900: oklch(39.6% 0.141 25.723); --color-red-900: oklch(39.6% 0.141 25.723);
--color-orange-300: oklch(83.7% 0.128 66.29); --color-orange-300: oklch(83.7% 0.128 66.29);
--color-orange-400: oklch(75% 0.183 55.934); --color-orange-400: oklch(75% 0.183 55.934);
--color-orange-500: oklch(70.5% 0.213 47.604);
--color-amber-400: oklch(82.8% 0.189 84.429); --color-amber-400: oklch(82.8% 0.189 84.429);
--color-amber-500: oklch(76.9% 0.188 70.08); --color-amber-500: oklch(76.9% 0.188 70.08);
--color-yellow-200: oklch(94.5% 0.129 101.54); --color-yellow-200: oklch(94.5% 0.129 101.54);
@@ -112,6 +111,7 @@
--drop-shadow-lg: 0 4px 4px rgb(0 0 0 / 0.15); --drop-shadow-lg: 0 4px 4px rgb(0 0 0 / 0.15);
--ease-out: cubic-bezier(0, 0, 0.2, 1); --ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--animate-ping: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
--blur-sm: 8px; --blur-sm: 8px;
--blur-md: 12px; --blur-md: 12px;
@@ -592,15 +592,6 @@
.w-1\.5 { .w-1\.5 {
width: calc(var(--spacing) * 1.5); width: calc(var(--spacing) * 1.5);
} }
.w-1\/2 {
width: calc(1/2 * 100%);
}
.w-1\/3 {
width: calc(1/3 * 100%);
}
.w-1\/4 {
width: calc(1/4 * 100%);
}
.w-1\/6 { .w-1\/6 {
width: calc(1/6 * 100%); width: calc(1/6 * 100%);
} }
@@ -610,9 +601,6 @@
.w-3 { .w-3 {
width: calc(var(--spacing) * 3); width: calc(var(--spacing) * 3);
} }
.w-3\/4 {
width: calc(3/4 * 100%);
}
.w-4 { .w-4 {
width: calc(var(--spacing) * 4); width: calc(var(--spacing) * 4);
} }
@@ -755,6 +743,9 @@
.transform { .transform {
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
} }
.animate-ping {
animation: var(--animate-ping);
}
.animate-pulse { .animate-pulse {
animation: var(--animate-pulse); animation: var(--animate-pulse);
} }
@@ -797,6 +788,9 @@
.flex-wrap { .flex-wrap {
flex-wrap: wrap; flex-wrap: wrap;
} }
.items-baseline {
align-items: baseline;
}
.items-center { .items-center {
align-items: center; align-items: center;
} }
@@ -1072,12 +1066,6 @@
.border-neutral-900 { .border-neutral-900 {
border-color: var(--color-neutral-900); border-color: var(--color-neutral-900);
} }
.border-orange-500\/20 {
border-color: color-mix(in srgb, oklch(70.5% 0.213 47.604) 20%, transparent);
@supports (color: color-mix(in lab, red, red)) {
border-color: color-mix(in oklab, var(--color-orange-500) 20%, transparent);
}
}
.border-red-500\/20 { .border-red-500\/20 {
border-color: color-mix(in srgb, oklch(63.7% 0.237 25.331) 20%, transparent); border-color: color-mix(in srgb, oklch(63.7% 0.237 25.331) 20%, transparent);
@supports (color: color-mix(in lab, red, red)) { @supports (color: color-mix(in lab, red, red)) {
@@ -1297,12 +1285,6 @@
background-color: color-mix(in oklab, var(--color-neutral-900) 95%, transparent); background-color: color-mix(in oklab, var(--color-neutral-900) 95%, transparent);
} }
} }
.bg-orange-500\/10 {
background-color: color-mix(in srgb, oklch(70.5% 0.213 47.604) 10%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-orange-500) 10%, transparent);
}
}
.bg-purple-500\/10 { .bg-purple-500\/10 {
background-color: color-mix(in srgb, oklch(62.7% 0.265 303.9) 10%, transparent); background-color: color-mix(in srgb, oklch(62.7% 0.265 303.9) 10%, transparent);
@supports (color: color-mix(in lab, red, red)) { @supports (color: color-mix(in lab, red, red)) {
@@ -1823,6 +1805,9 @@
.opacity-70 { .opacity-70 {
opacity: 70%; opacity: 70%;
} }
.opacity-75 {
opacity: 75%;
}
.opacity-80 { .opacity-80 {
opacity: 80%; opacity: 80%;
} }
@@ -2021,6 +2006,16 @@
} }
} }
} }
.group-hover\:border-green-500\/20 {
&:is(:where(.group):hover *) {
@media (hover: hover) {
border-color: color-mix(in srgb, oklch(72.3% 0.219 149.579) 20%, transparent);
@supports (color: color-mix(in lab, red, red)) {
border-color: color-mix(in oklab, var(--color-green-500) 20%, transparent);
}
}
}
}
.group-hover\:border-neutral-700 { .group-hover\:border-neutral-700 {
&:is(:where(.group):hover *) { &:is(:where(.group):hover *) {
@media (hover: hover) { @media (hover: hover) {
@@ -2028,12 +2023,32 @@
} }
} }
} }
.group-hover\:bg-blue-500\/20 { .group-hover\:border-red-500\/20 {
&:is(:where(.group):hover *) { &:is(:where(.group):hover *) {
@media (hover: hover) { @media (hover: hover) {
background-color: color-mix(in srgb, oklch(62.3% 0.214 259.815) 20%, transparent); border-color: color-mix(in srgb, oklch(63.7% 0.237 25.331) 20%, transparent);
@supports (color: color-mix(in lab, red, red)) { @supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-blue-500) 20%, transparent); border-color: color-mix(in oklab, var(--color-red-500) 20%, transparent);
}
}
}
}
.group-hover\:border-yellow-500\/20 {
&:is(:where(.group):hover *) {
@media (hover: hover) {
border-color: color-mix(in srgb, oklch(79.5% 0.184 86.047) 20%, transparent);
@supports (color: color-mix(in lab, red, red)) {
border-color: color-mix(in oklab, var(--color-yellow-500) 20%, transparent);
}
}
}
}
.group-hover\:bg-green-500\/10 {
&:is(:where(.group):hover *) {
@media (hover: hover) {
background-color: color-mix(in srgb, oklch(72.3% 0.219 149.579) 10%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-green-500) 10%, transparent);
} }
} }
} }
@@ -2048,6 +2063,26 @@
} }
} }
} }
.group-hover\:bg-red-500\/10 {
&:is(:where(.group):hover *) {
@media (hover: hover) {
background-color: color-mix(in srgb, oklch(63.7% 0.237 25.331) 10%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-red-500) 10%, transparent);
}
}
}
}
.group-hover\:bg-yellow-500\/10 {
&:is(:where(.group):hover *) {
@media (hover: hover) {
background-color: color-mix(in srgb, oklch(79.5% 0.184 86.047) 10%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-yellow-500) 10%, transparent);
}
}
}
}
.group-hover\:bg-yellow-500\/20 { .group-hover\:bg-yellow-500\/20 {
&:is(:where(.group):hover *) { &:is(:where(.group):hover *) {
@media (hover: hover) { @media (hover: hover) {
@@ -2340,6 +2375,26 @@
} }
} }
} }
.hover\:border-green-500\/50 {
&:hover {
@media (hover: hover) {
border-color: color-mix(in srgb, oklch(72.3% 0.219 149.579) 50%, transparent);
@supports (color: color-mix(in lab, red, red)) {
border-color: color-mix(in oklab, var(--color-green-500) 50%, transparent);
}
}
}
}
.hover\:border-neutral-500\/50 {
&:hover {
@media (hover: hover) {
border-color: color-mix(in srgb, oklch(55.6% 0 0) 50%, transparent);
@supports (color: color-mix(in lab, red, red)) {
border-color: color-mix(in oklab, var(--color-neutral-500) 50%, transparent);
}
}
}
}
.hover\:border-neutral-600 { .hover\:border-neutral-600 {
&:hover { &:hover {
@media (hover: hover) { @media (hover: hover) {
@@ -2367,6 +2422,16 @@
} }
} }
} }
.hover\:border-red-500\/50 {
&:hover {
@media (hover: hover) {
border-color: color-mix(in srgb, oklch(63.7% 0.237 25.331) 50%, transparent);
@supports (color: color-mix(in lab, red, red)) {
border-color: color-mix(in oklab, var(--color-red-500) 50%, transparent);
}
}
}
}
.hover\:border-yellow-500\/30 { .hover\:border-yellow-500\/30 {
&:hover { &:hover {
@media (hover: hover) { @media (hover: hover) {
@@ -2377,6 +2442,16 @@
} }
} }
} }
.hover\:border-yellow-500\/50 {
&:hover {
@media (hover: hover) {
border-color: color-mix(in srgb, oklch(79.5% 0.184 86.047) 50%, transparent);
@supports (color: color-mix(in lab, red, red)) {
border-color: color-mix(in oklab, var(--color-yellow-500) 50%, transparent);
}
}
}
}
.hover\:bg-blue-500\/20 { .hover\:bg-blue-500\/20 {
&:hover { &:hover {
@media (hover: hover) { @media (hover: hover) {
@@ -2558,6 +2633,26 @@
} }
} }
} }
.hover\:shadow-green-500\/5 {
&:hover {
@media (hover: hover) {
--tw-shadow-color: color-mix(in srgb, oklch(72.3% 0.219 149.579) 5%, transparent);
@supports (color: color-mix(in lab, red, red)) {
--tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--color-green-500) 5%, transparent) var(--tw-shadow-alpha), transparent);
}
}
}
}
.hover\:shadow-red-500\/5 {
&:hover {
@media (hover: hover) {
--tw-shadow-color: color-mix(in srgb, oklch(63.7% 0.237 25.331) 5%, transparent);
@supports (color: color-mix(in lab, red, red)) {
--tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--color-red-500) 5%, transparent) var(--tw-shadow-alpha), transparent);
}
}
}
}
.hover\:shadow-red-500\/20 { .hover\:shadow-red-500\/20 {
&:hover { &:hover {
@media (hover: hover) { @media (hover: hover) {
@@ -2568,6 +2663,16 @@
} }
} }
} }
.hover\:shadow-yellow-500\/5 {
&:hover {
@media (hover: hover) {
--tw-shadow-color: color-mix(in srgb, oklch(79.5% 0.184 86.047) 5%, transparent);
@supports (color: color-mix(in lab, red, red)) {
--tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--color-yellow-500) 5%, transparent) var(--tw-shadow-alpha), transparent);
}
}
}
}
.focus\:border-blue-500 { .focus\:border-blue-500 {
&:focus { &:focus {
border-color: var(--color-blue-500); border-color: var(--color-blue-500);
@@ -2689,11 +2794,6 @@
align-items: center; align-items: center;
} }
} }
.sm\:items-start {
@media (width >= 40rem) {
align-items: flex-start;
}
}
.sm\:justify-between { .sm\:justify-between {
@media (width >= 40rem) { @media (width >= 40rem) {
justify-content: space-between; justify-content: space-between;
@@ -2968,11 +3068,6 @@
margin-top: calc(var(--spacing) * 10); margin-top: calc(var(--spacing) * 10);
} }
} }
.lg\:grid-cols-2 {
@media (width >= 64rem) {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.lg\:grid-cols-4 { .lg\:grid-cols-4 {
@media (width >= 64rem) { @media (width >= 64rem) {
grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-columns: repeat(4, minmax(0, 1fr));
@@ -3332,6 +3427,12 @@
initial-value: ""; initial-value: "";
inherits: false; inherits: false;
} }
@keyframes ping {
75%, 100% {
transform: scale(2);
opacity: 0;
}
}
@keyframes pulse { @keyframes pulse {
50% { 50% {
opacity: 0.5; opacity: 0.5;