Reverted the changes, as performance and core usage was effected
This commit is contained in:
sassanix
2025-11-16 14:06:47 -04:00
parent 9990422f2e
commit 7ead2f7aa7
22 changed files with 2517 additions and 931 deletions
+1 -176
View File
@@ -1,180 +1,5 @@
# Changelog
## 1.0.3 - 2025-11-07
### Added
- **Modular Entry Orchestrator and Feature Modules**
- Added a lean ES module entry orchestrator `index.js` that initializes auth and feature modules after `authStateReady`.
- Created feature-specific modules to prepare deconstruction of the monolith while preserving identical behavior:
- `warrantyListController.js` (list loading/filtering/rendering controller surface)
- `tagManager.js` (tag management: load/render/create/update/delete)
- `addWarrantyForm.js` (multi-step add warranty wizard surface)
- Wired pages to load the new module entry with `<script type="module" src="./js/index.js"></script>` alongside legacy `script.js` for zero-regression rollout.
- _Files: `frontend/js/index.js`, `frontend/js/controllers/warrantyListController.js`, `frontend/js/components/tagManager.js`, `frontend/js/components/addWarrantyForm.js`, `frontend/index.html`, `frontend/about.html`, `frontend/status.html`, `frontend/register.html`, `frontend/reset-password.html`, `frontend/reset-password-request.html`_
- **Centralized Authentication Service**
- New module `authService.js` exposes `initAuth`, `login`, `logout`, `getToken`, `getCurrentUser`, `isAuthenticated`.
- Backwards compatibility: attaches a `window.auth` facade so existing code continues to work.
- Handles header UI state, user menu/settings dropdown toggling, and dispatches a unified `authStateReady` event.
- Robustly hides “Create Account” UI when registration is disabled (with a MutationObserver to keep it hidden after other scripts mutate the DOM).
- _Files: `frontend/js/services/authService.js`, `frontend/*.html` (module includes)_
- **Dedicated API Service**
- New `apiService.js` provides a `baseRequest` wrapper that injects the `Authorization` header and normalizes error handling.
- Exposes named helpers (e.g., `getWarranties`, `updateWarranty`, `deleteWarranty`, `getStatistics`, `savePreferences`) and a `window.api` shim for non-module scripts.
- Safe global `fetch` shim adds the Authorization header for `/api/...` calls when missing.
- _Files: `frontend/js/services/apiService.js`, `frontend/*.html` (module includes)_
- **Central State Management Store**
- Introduced a `store.js` module to act as a single source of truth for application data (warranties, filters, loading states), eliminating scattered global variables.
- UI updates are now driven by custom events, creating a predictable data flow.
- _Files: `frontend/js/store.js`_
- **Component-Based UI Modules**
- Created a suite of new, reusable UI component modules to replace HTML string concatenation. Each is responsible for building a specific part of the UI via safe DOM manipulation.
- Modules include: `warrantyCard.js`, `tag.js`, `modals.js`, `editModal.js`, `claims.js`, `notes.js`, `paperless.js`, and `ui.js`.
- _Files: `frontend/js/components/*`_
- **HTML `<template>` for Warranty Cards**
- Added a `<template id="warranty-card-template">` to `index.html` to define the warranty card structure, separating markup from rendering logic for improved performance and maintainability.
- _Files: `frontend/index.html`_
### Enhanced
- **Dockerfile: simplified static file serving**
- Removed build stage; frontend files are now copied directly to `/var/www/html/` for static serving.
- No build step required; all frontend assets are served as-is.
- _Files: `Dockerfile`_
- **HTML and asset loading**
- Switched all page `<script>` and `<link>` paths to relative paths (e.g., `./script.js`) for static file serving.
- Ensured `script.js` is loaded with `defer` across pages to avoid early DOM access issues.
- All assets are served directly from the `frontend/` directory without a build step.
- _Files: `frontend/*.html`, `frontend/**/*`_
- **Frontend structure**
- Consolidated all frontend files into a single `frontend/` directory for simplified static serving.
- _Files: `frontend/settings-styles.css`_
- **Global State Management Refactor**
- Migrated `frontend/script.js` to read/write state exclusively via the centralized `window.store` API.
- Replaced direct usages of `warranties`, `currentFilters`, and `allTags` with `store.getWarranties/setWarranties`, `store.getFilters/updateFilter`, and `store.getAllTags/setAllTags`.
- Updated filter event listeners, export flow, tag management (create/update/delete), and the filtering/rendering pipeline (`applyFilters`, `renderWarranties`) to consume store-backed data.
- `loadTags()` now persists tags to the store and dispatches events to refresh dependent UIs.
- Replaced remaining direct reads of removed globals with store getters in `script.js` (e.g., card action handlers, `filterWarranties()`, `openClaimsModal()`, and notes modal handlers). App behavior unchanged; all reads/writes now flow through `window.store`.
- _Files: `frontend/script.js`_
- **Major UI Refactor for Maintainability & Security**
- Replaced nearly all `innerHTML` assignments with a robust, component-based rendering approach. This makes the UI easier to debug, maintain, and more secure against XSS vulnerabilities.
- **Warranty List:** The main warranty display for grid, list, and table views is now rendered using the `warrantyCard.js` component.
- **Edit Modal:** The complex dynamic sections (serial numbers, current document displays for invoices/manuals/photos, and tag selection) are now built with the `editModal.js` component.
- **Claims Modal:** The entire claims view, including the header, loading/error states, and claims list, is now rendered by the `claims.js` component.
- **Other Modals:** Refactored the rendering logic for the Tag Management, Notes, and Paperless-ngx document browser modals to use their respective new components.
- _Files: `frontend/script.js`, `frontend/js/components/*`_
- **Begin Extraction of UI Event Listeners**
- Moved the global UI event initialization (`setupUIEventListeners`) into `warrantyListController.initEventListeners()` as part of the modularization.
- Legacy `frontend/script.js` delegates to the controller to attach listeners, maintaining identical behavior during the staged rollout.
- _Files: `frontend/js/controllers/warrantyListController.js`, `frontend/script.js`_
- **Controller-based Filter Pipeline**
- Migrated `applyFilters` logic into `warrantyListController.js` to centralize filtering while preserving identical behavior.
- Legacy `applyFilters` in `frontend/script.js` now delegates to the controller to avoid duplication.
- _Files: `frontend/js/controllers/warrantyListController.js`, `frontend/script.js`_
- **Filter Dropdowns and Preference Persistence Extraction**
- Moved filter dropdown population functions (`populateTagFilter`, `populateVendorFilter`, `populateWarrantyTypeFilter`) and preference persistence (`saveFilterPreferences`, `loadFilterAndSortPreferences`) into `warrantyListController.js`.
- Legacy functions in `frontend/script.js` now delegate to the controller, ensuring a single source of truth.
- _Files: `frontend/js/controllers/warrantyListController.js`, `frontend/script.js`_
- **Read-only State Bridges for Module Access**
- Exposed read-only getters on `window` for `currentView`, `lastLoadedArchived`, and `lastLoadedIncludesArchived` to allow module code to read legacy flags safely without changing their source of truth.
- _Files: `frontend/script.js`_
- **Delegated List Actions Fallback in Controller**
- Added robust delegated handlers in `warrantyListController.initEventListeners()` for `#warrantiesList` (claims, edit, delete, notes, archive/unarchive) that activate only when the legacy initializer is not present, preventing double bindings.
- _Files: `frontend/js/controllers/warrantyListController.js`_
- **Controller-based Data Loader (`loadWarranties`)**
- Migrated the full `loadWarranties` flow into `warrantyListController.js` while preserving behavior: authentication check, scope selection (Personal/Global), Archived vs All handling with archived merge, processing via `processWarrantyData`, filter dropdown repopulation, and final render through `applyFilters()`.
- Legacy `frontend/script.js` now delegates `loadWarranties` to the controller.
- _Files: `frontend/js/controllers/warrantyListController.js`, `frontend/script.js`_
- **Warranty List Rendering (component-based refactor, public bundle)**
- Rewrote `renderWarranties` in `frontend/script.js` to stop concatenating HTML strings and instead delegate card creation to `window.components.createWarrantyCard(warranty, options)`.
- Builds `actionsHtml`, `contentHtml`, `statusText/statusClass`, and view-specific root class via helpers; appends tags using `window.components.appendTags`; preserves document links row placement per view.
- Uses `DocumentFragment` to batch DOM insertions for performance and `window.components.ui.renderEmptyState` for empty states.
- Centralized card action handling with a single delegated listener on `#warrantiesList` for edit, delete, archive/unarchive, claims, and notes.
- Updated all call sites to the no-arg `renderWarranties()`; visual output and behavior remain identical across Grid, List, and Table views.
- _Files: `frontend/script.js`_
- **Finalized Legacy Script Delegation**
- Completed migration of UI event listeners from `frontend/script.js` (`setupUIEventListeners`) into `warrantyListController.initEventListeners()`:
- Filters: `statusFilter`, `tagFilter`, `vendorFilter`, `warrantyTypeFilter`
- Sort: `sortBy`
- View switchers: `gridViewBtn`, `listViewBtn`, `tableViewBtn`
- Actions: `refreshBtn`, `exportBtn`, `importBtn`
- Replaced the legacy initializer call site and removed `setupUIEventListeners` from `script.js` (now delegates to the controller).
- Delegated modal openings to component layer: `openDeleteModal`/`openArchiveModal` now call `window.components.modals.openDeleteModal/openArchiveModal` which owns DOM updates and visibility.
- Moved Edit modal save flow out of legacy script: `saveWarranty` now lives in `frontend/js/components/editModal.js`, uses `apiService.updateWarranty`, and is exposed as `window.components.editModal.saveWarranty`.
- Updated Save button wiring: controller binds Save to `editModal.saveWarranty`, preserving `setupSaveWarrantyObserver` wrapping when present.
- Consolidated card action delegation: the single `#warrantiesList` delegated listener resides exclusively in the controller; removed the overlapping legacy delegation from `script.js`.
- `script.js` is now a thin delegator with identical external behavior.
- _Files: `frontend/script.js`, `frontend/js/controllers/warrantyListController.js`, `frontend/js/components/modals.js`, `frontend/js/components/editModal.js`_
- **Page-Specific Script Modularization (`status.js`)**
- Converted `frontend/status.js` from a legacy script into a self-contained ES Module.
- Replaced all `fetch` calls with methods from the imported `apiService.js` and all authentication logic with `authService.js`, removing dependencies on the global scope.
- The module now initializes itself via a `DOMContentLoaded` listener, making it independent of the legacy `script.js`.
- Updated `frontend/status.html` to load `status.js` with `type="module"`, ensuring modern script handling.
- _Files: `frontend/status.js`, `frontend/status.html`_
- **API Service Extension**
- Added a generic `apiService.request()` method to handle various API calls, which was utilized in the refactoring of `status.js`.
- _Files: `frontend/js/services/apiService.js`_
### Fixed
- 404s for non-module assets in production by ensuring all assets are properly referenced with relative paths.
- User menu not opening: restored click/close listeners within `authService.js`.
- Login page “Create Account” visible while registration disabled: force-hidden with strong CSS and observer.
- Status page edit actions blocked by early `document.body.appendChild(...)`: added `defer` to `script.js` includes.
- **Thumbnail sizing on initial load (Grid/List/Table)**
- Removed inline image sizing in the renderer so thumbnail sizes are now driven by CSS view classes, ensuring correct sizes apply immediately without hard reload or manual view switch.
- Applied view preference before first render during data load to guarantee correct view class on first paint.
- Tuned responsive CSS for very small screens (≤360px): List view 60×60, Table view 20×20; preserved existing ≤768px sizes.
- _Files: `frontend/script.js`, `frontend/style.css`, `frontend/js/controllers/warrantyListController.js`_
- **Security:** Mitigated potential cross-site scripting (XSS) vulnerabilities by removing reliance on building the UI with `innerHTML` from dynamic data.
- **Performance:** Improved rendering performance by using efficient DOM creation and appending (`<template>` clones, `document.createElement`) instead of causing the browser to re-parse large HTML strings on every update.
- **Code Quality:** Drastically reduced code complexity and repetition in `script.js` by delegating UI creation to specialized component modules.
- Eliminated 404s for removed stylesheet by deleting the `/styles.css` link in `frontend/about.html` and removing it from the service worker pre-cache (`frontend/sw.js`).
- _Files: `frontend/about.html`, `frontend/sw.js`_
- Resolved transient `ReferenceError` messages (e.g., `allTags`/`warranties`/`currentFilters` not defined) by completing the store-backed migration in `frontend/script.js`.
### Removed
- Legacy, scattered auth scripts now replaced by `authService.js`:
- `frontend/auth.js`, `frontend/auth-new.js`, `frontend/include-auth-new.js`, `frontend/fix-auth-buttons.js`, `frontend/fix-auth-buttons-loader.js`, `frontend/registration-status.js`.
- **Global State Variables**
- Eliminated global variables like `let warranties = []` and `let currentFilters = {}` from `script.js`, centralizing all state management within `store.js`.
- **Hardcoded HTML Strings**
- Removed large, multi-line HTML string templates from JavaScript files, which were fragile and difficult to maintain.
- Frontend duplicate/unused assets removed:
- Removed unused stylesheet: `frontend/styles.css`
- Removed debug files: `frontend/js/i18n-debug.js`, `frontend/temp-toast-debug.js`
- Backend cleanup: removed unused `backend/fix_notification_columns.py`.
### House Cleaning
- Deleted generated artifacts not needed in repo: `frontend/dist`, `frontend/node_modules` (frontend is now served statically without build)
- Purged Python caches: `backend/**/__pycache__`, stray `.pyc`
- Removed old backup: `backend/fix_permissions.sql.bak`
- **Code Refinement in `settings-new.js`**
- **Settings page refactor — ES Module & event wiring**
- Converted `frontend/settings-new.js` into a self-contained ES Module and updated `settings-new.html` to load it with a `<script type="module" src="./settings-new.js"></script>` tag. The module imports `authService` and `apiService` rather than relying on global functions.
- Removed inline `onclick` handlers from the Settings HTML (notably the Delete User modal) and rewired UI interactions using `addEventListener` inside the module so handlers are module-scoped instead of being attached to `window`.
- Extended `frontend/js/services/apiService.js` with settings/admin endpoints consumed by the module (for example: `getUser`, `getUsers`, `updateUser`, `deleteUser`, `getSiteSettings`, `saveSiteSettings`, `getAuditTrail`, `triggerNotifications`) and preserved the `window.api` shim for backward compatibility.
- Fixed a porting-induced syntax/parsing issue (unbalanced brace) and removed a leftover global (`window.currentDeleteUserId`) in favor of a module-scoped `currentDeleteUserId` to avoid leaking state.
- The refactored module parses without syntax errors. Runtime smoke tests are planned/encouraged (see next steps) and final decommission of the legacy `script.js` will follow once runtime verification is complete.
- _Files: `frontend/settings-new.html`, `frontend/settings-new.js`, `frontend/js/services/apiService.js`_
## 1.0.3 - 2025-10-31
## 1.0.2 - 2025-10-30
+1 -1
View File
@@ -112,7 +112,7 @@ COPY --chown=warracker:warracker backend/migrations/ ./migrations/
COPY --chown=warracker:warracker locales/ ./locales/
COPY --chown=warracker:warracker locales/ /var/www/html/locales/
# 4. Frontend (static files, no build step)
# 4. Frontend (bundled in one instruction)
COPY --chown=warracker:warracker frontend/ /var/www/html/
# 5. Backend (bundled in one instruction)
+23 -25
View File
@@ -7,33 +7,31 @@
<title data-i18n="about.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">
<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?v=20250119004"> <!-- Main stylesheet -->
<link rel="stylesheet" href="style.css?v=20250119004"> <!-- Main stylesheet -->
<link rel="stylesheet" href="styles.css?v=20250119001"> <!-- Additional styles -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css"> <!-- Font Awesome -->
<link rel="stylesheet" href="./header-fix.css?v=20250119001"> <!-- Header specific styles -->
<link rel="stylesheet" href="header-fix.css?v=20250119001"> <!-- Header specific styles -->
<!-- Mobile Header specific styles -->
<link rel="stylesheet" href="./mobile-header.css?v=20250119002">
<link rel="stylesheet" href="mobile-header.css?v=20250119002">
<!-- Scripts loaded in head -->
<script src="./theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<script type="module" src="./js/services/authService.js"></script>
<script type="module" src="./js/services/apiService.js"></script>
<script src="./script.js?v=20250119001" defer></script>
<script type="module" src="./js/index.js"></script>
<script src="theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<script src="include-auth-new.js?v=20250119001"></script> <!-- Handles auth state display -->
<script src="fix-auth-buttons-loader.js?v=20250119001"></script> <!-- Fixes auth button display timing -->
<script src="script.js?v=20250119001" defer></script>
<!-- i18next Local Scripts -->
<script src="./js/lib/i18next.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="js/lib/i18next.min.js?v=20250119001"></script>
<script src="js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<!-- i18n initialization script -->
<script src="./js/i18n.js?v=20250119001"></script>
<script src="js/i18n.js?v=20250119001"></script>
<style>
.about-hero {
@@ -335,7 +333,7 @@
<!-- Hero Section -->
<div class="about-hero">
<h1><i class="fas fa-shield-alt"></i> Warracker</h1>
<div class="version" id="versionDisplay" data-i18n="about.version">Version v1.0.3</div>
<div class="version" id="versionDisplay" data-i18n="about.version">Version v1.0.2</div>
<p class="description" data-i18n="about.description">
A comprehensive warranty management system designed to help you track, organize, and manage all your product warranties in one secure, user-friendly platform.
</p>
@@ -412,16 +410,16 @@
</div>
<!-- Scripts loaded at the end of body -->
<script src="auth.js?v=20250119001"></script>
<!-- Version Checker -->
<script src="./version-checker.js?v=20251030001" defer></script> <!-- Version checker script -->
<script src="version-checker.js?v=20251030001" defer></script> <!-- Version checker script -->
<!-- Footer Width Fix -->
<script src="./footer-fix.js?v=20251024001"></script>
<script src="footer-fix.js?v=20251024001"></script>
<!-- Footer Content Manager -->
<script src="./footer-content.js?v=20250119001"></script>
<script src="footer-content.js?v=20250119001"></script>
<!-- Powered by Warracker Footer -->
<footer class="warracker-footer" id="warrackerFooter">
@@ -436,7 +434,7 @@
// Update version display dynamically
const versionDisplay = document.getElementById('versionDisplay');
if (versionDisplay && window.i18next) {
const currentVersion = '1.0.3'; // This should match version-checker.js
const currentVersion = '1.0.2'; // This should match version-checker.js
versionDisplay.textContent = window.i18next.t('about.version') + ' v' + currentVersion;
}
+224
View File
@@ -0,0 +1,224 @@
/**
* Authentication functionality for Warracker (New UI)
* Handles user login, logout, and authentication state management for the new UI design
*/
// Authentication state
let currentUser = null;
let authToken = null;
// Initialize authentication
document.addEventListener('DOMContentLoaded', () => {
console.log('Initializing auth-new.js for new UI');
// Initial check of authentication state
checkAuthState();
// Set up periodic check of auth state (every 30 seconds)
setInterval(checkAuthState, 30000);
// Set up logout functionality
setupLogout();
});
/**
* Set up logout functionality
*/
function setupLogout() {
// Look for logout button in the new UI
const logoutButton = document.querySelector('.logout-btn');
if (logoutButton) {
console.log('Found logout button in new UI');
// Add click event to logout
logoutButton.addEventListener('click', (e) => {
e.preventDefault();
logout();
});
}
}
/**
* Check if user is authenticated and update UI accordingly
*/
function checkAuthState() {
console.log('Checking auth state for new UI');
// Get auth token from localStorage
authToken = localStorage.getItem('auth_token');
const userInfo = localStorage.getItem('user_info');
if (authToken && userInfo) {
try {
currentUser = JSON.parse(userInfo);
updateUIForAuthenticatedUser();
validateToken();
} catch (error) {
console.error('Error parsing user info:', error);
clearAuthData();
updateUIForUnauthenticatedUser();
}
} else {
updateUIForUnauthenticatedUser();
}
}
/**
* Update UI elements for authenticated user in the new UI
*/
function updateUIForAuthenticatedUser() {
console.log('Updating UI for authenticated user');
// Find login/register buttons in the new UI based on the screenshot
const loginButton = document.querySelector('a[href="login.html"], a.login');
const registerButton = document.querySelector('a[href="register.html"], a.register');
// Hide login/register buttons if they exist
if (loginButton) {
console.log('Hiding login button');
loginButton.style.display = 'none';
}
if (registerButton) {
console.log('Hiding register button');
registerButton.style.display = 'none';
}
// Show user info if it exists
const userDisplay = document.querySelector('.user-display');
if (userDisplay && currentUser) {
let displayName = currentUser.username || 'User';
userDisplay.textContent = displayName;
userDisplay.style.display = 'inline-block';
}
}
/**
* Update UI elements for unauthenticated user in the new UI
*/
function updateUIForUnauthenticatedUser() {
console.log('Updating UI for unauthenticated user');
// Find login/register buttons in the new UI based on the screenshot
const loginButton = document.querySelector('a[href="login.html"], a.login');
const registerButton = document.querySelector('a[href="register.html"], a.register');
// Show login/register buttons if they exist
if (loginButton) {
console.log('Showing login button');
loginButton.style.display = 'inline-block';
}
if (registerButton) {
console.log('Showing register button');
registerButton.style.display = 'inline-block';
}
// Hide user info if it exists
const userDisplay = document.querySelector('.user-display');
if (userDisplay) {
userDisplay.style.display = 'none';
}
}
/**
* Logout user
*/
async function logout() {
try {
console.log('Logging out user');
// Call logout API
const response = await fetch('/api/auth/logout', {
method: 'POST',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
}
});
// Clear auth data regardless of API response
clearAuthData();
updateUIForUnauthenticatedUser();
// Show success message
console.log('Logged out successfully');
// Reload page to refresh UI
window.location.reload();
} catch (error) {
console.error('Logout error:', error);
// Still clear auth data even if API call fails
clearAuthData();
updateUIForUnauthenticatedUser();
console.log('Logged out with errors');
}
}
/**
* Clear authentication data from localStorage
*/
function clearAuthData() {
localStorage.removeItem('auth_token');
localStorage.removeItem('user_info');
authToken = null;
currentUser = null;
}
/**
* Validate token with the server
*/
async function validateToken() {
if (!authToken) {
clearAuthData();
updateUIForUnauthenticatedUser();
return;
}
try {
// Use the full URL to avoid path issues
const apiUrl = window.location.origin + '/api/auth/validate-token';
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Authorization': `Bearer ${authToken}`
}
});
if (!response.ok) {
const errorData = await response.json();
console.error('Token validation failed:', errorData.message);
throw new Error(errorData.message || 'Invalid token');
}
// Token is valid, update last active time
const data = await response.json();
if (data.user) {
currentUser = data.user;
localStorage.setItem('user_info', JSON.stringify(currentUser));
updateUIForAuthenticatedUser();
}
return true;
} catch (error) {
console.error('Token validation error:', error);
clearAuthData();
updateUIForUnauthenticatedUser();
return false;
}
}
// Export authentication functions for use in other scripts
window.authNew = {
isAuthenticated: () => !!authToken,
getCurrentUser: () => currentUser,
getToken: () => authToken,
checkAuthState,
logout
};
+9 -11
View File
@@ -4,10 +4,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Authenticating...</title>
<link rel="stylesheet" href="./style.css?v=20250119004">
<script src="./theme-loader.js?v=20250119001"></script>
<script type="module" src="./js/services/authService.js"></script>
<script type="module" src="./js/services/apiService.js"></script>
<link rel="stylesheet" href="style.css?v=20250119004">
<script src="theme-loader.js?v=20250119001"></script>
<style>
body {
display: flex;
@@ -161,20 +159,20 @@
</script>
<!-- i18next Local Scripts -->
<script src="./js/lib/i18next.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="js/lib/i18next.min.js?v=20250119001"></script>
<script src="js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<!-- i18n Configuration -->
<script src="./js/i18n.js?v=20250119001"></script>
<script src="js/i18n.js?v=20250119001"></script>
<script src="./auth-redirect.js?v=20250119001"></script>
<script src="auth-redirect.js?v=20250119001"></script>
<!-- Footer Width Fix -->
<script src="./footer-fix.js?v=20251024001"></script>
<script src="footer-fix.js?v=20251024001"></script>
<!-- Footer Content Manager -->
<script src="./footer-content.js?v=20250119001"></script>
<script src="footer-content.js?v=20250119001"></script>
<!-- Powered by Warracker Footer -->
<footer class="warracker-footer" id="warrackerFooter">
+364
View File
@@ -0,0 +1,364 @@
/**
* Authentication functionality for Warracker
* Handles user login, logout, and authentication state management
*/
class AuthManager {
constructor() {
this.token = null;
this.currentUser = null;
this.onLogoutCallbacks = [];
// Initial state load from localStorage
this.token = localStorage.getItem('auth_token');
const userInfoString = localStorage.getItem('user_info');
if (userInfoString) {
try {
this.currentUser = JSON.parse(userInfoString);
} catch (e) {
console.error('Auth.js: Corrupt user_info in localStorage. Clearing.');
this.currentUser = null;
localStorage.removeItem('user_info');
// Consider clearing token as well if user_info is corrupt
// localStorage.removeItem('auth_token');
// this.token = null;
}
}
console.log('[Auth.js] Initial state:', { token: this.token ? 'present' : 'null', currentUser: this.currentUser });
}
isAuthenticated() {
// User is authenticated if both token and currentUser (with an id) are present
return !!(this.token && this.currentUser && this.currentUser.id);
}
getCurrentUser() {
return this.currentUser;
}
getToken() {
// Always return the current state of this.token, which should be synced with localStorage
return this.token;
}
clearAuthData() {
console.log('[Auth.js] Clearing auth data.');
localStorage.removeItem('auth_token');
localStorage.removeItem('user_info');
this.token = null;
this.currentUser = null;
this.onLogoutCallbacks.forEach(cb => cb());
}
onLogout(callback) {
if (typeof callback === 'function') {
this.onLogoutCallbacks.push(callback);
}
}
async checkAuthState(isInitialLoad = false) {
console.log('[Auth.js] checkAuthState called. Initial load:', isInitialLoad);
this.token = localStorage.getItem('auth_token'); // Re-read token, might have changed (e.g. by auth-redirect.js)
const userInfoString = localStorage.getItem('user_info');
this.currentUser = null; // Reset before check
if (userInfoString) {
try {
this.currentUser = JSON.parse(userInfoString);
} catch (e) {
console.error('Auth.js: Failed to parse user_info from localStorage during checkAuthState. Clearing auth data.', e);
this.clearAuthData(); // Clear potentially corrupt data
this.updateUIBasedOnAuthState(); // Update UI to reflect logged-out state
return; // Exit early
}
}
if (this.token) {
// If token exists, try to validate it and fetch/confirm user_info
// This is crucial if user_info was missing or to refresh/validate existing user_info
console.log('[Auth.js] Token found. Validating and fetching user info...');
try {
const response = await fetch('/api/auth/validate-token', {
headers: { 'Authorization': `Bearer ${this.token}` }
});
if (response.ok) {
const data = await response.json();
if (data.valid && data.user && data.user.id) {
this.currentUser = data.user;
localStorage.setItem('user_info', JSON.stringify(this.currentUser)); // Ensure localStorage is up-to-date
console.log('[Auth.js] Token validated, user_info updated/confirmed:', this.currentUser);
} else {
console.warn('[Auth.js] Token validation failed or user data invalid from API. Clearing auth data.');
this.clearAuthData();
}
} else {
console.warn(`[Auth.js] Token validation API call failed (status: ${response.status}). Clearing auth data.`);
this.clearAuthData();
}
} catch (error) {
console.error('[Auth.js] Error validating token / fetching user info:', error);
this.clearAuthData();
}
} else {
// No token, ensure everything is cleared
if (this.currentUser) { // If there was user_info but no token, clear user_info
console.log('[Auth.js] No token found, but user_info was present. Clearing user_info.');
this.clearAuthData();
}
}
this.updateUIBasedOnAuthState();
}
updateUIBasedOnAuthState() {
const isAuthenticated = this.isAuthenticated();
this._updateDOMForAuthState(isAuthenticated, this.currentUser);
this.dispatchAuthStateEvent(isAuthenticated, this.currentUser);
}
_updateDOMForAuthState(isAuthenticated, user) {
const authContainer = document.getElementById('authContainer');
const userMenu = document.getElementById('userMenu');
const userDisplayName = document.getElementById('userDisplayName');
const userNameMenu = document.getElementById('userName');
const userEmailMenu = document.getElementById('userEmail');
const logoutMenuItem = document.getElementById('logoutMenuItem');
// Select all potential login/register buttons more broadly
const loginButtons = document.querySelectorAll('a[href="login.html"], .login-btn');
const registerButtons = document.querySelectorAll('a[href="register.html"], .register-btn');
const genericAuthButtonsContainers = document.querySelectorAll('.auth-buttons');
if (isAuthenticated && user) {
console.log('Auth.js: Updating UI for AUTHENTICATED user:', user);
if (authContainer) { authContainer.style.display = 'none'; authContainer.style.visibility = 'hidden'; }
if (userMenu) {
userMenu.style.display = 'block'; // Or 'flex' based on CSS
userMenu.style.visibility = 'visible';
const displayNameText = user.first_name || user.username || 'User';
const fullNameText = `${user.first_name || ''} ${user.last_name || ''}`.trim() || user.username || 'User Name';
if (userDisplayName) userDisplayName.textContent = displayNameText;
if (userNameMenu) userNameMenu.textContent = fullNameText;
if (userEmailMenu && user.email) userEmailMenu.textContent = user.email;
}
loginButtons.forEach(btn => { btn.style.display = 'none'; btn.style.visibility = 'hidden'; });
registerButtons.forEach(btn => { btn.style.display = 'none'; btn.style.visibility = 'hidden'; });
genericAuthButtonsContainers.forEach(container => {
if (container.id !== 'authContainer') { // Avoid double-hiding if authContainer also has .auth-buttons
container.style.display = 'none'; container.style.visibility = 'hidden';
}
});
if (logoutMenuItem) {
logoutMenuItem.style.display = 'flex'; // Assuming it's a flex item
// Ensure logout listener is attached (can be done once in constructor or DOMContentLoaded)
}
} else {
console.log('Auth.js: Updating UI for UNAUTHENTICATED user.');
if (authContainer) { authContainer.style.display = 'flex'; authContainer.style.visibility = 'visible'; }
if (userMenu) { userMenu.style.display = 'none'; userMenu.style.visibility = 'hidden'; }
// Reset user display names if they exist
if (userDisplayName) userDisplayName.textContent = 'User';
if (userNameMenu) userNameMenu.textContent = 'User Name';
if (userEmailMenu) userEmailMenu.textContent = 'user@example.com';
loginButtons.forEach(btn => { btn.style.display = 'inline-block'; btn.style.visibility = 'visible'; });
registerButtons.forEach(btn => { btn.style.display = 'inline-block'; btn.style.visibility = 'visible'; });
genericAuthButtonsContainers.forEach(container => {
if (container.id !== 'authContainer') {
container.style.display = 'flex'; // Or 'block'
container.style.visibility = 'visible';
}
});
if (logoutMenuItem) { logoutMenuItem.style.display = 'none'; }
}
}
dispatchAuthStateEvent(isAuthenticated, user) {
console.log('[Auth.js] Dispatching authStateReady event', { isAuthenticated, user });
// Ensure this event is dispatched after the current call stack clears
setTimeout(() => {
window.dispatchEvent(new CustomEvent('authStateReady', {
detail: { isAuthenticated, user }
}));
}, 0);
}
async login(username, password) {
// Assuming showLoading/hideLoading are global or part of another module
if (typeof showLoading === 'function') showLoading();
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await response.json();
if (response.ok) {
this.token = data.token;
this.currentUser = data.user;
localStorage.setItem('auth_token', this.token);
localStorage.setItem('user_info', JSON.stringify(this.currentUser));
this.updateUIBasedOnAuthState();
return data; // Return data for login.js to handle redirect
} else {
throw new Error(data.message || 'Login failed');
}
} catch (error) {
this.clearAuthData(); // Ensure state is cleared on login failure
this.updateUIBasedOnAuthState();
throw error; // Re-throw for login.js to handle
} finally {
if (typeof hideLoading === 'function') hideLoading();
}
}
async logout() {
if (typeof showLoading === 'function') showLoading();
const currentTokenForApiCall = this.token; // Use current token for API call
this.clearAuthData(); // Clear local state immediately
this.updateUIBasedOnAuthState(); // Update UI to logged-out state
try {
if (currentTokenForApiCall) {
await fetch('/api/auth/logout', {
method: 'POST',
headers: { 'Authorization': `Bearer ${currentTokenForApiCall}` }
});
console.log('[Auth.js] Logout API call successful.');
}
} catch (error) {
console.error('[Auth.js] Logout API call failed, but user is logged out locally.', error);
} finally {
if (typeof hideLoading === 'function') hideLoading();
// Redirect to login page after all operations
if (window.location.pathname !== '/login.html') {
window.location.href = 'login.html';
}
}
}
addAuthHeader(options = {}) {
const token = this.getToken();
if (!token) return options;
const headers = options.headers || {};
return { ...options, headers: { ...headers, 'Authorization': `Bearer ${token}`}};
}
}
// Initialize and export
window.auth = new AuthManager();
// Initial check on DOMContentLoaded
document.addEventListener('DOMContentLoaded', async () => {
console.log('[Auth.js] DOMContentLoaded - performing initial async auth state check.');
await window.auth.checkAuthState(true); // Ensure auth state is processed first
// Setup user menu toggle
const userMenuBtn_original = document.getElementById('userMenuBtn');
const userMenuDropdown = document.getElementById('userMenuDropdown');
if (userMenuBtn_original && userMenuDropdown) {
console.log('[Auth.js] Setting up user menu. Button:', userMenuBtn_original, 'Dropdown:', userMenuDropdown);
// Robust listener attachment: clone the button to remove any prior listeners
const userMenuBtn = userMenuBtn_original.cloneNode(true);
userMenuBtn_original.parentNode.replaceChild(userMenuBtn, userMenuBtn_original);
userMenuBtn.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent click from immediately closing due to document listener
console.log('[Auth.js] userMenuBtn clicked - DEBUG INFO:', {
userMenuDropdown: !!userMenuDropdown,
dropdownClassList: userMenuDropdown ? Array.from(userMenuDropdown.classList) : 'not found',
hasActiveClass: userMenuDropdown ? userMenuDropdown.classList.contains('active') : 'dropdown not found',
buttonId: userMenuBtn.id,
dropdownId: userMenuDropdown ? userMenuDropdown.id : 'not found'
});
userMenuDropdown.classList.toggle('active');
const isNowActive = userMenuDropdown.classList.contains('active');
console.log('[Auth.js] User menu toggled via userMenuBtn. Active:', isNowActive);
// Add a temporary debug check to see if it gets closed immediately
setTimeout(() => {
const stillActive = userMenuDropdown.classList.contains('active');
console.log('[Auth.js] User menu status after 100ms:', stillActive);
if (isNowActive && !stillActive) {
console.warn('[Auth.js] User menu was closed immediately! Possible global click interference.');
}
}, 100);
});
console.log('[Auth.js] User menu click listener attached to userMenuBtn.');
} else {
console.warn('[Auth.js] User menu button (userMenuBtn) or dropdown (userMenuDropdown) not found. Menu interactivity might be affected.');
}
// Setup settings gear menu toggle (if elements exist on the current page)
const settingsBtn_original = document.getElementById('settingsBtn');
const settingsMenu = document.getElementById('settingsMenu'); // The dropdown menu itself
if (settingsBtn_original && settingsMenu) {
const settingsBtn = settingsBtn_original.cloneNode(true);
settingsBtn_original.parentNode.replaceChild(settingsBtn, settingsBtn_original);
settingsBtn.addEventListener('click', function(e) {
e.stopPropagation();
settingsMenu.classList.toggle('active');
console.log('[Auth.js] Settings menu toggled via settingsBtn.');
});
console.log('[Auth.js] Settings menu click listener attached to settingsBtn.');
}
// Global click listener to close dropdowns - ensure this is added only once
if (!window._authJsGlobalClickListenerAdded) {
document.addEventListener('click', (e) => {
console.log('[Auth.js] Global click detected on:', e.target);
// Re-fetch elements by ID inside the listener to ensure they are current
const currentDropdown = document.getElementById('userMenuDropdown');
const currentButton = document.getElementById('userMenuBtn'); // Use the standardized ID
if (currentDropdown && currentButton && currentDropdown.classList.contains('active')) {
console.log('[Auth.js] User menu is active, checking if click is outside...');
const isOutsideDropdown = !currentDropdown.contains(e.target);
const isOutsideButton = !currentButton.contains(e.target);
console.log('[Auth.js] Click outside dropdown:', isOutsideDropdown, 'outside button:', isOutsideButton);
if (isOutsideDropdown && isOutsideButton) {
currentDropdown.classList.remove('active');
console.log('[Auth.js] User menu closed by global click.');
}
}
const currentSettingsMenu = document.getElementById('settingsMenu');
const currentSettingsBtn = document.getElementById('settingsBtn');
if (currentSettingsMenu && currentSettingsBtn && currentSettingsMenu.classList.contains('active') &&
!currentSettingsMenu.contains(e.target) && !currentSettingsBtn.contains(e.target)) {
currentSettingsMenu.classList.remove('active');
console.log('[Auth.js] Settings menu closed by global click.');
}
});
window._authJsGlobalClickListenerAdded = true;
console.log('[Auth.js] Global click listener for dropdowns added.');
}
// Attach logout listener to logout menu item
const logoutMenuItem_original = document.getElementById('logoutMenuItem');
if (logoutMenuItem_original) {
const logoutMenuItem = logoutMenuItem_original.cloneNode(true); // Ensures fresh listener
logoutMenuItem_original.parentNode.replaceChild(logoutMenuItem, logoutMenuItem_original);
logoutMenuItem.addEventListener('click', () => {
console.log('[Auth.js] Logout menu item clicked.');
window.auth.logout();
});
console.log('[Auth.js] Logout menu item listener attached.');
}
});
+48
View File
@@ -0,0 +1,48 @@
/**
* Script to load the fix-auth-buttons.js script on all pages
* This script should be included in the head of each HTML file
*/
// Log authentication status for debugging
console.log('fix-auth-buttons-loader.js is running');
console.log('Auth token exists:', !!localStorage.getItem('auth_token'));
console.log('User info exists:', !!localStorage.getItem('user_info'));
// Execute immediately to hide buttons as soon as possible
if (localStorage.getItem('auth_token')) {
console.log('User is logged in, attempting to hide login/register buttons immediately');
// Hide auth container if it exists
const authContainer = document.getElementById('authContainer');
if (authContainer) {
console.log('Found authContainer, hiding it');
authContainer.style.display = 'none';
}
// Hide individual buttons if they exist
const loginButtons = document.querySelectorAll('a[href="login.html"], .login-btn, .auth-btn.login-btn');
const registerButtons = document.querySelectorAll('a[href="register.html"], .register-btn, .auth-btn.register-btn');
console.log('Found login buttons:', loginButtons.length);
console.log('Found register buttons:', registerButtons.length);
loginButtons.forEach(button => button.style.display = 'none');
registerButtons.forEach(button => button.style.display = 'none');
// Show user menu if it exists
const userMenu = document.getElementById('userMenu');
if (userMenu) {
console.log('Found userMenu, showing it');
userMenu.style.display = 'block';
}
}
// Create a script element to load the actual fix script
const script = document.createElement('script');
script.src = 'fix-auth-buttons.js';
script.async = true;
// Add the script to the document
document.head.appendChild(script);
console.log('Added fix-auth-buttons.js script to the page');
+76
View File
@@ -0,0 +1,76 @@
/**
* Script to fix the login and register buttons in the new UI
* This script specifically targets the buttons shown in the screenshot
*/
// Log execution
console.log('fix-auth-buttons.js loaded and executing');
// Function to check if user is authenticated
function isAuthenticated() {
const token = localStorage.getItem('auth_token');
// console.log('Auth token check:', !!token); // Keep console logs minimal here if auth.js is primary
return !!token;
}
// Function to find elements by text content
function getElementsByText(selector, text) {
const elements = document.querySelectorAll(selector);
return Array.prototype.filter.call(elements, element => element.textContent.trim() === text);
}
// Function to hide login and register buttons if user is authenticated
function updateAuthButtons() {
// console.log('fix-auth-buttons.js: updateAuthButtons executing...'); // Keep console logs minimal here
if (isAuthenticated()) {
// console.log('fix-auth-buttons.js: User is authenticated, hiding login/register buttons');
const loginButtons = document.querySelectorAll('a[href="login.html"], .login-btn, .auth-btn.login-btn');
const registerButtons = document.querySelectorAll('a[href="register.html"], .register-btn, .auth-btn.register-btn');
const authContainer = document.getElementById('authContainer');
const userMenu = document.getElementById('userMenu'); // Ensure this ID is consistent or use userMenuBtn's parent
loginButtons.forEach(button => { button.style.display = 'none'; button.style.visibility = 'hidden'; });
registerButtons.forEach(button => { button.style.display = 'none'; button.style.visibility = 'hidden'; });
if (authContainer) { authContainer.style.display = 'none'; authContainer.style.visibility = 'hidden';}
if (userMenu) { userMenu.style.display = 'block'; userMenu.style.visibility = 'visible'; }
const userInfo = localStorage.getItem('user_info');
if (userInfo) {
try {
const user = JSON.parse(userInfo);
const displayName = user.first_name || user.username || 'User';
const userDisplayName = document.getElementById('userDisplayName');
if (userDisplayName) userDisplayName.textContent = displayName;
const userNameMenu = document.getElementById('userName');
if (userNameMenu) {
userNameMenu.textContent = `${user.first_name || ''} ${user.last_name || ''}`.trim() || user.username || 'User Name';
}
const userEmailMenu = document.getElementById('userEmail');
if (userEmailMenu && user.email) userEmailMenu.textContent = user.email;
} catch (error) { /* console.error('fix-auth-buttons.js: Error parsing user info:', error); */ }
}
} else {
// console.log('fix-auth-buttons.js: User is not authenticated, showing login/register buttons');
const loginButtons = document.querySelectorAll('a[href="login.html"], .login-btn, .auth-btn.login-btn');
const registerButtons = document.querySelectorAll('a[href="register.html"], .register-btn, .auth-btn.register-btn');
const authContainer = document.getElementById('authContainer');
const userMenu = document.getElementById('userMenu');
loginButtons.forEach(button => { button.style.display = 'inline-block'; button.style.visibility = 'visible'; });
registerButtons.forEach(button => { button.style.display = 'inline-block'; button.style.visibility = 'visible'; });
if (authContainer) { authContainer.style.display = 'flex'; authContainer.style.visibility = 'visible'; }
if (userMenu) { userMenu.style.display = 'none'; userMenu.style.visibility = 'hidden'; }
}
}
// Run immediately
// console.log('Running updateAuthButtons immediately from fix-auth-buttons.js');
updateAuthButtons();
// Update auth buttons when page loads
document.addEventListener('DOMContentLoaded', () => {
// console.log('DOMContentLoaded event triggered in fix-auth-buttons.js, updating auth buttons');
updateAuthButtons();
// REMOVE: setupUserMenuDropdown();
});
+128
View File
@@ -0,0 +1,128 @@
/**
* Immediate Authentication State Handler
*
* This script runs as soon as possible to hide login/register buttons if a user is logged in
* It should be included directly in the HTML before any other scripts
*/
console.log('include-auth-new.js: Running immediate auth check');
// Function to update UI based on auth state (extracted for reuse)
function updateAuthUI() {
if (localStorage.getItem('auth_token')) {
console.log('include-auth-new.js: Updating UI for authenticated user');
// Inject CSS to hide auth buttons and show user menu
const styleId = 'auth-ui-style';
let style = document.getElementById(styleId);
if (!style) {
style = document.createElement('style');
style.id = styleId;
document.head.appendChild(style);
}
style.textContent = `
#authContainer, .auth-buttons, a[href="login.html"], a[href="register.html"],
.login-btn, .register-btn, .auth-btn.login-btn, .auth-btn.register-btn {
display: none !important;
visibility: hidden !important;
}
#userMenu, .user-menu {
display: block !important;
visibility: visible !important;
}
`;
// Update user info display elements immediately
try {
var userInfoStr = localStorage.getItem('user_info');
if (userInfoStr) {
var userInfo = JSON.parse(userInfoStr);
var displayName = userInfo.username || 'User';
var userDisplayName = document.getElementById('userDisplayName');
if (userDisplayName) userDisplayName.textContent = displayName;
var userName = document.getElementById('userName');
if (userName) {
userName.textContent = (userInfo.first_name || '') + ' ' + (userInfo.last_name || '');
if (!userName.textContent.trim()) userName.textContent = userInfo.username || 'User';
}
var userEmail = document.getElementById('userEmail');
if (userEmail && userInfo.email) userEmail.textContent = userInfo.email;
}
} catch (e) {
console.error('include-auth-new.js: Error updating user info display:', e);
}
} else {
console.log('include-auth-new.js: Updating UI for logged-out user');
// Inject CSS to show auth buttons and hide user menu
const styleId = 'auth-ui-style';
let style = document.getElementById(styleId);
if (!style) {
style = document.createElement('style');
style.id = styleId;
document.head.appendChild(style);
}
style.textContent = `
#authContainer, .auth-buttons {
display: flex !important; /* Use flex for container */
visibility: visible !important;
}
a[href="login.html"], a[href="register.html"],
.login-btn, .register-btn, .auth-btn.login-btn, .auth-btn.register-btn {
display: inline-block !important; /* Use inline-block for buttons */
visibility: visible !important;
}
#userMenu, .user-menu {
display: none !important;
visibility: hidden !important;
}
`;
}
}
// Immediately check auth state and update UI
updateAuthUI();
// Listen for changes to localStorage and update UI without reloading
window.addEventListener('storage', function(event) {
if (event.key === 'auth_token' || event.key === 'user_info') {
console.log(`include-auth-new.js: Storage event detected for ${event.key}. Updating UI.`);
updateAuthUI(); // Update UI instead of reloading
// window.location.reload(); // <-- Keep commented out / Remove permanently
}
});
/**
* Script to include the new authentication script in existing HTML files
* This script should be included at the end of the body in each HTML file
*/
// Function to load and execute the new authentication script
function loadAuthNewScript() {
// Create a script element
const script = document.createElement('script');
script.src = 'auth-new.js';
script.async = true;
// Add the script to the document
document.body.appendChild(script);
console.log('Added auth-new.js script to the page');
}
// Function to check if we're using the new UI
function isNewUI() {
// Check for elements that are specific to the new UI
const loginButton = document.querySelector('a[href="login.html"].auth-btn, a.login');
const registerButton = document.querySelector('a[href="register.html"].auth-btn, a.register');
// If we find these elements, we're using the new UI
return !!(loginButton || registerButton);
}
// Load the new authentication script if we're using the new UI
if (isNewUI()) {
console.log('Detected new UI, loading auth-new.js');
loadAuthNewScript();
} else {
console.log('Using old UI, not loading auth-new.js');
}
+22 -52
View File
@@ -2,56 +2,45 @@
<html>
<head>
<!-- Authentication redirect script -->
<script src="./auth-redirect.js?v=20250119001" data-protected="true"></script>
<script src="auth-redirect.js?v=20250119001" data-protected="true"></script>
<!-- Include authentication script first to handle login state immediately -->
<script src="include-auth-new.js?v=20250119001"></script>
<!-- File utilities script for secure file handling -->
<script src="./file-utils.js?v=20250119001"></script>
<script src="file-utils.js?v=20250119001"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Warracker - Warranty Tracker</title>
<!-- Add standard favicon.ico link -->
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<!-- Replace the old favicon link -->
<!-- <link rel="icon" type="image/png" href="img/favicon.png"> -->
<!-- Add new favicon links -->
<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">
<link rel="stylesheet" href="./style.css?v=20250119004">
<script src="./theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<script type="module" src="./js/store.js"></script>
<script type="module" src="./js/services/authService.js"></script>
<script type="module" src="./js/services/apiService.js"></script>
<script type="module" src="./js/components/warrantyCard.js"></script>
<script type="module" src="./js/components/tag.js"></script>
<script type="module" src="./js/components/editModal.js"></script>
<script type="module" src="./js/components/modals.js"></script>
<script type="module" src="./js/components/claims.js"></script>
<script type="module" src="./js/components/notes.js"></script>
<script type="module" src="./js/components/paperless.js"></script>
<script type="module" src="./js/components/ui.js"></script>
<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">
<link rel="stylesheet" href="style.css?v=20250119004">
<script src="theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
<!-- Load header fix styles to ensure consistent header styling -->
<link rel="stylesheet" href="./header-fix.css?v=20250119001">
<link rel="stylesheet" href="header-fix.css?v=20250119001">
<!-- Load fix for auth buttons -->
<script src="fix-auth-buttons-loader.js?v=20250119001"></script>
<!-- Mobile Header specific styles -->
<link rel="stylesheet" href="./mobile-header.css?v=20250119002">
<link rel="stylesheet" href="mobile-header.css?v=20250119002">
<!-- i18next Local Scripts -->
<script src="./js/lib/i18next.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="js/lib/i18next.min.js?v=20250119001"></script>
<script src="js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<!-- i18n Configuration -->
<script src="./js/i18n.js?v=20250119001"></script>
<script src="js/i18n.js?v=20250119001"></script>
<!-- Immediate authentication check script -->
<script>
@@ -106,7 +95,7 @@
</script>
<!-- Registration status check script -->
<script src="registration-status.js?v=20250119001"></script>
</head>
<body>
<!-- Header -->
@@ -1336,9 +1325,8 @@
<div class="loading-spinner"></div>
</div>
<script src="./script.js?v=20250119002" defer></script>
<script type="module" src="./js/index.js"></script>
<script src="auth.js?v=20250119001"></script>
<script src="script.js?v=20250119002"></script>
<script>
// Lightweight UI logic for Filter/Sort popovers (no change to core logic)
document.addEventListener('DOMContentLoaded', function () {
@@ -1647,10 +1635,10 @@
</script>
<!-- Footer Width Fix -->
<script src="./footer-fix.js?v=20251024001"></script>
<script src="footer-fix.js?v=20251024001"></script>
<!-- Footer Content Manager -->
<script src="./footer-content.js?v=20250119001"></script>
<script src="footer-content.js?v=20250119001"></script>
<!-- Powered by Warracker Footer -->
<footer class="warracker-footer" id="warrackerFooter">
@@ -1696,23 +1684,5 @@
});
</script>
<!-- Templates (do not affect layout) -->
<template id="warranty-card-template">
<div class="warranty-card">
<div class="product-name-header">
<h3 class="warranty-title"></h3>
<div class="warranty-actions">
<button class="action-btn edit-btn" title="Edit"><i class="fas fa-edit"></i></button>
<button class="action-btn delete-btn" title="Delete"><i class="fas fa-trash"></i></button>
</div>
</div>
<div class="warranty-content">
<div class="warranty-info"></div>
</div>
<div class="warranty-status-row"><span></span></div>
</div>
</template>
</body>
</html>
</html>
+15 -17
View File
@@ -1,19 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<script src="./auth-redirect.js?v=20250119001" data-protected="false"></script>
<script src="auth-redirect.js?v=20250119001" data-protected="false"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="auth.login_title">Login - Warracker</title>
<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">
<link rel="stylesheet" href="./style.css?v=20250119004">
<script src="./theme-loader.js?v=20250119001"></script>
<script type="module" src="./js/services/authService.js"></script>
<script type="module" src="./js/services/apiService.js"></script>
<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">
<link rel="stylesheet" href="style.css?v=20250119004">
<script src="theme-loader.js?v=20250119001"></script>
<script>
// If user is authenticated and has a theme in user_info (from previous sessions), reflect it early
try {
@@ -24,11 +22,11 @@
} catch (_) {}
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
<script src="./js/lib/i18next.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="./js/i18n.js?v=20250119001"></script>
<script src="js/lib/i18next.min.js?v=20250119001"></script>
<script src="js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="js/i18n.js?v=20250119001"></script>
<script src="registration-status.js?v=20250119001"></script>
</head>
<body class="login-page-body">
@@ -84,7 +82,7 @@
</form>
<div id="ssoSection" style="display: none;">
<div class="separator"><span>OR</span></div>
<a href="./api/oidc/login" id="oidcLoginButton" class="btn btn-secondary btn-block btn-sso" style="text-decoration: none;">
<a href="/api/oidc/login" id="oidcLoginButton" class="btn btn-secondary btn-block btn-sso" style="text-decoration: none;">
<i class="fab fa-openid" style="margin-right: 8px;"></i> <span>Login with SSO Provider</span>
</a>
</div>
@@ -96,7 +94,7 @@
</div>
</div>
<script src="auth.js?v=20250119001"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Check for OIDC errors in URL parameters and display them
+20 -23
View File
@@ -2,37 +2,34 @@
<html>
<head>
<!-- Authentication redirect script -->
<script src="./auth-redirect.js?v=20250119001" data-protected="false"></script>
<script src="auth-redirect.js?v=20250119001" data-protected="false"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="auth.register">Register - 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">
<link rel="stylesheet" href="./style.css?v=20250119004">
<script src="./theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<script type="module" src="./js/services/authService.js"></script>
<script type="module" src="./js/services/apiService.js"></script>
<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">
<link rel="stylesheet" href="style.css?v=20250119004">
<script src="theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
<!-- Load header fix styles to ensure consistent header styling -->
<link rel="stylesheet" href="./header-fix.css?v=20250119001">
<link rel="stylesheet" href="header-fix.css?v=20250119001">
<!-- Mobile Header specific styles -->
<link rel="stylesheet" href="./mobile-header.css?v=20250119002">
<link rel="stylesheet" href="mobile-header.css?v=20250119002">
<!-- i18next Local Scripts -->
<script src="./js/lib/i18next.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="js/lib/i18next.min.js?v=20250119001"></script>
<script src="js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<!-- i18n initialization script -->
<script src="./js/i18n.js?v=20250119001"></script>
<script src="js/i18n.js?v=20250119001"></script>
<!-- Load fix for auth buttons -->
<script src="./script.js?v=20250119001" defer></script>
<script type="module" src="./js/index.js"></script>
<script src="fix-auth-buttons-loader.js?v=20250119001"></script>
<script src="script.js?v=20250119001" defer></script>
<style>
.auth-container {
max-width: 500px;
@@ -240,7 +237,7 @@
</style>
<!-- Registration status check script -->
<script src="registration-status.js?v=20250119001"></script>
<script>
// Additional handling for the register page
document.addEventListener('DOMContentLoaded', function() {
@@ -380,7 +377,7 @@
</div>
<!-- Script for authentication -->
<script src="auth.js?v=20250119001"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const registerForm = document.getElementById('registerForm');
@@ -622,10 +619,10 @@
</script>
<!-- Footer Width Fix -->
<script src="./footer-fix.js?v=20251024001"></script>
<script src="footer-fix.js?v=20251024001"></script>
<!-- Footer Content Manager -->
<script src="./footer-content.js?v=20250119001"></script>
<script src="footer-content.js?v=20250119001"></script>
<!-- Powered by Warracker Footer -->
<footer class="warracker-footer" id="warrackerFooter">
+92
View File
@@ -0,0 +1,92 @@
/**
* Registration Status Checker
*
* This script checks if registration is enabled on the site and hides registration buttons/links if disabled.
* Include this script in all pages that have registration links or buttons.
*/
document.addEventListener('DOMContentLoaded', function() {
// Only check if user is not logged in
if (!localStorage.getItem('auth_token')) {
checkRegistrationStatus();
}
});
/**
* Check if registration is enabled and hide registration elements if disabled
*/
function checkRegistrationStatus() {
fetch('/api/auth/registration-status')
.then(response => response.json())
.then(data => {
if (!data.enabled) {
// Hide all registration links and buttons
hideRegistrationElements();
}
})
.catch(error => {
console.error('Error checking registration status:', error);
});
}
/**
* Hide all registration-related elements on the page
*/
function hideRegistrationElements() {
// Hide elements with specific classes
const elements = [
'.register-btn', // Main navigation register button
'a[href="register.html"]', // Links to register page
'a[href="./register.html"]', // Links to register page (relative)
'a[href="/register.html"]' // Links to register page (root relative)
];
// Apply to all matching elements
elements.forEach(selector => {
document.querySelectorAll(selector).forEach(element => {
element.style.display = 'none';
});
});
// Special case for auth container in the header
const authContainer = document.getElementById('authContainer');
if (authContainer) {
// Check if it has multiple children and at least one is hidden
const children = authContainer.children;
let visibleCount = 0;
for (let i = 0; i < children.length; i++) {
if (children[i].style.display !== 'none') {
visibleCount++;
}
}
// If there's only one visible child, adjust the container styling
if (visibleCount === 1) {
authContainer.style.justifyContent = 'flex-end';
}
}
// Special case for auth links in login/register pages
const authLinks = document.querySelector('.auth-links');
if (authLinks) {
const links = authLinks.querySelectorAll('a');
links.forEach(link => {
if (link.textContent === 'Create Account' ||
link.href.includes('register.html')) {
link.style.display = 'none';
}
});
// Add a message about registration being disabled if we're on the login page
if (window.location.pathname.includes('login.html')) {
const infoMessage = document.createElement('div');
infoMessage.className = 'registration-info';
infoMessage.innerHTML = '<small>New account registration is currently disabled</small>';
infoMessage.style.color = 'var(--text-muted)';
infoMessage.style.fontSize = '0.8em';
infoMessage.style.marginTop = '10px';
authLinks.appendChild(infoMessage);
}
}
}
+19 -22
View File
@@ -5,31 +5,28 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="auth.reset_password_title">Warracker - Reset Password</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">
<link rel="stylesheet" href="./style.css?v=20250119004">
<script src="./theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<script type="module" src="./js/services/authService.js"></script>
<script type="module" src="./js/services/apiService.js"></script>
<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">
<link rel="stylesheet" href="style.css?v=20250119004">
<script src="theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
<!-- Load header fix styles to ensure consistent header styling -->
<link rel="stylesheet" href="./header-fix.css?v=20250119001">
<link rel="stylesheet" href="header-fix.css?v=20250119001">
<!-- Mobile Header specific styles -->
<link rel="stylesheet" href="./mobile-header.css?v=20250119002">
<link rel="stylesheet" href="mobile-header.css?v=20250119002">
<!-- i18next Local Scripts -->
<script src="./js/lib/i18next.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="js/lib/i18next.min.js?v=20250119001"></script>
<script src="js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<!-- i18n initialization script -->
<script src="./js/i18n.js?v=20250119001"></script>
<script src="js/i18n.js?v=20250119001"></script>
<!-- Load fix for auth buttons -->
<script src="./script.js?v=20250119001" defer></script>
<script type="module" src="./js/index.js"></script>
<script src="fix-auth-buttons-loader.js?v=20250119001"></script>
<script src="script.js?v=20250119001" defer></script>
<style>
.auth-container {
max-width: 400px;
@@ -100,7 +97,7 @@
</style>
<!-- Registration status check script -->
<script src="registration-status.js?v=20250119001"></script>
</head>
<body>
<!-- Header -->
@@ -262,13 +259,13 @@
document.addEventListener('DOMContentLoaded', initializeTheme);
</script>
<script src="auth.js?v=20250119001"></script>
<!-- Footer Width Fix -->
<script src="./footer-fix.js?v=20251024001"></script>
<script src="footer-fix.js?v=20251024001"></script>
<!-- Footer Content Manager -->
<script src="./footer-content.js?v=20250119001"></script>
<script src="footer-content.js?v=20250119001"></script>
<!-- Powered by Warracker Footer -->
<footer class="warracker-footer" id="warrackerFooter">
+19 -22
View File
@@ -5,31 +5,28 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="auth.set_new_password_title">Warracker - Set New Password</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">
<link rel="stylesheet" href="./style.css?v=20250119004">
<script src="./theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<script type="module" src="./js/services/authService.js"></script>
<script type="module" src="./js/services/apiService.js"></script>
<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">
<link rel="stylesheet" href="style.css?v=20250119004">
<script src="theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
<!-- Load header fix styles to ensure consistent header styling -->
<link rel="stylesheet" href="./header-fix.css?v=20250119001">
<link rel="stylesheet" href="header-fix.css?v=20250119001">
<!-- Mobile Header specific styles -->
<link rel="stylesheet" href="./mobile-header.css?v=20250119002">
<link rel="stylesheet" href="mobile-header.css?v=20250119002">
<!-- i18next Local Scripts -->
<script src="./js/lib/i18next.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="js/lib/i18next.min.js?v=20250119001"></script>
<script src="js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<!-- i18n initialization script -->
<script src="./js/i18n.js?v=20250119001"></script>
<script src="js/i18n.js?v=20250119001"></script>
<!-- Load fix for auth buttons -->
<script src="./script.js?v=20250119001" defer></script>
<script type="module" src="./js/index.js"></script>
<script src="fix-auth-buttons-loader.js?v=20250119001"></script>
<script src="script.js?v=20250119001" defer></script>
<style>
.auth-container {
max-width: 400px;
@@ -157,7 +154,7 @@
</style>
<!-- Registration status check script -->
<script src="registration-status.js?v=20250119001"></script>
</head>
<body>
<!-- Header -->
@@ -429,13 +426,13 @@
</script>
<!-- Scripts -->
<script src="auth.js?v=20250119001"></script>
<!-- Footer Width Fix -->
<script src="./footer-fix.js?v=20251024001"></script>
<script src="footer-fix.js?v=20251024001"></script>
<!-- Footer Content Manager -->
<script src="./footer-content.js?v=20250119001"></script>
<script src="footer-content.js?v=20250119001"></script>
<!-- Powered by Warracker Footer -->
<footer class="warracker-footer" id="warrackerFooter">
+1321 -499
View File
File diff suppressed because it is too large Load Diff
+64 -28
View File
@@ -2,47 +2,48 @@
<html>
<head>
<!-- Authentication redirect script -->
<script src="./auth-redirect.js?v=20250119001" data-protected="true"></script>
<script src="auth-redirect.js?v=20250119001" data-protected="true"></script>
<!-- Include authentication script first to handle login state immediately -->
<script src="include-auth-new.js?v=20250119001"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Settings - 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">
<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">
<!-- Load the main site styles first -->
<link rel="stylesheet" href="./style.css?v=20250119004">
<link rel="stylesheet" href="style.css?v=20250119004">
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
<!-- Then load settings-specific styles -->
<link rel="stylesheet" href="./settings-styles.css?v=20250119001">
<link rel="stylesheet" href="settings-styles.css?v=20250119001">
<!-- Apply theme immediately -->
<script src="./theme-loader.js?v=20250119001"></script>
<script type="module" src="./js/services/authService.js"></script>
<script type="module" src="./js/services/apiService.js"></script>
<script src="theme-loader.js?v=20250119001"></script>
<!-- Load header fix styles last to override any conflicting styles -->
<link rel="stylesheet" href="./header-fix.css?v=20250119001">
<link rel="stylesheet" href="header-fix.css?v=20250119001">
<!-- Mobile Header specific styles -->
<link rel="stylesheet" href="./mobile-header.css?v=20250119002">
<link rel="stylesheet" href="mobile-header.css?v=20250119002">
<!-- Load fix for auth buttons -->
<script src="fix-auth-buttons-loader.js?v=20250119001"></script>
<!-- i18next Local Scripts -->
<script src="./js/lib/i18next.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="js/lib/i18next.min.js?v=20250119001"></script>
<script src="js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<!-- i18n Configuration -->
<script src="./js/i18n.js?v=20250119001"></script>
<script src="js/i18n.js?v=20250119001"></script>
<!-- Temporary debug script -->
<script src="js/i18n-debug.js?v=20250119001"></script>
<!-- Mobile menu logic isolated to avoid collisions on settings -->
<script src="./mobile-menu.js?v=20250119002" defer></script>
<script src="mobile-menu.js?v=20250119002" defer></script>
<!-- Immediate authentication check script -->
<script>
@@ -1076,17 +1077,52 @@
<p style="font-size: 0.8em; color: #666;">User ID: <span id="displayUserId"></span></p>
<div class="modal-footer">
<button type="button" class="btn btn-secondary close-modal">Cancel</button>
<button type="button" id="confirmDeleteUserBtn" class="btn btn-danger">Delete</button>
<button type="button" id="confirmDeleteUserBtn" class="btn btn-danger" onclick="deleteUser()">Delete</button>
</div>
<div style="margin-top: 20px; border-top: 1px solid #ddd; padding-top: 10px;">
<p><small>If the delete button doesn't work, try these alternatives:</small></p>
<ul style="font-size: 0.8em;">
<li><a href="#" id="directDeleteLink" style="color: #dc3545;">Alternative 1: Direct function call</a></li>
<li><a href="#" id="directAPILink" style="color: #dc3545;">Alternative 2: Direct API call</a></li>
<li><a href="#" id="directDeleteLink" style="color: #dc3545;" onclick="deleteUser(); return false;">Alternative 1: Direct function call</a></li>
<li><a href="#" id="directAPILink" style="color: #dc3545;" onclick="directDeleteUserAPI(document.getElementById('deleteUserId').value); return false;">Alternative 2: Direct API call</a></li>
<li><button type="submit" class="btn btn-sm btn-danger">Alternative 3: Form Submit</button></li>
</ul>
<div style="margin-top: 10px;">
<button type="button" id="emergencyDeleteBtn" class="btn btn-sm btn-danger">Emergency Delete</button>
<button type="button" id="emergencyDeleteBtn" class="btn btn-sm btn-danger"
onclick="(function() {
const userId = document.getElementById('deleteUserId').value || document.getElementById('displayUserId').textContent;
if (!userId) {
alert('Error: User ID is missing');
return;
}
const token = localStorage.getItem('auth_token');
if (!token) {
alert('Error: Authentication token is missing');
return;
}
alert('Emergency delete for user ID: ' + userId);
fetch('/api/admin/users/' + userId, {
method: 'DELETE',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
}
})
.then(response => {
alert('Response status: ' + response.status);
if (response.status >= 200 && response.status < 300) {
alert('User deleted successfully');
document.querySelectorAll('.modal-backdrop').forEach(m => m.style.display = 'none');
location.reload();
} else {
alert('Failed to delete user: ' + response.status);
}
})
.catch(error => {
alert('Error: ' + error.message);
});
})(); return false;">
Emergency Delete (Inline JS)
</button>
</div>
</div>
</form>
@@ -1136,14 +1172,14 @@
<div id="toastContainer" class="toast-container"></div>
<!-- Scripts -->
<script type="module" src="./settings-new.js"></script>
<script src="auth.js?v=20250119001"></script>
<script src="settings-new.js?v=20250119001"></script>
<!-- Footer Width Fix -->
<script src="./footer-fix.js?v=20251024001"></script>
<script src="footer-fix.js?v=20251024001"></script>
<!-- Footer Content Manager -->
<script src="./footer-content.js?v=20250119001"></script>
<script src="footer-content.js?v=20250119001"></script>
<!-- Powered by Warracker Footer -->
<footer class="warracker-footer" id="warrackerFooter">
+24 -28
View File
@@ -2,47 +2,43 @@
<html>
<head>
<!-- Authentication redirect script -->
<script src="./auth-redirect.js?v=20250119001" data-protected="true"></script>
<script src="auth-redirect.js?v=20250119001" data-protected="true"></script>
<!-- Include authentication script first to handle login state immediately -->
<script src="include-auth-new.js?v=20250119001"></script>
<!-- File utilities script for secure file handling -->
<script src="./file-utils.js?v=20250119001"></script>
<script src="file-utils.js?v=20250119001"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-i18n="status.title">Status - 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">
<link rel="stylesheet" href="./style.css?v=20250119004">
<script src="./theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<script type="module" src="./js/store.js"></script>
<script type="module" src="./js/services/authService.js"></script>
<script type="module" src="./js/services/apiService.js"></script>
<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">
<link rel="stylesheet" href="style.css?v=20250119004">
<script src="theme-loader.js?v=20250119001"></script> <!-- Apply theme early -->
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css">
<!-- Load header fix styles to ensure consistent header styling -->
<link rel="stylesheet" href="./header-fix.css?v=20250119001">
<link rel="stylesheet" href="header-fix.css?v=20250119001">
<!-- Mobile Header specific styles -->
<link rel="stylesheet" href="./mobile-header.css?v=20250119002">
<link rel="stylesheet" href="mobile-header.css?v=20250119002">
<!-- Chart.js for visualizations -->
<script src="./chart.js?v=20250119001"></script>
<script src="chart.js?v=20250119001"></script>
<!-- i18next Local Scripts -->
<script src="./js/lib/i18next.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="./js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<script src="js/lib/i18next.min.js?v=20250119001"></script>
<script src="js/lib/i18nextHttpBackend.min.js?v=20250119001"></script>
<script src="js/lib/i18nextBrowserLanguageDetector.min.js?v=20250119001"></script>
<!-- i18n initialization script -->
<script src="./js/i18n.js?v=20250119001"></script>
<script src="js/i18n.js?v=20250119001"></script>
<!-- Load fix for auth buttons -->
<script src="./script.js?v=20250119002" defer></script> <!-- Added script.js -->
<script type="module" src="./js/index.js"></script>
<script type="module" src="./status.js?v=20250119001"></script> <!-- Status page specific functionality -->
<script src="fix-auth-buttons-loader.js?v=20250119001"></script>
<script src="script.js?v=20250119002" defer></script> <!-- Added script.js -->
<script src="status.js?v=20250119001" defer></script> <!-- Status page specific functionality -->
<style>
.user-menu {
position: relative;
@@ -826,14 +822,14 @@
<div class="loading-spinner"></div>
</div>
<script type="module" src="./status.js?v=20250119001"></script>
<script src="auth.js?v=20250119001"></script>
<script src="status.js?v=20250119001" defer></script>
<!-- Footer Width Fix -->
<script src="./footer-fix.js?v=20251024001"></script>
<script src="footer-fix.js?v=20251024001"></script>
<!-- Footer Content Manager -->
<script src="./footer-content.js?v=20250119001"></script>
<script src="footer-content.js?v=20250119001"></script>
<!-- Powered by Warracker Footer -->
<footer class="warracker-footer" id="warrackerFooter">
+38 -19
View File
@@ -1,5 +1,3 @@
import apiService from './js/services/apiService.js';
import authService from './js/services/authService.js';
(function() {
// DOM Elements
const loadingIndicator = document.getElementById('loadingIndicator');
@@ -103,13 +101,17 @@ import authService from './js/services/authService.js';
async function fetchStatistics() {
try {
console.log('Checking authentication status... (status.js IIFE)');
if (!authService || !authService.isAuthenticated()) {
if (!window.auth || !window.auth.isAuthenticated()) {
throw new Error('Authentication required.');
}
const token = authService.getToken();
const token = window.auth.getToken();
if (!token) {
throw new Error('Authentication token not available.');
}
const options = {
method: 'GET',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json'}
};
// Check saved view scope preference to determine which API endpoint to use
const savedScope = loadViewScopePreference();
@@ -119,15 +121,17 @@ import authService from './js/services/authService.js';
const apiUrl = shouldUseGlobalView ? GLOBAL_STATISTICS_API_URL : STATISTICS_API_URL;
console.log('Fetching statistics from:', apiUrl, '(Global view preference:', savedScope, ')');
const response = shouldUseGlobalView
? await apiService.request('/api/statistics/global')
: await apiService.request('/api/statistics');
const json = await response.json();
const response = await fetch(apiUrl, options);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to fetch statistics: ${response.status} ${errorText}`);
}
// Update isGlobalView to match the loaded data
isGlobalView = shouldUseGlobalView;
console.log(`[DEBUG] Set isGlobalView to: ${isGlobalView}`);
return json;
return await response.json();
} catch (error) {
console.error('Error fetching statistics (status.js IIFE):', error);
throw error;
@@ -154,10 +158,13 @@ import authService from './js/services/authService.js';
try {
// Check if global view is enabled for this user
const token = authService.getToken();
const token = window.auth.getToken();
if (!token) return;
const response = await apiService.request('/api/settings/global-view-status', { method: 'GET' });
const response = await fetch('/api/settings/global-view-status', {
method: 'GET',
headers: { 'Authorization': `Bearer ${token}` }
});
if (response.ok) {
const result = await response.json();
@@ -336,8 +343,11 @@ import authService from './js/services/authService.js';
try {
// Check if global view is still available
const token = authService.getToken();
const response = await apiService.request('/api/settings/global-view-status', { method: 'GET' });
const token = window.auth.getToken();
const response = await fetch('/api/settings/global-view-status', {
method: 'GET',
headers: { 'Authorization': `Bearer ${token}` }
});
if (response.ok) {
const result = await response.json();
@@ -934,9 +944,14 @@ import authService from './js/services/authService.js';
let allWarrantiesForTimeline = [];
try {
// Try to fetch all warranties for a complete timeline
const token = authService && authService.getToken ? authService.getToken() : null;
const token = window.auth && window.auth.getToken ? window.auth.getToken() : null;
if (token) {
const allWarrantiesResponse = await apiService.request('/api/warranties', { method: 'GET' });
const allWarrantiesResponse = await fetch('/api/warranties', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (allWarrantiesResponse.ok) {
allWarrantiesForTimeline = await allWarrantiesResponse.json();
} else {
@@ -1017,8 +1032,9 @@ import authService from './js/services/authService.js';
async function loadUserPreferences() {
try {
if (!authService || !authService.getToken()) { console.warn('Auth or token not available for prefs in status.js'); return; }
const response = await apiService.request('/api/auth/preferences', { method: 'GET' });
if (!window.auth || !window.auth.getToken()) { console.warn('Auth or token not available for prefs in status.js'); return; }
const token = window.auth.getToken();
const response = await fetch('/api/auth/preferences', { headers: { 'Authorization': `Bearer ${token}` } });
if (response.ok) {
const prefs = await response.json();
if (prefs && prefs.expiring_soon_days !== undefined) { // Check for undefined specifically
@@ -1072,10 +1088,13 @@ import authService from './js/services/authService.js';
// ALWAYS fetch fresh details when editing from the status page to ensure modal has latest data.
console.log(`[DEBUG status.js] Edit from status page for warranty ${warrantyId}. ALWAYS fetching fresh details from /api/debug/warranty/:id.`);
showLoadingSpinner(); // Show spinner for this specific fetch
const token = authService.getToken();
const token = localStorage.getItem('auth_token') || (window.auth && window.auth.getToken());
if (!token) throw new Error('Authentication token not available for fetching fresh details.');
const response = await apiService.request(`/api/debug/warranty/${warrantyId}`, { method: 'GET' });
const response = await fetch(`/api/debug/warranty/${warrantyId}`, {
method: 'GET',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
+7 -7
View File
@@ -4827,18 +4827,18 @@ html.dark-mode .warracker-footer a:hover {
}
}
@media (max-width: 360px) {
@media (max-width: 768px) {
.grid-view .product-photo-thumbnail img {
width: 30px;
height: 30px;
width: 30px;
height: 30px;
}
.list-view .product-photo-thumbnail img {
width: 60px;
height: 60px;
width: 25px;
height: 25px;
}
.table-view .product-photo-thumbnail img {
width: 20px;
height: 20px;
width: 20px;
height: 20px;
}
.product-photo-thumbnail {
margin-right: 5px;
+1
View File
@@ -15,6 +15,7 @@ const urlsToCache = [
// Stylesheets (versioned)
'./style.css?v=20250119004',
'./styles.css?v=20250119001',
'./settings-styles.css?v=20250119001',
'./header-fix.css?v=20250119001',
'./mobile-header.css?v=20250119002',
+1 -1
View File
@@ -1,6 +1,6 @@
// Version checker for Warracker
document.addEventListener('DOMContentLoaded', () => {
const currentVersion = '1.0.3'; // Current version of the application
const currentVersion = '1.0.2'; // Current version of the application
const updateStatus = document.getElementById('updateStatus');
const updateLink = document.getElementById('updateLink');
const versionDisplay = document.getElementById('versionDisplay');