Dexter Edep
Update UI
71c2730
"""
Costs Display Component
Displays material costs and estimates
"""
from models import CostData, MaterialCost
from typing import Dict, List
def format_costs_display(cost_data) -> str:
"""
Format cost data for display with itemized materials and estimates
Args:
cost_data: Material cost data (dict or CostData object)
Returns:
Formatted HTML/Markdown string
"""
import logging
logger = logging.getLogger(__name__)
# Debug logging
logger.info(f"format_costs_display received type: {type(cost_data)}")
if not cost_data:
return "⚠️ Cost data unavailable"
# Handle both dict and object formats
success = cost_data.get('success', True) if isinstance(cost_data, dict) else getattr(cost_data, 'success', True)
if not success:
return "⚠️ Cost data unavailable"
html = """
<div style="padding: 20px;">
<h2 style="margin-top: 0; color: #ffffff !important; font-weight: 700;">πŸ’° Material Cost Analysis</h2>
"""
# Total Cost Estimate - handle both dict and object
total_estimate = cost_data.get('total_estimate') if isinstance(cost_data, dict) else getattr(cost_data, 'total_estimate', None)
if total_estimate:
# Extract values from dict or object
currency = total_estimate.get('currency', 'PHP') if isinstance(total_estimate, dict) else getattr(total_estimate, 'currency', 'PHP')
low = total_estimate.get('low', 0) if isinstance(total_estimate, dict) else getattr(total_estimate, 'low', 0)
mid = total_estimate.get('mid', 0) if isinstance(total_estimate, dict) else getattr(total_estimate, 'mid', 0)
high = total_estimate.get('high', 0) if isinstance(total_estimate, dict) else getattr(total_estimate, 'high', 0)
html += f"""
<div style="background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); padding: 25px; border-radius: 8px; margin-bottom: 20px; color: white; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
<h3 style="margin-top: 0; color: white; font-weight: 600;">πŸ’΅ Total Cost Estimate</h3>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-top: 15px;">
<div style="background: rgba(255,255,255,0.25); padding: 15px; border-radius: 6px; text-align: center;">
<div style="font-size: 0.9em; font-weight: 500;">Low Estimate</div>
<div style="font-size: 1.5em; font-weight: bold; margin-top: 5px;">
{currency} {low:,.2f}
</div>
</div>
<div style="background: rgba(255,255,255,0.35); padding: 15px; border-radius: 6px; text-align: center;">
<div style="font-size: 0.9em; font-weight: 500;">Mid Estimate</div>
<div style="font-size: 1.5em; font-weight: bold; margin-top: 5px;">
{currency} {mid:,.2f}
</div>
</div>
<div style="background: rgba(255,255,255,0.25); padding: 15px; border-radius: 6px; text-align: center;">
<div style="font-size: 0.9em; font-weight: 500;">High Estimate</div>
<div style="font-size: 1.5em; font-weight: bold; margin-top: 5px;">
{currency} {high:,.2f}
</div>
</div>
</div>
</div>
"""
# Market Conditions - handle both dict and object
market_conditions = cost_data.get('market_conditions') if isinstance(cost_data, dict) else getattr(cost_data, 'market_conditions', None)
last_updated = cost_data.get('last_updated') if isinstance(cost_data, dict) else getattr(cost_data, 'last_updated', None)
if market_conditions or last_updated:
html += """
<div style="background: white; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 5px solid #f59e0b; border: 1px solid #fde68a;">
<h4 style="margin-top: 0; color: #d97706 !important;">πŸ“Š Market Information</h4>
"""
if market_conditions:
html += f"<p style='margin: 5px 0; color: #0f172a !important;'><strong style='color: #0f172a !important;'>Market Conditions:</strong> {market_conditions}</p>"
if last_updated:
html += f"<p style='margin: 5px 0; color: #0f172a !important;'><strong style='color: #0f172a !important;'>Last Updated:</strong> {last_updated}</p>"
html += """
</div>
"""
# Itemized Materials by Category - handle both dict and object
materials = cost_data.get('materials', []) if isinstance(cost_data, dict) else getattr(cost_data, 'materials', [])
if materials:
# Group materials by category
materials_by_category = _group_materials_by_category(materials)
for category, materials_list in materials_by_category.items():
# Calculate category total - handle both dict and object
category_total = 0
first_currency = 'PHP'
for m in materials_list:
total_cost = m.get('total_cost', 0) if isinstance(m, dict) else getattr(m, 'total_cost', 0)
if total_cost:
category_total += total_cost
if first_currency == 'PHP':
first_currency = m.get('currency', 'PHP') if isinstance(m, dict) else getattr(m, 'currency', 'PHP')
html += f"""
<div style="background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); border: 1px solid #e2e8f0;">
<h3 style="color: #1e40af !important; margin-top: 0; font-weight: 600;">
{_get_category_icon(category)} {category}
{f'<span style="float: right; color: #15803d !important; font-weight: 700;">Total: {first_currency} {category_total:,.2f}</span>' if category_total > 0 else ''}
</h3>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background: #f8fafc;">
<th style="padding: 12px; text-align: left; border-bottom: 2px solid #cbd5e1; color: #0f172a !important; font-weight: 600;">Material</th>
<th style="padding: 12px; text-align: right; border-bottom: 2px solid #cbd5e1; color: #0f172a !important; font-weight: 600;">Unit Price</th>
<th style="padding: 12px; text-align: right; border-bottom: 2px solid #cbd5e1; color: #0f172a !important; font-weight: 600;">Quantity</th>
<th style="padding: 12px; text-align: right; border-bottom: 2px solid #cbd5e1; color: #0f172a !important; font-weight: 600;">Total Cost</th>
</tr>
</thead>
<tbody>
"""
for material in materials_list:
# Extract material properties - handle both dict and object
material_name = material.get('material_name', 'Unknown') if isinstance(material, dict) else getattr(material, 'material_name', 'Unknown')
source = material.get('source') if isinstance(material, dict) else getattr(material, 'source', None)
currency = material.get('currency', 'PHP') if isinstance(material, dict) else getattr(material, 'currency', 'PHP')
price_per_unit = material.get('price_per_unit', 0) if isinstance(material, dict) else getattr(material, 'price_per_unit', 0)
unit = material.get('unit', 'unit') if isinstance(material, dict) else getattr(material, 'unit', 'unit')
quantity_needed = material.get('quantity_needed') if isinstance(material, dict) else getattr(material, 'quantity_needed', None)
total_cost = material.get('total_cost') if isinstance(material, dict) else getattr(material, 'total_cost', None)
quantity_str = f"{quantity_needed:.2f} {unit}" if quantity_needed else "TBD"
total_str = f"{currency} {total_cost:,.2f}" if total_cost else "TBD"
html += f"""
<tr style="border-bottom: 1px solid #e2e8f0;">
<td style="padding: 12px; color: #0f172a !important;">
<strong style="color: #0f172a !important;">{material_name}</strong>
{f'<br><span style="font-size: 0.85em; color: #475569 !important;">Source: {source}</span>' if source else ''}
</td>
<td style="padding: 12px; text-align: right; color: #0f172a !important;">
{currency} {price_per_unit:,.2f} / {unit}
</td>
<td style="padding: 12px; text-align: right; color: #0f172a !important;">{quantity_str}</td>
<td style="padding: 12px; text-align: right; font-weight: 700; color: #15803d !important;">{total_str}</td>
</tr>
"""
html += """
</tbody>
</table>
</div>
"""
else:
html += """
<div style="background: white; padding: 20px; border-radius: 8px; text-align: center; color: #6b7280;">
<p>No material cost data available</p>
</div>
"""
# Disclaimer
html += """
<div style="background: white; padding: 15px; border-radius: 8px; margin-top: 20px; font-size: 0.9em; border: 1px solid #e2e8f0;">
<p style="margin: 0; color: #0f172a !important;"><strong style="color: #0f172a !important;">Note:</strong> Cost estimates are based on current market conditions and may vary.
Actual costs depend on supplier, location, quantity discounts, and market fluctuations.
Please consult with local suppliers for accurate quotes.</p>
</div>
</div>
"""
return html
def _group_materials_by_category(materials: List) -> Dict[str, List]:
"""Group materials by category (handles both dict and object formats)"""
grouped = {}
for material in materials:
# Handle both dict and object formats
category = material.get('category', 'Other') if isinstance(material, dict) else getattr(material, 'category', 'Other')
if not category:
category = "Other"
if category not in grouped:
grouped[category] = []
grouped[category].append(material)
return grouped
def _get_category_icon(category: str) -> str:
"""Get icon for material category"""
icons = {
"Structural": "πŸ—οΈ",
"Roofing": "🏠",
"Foundation": "βš“",
"Disaster-Resistant Features": "πŸ›‘οΈ",
"Finishing": "🎨",
"Electrical": "⚑",
"Plumbing": "🚰",
"Other": "πŸ“¦"
}
return icons.get(category, "πŸ“¦")