Feature user and admin panels (#30)

This commit is contained in:
2026-02-16 20:30:36 +00:00
parent c5866ae785
commit 2fc4f6980c
15 changed files with 1805 additions and 111 deletions

View File

@@ -4,9 +4,10 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat Switchboard</title>
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/styles.css?v=0.3.4">
</head>
<body>
<div id="appContainer" class="app-hidden" style="display:none">
<header class="header">
<h1>🔀 Chat Switchboard</h1>
<div class="header-actions">
@@ -29,6 +30,9 @@
<button class="dropdown-item" onclick="exportChat('text')">📝 Plain Text</button>
</div>
</div>
<button class="btn btn-secondary admin-only" id="adminBtn" title="Admin Panel" style="display:none">
🛡️ Admin
</button>
<button class="btn btn-secondary" id="settingsBtn" title="Settings (Ctrl+,)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"></circle>
@@ -113,6 +117,36 @@
<button class="modal-close" id="closeModalBtn"></button>
</div>
<div class="modal-body">
<!-- Profile (managed mode only) -->
<div class="settings-section" id="profileSection" style="display:none">
<h3 class="settings-section-title">Profile</h3>
<div class="form-group">
<label for="profileDisplayName">Display Name</label>
<input type="text" id="profileDisplayName" placeholder="How you want to be called">
</div>
<div class="form-group">
<label for="profileEmail">Email</label>
<input type="email" id="profileEmail" placeholder="you@example.com">
</div>
<div class="profile-password-row">
<button class="btn btn-secondary btn-small" id="profileChangePwBtn">🔑 Change Password</button>
</div>
<div id="profileChangePwForm" style="display:none">
<div class="form-group">
<label for="profileCurrentPw">Current Password</label>
<input type="password" id="profileCurrentPw" placeholder="••••••••">
</div>
<div class="form-group">
<label for="profileNewPw">New Password</label>
<input type="password" id="profileNewPw" placeholder="At least 8 characters">
</div>
<button class="btn btn-primary btn-small" id="profileSavePwBtn">Update Password</button>
</div>
<hr class="settings-divider">
</div>
<!-- API Settings (unmanaged / offline only) -->
<div id="unmanagedSettings">
<div class="form-group">
<label for="apiEndpoint">API Endpoint</label>
<input type="text" id="apiEndpoint" placeholder="https://api.openai.com/v1">
@@ -124,6 +158,7 @@
<input type="password" id="apiKey" placeholder="sk-...">
<small>Your API key for authentication</small>
</div>
</div> <!-- /unmanagedSettings -->
<div class="form-group">
<label for="model">Default Model</label>
@@ -198,14 +233,97 @@
</div>
</div>
<!-- Auth Modal -->
<div class="modal-overlay" id="authModal">
<div class="modal auth-modal">
<!-- Admin Panel Modal -->
<div class="modal-overlay" id="adminModal">
<div class="modal admin-modal">
<div class="modal-header">
<h2>🔀 Chat Switchboard</h2>
<button class="modal-close" id="authCloseBtn"></button>
<h2>🛡️ Admin Panel</h2>
<button class="modal-close" id="adminCloseBtn"></button>
</div>
<div class="modal-body">
<div class="admin-tabs">
<button class="admin-tab active" data-tab="users">Users</button>
<button class="admin-tab" data-tab="settings">Settings</button>
<button class="admin-tab" data-tab="stats">Stats</button>
</div>
<!-- Users Tab -->
<div class="admin-tab-content active" id="adminUsersTab">
<div class="admin-add-user-toggle">
<button class="btn btn-primary btn-small" id="adminShowAddUser">+ Add User</button>
</div>
<div class="admin-add-user-form" id="adminAddUserForm" style="display:none">
<div class="admin-form-row">
<input type="text" id="adminNewUsername" placeholder="Username" class="admin-input">
<input type="email" id="adminNewEmail" placeholder="Email" class="admin-input">
</div>
<div class="admin-form-row">
<input type="password" id="adminNewPassword" placeholder="Password (min 8)" class="admin-input">
<select id="adminNewRole" class="admin-role-select">
<option value="user">user</option>
<option value="admin">admin</option>
<option value="moderator">moderator</option>
</select>
</div>
<div class="admin-form-row">
<button class="btn btn-primary btn-small" id="adminCreateUserBtn">Create</button>
<button class="btn btn-secondary btn-small" id="adminCancelAddUser">Cancel</button>
</div>
</div>
<div id="adminUserList" class="admin-user-list">Loading...</div>
</div>
<!-- Reset Password Dialog (inline) -->
<div class="admin-reset-dialog" id="adminResetDialog" style="display:none">
<div class="admin-reset-card">
<h3>Reset Password</h3>
<p id="adminResetTarget"></p>
<input type="password" id="adminResetNewPw" placeholder="New password (min 8)" class="admin-input">
<div class="admin-form-row">
<button class="btn btn-primary btn-small" id="adminResetConfirmBtn">Reset</button>
<button class="btn btn-secondary btn-small" id="adminResetCancelBtn">Cancel</button>
</div>
</div>
</div>
<!-- Settings Tab -->
<div class="admin-tab-content" id="adminSettingsTab" style="display:none">
<div class="form-group">
<label class="toggle-label">
<input type="checkbox" id="adminRegToggle" checked>
<span>Allow new user registration</span>
</label>
</div>
<div class="form-group">
<label for="adminSiteName">Site Name</label>
<input type="text" id="adminSiteName" value="Chat Switchboard">
</div>
<div class="form-group">
<label for="adminTagline">Tagline</label>
<input type="text" id="adminTagline" value="Multi-Model AI Chat">
</div>
<button class="btn btn-primary" id="adminSaveSettings">💾 Save</button>
</div>
<!-- Stats Tab -->
<div class="admin-tab-content" id="adminStatsTab" style="display:none">
<div id="adminStats" class="admin-stats">Loading...</div>
</div>
</div>
</div>
</div>
</div> <!-- /appContainer -->
<!-- ── Auth Splash Gate ────────────────── -->
<div class="splash-gate" id="splashGate" style="display:none">
<div class="splash-card">
<div class="splash-brand">
<div class="splash-logo">🔀</div>
<h1>Chat Switchboard</h1>
<p class="splash-tagline">Multi-Model AI Chat</p>
</div>
<div class="splash-form">
<div class="auth-tabs">
<button class="auth-tab active" id="authTabLogin">Sign In</button>
<button class="auth-tab" id="authTabRegister">Register</button>
@@ -235,22 +353,24 @@
</div>
</div>
<div class="auth-error" id="authError"></div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" id="authSkipBtn">Use Offline</button>
<button class="btn btn-primary" id="authLoginBtn">Sign In</button>
<button class="btn btn-primary" id="authRegisterBtn" style="display:none;">Create Account</button>
<div class="splash-actions">
<button class="btn btn-primary btn-full" id="authLoginBtn">Sign In</button>
<button class="btn btn-primary btn-full" id="authRegisterBtn" style="display:none;">Create Account</button>
<div class="splash-divider"><span>or</span></div>
<button class="btn btn-secondary btn-full" id="authSkipBtn">Use Offline</button>
</div>
</div>
</div>
</div>
<div class="toast-container" id="toastContainer"></div>
<script src="js/storage.js"></script>
<script src="js/backend.js"></script>
<script src="js/state.js"></script>
<script src="js/api.js"></script>
<script src="js/ui.js"></script>
<script src="js/app.js"></script>
<script src="js/storage.js?v=0.3.4"></script>
<script src="js/backend.js?v=0.3.4"></script>
<script src="js/state.js?v=0.3.4"></script>
<script src="js/api.js?v=0.3.4"></script>
<script src="js/ui.js?v=0.3.4"></script>
<script src="js/admin.js?v=0.3.4"></script>
<script src="js/app.js?v=0.3.4"></script>
</body>
</html>