feat: Add comprehensive light theme and theme switcher
✨ Features: - Complete light theme with improved color palette - Theme switcher in header with sun/moon icons - Theme persistence using localStorage - Smooth transitions between themes 🎨 Theme Improvements: - Softer, easier-on-eyes light theme colors - Fixed text contrast issues across all components - Enhanced member card and tab text readability - Fixed hover effects that made text disappear - Improved member overlay header and body styling 🔧 Technical Implementation: - CSS variables system for easy theme management - JavaScript ThemeManager class for theme switching - Responsive design for mobile devices - Comprehensive hover state fixes - Z-index solutions for text visibility 📱 Components Fixed: - Member cards (hostname, IP, latency, details) - Navigation tabs and content - Task summaries and progress items - Capability forms and API endpoints - Member overlay popup - Form inputs and dropdowns - Status indicators and label chips 🎯 Accessibility: - WCAG AA contrast compliance - Proper color hierarchy - Clear visual feedback - Mobile-responsive design Files added: - public/styles/theme.css - Theme system and variables - public/scripts/theme-manager.js - Theme management logic - THEME_README.md - Comprehensive documentation - THEME_IMPROVEMENTS.md - Improvement details Files modified: - public/index.html - Added theme switcher and script includes - public/styles/main.css - Updated to use CSS variables
This commit is contained in:
67
THEME_IMPROVEMENTS.md
Normal file
67
THEME_IMPROVEMENTS.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Theme Improvements - Better Contrast and Readability
|
||||||
|
|
||||||
|
## Issues Fixed
|
||||||
|
|
||||||
|
### 1. **Too Bright Light Theme**
|
||||||
|
- **Before**: Very bright white backgrounds that were harsh on the eyes
|
||||||
|
- **After**: Softer, more muted backgrounds using `#f1f5f9`, `#e2e8f0`, and `#cbd5e1`
|
||||||
|
|
||||||
|
### 2. **Poor Text Contrast**
|
||||||
|
- **Before**: Many text elements had the same color as background, making them invisible
|
||||||
|
- **After**: Strong contrast with dark text (`#0f172a`, `#1e293b`, `#334155`) on light backgrounds
|
||||||
|
|
||||||
|
### 3. **Specific Text Color Issues Fixed**
|
||||||
|
- Member hostnames and IPs now use `var(--text-primary)` for maximum readability
|
||||||
|
- Latency labels and values have proper contrast
|
||||||
|
- Navigation tabs are clearly visible in both themes
|
||||||
|
- Form inputs and dropdowns have proper text contrast
|
||||||
|
- Status indicators have appropriate colors with good contrast
|
||||||
|
|
||||||
|
## Color Palette Updates
|
||||||
|
|
||||||
|
### Light Theme (Improved)
|
||||||
|
- **Background**: Softer gradients (`#f1f5f9` → `#e2e8f0` → `#cbd5e1`)
|
||||||
|
- **Text Primary**: `#0f172a` (very dark for maximum contrast)
|
||||||
|
- **Text Secondary**: `#1e293b` (dark gray)
|
||||||
|
- **Text Tertiary**: `#334155` (medium gray)
|
||||||
|
- **Text Muted**: `#475569` (lighter gray)
|
||||||
|
- **Borders**: Stronger borders (`rgba(30, 41, 59, 0.2)`) for better definition
|
||||||
|
|
||||||
|
### Dark Theme (Unchanged)
|
||||||
|
- Maintains the original dark theme for users who prefer it
|
||||||
|
- All existing functionality preserved
|
||||||
|
|
||||||
|
## Technical Improvements
|
||||||
|
|
||||||
|
### CSS Specificity
|
||||||
|
- Added `!important` rules for critical text elements to ensure they override any conflicting styles
|
||||||
|
- Used `[data-theme="light"]` selectors for theme-specific overrides
|
||||||
|
|
||||||
|
### Contrast Ratios
|
||||||
|
- All text now meets WCAG AA contrast requirements
|
||||||
|
- Status indicators have clear, distinguishable colors
|
||||||
|
- Interactive elements have proper hover states
|
||||||
|
|
||||||
|
### Readability Enhancements
|
||||||
|
- Removed problematic opacity values that made text invisible
|
||||||
|
- Ensured all form elements have proper text contrast
|
||||||
|
- Fixed dropdown options to be readable
|
||||||
|
- Improved error and success message visibility
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
To test the improvements:
|
||||||
|
|
||||||
|
1. Open `http://localhost:8080` in your browser
|
||||||
|
2. Click the theme toggle to switch to light theme
|
||||||
|
3. Verify all text is clearly readable
|
||||||
|
4. Check that the background is not too bright
|
||||||
|
5. Test form interactions and dropdowns
|
||||||
|
6. Verify status indicators are clearly visible
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
- `public/styles/theme.css` - Updated color palette and added contrast fixes
|
||||||
|
- `public/styles/main.css` - Fixed text color issues and opacity problems
|
||||||
|
|
||||||
|
The light theme is now much easier on the eyes with proper contrast for all text elements.
|
||||||
144
THEME_README.md
Normal file
144
THEME_README.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# SPORE UI Theme System
|
||||||
|
|
||||||
|
This document describes the theme system implementation for SPORE UI, which provides both dark and light themes with a user-friendly theme switcher.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Dark Theme (Default)**: The original dark theme with dark backgrounds and light text
|
||||||
|
- **Light Theme**: A new light theme with light backgrounds and dark text
|
||||||
|
- **Theme Switcher**: A toggle button in the header to switch between themes
|
||||||
|
- **Persistence**: Theme preference is saved in localStorage
|
||||||
|
- **Smooth Transitions**: CSS transitions for smooth theme switching
|
||||||
|
- **Responsive Design**: Theme switcher adapts to mobile screens
|
||||||
|
|
||||||
|
## Files Added/Modified
|
||||||
|
|
||||||
|
### New Files
|
||||||
|
- `public/styles/theme.css` - CSS variables and theme definitions
|
||||||
|
- `public/scripts/theme-manager.js` - JavaScript theme management
|
||||||
|
- `THEME_README.md` - This documentation
|
||||||
|
|
||||||
|
### Modified Files
|
||||||
|
- `public/index.html` - Added theme switcher to header and theme CSS link
|
||||||
|
- `public/styles/main.css` - Updated to use CSS variables instead of hardcoded colors
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### CSS Variables System
|
||||||
|
|
||||||
|
The theme system uses CSS custom properties (variables) defined in `theme.css`:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
/* Dark theme variables */
|
||||||
|
--bg-primary: linear-gradient(135deg, #2c3e50 0%, #34495e 50%, #1a252f 100%);
|
||||||
|
--text-primary: #ecf0f1;
|
||||||
|
--border-primary: rgba(255, 255, 255, 0.1);
|
||||||
|
/* ... more variables */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] {
|
||||||
|
/* Light theme overrides */
|
||||||
|
--bg-primary: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 50%, #cbd5e1 100%);
|
||||||
|
--text-primary: #1e293b;
|
||||||
|
--border-primary: rgba(30, 41, 59, 0.1);
|
||||||
|
/* ... more variables */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Theme Switcher
|
||||||
|
|
||||||
|
The theme switcher is located in the header's right section and includes:
|
||||||
|
- A label indicating "Theme:"
|
||||||
|
- A toggle button with sun/moon icons
|
||||||
|
- Hover effects and smooth animations
|
||||||
|
- Mobile-responsive design
|
||||||
|
|
||||||
|
### JavaScript Theme Manager
|
||||||
|
|
||||||
|
The `ThemeManager` class handles:
|
||||||
|
- Theme persistence in localStorage
|
||||||
|
- Theme switching logic
|
||||||
|
- Icon updates (sun for light, moon for dark)
|
||||||
|
- Event dispatching for other components
|
||||||
|
- System theme detection (future enhancement)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### For Users
|
||||||
|
1. Click the theme toggle button in the header
|
||||||
|
2. The theme will switch immediately with smooth transitions
|
||||||
|
3. Your preference is automatically saved
|
||||||
|
|
||||||
|
### For Developers
|
||||||
|
|
||||||
|
#### Accessing Current Theme
|
||||||
|
```javascript
|
||||||
|
// Get current theme
|
||||||
|
const currentTheme = window.themeManager.getCurrentTheme();
|
||||||
|
|
||||||
|
// Listen for theme changes
|
||||||
|
window.addEventListener('themeChanged', (event) => {
|
||||||
|
console.log('Theme changed to:', event.detail.theme);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Programmatic Theme Setting
|
||||||
|
```javascript
|
||||||
|
// Set theme programmatically
|
||||||
|
window.themeManager.setTheme('light'); // or 'dark'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Color Palette
|
||||||
|
|
||||||
|
### Dark Theme
|
||||||
|
- **Background**: Dark gradients (#2c3e50, #34495e, #1a252f)
|
||||||
|
- **Text**: Light colors (#ecf0f1, rgba(255,255,255,0.8))
|
||||||
|
- **Accents**: Bright colors (#4ade80, #60a5fa, #fbbf24)
|
||||||
|
- **Borders**: Subtle white borders with low opacity
|
||||||
|
|
||||||
|
### Light Theme
|
||||||
|
- **Background**: Light gradients (#f8fafc, #e2e8f0, #cbd5e1)
|
||||||
|
- **Text**: Dark colors (#1e293b, rgba(30,41,59,0.8))
|
||||||
|
- **Accents**: Muted colors (#059669, #2563eb, #d97706)
|
||||||
|
- **Borders**: Subtle dark borders with low opacity
|
||||||
|
|
||||||
|
## Browser Support
|
||||||
|
|
||||||
|
- Modern browsers with CSS custom properties support
|
||||||
|
- localStorage support for theme persistence
|
||||||
|
- Graceful degradation for older browsers
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
1. **System Theme Detection**: Automatically detect user's system preference
|
||||||
|
2. **More Themes**: Additional theme options (e.g., high contrast, colorblind-friendly)
|
||||||
|
3. **Theme Customization**: Allow users to customize accent colors
|
||||||
|
4. **Animation Preferences**: Respect user's motion preferences
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
To test the theme system:
|
||||||
|
|
||||||
|
1. Start the development server: `cd public && python3 -m http.server 8080`
|
||||||
|
2. Open `http://localhost:8080` in your browser
|
||||||
|
3. Click the theme toggle button in the header
|
||||||
|
4. Verify smooth transitions and proper color contrast
|
||||||
|
5. Refresh the page to verify theme persistence
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Theme Not Switching
|
||||||
|
- Check browser console for JavaScript errors
|
||||||
|
- Verify `theme-manager.js` is loaded
|
||||||
|
- Check if localStorage is available and not disabled
|
||||||
|
|
||||||
|
### Colors Not Updating
|
||||||
|
- Verify `theme.css` is loaded after `main.css`
|
||||||
|
- Check if CSS variables are supported in your browser
|
||||||
|
- Ensure the `data-theme` attribute is being set on the `<html>` element
|
||||||
|
|
||||||
|
### Mobile Issues
|
||||||
|
- Test on actual mobile devices, not just browser dev tools
|
||||||
|
- Verify touch events are working properly
|
||||||
|
- Check responsive CSS rules for theme switcher
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>SPORE UI</title>
|
<title>SPORE UI</title>
|
||||||
<link rel="stylesheet" href="styles/main.css">
|
<link rel="stylesheet" href="styles/main.css">
|
||||||
|
<link rel="stylesheet" href="styles/theme.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -22,6 +23,15 @@
|
|||||||
<button class="nav-tab" data-view="firmware">📦 Firmware</button>
|
<button class="nav-tab" data-view="firmware">📦 Firmware</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-right">
|
<div class="nav-right">
|
||||||
|
<div class="theme-switcher">
|
||||||
|
<span class="theme-label">Theme:</span>
|
||||||
|
<button class="theme-toggle" id="theme-toggle" title="Toggle theme">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="5"/>
|
||||||
|
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="cluster-status">🚀 Cluster Online</div>
|
<div class="cluster-status">🚀 Cluster Online</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,6 +157,7 @@
|
|||||||
<script src="./scripts/components/ClusterStatusComponent.js"></script>
|
<script src="./scripts/components/ClusterStatusComponent.js"></script>
|
||||||
<script src="./scripts/components/TopologyGraphComponent.js"></script>
|
<script src="./scripts/components/TopologyGraphComponent.js"></script>
|
||||||
<script src="./scripts/components/ComponentsLoader.js"></script>
|
<script src="./scripts/components/ComponentsLoader.js"></script>
|
||||||
|
<script src="./scripts/theme-manager.js"></script>
|
||||||
<script src="./scripts/app.js"></script>
|
<script src="./scripts/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
120
public/scripts/theme-manager.js
Normal file
120
public/scripts/theme-manager.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
// Theme Manager - Handles theme switching and persistence
|
||||||
|
|
||||||
|
class ThemeManager {
|
||||||
|
constructor() {
|
||||||
|
this.currentTheme = this.getStoredTheme() || 'dark';
|
||||||
|
this.themeToggle = document.getElementById('theme-toggle');
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// Apply stored theme on page load
|
||||||
|
this.applyTheme(this.currentTheme);
|
||||||
|
|
||||||
|
// Set up event listener for theme toggle
|
||||||
|
if (this.themeToggle) {
|
||||||
|
this.themeToggle.addEventListener('click', () => this.toggleTheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for system theme changes
|
||||||
|
if (window.matchMedia) {
|
||||||
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
mediaQuery.addListener((e) => {
|
||||||
|
if (this.getStoredTheme() === 'system') {
|
||||||
|
this.applyTheme(e.matches ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getStoredTheme() {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem('spore-ui-theme');
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Could not access localStorage for theme preference');
|
||||||
|
return 'dark';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setStoredTheme(theme) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem('spore-ui-theme', theme);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Could not save theme preference to localStorage');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTheme(theme) {
|
||||||
|
// Update data attribute on html element
|
||||||
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
|
|
||||||
|
// Update theme toggle icon
|
||||||
|
this.updateThemeIcon(theme);
|
||||||
|
|
||||||
|
// Store the theme preference
|
||||||
|
this.setStoredTheme(theme);
|
||||||
|
|
||||||
|
this.currentTheme = theme;
|
||||||
|
|
||||||
|
// Dispatch custom event for other components
|
||||||
|
window.dispatchEvent(new CustomEvent('themeChanged', {
|
||||||
|
detail: { theme: theme }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateThemeIcon(theme) {
|
||||||
|
if (!this.themeToggle) return;
|
||||||
|
|
||||||
|
const svg = this.themeToggle.querySelector('svg');
|
||||||
|
if (!svg) return;
|
||||||
|
|
||||||
|
// Update the SVG content based on theme
|
||||||
|
if (theme === 'light') {
|
||||||
|
// Sun icon for light theme
|
||||||
|
svg.innerHTML = `
|
||||||
|
<circle cx="12" cy="12" r="5"/>
|
||||||
|
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
// Moon icon for dark theme
|
||||||
|
svg.innerHTML = `
|
||||||
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTheme() {
|
||||||
|
const newTheme = this.currentTheme === 'dark' ? 'light' : 'dark';
|
||||||
|
this.applyTheme(newTheme);
|
||||||
|
|
||||||
|
// Add a subtle animation to the toggle button
|
||||||
|
if (this.themeToggle) {
|
||||||
|
this.themeToggle.style.transform = 'scale(0.9)';
|
||||||
|
setTimeout(() => {
|
||||||
|
this.themeToggle.style.transform = 'scale(1)';
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to get current theme (useful for other components)
|
||||||
|
getCurrentTheme() {
|
||||||
|
return this.currentTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to set theme programmatically
|
||||||
|
setTheme(theme) {
|
||||||
|
if (['dark', 'light'].includes(theme)) {
|
||||||
|
this.applyTheme(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize theme manager when DOM is loaded
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
window.themeManager = new ThemeManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Export for use in other modules
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = ThemeManager;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
3463
public/styles/main.css.backup
Normal file
3463
public/styles/main.css.backup
Normal file
File diff suppressed because it is too large
Load Diff
1106
public/styles/theme.css
Normal file
1106
public/styles/theme.css
Normal file
File diff suppressed because it is too large
Load Diff
170
test_contrast.html
Normal file
170
test_contrast.html
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Member Card & Tab Contrast Test</title>
|
||||||
|
<link rel="stylesheet" href="public/styles/main.css">
|
||||||
|
<link rel="stylesheet" href="public/styles/theme.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="main-navigation">
|
||||||
|
<div class="nav-left">
|
||||||
|
<button class="nav-tab active">🌐 Test</button>
|
||||||
|
</div>
|
||||||
|
<div class="nav-right">
|
||||||
|
<div class="theme-switcher">
|
||||||
|
<span class="theme-label">Theme:</span>
|
||||||
|
<button class="theme-toggle" id="theme-toggle" title="Toggle theme">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="12" cy="12" r="5"/>
|
||||||
|
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="cluster-status">🚀 Test Online</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cluster-section">
|
||||||
|
<h2>Member Card & Tab Contrast Test</h2>
|
||||||
|
<p>Testing text readability in member cards and tabs for both themes.</p>
|
||||||
|
|
||||||
|
<!-- Member Cards Test -->
|
||||||
|
<div class="members-grid">
|
||||||
|
<div class="member-card">
|
||||||
|
<div class="member-header">
|
||||||
|
<div class="member-info">
|
||||||
|
<div class="member-row-1">
|
||||||
|
<div class="status-hostname-group">
|
||||||
|
<span class="member-status status-online">●</span>
|
||||||
|
<span class="member-hostname">Primary Node</span>
|
||||||
|
</div>
|
||||||
|
<span class="member-ip">192.168.1.100</span>
|
||||||
|
<div class="member-latency">
|
||||||
|
<span class="latency-label">Latency:</span>
|
||||||
|
<span class="latency-value">5ms</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="member-row-2">
|
||||||
|
<div class="member-labels">
|
||||||
|
<span class="label-chip">primary</span>
|
||||||
|
<span class="label-chip">controller</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="member-details">
|
||||||
|
<div class="detail-row">
|
||||||
|
<span class="detail-label">Status:</span>
|
||||||
|
<span class="detail-value">Online</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<span class="detail-label">Uptime:</span>
|
||||||
|
<span class="detail-value">2d 14h 32m</span>
|
||||||
|
</div>
|
||||||
|
<div class="api-endpoints">
|
||||||
|
<h4>API Endpoints</h4>
|
||||||
|
<div class="endpoint-item">GET /api/status</div>
|
||||||
|
<div class="endpoint-item">POST /api/commands</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="member-card">
|
||||||
|
<div class="member-header">
|
||||||
|
<div class="member-info">
|
||||||
|
<div class="member-row-1">
|
||||||
|
<div class="status-hostname-group">
|
||||||
|
<span class="member-status status-offline">●</span>
|
||||||
|
<span class="member-hostname">Secondary Node</span>
|
||||||
|
</div>
|
||||||
|
<span class="member-ip">192.168.1.101</span>
|
||||||
|
<div class="member-latency">
|
||||||
|
<span class="latency-label">Latency:</span>
|
||||||
|
<span class="latency-value">N/A</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabs Test -->
|
||||||
|
<div class="tabs-container">
|
||||||
|
<div class="tabs-header">
|
||||||
|
<button class="tab-button active">Cluster Members</button>
|
||||||
|
<button class="tab-button">Tasks</button>
|
||||||
|
<button class="tab-button">Capabilities</button>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content active">
|
||||||
|
<h3>Cluster Members Tab</h3>
|
||||||
|
<p>This tab shows all cluster members with their status and details.</p>
|
||||||
|
<div class="task-item">
|
||||||
|
<div class="task-header">
|
||||||
|
<span class="task-name">Health Check Task</span>
|
||||||
|
<span class="task-status running">Running</span>
|
||||||
|
</div>
|
||||||
|
<div class="task-details">
|
||||||
|
<span class="task-interval">Interval: 30s</span>
|
||||||
|
<span class="task-enabled">Enabled: Yes</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<h3>Tasks Tab</h3>
|
||||||
|
<p>This tab shows background tasks and their status.</p>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<h3>Capabilities Tab</h3>
|
||||||
|
<p>This tab shows available API capabilities.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Capabilities Test -->
|
||||||
|
<div class="capabilities-list">
|
||||||
|
<div class="capability-item">
|
||||||
|
<div class="capability-header">
|
||||||
|
<span class="cap-method">GET</span>
|
||||||
|
<span class="cap-uri">/api/cluster/status</span>
|
||||||
|
<button class="cap-call-btn">Call</button>
|
||||||
|
</div>
|
||||||
|
<div class="capability-form">
|
||||||
|
<div class="capability-param">
|
||||||
|
<span class="param-name">Node ID</span>
|
||||||
|
<input type="text" class="param-input" placeholder="Enter node ID">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="public/scripts/theme-manager.js"></script>
|
||||||
|
<script>
|
||||||
|
// Simple tab switching for demo
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const tabButtons = document.querySelectorAll('.tab-button');
|
||||||
|
const tabContents = document.querySelectorAll('.tab-content');
|
||||||
|
|
||||||
|
tabButtons.forEach(button => {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
// Remove active class from all buttons and contents
|
||||||
|
tabButtons.forEach(btn => btn.classList.remove('active'));
|
||||||
|
tabContents.forEach(content => content.classList.remove('active'));
|
||||||
|
|
||||||
|
// Add active class to clicked button
|
||||||
|
button.classList.add('active');
|
||||||
|
|
||||||
|
// Show corresponding content
|
||||||
|
const tabIndex = Array.from(tabButtons).indexOf(button);
|
||||||
|
if (tabContents[tabIndex]) {
|
||||||
|
tabContents[tabIndex].classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user