diff --git a/src.zip b/src.zip new file mode 100644 index 00000000..968b49a9 Binary files /dev/null and b/src.zip differ diff --git a/src/assets/_navbar.php b/src/assets/_navbar.php index 5faab4d5..8c65c23b 100755 --- a/src/assets/_navbar.php +++ b/src/assets/_navbar.php @@ -24,7 +24,7 @@ $user_initial = strtoupper($current_email[0]);
-
+
diff --git a/src/assets/_sidebar.php b/src/assets/_sidebar.php index e1f984fd..cbfe548a 100755 --- a/src/assets/_sidebar.php +++ b/src/assets/_sidebar.php @@ -33,14 +33,14 @@ $inactiveClass = 'text-neutral-400 hover:text-white hover:bg-neutral-800/60 bord - - + + 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" /> - Status Board + Admin Overview 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' => '' + ]; +} 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' => '' + ]; +} 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' => '' + ]; +} +?> + +
+ +
+
+

Campus Status

+

Live infrastructure and service health

+
+ +
+ + + + + + Operational + + + + + + + Maintenance + + + + + + + Outage + +
+
+ +
+ +
+
+

Overall System

+

+

+
+
+ + + +
+
+ +
+
+

Active System

+
+

+ / +
+

services inactive

+
+
+ +
+
+ +
+
+

In Maintenance

+

+

Fixing the things

+
+
+ +
+
+ +
+
+

Outage System

+

+

Take an action now

+
+
+ +
+
+ +
+ +
+

+ + Services Status +

+ +
+ +
+
+
+ + + +
+ +
+ + +
+ +
+

+

+
+ +
+
+ +
\ No newline at end of file diff --git a/src/assets/admin/_status_board.php b/src/assets/admin/_status_board.php deleted file mode 100755 index 2d363618..00000000 --- a/src/assets/admin/_status_board.php +++ /dev/null @@ -1,296 +0,0 @@ -
- -
-
-

Status Board

-

Live infrastructure and service health

-
-
-
-
- Operational -
-
-
- Degraded -
-
-
- Outage -
-
-
- - -
-
-
-
- Overall System -
-
-
Operational
-
- -
-
- Services Operational -
-
-
12/14
-
- -
-
- Active Incidents -
-
-
2
-
- -
-
- Regions Affected -
-
-
0/8
-
-
-
- - -
-

Services Status

-
-
-
-
-
-

Power Grid

-
- Operational -
-

Running normally

-

Last updated: 2 minutes ago

-
- -
-
-
-
-

Water Supply

-
- Operational -
-

Running normally

-

Last updated: 5 minutes ago

-
- -
-
-
-
-

Network Infrastructure

-
- Degraded -
-

Partial outage in Zone C

-

Last updated: 1 minute ago

-
- -
-
-
-
-

Road Network

-
- Operational -
-

All routes clear

-

Last updated: 3 minutes ago

-
- -
-
-
-
-

Public Transit

-
- Outage -
-

Service suspended on Line 5

-

Last updated: 8 minutes ago

-
- -
-
-
-
-

Emergency Services

-
- Operational -
-

All systems nominal

-

Last updated: 1 minute ago

-
-
-
- - -
-

Regional Status

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Region / AreaAffected ServicesStatusLast Update
North DistrictNone - -
- Operational -
-
2 minutes ago
South DistrictNone - -
- Operational -
-
4 minutes ago
East DistrictNetwork, Transit - -
- Degraded -
-
1 minute ago
West DistrictNone - -
- Operational -
-
3 minutes ago
Central DistrictNone - -
- Operational -
-
5 minutes ago
-
-
-
- - -
-

Active Incidents

-
-
-
-

Network connectivity issues in Zone C

- High -
-
- Investigating - 15 minutes ago -
-
- -
-
-

Transit Line 5 service disruption

