Files
Warracker/frontend/about.html
sassanix 7535e8d1ef Public Global View, Apprise Integration, Filtering, and Major Fixes
### Major Features
- **Public Global Warranty View:** All authenticated users can now view global warranties. Admins retain full control; regular users get read-only access to others’ warranties.
- **Global View Admin Controls:** Admins can now toggle global view availability and limit it to admins only via site settings.
- **Global Status Dashboard View:** Extended global view to warranty statistics and dashboards with full permissions enforcement.
- **Apprise Push Notifications:** Integrated Apprise for multi-platform warranty alerts with extensive backend and frontend support (80+ services).
- **Warranty Type Filtering/Sorting:** Introduced dynamic, case-insensitive filtering and sorting by warranty type on the main page.
- **Admin Global Warranty View:** Dedicated admin tools and UI for viewing all warranties with enhanced styling and user info.

### UX/UI Enhancements
- **Product Photo Thumbnails:** Added interactive, responsive photo previews on warranty cards across all views.
- **Updated Footer Links:** All "Powered by Warracker" footers now link to the official website (`https://warracker.com`).

### Fixes and Stability Improvements
- **Status Dashboard Chart Fixes:** Resolved canvas reuse errors and chart switching issues.
- **CSS Cache Busting:** Ensured consistent styling across domain/IP access by versioning CSS/JS and updating service worker.
- **Settings Access Fixes:** Regular users can now access the settings page without triggering admin-only API calls.
- **Settings Persistence Fixes:** Addressed major frontend/backend issues preventing correct saving/loading of user preferences.
- **Notification Timing Overhaul:** Rewrote logic for precise notification delivery and implemented duplicate prevention.

### Security and Technical Enhancements
- Global view maintains secure ownership enforcement.
- Improved permission checks, graceful degradation, and responsive design across all new features.
2025-06-10 21:59:17 -03:00

