1978 lines
73 KiB
HTML
1978 lines
73 KiB
HTML
|
|
<div class="settings-layout">
|
||
|
|
<!-- Sidebar Navigation -->
|
||
|
|
<aside class="settings-sidebar">
|
||
|
|
<div class="settings-header">
|
||
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<circle cx="12" cy="12" r="3"></circle>
|
||
|
|
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
||
|
|
</svg>
|
||
|
|
<span>Settings</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<nav class="settings-nav">
|
||
|
|
<a href="#profile" class="nav</span>-item active" onclick="showSection('profile', this)">
|
||
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||
|
|
<circle cx="12" cy="7" r="4"></circle>
|
||
|
|
</svg>
|
||
|
|
<span>Profile</span>
|
||
|
|
</a>
|
||
|
|
<a href="#security" class="nav-item" onclick="showSection('security', this)">
|
||
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||
|
|
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||
|
|
</svg>
|
||
|
|
<span>Security</span>
|
||
|
|
</a>
|
||
|
|
<a href="#appearance" class="nav-item" onclick="showSection('appearance', this)">
|
||
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<circle cx="12" cy="12" r="5"></circle>
|
||
|
|
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||
|
|
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||
|
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||
|
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||
|
|
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||
|
|
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||
|
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||
|
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||
|
|
</svg>
|
||
|
|
<span>Appearance</span>
|
||
|
|
</a>
|
||
|
|
<a href="#notifications" class="nav-item" onclick="showSection('notifications', this)">
|
||
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
|
||
|
|
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
|
||
|
|
</svg>
|
||
|
|
<span>Notifications</span>
|
||
|
|
</a>
|
||
|
|
<a href="#storage" class="nav-item" onclick="showSection('storage', this)">
|
||
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse>
|
||
|
|
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path>
|
||
|
|
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>
|
||
|
|
</svg>
|
||
|
|
<span>Storage</span>
|
||
|
|
</a>
|
||
|
|
<a href="#integrations" class="nav-item" onclick="showSection('integrations', this)">
|
||
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<polyline points="16 18 22 12 16 6"></polyline>
|
||
|
|
<polyline points="8 6 2 12 8 18"></polyline>
|
||
|
|
</svg>
|
||
|
|
<span>Integrations</span>
|
||
|
|
</a>
|
||
|
|
<a href="#privacy" class="nav-item" onclick="showSection('privacy', this)">
|
||
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
|
||
|
|
</svg>
|
||
|
|
<span>Privacy</span>
|
||
|
|
</a>
|
||
|
|
<a href="#billing" class="nav-item" onclick="showSection('billing', this)">
|
||
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<rect x="1" y="4" width="22" height="16" rx="2" ry="2"></rect>
|
||
|
|
<line x1="1" y1="10" x2="23" y2="10"></line>
|
||
|
|
</svg>
|
||
|
|
<span>Billing</span>
|
||
|
|
</a>
|
||
|
|
</nav>
|
||
|
|
|
||
|
|
<div class="settings-footer">
|
||
|
|
<a href="/suite" class="back-link">
|
||
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<line x1="19" y1="12" x2="5" y2="12"></line>
|
||
|
|
<polyline points="12 19 5 12 12 5"></polyline>
|
||
|
|
</svg>
|
||
|
|
Back to Suite
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</aside>
|
||
|
|
|
||
|
|
<!-- Main Content Area -->
|
||
|
|
<main class="settings-main">
|
||
|
|
<!-- Profile Section -->
|
||
|
|
<section id="profile-section" class="settings-section active">
|
||
|
|
<div class="section-header">
|
||
|
|
<h1>Profile Settings</h1>
|
||
|
|
<p class="subtitle">Manage your personal information and preferences</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<form class="settings-form"
|
||
|
|
hx-put="/api/user/profile"
|
||
|
|
hx-swap="none"
|
||
|
|
hx-on::after-request="showToast('Profile updated successfully')">
|
||
|
|
<!-- Avatar -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Profile Photo</h2>
|
||
|
|
<p>Your profile photo is visible to other users</p>
|
||
|
|
</div>
|
||
|
|
<div class="avatar-section">
|
||
|
|
<div class="current-avatar" i</p>d="current-avatar">
|
||
|
|
<span>JD</span>
|
||
|
|
</div>
|
||
|
|
<div class="avatar-actions">
|
||
|
|
<label class="btn-secondary upload-btn"></span>
|
||
|
|
<input type="file" name="avatar" accept="image/*" hidden onchange="previewAvatar(this)">
|
||
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||
|
|
<polyline points="17 8 12 3 7 8"></polyline>
|
||
|
|
<line x1="12" y1="3" x2="12" y2="15"></line>
|
||
|
|
</svg>
|
||
|
|
Upload Photo
|
||
|
|
</label>
|
||
|
|
<button type="button" class="btn-text" onclick="removeAvatar()">Remove</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Basic Info -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Basic Information</h2>
|
||
|
|
</div>
|
||
|
|
<div class="form-grid">
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Display Name</label>
|
||
|
|
<input type="text" name="display_name" placeholder="John Doe"
|
||
|
|
hx-get="/api/user/profile"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="outerHTML"
|
||
|
|
hx-select="[name='display_name']">
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Username</label>
|
||
|
|
<input type="text" name="username" placeholder="johndoe">
|
||
|
|
</div>
|
||
|
|
<div class="form-group full-width">
|
||
|
|
<label>Email Address</label>
|
||
|
|
<input type="email" name="email" placeholder="john@example.com">
|
||
|
|
</div>
|
||
|
|
<div class="form-group full-width">
|
||
|
|
<label>Bio</label>
|
||
|
|
<textarea name="bio" rows="3" placeholder="Tell us about yourself..."></textarea>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Contact Info -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Contact Information</h2>
|
||
|
|
</div>
|
||
|
|
<div class="form-grid">
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Phone Number</label>
|
||
|
|
<input type="tel" name="phone" placeholder="+1 (555) 000-0000">
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Location</label>
|
||
|
|
<input type="text" name="location" placeholder="City, Country">
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Website</label>
|
||
|
|
<input type="url" name="website" placeholder="https://example.com">
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Timezone</label>
|
||
|
|
<select name="timezone">
|
||
|
|
<option value="UTC">UTC</option>
|
||
|
|
<option value="America/New_York">Eastern Time</option>
|
||
|
|
<option value="America/Chicago">Central Time</option>
|
||
|
|
<option value="America/Denver">Mountain Time</option>
|
||
|
|
<option value="America/Los_Angeles">Pacific Time</option>
|
||
|
|
<option value="Europe/London">London</option>
|
||
|
|
<option value="Europe/Paris">Paris</option>
|
||
|
|
<option value="Asia/Tokyo">Tokyo</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="form-actions">
|
||
|
|
<button type="submit" class="btn-primary">Save Changes</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- Security Section -->
|
||
|
|
<section id="security-section" class="settings-section">
|
||
|
|
<div class="section-header">
|
||
|
|
<h1>Security Settings</h1>
|
||
|
|
<p class="subtitle">Protect your account with enhanced security</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Password Change -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Change Password</h2>
|
||
|
|
<p>Update your password regularly for better security</p>
|
||
|
|
</div>
|
||
|
|
<form hx-post="/api/user/password"
|
||
|
|
hx-swap="none"
|
||
|
|
hx-on::after-request="this.reset(); showToast('Password changed successfully')">
|
||
|
|
<div class="form-grid">
|
||
|
|
<div class="form-group full-width">
|
||
|
|
<label>Current Password</label>
|
||
|
|
<input type="password" name="current_password" required>
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>New Password</label>
|
||
|
|
<input type="password" name="new_password" required minlength="8">
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Confirm New Password</label>
|
||
|
|
<input type="password" name="confirm_password" required>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="form-actions">
|
||
|
|
<button type="submit" class="btn-primary">Update Password</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Two-Factor Authentication -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Two-Factor Authentication</h2>
|
||
|
|
<p>Add an extra layer of security to your account</p>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row" id="2fa-status"
|
||
|
|
hx-get="/api/user/security/2fa/status"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Authenticator App</span>
|
||
|
|
<span class="setting-desc">Use an authenticator app for 2FA codes</span>
|
||
|
|
</div>
|
||
|
|
<button class="btn-primary"
|
||
|
|
hx-post="/api/user/security/2fa/enable"
|
||
|
|
hx-target="#2fa-modal-content"
|
||
|
|
hx-on::after-request="document.getElementById('2fa-modal').showModal()">
|
||
|
|
Enable 2FA
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Active Sessions -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Active Sessions</h2>
|
||
|
|
<p>Manage your active login sessions</p>
|
||
|
|
</div>
|
||
|
|
<div class="sessions-list" id="sessions-list"
|
||
|
|
hx-get="/api/user/security</p>/sessions"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<div class="loading-state">
|
||
|
|
<div class="spinner"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="card-footer">
|
||
|
|
<button class="btn-danger-outline"
|
||
|
|
hx-post="/api/user/security/sessions/revoke-all"
|
||
|
|
hx-confirm="Sign out of all other devices?"
|
||
|
|
hx-on::after-request="htmx.trigger('#sessions-list', 'load'); showToast('All other sessions revoked')">
|
||
|
|
Sign Out All Other Sessions
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Connected Devices -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Connected Devices</h2>
|
||
|
|
<p>Devices that have accessed your account</p>
|
||
|
|
</div>
|
||
|
|
<div class="devices-list" id="devices-list"
|
||
|
|
hx-get="/api/user/security/devices"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<div class="loading-state">
|
||
|
|
<div class="spinner"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- Appearance Section -->
|
||
|
|
<section id="appearance-section" class="settings-section">
|
||
|
|
<div class="section-header">
|
||
|
|
<h1>Appearance</h1>
|
||
|
|
<p class="subtitle">Customize how the application looks</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Theme Selection -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Theme</h2>
|
||
|
|
<p>Choose your preferred color theme</p>
|
||
|
|
</div>
|
||
|
|
<div class="theme-grid">
|
||
|
|
<button class="theme-option" data-theme="dark" onclick="setTheme('dark', this)">
|
||
|
|
<div class="theme-preview dark">
|
||
|
|
<div class="preview-sidebar"></div>
|
||
|
|
<div class="preview-content">
|
||
|
|
<div class="preview-header"></div>
|
||
|
|
<div class="preview-body"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<span>Dark</span>
|
||
|
|
</button>
|
||
|
|
<button class="theme-option" data-theme="light" onclick="setTheme('light', this)">
|
||
|
|
<div class="theme-preview light">
|
||
|
|
<div class="preview-sidebar"></div>
|
||
|
|
<div class="preview-content">
|
||
|
|
<div class="preview-header"></div>
|
||
|
|
<div class="preview-body"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<span>Light</span>
|
||
|
|
</button>
|
||
|
|
<button class="theme-option" data-theme="blue" onclick="setTheme('blue', this)">
|
||
|
|
<div class="theme-preview blue">
|
||
|
|
<div class="preview-sidebar"></div>
|
||
|
|
<div class="preview-content">
|
||
|
|
<div class="preview-header"></div>
|
||
|
|
<div class="preview-body"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<span>Blue</span>
|
||
|
|
</button>
|
||
|
|
<button class="theme-option" data-theme="purple" onclick="setTheme('purple', this)">
|
||
|
|
<div class="theme-preview purple">
|
||
|
|
<div class="preview-sidebar"></div>
|
||
|
|
<div class="preview-content">
|
||
|
|
<div class="preview-header"></div>
|
||
|
|
<div class="preview-body"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<span>Purple</span>
|
||
|
|
</button>
|
||
|
|
<button class="theme-option" data-theme="green" onclick="setTheme('green', this)">
|
||
|
|
<div class="theme-preview green">
|
||
|
|
<div class="preview-sidebar"></div>
|
||
|
|
<div class="preview-content">
|
||
|
|
<div class="preview-header"></div>
|
||
|
|
<div class="preview-body"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<span>Green</span>
|
||
|
|
</button>
|
||
|
|
<button class="theme-option" data-theme="orange" onclick="setTheme('orange', this)">
|
||
|
|
<div class="theme-preview orange">
|
||
|
|
<div class="preview-sidebar"></div>
|
||
|
|
<div class="preview-content">
|
||
|
|
<div class="preview-header"></div>
|
||
|
|
<div class="preview-body"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<span>Orange</span>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Layout Preferences -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Layout Preferences</h2>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Compact Mode</span>
|
||
|
|
<span class="setting-desc">Reduce spacing for more content</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="compact_mode" onchange="toggleCompactMode(this)">
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Show Sidebar</span>
|
||
|
|
<span class="setting-desc">Always show navigation sidebar</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="show_sidebar" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Animations</span>
|
||
|
|
<span class="setting-desc">Enable UI animations and transitions</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="animations" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- Notifications Section -->
|
||
|
|
<section id="notifications-section" class="settings-section">
|
||
|
|
<div class="section-header">
|
||
|
|
<h1>Notifications</h1>
|
||
|
|
<p class="subtitle">Control how you receive notifications</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<form hx-put="/api/user/notifications/preferences"
|
||
|
|
hx-swap="none"
|
||
|
|
hx-on::after-request="showToast('Notification preferences saved')">
|
||
|
|
|
||
|
|
<!-- Email Notifications -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Email Notifications</h2>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Direct Messages</span>
|
||
|
|
<span class="setting-desc">Receive email for new direct messages</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="email_dm" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Mentions</span>
|
||
|
|
<span class="setting-desc">Receive email when someone mentions you</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="email_mentions" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Weekly Digest</span>
|
||
|
|
<span class="setting-desc">Get a weekly summary of activity</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="email_digest">
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Marketing</span>
|
||
|
|
<span class="setting-desc">Receive news and product updates</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="email_marketing">
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Push Notifications -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Push Notifications</h2>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Enable Push Notifications</span>
|
||
|
|
<span class="setting-desc">Receive browser push notifications</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="push_enabled" onchange="togglePushNotifications(this)">
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Sound</span>
|
||
|
|
<span class="setting-desc">Play sound for notifications</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="push_sound" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- In-App Notifications -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>In-App Notifications</h2>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Desktop Notifications</span>
|
||
|
|
<span class="setting-desc">Show notifications on desktop</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="desktop_notifications" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Badge Count</span>
|
||
|
|
<span class="setting-desc">Show unread count badge</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="badge_count" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="form-actions">
|
||
|
|
<button type="submit" class="btn-primary">Save Preferences</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- Storage Section -->
|
||
|
|
<section id="storage-section" class="settings-section">
|
||
|
|
<div class="section-header">
|
||
|
|
<h1>Storage</h1>
|
||
|
|
<p class="subtitle">Manage your storage and sync settings</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Storage Usage -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Storage Usage</h2>
|
||
|
|
</div>
|
||
|
|
<div class="storage-display"
|
||
|
|
hx-get="/api/user/storage"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<div class="storage-bar-container">
|
||
|
|
<div class="storage-bar">
|
||
|
|
<div class="storage-bar-fill" style="width: 45%"></div>
|
||
|
|
</div>
|
||
|
|
<div class="storage-stats">
|
||
|
|
<span>4.5 GB used of 10 GB</span>
|
||
|
|
<span>5.5 GB available</span>
|
||
|
|
</div>
|
||
|
|
</div></span>
|
||
|
|
<div class="storage-breakdown">
|
||
|
|
<div class="breakdown-item">
|
||
|
|
<div class="breakdown-color" style="background: #3b82f6"></div>
|
||
|
|
<span class="breakdown-label">Documents</span>
|
||
|
|
<span class="breakdown-value">2.1 GB</span>
|
||
|
|
</div>
|
||
|
|
<div class="breakdown-item">
|
||
|
|
<div class="breakdown-color" style="background: #10b981"></div>
|
||
|
|
<span class="breakdown-label">Images</span>
|
||
|
|
<span class="breakdown-value">1.8 GB</span>
|
||
|
|
</div>
|
||
|
|
<div class="breakdown-item">
|
||
|
|
<div class="breakdown-color" style="background: #f59e0b"></div>
|
||
|
|
<span class="breakdown-label">Videos</span>
|
||
|
|
<span class="breakdown-value">0.4 GB</span>
|
||
|
|
</div>
|
||
|
|
<div class="breakdown-item">
|
||
|
|
<div class="breakdown-color" style="background: #6b7280"></div>
|
||
|
|
<span class="breakdown-label">Other</span>
|
||
|
|
<span class="breakdown-value">0.2 GB</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Sync Settings -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Sync Settings</h2>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Auto-Sync</span>
|
||
|
|
<span class="setting-desc">Automatically sync files across devices</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="auto_sync" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Sync on Wi-Fi Only</span>
|
||
|
|
<span class="setting-desc">Only sync when connected to Wi-Fi</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="wifi_only">
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Offline Access</span>
|
||
|
|
<span class="setting-desc">Keep recent files available offline</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="offline_access" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Connected Storage -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Connected Cloud Storage</h2>
|
||
|
|
</div>
|
||
|
|
<div class="connections-list" id="storage-connections"
|
||
|
|
hx-get="/api/user/storage/connections"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<div class="connection-item">
|
||
|
|
<div class="connection-icon google-drive">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||
|
|
<path d="M4.433 22l-1.6-2.773 8.167-14.167L19.4 19.227 17.8 22H4.433z"></path>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div class="connection-info">
|
||
|
|
<span class="connection-name">Google Drive</span>
|
||
|
|
<span class="connection-status">Not connected</span>
|
||
|
|
</div>
|
||
|
|
<button class="btn-secondary btn-sm">Connect</button>
|
||
|
|
</div>
|
||
|
|
<div class="connection-item">
|
||
|
|
<div class="connection-icon dropbox">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||
|
|
<path d="M6 2l6 3.75L6 9.5 0 5.75 6 2zm12 0l6 3.75-6 3.75-6-3.75L18 2zM0 13.25L6 9.5l6 3.75-6 3.75-6-3.75zm18-3.75l6 3.75-6 3.75-6-3.75 6-3.75zM6 18.25l6-3.75 6 3.75-6 3.75-6-3.75z"></path>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div class="connection-info">
|
||
|
|
<span class="connection-name">Dropbox</span>
|
||
|
|
<span class="connection-status">Not connected</span>
|
||
|
|
</div>
|
||
|
|
<button class="btn-secondary btn-sm">Connect</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- Integrations Section -->
|
||
|
|
<section id="integrations-section" class="settings-section">
|
||
|
|
<div class="section-header">
|
||
|
|
<h1>Integrations</h1>
|
||
|
|
<p class="subtitle">Connect external services and manage API access</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- API Keys -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>API Keys</h2>
|
||
|
|
<button class="btn-primary btn-sm" onclick="document.getElementById('create-api-key-modal').showModal()">
|
||
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||
|
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||
|
|
</svg>
|
||
|
|
Create Key
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div class="api-keys-list" id="api-keys-list"
|
||
|
|
hx-get="/api/user/api-keys"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<div class="empty-state">
|
||
|
|
<p>No API keys created yet</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Webhooks -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Webhooks</h2>
|
||
|
|
<button class="btn-primary btn-sm" onclick="document.getElementById('create-webhook-modal').showModal()">
|
||
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||
|
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||
|
|
</svg>
|
||
|
|
Add Webhook
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div class="webhooks-list" id="webhooks-list"
|
||
|
|
hx-get="/api/user/webhooks"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<div class="empty-state">
|
||
|
|
<p>No webhooks configured</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- OAuth Connections -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Connected Accounts</h2>
|
||
|
|
</div>
|
||
|
|
<div class="oauth-list">
|
||
|
|
<div class="oauth-item">
|
||
|
|
<div class="oauth-icon google">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24">
|
||
|
|
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"></path>
|
||
|
|
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"></path>
|
||
|
|
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"></path>
|
||
|
|
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"></path>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div class="oauth-info">
|
||
|
|
<span class="oauth-name">Google</span>
|
||
|
|
<span class="oauth-status">Not connected</span>
|
||
|
|
</div>
|
||
|
|
<button class="btn-secondary btn-sm"
|
||
|
|
hx-post="/api/oauth/google/connect"
|
||
|
|
hx-swap="none">Connect</button>
|
||
|
|
</div>
|
||
|
|
<div class="oauth-item">
|
||
|
|
<div class="oauth-icon microsoft">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24">
|
||
|
|
<path fill="#F25022" d="M1 1h10v10H1z"></path>
|
||
|
|
<path fill="#00A4EF" d="M1 13h10v10H1z"></path>
|
||
|
|
<path fill="#7FBA00" d="M13 1h10v10H13z"></path>
|
||
|
|
<path fill="#FFB900" d="M13 13h10v10H13z"></path>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div class="oauth-info">
|
||
|
|
<span class="oauth-name">Microsoft</span>
|
||
|
|
<span class="oauth-status">Not connected</span>
|
||
|
|
</div>
|
||
|
|
<button class="btn-secondary btn-sm"
|
||
|
|
hx-post="/api/oauth/microsoft/connect"
|
||
|
|
hx-swap="none">Connect</button>
|
||
|
|
</div>
|
||
|
|
<div class="oauth-item">
|
||
|
|
<div class="oauth-icon github">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||
|
|
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"></path>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div class="oauth-info">
|
||
|
|
<span class="oauth-name">GitHub</span>
|
||
|
|
<span class="oauth-status">Not connected</span>
|
||
|
|
</div>
|
||
|
|
<button class="btn-secondary btn-sm"
|
||
|
|
hx-post="/api/oauth/github/connect"
|
||
|
|
hx-swap="none">Connect</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- Privacy Section -->
|
||
|
|
<section id="privacy-section" class="settings-section">
|
||
|
|
<div class="section-header">
|
||
|
|
<h1>Privacy</h1>
|
||
|
|
<p class="subtitle">Manage your data and privacy settings</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Privacy Settings -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Privacy Preferences</h2>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Profile Visibility</span>
|
||
|
|
<span class="setting-desc">Who can see your profile</span>
|
||
|
|
</div>
|
||
|
|
<select name="profile_visibility" class="select-sm">
|
||
|
|
<option value="public">Public</option>
|
||
|
|
<option value="contacts">Contacts Only</option>
|
||
|
|
<option value="private">Private</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Online Status</span>
|
||
|
|
<span class="setting-desc">Show when you're online</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="show_online" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Read Receipts</span>
|
||
|
|
<span class="setting-desc">Let others know when you've read messages</span>
|
||
|
|
</div>
|
||
|
|
<label class="toggle">
|
||
|
|
<input type="checkbox" name="read_receipts" checked>
|
||
|
|
<span class="toggle-slider"></span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Data Export -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Your Data</h2>
|
||
|
|
<p>Download a copy of your data</p>
|
||
|
|
</div>
|
||
|
|
<div class="setting-row">
|
||
|
|
<div class="setting-info">
|
||
|
|
<span class="setting-title">Export Data</span>
|
||
|
|
<span class="setting-desc">Download all your data in a portable format</span>
|
||
|
|
</div>
|
||
|
|
<button class="btn-secondary"
|
||
|
|
hx-post="/api/user/data/export"
|
||
|
|
hx-swap="none"
|
||
|
|
hx-on::after-request="showToast('Export started. You will receive an email when ready.')">
|
||
|
|
Request Export
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Account Deletion -->
|
||
|
|
<div class="setting-card danger-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Delete Account</h2>
|
||
|
|
<p>Permanently delete your account and all associated data</p>
|
||
|
|
</div>
|
||
|
|
<div class="danger-content">
|
||
|
|
<p>Once you delete your account, there is no going back. Please be certain.</p>
|
||
|
|
<button class="btn-danger" onclick="document.getElementById('delete-account-modal').showModal()">
|
||
|
|
Delete My Account
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<!-- Billing Section -->
|
||
|
|
<section id="billing-section" class="settings-section">
|
||
|
|
<div class="section-header">
|
||
|
|
<h1>Billing</h1>
|
||
|
|
<p class="subtitle">Manage your subscription and payment methods</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Current Plan -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Current Plan</h2>
|
||
|
|
</div>
|
||
|
|
<div class="plan-display"
|
||
|
|
hx-get="/api/user/billing/plan"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<div class="plan-info">
|
||
|
|
<div class="plan-badge">Pro</div>
|
||
|
|
<div class="plan-details">
|
||
|
|
<span class="plan-price">$19/month</span>
|
||
|
|
<span class="plan-cycle">Billed monthly</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="plan-actions">
|
||
|
|
<button class="btn-secondary">Change Plan</button>
|
||
|
|
<button class="btn-text">Cancel Subscription</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Payment Method -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Payment Method</h2>
|
||
|
|
<button class="btn-primary btn-sm" onclick="document.getElementById('add-payment-modal').showModal()">
|
||
|
|
Add Method
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div class="payment-methods" id="payment-methods"
|
||
|
|
hx-get="/api/user/billing/payment-methods"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<div class="payment-card">
|
||
|
|
<div class="card-brand visa">
|
||
|
|
<svg width="32" height="20" viewBox="0 0 32 20">
|
||
|
|
<rect width="32" height="20" rx="2" fill="#1A1F71"></rect>
|
||
|
|
<text x="16" y="13" text-anchor="middle" fill="white" font-size="8" font-weight="bold">VISA</text>
|
||
|
|
</svg>
|
||
|
|
</div>
|
||
|
|
<div class="card-details">
|
||
|
|
<span class="card-number">•••• •••• •••• 4242</span>
|
||
|
|
<span class="card-expiry">Expires 12/25</span>
|
||
|
|
</div>
|
||
|
|
<span class="default-badge">Default</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Invoices -->
|
||
|
|
<div class="setting-card">
|
||
|
|
<div class="card-header">
|
||
|
|
<h2>Billing History</h2>
|
||
|
|
</div>
|
||
|
|
<div class="invoices-list" id="invoices-list"
|
||
|
|
hx-get="/api/user/billing/invoices"
|
||
|
|
hx-trigger="load"
|
||
|
|
hx-swap="innerHTML">
|
||
|
|
<table class="invoices-table">
|
||
|
|
<thead>
|
||
|
|
<tr>
|
||
|
|
<th>Date</th>
|
||
|
|
<th>Description</th>
|
||
|
|
<th>Amount</th>
|
||
|
|
<th>Status</th>
|
||
|
|
<th></th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
<tr>
|
||
|
|
<td>Jan 1, 2025</td>
|
||
|
|
<td>Pro Plan - Monthly</td>
|
||
|
|
<td>$19.00</td>
|
||
|
|
<td><span class="status-badge paid">Paid</span></td>
|
||
|
|
<td><a href="#" class="invoice-link">Download</a></td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<td>Dec 1, 2024</td>
|
||
|
|
<td>Pro Plan - Monthly</td>
|
||
|
|
<td>$19.00</td>
|
||
|
|
<td><span class="status-badge paid">Paid</span></td>
|
||
|
|
<td><a href="#" class="invoice-link">Download</a></td>
|
||
|
|
</tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
</main>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- 2FA Modal -->
|
||
|
|
<dialog id="2fa-modal" class="modal">
|
||
|
|
<div class="modal-content">
|
||
|
|
<div class="modal-header">
|
||
|
|
<h2>Enable Two-Factor Authentication</h2>
|
||
|
|
<button type="button" class="close-btn" onclick="document.getElementById('2fa-modal').close()">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div id="2fa-modal-content" class="modal-body">
|
||
|
|
<!-- Content loaded via HTMX -->
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</dialog>
|
||
|
|
|
||
|
|
<!-- Create API Key Modal -->
|
||
|
|
<dialog id="create-api-key-modal" class="modal">
|
||
|
|
<div class="modal-content">
|
||
|
|
<div class="modal-header">
|
||
|
|
<h2>Create API Key</h2>
|
||
|
|
<button type="button" class="close-btn" onclick="document.getElementById('create-api-key-modal').close()">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<form hx-post="/api/user/api-keys"
|
||
|
|
hx-target="#api-keys-list"
|
||
|
|
hx-swap="innerHTML"
|
||
|
|
hx-on::after-request="document.getElementById('create-api-key-modal').close()">
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Key Name</label>
|
||
|
|
<input type="text" name="name" required placeholder="My API Key">
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Permissions</label>
|
||
|
|
<div class="checkbox-group">
|
||
|
|
<label class="checkbox-item">
|
||
|
|
<input type="checkbox" name="permissions[]" value="read" checked>
|
||
|
|
<span>Read</span>
|
||
|
|
</label>
|
||
|
|
<label class="checkbox-item">
|
||
|
|
<input type="checkbox" name="permissions[]" value="write">
|
||
|
|
<span>Write</span>
|
||
|
|
</label>
|
||
|
|
<label class="checkbox-item">
|
||
|
|
<input type="checkbox" name="permissions[]" value="delete">
|
||
|
|
<span>Delete</span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="modal-footer">
|
||
|
|
<button type="button" class="btn-secondary" onclick="document.getElementById('create-api-key-modal').close()">Cancel</button>
|
||
|
|
<button type="submit" class="btn-primary">Create Key</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
</dialog>
|
||
|
|
|
||
|
|
<!-- Add Webhook Modal -->
|
||
|
|
<dialog id="add-webhook-modal" class="modal">
|
||
|
|
<div class="modal-content">
|
||
|
|
<div class="modal-header">
|
||
|
|
<h2>Add Webhook</h2>
|
||
|
|
<button type="button" class="close-btn" onclick="document.getElementById('add-webhook-modal').close()">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<form hx-post="/api/user/webhooks"
|
||
|
|
hx-target="#webhooks-list"
|
||
|
|
hx-swap="innerHTML"
|
||
|
|
hx-on::after-request="document.getElementById('add-webhook-modal').close()">
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Webhook URL</label>
|
||
|
|
<input type="url" name="url" required placeholder="https://example.com/webhook">
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Events</label>
|
||
|
|
<div class="checkbox-group">
|
||
|
|
<label class="checkbox-item">
|
||
|
|
<input type="checkbox" name="events[]" value="message.created" checked>
|
||
|
|
<span>Message Created</span>
|
||
|
|
</label>
|
||
|
|
<label class="checkbox-item">
|
||
|
|
<input type="checkbox" name="events[]" value="file.uploaded">
|
||
|
|
<span>File Uploaded</span>
|
||
|
|
</label>
|
||
|
|
<label class="checkbox-item">
|
||
|
|
<input type="checkbox" name="events[]" value="task.completed">
|
||
|
|
<span>Task Completed</span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="modal-footer">
|
||
|
|
<button type="button" class="btn-secondary" onclick="document.getElementById('add-webhook-modal').close()">Cancel</button>
|
||
|
|
<button type="submit" class="btn-primary">Add Webhook</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
</dialog>
|
||
|
|
|
||
|
|
<!-- Delete Account Modal -->
|
||
|
|
<dialog id="delete-account-modal" class="modal">
|
||
|
|
<div class="modal-content">
|
||
|
|
<div class="modal-header">
|
||
|
|
<h2>Delete Account</h2>
|
||
|
|
<button type="button" class="close-btn" onclick="document.getElementById('delete-account-modal').close()">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div class="modal-body">
|
||
|
|
<div class="danger-alert">
|
||
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
|
||
|
|
<line x1="12" y1="9" x2="12" y2="13"></line>
|
||
|
|
<line x1="12" y1="17" x2="12.01" y2="17"></line>
|
||
|
|
</svg>
|
||
|
|
<p>This action cannot be undone. All your data will be permanently deleted.</p>
|
||
|
|
</div>
|
||
|
|
<form hx-delete="/api/user/account"
|
||
|
|
hx-swap="none"
|
||
|
|
hx-confirm="Are you absolutely sure?">
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Type "DELETE" to confirm</label>
|
||
|
|
<input type="text" name="confirmation" required pattern="DELETE" placeholder="DELETE">
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Enter your password</label>
|
||
|
|
<input type="password" name="password" required>
|
||
|
|
</div>
|
||
|
|
<div class="modal-footer">
|
||
|
|
<button type="button" class="btn-secondary" onclick="document.getElementById('delete-account-modal').close()">Cancel</button>
|
||
|
|
<button type="submit" class="btn-danger">Delete My Account</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</dialog>
|
||
|
|
|
||
|
|
<!-- Add Payment Method Modal -->
|
||
|
|
<dialog id="add-payment-modal" class="modal">
|
||
|
|
<div class="modal-content">
|
||
|
|
<div class="modal-header">
|
||
|
|
<h2>Add Payment Method</h2>
|
||
|
|
<button type="button" class="close-btn" onclick="document.getElementById('add-payment-modal').close()">
|
||
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<form hx-post="/api/user/billing/payment-methods"
|
||
|
|
hx-target="#payment-methods"
|
||
|
|
hx-swap="innerHTML"
|
||
|
|
hx-on::after-request="document.getElementById('add-payment-modal').close()">
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Card Number</label>
|
||
|
|
<input type="text" name="card_number" required placeholder="1234 5678 9012 3456" maxlength="19">
|
||
|
|
</div>
|
||
|
|
<div class="form-row">
|
||
|
|
<div class="form-group">
|
||
|
|
<label>Expiry Date</label>
|
||
|
|
<input type="text" name="expiry" required placeholder="MM/YY" maxlength="5">
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label>CVC</label>
|
||
|
|
<input type="text" name="cvc" required placeholder="123" maxlength="4">
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="form-group">
|
||
|
|
<label class="checkbox-item">
|
||
|
|
<input type="checkbox" name="default" checked>
|
||
|
|
<span>Set as default payment method</span>
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
<div class="modal-footer">
|
||
|
|
<button type="button" class="btn-secondary" onclick="document.getElementById('add-payment-modal').close()">Cancel</button>
|
||
|
|
<button type="submit" class="btn-primary">Add Card</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
</dialog>
|
||
|
|
|
||
|
|
<style>
|
||
|
|
.settings-layout {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: 260px 1fr;
|
||
|
|
min-height: 100%;
|
||
|
|
background: var(--bg);
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-sidebar {
|
||
|
|
background: var(--surface);
|
||
|
|
border-right: 1px solid var(--border);
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
padding: 24px 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-header {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 12px;
|
||
|
|
padding: 0 24px 24px;
|
||
|
|
border-bottom: 1px solid var(--border);
|
||
|
|
margin-bottom: 16px;
|
||
|
|
color: var(--text);
|
||
|
|
font-size: 1.25rem;
|
||
|
|
font-weight: 600;
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-header svg {
|
||
|
|
color: var(--primary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-nav {
|
||
|
|
flex: 1;
|
||
|
|
padding: 0 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-item {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 12px;
|
||
|
|
padding: 12px 16px;
|
||
|
|
color: var(--text-secondary);
|
||
|
|
text-decoration: none;
|
||
|
|
border-radius: 8px;
|
||
|
|
transition: all 0.2s;
|
||
|
|
margin-bottom: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-item:hover {
|
||
|
|
background: var(--hover);
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-item.active {
|
||
|
|
background: var(--primary);
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-item.active svg {
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-footer {
|
||
|
|
padding: 16px 24px;
|
||
|
|
border-top: 1px solid var(--border);
|
||
|
|
}
|
||
|
|
|
||
|
|
.back-link {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 8px;
|
||
|
|
color: var(--text-secondary);
|
||
|
|
text-decoration: none;
|
||
|
|
font-size: 0.875rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.back-link:hover {
|
||
|
|
color: var(--primary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-main {
|
||
|
|
padding: 32px 48px;
|
||
|
|
overflow-y: auto;
|
||
|
|
max-width: 900px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-section {
|
||
|
|
display: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-section.active {
|
||
|
|
display: block;
|
||
|
|
}
|
||
|
|
|
||
|
|
.section-header {
|
||
|
|
margin-bottom: 32px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.section-header h1 {
|
||
|
|
font-size: 1.75rem;
|
||
|
|
font-weight: 600;
|
||
|
|
color: var(--text);
|
||
|
|
margin: 0 0 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.section-header .subtitle {
|
||
|
|
color: var(--text-secondary);
|
||
|
|
margin: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-card {
|
||
|
|
background: var(--surface);
|
||
|
|
border: 1px solid var(--border);
|
||
|
|
border-radius: 12px;
|
||
|
|
padding: 24px;
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-card.danger-card {
|
||
|
|
border-color: var(--danger);
|
||
|
|
}
|
||
|
|
|
||
|
|
.card-header {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: flex-start;
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.card-header h2 {
|
||
|
|
font-size: 1.125rem;
|
||
|
|
font-weight: 600;
|
||
|
|
color: var(--text);
|
||
|
|
margin: 0 0 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.card-header p {
|
||
|
|
color: var(--text-secondary);
|
||
|
|
font-size: 0.875rem;
|
||
|
|
margin: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-row {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: center;
|
||
|
|
padding: 16px 0;
|
||
|
|
border-bottom: 1px solid var(--border);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-row:last-child {
|
||
|
|
border-bottom: none;
|
||
|
|
padding-bottom: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-row:first-child {
|
||
|
|
padding-top: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-info {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-title {
|
||
|
|
font-weight: 500;
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-desc {
|
||
|
|
font-size: 0.875rem;
|
||
|
|
color: var(--text-secondary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.form-group {
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.form-group label {
|
||
|
|
display: block;
|
||
|
|
font-weight: 500;
|
||
|
|
color: var(--text);
|
||
|
|
margin-bottom: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.form-group input,
|
||
|
|
.form-group select,
|
||
|
|
.form-group textarea {
|
||
|
|
width: 100%;
|
||
|
|
padding: 10px 14px;
|
||
|
|
background: var(--bg);
|
||
|
|
border: 1px solid var(--border);
|
||
|
|
border-radius: 8px;
|
||
|
|
color: var(--text);
|
||
|
|
font-size: 0.9375rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.form-group input:focus,
|
||
|
|
.form-group select:focus,
|
||
|
|
.form-group textarea:focus {
|
||
|
|
outline: none;
|
||
|
|
border-color: var(--primary);
|
||
|
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.form-row {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: 1fr 1fr;
|
||
|
|
gap: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-primary {
|
||
|
|
background: var(--primary);
|
||
|
|
color: white;
|
||
|
|
border: none;
|
||
|
|
padding: 10px 20px;
|
||
|
|
border-radius: 8px;
|
||
|
|
font-weight: 500;
|
||
|
|
cursor: pointer;
|
||
|
|
transition: all 0.2s;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-primary:hover {
|
||
|
|
filter: brightness(1.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-secondary {
|
||
|
|
background: var(--surface);
|
||
|
|
color: var(--text);
|
||
|
|
border: 1px solid var(--border);
|
||
|
|
padding: 10px 20px;
|
||
|
|
border-radius: 8px;
|
||
|
|
font-weight: 500;
|
||
|
|
cursor: pointer;
|
||
|
|
transition: all 0.2s;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-secondary:hover {
|
||
|
|
background: var(--hover);
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-danger {
|
||
|
|
background: var(--danger);
|
||
|
|
color: white;
|
||
|
|
border: none;
|
||
|
|
padding: 10px 20px;
|
||
|
|
border-radius: 8px;
|
||
|
|
font-weight: 500;
|
||
|
|
cursor: pointer;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-danger:hover {
|
||
|
|
filter: brightness(1.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-sm {
|
||
|
|
padding: 6px 12px;
|
||
|
|
font-size: 0.875rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-text {
|
||
|
|
background: none;
|
||
|
|
border: none;
|
||
|
|
color: var(--text-secondary);
|
||
|
|
cursor: pointer;
|
||
|
|
padding: 10px 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-text:hover {
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.toggle {
|
||
|
|
position: relative;
|
||
|
|
display: inline-block;
|
||
|
|
width: 48px;
|
||
|
|
height: 26px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.toggle input {
|
||
|
|
opacity: 0;
|
||
|
|
width: 0;
|
||
|
|
height: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.toggle-slider {
|
||
|
|
position: absolute;
|
||
|
|
cursor: pointer;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
right: 0;
|
||
|
|
bottom: 0;
|
||
|
|
background: var(--border);
|
||
|
|
border-radius: 26px;
|
||
|
|
transition: 0.3s;
|
||
|
|
}
|
||
|
|
|
||
|
|
.toggle-slider::before {
|
||
|
|
content: "";
|
||
|
|
position: absolute;
|
||
|
|
height: 20px;
|
||
|
|
width: 20px;
|
||
|
|
left: 3px;
|
||
|
|
bottom: 3px;
|
||
|
|
background: white;
|
||
|
|
border-radius: 50%;
|
||
|
|
transition: 0.3s;
|
||
|
|
}
|
||
|
|
|
||
|
|
.toggle input:checked + .toggle-slider {
|
||
|
|
background: var(--primary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.toggle input:checked + .toggle-slider::before {
|
||
|
|
transform: translateX(22px);
|
||
|
|
}
|
||
|
|
|
||
|
|
.avatar-upload {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 24px;
|
||
|
|
margin-bottom: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.current-avatar {
|
||
|
|
width: 80px;
|
||
|
|
height: 80px;
|
||
|
|
border-radius: 50%;
|
||
|
|
background: var(--primary);
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
font-size: 2rem;
|
||
|
|
color: white;
|
||
|
|
}
|
||
|
|
|
||
|
|
.theme-grid {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(3, 1fr);
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.theme-card {
|
||
|
|
background: var(--bg);
|
||
|
|
border: 2px solid var(--border);
|
||
|
|
border-radius: 12px;
|
||
|
|
padding: 16px;
|
||
|
|
cursor: pointer;
|
||
|
|
transition: all 0.2s;
|
||
|
|
text-align: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.theme-card:hover {
|
||
|
|
border-color: var(--primary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.theme-card.active {
|
||
|
|
border-color: var(--primary);
|
||
|
|
background: rgba(59, 130, 246, 0.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.theme-preview {
|
||
|
|
width: 100%;
|
||
|
|
height: 60px;
|
||
|
|
border-radius: 8px;
|
||
|
|
margin-bottom: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.theme-name {
|
||
|
|
font-weight: 500;
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.storage-bar {
|
||
|
|
height: 8px;
|
||
|
|
background: var(--border);
|
||
|
|
border-radius: 4px;
|
||
|
|
overflow: hidden;
|
||
|
|
margin: 16px 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.storage-bar-fill {
|
||
|
|
height: 100%;
|
||
|
|
background: var(--primary);
|
||
|
|
border-radius: 4px;
|
||
|
|
transition: width 0.3s;
|
||
|
|
}
|
||
|
|
|
||
|
|
.storage-info {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
color: var(--text-secondary);
|
||
|
|
font-size: 0.875rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.breakdown-item {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 12px;
|
||
|
|
padding: 8px 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.breakdown-color {
|
||
|
|
width: 12px;
|
||
|
|
height: 12px;
|
||
|
|
border-radius: 3px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.breakdown-label {
|
||
|
|
flex: 1;
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.breakdown-value {
|
||
|
|
color: var(--text-secondary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-display {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-info {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-badge {
|
||
|
|
background: var(--primary);
|
||
|
|
color: white;
|
||
|
|
padding: 6px 16px;
|
||
|
|
border-radius: 20px;
|
||
|
|
font-weight: 600;
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-details {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-price {
|
||
|
|
font-size: 1.25rem;
|
||
|
|
font-weight: 600;
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-cycle {
|
||
|
|
color: var(--text-secondary);
|
||
|
|
font-size: 0.875rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-actions {
|
||
|
|
display: flex;
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-card {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 16px;
|
||
|
|
padding: 16px;
|
||
|
|
background: var(--bg);
|
||
|
|
border-radius: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.card-details {
|
||
|
|
flex: 1;
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
}
|
||
|
|
|
||
|
|
.card-number {
|
||
|
|
font-weight: 500;
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.card-expiry {
|
||
|
|
font-size: 0.875rem;
|
||
|
|
color: var(--text-secondary);
|
||
|
|
}
|
||
|
|
|
||
|
|
.default-badge {
|
||
|
|
background: var(--success);
|
||
|
|
color: white;
|
||
|
|
padding: 4px 10px;
|
||
|
|
border-radius: 12px;
|
||
|
|
font-size: 0.75rem;
|
||
|
|
font-weight: 500;
|
||
|
|
}
|
||
|
|
|
||
|
|
.invoices-table {
|
||
|
|
width: 100%;
|
||
|
|
border-collapse: collapse;
|
||
|
|
}
|
||
|
|
|
||
|
|
.invoices-table th,
|
||
|
|
.invoices-table td {
|
||
|
|
padding: 12px;
|
||
|
|
text-align: left;
|
||
|
|
border-bottom: 1px solid var(--border);
|
||
|
|
}
|
||
|
|
|
||
|
|
.invoices-table th {
|
||
|
|
color: var(--text-secondary);
|
||
|
|
font-weight: 500;
|
||
|
|
font-size: 0.875rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.invoices-table td {
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.status-badge {
|
||
|
|
padding: 4px 10px;
|
||
|
|
border-radius: 12px;
|
||
|
|
font-size: 0.75rem;
|
||
|
|
font-weight: 500;
|
||
|
|
}
|
||
|
|
|
||
|
|
.status-badge.paid {
|
||
|
|
background: rgba(16, 185, 129, 0.1);
|
||
|
|
color: var(--success);
|
||
|
|
}
|
||
|
|
|
||
|
|
.invoice-link {
|
||
|
|
color: var(--primary);
|
||
|
|
text-decoration: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.invoice-link:hover {
|
||
|
|
text-decoration: underline;
|
||
|
|
}
|
||
|
|
|
||
|
|
.danger-content {
|
||
|
|
text-align: center;
|
||
|
|
padding: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.danger-content p {
|
||
|
|
color: var(--text-secondary);
|
||
|
|
margin-bottom: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.danger-alert {
|
||
|
|
display: flex;
|
||
|
|
align-items: flex-start;
|
||
|
|
gap: 12px;
|
||
|
|
padding: 16px;
|
||
|
|
background: rgba(239, 68, 68, 0.1);
|
||
|
|
border-radius: 8px;
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.danger-alert svg {
|
||
|
|
color: var(--danger);
|
||
|
|
flex-shrink: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.danger-alert p {
|
||
|
|
margin: 0;
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Modal Styles */
|
||
|
|
.modal {
|
||
|
|
background: transparent;
|
||
|
|
border: none;
|
||
|
|
padding: 0;
|
||
|
|
max-width: 500px;
|
||
|
|
width: 90%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.modal::backdrop {
|
||
|
|
background: rgba(0, 0, 0, 0.5);
|
||
|
|
}
|
||
|
|
|
||
|
|
.modal-content {
|
||
|
|
background: var(--surface);
|
||
|
|
border-radius: 16px;
|
||
|
|
border: 1px solid var(--border);
|
||
|
|
overflow: hidden;
|
||
|
|
}
|
||
|
|
|
||
|
|
.modal-header {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: center;
|
||
|
|
padding: 20px 24px;
|
||
|
|
border-bottom: 1px solid var(--border);
|
||
|
|
}
|
||
|
|
|
||
|
|
.modal-header h2 {
|
||
|
|
margin: 0;
|
||
|
|
font-size: 1.25rem;
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.close-btn {
|
||
|
|
background: none;
|
||
|
|
border: none;
|
||
|
|
color: var(--text-secondary);
|
||
|
|
cursor: pointer;
|
||
|
|
padding: 4px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.close-btn:hover {
|
||
|
|
color: var(--text);
|
||
|
|
}
|
||
|
|
|
||
|
|
.modal-body {
|
||
|
|
padding: 24px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.modal-footer {
|
||
|
|
display: flex;
|
||
|
|
justify-content: flex-end;
|
||
|
|
gap: 12px;
|
||
|
|
padding-top: 16px;
|
||
|
|
border-top: 1px solid var(--border);
|
||
|
|
margin-top: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.checkbox-group {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 8px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.checkbox-item {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 8px;
|
||
|
|
cursor: pointer;
|
||
|
|
}
|
||
|
|
|
||
|
|
.checkbox-item input[type="checkbox"] {
|
||
|
|
width: 18px;
|
||
|
|
height: 18px;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Mobile Toggle Button */
|
||
|
|
.mobile-menu-toggle {
|
||
|
|
display: none;
|
||
|
|
position: fixed;
|
||
|
|
bottom: 24px;
|
||
|
|
right: 24px;
|
||
|
|
width: 56px;
|
||
|
|
height: 56px;
|
||
|
|
border-radius: 50%;
|
||
|
|
background: var(--primary);
|
||
|
|
color: white;
|
||
|
|
border: none;
|
||
|
|
cursor: pointer;
|
||
|
|
z-index: 100;
|
||
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Responsive */
|
||
|
|
@media (max-width: 1024px) {
|
||
|
|
.settings-main {
|
||
|
|
padding: 24px 32px;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (max-width: 768px) {
|
||
|
|
.settings-layout {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-sidebar {
|
||
|
|
position: fixed;
|
||
|
|
left: -280px;
|
||
|
|
top: 0;
|
||
|
|
bottom: 0;
|
||
|
|
width: 280px;
|
||
|
|
z-index: 200;
|
||
|
|
transition: left 0.3s ease;
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-sidebar.open {
|
||
|
|
left: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.sidebar-overlay {
|
||
|
|
display: none;
|
||
|
|
position: fixed;
|
||
|
|
inset: 0;
|
||
|
|
background: rgba(0, 0, 0, 0.5);
|
||
|
|
z-index: 199;
|
||
|
|
}
|
||
|
|
|
||
|
|
.sidebar-overlay.show {
|
||
|
|
display: block;
|
||
|
|
}
|
||
|
|
|
||
|
|
.mobile-menu-toggle {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.settings-main {
|
||
|
|
padding: 20px 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.section-header h1 {
|
||
|
|
font-size: 1.5rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.form-row {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-row {
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: flex-start;
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-display {
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: flex-start;
|
||
|
|
gap: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-actions {
|
||
|
|
width: 100%;
|
||
|
|
flex-direction: column;
|
||
|
|
}
|
||
|
|
|
||
|
|
.plan-actions button {
|
||
|
|
width: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.theme-grid {
|
||
|
|
grid-template-columns: repeat(2, 1fr);
|
||
|
|
}
|
||
|
|
|
||
|
|
.card-header {
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 12px;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (max-width: 480px) {
|
||
|
|
.avatar-upload {
|
||
|
|
flex-direction: column;
|
||
|
|
text-align: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.theme-grid {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
|
||
|
|
.invoices-table {
|
||
|
|
font-size: 0.875rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.invoices-table th:nth-child(3),
|
||
|
|
.invoices-table td:nth-child(3) {
|
||
|
|
display: none;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
|
||
|
|
<!-- Sidebar overlay for mobile -->
|
||
|
|
<div class="sidebar-overlay" id="sidebar-overlay" onclick="toggleSettingsSidebar()"></div>
|
||
|
|
|
||
|
|
<!-- Mobile menu toggle -->
|
||
|
|
<button class="mobile-menu-toggle" id="mobile-menu-toggle" onclick="toggleSettingsSidebar()" aria-label="Toggle settings menu">
|
||
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
|
|
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||
|
|
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||
|
|
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||
|
|
</svg>
|
||
|
|
</button>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
function showSection(sectionId, element) {
|
||
|
|
// Hide all sections
|
||
|
|
document.querySelectorAll('.settings-section').forEach(section => {
|
||
|
|
section.classList.remove('active');
|
||
|
|
});
|
||
|
|
|
||
|
|
// Show selected section
|
||
|
|
document.getElementById(sectionId + '-section').classList.add('active');
|
||
|
|
|
||
|
|
// Update nav items
|
||
|
|
document.querySelectorAll('.nav-item').forEach(item => {
|
||
|
|
item.classList.remove('active');
|
||
|
|
});
|
||
|
|
element.classList.add('active');
|
||
|
|
|
||
|
|
// Close mobile sidebar
|
||
|
|
if (window.innerWidth <= 768) {
|
||
|
|
toggleSettingsSidebar();
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
function toggleSettingsSidebar() {
|
||
|
|
const sidebar = document.querySelector('.settings-sidebar');
|
||
|
|
const overlay = document.getElementById('sidebar-overlay');
|
||
|
|
sidebar.classList.toggle('open');
|
||
|
|
overlay.classList.toggle('show');
|
||
|
|
}
|
||
|
|
|
||
|
|
function showToast(message, type = 'success') {
|
||
|
|
if (window.showNotification) {
|
||
|
|
window.showNotification(message, type);
|
||
|
|
} else {
|
||
|
|
alert(message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle hash navigation on load
|
||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
||
|
|
const hash = window.location.hash.substring(1);
|
||
|
|
if (hash) {
|
||
|
|
const navItem = document.querySelector(`a[href="#${hash}"]`);
|
||
|
|
if (navItem) {
|
||
|
|
showSection(hash, navItem);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Theme selection
|
||
|
|
document.querySelectorAll('.theme-card').forEach(card => {
|
||
|
|
card.addEventListener('click', function() {
|
||
|
|
const theme = this.dataset.theme;
|
||
|
|
document.body.setAttribute('data-theme', theme);
|
||
|
|
localStorage.setItem('gb-theme', theme);
|
||
|
|
|
||
|
|
document.querySelectorAll('.theme-card').forEach(c => c.classList.remove('active'));
|
||
|
|
this.classList.add('active');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
</script>
|