Files
CGW-Quote-Builder/app/blueprints/products.py
T
2026-06-11 11:10:18 -05:00

257 lines
8.1 KiB
Python

"""
Product Finder Blueprint
Handles the product quiz and recommendations
"""
from flask import Blueprint, render_template, send_from_directory, session, jsonify, redirect, url_for, request
from blueprints.auth import login_required, get_current_user, can_user
import data_access as da
import os
products_bp = Blueprint('products', __name__, url_prefix='/quiz')
# Get base directory
basedir = os.path.dirname(os.path.dirname(__file__))
@products_bp.route('/')
@login_required
def product_finder():
"""Serve the product finder quiz page"""
return render_template('product_finder.html')
@products_bp.route('/index')
@login_required
def quiz_index():
"""Alternative route for product finder"""
return render_template('product_finder.html')
@products_bp.route('/advanced-search')
@login_required
def advanced_search():
"""Serve the advanced search page"""
return render_template('advanced_search.html')
@products_bp.route('/list')
@login_required
def product_list():
"""Serve the product listing page with search and pagination"""
# Check if user has permission to manage products
if not can_user('manage_products'):
return redirect(url_for('products.product_finder'))
return render_template('product_list.html')
@products_bp.route('/manage')
@login_required
def product_manager():
"""Serve the product management page"""
# Check if user has permission to manage products
if not can_user('manage_products'):
return redirect(url_for('products.product_finder'))
return render_template('product_manager.html')
@products_bp.route('/api/user-session')
@login_required
def get_user_session():
"""Return current user's session data including accessible locations"""
user = get_current_user()
permissions = user.get('permissions', {}) if user else {}
return jsonify({
'username': session.get('username'),
'userId': None, # Not needed with username-based sessions
'currentLocation': session.get('currentLocation'),
'accessibleLocations': session.get('accessibleLocations', []),
'role': session.get('role', 'user'),
'permissions': permissions
})
# Product API Endpoints
@products_bp.route('/api/products', methods=['GET'])
@login_required
def get_products():
"""Get all products or filter by location"""
location = request.args.get('location')
products = da.get_all_products(location=location)
return jsonify({
'status': 'success',
'products': products
})
@products_bp.route('/api/products/search', methods=['GET'])
@login_required
def search_products():
"""Search products with pagination and filtering"""
# Get query parameters
search_query = request.args.get('q', '').strip().lower()
page = int(request.args.get('page', 1))
per_page = int(request.args.get('per_page', 20))
location = request.args.get('location')
status_filter = request.args.get('status', 'all') # 'all', 'active', 'discontinued'
# Get all products
all_products = da.get_all_products(location=location)
# Filter by search query (description and product code)
if search_query:
filtered_products = [
p for p in all_products
if search_query in p.get('description', '').lower()
or search_query in p.get('productCode', '').lower()
]
else:
filtered_products = all_products
# Filter by status
if status_filter == 'active':
filtered_products = [p for p in filtered_products if not p.get('discontinued', False)]
elif status_filter == 'discontinued':
filtered_products = [p for p in filtered_products if p.get('discontinued', False)]
# Calculate pagination
total_products = len(filtered_products)
total_pages = (total_products + per_page - 1) // per_page if per_page > 0 else 1
start_idx = (page - 1) * per_page
end_idx = start_idx + per_page
# Get page slice
products_page = filtered_products[start_idx:end_idx]
return jsonify({
'status': 'success',
'products': products_page,
'pagination': {
'page': page,
'per_page': per_page,
'total_products': total_products,
'total_pages': total_pages,
'has_prev': page > 1,
'has_next': page < total_pages
}
})
@products_bp.route('/api/products/<product_code>', methods=['GET'])
@login_required
def get_product(product_code):
"""Get a specific product by code"""
product = da.get_product_by_code(product_code)
if not product:
return jsonify({
'status': 'error',
'message': 'Product not found'
}), 404
return jsonify({
'status': 'success',
'product': product
})
@products_bp.route('/api/products', methods=['POST'])
@login_required
def create_product():
"""Create a new product"""
if not can_user('manage_products'):
return jsonify({
'status': 'error',
'message': 'Permission denied'
}), 403
try:
data = request.json
# Create product via data access layer
product = da.create_product(data)
if not product:
return jsonify({
'status': 'error',
'message': 'Product code already exists'
}), 400
return jsonify({
'status': 'success',
'message': 'Product created successfully',
'product': product
})
except Exception as e:
da.rollback()
return jsonify({
'status': 'error',
'message': str(e)
}), 500
@products_bp.route('/api/products/<product_code>', methods=['PUT'])
@login_required
def update_product(product_code):
"""Update an existing product"""
if not can_user('manage_products'):
return jsonify({
'status': 'error',
'message': 'Permission denied'
}), 403
try:
data = request.json
# Update product via data access layer
product = da.update_product(product_code, data)
if not product:
return jsonify({
'status': 'error',
'message': 'Product not found'
}), 404
return jsonify({
'status': 'success',
'message': 'Product updated successfully',
'product': product
})
except Exception as e:
da.rollback()
return jsonify({
'status': 'error',
'message': str(e)
}), 500
@products_bp.route('/api/product-attributes', methods=['GET'])
@login_required
def get_product_attributes():
"""Get product attributes configuration"""
attributes = da.get_product_attributes()
return jsonify({
'status': 'success',
'attributes': attributes
})
@products_bp.route('/css/<path:filename>')
def serve_css(filename):
"""Serve CSS files"""
return send_from_directory(os.path.join(basedir, 'static', 'css'), filename)
@products_bp.route('/js/<path:filename>')
def serve_js(filename):
"""Serve JavaScript files"""
return send_from_directory(os.path.join(basedir, 'static', 'js'), filename)
@products_bp.route('/images/<path:filename>')
def serve_images(filename):
"""Serve image files"""
return send_from_directory(os.path.join(basedir, 'static', 'images'), filename)
@products_bp.route('/data/<path:filename>')
def serve_data(filename):
"""Serve data files (JSON)"""
return send_from_directory(os.path.join(basedir, 'data'), filename)
@products_bp.route('/product/<product_code>')
@login_required
def product_detail(product_code):
"""
Serve product detail page with URL-based navigation
This allows direct linking to products via /quiz/product/404
"""
return render_template('product_finder.html', initial_product=product_code)