Files
shorebird-server/web/index.html
T
2026-06-12 04:22:58 +08:00

241 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shorebird Self-Hosted</title>
<link rel="stylesheet" href="/web/css/style.css">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='8' fill='%230891b2'/><text x='50%25' y='55%25' text-anchor='middle' dy='.1em' font-size='20' fill='white'>🐦</text></svg>">
</head>
<body>
<!-- ============================================================
LOGIN PAGE
============================================================ -->
<div id="loginPage" class="login-container">
<div class="login-card">
<div class="logo-icon">🐦</div>
<h1>Shorebird</h1>
<p class="subtitle">Self-Hosted Dashboard</p>
<div id="loginError" class="login-error"></div>
<form id="loginForm">
<div class="form-group">
<label for="loginEmail">Email</label>
<input type="email" id="loginEmail" placeholder="admin@example.com" required autofocus>
</div>
<div class="form-group">
<label for="loginPassword">Password</label>
<input type="password" id="loginPassword" placeholder="Enter password" required>
</div>
<button type="submit" class="btn btn-primary" style="width:100%;justify-content:center;padding:12px;">
Sign In
</button>
</form>
<p style="text-align:center;margin-top:16px;font-size:13px;color:var(--text-muted);">
No account? <a href="#" id="showRegister" style="color:var(--primary);">Register</a>
</p>
</div>
</div>
<!-- Registration modal on login page -->
<div id="registerOverlay" class="modal-overlay">
<div class="modal">
<h3>Create Account</h3>
<form id="registerForm">
<div class="form-group">
<label for="regName">Name</label>
<input type="text" id="regName" placeholder="Your name" required>
</div>
<div class="form-group">
<label for="regEmail">Email</label>
<input type="email" id="regEmail" placeholder="you@example.com" required>
</div>
<div class="form-group">
<label for="regPassword">Password</label>
<input type="password" id="regPassword" placeholder="Min 6 characters" required minlength="6">
</div>
<div class="modal-actions">
<button type="button" class="btn btn-outline" onclick="closeRegister()">Cancel</button>
<button type="submit" class="btn btn-primary">Register & Sign In</button>
</div>
</form>
</div>
</div>
<!-- ============================================================
APP SHELL (hidden until authenticated)
============================================================ -->
<div id="appContainer" class="app-container">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<div class="logo">🐦</div>
<h2>Shorebird</h2>
</div>
<nav class="sidebar-nav" id="sidebarNav">
<a href="#dashboard" class="active" data-page="dashboard">
<span class="nav-icon">📊</span> Dashboard
</a>
<a href="#apps" data-page="apps">
<span class="nav-icon">📱</span> Apps
</a>
<a href="#users" data-page="users">
<span class="nav-icon">👥</span> Users
</a>
<a href="#settings" data-page="settings">
<span class="nav-icon">⚙️</span> Settings
</a>
</nav>
<div class="sidebar-footer" id="sidebarFooter">
Shorebird v1.0 · Self-Hosted
</div>
</aside>
<!-- Main Content Area -->
<main class="main-content">
<!-- Top Bar -->
<div class="top-bar">
<h2 id="pageTitle">Dashboard</h2>
<div class="user-info">
<span id="currentUserEmail"></span>
<button class="btn-sm" id="logoutBtn" title="Sign out">Sign Out</button>
</div>
</div>
<!-- Toast container -->
<div class="toast-container" id="toastContainer"></div>
<!-- ================================================
DASHBOARD PAGE
================================================ -->
<section id="page-dashboard" class="page-section active">
<div class="stats-grid" id="statsGrid">
<div class="stat-card primary"><div class="stat-value" id="statApps"></div><div class="stat-label">Apps</div></div>
<div class="stat-card success"><div class="stat-value" id="statPatches"></div><div class="stat-label">Patches</div></div>
<div class="stat-card warning"><div class="stat-value" id="statUsers"></div><div class="stat-label">Users</div></div>
<div class="stat-card"><div class="stat-value" id="statOrganizations"></div><div class="stat-label">Organizations</div></div>
</div>
<div class="card">
<div class="card-header"><h3>Recent Apps</h3></div>
<div class="card-body" id="recentAppsTable"></div>
</div>
</section>
<!-- ================================================
APPS PAGE
================================================ -->
<section id="page-apps" class="page-section">
<div class="card">
<div class="card-header">
<h3>All Applications</h3>
<button class="btn btn-primary" onclick="openCreateAppModal()">+ New App</button>
</div>
<div class="card-body" id="appsTable"></div>
</div>
</section>
<!-- ================================================
USERS PAGE
================================================ -->
<section id="page-users" class="page-section">
<div class="card">
<div class="card-header">
<h3>All Users</h3>
<button class="btn btn-primary" onclick="openCreateUserModal()">+ New User</button>
</div>
<div class="card-body" id="usersTable"></div>
</div>
</section>
<!-- ================================================
SETTINGS PAGE
================================================ -->
<section id="page-settings" class="page-section">
<div class="card">
<div class="card-header"><h3>Server Configuration</h3></div>
<div class="card-body" style="padding:24px;">
<div class="info-grid" id="serverInfo"></div>
</div>
</div>
<div class="card" style="margin-top:20px;">
<div class="card-header"><h3>API Token</h3></div>
<div class="card-body" style="padding:24px;">
<p style="font-size:14px;color:var(--text-secondary);margin-bottom:12px;">
Use this token to authenticate the Shorebird CLI:
</p>
<div class="form-group">
<input type="text" id="apiTokenDisplay" readonly
style="font-family:var(--mono);font-size:13px;cursor:pointer;"
onclick="this.select();document.execCommand('copy')"
title="Click to copy">
</div>
<p class="form-hint">Click the token to copy. Set as <code>SHOREBIRD_TOKEN</code> env var.</p>
</div>
</div>
</section>
</main>
</div>
<!-- ============================================================
MODALS
============================================================ -->
<!-- Create App Modal -->
<div id="createAppOverlay" class="modal-overlay">
<div class="modal">
<h3>Create New App</h3>
<form id="createAppForm">
<div class="form-group">
<label for="appDisplayName">Display Name</label>
<input type="text" id="appDisplayName" placeholder="My Flutter App" required>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-outline" onclick="closeModal('createAppOverlay')">Cancel</button>
<button type="submit" class="btn btn-primary">Create App</button>
</div>
</form>
</div>
</div>
<!-- Create User Modal -->
<div id="createUserOverlay" class="modal-overlay">
<div class="modal">
<h3>Create New User</h3>
<form id="createUserForm">
<div class="form-group">
<label for="newUserName">Name</label>
<input type="text" id="newUserName" placeholder="Full name" required>
</div>
<div class="form-group">
<label for="newUserEmail">Email</label>
<input type="email" id="newUserEmail" placeholder="user@example.com" required>
</div>
<div class="form-group">
<label for="newUserPassword">Password</label>
<input type="password" id="newUserPassword" placeholder="Min 6 characters" required minlength="6">
</div>
<div class="modal-actions">
<button type="button" class="btn btn-outline" onclick="closeModal('createUserOverlay')">Cancel</button>
<button type="submit" class="btn btn-primary">Create User</button>
</div>
</form>
</div>
</div>
<!-- Confirm Delete Modal -->
<div id="confirmModal" class="modal-overlay">
<div class="modal">
<h3 id="confirmTitle">Confirm</h3>
<p id="confirmMessage" style="font-size:14px;color:var(--text-secondary);"></p>
<div class="modal-actions">
<button class="btn btn-outline" onclick="closeModal('confirmModal')">Cancel</button>
<button class="btn btn-danger" id="confirmBtn" onclick="executeConfirm()">Delete</button>
</div>
</div>
</div>
<script src="/web/js/app.js"></script>
</body>
</html>