- Critical -
-
- Identified - 32 minutes ago -
-
-
-
- - - - - - -
\ No newline at end of file diff --git a/src/core/actions/user_gemini.php b/src/core/actions/user_gemini.php index 948499f5..6f85d9e4 100644 --- a/src/core/actions/user_gemini.php +++ b/src/core/actions/user_gemini.php @@ -54,7 +54,7 @@ $secPhone = $_ENV['SECURITY_PHONE']; $systemPrompt = << [ - 'parts' => [[ 'text' => $systemPrompt ]] + 'parts' => [['text' => $systemPrompt]] ], 'contents' => [ [ diff --git a/src/core/router.php b/src/core/router.php index 3b0aecf2..ac99028d 100644 --- a/src/core/router.php +++ b/src/core/router.php @@ -23,11 +23,11 @@ $routes = [ 'file' => './assets/users/_reports.php', 'nav' => true, ], - 'status-board' => [ + 'admin_overview' => [ 'role' => 'admin', - 'label' => 'Status Board', + 'label' => 'Admin Overview', 'icon' => 'activity', - 'file' => './assets/admin/_status_board.php', + 'file' => './assets/admin/_admin_overview.php', 'nav' => true, ], 'activity-logs' => [ @@ -47,7 +47,7 @@ $routes = [ ]; if (!isset($routes[$page])) { - $page = $is_admin ? 'status-board' : 'overview'; + $page = $is_admin ? 'admin_overview' : 'overview'; } $routeRole = $routes[$page]['role']; @@ -56,7 +56,7 @@ if ( ($routeRole === 'admin' && !$is_admin) || ($routeRole === 'user' && $is_admin) ) { - $page = $is_admin ? 'status-board' : 'overview'; + $page = $is_admin ? 'admin_overview' : 'overview'; } $current_route = $routes[$page]; diff --git a/src/pages/dashboard.php b/src/pages/dashboard.php index 60f3fb80..65c8bcbc 100644 --- a/src/pages/dashboard.php +++ b/src/pages/dashboard.php @@ -33,6 +33,7 @@ error_reporting(0); + diff --git a/src/src/output.css b/src/src/output.css index 9cdbc091..62880858 100644 --- a/src/src/output.css +++ b/src/src/output.css @@ -15,7 +15,6 @@ --color-red-900: oklch(39.6% 0.141 25.723); --color-orange-300: oklch(83.7% 0.128 66.29); --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-500: oklch(76.9% 0.188 70.08); --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); --ease-out: cubic-bezier(0, 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; --blur-sm: 8px; --blur-md: 12px; @@ -592,15 +592,6 @@ .w-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 { width: calc(1/6 * 100%); } @@ -610,9 +601,6 @@ .w-3 { width: calc(var(--spacing) * 3); } - .w-3\/4 { - width: calc(3/4 * 100%); - } .w-4 { width: calc(var(--spacing) * 4); } @@ -755,6 +743,9 @@ .transform { 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 { animation: var(--animate-pulse); } @@ -797,6 +788,9 @@ .flex-wrap { flex-wrap: wrap; } + .items-baseline { + align-items: baseline; + } .items-center { align-items: center; } @@ -1072,12 +1066,6 @@ .border-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-color: color-mix(in srgb, oklch(63.7% 0.237 25.331) 20%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -1297,12 +1285,6 @@ 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 { background-color: color-mix(in srgb, oklch(62.7% 0.265 303.9) 10%, transparent); @supports (color: color-mix(in lab, red, red)) { @@ -1823,6 +1805,9 @@ .opacity-70 { opacity: 70%; } + .opacity-75 { + opacity: 75%; + } .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 { &:is(:where(.group):hover *) { @media (hover: hover) { @@ -2028,12 +2023,32 @@ } } } - .group-hover\:bg-blue-500\/20 { + .group-hover\:border-red-500\/20 { &:is(:where(.group):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)) { - 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 { &:is(:where(.group):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 { @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 { @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 { @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 { @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-color: var(--color-blue-500); @@ -2689,11 +2794,6 @@ align-items: center; } } - .sm\:items-start { - @media (width >= 40rem) { - align-items: flex-start; - } - } .sm\:justify-between { @media (width >= 40rem) { justify-content: space-between; @@ -2968,11 +3068,6 @@ 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 { @media (width >= 64rem) { grid-template-columns: repeat(4, minmax(0, 1fr)); @@ -3332,6 +3427,12 @@ initial-value: ""; inherits: false; } +@keyframes ping { + 75%, 100% { + transform: scale(2); + opacity: 0; + } +} @keyframes pulse { 50% { opacity: 0.5;