Initial commit: LotusGuild Terminal Design System v1.0
Unified CSS, JavaScript utilities, HTML template, and framework skeleton files for Tinker Tickets (PHP), PULSE (Node.js), and GANDALF (Flask). Includes aesthetic_diff.md documenting every divergence between the three apps with prioritised recommendations for convergence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
154
php/layout.php
Normal file
154
php/layout.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* LOTUSGUILD TERMINAL DESIGN SYSTEM — PHP Base Layout
|
||||
* Copy this into your PHP app as the base view template.
|
||||
*
|
||||
* Requires:
|
||||
* - middleware/SecurityHeadersMiddleware.php (provides $nonce)
|
||||
* - middleware/CsrfMiddleware.php (provides CSRF token)
|
||||
* - middleware/AuthMiddleware.php (provides $currentUser)
|
||||
* - config/config.php (provides $config)
|
||||
*
|
||||
* Usage:
|
||||
* Call SecurityHeadersMiddleware::apply() before any output.
|
||||
* Then include this file (or extend it) in your view.
|
||||
*
|
||||
* Variables expected:
|
||||
* $nonce string CSP nonce from SecurityHeadersMiddleware::getNonce()
|
||||
* $currentUser array ['username', 'name', 'is_admin', 'groups']
|
||||
* $pageTitle string Page <title> suffix
|
||||
* $activeNav string Which nav link is active ('dashboard','tickets',etc.)
|
||||
* $config array From config/config.php
|
||||
*/
|
||||
|
||||
// Defensive defaults
|
||||
$nonce = $nonce ?? '';
|
||||
$currentUser = $currentUser ?? [];
|
||||
$pageTitle = $pageTitle ?? 'Dashboard';
|
||||
$activeNav = $activeNav ?? '';
|
||||
$config = $config ?? [];
|
||||
$isAdmin = $currentUser['is_admin'] ?? false;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($pageTitle); ?> — <?php echo htmlspecialchars($config['APP_NAME'] ?? 'LotusGuild'); ?></title>
|
||||
<meta name="description" content="LotusGuild infrastructure management">
|
||||
|
||||
<!-- Unified design system CSS -->
|
||||
<link rel="stylesheet" href="/web_template/base.css">
|
||||
<!-- App-specific CSS (extends base, never overrides variables without good reason) -->
|
||||
<link rel="stylesheet" href="/assets/css/app.css?v=<?php echo $config['CSS_VERSION'] ?? '20260314'; ?>">
|
||||
|
||||
<link rel="icon" href="/assets/images/favicon.png" type="image/png">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Boot overlay — runs once per session via lt.boot.run() -->
|
||||
<div id="lt-boot" class="lt-boot-overlay"
|
||||
data-app-name="<?php echo htmlspecialchars($config['APP_NAME'] ?? 'APP'); ?>"
|
||||
style="display:none">
|
||||
<pre id="lt-boot-text" class="lt-boot-text"></pre>
|
||||
</div>
|
||||
|
||||
<!-- =========================================================
|
||||
HEADER
|
||||
========================================================= -->
|
||||
<header class="lt-header">
|
||||
<div class="lt-header-left">
|
||||
|
||||
<div class="lt-brand">
|
||||
<a href="/" class="lt-brand-title" style="text-decoration:none">
|
||||
<?php echo htmlspecialchars($config['APP_NAME'] ?? 'APP'); ?>
|
||||
</a>
|
||||
<span class="lt-brand-subtitle"><?php echo htmlspecialchars($config['APP_SUBTITLE'] ?? 'LotusGuild Infrastructure'); ?></span>
|
||||
</div>
|
||||
|
||||
<nav class="lt-nav" aria-label="Main navigation">
|
||||
<a href="/" class="lt-nav-link <?php echo $activeNav === 'dashboard' ? 'active' : ''; ?>">Dashboard</a>
|
||||
<a href="/tickets" class="lt-nav-link <?php echo $activeNav === 'tickets' ? 'active' : ''; ?>">Tickets</a>
|
||||
|
||||
<?php if ($isAdmin): ?>
|
||||
<div class="lt-nav-dropdown">
|
||||
<a href="#" class="lt-nav-link <?php echo str_starts_with($activeNav, 'admin') ? 'active' : ''; ?>">
|
||||
Admin ▾
|
||||
</a>
|
||||
<ul class="lt-nav-dropdown-menu">
|
||||
<li><a href="/admin/templates">Templates</a></li>
|
||||
<li><a href="/admin/workflow">Workflow</a></li>
|
||||
<li><a href="/admin/recurring-tickets">Recurring</a></li>
|
||||
<li><a href="/admin/custom-fields">Custom Fields</a></li>
|
||||
<li><a href="/admin/user-activity">User Activity</a></li>
|
||||
<li><a href="/admin/audit-log">Audit Log</a></li>
|
||||
<li><a href="/admin/api-keys">API Keys</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="lt-header-right">
|
||||
<?php if (!empty($currentUser['name'])): ?>
|
||||
<span class="lt-header-user"><?php echo htmlspecialchars($currentUser['name']); ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if ($isAdmin): ?>
|
||||
<span class="lt-badge-admin">admin</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- =========================================================
|
||||
MAIN CONTENT — provided by the including view
|
||||
========================================================= -->
|
||||
<main class="lt-main lt-container">
|
||||
|
||||
<!-- CONTENT SLOT: the including view outputs its content here -->
|
||||
<?php
|
||||
// If using output buffering pattern:
|
||||
// ob_start(); include 'views/DashboardView.php'; $content = ob_get_clean();
|
||||
// echo $content;
|
||||
//
|
||||
// Or simply include views inline after including this layout.
|
||||
?>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- =========================================================
|
||||
SCRIPTS
|
||||
All <script> tags MUST include the CSP nonce attribute.
|
||||
========================================================= -->
|
||||
|
||||
<!-- Inject runtime config + CSRF token (nonce required for CSP) -->
|
||||
<script nonce="<?php echo $nonce; ?>">
|
||||
window.CSRF_TOKEN = '<?php echo CsrfMiddleware::getToken(); ?>';
|
||||
window.APP_TIMEZONE = '<?php echo htmlspecialchars($config['TIMEZONE'] ?? 'UTC'); ?>';
|
||||
window.APP_TIMEZONE_ABBREV = '<?php echo htmlspecialchars($config['TIMEZONE_ABBREV'] ?? 'UTC'); ?>';
|
||||
window.CURRENT_USER = {
|
||||
id: <?php echo (int)($currentUser['user_id'] ?? 0); ?>,
|
||||
username: <?php echo json_encode($currentUser['username'] ?? ''); ?>,
|
||||
isAdmin: <?php echo $isAdmin ? 'true' : 'false'; ?>,
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- Unified design system JS -->
|
||||
<script nonce="<?php echo $nonce; ?>" src="/web_template/base.js"></script>
|
||||
|
||||
<!-- App-specific JS (cache-busted) -->
|
||||
<script nonce="<?php echo $nonce; ?>"
|
||||
src="/assets/js/app.js?v=<?php echo $config['JS_VERSION'] ?? '20260314'; ?>">
|
||||
</script>
|
||||
|
||||
<!-- Per-page inline JS goes here in the including view, e.g.: -->
|
||||
<!--
|
||||
<script nonce="<?php echo $nonce; ?>">
|
||||
lt.sortTable.init('ticket-table');
|
||||
lt.tableNav.init('ticket-table');
|
||||
lt.keys.on('n', () => window.location.href = '/ticket/create');
|
||||
lt.autoRefresh.start(() => fetch('/api/status').then(r=>r.json()).then(updateUI), 30000);
|
||||
</script>
|
||||
-->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user