mirror of
https://github.com/sassanix/Warracker.git
synced 2026-01-06 05:29:39 -06:00
Fix global warranties view, add Model Number field, and enhance modal tab responsiveness
* **Fixed:** * Global view on Index page now correctly shows warranties from all users, including archived ones. * Added `GET /api/warranties/global/archived` and unified global queries with correlated subqueries to avoid missing or collapsed rows. * Updated frontend logic to merge archived warranties from the new endpoint when Global scope and Status = “All.” * Bumped `script.js` and service worker cache to ensure clients receive updated logic. * Updated files: `backend/warranties_routes.py`, `frontend/script.js`, `frontend/sw.js`, `frontend/index.html`, `frontend/status.html`. * **Added:** * Introduced **Model Number** field to warranties. * Backend: Added `model_number` column, integrated into GET/POST/PUT routes. * Frontend: Added Model Number input in New/Edit modals and display on warranty cards. * Updated files: `backend/migrations/047_add_model_number_to_warranties.sql`, `backend/warranties_routes.py`, `frontend/index.html`, `frontend/status.html`, `frontend/script.js`, `locales/en/translation.json`. * **Enhanced:** * Improved **Add Warranty modal** tab alignment for responsive layouts (≤740px). * Adjusted tab label size and spacing to prevent wrapping while keeping icons and labels visible. * Ensured consistent five-step progress indicator across all breakpoints. * Updated file: `frontend/style.css`.
This commit is contained in:
@@ -323,7 +323,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 v0.10.1.14</div>
|
||||
<div class="version" id="versionDisplay" data-i18n="about.version">Version v0.10.1.15</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>
|
||||
@@ -421,7 +421,7 @@
|
||||
// Update version display dynamically
|
||||
const versionDisplay = document.getElementById('versionDisplay');
|
||||
if (versionDisplay && window.i18next) {
|
||||
const currentVersion = '0.10.1.14'; // This should match version-checker.js
|
||||
const currentVersion = '0.10.1.15'; // This should match version-checker.js
|
||||
versionDisplay.textContent = window.i18next.t('about.version') + ' v' + currentVersion;
|
||||
}
|
||||
|
||||
|
||||
@@ -415,6 +415,10 @@
|
||||
<input type="text" name="serial_numbers[]" class="form-control" style="margin-bottom: 8px;" data-i18n-placeholder="warranties.enter_serial_number" placeholder="Enter serial number">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="modelNumber" data-i18n="warranties.model_number_optional">Model Number (Optional)</label>
|
||||
<input type="text" id="modelNumber" name="model_number" class="form-control" data-i18n-placeholder="warranties.model_number_placeholder" placeholder="e.g. SM-G991U1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vendor" data-i18n="warranties.vendor_optional">Vendor (Optional)</label>
|
||||
<input type="text" id="vendor" name="vendor" class="form-control" data-i18n-placeholder="warranties.vendor_placeholder" placeholder="e.g. Amazon, Best Buy, etc.">
|
||||
@@ -819,6 +823,10 @@
|
||||
<!-- Serial number inputs will be added dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editModelNumber" data-i18n="warranties.model_number_optional">Model Number (Optional)</label>
|
||||
<input type="text" id="editModelNumber" name="model_number" class="form-control" data-i18n-placeholder="warranties.model_number_placeholder" placeholder="e.g. SM-G991U1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editVendor" data-i18n="warranties.vendor_optional">Vendor (Optional)</label>
|
||||
<input type="text" id="editVendor" name="vendor" class="form-control" data-i18n-placeholder="warranties.vendor_placeholder" placeholder="e.g. Amazon, Best Buy, etc.">
|
||||
@@ -1318,7 +1326,7 @@
|
||||
</div>
|
||||
|
||||
<script src="auth.js?v=20250119001"></script>
|
||||
<script src="script.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 () {
|
||||
|
||||
@@ -936,6 +936,9 @@ let formTabs = []; // Changed from const to let, initialized as empty
|
||||
if (notesModalWarrantyObj.warranty_type) {
|
||||
formData.append('warranty_type', notesModalWarrantyObj.warranty_type);
|
||||
}
|
||||
if (typeof notesModalWarrantyObj.model_number !== 'undefined' && notesModalWarrantyObj.model_number !== null) {
|
||||
formData.append('model_number', notesModalWarrantyObj.model_number);
|
||||
}
|
||||
if (notesModalWarrantyObj.serial_numbers && Array.isArray(notesModalWarrantyObj.serial_numbers)) {
|
||||
notesModalWarrantyObj.serial_numbers.forEach(sn => {
|
||||
if (sn && sn.trim() !== '') {
|
||||
@@ -953,6 +956,11 @@ let formTabs = []; // Changed from const to let, initialized as empty
|
||||
formData.append('tag_ids', JSON.stringify([]));
|
||||
}
|
||||
formData.append('notes', notesValue);
|
||||
// Also include model number value from edit input if present on DOM
|
||||
const editModelNumberInput = document.getElementById('editModelNumber');
|
||||
if (editModelNumberInput && editModelNumberInput.value.trim() !== '') {
|
||||
formData.set('model_number', editModelNumberInput.value.trim());
|
||||
}
|
||||
const response = await fetch(`/api/warranties/${warrantyId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@@ -2182,10 +2190,10 @@ async function loadWarranties(isAuthenticated) { // Added isAuthenticated parame
|
||||
|
||||
// Use the appropriate API endpoint based on saved preference
|
||||
const baseUrl = window.location.origin;
|
||||
// If status is 'archived', use archived endpoint (user-scoped only for now)
|
||||
// If status is 'archived', use archived endpoint (support global vs personal)
|
||||
const isArchivedView = currentFilters && currentFilters.status === 'archived';
|
||||
const apiUrl = isArchivedView
|
||||
? `${baseUrl}/api/warranties/archived`
|
||||
? (shouldUseGlobalView ? `${baseUrl}/api/warranties/global/archived` : `${baseUrl}/api/warranties/archived`)
|
||||
: (shouldUseGlobalView ? `${baseUrl}/api/warranties/global` : `${baseUrl}/api/warranties`);
|
||||
|
||||
console.log(`[DEBUG] Using API endpoint based on saved preference '${savedScope}', archivedView=${isArchivedView}: ${apiUrl}`);
|
||||
@@ -2235,9 +2243,9 @@ async function loadWarranties(isAuthenticated) { // Added isAuthenticated parame
|
||||
// Optionally merge archived items into the "All" view (only in personal scope)
|
||||
let combinedData = Array.isArray(data) ? data : [];
|
||||
lastLoadedIncludesArchived = false;
|
||||
if (!shouldUseGlobalView && !isArchivedView && currentFilters && currentFilters.status === 'all') {
|
||||
if (!isArchivedView && currentFilters && currentFilters.status === 'all') {
|
||||
try {
|
||||
const archivedUrl = `${baseUrl}/api/warranties/archived`;
|
||||
const archivedUrl = shouldUseGlobalView ? `${baseUrl}/api/warranties/global/archived` : `${baseUrl}/api/warranties/archived`;
|
||||
const archivedResp = await fetch(archivedUrl, options);
|
||||
if (archivedResp.ok) {
|
||||
const archivedData = await archivedResp.json();
|
||||
@@ -2709,6 +2717,7 @@ async function renderWarranties(warrantiesToRender) {
|
||||
</div>
|
||||
` : ''}
|
||||
` : ''}
|
||||
${warranty.model_number ? `<div><i class="fas fa-tag"></i> ${window.i18next ? window.i18next.t('warranties.model_number') : 'Model Number'}: <span>${warranty.model_number}</span></div>` : ''}
|
||||
${warranty.vendor ? `<div><i class="fas fa-store"></i> ${window.i18next ? window.i18next.t('warranties.vendor') : 'Vendor'}: <span>${warranty.vendor}</span></div>` : ''}
|
||||
${warranty.warranty_type ? `<div><i class="fas fa-shield-alt"></i> ${window.i18next ? window.i18next.t('warranties.type') : 'Type'}: <span>${warranty.warranty_type}</span></div>` : ''}
|
||||
</div>
|
||||
@@ -2771,6 +2780,7 @@ async function renderWarranties(warrantiesToRender) {
|
||||
</div>
|
||||
` : ''}
|
||||
` : ''}
|
||||
${warranty.model_number ? `<div><i class=\"fas fa-tag\"></i> ${window.i18next ? window.i18next.t('warranties.model_number') : 'Model Number'}: <span>${warranty.model_number}</span></div>` : ''}
|
||||
${warranty.vendor ? `<div><i class="fas fa-store"></i> ${window.i18next ? window.i18next.t('warranties.vendor') : 'Vendor'}: <span>${warranty.vendor}</span></div>` : ''}
|
||||
${warranty.warranty_type ? `<div><i class="fas fa-shield-alt"></i> ${window.i18next ? window.i18next.t('warranties.type') : 'Type'}: <span>${warranty.warranty_type}</span></div>` : ''}
|
||||
</div>
|
||||
@@ -2833,6 +2843,7 @@ async function renderWarranties(warrantiesToRender) {
|
||||
</div>
|
||||
` : ''}
|
||||
` : ''}
|
||||
${warranty.model_number ? `<div><i class=\"fas fa-tag\"></i> ${window.i18next ? window.i18next.t('warranties.model_number') : 'Model Number'}: <span>${warranty.model_number}</span></div>` : ''}
|
||||
${warranty.vendor ? `<div><i class="fas fa-store"></i> ${window.i18next ? window.i18next.t('warranties.vendor') : 'Vendor'}: <span>${warranty.vendor}</span></div>` : ''}
|
||||
${warranty.warranty_type ? `<div><i class="fas fa-shield-alt"></i> ${window.i18next ? window.i18next.t('warranties.type') : 'Type'}: <span>${warranty.warranty_type}</span></div>` : ''}
|
||||
</div>
|
||||
@@ -3205,6 +3216,10 @@ async function openEditModal(warranty) {
|
||||
// Populate form fields
|
||||
document.getElementById('editProductName').value = warranty.product_name;
|
||||
document.getElementById('editProductUrl').value = warranty.product_url || '';
|
||||
const editModelNumberInput = document.getElementById('editModelNumber');
|
||||
if (editModelNumberInput) {
|
||||
editModelNumberInput.value = warranty.model_number || '';
|
||||
}
|
||||
document.getElementById('editPurchaseDate').value = warranty.purchase_date.split('T')[0];
|
||||
// Populate new duration fields
|
||||
document.getElementById('editWarrantyDurationYears').value = warranty.warranty_duration_years || 0;
|
||||
@@ -3890,6 +3905,11 @@ async function handleFormSubmit(event) { // Made async to properly await paperle
|
||||
|
||||
// Create form data object
|
||||
const formData = new FormData(warrantyForm);
|
||||
// Ensure model_number is included if present
|
||||
const modelNumberInput = document.getElementById('modelNumber');
|
||||
if (modelNumberInput && modelNumberInput.value.trim() !== '') {
|
||||
formData.set('model_number', modelNumberInput.value.trim());
|
||||
}
|
||||
|
||||
// Handle warranty type - use custom value if "other" is selected
|
||||
const warrantyTypeSelect = document.getElementById('warrantyType');
|
||||
@@ -6359,6 +6379,15 @@ function saveWarranty() {
|
||||
formData.append('notes', '');
|
||||
}
|
||||
|
||||
// Add model number to form data (optional)
|
||||
const editModelNumber = document.getElementById('editModelNumber');
|
||||
if (editModelNumber && editModelNumber.value.trim() !== '') {
|
||||
formData.append('model_number', editModelNumber.value.trim());
|
||||
} else {
|
||||
// Explicitly clear if empty
|
||||
formData.append('model_number', '');
|
||||
}
|
||||
|
||||
// Add vendor/retailer to form data
|
||||
const editVendorInput = document.getElementById('editVendor'); // Use the correct ID
|
||||
formData.append('vendor', editVendorInput ? editVendorInput.value.trim() : ''); // Use the correct variable
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<script src="js/i18n.js?v=20250119001"></script>
|
||||
<!-- Load fix for auth buttons -->
|
||||
<script src="fix-auth-buttons-loader.js?v=20250119001"></script>
|
||||
<script src="script.js?v=20250119001" defer></script> <!-- Added script.js -->
|
||||
<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 {
|
||||
@@ -551,6 +551,10 @@
|
||||
<!-- Serial number inputs will be added dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editModelNumber" data-i18n="warranties.model_number_optional">Model Number (Optional)</label>
|
||||
<input type="text" id="editModelNumber" name="model_number" class="form-control" data-i18n-placeholder="warranties.model_number_placeholder" placeholder="e.g. SM-G991U1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editVendor" data-i18n="warranties.vendor_optional">Vendor (Optional)</label>
|
||||
<input type="text" id="editVendor" name="vendor" class="form-control" data-i18n-placeholder="warranties.vendor_placeholder" placeholder="e.g. Amazon, Best Buy, etc.">
|
||||
|
||||
@@ -304,35 +304,16 @@ header .container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-tabs::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
height: 2px;
|
||||
background-color: var(--secondary-color);
|
||||
transition: all 0.3s ease;
|
||||
width: 25%;
|
||||
z-index: 1;
|
||||
}
|
||||
.form-tabs::before { display: none; }
|
||||
|
||||
.form-tabs[data-step="0"]::before {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.form-tabs[data-step="1"]::before {
|
||||
left: 25%;
|
||||
}
|
||||
|
||||
.form-tabs[data-step="2"]::before {
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.form-tabs[data-step="3"]::before {
|
||||
left: 75%;
|
||||
}
|
||||
/* Progress handled via ::after below */
|
||||
|
||||
.form-tab {
|
||||
flex: 1;
|
||||
flex: 1 1 20%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
text-align: center;
|
||||
padding: 15px 10px;
|
||||
cursor: pointer;
|
||||
@@ -340,7 +321,9 @@ header .container {
|
||||
color: var(--dark-gray);
|
||||
transition: color 0.3s ease;
|
||||
box-sizing: border-box;
|
||||
width: 25%; /* Ensure each tab is exactly 25% width */
|
||||
width: 20%; /* Five tabs fit on one row */
|
||||
min-width: 0; /* Allow shrinking without wrapping inner content */
|
||||
white-space: nowrap; /* Prevent label wrapping */
|
||||
}
|
||||
|
||||
.form-tab:hover {
|
||||
@@ -430,16 +413,22 @@ input.invalid {
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.form-tabs[data-step="0"]::after {
|
||||
width: calc(100% / 3);
|
||||
.form-tabs[data-step="0"]::after { width: calc(100% / 5); }
|
||||
.form-tabs[data-step="1"]::after { width: calc(200% / 5); }
|
||||
.form-tabs[data-step="2"]::after { width: calc(300% / 5); }
|
||||
.form-tabs[data-step="3"]::after { width: calc(400% / 5); }
|
||||
.form-tabs[data-step="4"]::after { width: 100%; }
|
||||
|
||||
@media (max-width: 788px) {
|
||||
.form-tab { padding: 12px 6px; }
|
||||
.form-tab i { margin-right: 6px; }
|
||||
}
|
||||
|
||||
.form-tabs[data-step="1"]::after {
|
||||
width: calc(200% / 3);
|
||||
}
|
||||
|
||||
.form-tabs[data-step="2"]::after {
|
||||
width: 100%;
|
||||
/* On narrow screens, shrink labels to keep tabs uniform (keep text visible) */
|
||||
@media (max-width: 740px) {
|
||||
.form-tab { padding: 10px 4px; gap: 4px; }
|
||||
.form-tab i { margin-right: 4px; font-size: 0.95rem; }
|
||||
.form-tab span { display: inline; font-size: 0.85rem; }
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const CACHE_NAME = 'warracker-cache-v20250119005';
|
||||
const CACHE_NAME = 'warracker-cache-v20250119006';
|
||||
const urlsToCache = [
|
||||
'./',
|
||||
'./index.html',
|
||||
@@ -8,7 +8,7 @@ const urlsToCache = [
|
||||
'./settings-styles.css?v=20250119001',
|
||||
'./header-fix.css?v=20250119001',
|
||||
'./mobile-header.css?v=20250119002',
|
||||
'./script.js?v=20250119001',
|
||||
'./script.js?v=20250119002',
|
||||
'./auth.js?v=20250119001',
|
||||
'./settings-new.js?v=20250119001',
|
||||
'./status.js?v=20250119001',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Version checker for Warracker
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const currentVersion = '0.10.1.14'; // Current version of the application
|
||||
const currentVersion = '0.10.1.15'; // Current version of the application
|
||||
const updateStatus = document.getElementById('updateStatus');
|
||||
const updateLink = document.getElementById('updateLink');
|
||||
const versionDisplay = document.getElementById('versionDisplay');
|
||||
|
||||
Reference in New Issue
Block a user