interface Implements multi-user authentication system with email account management, profile settings, drive configuration, and security controls. Includes database migrations for user accounts, email credentials, preferences, and session management. Frontend provides intuitive UI for adding IMAP/SMTP accounts with provider presets and connection testing. Backend supports per-user vector databases for email and file indexing with Zitadel SSO integration and automatic workspace initialization. ```
1073 lines
23 KiB
HTML
1073 lines
23 KiB
HTML
<div class="account-layout" x-data="accountApp()" x-cloak>
|
||
<!-- Header -->
|
||
<div class="account-header">
|
||
<h1>Account Settings</h1>
|
||
<p class="subtitle">Manage your email accounts, preferences, and profile</h1></p>
|
||
</div>
|
||
|
||
<!-- Navigation Tabs -->
|
||
<div class="tabs">
|
||
<button
|
||
class="tab"
|
||
:class="{ active: currentTab === 'profile' }"
|
||
@click="currentTab = 'profile'">
|
||
👤 Profile
|
||
</button>
|
||
<button
|
||
class="tab"
|
||
:class="{ active: currentTab === 'email' }"
|
||
@click="currentTab = 'email'">
|
||
📧 Email Accounts
|
||
</button>
|
||
<button
|
||
class="tab"
|
||
:class="{ active: currentTab === 'drive' }"
|
||
@click="currentTab = 'drive'">
|
||
💾 Drive Settings
|
||
</button>
|
||
<button
|
||
class="tab"
|
||
:class="{ active: currentTab === 'security' }"
|
||
@click="currentTab = 'security'">
|
||
🔒 Security
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Content Panels -->
|
||
<div class="tab-content">
|
||
|
||
<!-- Profile Tab -->
|
||
<template x-if="currentTab === 'profile'">
|
||
<div class="panel-content">
|
||
<h2>Profile Information</h2>
|
||
<form @submit.prevent="saveProfile" class="form">
|
||
<div class="form-group">
|
||
<label for="username">Username</label>
|
||
<input
|
||
type="text"
|
||
id="username"
|
||
x-model="profile.username"
|
||
placeholder="Enter username"
|
||
disabled>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="email">Email Address</label>
|
||
<input
|
||
type="email"
|
||
id="email"
|
||
x-model="profile.email"
|
||
placeholder="Enter email"
|
||
disabled>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="displayName">Display Name</label>
|
||
<input
|
||
type="text"
|
||
id="displayName"
|
||
x-model="profile.displayName"
|
||
placeholder="Enter display name">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="phone">Phone Number</label>
|
||
<input
|
||
type="tel"
|
||
id="phone"
|
||
x-model="profile.phone"
|
||
placeholder="Enter phone number">
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary" :disabled="saving">
|
||
<span x-show="!saving">💾 Save Changes</span>
|
||
<span x-show="saving">⏳ Saving...</span>
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- Email Accounts Tab -->
|
||
<template x-if="currentTab === 'email'">
|
||
<div class="panel-content">
|
||
<div class="section-header">
|
||
<h2>Email Accounts</h2>
|
||
<button class="btn btn-primary" @click="showAddAccount = true">
|
||
➕ Add Account
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Email Accounts List -->
|
||
<div class="accounts-list">
|
||
<template x-if="emailAccounts.length === 0">
|
||
<div class="empty-state">
|
||
<div class="empty-icon">📧</div>
|
||
<h3>No email accounts</h3>
|
||
<p>Add your first email account to start using the mail client</p>
|
||
<button class="btn btn-primary" @click="showAddAccount = true">
|
||
➕ Add Email Account
|
||
</button>
|
||
</div>
|
||
</template>
|
||
|
||
<template x-for="account in emailAccounts" :key="account.id">
|
||
<div class="account-card">
|
||
<div class="account-info">
|
||
<div class="account-icon">📧</div>
|
||
<div class="account-details">
|
||
<h3 x-text="account.display_name || account.email"></h3>
|
||
<p class="account-email" x-text="account.email"></p>
|
||
<div class="account-meta">
|
||
<span x-text="`${account.imap_server}:${account.imap_port}`"></span>
|
||
<span x-show="account.is_primary" class="badge badge-primary">Primary</span>
|
||
<span x-show="account.is_active" class="badge badge-success">Active</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="account-actions">
|
||
<button
|
||
class="btn btn-sm btn-secondary"
|
||
@click="testAccount(account)"
|
||
:disabled="testingAccount === account.id">
|
||
<span x-show="testingAccount !== account.id">🔌 Test</span>
|
||
<span x-show="testingAccount === account.id">⏳</span>
|
||
</button>
|
||
<button
|
||
class="btn btn-sm btn-secondary"
|
||
@click="editAccount(account)">
|
||
✏️ Edit
|
||
</button>
|
||
<button
|
||
class="btn btn-sm btn-danger"
|
||
@click="deleteAccount(account.id)">
|
||
🗑️ Delete
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- Drive Settings Tab -->
|
||
<template x-if="currentTab === 'drive'">
|
||
<div class="panel-content">
|
||
<h2>Drive Storage Settings</h2>
|
||
<div class="storage-info">
|
||
<div class="storage-usage">
|
||
<h3>Storage Usage</h3>
|
||
<div class="progress-bar">
|
||
<div
|
||
class="progress-fill"
|
||
:style="`width: ${storageUsagePercent}%`">
|
||
</div>
|
||
</div>
|
||
<p x-text="`${storageUsed} of ${storageTotal} used`"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<form @submit.prevent="saveDriveSettings" class="form">
|
||
<div class="form-group">
|
||
<label for="driveServer">Drive Server</label>
|
||
<input
|
||
type="text"
|
||
id="driveServer"
|
||
x-model="driveSettings.server"
|
||
placeholder="e.g., drive.example.com"
|
||
readonly>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>
|
||
<input type="checkbox" x-model="driveSettings.autoSync">
|
||
Enable automatic file synchronization
|
||
</label>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>
|
||
<input type="checkbox" x-model="driveSettings.offlineMode">
|
||
Enable offline mode
|
||
</label>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary">
|
||
💾 Save Settings
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- Security Tab -->
|
||
<template x-if="currentTab === 'security'">
|
||
<div class="panel-content">
|
||
<h2>Security Settings</h2>
|
||
|
||
<div class="security-section">
|
||
<h3>Change Password</h3>
|
||
<form @submit.prevent="changePassword" class="form">
|
||
<div class="form-group">
|
||
<label for="currentPassword">Current Password</label>
|
||
<input
|
||
type="password"
|
||
id="currentPassword"
|
||
x-model="security.currentPassword"
|
||
placeholder="Enter current password">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="newPassword">New Password</label>
|
||
<input
|
||
type="password"
|
||
id="newPassword"
|
||
x-model="security.newPassword"
|
||
placeholder="Enter new password">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="confirmPassword">Confirm New Password</label>
|
||
<input
|
||
type="password"
|
||
id="confirmPassword"
|
||
x-model="security.confirmPassword"
|
||
placeholder="Confirm new password">
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary">
|
||
🔒 Change Password
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="security-section">
|
||
<h3>Active Sessions</h3>
|
||
<div class="sessions-list">
|
||
<template x-for="session in activeSessions" :key="session.id">
|
||
<div class="session-card">
|
||
<div class="session-info">
|
||
<h4 x-text="session.device"></h4>
|
||
<p x-text="`Last active: ${session.lastActive}`"></p>
|
||
<p x-text="`IP: ${session.ip}`"></p>
|
||
</div>
|
||
<button
|
||
class="btn btn-sm btn-danger"
|
||
@click="revokeSession(session.id)">
|
||
❌ Revoke
|
||
</button>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
</div>
|
||
|
||
<!-- Add Email Account Modal -->
|
||
<template x-if="showAddAccount">
|
||
<div class="modal-overlay" @click.self="showAddAccount = false">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h2>Add Email Account</h2>
|
||
<button class="close-btn" @click="showAddAccount = false">✕</button>
|
||
</div>
|
||
|
||
<div class="modal-body">
|
||
<form @submit.prevent="addEmailAccount" class="form">
|
||
<div class="form-group">
|
||
<label for="newEmail">Email Address *</label>
|
||
<input
|
||
type="email"
|
||
id="newEmail"
|
||
x-model="newAccount.email"
|
||
placeholder="user@example.com"
|
||
required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="newDisplayName">Display Name</label>
|
||
<input
|
||
type="text"
|
||
id="newDisplayName"
|
||
x-model="newAccount.displayName"
|
||
placeholder="Your Name">
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="newImapServer">IMAP Server *</label>
|
||
<input
|
||
type="text"
|
||
id="newImapServer"
|
||
x-model="newAccount.imapServer"
|
||
placeholder="imap.gmail.com"
|
||
required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="newImapPort">IMAP Port *</label>
|
||
<input
|
||
type="number"
|
||
id="newImapPort"
|
||
x-model="newAccount.imapPort"
|
||
placeholder="993"
|
||
required>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="newSmtpServer">SMTP Server *</label>
|
||
<input
|
||
type="text"
|
||
id="newSmtpServer"
|
||
x-model="newAccount.smtpServer"
|
||
placeholder="smtp.gmail.com"
|
||
required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="newSmtpPort">SMTP Port *</label>
|
||
<input
|
||
type="number"
|
||
id="newSmtpPort"
|
||
x-model="newAccount.smtpPort"
|
||
placeholder="587"
|
||
required>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="newUsername">Username *</label>
|
||
<input
|
||
type="text"
|
||
id="newUsername"
|
||
x-model="newAccount.username"
|
||
placeholder="username or email"
|
||
required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="newPassword">Password *</label>
|
||
<input
|
||
type="password"
|
||
id="newPassword"
|
||
x-model="newAccount.password"
|
||
placeholder="Enter password"
|
||
required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>
|
||
<input type="checkbox" x-model="newAccount.isPrimary">
|
||
Set as primary email account
|
||
</label>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="button" class="btn btn-secondary" @click="showAddAccount = false">
|
||
Cancel
|
||
</button>
|
||
<button type="submit" class="btn btn-primary" :disabled="addingAccount">
|
||
<span x-show="!addingAccount">➕ Add Account</span>
|
||
<span x-show="addingAccount">⏳ Adding...</span>
|
||
</button>
|
||
</div>
|
||
</form>
|
||
|
||
<div class="help-text">
|
||
<h4>Common IMAP/SMTP Settings:</h4>
|
||
<ul>
|
||
<li><strong>Gmail:</strong> imap.gmail.com:993, smtp.gmail.com:587</li>
|
||
<li><strong>Outlook:</strong> outlook.office365.com:993, smtp.office365.com:587</li>
|
||
<li><strong>Yahoo:</strong> imap.mail.yahoo.com:993, smtp.mail.yahoo.com:587</li>
|
||
</ul>
|
||
<p><strong>Note:</strong> You may need to enable "Less secure app access" or use app-specific passwords for some providers.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
|
||
<style>
|
||
.account-layout {
|
||
padding: 2rem;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.account-header {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.account-header h1 {
|
||
font-size: 2rem;
|
||
font-weight: 600;
|
||
margin: 0 0 0.5rem 0;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .account-header h1 {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.subtitle {
|
||
color: #5f6368;
|
||
margin: 0;
|
||
}
|
||
|
||
[data-theme="dark"] .subtitle {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
/* Tabs */
|
||
.tabs {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
margin-bottom: 2rem;
|
||
border-bottom: 2px solid #e0e0e0;
|
||
}
|
||
|
||
[data-theme="dark"] .tabs {
|
||
border-bottom-color: #3c4043;
|
||
}
|
||
|
||
.tab {
|
||
padding: 1rem 1.5rem;
|
||
background: none;
|
||
border: none;
|
||
border-bottom: 3px solid transparent;
|
||
color: #5f6368;
|
||
font-size: 0.95rem;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
margin-bottom: -2px;
|
||
}
|
||
|
||
.tab:hover {
|
||
color: #1a73e8;
|
||
background: rgba(26, 115, 232, 0.04);
|
||
}
|
||
|
||
.tab.active {
|
||
color: #1a73e8;
|
||
border-bottom-color: #1a73e8;
|
||
font-weight: 500;
|
||
}
|
||
|
||
[data-theme="dark"] .tab {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
[data-theme="dark"] .tab:hover {
|
||
color: #8ab4f8;
|
||
background: rgba(138, 180, 248, 0.08);
|
||
}
|
||
|
||
[data-theme="dark"] .tab.active {
|
||
color: #8ab4f8;
|
||
border-bottom-color: #8ab4f8;
|
||
}
|
||
|
||
/* Panel Content */
|
||
.panel-content {
|
||
background: #ffffff;
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 12px;
|
||
padding: 2rem;
|
||
}
|
||
|
||
[data-theme="dark"] .panel-content {
|
||
background: #202124;
|
||
border-color: #3c4043;
|
||
}
|
||
|
||
.panel-content h2 {
|
||
font-size: 1.5rem;
|
||
font-weight: 500;
|
||
margin: 0 0 1.5rem 0;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .panel-content h2 {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
/* Forms */
|
||
.form {
|
||
max-width: 600px;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 0.5rem;
|
||
font-weight: 500;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .form-group label {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.form-group input[type="text"],
|
||
.form-group input[type="email"],
|
||
.form-group input[type="tel"],
|
||
.form-group input[type="password"],
|
||
.form-group input[type="number"] {
|
||
width: 100%;
|
||
padding: 0.75rem;
|
||
border: 1px solid #dadce0;
|
||
border-radius: 8px;
|
||
font-size: 0.95rem;
|
||
background: #ffffff;
|
||
color: #202124;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
[data-theme="dark"] .form-group input[type="text"],
|
||
[data-theme="dark"] .form-group input[type="email"],
|
||
[data-theme="dark"] .form-group input[type="tel"],
|
||
[data-theme="dark"] .form-group input[type="password"],
|
||
[data-theme="dark"] .form-group input[type="number"] {
|
||
background: #292a2d;
|
||
border-color: #5f6368;
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.form-group input:focus {
|
||
outline: none;
|
||
border-color: #1a73e8;
|
||
box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.1);
|
||
}
|
||
|
||
[data-theme="dark"] .form-group input:focus {
|
||
border-color: #8ab4f8;
|
||
box-shadow: 0 0 0 3px rgba(138, 180, 248, 0.1);
|
||
}
|
||
|
||
.form-group input:disabled,
|
||
.form-group input[readonly] {
|
||
background: #f8f9fa;
|
||
color: #5f6368;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
[data-theme="dark"] .form-group input:disabled,
|
||
[data-theme="dark"] .form-group input[readonly] {
|
||
background: #1a1a1a;
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
.form-row {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.form-actions {
|
||
margin-top: 2rem;
|
||
display: flex;
|
||
gap: 1rem;
|
||
}
|
||
|
||
/* Buttons */
|
||
.btn {
|
||
padding: 0.75rem 1.5rem;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 0.95rem;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #1a73e8;
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover:not(:disabled) {
|
||
background: #1557b0;
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: #f1f3f4;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .btn-secondary {
|
||
background: #3c4043;
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.btn-secondary:hover:not(:disabled) {
|
||
background: #e8eaed;
|
||
}
|
||
|
||
[data-theme="dark"] .btn-secondary:hover:not(:disabled) {
|
||
background: #5f6368;
|
||
}
|
||
|
||
.btn-danger {
|
||
background: #d93025;
|
||
color: white;
|
||
}
|
||
|
||
.btn-danger:hover:not(:disabled) {
|
||
background: #b31412;
|
||
}
|
||
|
||
.btn-sm {
|
||
padding: 0.5rem 1rem;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
/* Section Header */
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.section-header h2 {
|
||
margin: 0;
|
||
}
|
||
|
||
/* Accounts List */
|
||
.accounts-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.account-card {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 1.5rem;
|
||
background: #f8f9fa;
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 12px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
[data-theme="dark"] .account-card {
|
||
background: #292a2d;
|
||
border-color: #3c4043;
|
||
}
|
||
|
||
.account-card:hover {
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.account-info {
|
||
display: flex;
|
||
gap: 1rem;
|
||
flex: 1;
|
||
}
|
||
|
||
.account-icon {
|
||
font-size: 2rem;
|
||
width: 3rem;
|
||
height: 3rem;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #e8f0fe;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
[data-theme="dark"] .account-icon {
|
||
background: #1e3a5f;
|
||
}
|
||
|
||
.account-details h3 {
|
||
margin: 0 0 0.25rem 0;
|
||
font-size: 1.1rem;
|
||
font-weight: 500;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .account-details h3 {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.account-email {
|
||
margin: 0 0 0.5rem 0;
|
||
color: #5f6368;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
[data-theme="dark"] .account-email {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
.account-meta {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
font-size: 0.85rem;
|
||
color: #5f6368;
|
||
}
|
||
|
||
[data-theme="dark"] .account-meta {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
.account-actions {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
/* Badges */
|
||
.badge {
|
||
padding: 0.25rem 0.75rem;
|
||
border-radius: 12px;
|
||
font-size: 0.75rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.badge-primary {
|
||
background: #e8f0fe;
|
||
color: #1a73e8;
|
||
}
|
||
|
||
[data-theme="dark"] .badge-primary {
|
||
background: #1e3a5f;
|
||
color: #8ab4f8;
|
||
}
|
||
|
||
.badge-success {
|
||
background: #e6f4ea;
|
||
color: #1e8e3e;
|
||
}
|
||
|
||
[data-theme="dark"] .badge-success {
|
||
background: #1e3a2e;
|
||
color: #81c995;
|
||
}
|
||
|
||
/* Empty State */
|
||
.empty-state {
|
||
text-align: center;
|
||
padding: 3rem;
|
||
color: #5f6368;
|
||
}
|
||
|
||
[data-theme="dark"] .empty-state {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 4rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.empty-state h3 {
|
||
font-size: 1.25rem;
|
||
margin: 0 0 0.5rem 0;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .empty-state h3 {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.empty-state p {
|
||
margin: 0 0 1.5rem 0;
|
||
}
|
||
|
||
/* Modal */
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
padding: 2rem;
|
||
}
|
||
|
||
.modal {
|
||
background: #ffffff;
|
||
border-radius: 12px;
|
||
max-width: 600px;
|
||
width: 100%;
|
||
max-height: 90vh;
|
||
overflow-y: auto;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
[data-theme="dark"] .modal {
|
||
background: #202124;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 1.5rem;
|
||
border-bottom: 1px solid #e0e0e0;
|
||
}
|
||
|
||
[data-theme="dark"] .modal-header {
|
||
border-bottom-color: #3c4043;
|
||
}
|
||
|
||
.modal-header h2 {
|
||
margin: 0;
|
||
font-size: 1.5rem;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .modal-header h2 {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.close-btn {
|
||
background: none;
|
||
border: none;
|
||
font-size: 1.5rem;
|
||
color: #5f6368;
|
||
cursor: pointer;
|
||
padding: 0.5rem;
|
||
line-height: 1;
|
||
transition: color 0.2s;
|
||
}
|
||
|
||
.close-btn:hover {
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .close-btn {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
[data-theme="dark"] .close-btn:hover {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 1.5rem;
|
||
}
|
||
|
||
.help-text {
|
||
margin-top: 2rem;
|
||
padding: 1rem;
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
[data-theme="dark"] .help-text {
|
||
background: #292a2d;
|
||
}
|
||
|
||
.help-text h4 {
|
||
margin: 0 0 0.5rem 0;
|
||
font-size: 0.95rem;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .help-text h4 {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.help-text ul {
|
||
margin: 0.5rem 0;
|
||
padding-left: 1.5rem;
|
||
color: #5f6368;
|
||
}
|
||
|
||
[data-theme="dark"] .help-text ul {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
.help-text p {
|
||
margin: 0.5rem 0 0 0;
|
||
color: #5f6368;
|
||
}
|
||
|
||
[data-theme="dark"] .help-text p {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
/* Storage Info */
|
||
.storage-info {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.storage-usage {
|
||
padding: 1.5rem;
|
||
background: #f8f9fa;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
[data-theme="dark"] .storage-usage {
|
||
background: #292a2d;
|
||
}
|
||
|
||
.storage-usage h3 {
|
||
margin: 0 0 1rem 0;
|
||
font-size: 1.1rem;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .storage-usage h3 {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.progress-bar {
|
||
height: 12px;
|
||
background: #e0e0e0;
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
[data-theme="dark"] .progress-bar {
|
||
background: #3c4043;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #1a73e8, #34a853);
|
||
transition: width 0.3s;
|
||
}
|
||
|
||
.storage-usage p {
|
||
margin: 0;
|
||
color: #5f6368;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
[data-theme="dark"] .storage-usage p {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
/* Security Section */
|
||
.security-section {
|
||
margin-bottom: 3rem;
|
||
}
|
||
|
||
.security-section h3 {
|
||
font-size: 1.25rem;
|
||
margin: 0 0 1.5rem 0;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .security-section h3 {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.sessions-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.session-card {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 1rem;
|
||
background: #f8f9fa;
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
[data-theme="dark"] .session-card {
|
||
background: #292a2d;
|
||
border-color: #3c4043;
|
||
}
|
||
|
||
.session-info h4 {
|
||
margin: 0 0 0.25rem 0;
|
||
font-size: 1rem;
|
||
color: #202124;
|
||
}
|
||
|
||
[data-theme="dark"] .session-info h4 {
|
||
color: #e8eaed;
|
||
}
|
||
|
||
.session-info p {
|
||
margin: 0;
|
||
font-size: 0.875rem;
|
||
color: #5f6368;
|
||
}
|
||
|
||
[data-theme="dark"] .session-info p {
|
||
color: #9aa0a6;
|
||
}
|
||
|
||
/* Alpine cloak */
|
||
[x-cloak] {
|
||
display: none !important;
|
||
}
|
||
|
||
/* Responsive */
|
||
@media (max-width: 768px) {
|
||
.account-layout {
|
||
padding: 1rem;
|
||
}
|
||
|
||
.tabs {
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.tab {
|
||
padding: 0.75rem 1rem;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.panel-content {
|
||
padding: 1.5rem;
|
||
}
|
||
|
||
.form-row {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.account-card {
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.account-actions {
|
||
width: 100%;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.section-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.modal-overlay {
|
||
padding: 1rem;
|
||
}
|
||
}
|
||
</style>
|