index.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. <?php
  2. declare(strict_types=1);
  3. require_once __DIR__ . '/src/bootstrap.php';
  4. $status = app_monitor_service()->getStatus();
  5. $appName = $status['app']['name'] ?? 'Getränkeautomat Monitor';
  6. header('Content-Type: text/html; charset=UTF-8');
  7. ?>
  8. <!DOCTYPE html>
  9. <html lang="de">
  10. <head>
  11. <meta charset="UTF-8">
  12. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  13. <title><?= htmlspecialchars($appName, ENT_QUOTES) ?></title>
  14. <link rel="stylesheet" href="<?= htmlspecialchars(app_url('/styles.css'), ENT_QUOTES) ?>">
  15. </head>
  16. <body>
  17. <div class="page-shell">
  18. <header class="hero">
  19. <div class="hero__copy">
  20. <p class="eyebrow">Füllstand live im Blick</p>
  21. <h1><?= htmlspecialchars($appName, ENT_QUOTES) ?></h1>
  22. <p class="hero__lede">
  23. Überwache Sensorwerte, erkenne Leerstände frühzeitig und springe direkt ins Adminpanel, wenn sich Grenzwerte oder Alarmwege ändern sollen.
  24. </p>
  25. </div>
  26. <div class="hero__stats">
  27. <div class="stat-card">
  28. <span>Automaten</span>
  29. <strong data-summary="machine_count"><?= (int) ($status['summary']['machine_count'] ?? 0) ?></strong>
  30. </div>
  31. <div class="stat-card">
  32. <span>Fächer</span>
  33. <strong data-summary="slot_count"><?= (int) ($status['summary']['slot_count'] ?? 0) ?></strong>
  34. </div>
  35. <div class="stat-card stat-card--alert">
  36. <span>Kritisch</span>
  37. <strong data-summary="critical_count"><?= (int) ($status['summary']['critical_count'] ?? 0) ?></strong>
  38. </div>
  39. </div>
  40. <div class="hero__actions">
  41. <a class="button button--primary" href="<?= htmlspecialchars(app_url('/admin/'), ENT_QUOTES) ?>">Adminpanel</a>
  42. <a class="button button--ghost" href="<?= htmlspecialchars(app_url('/api/v1/status.php'), ENT_QUOTES) ?>" target="_blank" rel="noreferrer">Status JSON</a>
  43. <a class="button button--ghost" href="<?= htmlspecialchars(app_url('/api-docs/'), ENT_QUOTES) ?>" target="_blank" rel="noreferrer">OpenAPI/Swagger</a>
  44. </div>
  45. </header>
  46. <main class="dashboard">
  47. <section class="panel panel--controls">
  48. <div>
  49. <h2>Automatenansicht</h2>
  50. <p>Filtere einzelne Automaten oder beobachte den Gesamtzustand auf einen Blick.</p>
  51. </div>
  52. <div class="chip-row" id="machine-filter"></div>
  53. </section>
  54. <section class="panel">
  55. <div class="panel__header">
  56. <div>
  57. <h2>Füllstand nach Fach</h2>
  58. <p>Die Karten zeigen Messwert, geschätzten Bestand und den Alarmstatus pro Sensor.</p>
  59. </div>
  60. <p class="timestamp">Letzte Aktualisierung: <span id="generated-at"><?= htmlspecialchars($status['generated_at'], ENT_QUOTES) ?></span></p>
  61. </div>
  62. <div id="machine-grid" class="machine-grid"></div>
  63. </section>
  64. <section class="panel">
  65. <div class="panel__header">
  66. <div>
  67. <h2>Letzte Alarmereignisse</h2>
  68. <p>Es werden nur Zustandswechsel geloggt, keine wiederholten Daueralarme.</p>
  69. </div>
  70. </div>
  71. <div id="alert-list" class="alert-list"></div>
  72. </section>
  73. </main>
  74. </div>
  75. <script
  76. id="initial-status"
  77. type="application/json"
  78. data-base-path="<?= htmlspecialchars(app_base_path(), ENT_QUOTES) ?>"
  79. ><?= json_encode($status, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?></script>
  80. <script src="<?= htmlspecialchars(app_url('/app.js'), ENT_QUOTES) ?>" defer></script>
  81. </body>
  82. </html>