Update PROMPT.md and UI changes
This commit is contained in:
parent
e32e9b793a
commit
1435d1016f
7 changed files with 1304 additions and 1074 deletions
|
|
@ -61,6 +61,7 @@ const SUITE_DIRS: &[&str] = &[
|
|||
"billing",
|
||||
"products",
|
||||
"tickets",
|
||||
"about",
|
||||
];
|
||||
|
||||
pub async fn index() -> impl IntoResponse {
|
||||
|
|
|
|||
2
ui/suite/about/about.css
Normal file
2
ui/suite/about/about.css
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/* About page styles - most styles are inline in about.html */
|
||||
/* This file is for additional customization if needed */
|
||||
571
ui/suite/about/about.html
Normal file
571
ui/suite/about/about.html
Normal file
|
|
@ -0,0 +1,571 @@
|
|||
<script>
|
||||
CSSLoader.loadMultiple(['about.css']);
|
||||
|
||||
(async function loadAboutData() {
|
||||
try {
|
||||
const response = await fetch('/api/product');
|
||||
const config = await response.json();
|
||||
|
||||
// Update version info
|
||||
const versionEl = document.getElementById('about-version');
|
||||
if (versionEl) versionEl.textContent = config.version || '6.1.0';
|
||||
|
||||
// Update product name
|
||||
const nameEl = document.getElementById('about-product-name');
|
||||
if (nameEl) nameEl.textContent = config.name || 'General Bots';
|
||||
|
||||
// Populate compiled features
|
||||
const compiledContainer = document.getElementById('compiled-features');
|
||||
if (compiledContainer && config.compiled_features) {
|
||||
compiledContainer.innerHTML = config.compiled_features
|
||||
.sort()
|
||||
.map(f => `<span class="feature-badge enabled">${f}</span>`)
|
||||
.join('');
|
||||
}
|
||||
|
||||
// Populate enabled apps
|
||||
const enabledContainer = document.getElementById('enabled-apps');
|
||||
if (enabledContainer && config.apps) {
|
||||
enabledContainer.innerHTML = config.apps
|
||||
.sort()
|
||||
.map(a => `<span class="feature-badge active">${a}</span>`)
|
||||
.join('');
|
||||
}
|
||||
|
||||
// Calculate disabled features
|
||||
const disabledContainer = document.getElementById('disabled-features');
|
||||
if (disabledContainer && config.compiled_features && config.apps) {
|
||||
const compiled = new Set(config.compiled_features);
|
||||
const enabled = new Set(config.apps.map(a => a.toLowerCase()));
|
||||
const disabled = [...compiled].filter(f => !enabled.has(f.toLowerCase()));
|
||||
|
||||
if (disabled.length > 0) {
|
||||
disabledContainer.innerHTML = disabled
|
||||
.sort()
|
||||
.map(f => `<span class="feature-badge disabled">${f}</span>`)
|
||||
.join('');
|
||||
} else {
|
||||
disabledContainer.innerHTML = '<span class="text-muted">All compiled features are enabled</span>';
|
||||
}
|
||||
}
|
||||
|
||||
// Update copyright
|
||||
const copyrightEl = document.getElementById('about-copyright');
|
||||
if (copyrightEl) copyrightEl.textContent = config.copyright || '© 2026 General Bots. All rights reserved.';
|
||||
|
||||
} catch (e) {
|
||||
console.warn('Failed to load product config:', e);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<div class="about-container">
|
||||
<!-- Header with Logo and Branding -->
|
||||
<header class="about-header">
|
||||
<div class="about-logo">
|
||||
<svg class="logo-svg" width="80" height="50" viewBox="0 0 140 80" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- Left antenna -->
|
||||
<line x1="5" y1="40" x2="25" y2="40"/>
|
||||
<line x1="25" y1="28" x2="25" y2="52"/>
|
||||
<!-- Left gear -->
|
||||
<circle cx="50" cy="40" r="16"/>
|
||||
<circle cx="50" cy="40" r="6"/>
|
||||
<line x1="50" y1="20" x2="50" y2="26"/>
|
||||
<line x1="50" y1="54" x2="50" y2="60"/>
|
||||
<line x1="30" y1="40" x2="36" y2="40"/>
|
||||
<line x1="64" y1="40" x2="70" y2="40"/>
|
||||
<!-- Right gear -->
|
||||
<circle cx="90" cy="40" r="16"/>
|
||||
<circle cx="90" cy="40" r="6"/>
|
||||
<line x1="90" y1="20" x2="90" y2="26"/>
|
||||
<line x1="90" y1="54" x2="90" y2="60"/>
|
||||
<line x1="70" y1="40" x2="76" y2="40"/>
|
||||
<line x1="104" y1="40" x2="110" y2="40"/>
|
||||
<!-- Right antenna -->
|
||||
<line x1="115" y1="40" x2="135" y2="40"/>
|
||||
<line x1="115" y1="28" x2="115" y2="52"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 id="about-product-name">General Bots</h1>
|
||||
<p class="tagline">AI-Powered Agentic Office Suite</p>
|
||||
<div class="version-badge">
|
||||
<span class="version-label">Version</span>
|
||||
<span id="about-version" class="version-number">6.1.0</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Key Information Cards -->
|
||||
<div class="about-cards">
|
||||
<div class="about-card">
|
||||
<div class="card-icon">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
|
||||
<path d="M2 17l10 5 10-5"/>
|
||||
<path d="M2 12l10 5 10-5"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3>Architecture</h3>
|
||||
<p>Rust-based high-performance server with HTMX-powered reactive UI</p>
|
||||
</div>
|
||||
|
||||
<div class="about-card">
|
||||
<div class="card-icon">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M12 1v6"/>
|
||||
<path d="M12 17v6"/>
|
||||
<path d="M4.22 4.22l4.24 4.24"/>
|
||||
<path d="M15.54 15.54l4.24 4.24"/>
|
||||
<path d="M1 12h6"/>
|
||||
<path d="M17 12h6"/>
|
||||
<path d="M4.22 19.78l4.24-4.24"/>
|
||||
<path d="M15.54 8.46l4.24-4.24"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3>Multi-Agent AI</h3>
|
||||
<p>Collaborative AI agents with specialized roles for complex task automation</p>
|
||||
</div>
|
||||
|
||||
<div class="about-card">
|
||||
<div class="card-icon">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
||||
<line x1="3" y1="9" x2="21" y2="9"/>
|
||||
<line x1="9" y1="21" x2="9" y2="9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3>Modular Design</h3>
|
||||
<p>Feature-gated compilation for minimal binary size and resource usage</p>
|
||||
</div>
|
||||
|
||||
<div class="about-card">
|
||||
<div class="card-icon">
|
||||
<svg width="24" height="24" 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"/>
|
||||
<polyline points="9,12 12,15 15,9"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3>Enterprise Security</h3>
|
||||
<p>RBAC, SOC 2 compliance ready, multi-tenant architecture</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Feature Matrix Section -->
|
||||
<section class="feature-matrix">
|
||||
<h2>Feature Matrix</h2>
|
||||
|
||||
<div class="feature-section">
|
||||
<h3>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="20 6 9 17 4 12"/>
|
||||
</svg>
|
||||
Compiled Features
|
||||
</h3>
|
||||
<p class="section-description">Features included in this build</p>
|
||||
<div id="compiled-features" class="feature-badges">
|
||||
<span class="feature-badge enabled">chat</span>
|
||||
<span class="feature-badge enabled">drive</span>
|
||||
<span class="feature-badge enabled">tasks</span>
|
||||
<span class="feature-badge enabled">automation</span>
|
||||
<span class="feature-badge enabled">cache</span>
|
||||
<span class="feature-badge enabled">directory</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="feature-section">
|
||||
<h3>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polyline points="12 6 12 12 16 14"/>
|
||||
</svg>
|
||||
Enabled Apps
|
||||
</h3>
|
||||
<p class="section-description">Applications active for this instance</p>
|
||||
<div id="enabled-apps" class="feature-badges">
|
||||
<span class="feature-badge active">chat</span>
|
||||
<span class="feature-badge active">drive</span>
|
||||
<span class="feature-badge active">tasks</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="feature-section">
|
||||
<h3>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="15" y1="9" x2="9" y2="15"/>
|
||||
<line x1="9" y1="9" x2="15" y2="15"/>
|
||||
</svg>
|
||||
Not Compiled / Disabled
|
||||
</h3>
|
||||
<p class="section-description">Features not included or disabled in configuration</p>
|
||||
<div id="disabled-features" class="feature-badges">
|
||||
<span class="text-muted">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Feature Dependency Tree -->
|
||||
<section class="dependency-tree-section">
|
||||
<h2>Feature Dependency Tree</h2>
|
||||
<p class="section-description">How features relate to each other</p>
|
||||
<div class="tree-container">
|
||||
<img src="/suite/about/feature-tree.svg" alt="Feature Dependency Tree" class="dependency-tree-svg" onerror="this.parentElement.innerHTML='<p class=\'text-muted\'>Dependency tree not available</p>'"/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- System Information -->
|
||||
<section class="system-info">
|
||||
<h2>System Information</h2>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<span class="info-label">Platform</span>
|
||||
<span class="info-value">Rust + Axum + HTMX</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Database</span>
|
||||
<span class="info-value">PostgreSQL + Diesel ORM</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Cache</span>
|
||||
<span class="info-value">Redis</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Vector DB</span>
|
||||
<span class="info-value">Qdrant (optional)</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">License</span>
|
||||
<span class="info-value">AGPL-3.0</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Documentation</span>
|
||||
<a href="https://docs.pragmatismo.com.br" target="_blank" class="info-link">docs.pragmatismo.com.br</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Links and Resources -->
|
||||
<section class="resources">
|
||||
<h2>Resources</h2>
|
||||
<div class="resource-links">
|
||||
<a href="https://github.com/GeneralBots" target="_blank" class="resource-link">
|
||||
<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"/>
|
||||
</svg>
|
||||
GitHub
|
||||
</a>
|
||||
<a href="https://docs.pragmatismo.com.br" target="_blank" class="resource-link">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
|
||||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
|
||||
</svg>
|
||||
Documentation
|
||||
</a>
|
||||
<a href="mailto:support@pragmatismo.cloud" class="resource-link">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/>
|
||||
<polyline points="22,6 12,13 2,6"/>
|
||||
</svg>
|
||||
Support
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="about-footer">
|
||||
<p id="about-copyright">© 2026 General Bots. All rights reserved.</p>
|
||||
<p class="built-with">Built with ❤️ by <a href="https://pragmatismo.com.br" target="_blank">Pragmatismo</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.about-container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.about-header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
padding: 2rem;
|
||||
background: linear-gradient(135deg, var(--surface) 0%, var(--surface-hover) 100%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.about-logo {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.logo-svg {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.about-header h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
background: linear-gradient(135deg, var(--primary), var(--text));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
margin: 0.5rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.version-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--primary);
|
||||
color: var(--primary-contrast, #000);
|
||||
border-radius: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.version-label {
|
||||
opacity: 0.8;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.version-number {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.about-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.about-card {
|
||||
padding: 1.5rem;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.about-card:hover {
|
||||
border-color: var(--primary);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 0 auto 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--primary-light, rgba(212, 245, 5, 0.1));
|
||||
border-radius: 12px;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.about-card h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.about-card p {
|
||||
margin: 0;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Feature Matrix */
|
||||
.feature-matrix {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.feature-matrix h2,
|
||||
.dependency-tree-section h2,
|
||||
.system-info h2,
|
||||
.resources h2 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid var(--primary);
|
||||
}
|
||||
|
||||
.feature-section {
|
||||
margin-bottom: 2rem;
|
||||
padding: 1.5rem;
|
||||
background: var(--surface);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.feature-section h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.section-description {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
.feature-badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.feature-badge {
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.feature-badge.enabled {
|
||||
background: rgba(34, 197, 94, 0.15);
|
||||
color: #22c55e;
|
||||
border: 1px solid rgba(34, 197, 94, 0.3);
|
||||
}
|
||||
|
||||
.feature-badge.active {
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
color: #3b82f6;
|
||||
border: 1px solid rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.feature-badge.disabled {
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
color: #ef4444;
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: var(--text-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Dependency Tree */
|
||||
.dependency-tree-section {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.tree-container {
|
||||
padding: 1.5rem;
|
||||
background: var(--surface);
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
text-align: center;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.dependency-tree-svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* System Info */
|
||||
.system-info {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
background: var(--surface);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.info-link {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.info-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Resources */
|
||||
.resources {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.resource-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.resource-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.resource-link:hover {
|
||||
border-color: var(--primary);
|
||||
background: var(--surface-hover);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.about-footer {
|
||||
text-align: center;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.about-footer p {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.built-with a {
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.built-with a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
314
ui/suite/about/feature-tree.svg
Normal file
314
ui/suite/about/feature-tree.svg
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 700" style="background: transparent;">
|
||||
<defs>
|
||||
<!-- Gradients -->
|
||||
<linearGradient id="nodeGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#22c55e;stop-opacity:0.2"/>
|
||||
<stop offset="100%" style="stop-color:#22c55e;stop-opacity:0.05"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="coreGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#d4f505;stop-opacity:0.3"/>
|
||||
<stop offset="100%" style="stop-color:#d4f505;stop-opacity:0.1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="bundleGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#3b82f6;stop-opacity:0.2"/>
|
||||
<stop offset="100%" style="stop-color:#3b82f6;stop-opacity:0.05"/>
|
||||
</linearGradient>
|
||||
|
||||
<!-- Arrow marker -->
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto" fill="#888">
|
||||
<polygon points="0 0, 10 3.5, 0 7"/>
|
||||
</marker>
|
||||
|
||||
<!-- Glow filter -->
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="2" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.title { font: bold 24px sans-serif; fill: #e0e0e0; }
|
||||
.subtitle { font: 14px sans-serif; fill: #888; }
|
||||
.node-label { font: bold 11px monospace; fill: #e0e0e0; text-anchor: middle; }
|
||||
.category-label { font: bold 12px sans-serif; fill: #888; text-anchor: middle; }
|
||||
.dep-text { font: 10px sans-serif; fill: #666; }
|
||||
.legend-text { font: 11px sans-serif; fill: #888; }
|
||||
.node { stroke-width: 2; }
|
||||
.node-core { fill: url(#coreGradient); stroke: #d4f505; }
|
||||
.node-app { fill: url(#nodeGradient); stroke: #22c55e; }
|
||||
.node-bundle { fill: url(#bundleGradient); stroke: #3b82f6; }
|
||||
.edge { stroke: #555; stroke-width: 1.5; fill: none; marker-end: url(#arrowhead); }
|
||||
.edge-deps { stroke: #444; stroke-width: 1; stroke-dasharray: 4,2; }
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="35" class="title" text-anchor="middle">General Bots Feature Dependency Tree</text>
|
||||
<text x="450" y="55" class="subtitle" text-anchor="middle">Cargo.toml Feature Flags and Dependencies</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(20, 80)">
|
||||
<rect x="0" y="0" width="130" height="90" rx="8" fill="#1a1a2e" stroke="#333"/>
|
||||
<text x="10" y="20" class="legend-text" font-weight="bold">Legend</text>
|
||||
<rect x="10" y="30" width="16" height="16" rx="4" class="node-core"/>
|
||||
<text x="32" y="42" class="legend-text">Core Technology</text>
|
||||
<rect x="10" y="50" width="16" height="16" rx="4" class="node-app"/>
|
||||
<text x="32" y="62" class="legend-text">Application</text>
|
||||
<rect x="10" y="70" width="16" height="16" rx="4" class="node-bundle"/>
|
||||
<text x="32" y="82" class="legend-text">Bundle (group)</text>
|
||||
</g>
|
||||
|
||||
<!-- ================ CORE TECHNOLOGIES (Bottom Layer) ================ -->
|
||||
<text x="450" y="650" class="category-label">CORE TECHNOLOGIES</text>
|
||||
|
||||
<!-- cache -->
|
||||
<g transform="translate(150, 590)">
|
||||
<rect x="-40" y="-15" width="80" height="30" rx="6" class="node node-core"/>
|
||||
<text y="4" class="node-label">cache</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">redis</text>
|
||||
</g>
|
||||
|
||||
<!-- automation -->
|
||||
<g transform="translate(280, 590)">
|
||||
<rect x="-50" y="-15" width="100" height="30" rx="6" class="node node-core"/>
|
||||
<text y="4" class="node-label">automation</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">rhai, cron</text>
|
||||
</g>
|
||||
|
||||
<!-- llm -->
|
||||
<g transform="translate(410, 590)">
|
||||
<rect x="-30" y="-15" width="60" height="30" rx="6" class="node node-core"/>
|
||||
<text y="4" class="node-label">llm</text>
|
||||
</g>
|
||||
|
||||
<!-- vectordb -->
|
||||
<g transform="translate(520, 590)">
|
||||
<rect x="-40" y="-15" width="80" height="30" rx="6" class="node node-core"/>
|
||||
<text y="4" class="node-label">vectordb</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">qdrant</text>
|
||||
</g>
|
||||
|
||||
<!-- monitoring -->
|
||||
<g transform="translate(650, 590)">
|
||||
<rect x="-50" y="-15" width="100" height="30" rx="6" class="node node-core"/>
|
||||
<text y="4" class="node-label">monitoring</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">sysinfo</text>
|
||||
</g>
|
||||
|
||||
<!-- directory -->
|
||||
<g transform="translate(780, 590)">
|
||||
<rect x="-45" y="-15" width="90" height="30" rx="6" class="node node-core"/>
|
||||
<text y="4" class="node-label">directory</text>
|
||||
</g>
|
||||
|
||||
<!-- ================ PRODUCTIVITY APPS (Layer 2) ================ -->
|
||||
<text x="200" y="480" class="category-label">PRODUCTIVITY</text>
|
||||
|
||||
<!-- tasks (depends on automation, cron) -->
|
||||
<g transform="translate(100, 510)">
|
||||
<rect x="-35" y="-15" width="70" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">tasks</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">cron</text>
|
||||
</g>
|
||||
<path d="M 100 540 Q 100 565 150 575 L 150 575" class="edge"/>
|
||||
<path d="M 100 540 Q 180 560 230 575" class="edge"/>
|
||||
|
||||
<!-- calendar -->
|
||||
<g transform="translate(200, 510)">
|
||||
<rect x="-45" y="-15" width="90" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">calendar</text>
|
||||
</g>
|
||||
|
||||
<!-- project (depends on quick-xml) -->
|
||||
<g transform="translate(300, 510)">
|
||||
<rect x="-40" y="-15" width="80" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">project</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">quick-xml</text>
|
||||
</g>
|
||||
|
||||
<!-- goals -->
|
||||
<g transform="translate(400, 510)">
|
||||
<rect x="-35" y="-15" width="70" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">goals</text>
|
||||
</g>
|
||||
|
||||
<!-- ================ COMMUNICATION APPS (Layer 2) ================ -->
|
||||
<text x="700" y="480" class="category-label">COMMUNICATION</text>
|
||||
|
||||
<!-- chat -->
|
||||
<g transform="translate(550, 510)">
|
||||
<rect x="-30" y="-15" width="60" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">chat</text>
|
||||
</g>
|
||||
|
||||
<!-- mail (has deps) -->
|
||||
<g transform="translate(650, 510)">
|
||||
<rect x="-30" y="-15" width="60" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">mail</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">lettre,imap</text>
|
||||
</g>
|
||||
|
||||
<!-- meet (has deps) -->
|
||||
<g transform="translate(750, 510)">
|
||||
<rect x="-30" y="-15" width="60" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">meet</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">livekit</text>
|
||||
</g>
|
||||
|
||||
<!-- people -->
|
||||
<g transform="translate(850, 510)">
|
||||
<rect x="-40" y="-15" width="80" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">people</text>
|
||||
</g>
|
||||
|
||||
<!-- ================ DOCUMENT APPS (Layer 3) ================ -->
|
||||
<text x="200" y="360" class="category-label">DOCUMENTS</text>
|
||||
|
||||
<!-- drive (has deps) -->
|
||||
<g transform="translate(100, 395)">
|
||||
<rect x="-35" y="-15" width="70" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">drive</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">aws-s3</text>
|
||||
</g>
|
||||
|
||||
<!-- docs (has deps) -->
|
||||
<g transform="translate(200, 395)">
|
||||
<rect x="-30" y="-15" width="60" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">docs</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">docx-rs</text>
|
||||
</g>
|
||||
|
||||
<!-- sheet (has deps) -->
|
||||
<g transform="translate(300, 395)">
|
||||
<rect x="-35" y="-15" width="70" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">sheet</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">calamine</text>
|
||||
</g>
|
||||
|
||||
<!-- slides -->
|
||||
<g transform="translate(400, 395)">
|
||||
<rect x="-40" y="-15" width="80" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">slides</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">ooxmlsdk</text>
|
||||
</g>
|
||||
|
||||
<!-- paper (depends on docs, pdf-extract) -->
|
||||
<g transform="translate(150, 320)">
|
||||
<rect x="-35" y="-15" width="70" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">paper</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">pdf-extract</text>
|
||||
</g>
|
||||
<path d="M 150 350 L 172 380" class="edge"/>
|
||||
|
||||
<!-- ================ RESEARCH / LEARNING (Layer 3) ================ -->
|
||||
<text x="600" y="360" class="category-label">RESEARCH & LEARNING</text>
|
||||
|
||||
<!-- research (depends on llm, vectordb) -->
|
||||
<g transform="translate(550, 395)">
|
||||
<rect x="-50" y="-15" width="100" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">research</text>
|
||||
</g>
|
||||
<path d="M 520 410 Q 470 530 440 575" class="edge"/>
|
||||
<path d="M 580 410 L 510 575" class="edge"/>
|
||||
|
||||
<!-- learn -->
|
||||
<g transform="translate(680, 395)">
|
||||
<rect x="-35" y="-15" width="70" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">learn</text>
|
||||
</g>
|
||||
|
||||
<!-- sources -->
|
||||
<g transform="translate(780, 395)">
|
||||
<rect x="-45" y="-15" width="90" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">sources</text>
|
||||
</g>
|
||||
|
||||
<!-- ================ ADMIN / ANALYTICS (Layer 4) ================ -->
|
||||
<text x="200" y="240" class="category-label">ADMIN & ANALYTICS</text>
|
||||
|
||||
<!-- analytics -->
|
||||
<g transform="translate(100, 275)">
|
||||
<rect x="-50" y="-15" width="100" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">analytics</text>
|
||||
</g>
|
||||
|
||||
<!-- dashboards -->
|
||||
<g transform="translate(220, 275)">
|
||||
<rect x="-55" y="-15" width="110" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">dashboards</text>
|
||||
</g>
|
||||
|
||||
<!-- admin -->
|
||||
<g transform="translate(340, 275)">
|
||||
<rect x="-35" y="-15" width="70" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">admin</text>
|
||||
</g>
|
||||
|
||||
<!-- settings -->
|
||||
<g transform="translate(440, 275)">
|
||||
<rect x="-45" y="-15" width="90" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">settings</text>
|
||||
</g>
|
||||
|
||||
<!-- ================ DEVELOPMENT (Layer 4) ================ -->
|
||||
<text x="650" y="240" class="category-label">DEVELOPMENT</text>
|
||||
|
||||
<!-- designer -->
|
||||
<g transform="translate(580, 275)">
|
||||
<rect x="-45" y="-15" width="90" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">designer</text>
|
||||
</g>
|
||||
|
||||
<!-- editor -->
|
||||
<g transform="translate(700, 275)">
|
||||
<rect x="-40" y="-15" width="80" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">editor</text>
|
||||
</g>
|
||||
|
||||
<!-- console (depends on monitoring) -->
|
||||
<g transform="translate(820, 275)">
|
||||
<rect x="-45" y="-15" width="90" height="30" rx="6" class="node node-app"/>
|
||||
<text y="4" class="node-label">console</text>
|
||||
<text y="25" class="dep-text" text-anchor="middle">crossterm</text>
|
||||
</g>
|
||||
<path d="M 780 290 Q 720 450 670 575" class="edge"/>
|
||||
|
||||
<!-- ================ BUNDLE FEATURES (Top Layer) ================ -->
|
||||
<text x="450" y="120" class="category-label">BUNDLE FEATURES</text>
|
||||
|
||||
<!-- communications bundle -->
|
||||
<g transform="translate(200, 160)">
|
||||
<rect x="-80" y="-20" width="160" height="40" rx="8" class="node node-bundle"/>
|
||||
<text y="5" class="node-label">communications</text>
|
||||
</g>
|
||||
<path d="M 200 180 Q 200 350 550 495" class="edge-deps"/>
|
||||
<path d="M 200 180 Q 350 350 650 495" class="edge-deps"/>
|
||||
<path d="M 200 180 Q 500 350 750 495" class="edge-deps"/>
|
||||
|
||||
<!-- productivity bundle -->
|
||||
<g transform="translate(400, 160)">
|
||||
<rect x="-65" y="-20" width="130" height="40" rx="8" class="node node-bundle"/>
|
||||
<text y="5" class="node-label">productivity</text>
|
||||
</g>
|
||||
<path d="M 340 180 Q 200 350 100 495" class="edge-deps"/>
|
||||
<path d="M 380 180 Q 260 350 200 495" class="edge-deps"/>
|
||||
<path d="M 420 180 Q 350 350 300 495" class="edge-deps"/>
|
||||
|
||||
<!-- documents bundle -->
|
||||
<g transform="translate(580, 160)">
|
||||
<rect x="-60" y="-20" width="120" height="40" rx="8" class="node node-bundle"/>
|
||||
<text y="5" class="node-label">documents</text>
|
||||
</g>
|
||||
<path d="M 530 180 Q 200 300 100 380" class="edge-deps"/>
|
||||
<path d="M 560 180 Q 320 300 200 380" class="edge-deps"/>
|
||||
<path d="M 590 180 Q 400 300 300 380" class="edge-deps"/>
|
||||
<path d="M 620 180 Q 480 300 400 380" class="edge-deps"/>
|
||||
|
||||
<!-- full bundle -->
|
||||
<g transform="translate(760, 160)">
|
||||
<rect x="-45" y="-20" width="90" height="40" rx="8" class="node node-bundle"/>
|
||||
<text y="5" class="node-label">full</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -42,7 +42,25 @@ function applyProductConfig(config) {
|
|||
|
||||
// Filter apps based on enabled list
|
||||
if (config.apps && Array.isArray(config.apps)) {
|
||||
filterAppsByConfig(config.apps);
|
||||
let effectiveApps = config.apps;
|
||||
|
||||
// Check if we have compiled_features info to filter even further
|
||||
// This ensures we don't show apps that are enabled in config but not compiled in binary
|
||||
if (config.compiled_features && Array.isArray(config.compiled_features)) {
|
||||
const compiledSet = new Set(config.compiled_features.map(f => f.toLowerCase()));
|
||||
effectiveApps = effectiveApps.filter(app =>
|
||||
compiledSet.has(app.toLowerCase()) ||
|
||||
app.toLowerCase() === 'settings' ||
|
||||
app.toLowerCase() === 'auth' ||
|
||||
app.toLowerCase() === 'admin' // Admin usually contains settings which is always there
|
||||
);
|
||||
|
||||
// Also call a helper to hide UI elements for non-compiled features explicitly
|
||||
// This handles features that might not be "apps" but are UI sections
|
||||
hideNonCompiledUI(compiledSet);
|
||||
}
|
||||
|
||||
filterAppsByConfig(effectiveApps);
|
||||
}
|
||||
|
||||
// Apply custom logo
|
||||
|
|
@ -74,6 +92,28 @@ function applyProductConfig(config) {
|
|||
}
|
||||
}
|
||||
|
||||
// Hide UI elements that require features not compiled in the binary
|
||||
function hideNonCompiledUI(compiledSet) {
|
||||
// Hide elements with data-feature attribute that aren't in compiled set
|
||||
document.querySelectorAll('[data-feature]').forEach(el => {
|
||||
const feature = el.getAttribute('data-feature').toLowerCase();
|
||||
// Allow settings/admin as they are usually core
|
||||
if (!compiledSet.has(feature) && feature !== 'settings' && feature !== 'admin') {
|
||||
el.style.display = 'none';
|
||||
el.classList.add('hidden-uncompiled');
|
||||
}
|
||||
});
|
||||
|
||||
// Also look for specific sections that might map to features
|
||||
// e.g. .feature-mail, .feature-meet classes
|
||||
compiledSet.forEach(feature => {
|
||||
// This loop defines what IS available.
|
||||
// Logic should be inverse: find all feature- classes and hide if not in set
|
||||
// But scanning all classes is expensive.
|
||||
// Better to rely on data-feature or explicit app hiding which filterAppsByConfig does.
|
||||
});
|
||||
}
|
||||
|
||||
// Filter visible apps based on enabled list
|
||||
function filterAppsByConfig(enabledApps) {
|
||||
const enabledSet = new Set(enabledApps.map((a) => a.toLowerCase()));
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
<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>
|
||||
<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>
|
||||
|
|
@ -44,7 +46,9 @@
|
|||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="2" y1="12" x2="22" y2="12"></line>
|
||||
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
|
||||
<path
|
||||
d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z">
|
||||
</path>
|
||||
</svg>
|
||||
<span>Language</span>
|
||||
</a>
|
||||
|
|
@ -121,7 +125,9 @@
|
|||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="2" y1="12" x2="22" y2="12"></line>
|
||||
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
|
||||
<path
|
||||
d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z">
|
||||
</path>
|
||||
</svg>
|
||||
<span data-i18n="admin-dns">DNS</span>
|
||||
</a>
|
||||
|
|
@ -135,6 +141,15 @@
|
|||
</svg>
|
||||
<span data-i18n="admin-audit">Audit Log</span>
|
||||
</a>
|
||||
<a href="#about" class="nav-item" hx-get="/suite/about/about.html" hx-target="#main-content"
|
||||
hx-push-url="/#about">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="12" y1="16" x2="12" y2="12"></line>
|
||||
<line x1="12" y1="8" x2="12.01" y2="8"></line>
|
||||
</svg>
|
||||
<span data-i18n="nav-about">About</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="settings-footer">
|
||||
|
|
@ -157,9 +172,7 @@
|
|||
<p class="subtitle">Manage your personal information and preferences</p>
|
||||
</div>
|
||||
|
||||
<form class="settings-form"
|
||||
hx-put="/api/user/profile"
|
||||
hx-swap="none"
|
||||
<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">
|
||||
|
|
@ -174,7 +187,8 @@
|
|||
<div class="avatar-actions">
|
||||
<label class="btn-secondary upload-btn">
|
||||
<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">
|
||||
<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>
|
||||
|
|
@ -194,11 +208,8 @@
|
|||
<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']">
|
||||
<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>
|
||||
|
|
@ -268,8 +279,7 @@
|
|||
<h2>Change Password</h2>
|
||||
<p>Update your password regularly for better security</p>
|
||||
</div>
|
||||
<form hx-post="/api/user/password"
|
||||
hx-swap="none"
|
||||
<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">
|
||||
|
|
@ -278,7 +288,8 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label>New Password</label>
|
||||
<input type="password" name="new_password" required minlength="8" autocomplete="new-password">
|
||||
<input type="password" name="new_password" required minlength="8"
|
||||
autocomplete="new-password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Confirm New Password</label>
|
||||
|
|
@ -297,17 +308,13 @@
|
|||
<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"
|
||||
<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-swap="none">
|
||||
<button class="btn-primary" hx-post="/api/user/security/2fa/enable" hx-swap="none">
|
||||
Enable 2FA
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -319,17 +326,14 @@
|
|||
<h2>Active Sessions</h2>
|
||||
<p>Manage your active login sessions</p>
|
||||
</div>
|
||||
<div class="sessions-list" id="sessions-list"
|
||||
hx-get="/api/user/security/sessions"
|
||||
hx-trigger="load"
|
||||
<div class="sessions-list" id="sessions-list" hx-get="/api/user/security/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"
|
||||
<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
|
||||
|
|
@ -343,9 +347,7 @@
|
|||
<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"
|
||||
<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>
|
||||
|
|
@ -367,69 +369,86 @@
|
|||
<h2>Display Language</h2>
|
||||
<p>Choose the language for the application interface</p>
|
||||
</div>
|
||||
<div class="language-grid" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin-top: 16px;">
|
||||
<button class="language-option" data-locale="en" onclick="selectLanguage('en', this)" style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: pointer; position: relative; transition: all 0.2s; text-align: left;">
|
||||
<div class="language-grid"
|
||||
style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin-top: 16px;">
|
||||
<button class="language-option" data-locale="en" onclick="selectLanguage('en', this)"
|
||||
style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: pointer; position: relative; transition: all 0.2s; text-align: left;">
|
||||
<span style="font-size: 40px; line-height: 1;">🇺🇸</span>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #f8fafc;">English</div>
|
||||
<div style="font-size: 13px; color: #94a3b8; margin-top: 4px;">English</div>
|
||||
</div>
|
||||
<svg class="lang-check" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="3" style="opacity: 0;">
|
||||
<svg class="lang-check" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#3b82f6"
|
||||
stroke-width="3" style="opacity: 0;">
|
||||
<polyline points="20 6 9 17 4 12"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="language-option" data-locale="pt-BR" onclick="selectLanguage('pt-BR', this)" style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: pointer; position: relative; transition: all 0.2s; text-align: left;">
|
||||
<button class="language-option" data-locale="pt-BR" onclick="selectLanguage('pt-BR', this)"
|
||||
style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: pointer; position: relative; transition: all 0.2s; text-align: left;">
|
||||
<span style="font-size: 40px; line-height: 1;">🇧🇷</span>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #f8fafc;">Portuguese (Brazil)</div>
|
||||
<div style="font-size: 13px; color: #94a3b8; margin-top: 4px;">Português (Brasil)</div>
|
||||
</div>
|
||||
<svg class="lang-check" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="3" style="opacity: 0;">
|
||||
<svg class="lang-check" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#3b82f6"
|
||||
stroke-width="3" style="opacity: 0;">
|
||||
<polyline points="20 6 9 17 4 12"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="language-option" data-locale="es" disabled style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: not-allowed; position: relative; opacity: 0.5; text-align: left;">
|
||||
<button class="language-option" data-locale="es" disabled
|
||||
style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: not-allowed; position: relative; opacity: 0.5; text-align: left;">
|
||||
<span style="font-size: 40px; line-height: 1;">🇪🇸</span>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #f8fafc;">Spanish</div>
|
||||
<div style="font-size: 13px; color: #94a3b8; margin-top: 4px;">Español</div>
|
||||
</div>
|
||||
<span style="font-size: 10px; font-weight: 700; text-transform: uppercase; padding: 4px 8px; background: #334155; border-radius: 4px; color: #94a3b8;">Coming Soon</span>
|
||||
<span
|
||||
style="font-size: 10px; font-weight: 700; text-transform: uppercase; padding: 4px 8px; background: #334155; border-radius: 4px; color: #94a3b8;">Coming
|
||||
Soon</span>
|
||||
</button>
|
||||
<button class="language-option" data-locale="zh-CN" disabled style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: not-allowed; position: relative; opacity: 0.5; text-align: left;">
|
||||
<button class="language-option" data-locale="zh-CN" disabled
|
||||
style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: not-allowed; position: relative; opacity: 0.5; text-align: left;">
|
||||
<span style="font-size: 40px; line-height: 1;">🇨🇳</span>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #f8fafc;">Chinese (Simplified)</div>
|
||||
<div style="font-size: 13px; color: #94a3b8; margin-top: 4px;">简体中文</div>
|
||||
</div>
|
||||
<span style="font-size: 10px; font-weight: 700; text-transform: uppercase; padding: 4px 8px; background: #334155; border-radius: 4px; color: #94a3b8;">Coming Soon</span>
|
||||
<span
|
||||
style="font-size: 10px; font-weight: 700; text-transform: uppercase; padding: 4px 8px; background: #334155; border-radius: 4px; color: #94a3b8;">Coming
|
||||
Soon</span>
|
||||
</button>
|
||||
<button class="language-option" data-locale="fr" disabled style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: not-allowed; position: relative; opacity: 0.5; text-align: left;">
|
||||
<button class="language-option" data-locale="fr" disabled
|
||||
style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: not-allowed; position: relative; opacity: 0.5; text-align: left;">
|
||||
<span style="font-size: 40px; line-height: 1;">🇫🇷</span>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #f8fafc;">French</div>
|
||||
<div style="font-size: 13px; color: #94a3b8; margin-top: 4px;">Français</div>
|
||||
</div>
|
||||
<span style="font-size: 10px; font-weight: 700; text-transform: uppercase; padding: 4px 8px; background: #334155; border-radius: 4px; color: #94a3b8;">Coming Soon</span>
|
||||
<span
|
||||
style="font-size: 10px; font-weight: 700; text-transform: uppercase; padding: 4px 8px; background: #334155; border-radius: 4px; color: #94a3b8;">Coming
|
||||
Soon</span>
|
||||
</button>
|
||||
<button class="language-option" data-locale="de" disabled style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: not-allowed; position: relative; opacity: 0.5; text-align: left;">
|
||||
<button class="language-option" data-locale="de" disabled
|
||||
style="display: flex; align-items: center; gap: 16px; padding: 20px; background: #0f172a; border: 2px solid #334155; border-radius: 12px; cursor: not-allowed; position: relative; opacity: 0.5; text-align: left;">
|
||||
<span style="font-size: 40px; line-height: 1;">🇩🇪</span>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #f8fafc;">German</div>
|
||||
<div style="font-size: 13px; color: #94a3b8; margin-top: 4px;">Deutsch</div>
|
||||
</div>
|
||||
<span style="font-size: 10px; font-weight: 700; text-transform: uppercase; padding: 4px 8px; background: #334155; border-radius: 4px; color: #94a3b8;">Coming Soon</span>
|
||||
<span
|
||||
style="font-size: 10px; font-weight: 700; text-transform: uppercase; padding: 4px 8px; background: #334155; border-radius: 4px; color: #94a3b8;">Coming
|
||||
Soon</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
(function () {
|
||||
function initLanguageSelector() {
|
||||
var savedLocale = localStorage.getItem('gb-locale') || 'en';
|
||||
var buttons = document.querySelectorAll('.language-option[data-locale]');
|
||||
|
||||
buttons.forEach(function(btn) {
|
||||
buttons.forEach(function (btn) {
|
||||
var locale = btn.dataset.locale;
|
||||
var checkmark = btn.querySelector('.lang-check');
|
||||
|
||||
|
|
@ -439,13 +458,13 @@
|
|||
checkmark.style.opacity = '1';
|
||||
}
|
||||
|
||||
btn.addEventListener('mouseover', function() {
|
||||
btn.addEventListener('mouseover', function () {
|
||||
if (!btn.disabled) {
|
||||
btn.style.borderColor = '#3b82f6';
|
||||
}
|
||||
});
|
||||
|
||||
btn.addEventListener('mouseout', function() {
|
||||
btn.addEventListener('mouseout', function () {
|
||||
if (!btn.disabled && locale !== localStorage.getItem('gb-locale')) {
|
||||
btn.style.borderColor = '#334155';
|
||||
btn.style.background = '#0f172a';
|
||||
|
|
@ -454,9 +473,9 @@
|
|||
});
|
||||
}
|
||||
|
||||
window.selectLanguage = function(locale, element) {
|
||||
window.selectLanguage = function (locale, element) {
|
||||
var buttons = document.querySelectorAll('.language-option[data-locale]');
|
||||
buttons.forEach(function(btn) {
|
||||
buttons.forEach(function (btn) {
|
||||
btn.style.borderColor = '#334155';
|
||||
btn.style.background = '#0f172a';
|
||||
var check = btn.querySelector('.lang-check');
|
||||
|
|
@ -477,7 +496,7 @@
|
|||
showToast('Language changed to ' + (locale === 'pt-BR' ? 'Português' : 'English') + '. Reloading...');
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
};
|
||||
|
|
@ -659,7 +678,8 @@
|
|||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<span class="setting-title" data-i18n="settings-display-language">Display Language</span>
|
||||
<span class="setting-desc" data-i18n="settings-language-affects">Affects all text in the application</span>
|
||||
<span class="setting-desc" data-i18n="settings-language-affects">Affects all text in the
|
||||
application</span>
|
||||
</div>
|
||||
<select id="language-select" class="form-select" onchange="changeLanguage(this.value)">
|
||||
<option value="en">🇺🇸 English</option>
|
||||
|
|
@ -675,7 +695,8 @@
|
|||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<span class="setting-title" data-i18n="settings-date-format">Date Format</span>
|
||||
<span class="setting-desc" data-i18n="settings-date-format-desc">How dates are displayed</span>
|
||||
<span class="setting-desc" data-i18n="settings-date-format-desc">How dates are
|
||||
displayed</span>
|
||||
</div>
|
||||
<select id="date-format-select" class="form-select" onchange="changeDateFormat(this.value)">
|
||||
<option value="MM/DD/YYYY">MM/DD/YYYY (US)</option>
|
||||
|
|
@ -686,7 +707,8 @@
|
|||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<span class="setting-title" data-i18n="settings-time-format">Time Format</span>
|
||||
<span class="setting-desc" data-i18n="settings-time-format-desc">12-hour or 24-hour clock</span>
|
||||
<span class="setting-desc" data-i18n="settings-time-format-desc">12-hour or 24-hour
|
||||
clock</span>
|
||||
</div>
|
||||
<select id="time-format-select" class="form-select" onchange="changeTimeFormat(this.value)">
|
||||
<option value="12h">12-hour (1:30 PM)</option>
|
||||
|
|
@ -704,8 +726,7 @@
|
|||
<p class="subtitle">Control how you receive notifications</p>
|
||||
</div>
|
||||
|
||||
<form hx-put="/api/user/notifications/preferences"
|
||||
hx-swap="none"
|
||||
<form hx-put="/api/user/notifications/preferences" hx-swap="none"
|
||||
hx-on::after-request="showToast('Notification preferences saved')">
|
||||
|
||||
<!-- Email Notifications -->
|
||||
|
|
@ -827,10 +848,7 @@
|
|||
<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-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>
|
||||
|
|
@ -907,10 +925,8 @@
|
|||
<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="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">
|
||||
|
|
@ -926,7 +942,9 @@
|
|||
<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>
|
||||
<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">
|
||||
|
|
@ -951,16 +969,15 @@
|
|||
<div class="card-header">
|
||||
<h2>API Keys</h2>
|
||||
<button class="btn-primary btn-sm" disabled title="Coming soon">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<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"
|
||||
<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>
|
||||
|
|
@ -973,16 +990,15 @@
|
|||
<div class="card-header">
|
||||
<h2>Webhooks</h2>
|
||||
<button class="btn-primary btn-sm" disabled title="Coming soon">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<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"
|
||||
<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>
|
||||
|
|
@ -999,18 +1015,25 @@
|
|||
<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>
|
||||
<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"
|
||||
<button class="btn-secondary btn-sm" hx-post="/api/oauth/google/connect"
|
||||
hx-swap="none">Connect</button>
|
||||
</div>
|
||||
<div class="oauth-item">
|
||||
|
|
@ -1026,22 +1049,22 @@
|
|||
<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"
|
||||
<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>
|
||||
<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"
|
||||
<button class="btn-secondary btn-sm" hx-post="/api/oauth/github/connect"
|
||||
hx-swap="none">Connect</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1104,9 +1127,7 @@
|
|||
<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"
|
||||
<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>
|
||||
|
|
@ -1140,10 +1161,7 @@
|
|||
<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-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">
|
||||
|
|
@ -1166,15 +1184,14 @@
|
|||
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-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>
|
||||
<text x="16" y="13" text-anchor="middle" fill="white" font-size="8"
|
||||
font-weight="bold">VISA</text>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="card-details">
|
||||
|
|
@ -1191,9 +1208,7 @@
|
|||
<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"
|
||||
<div class="invoices-list" id="invoices-list" hx-get="/api/user/billing/invoices" hx-trigger="load"
|
||||
hx-swap="innerHTML">
|
||||
<table class="invoices-table">
|
||||
<thead>
|
||||
|
|
@ -1236,8 +1251,10 @@
|
|||
<div class="setting-card">
|
||||
<div class="card-header">
|
||||
<h2>User Management</h2>
|
||||
<button class="btn-primary btn-sm" onclick="document.getElementById('create-user-modal').showModal()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<button class="btn-primary btn-sm"
|
||||
onclick="document.getElementById('create-user-modal').showModal()">
|
||||
<svg width="16" height="16" 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>
|
||||
|
|
@ -1263,8 +1280,10 @@
|
|||
<div class="setting-card">
|
||||
<div class="card-header">
|
||||
<h2>Group Management</h2>
|
||||
<button class="btn-primary btn-sm" onclick="document.getElementById('create-group-modal').showModal()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<button class="btn-primary btn-sm"
|
||||
onclick="document.getElementById('create-group-modal').showModal()">
|
||||
<svg width="16" height="16" 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>
|
||||
|
|
@ -1310,8 +1329,10 @@
|
|||
<div class="setting-card">
|
||||
<div class="card-header">
|
||||
<h2>Domain Configuration</h2>
|
||||
<button class="btn-primary btn-sm" onclick="document.getElementById('register-dns-modal').showModal()">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<button class="btn-primary btn-sm"
|
||||
onclick="document.getElementById('register-dns-modal').showModal()">
|
||||
<svg width="16" height="16" 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>
|
||||
|
|
@ -1361,7 +1382,8 @@
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<form hx-post="/api/users/create" hx-swap="none" hx-on::after-request="if(event.detail.successful) { document.getElementById('create-user-modal').close(); showToast('User created successfully'); }">
|
||||
<form hx-post="/api/users/create" hx-swap="none"
|
||||
hx-on::after-request="if(event.detail.successful) { document.getElementById('create-user-modal').close(); showToast('User created successfully'); }">
|
||||
<div class="form-group">
|
||||
<label>Username</label>
|
||||
<input type="text" name="username" required placeholder="username" autocomplete="username">
|
||||
|
|
@ -1387,7 +1409,8 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn-secondary" onclick="document.getElementById('create-user-modal').close()">Cancel</button>
|
||||
<button type="button" class="btn-secondary"
|
||||
onclick="document.getElementById('create-user-modal').close()">Cancel</button>
|
||||
<button type="submit" class="btn-primary">Create User</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1406,7 +1429,8 @@
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<form hx-post="/api/groups/create" hx-swap="none" hx-on::after-request="if(event.detail.successful) { document.getElementById('create-group-modal').close(); showToast('Group created successfully'); }">
|
||||
<form hx-post="/api/groups/create" hx-swap="none"
|
||||
hx-on::after-request="if(event.detail.successful) { document.getElementById('create-group-modal').close(); showToast('Group created successfully'); }">
|
||||
<div class="form-group">
|
||||
<label>Group Name</label>
|
||||
<input type="text" name="name" required placeholder="Engineering Team">
|
||||
|
|
@ -1416,7 +1440,8 @@
|
|||
<textarea name="description" placeholder="Group description..." rows="3"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn-secondary" onclick="document.getElementById('create-group-modal').close()">Cancel</button>
|
||||
<button type="button" class="btn-secondary"
|
||||
onclick="document.getElementById('create-group-modal').close()">Cancel</button>
|
||||
<button type="submit" class="btn-primary">Create Group</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1435,7 +1460,8 @@
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<form hx-post="/api/dns/register" hx-swap="none" hx-on::after-request="if(event.detail.successful) { document.getElementById('register-dns-modal').close(); showToast('DNS record registered'); }">
|
||||
<form hx-post="/api/dns/register" hx-swap="none"
|
||||
hx-on::after-request="if(event.detail.successful) { document.getElementById('register-dns-modal').close(); showToast('DNS record registered'); }">
|
||||
<div class="form-group">
|
||||
<label>Hostname</label>
|
||||
<input type="text" name="hostname" required placeholder="mybot.example.com">
|
||||
|
|
@ -1453,7 +1479,8 @@
|
|||
<input type="text" name="target" placeholder="192.168.1.1 or target.domain.com">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn-secondary" onclick="document.getElementById('register-dns-modal').close()">Cancel</button>
|
||||
<button type="button" class="btn-secondary"
|
||||
onclick="document.getElementById('register-dns-modal').close()">Cancel</button>
|
||||
<button type="submit" class="btn-primary">Register</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -1532,7 +1559,9 @@
|
|||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.settings-layout {
|
||||
|
|
@ -1858,11 +1887,11 @@
|
|||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.toggle input:checked + .toggle-slider {
|
||||
.toggle input:checked+.toggle-slider {
|
||||
background: var(--primary);
|
||||
}
|
||||
|
||||
.toggle input:checked + .toggle-slider::before {
|
||||
.toggle input:checked+.toggle-slider::before {
|
||||
transform: translateX(22px);
|
||||
}
|
||||
|
||||
|
|
@ -2314,7 +2343,8 @@
|
|||
<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">
|
||||
<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>
|
||||
|
|
@ -2367,7 +2397,7 @@
|
|||
}
|
||||
|
||||
// Handle hash navigation on load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const hash = window.location.hash.substring(1);
|
||||
if (hash) {
|
||||
const navItem = document.querySelector(`a[href="#${hash}"]`);
|
||||
|
|
@ -2379,7 +2409,7 @@
|
|||
|
||||
// Theme selection - handle both .theme-card and .theme-option elements
|
||||
document.querySelectorAll('.theme-card').forEach(card => {
|
||||
card.addEventListener('click', function() {
|
||||
card.addEventListener('click', function () {
|
||||
const theme = this.dataset.theme;
|
||||
document.body.setAttribute('data-theme', theme);
|
||||
localStorage.setItem('gb-theme', theme);
|
||||
|
|
@ -2416,7 +2446,7 @@
|
|||
(function loadTranslations() {
|
||||
var script = document.createElement('script');
|
||||
script.src = '/suite/js/translations.js';
|
||||
script.onload = function() {
|
||||
script.onload = function () {
|
||||
if (window.gbTranslations) {
|
||||
window.gbTranslations.translatePage();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue