layout.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. <?php
  2. // layout.php – shared admin HTML chrome
  3. // Call admin_head($title) and admin_foot() around content.
  4. function admin_head(string $title, string $active = ''): void {
  5. $nav = [
  6. 'index.php' => 'Dashboard',
  7. 'activities.php' => 'Activities',
  8. 'activity_groups.php'=> 'Activity Groups',
  9. 'items.php' => 'Items',
  10. 'item_groups.php' => 'Item Groups',
  11. 'mappings.php' => 'Mappings',
  12. ];
  13. ?>
  14. <!DOCTYPE html>
  15. <html lang="en">
  16. <head>
  17. <meta charset="UTF-8">
  18. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  19. <title>PackIt Admin – <?= htmlspecialchars($title) ?></title>
  20. <link rel="preconnect" href="https://fonts.googleapis.com">
  21. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  22. <link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
  23. <style>
  24. :root{--bg:#0f1117;--surface:#181c27;--border:#2a2f3f;--accent:#e8c547;--accent2:#4fd1c5;--text:#e8eaf0;--muted:#7a8099;--danger:#e05555;--green:#5cb85c;--radius:8px;}
  25. *,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
  26. body{background:var(--bg);color:var(--text);font-family:'DM Sans',sans-serif;font-size:14px;display:flex;min-height:100vh;}
  27. /* sidebar */
  28. .sidebar{width:200px;flex-shrink:0;background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;padding:1.5rem 0;}
  29. .sidebar-logo{font-family:'Bebas Neue',sans-serif;font-size:1.8rem;color:var(--accent);letter-spacing:.05em;padding:0 1.25rem .5rem;}
  30. .sidebar-sub{font-size:.7rem;color:var(--muted);letter-spacing:.1em;text-transform:uppercase;padding:0 1.25rem 1.5rem;}
  31. .nav-link{display:block;padding:.55rem 1.25rem;color:var(--muted);text-decoration:none;font-size:.85rem;font-weight:500;border-left:3px solid transparent;transition:color .15s,border-color .15s,background .15s;}
  32. .nav-link:hover{color:var(--text);background:rgba(255,255,255,.03);}
  33. .nav-link.active{color:var(--accent);border-left-color:var(--accent);background:rgba(232,197,71,.05);}
  34. .nav-sep{height:1px;background:var(--border);margin:.5rem 1.25rem;}
  35. .sidebar-footer{margin-top:auto;padding:1rem 1.25rem;}
  36. .btn-logout{display:block;text-align:center;padding:.5rem;background:transparent;border:1px solid var(--border);border-radius:var(--radius);color:var(--muted);font-size:.8rem;cursor:pointer;text-decoration:none;transition:all .15s;width:100%;}
  37. .btn-logout:hover{border-color:var(--danger);color:var(--danger);}
  38. /* main */
  39. .admin-main{flex:1;display:flex;flex-direction:column;min-width:0;}
  40. .admin-topbar{padding:.9rem 1.75rem;border-bottom:1px solid var(--border);font-size:.85rem;color:var(--muted);display:flex;align-items:center;justify-content:space-between;}
  41. .admin-content{padding:1.75rem;flex:1;}
  42. h1{font-family:'Bebas Neue',sans-serif;font-size:1.8rem;letter-spacing:.05em;color:var(--accent);margin-bottom:1.25rem;}
  43. h2{font-size:1rem;font-weight:600;margin-bottom:.75rem;color:var(--text);}
  44. /* table */
  45. .tbl{width:100%;border-collapse:collapse;margin-bottom:1.5rem;}
  46. .tbl th{text-align:left;font-size:.75rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);padding:.6rem .9rem;border-bottom:1px solid var(--border);}
  47. .tbl td{padding:.6rem .9rem;border-bottom:1px solid rgba(42,47,63,.6);vertical-align:middle;}
  48. .tbl tr:hover td{background:rgba(255,255,255,.02);}
  49. /* Stable-layout table for inline-edit admin pages (opt-in via .tbl-fixed). */
  50. .tbl-fixed{table-layout:fixed;}
  51. .tbl-fixed td,.tbl-fixed th{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  52. .tbl-fixed tr[data-editing="false"] .edit{display:none;}
  53. .tbl-fixed tr[data-editing="true"] .view{display:none;}
  54. .tbl-fixed .edit input,.tbl-fixed .edit select{margin:0;width:100%;}
  55. .tbl-fixed .actions-buttons{display:flex;gap:.4rem;justify-content:flex-end;}
  56. .tbl-fixed tr[data-editing="true"]{background:rgba(79,209,197,.04);}
  57. /* form card */
  58. .card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:1.25rem 1.5rem;margin-bottom:1.5rem;}
  59. label{display:block;font-size:.8rem;font-weight:500;color:var(--muted);margin-bottom:.3rem;letter-spacing:.04em;text-transform:uppercase;}
  60. input[type=text],input[type=password],input[type=number],select,textarea{width:100%;background:var(--bg);border:1.5px solid var(--border);color:var(--text);padding:.55rem .8rem;border-radius:var(--radius);font-family:'DM Sans',sans-serif;font-size:.9rem;outline:none;transition:border-color .15s;margin-bottom:.9rem;}
  61. input:focus,select:focus,textarea:focus{border-color:var(--accent2);}
  62. .form-row{display:grid;grid-template-columns:1fr 1fr;gap:1rem;}
  63. /* buttons */
  64. .btn{display:inline-flex;align-items:center;gap:.4rem;padding:.5rem 1rem;border-radius:var(--radius);font-family:'DM Sans',sans-serif;font-size:.83rem;font-weight:500;cursor:pointer;border:1.5px solid transparent;text-decoration:none;transition:all .15s;}
  65. .btn-sm{padding:.3rem .7rem;font-size:.78rem;}
  66. .btn-primary{background:var(--accent);color:#0f1117;border-color:var(--accent);}
  67. .btn-primary:hover{background:#f0d260;}
  68. .btn-danger{background:transparent;color:var(--danger);border-color:var(--danger);}
  69. .btn-danger:hover{background:rgba(224,85,85,.08);}
  70. .btn-secondary{background:transparent;color:var(--text);border-color:var(--border);}
  71. .btn-secondary:hover{border-color:var(--muted);}
  72. .btn-teal{background:transparent;color:var(--accent2);border-color:var(--accent2);}
  73. .btn-teal:hover{background:rgba(79,209,197,.08);}
  74. /* alerts */
  75. .alert{padding:.75rem 1rem;border-radius:var(--radius);margin-bottom:1rem;font-size:.85rem;}
  76. .alert-success{background:rgba(92,184,92,.1);border:1px solid rgba(92,184,92,.3);color:var(--green);}
  77. .alert-error{background:rgba(224,85,85,.1);border:1px solid rgba(224,85,85,.3);color:var(--danger);}
  78. /* badges */
  79. .badge{display:inline-block;padding:.15rem .55rem;border-radius:999px;font-size:.72rem;font-weight:600;letter-spacing:.04em;}
  80. .badge-group{background:rgba(79,209,197,.1);color:var(--accent2);border:1px solid rgba(79,209,197,.3);}
  81. </style>
  82. </head>
  83. <body>
  84. <aside class="sidebar">
  85. <div class="sidebar-logo">PackIt</div>
  86. <div class="sidebar-sub">Admin Panel</div>
  87. <?php foreach ($nav as $file => $label): ?>
  88. <a href="<?= $file ?>" class="nav-link <?= $active === $file ? 'active' : '' ?>"><?= $label ?></a>
  89. <?php endforeach; ?>
  90. <div class="nav-sep"></div>
  91. <a href="../index.php" class="nav-link" style="font-size:.78rem">← Back to site</a>
  92. <div class="sidebar-footer">
  93. <a href="logout.php" class="btn-logout">Log out</a>
  94. </div>
  95. </aside>
  96. <div class="admin-main">
  97. <div class="admin-topbar">
  98. <span>PackIt Admin</span>
  99. <span style="color:var(--accent);font-weight:500"><?= htmlspecialchars($_SESSION['admin_user'] ?? '') ?></span>
  100. </div>
  101. <div class="admin-content">
  102. <?php } // end admin_head
  103. function admin_foot(): void { ?>
  104. </div></div></body></html>
  105. <?php }
  106. function flash(string $key, string $msg): void {
  107. $_SESSION["flash_$key"] = $msg;
  108. }
  109. function get_flash(string $key): string {
  110. $msg = $_SESSION["flash_$key"] ?? '';
  111. unset($_SESSION["flash_$key"]);
  112. return $msg;
  113. }
  114. function show_alerts(): void {
  115. $s = get_flash('success');
  116. $e = get_flash('error');
  117. if ($s) echo '<div class="alert alert-success">' . htmlspecialchars($s) . '</div>';
  118. if ($e) echo '<div class="alert alert-error">' . htmlspecialchars($e) . '</div>';
  119. }