From b4855af43f01496b342d3ee96e90c3947e2e686b Mon Sep 17 00:00:00 2001 From: sassanix <39465071+sassanix@users.noreply.github.com> Date: Fri, 7 Mar 2025 16:12:15 -0400 Subject: [PATCH] Grid view, manuals, prices, table view, export option, filters ## [0.5.0] - 2025-03-07 ### Added - Enhanced filtering and sorting capabilities - Status filter (All, Active, Expiring Soon, Expired) - Multiple sorting options (Expiration Date, Purchase Date, Name) - Export filtered warranties as CSV - Improved filter controls layout - Mobile-responsive filter design - Multiple view options for warranty display - Grid view with card layout (default) - List view for compact horizontal display - Table view for structured data presentation - View preference saved between sessions - Responsive design for all view types - Optional purchase price tracking - Users can now add purchase prices to warranties - Price information displayed in warranty cards - Currency formatting with dollar sign - Included in warranty summary and exports ### Changed - Completely redesigned user interface - Modern card-based layout for warranties - Enhanced filter controls with improved styling - Better visual hierarchy with labeled filter groups - Custom dropdown styling with intuitive icons - Improved spacing and alignment throughout - Consistent color scheme and visual feedback - Responsive grid layout for warranty cards ### Fixed - Status indicator borders now correctly displayed for all warranty states - Green border for active warranties - Orange border for warranties expiring soon - Red border for expired warranties - Consistent status styling across all warranty cards - Form now resets to first tab after successful warranty submission - Manual filename now properly cleared when form is reset ## [0.4.0] - 2025-03-07 ### Added - Improved warranty creation process - Multi-step form with intuitive navigation - Progress indicator showing completion status - Enhanced validation with clear error messages - Summary review step before submission - Expiration date preview in summary - Responsive design for all device sizes ### Fixed - Progress indicator alignment issue in multi-step form - Contained indicator within form boundaries - Prevented overflow with improved CSS approach - Ensured consistent tab widths for better alignment - Improved tab navigation visual feedback ## [0.3.0] - 2025-03-07 ### Added - Product manual upload support - Users can now upload a second document for product manuals - Manual documents are displayed alongside invoices in the warranty details - Both add and edit forms support manual uploads - Product URL support - Users can now add website URLs for products - Links to product websites displayed in warranty cards - Easy access to product support and information pages ### Changed - Improved document link styling for consistency - Enhanced visual appearance of document links - Consistent styling between invoice and manual links - Better hover effects for document links - Fixed styling inconsistencies between document links - Improved warranty card layout - Document links now displayed side by side for better space utilization - Responsive design adapts to different screen sizes - More compact and organized appearance ### Fixed - Styling inconsistency between View Invoice and View Manual buttons - Removed unused CSS file to prevent styling conflicts --- CHANGELOG.md | 86 +++ backend/Dockerfile | 17 + backend/app.py | 46 +- backend/init.sql | 2 + backend/migrations/002_add_purchase_price.sql | 2 + backend/run_migrations.py | 105 +++ docker-compose.yml | 4 +- frontend/api-test.html | 197 ++--- frontend/index.html | 256 ++++-- frontend/script.js | 507 ++++++++++-- frontend/style.css | 729 ++++++++++++++++-- frontend/test-api.js | 44 ++ nginx.conf | 2 +- 13 files changed, 1708 insertions(+), 289 deletions(-) create mode 100644 backend/Dockerfile create mode 100644 backend/migrations/002_add_purchase_price.sql create mode 100644 backend/run_migrations.py create mode 100644 frontend/test-api.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e016ec1..3a32d5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,91 @@ # Changelog + +## [0.5.0] - 2025-03-07 + +### Added +- Enhanced filtering and sorting capabilities + - Status filter (All, Active, Expiring Soon, Expired) + - Multiple sorting options (Expiration Date, Purchase Date, Name) + - Export filtered warranties as CSV + - Improved filter controls layout + - Mobile-responsive filter design +- Multiple view options for warranty display + - Grid view with card layout (default) + - List view for compact horizontal display + - Table view for structured data presentation + - View preference saved between sessions + - Responsive design for all view types +- Optional purchase price tracking + - Users can now add purchase prices to warranties + - Price information displayed in warranty cards + - Currency formatting with dollar sign + - Included in warranty summary and exports + +### Changed +- Completely redesigned user interface + - Modern card-based layout for warranties + - Enhanced filter controls with improved styling + - Better visual hierarchy with labeled filter groups + - Custom dropdown styling with intuitive icons + - Improved spacing and alignment throughout + - Consistent color scheme and visual feedback + - Responsive grid layout for warranty cards + +### Fixed +- Status indicator borders now correctly displayed for all warranty states + - Green border for active warranties + - Orange border for warranties expiring soon + - Red border for expired warranties +- Consistent status styling across all warranty cards +- Form now resets to first tab after successful warranty submission +- Manual filename now properly cleared when form is reset + +## [0.4.0] - 2025-03-07 + +### Added +- Improved warranty creation process + - Multi-step form with intuitive navigation + - Progress indicator showing completion status + - Enhanced validation with clear error messages + - Summary review step before submission + - Expiration date preview in summary + - Responsive design for all device sizes + +### Fixed +- Progress indicator alignment issue in multi-step form + - Contained indicator within form boundaries + - Prevented overflow with improved CSS approach + - Ensured consistent tab widths for better alignment +- Improved tab navigation visual feedback + +## [0.3.0] - 2025-03-07 + +### Added +- Product manual upload support + - Users can now upload a second document for product manuals + - Manual documents are displayed alongside invoices in the warranty details + - Both add and edit forms support manual uploads +- Product URL support + - Users can now add website URLs for products + - Links to product websites displayed in warranty cards + - Easy access to product support and information pages + +### Changed +- Improved document link styling for consistency + - Enhanced visual appearance of document links + - Consistent styling between invoice and manual links + - Better hover effects for document links + - Fixed styling inconsistencies between document links +- Improved warranty card layout + - Document links now displayed side by side for better space utilization + - Responsive design adapts to different screen sizes + - More compact and organized appearance + +### Fixed +- Styling inconsistency between View Invoice and View Manual buttons +- Removed unused CSS file to prevent styling conflicts + ## [0.2.5-beta] - 2025-03-07 ### Added diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..19f735d --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.9-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +# Create directory for uploads +RUN mkdir -p /data/uploads && chmod 777 /data/uploads + +# Expose port +EXPOSE 5000 + +# Start the application +CMD ["python", "app.py"] \ No newline at end of file diff --git a/backend/app.py b/backend/app.py index 55103a1..80f6554 100644 --- a/backend/app.py +++ b/backend/app.py @@ -7,6 +7,7 @@ from werkzeug.utils import secure_filename from flask_cors import CORS import logging import time +from decimal import Decimal app = Flask(__name__) CORS(app) # Enable CORS @@ -96,6 +97,8 @@ def init_db(): expiration_date DATE, invoice_path TEXT, manual_path TEXT, + product_url TEXT, + purchase_price DECIMAL(10, 2), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') @@ -147,6 +150,9 @@ def get_warranties(): for key, value in warranty_dict.items(): if isinstance(value, (datetime, date)): warranty_dict[key] = value.isoformat() + # Convert Decimal objects to float for JSON serialization + elif isinstance(value, Decimal): + warranty_dict[key] = float(value) # Get serial numbers for this warranty warranty_id = warranty_dict['id'] @@ -186,6 +192,17 @@ def add_warranty(): product_name = request.form['product_name'] purchase_date_str = request.form['purchase_date'] serial_numbers = request.form.getlist('serial_numbers') + product_url = request.form.get('product_url', '') + + # Handle purchase price (optional) + purchase_price = None + if request.form.get('purchase_price'): + try: + purchase_price = float(request.form.get('purchase_price')) + if purchase_price < 0: + return jsonify({"error": "Purchase price cannot be negative"}), 400 + except ValueError: + return jsonify({"error": "Purchase price must be a valid number"}), 400 try: purchase_date = datetime.strptime(purchase_date_str, '%Y-%m-%d') @@ -237,10 +254,10 @@ def add_warranty(): with conn.cursor() as cur: # Insert warranty cur.execute(''' - INSERT INTO warranties (product_name, purchase_date, warranty_years, expiration_date, invoice_path, manual_path) - VALUES (%s, %s, %s, %s, %s, %s) + INSERT INTO warranties (product_name, purchase_date, warranty_years, expiration_date, invoice_path, manual_path, product_url, purchase_price) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING id - ''', (product_name, purchase_date, warranty_years, expiration_date, db_invoice_path, db_manual_path)) + ''', (product_name, purchase_date, warranty_years, expiration_date, db_invoice_path, db_manual_path, product_url, purchase_price)) warranty_id = cur.fetchone()[0] # Insert serial numbers @@ -334,6 +351,17 @@ def update_warranty(warranty_id): product_name = request.form['product_name'] purchase_date_str = request.form['purchase_date'] serial_numbers = request.form.getlist('serial_numbers') + product_url = request.form.get('product_url', '') + + # Handle purchase price (optional) + purchase_price = None + if request.form.get('purchase_price'): + try: + purchase_price = float(request.form.get('purchase_price')) + if purchase_price < 0: + return jsonify({"error": "Purchase price cannot be negative"}), 400 + except ValueError: + return jsonify({"error": "Purchase price must be a valid number"}), 400 try: purchase_date = datetime.strptime(purchase_date_str, '%Y-%m-%d') @@ -400,10 +428,10 @@ def update_warranty(warranty_id): cur.execute(''' UPDATE warranties SET product_name = %s, purchase_date = %s, warranty_years = %s, - expiration_date = %s, invoice_path = %s, manual_path = %s + expiration_date = %s, invoice_path = %s, manual_path = %s, product_url = %s, purchase_price = %s WHERE id = %s ''', (product_name, purchase_date, warranty_years, expiration_date, - db_invoice_path, db_manual_path, warranty_id)) + db_invoice_path, db_manual_path, product_url, purchase_price, warranty_id)) # Update serial numbers # First, delete existing serial numbers for this warranty @@ -508,7 +536,7 @@ def get_statistics(): cursor.execute(""" SELECT id, product_name, purchase_date, warranty_years, - expiration_date, invoice_path + expiration_date, invoice_path, manual_path, product_url, purchase_price FROM warranties WHERE expiration_date >= %s AND expiration_date <= %s ORDER BY expiration_date @@ -526,6 +554,10 @@ def get_statistics(): warranty['purchase_date'] = warranty['purchase_date'].isoformat() if warranty['expiration_date']: warranty['expiration_date'] = warranty['expiration_date'].isoformat() + + # Convert Decimal objects to float for JSON serialization + if warranty.get('purchase_price') and isinstance(warranty['purchase_price'], Decimal): + warranty['purchase_price'] = float(warranty['purchase_price']) recent_warranties.append(warranty) @@ -545,7 +577,7 @@ def get_statistics(): return jsonify({"error": str(e)}), 500 finally: - if cursor and cursor.closed is False: + if cursor: cursor.close() if conn: release_db_connection(conn) diff --git a/backend/init.sql b/backend/init.sql index f2c5849..e47ec57 100644 --- a/backend/init.sql +++ b/backend/init.sql @@ -8,6 +8,8 @@ CREATE TABLE warranties ( expiration_date DATE, invoice_path TEXT, manual_path TEXT, + product_url TEXT, + purchase_price DECIMAL(10, 2), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); diff --git a/backend/migrations/002_add_purchase_price.sql b/backend/migrations/002_add_purchase_price.sql new file mode 100644 index 0000000..44697c9 --- /dev/null +++ b/backend/migrations/002_add_purchase_price.sql @@ -0,0 +1,2 @@ +-- Add purchase_price column to warranties table +ALTER TABLE warranties ADD COLUMN purchase_price DECIMAL(10, 2); \ No newline at end of file diff --git a/backend/run_migrations.py b/backend/run_migrations.py new file mode 100644 index 0000000..ad1b131 --- /dev/null +++ b/backend/run_migrations.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +import os +import sys +import psycopg2 +import logging +from psycopg2 import pool +import time + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# PostgreSQL connection details +DB_HOST = os.environ.get('DB_HOST', 'warrackerdb') +DB_NAME = os.environ.get('DB_NAME', 'warranty_db') +DB_USER = os.environ.get('DB_USER', 'warranty_user') +DB_PASSWORD = os.environ.get('DB_PASSWORD', 'warranty_password') + +def create_db_connection(max_retries=5, retry_delay=5): + """Create a database connection with retry logic""" + attempt = 0 + last_exception = None + + while attempt < max_retries: + try: + logger.info(f"Attempting to connect to database (attempt {attempt+1}/{max_retries})") + conn = psycopg2.connect( + host=DB_HOST, + database=DB_NAME, + user=DB_USER, + password=DB_PASSWORD + ) + logger.info("Database connection successful") + return conn + except Exception as e: + last_exception = e + logger.error(f"Database connection error: {e}") + logger.info(f"Retrying in {retry_delay} seconds...") + time.sleep(retry_delay) + attempt += 1 + + # If we got here, all connection attempts failed + logger.error(f"Failed to connect to database after {max_retries} attempts") + raise last_exception + +def run_migrations(): + """Run all migration scripts in order""" + conn = None + try: + conn = create_db_connection() + conn.autocommit = False + cursor = conn.cursor() + + # Get list of migration files + migration_dir = os.path.join(os.path.dirname(__file__), 'migrations') + migration_files = sorted([f for f in os.listdir(migration_dir) if f.endswith('.sql')]) + + # Create migrations table if it doesn't exist + cursor.execute(''' + CREATE TABLE IF NOT EXISTS migrations ( + id SERIAL PRIMARY KEY, + filename VARCHAR(255) NOT NULL UNIQUE, + applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + + # Get already applied migrations + cursor.execute('SELECT filename FROM migrations') + applied_migrations = {row[0] for row in cursor.fetchall()} + + # Apply new migrations + for migration_file in migration_files: + if migration_file in applied_migrations: + logger.info(f"Migration {migration_file} already applied, skipping") + continue + + logger.info(f"Applying migration: {migration_file}") + migration_path = os.path.join(migration_dir, migration_file) + + with open(migration_path, 'r') as f: + migration_sql = f.read() + + try: + cursor.execute(migration_sql) + cursor.execute('INSERT INTO migrations (filename) VALUES (%s)', (migration_file,)) + logger.info(f"Migration {migration_file} applied successfully") + except Exception as e: + conn.rollback() + logger.error(f"Error applying migration {migration_file}: {e}") + raise + + conn.commit() + logger.info("All migrations applied successfully") + + except Exception as e: + logger.error(f"Migration error: {e}") + if conn: + conn.rollback() + sys.exit(1) + finally: + if conn: + conn.close() + +if __name__ == "__main__": + run_migrations() \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 50c94f8..d43c1d3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,5 @@ +version: '3' + services: warracker: build: . @@ -30,6 +32,6 @@ services: interval: 10s timeout: 5s retries: 5 - + volumes: postgres_data: \ No newline at end of file diff --git a/frontend/api-test.html b/frontend/api-test.html index 25252eb..f32c83f 100644 --- a/frontend/api-test.html +++ b/frontend/api-test.html @@ -3,145 +3,88 @@
-Use this page to test various API endpoints and see if they're responding correctly.
- -Results will appear here-
Results will appear here-
Results will appear here-
Checking API connection...
+You can also test the API manually with tools like curl:
+curl -X GET http://localhost:8005/api/warranties+