374 lines
20 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<!-- Basic Meta Tags -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>About - Warracker</title>
<!-- Favicons -->
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="icon" type="image/png" sizes="16x16" href="img/favicon-16x16.png?v=2">
<link rel="icon" type="image/png" sizes="32x32" href="img/favicon-32x32.png?v=2">
<link rel="apple-touch-icon" sizes="180x180" href="img/favicon-512x512.png">
<link rel="manifest" href="manifest.json">
<!-- Stylesheets -->
<link rel="stylesheet" href="style.css"> <!-- Main stylesheet -->
<link rel="stylesheet" href="styles.css"> <!-- Additional styles -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <!-- Font Awesome -->
<link rel="stylesheet" href="header-fix.css"> <!-- Header specific styles -->
<!-- Mobile Header specific styles -->
<link rel="stylesheet" href="mobile-header.css">
<!-- Scripts loaded in head -->
<script src="theme-loader.js"></script> <!-- Apply theme early -->
<script src="include-auth-new.js"></script> <!-- Handles auth state display -->
<script src="fix-auth-buttons-loader.js"></script> <!-- Fixes auth button display timing -->
</head>
<body>
<!-- Standard Header (Copied from index.html) -->
<header>
<div class="container">
<div class="app-title">
<i class="fas fa-shield-alt"></i>
<h1><a href="index.html" style="color: inherit; text-decoration: none; cursor: pointer;">Warracker</a></h1>
</div>
<div class="nav-links">
<a href="index.html" class="nav-link">
<i class="fas fa-home"></i> Home
</a>
<a href="status.html" class="nav-link">
<i class="fas fa-chart-pie"></i> Status
</a>
<!-- Consider adding an 'active' class to an 'About' link if created here -->
</div>
<!-- Group for right-aligned elements -->
<div class="header-right-group">
<div id="authContainer" class="auth-buttons">
<a href="login.html" class="auth-btn login-btn">
<i class="fas fa-sign-in-alt"></i> Login
</a>
<a href="register.html" class="auth-btn register-btn">
<i class="fas fa-user-plus"></i> Register
</a>
</div>
<div id="userMenu" class="user-menu" style="display: none;">
<button id="userMenuBtn" class="user-btn">
<i class="fas fa-user-circle"></i>
<span id="userDisplayName">User</span>
</button>
<div id="userMenuDropdown" class="user-menu-dropdown">
<div class="user-info">
<div id="userName" class="user-name">User Name</div>
<div id="userEmail" class="user-email">user@example.com</div>
</div>
<div class="user-menu-item">
<a href="settings-new.html" style="color: inherit; text-decoration: none; display: block;">
<i class="fas fa-cog"></i> Settings
</a>
</div>
<div class="user-menu-item">
<i class="fas fa-info-circle"></i>
<a href="about.html" style="text-decoration: none; color: inherit;">About</a>
</div>
<div class="user-menu-item" id="logoutMenuItem">
<i class="fas fa-sign-out-alt"></i> Logout
</div>
</div>
</div>
</div> <!-- End header-right-group -->
</div>
</header>
<!-- Main Content Area -->
<div class="container" style="padding: 0;">
<div class="main-content" style="display: block;">
<!-- Existing About Content, now wrapped -->
<div class="about-container" style="background-color: var(--card-bg); color: var(--text-color); padding: 20px; border-radius: 8px; margin-top: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); width: 100%; box-sizing: border-box;">
<h1 style="color: var(--primary-color); margin-bottom: 20px;">About Warracker</h1>
<p><strong>Version:</strong> v0.10.1.0</p>
<div id="versionTracker" style="margin-bottom: 20px;">
<p><strong>Update Status:</strong> <span id="updateStatus">Checking for updates...</span></p>
<a id="updateLink" href="#" target="_blank" rel="noopener noreferrer" style="display: none; color: var(--link-color);">
<i class="fa-solid fa-arrow-up-right-from-square"></i> Release Notes
</a>
</div>
<p>
<strong>GitHub Repository:</strong>
<a href="https://github.com/sassanix/Warracker" target="_blank" rel="noopener noreferrer" style="color: var(--link-color);">
https://github.com/sassanix/Warracker
</a>
</p>
<p>
<strong>Release Notes:</strong>
<a href="https://github.com/sassanix/Warracker/releases" target="_blank" rel="noopener noreferrer" style="color: var(--link-color);">
View Releases
</a>
</p>
<p>
<strong>Author:</strong>
<a href="https://github.com/sassanix" target="_blank" rel="noopener noreferrer" style="color: var(--link-color);">
Sassanix
</a>
</p>
<p>
<strong>License:</strong>
<a href="https://github.com/sassanix/Warracker/blob/main/LICENSE" target="_blank" rel="noopener noreferrer" style="color: var(--link-color);">
AGPL-3.0
</a>
</p>
<p>
<strong>Issues & Feature Requests:</strong>
<a href="https://github.com/sassanix/Warracker/issues" target="_blank" rel="noopener noreferrer" style="color: var(--link-color);">
Report Bugs / Request Features
</a>
</p>
<!-- Buy Me A Coffee Button -->
<div style="text-align: center; margin-top: 25px;">
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="sassanix" data-color="#FFDD00" data-emoji="" data-font="Poppins" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff" ></script>
</div>
</div>
</div>
</div>
<!-- Scripts loaded at the end of body -->
<script src="auth.js"></script>
<!-- Version Checker -->
<script src="version-checker.js"></script>
<!-- Footer Width Fix -->
<script src="footer-fix.js"></script>
<!-- Footer Content Manager -->
<script src="footer-content.js"></script>
<!-- Powered by Warracker Footer -->
<footer class="warracker-footer" id="warrackerFooter">
<!-- Content will be dynamically generated by footer-content.js -->
</footer>
<!-- Explicitly call setupUIEventListeners for this page -->
<script>
document.addEventListener('DOMContentLoaded', () => {
console.log('About page: DOMContentLoaded triggered');
// Add safe loading functions for about page
window.showLoading = function() {
const loadingContainer = document.getElementById('loadingContainer');
if (loadingContainer && loadingContainer.classList) {
loadingContainer.classList.add('active');
console.log('About page: Loading spinner shown');
} else {
console.log('About page: Loading container not found or unavailable');
}
};
window.hideLoading = function() {
const loadingContainer = document.getElementById('loadingContainer');
if (loadingContainer && loadingContainer.classList) {
loadingContainer.classList.remove('active');
console.log('About page: Loading spinner hidden');
} else {
console.log('About page: Loading container not found or unavailable');
}
};
// Keep theme initialization here if needed for specific timing
if (typeof initializeTheme === 'function') {
console.log('About page: Calling initializeTheme');
initializeTheme(); // Call theme initialization if it exists
}
// Add specific debugging for user menu elements
setTimeout(() => {
const userMenuBtn = document.getElementById('userMenuBtn');
const userMenuDropdown = document.getElementById('userMenuDropdown');
console.log('About page: User menu debug info:', {
userMenuBtn: !!userMenuBtn,
userMenuDropdown: !!userMenuDropdown,
userMenuBtnId: userMenuBtn ? userMenuBtn.id : 'not found',
dropdownId: userMenuDropdown ? userMenuDropdown.id : 'not found',
authModuleAvailable: !!window.auth,
isAuthenticated: window.auth ? window.auth.isAuthenticated() : 'auth not available'
});
if (userMenuBtn) {
console.log('About page: userMenuBtn element:', userMenuBtn);
console.log('About page: userMenuBtn element found, proceeding with backup handler setup');
}
// Add a backup user menu handler specifically for about page
if (userMenuBtn && userMenuDropdown) {
console.log('About page: Setting up backup user menu handler');
console.log('About page: Current dropdown classes:', userMenuDropdown.className);
console.log('About page: Current dropdown active state:', userMenuDropdown.classList.contains('active'));
// Test CSS rules by temporarily adding/removing active class
console.log('About page: Testing CSS rules...');
userMenuDropdown.classList.add('active');
const testDisplayActive = window.getComputedStyle(userMenuDropdown).display;
userMenuDropdown.classList.remove('active');
const testDisplayInactive = window.getComputedStyle(userMenuDropdown).display;
console.log('About page: CSS test results:', {
displayWhenActive: testDisplayActive,
displayWhenInactive: testDisplayInactive
});
// Remove any existing click listeners and add our own
const newUserMenuBtn = userMenuBtn.cloneNode(true);
userMenuBtn.parentNode.replaceChild(newUserMenuBtn, userMenuBtn);
newUserMenuBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
console.log('About page: Backup user menu clicked');
console.log('About page: Dropdown before toggle:', {
classes: userMenuDropdown.className,
hasActive: userMenuDropdown.classList.contains('active'),
style: userMenuDropdown.style.display
});
const isCurrentlyActive = userMenuDropdown.classList.contains('active');
if (isCurrentlyActive) {
userMenuDropdown.classList.remove('active');
console.log('About page: User menu closed');
} else {
userMenuDropdown.classList.add('active');
console.log('About page: User menu opened');
}
// Double-check the state after toggle
console.log('About page: Dropdown after toggle:', {
classes: userMenuDropdown.className,
hasActive: userMenuDropdown.classList.contains('active'),
computedStyle: window.getComputedStyle(userMenuDropdown).display
});
});
// Add global click listener to close menu when clicking outside
const outsideClickHandler = (e) => {
if (userMenuDropdown.classList.contains('active') &&
!userMenuDropdown.contains(e.target) &&
!newUserMenuBtn.contains(e.target)) {
userMenuDropdown.classList.remove('active');
console.log('About page: User menu closed by outside click');
}
};
// Use setTimeout to ensure this listener is added after any others
setTimeout(() => {
document.addEventListener('click', outsideClickHandler);
console.log('About page: Outside click handler added');
}, 10);
console.log('About page: Backup user menu handler set up successfully');
// Add specific logout handler for the about page
const logoutMenuItem = document.getElementById('logoutMenuItem');
if (logoutMenuItem) {
// Clone the logout menu item to remove any existing listeners
const newLogoutMenuItem = logoutMenuItem.cloneNode(true);
logoutMenuItem.parentNode.replaceChild(newLogoutMenuItem, logoutMenuItem);
newLogoutMenuItem.addEventListener('click', async (e) => {
e.preventDefault();
e.stopPropagation();
console.log('About page: Logout clicked (backup handler)');
// Close the user menu
userMenuDropdown.classList.remove('active');
try {
// Clear auth data manually since showLoading might fail
localStorage.removeItem('auth_token');
localStorage.removeItem('user_info');
// Try to call the auth manager logout if available
if (window.auth && typeof window.auth.logout === 'function') {
console.log('About page: Calling auth.logout()');
await window.auth.logout();
} else {
console.log('About page: Auth manager not available, redirecting manually');
// Redirect manually if auth manager fails
window.location.href = 'login.html';
}
} catch (error) {
console.error('About page: Logout error, falling back to manual redirect:', error);
// Clear auth data and redirect as fallback
localStorage.removeItem('auth_token');
localStorage.removeItem('user_info');
window.location.href = 'login.html';
}
});
console.log('About page: Backup logout handler set up');
}
} else {
console.error('About page: User menu elements not found!', {
userMenuBtn: !!userMenuBtn,
userMenuDropdown: !!userMenuDropdown
});
}
}, 200); // Delay to allow auth.js to complete setup
// Move authentication check here, after auth.js has had time to set up
setTimeout(() => {
// Check if user is authenticated (with a small delay to allow auth.js to process)
const authToken = localStorage.getItem('auth_token');
const userInfo = localStorage.getItem('user_info');
if (!authToken || !userInfo) {
// User is not authenticated, redirect to login page
console.log('About page: User not authenticated, redirecting to login');
window.location.href = 'login.html';
return;
}
}, 100); // Small delay to allow auth.js to complete setup
});
</script>
<!-- Loading Spinner -->
<div class="loading-container" id="loadingContainer">
<div class="loading-spinner"></div>
</div>
<script>
function applyFooterStyles() {
const footer = document.getElementById('warrackerFooter');
const link = document.getElementById('warrackerFooterLink');
const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark' || document.documentElement.classList.contains('dark-mode') || document.body.classList.contains('dark-mode');
if (footer) {
if (isDarkMode) {
// Dark mode styles - using same background as header (#2d2d2d)
footer.style.cssText = 'margin-top: 50px; padding: 20px; text-align: center; border-top: 1px solid #444; background-color: #2d2d2d; color: #e0e0e0; font-size: 0.9rem;';
if (link) link.style.cssText = 'color: #4dabf7; text-decoration: none; font-weight: 500;';
} else {
// Light mode styles - using same background as header (#ffffff)
footer.style.cssText = 'margin-top: 50px; padding: 20px; text-align: center; border-top: 1px solid #e0e0e0; background-color: #ffffff; color: #333333; font-size: 0.9rem;';
if (link) link.style.cssText = 'color: #3498db; text-decoration: none; font-weight: 500;';
}
}
}
document.addEventListener('DOMContentLoaded', applyFooterStyles);
const obs = new MutationObserver(applyFooterStyles);
obs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme', 'class'] });
obs.observe(document.body, { attributes: true, attributeFilter: ['class'] });
</script>
</body>
</html>