Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>EPD Comparison Tool | Sustainable Building Materials</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-in-out; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .indicator-chip { | |
| transition: all 0.2s ease; | |
| } | |
| .indicator-chip:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| .stage-cell { | |
| position: relative; | |
| } | |
| .stage-cell:hover .stage-tooltip { | |
| display: block; | |
| } | |
| .stage-tooltip { | |
| display: none; | |
| position: absolute; | |
| bottom: 100%; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background: #1f2937; | |
| color: white; | |
| padding: 4px 8px; | |
| border-radius: 4px; | |
| font-size: 12px; | |
| white-space: nowrap; | |
| z-index: 10; | |
| } | |
| .chart-container { | |
| height: 300px; | |
| } | |
| .ai-chat { | |
| transition: all 0.3s ease; | |
| } | |
| .ai-chat.collapsed { | |
| height: 60px; | |
| overflow: hidden; | |
| } | |
| .smooth-scroll { | |
| scroll-behavior: smooth; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 text-gray-800 font-sans smooth-scroll"> | |
| <!-- Header --> | |
| <header class="bg-green-700 text-white shadow-md"> | |
| <div class="container mx-auto px-4 py-4 flex justify-between items-center"> | |
| <div class="flex items-center space-x-2"> | |
| <i class="fas fa-leaf text-2xl"></i> | |
| <h1 class="text-2xl font-bold">EPD Comparison Tool</h1> | |
| </div> | |
| <nav> | |
| <ul class="flex space-x-6"> | |
| <li><a href="#" class="hover:text-green-200 transition" id="nav-home">Home</a></li> | |
| <li><a href="#" class="hover:text-green-200 transition" id="nav-about">About</a></li> | |
| <li><a href="#" class="hover:text-green-200 transition" id="nav-help">Help</a></li> | |
| </ul> | |
| </nav> | |
| <div class="flex items-center space-x-4"> | |
| <button id="login-btn" class="bg-white text-green-700 px-4 py-2 rounded-md hover:bg-green-100 transition"> | |
| Sign In | |
| </button> | |
| <div id="user-profile" class="hidden items-center space-x-2"> | |
| <img src="https://via.placeholder.com/40" alt="User" class="rounded-full w-8 h-8"> | |
| <span class="font-medium">User</span> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="container mx-auto px-4 py-8"> | |
| <!-- Selection Page (Default View) --> | |
| <div id="selection-page" class="fade-in"> | |
| <div class="bg-white rounded-lg shadow-md p-6 mb-8"> | |
| <h2 class="text-xl font-semibold mb-4 text-green-700">Compare Environmental Product Declarations</h2> | |
| <p class="text-gray-600 mb-6">Select indicators and components to compare their environmental performance across different lifecycle stages.</p> | |
| <!-- Indicator Filter --> | |
| <div class="mb-8"> | |
| <label for="indicator-search" class="block text-sm font-medium text-gray-700 mb-2">Search Indicators</label> | |
| <div class="relative"> | |
| <input type="text" id="indicator-search" placeholder="Global Warming Potential, Ozone Depletion, etc." | |
| class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-green-500 focus:border-green-500"> | |
| <div id="indicator-suggestions" class="absolute z-10 mt-1 w-full bg-white border border-gray-300 rounded-md shadow-lg hidden max-h-60 overflow-y-auto"></div> | |
| </div> | |
| <div id="selected-indicators" class="mt-3 flex flex-wrap gap-2"></div> | |
| </div> | |
| <!-- Max Results Picker --> | |
| <div class="mb-8"> | |
| <label for="max-results" class="block text-sm font-medium text-gray-700 mb-2">Maximum Results</label> | |
| <select id="max-results" class="px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-green-500 focus:border-green-500"> | |
| <option value="5">5</option> | |
| <option value="10" selected>10</option> | |
| <option value="15">15</option> | |
| <option value="20">20</option> | |
| </select> | |
| </div> | |
| <!-- Component Search --> | |
| <div class="mb-8"> | |
| <label for="component-search" class="block text-sm font-medium text-gray-700 mb-2">Search Components</label> | |
| <div class="relative"> | |
| <input type="text" id="component-search" placeholder="Concrete, Steel, Wood, etc." | |
| class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-green-500 focus:border-green-500"> | |
| <div id="component-suggestions" class="absolute z-10 mt-1 w-full bg-white border border-gray-300 rounded-md shadow-lg hidden max-h-60 overflow-y-auto"></div> | |
| </div> | |
| <div id="component-list" class="mt-3 border border-gray-200 rounded-md p-3 max-h-60 overflow-y-auto"> | |
| <!-- Components will be added here --> | |
| <p class="text-gray-500 text-center py-4">Search for components to compare</p> | |
| </div> | |
| </div> | |
| <!-- Weighting Section --> | |
| <div class="mb-8"> | |
| <div class="flex justify-between items-center mb-2"> | |
| <label class="text-sm font-medium text-gray-700">Indicator Weighting</label> | |
| <button id="reset-weights" class="text-sm text-green-600 hover:text-green-800">Reset All</button> | |
| </div> | |
| <div id="weight-sliders" class="space-y-4"> | |
| <!-- Sliders will be added here --> | |
| <p class="text-gray-500 text-center py-4">Select indicators to enable weighting</p> | |
| </div> | |
| </div> | |
| <div class="flex justify-end"> | |
| <button id="compare-btn" class="bg-green-600 text-white px-6 py-2 rounded-md hover:bg-green-700 transition flex items-center space-x-2"> | |
| <i class="fas fa-chart-bar"></i> | |
| <span>Compare Components</span> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- AI Suggestions --> | |
| <div class="bg-white rounded-lg shadow-md p-6"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="text-lg font-semibold text-green-700 flex items-center"> | |
| <i class="fas fa-robot mr-2"></i> | |
| AI Suggestions | |
| </h3> | |
| <button id="refresh-suggestions" class="text-green-600 hover:text-green-800"> | |
| <i class="fas fa-sync-alt"></i> | |
| </button> | |
| </div> | |
| <div id="ai-suggestions" class="space-y-4"> | |
| <div class="p-4 bg-blue-50 rounded-md"> | |
| <p class="font-medium text-blue-800">Based on your previous searches, you might want to compare:</p> | |
| <ul class="list-disc pl-5 mt-2 text-blue-700"> | |
| <li>Concrete vs. Cross-Laminated Timber</li> | |
| <li>Steel vs. Aluminum structural elements</li> | |
| </ul> | |
| </div> | |
| <div class="p-4 bg-green-50 rounded-md"> | |
| <p class="font-medium text-green-800">For projects focusing on carbon reduction:</p> | |
| <p class="mt-1 text-green-700">Consider including "Global Warming Potential" and "Embodied Carbon" indicators in your comparison.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Results Page (Hidden by Default) --> | |
| <div id="results-page" class="hidden fade-in"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-semibold text-green-700">Comparison Results</h2> | |
| <div class="flex space-x-3"> | |
| <button id="back-to-selection" class="bg-gray-200 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-300 transition flex items-center space-x-2"> | |
| <i class="fas fa-arrow-left"></i> | |
| <span>Modify Selection</span> | |
| </button> | |
| <button id="export-pdf" class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition flex items-center space-x-2"> | |
| <i class="fas fa-file-pdf"></i> | |
| <span>Export PDF</span> | |
| </button> | |
| <button id="save-session" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition flex items-center space-x-2"> | |
| <i class="fas fa-save"></i> | |
| <span>Save Session</span> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Summary Cards --> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8"> | |
| <div class="bg-white p-4 rounded-lg shadow-sm border-l-4 border-green-500"> | |
| <h3 class="font-medium text-gray-700 mb-1">Selected Indicators</h3> | |
| <p id="summary-indicators" class="text-2xl font-bold text-green-600">3</p> | |
| </div> | |
| <div class="bg-white p-4 rounded-lg shadow-sm border-l-4 border-blue-500"> | |
| <h3 class="font-medium text-gray-700 mb-1">Compared Components</h3> | |
| <p id="summary-components" class="text-2xl font-bold text-blue-600">5</p> | |
| </div> | |
| <div class="bg-white p-4 rounded-lg shadow-sm border-l-4 border-purple-500"> | |
| <h3 class="font-medium text-gray-700 mb-1">Lifecycle Stages</h3> | |
| <p id="summary-stages" class="text-2xl font-bold text-purple-600">6</p> | |
| </div> | |
| </div> | |
| <!-- Key Insights --> | |
| <div class="bg-white rounded-lg shadow-md p-6 mb-8"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="text-lg font-semibold text-green-700 flex items-center"> | |
| <i class="fas fa-lightbulb mr-2"></i> | |
| Key Insights | |
| </h3> | |
| <button id="refresh-insights" class="text-green-600 hover:text-green-800"> | |
| <i class="fas fa-sync-alt"></i> | |
| </button> | |
| </div> | |
| <div id="insights-content" class="space-y-4"> | |
| <div class="p-4 bg-yellow-50 rounded-md"> | |
| <p class="font-medium text-yellow-800">Best Overall Performer:</p> | |
| <p class="mt-1 text-yellow-700">Cross-Laminated Timber shows the lowest environmental impact across 4 of your 5 selected indicators.</p> | |
| </div> | |
| <div class="p-4 bg-red-50 rounded-md"> | |
| <p class="font-medium text-red-800">Sustainability Warning:</p> | |
| <p class="mt-1 text-red-700">PVC has poor recyclability (Module D) and high ozone depletion potential. Consider alternatives for your project.</p> | |
| </div> | |
| <div class="p-4 bg-green-50 rounded-md"> | |
| <p class="font-medium text-green-800">Balanced Option:</p> | |
| <p class="mt-1 text-green-700">Recycled Steel offers a good compromise between structural performance and environmental impact.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Results Tabs --> | |
| <div class="mb-8"> | |
| <div class="border-b border-gray-200"> | |
| <nav class="-mb-px flex space-x-8"> | |
| <button id="tab-table" class="border-b-2 border-green-500 text-green-600 px-1 py-4 text-sm font-medium">Data Tables</button> | |
| <button id="tab-charts" class="border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 px-1 py-4 text-sm font-medium">Visualizations</button> | |
| <button id="tab-calculations" class="border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 px-1 py-4 text-sm font-medium">Calculation Details</button> | |
| </nav> | |
| </div> | |
| </div> | |
| <!-- Tables View --> | |
| <div id="tables-view"> | |
| <div id="indicator-tables" class="space-y-8"> | |
| <!-- Tables will be added here --> | |
| <div class="bg-white rounded-lg shadow-md p-6"> | |
| <h3 class="text-lg font-semibold mb-4 text-green-700">Global Warming Potential (kg CO₂ eq)</h3> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full divide-y divide-gray-200"> | |
| <thead class="bg-gray-50"> | |
| <tr> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Component</th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider stage-cell"> | |
| A1 (Raw Material) | |
| <span class="stage-tooltip">Raw material extraction and processing</span> | |
| </th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider stage-cell"> | |
| A1-A3 (Product) | |
| <span class="stage-tooltip">Cradle to gate (raw materials to manufactured product)</span> | |
| </th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider stage-cell"> | |
| C (End of Life) | |
| <span class="stage-tooltip">End-of-life processing</span> | |
| </th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider stage-cell"> | |
| D (Benefits) | |
| <span class="stage-tooltip">Potential benefits from reuse, recovery or recycling</span> | |
| </th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Total</th> | |
| </tr> | |
| </thead> | |
| <tbody class="bg-white divide-y divide-gray-200"> | |
| <tr> | |
| <td class="px-6 py-4 whitespace-nowrap font-medium">Concrete (30MPa)</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-red-100 text-red-800">245</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-red-100 text-red-800">320</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-yellow-100 text-yellow-800">45</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-green-100 text-green-800">-15</td> | |
| <td class="px-6 py-4 whitespace-nowrap font-semibold">350</td> | |
| </tr> | |
| <tr> | |
| <td class="px-6 py-4 whitespace-nowrap font-medium">Cross-Laminated Timber</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-green-100 text-green-800">35</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-green-100 text-green-800">75</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-green-100 text-green-800">5</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-green-100 text-green-800">-25</td> | |
| <td class="px-6 py-4 whitespace-nowrap font-semibold">55</td> | |
| </tr> | |
| <tr> | |
| <td class="px-6 py-4 whitespace-nowrap font-medium">Recycled Steel</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-yellow-100 text-yellow-800">120</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-yellow-100 text-yellow-800">150</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-green-100 text-green-800">10</td> | |
| <td class="px-6 py-4 whitespace-nowrap bg-green-100 text-green-800">-40</td> | |
| <td class="px-6 py-4 whitespace-nowrap font-semibold">120</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Charts View --> | |
| <div id="charts-view" class="hidden"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div class="bg-white rounded-lg shadow-md p-6"> | |
| <h3 class="text-lg font-semibold mb-4 text-green-700">Global Warming Potential Comparison</h3> | |
| <div class="chart-container"> | |
| <canvas id="gwp-chart"></canvas> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow-md p-6"> | |
| <h3 class="text-lg font-semibold mb-4 text-green-700">Lifecycle Stage Breakdown</h3> | |
| <div class="chart-container"> | |
| <canvas id="stage-chart"></canvas> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow-md p-6"> | |
| <h3 class="text-lg font-semibold mb-4 text-green-700">Weighted Environmental Score</h3> | |
| <div class="chart-container"> | |
| <canvas id="weighted-chart"></canvas> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-lg shadow-md p-6"> | |
| <h3 class="text-lg font-semibold mb-4 text-green-700">Indicator Comparison</h3> | |
| <div class="chart-container"> | |
| <canvas id="indicator-chart"></canvas> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Calculations View --> | |
| <div id="calculations-view" class="hidden"> | |
| <div class="bg-white rounded-lg shadow-md p-6"> | |
| <h3 class="text-lg font-semibold mb-4 text-green-700">Calculation Methodology</h3> | |
| <div class="prose max-w-none"> | |
| <h4 class="text-md font-medium text-gray-800">Data Sources</h4> | |
| <p>All environmental impact data is sourced from verified Environmental Product Declarations (EPDs) published in accordance with ISO 14025 and EN 15804 standards.</p> | |
| <h4 class="text-md font-medium text-gray-800 mt-4">Lifecycle Stages</h4> | |
| <p>The following lifecycle stages are considered in the calculations:</p> | |
| <ul class="list-disc pl-5"> | |
| <li><strong>A1:</strong> Raw material extraction and supply</li> | |
| <li><strong>A2:</strong> Transport to manufacturing facility</li> | |
| <li><strong>A3:</strong> Manufacturing process</li> | |
| <li><strong>C1-C4:</strong> End-of-life processing (recycling, disposal, etc.)</li> | |
| <li><strong>D:</strong> Benefits and loads beyond the system boundary</li> | |
| </ul> | |
| <h4 class="text-md font-medium text-gray-800 mt-4">Weighting Methodology</h4> | |
| <p>When indicator weights are applied, the following calculation is used:</p> | |
| <div class="bg-gray-50 p-4 rounded-md my-2"> | |
| <code>Weighted Score = Σ (Indicator Value × Indicator Weight) / Σ Indicator Weights</code> | |
| </div> | |
| <h4 class="text-md font-medium text-gray-800 mt-4">EPD References</h4> | |
| <table class="min-w-full divide-y divide-gray-200 mt-2"> | |
| <thead class="bg-gray-50"> | |
| <tr> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Component</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">EPD Reference</th> | |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Validity</th> | |
| </tr> | |
| </thead> | |
| <tbody class="bg-white divide-y divide-gray-200"> | |
| <tr> | |
| <td class="px-6 py-4 whitespace-nowrap">Concrete (30MPa)</td> | |
| <td class="px-6 py-4 whitespace-nowrap">EPD-CON-2022-12345</td> | |
| <td class="px-6 py-4 whitespace-nowrap">2022-2027</td> | |
| </tr> | |
| <tr> | |
| <td class="px-6 py-4 whitespace-nowrap">Cross-Laminated Timber</td> | |
| <td class="px-6 py-4 whitespace-nowrap">EPD-CLT-2023-67890</td> | |
| <td class="px-6 py-4 whitespace-nowrap">2023-2028</td> | |
| </tr> | |
| <tr> | |
| <td class="px-6 py-4 whitespace-nowrap">Recycled Steel</td> | |
| <td class="px-6 py-4 whitespace-nowrap">EPD-RST-2021-54321</td> | |
| <td class="px-6 py-4 whitespace-nowrap">2021-2026</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- AI Chat Panel --> | |
| <div class="bg-white rounded-lg shadow-md p-6 mt-8 ai-chat"> | |
| <div class="flex items-center justify-between mb-4 cursor-pointer" id="chat-toggle"> | |
| <h3 class="text-lg font-semibold text-green-700 flex items-center"> | |
| <i class="fas fa-comments mr-2"></i> | |
| AI Sustainability Assistant | |
| </h3> | |
| <i class="fas fa-chevron-down text-gray-500 transition-transform" id="chat-icon"></i> | |
| </div> | |
| <div id="chat-content"> | |
| <div class="bg-gray-50 rounded-md p-4 mb-4 max-h-60 overflow-y-auto" id="chat-messages"> | |
| <div class="flex mb-3"> | |
| <div class="bg-green-100 rounded-lg p-3 max-w-3/4"> | |
| <p class="text-green-800">Hello! I'm your sustainability assistant. Ask me anything about these EPD comparisons or sustainable building materials.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex space-x-2"> | |
| <input type="text" id="chat-input" placeholder="Ask a question about the results..." | |
| class="flex-1 px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-green-500 focus:border-green-500"> | |
| <button id="send-chat" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 transition"> | |
| <i class="fas fa-paper-plane"></i> | |
| </button> | |
| </div> | |
| <div class="mt-2 text-xs text-gray-500"> | |
| <p>Example questions: "Which component has the lowest carbon footprint?" or "What are alternatives to PVC?"</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="bg-gray-800 text-white py-8"> | |
| <div class="container mx-auto px-4"> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-8"> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-4">EPD Comparison Tool</h3> | |
| <p class="text-gray-400">Making sustainable building decisions easier through transparent environmental data comparison.</p> | |
| </div> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-4">Resources</h3> | |
| <ul class="space-y-2"> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">EPD Database</a></li> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Sustainability Standards</a></li> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Calculation Methodology</a></li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-4">Contact</h3> | |
| <ul class="space-y-2"> | |
| <li class="flex items-center space-x-2 text-gray-400"> | |
| <i class="fas fa-envelope"></i> | |
| <span>support@epdcompare.com</span> | |
| </li> | |
| <li class="flex items-center space-x-2 text-gray-400"> | |
| <i class="fas fa-phone"></i> | |
| <span>+1 (555) 123-4567</span> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div class="border-t border-gray-700 mt-8 pt-6 text-center text-gray-400"> | |
| <p>© 2023 EPD Comparison Tool. All rights reserved.</p> | |
| </div> | |
| </div> | |
| </footer> | |
| <!-- JavaScript --> | |
| <script> | |
| // Sample data | |
| const indicators = [ | |
| "Global Warming Potential (kg CO₂ eq)", | |
| "Ozone Depletion Potential (kg CFC-11 eq)", | |
| "Acidification Potential (kg SO₂ eq)", | |
| "Eutrophication Potential (kg PO₄ eq)", | |
| "Abiotic Depletion Potential (kg Sb eq)", | |
| "Water Consumption (m³)", | |
| "Primary Energy Demand (MJ)", | |
| "Photochemical Ozone Creation Potential (kg C₂H₄ eq)" | |
| ]; | |
| const components = [ | |
| "Concrete (30MPa)", | |
| "Concrete (40MPa)", | |
| "Reinforced Concrete", | |
| "Cross-Laminated Timber", | |
| "Glued Laminated Timber", | |
| "Structural Steel", | |
| "Recycled Steel", | |
| "Aluminum", | |
| "PVC", | |
| "Copper", | |
| "Glass", | |
| "Brick", | |
| "Aerated Concrete", | |
| "Fiber Cement Board", | |
| "Gypsum Board" | |
| ]; | |
| // DOM Elements | |
| const selectionPage = document.getElementById('selection-page'); | |
| const resultsPage = document.getElementById('results-page'); | |
| const compareBtn = document.getElementById('compare-btn'); | |
| const backToSelection = document.getElementById('back-to-selection'); | |
| const indicatorSearch = document.getElementById('indicator-search'); | |
| const indicatorSuggestions = document.getElementById('indicator-suggestions'); | |
| const selectedIndicators = document.getElementById('selected-indicators'); | |
| const componentSearch = document.getElementById('component-search'); | |
| const componentSuggestions = document.getElementById('component-suggestions'); | |
| const componentList = document.getElementById('component-list'); | |
| const weightSliders = document.getElementById('weight-sliders'); | |
| const resetWeights = document.getElementById('reset-weights'); | |
| const maxResults = document.getElementById('max-results'); | |
| const tablesView = document.getElementById('tables-view'); | |
| const chartsView = document.getElementById('charts-view'); | |
| const calculationsView = document.getElementById('calculations-view'); | |
| const tabTable = document.getElementById('tab-table'); | |
| const tabCharts = document.getElementById('tab-charts'); | |
| const tabCalculations = document.getElementById('tab-calculations'); | |
| const chatToggle = document.getElementById('chat-toggle'); | |
| const chatContent = document.getElementById('chat-content'); | |
| const chatIcon = document.getElementById('chat-icon'); | |
| const chatInput = document.getElementById('chat-input'); | |
| const sendChat = document.getElementById('send-chat'); | |
| const chatMessages = document.getElementById('chat-messages'); | |
| const loginBtn = document.getElementById('login-btn'); | |
| const userProfile = document.getElementById('user-profile'); | |
| // State | |
| let selectedIndicatorList = []; | |
| let selectedComponentList = []; | |
| let indicatorWeights = {}; | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Set up event listeners | |
| compareBtn.addEventListener('click', showResults); | |
| backToSelection.addEventListener('click', showSelection); | |
| indicatorSearch.addEventListener('input', handleIndicatorSearch); | |
| componentSearch.addEventListener('input', handleComponentSearch); | |
| resetWeights.addEventListener('click', resetAllWeights); | |
| tabTable.addEventListener('click', () => switchTab('table')); | |
| tabCharts.addEventListener('click', () => switchTab('charts')); | |
| tabCalculations.addEventListener('click', () => switchTab('calculations')); | |
| chatToggle.addEventListener('click', toggleChat); | |
| sendChat.addEventListener('click', sendMessage); | |
| chatInput.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') sendMessage(); | |
| }); | |
| loginBtn.addEventListener('click', mockLogin); | |
| // Initialize charts (mock data) | |
| initializeCharts(); | |
| }); | |
| // Functions | |
| function handleIndicatorSearch() { | |
| const query = indicatorSearch.value.toLowerCase(); | |
| if (query.length < 2) { | |
| indicatorSuggestions.classList.add('hidden'); | |
| return; | |
| } | |
| const matches = indicators.filter(ind => | |
| ind.toLowerCase().includes(query) && | |
| !selectedIndicatorList.includes(ind) | |
| ); | |
| if (matches.length === 0) { | |
| indicatorSuggestions.innerHTML = '<div class="px-4 py-2 text-gray-500">No matching indicators found</div>'; | |
| indicatorSuggestions.classList.remove('hidden'); | |
| return; | |
| } | |
| indicatorSuggestions.innerHTML = ''; | |
| matches.slice(0, 5).forEach(ind => { | |
| const div = document.createElement('div'); | |
| div.className = 'px-4 py-2 hover:bg-gray-100 cursor-pointer'; | |
| div.textContent = ind; | |
| div.addEventListener('click', () => selectIndicator(ind)); | |
| indicatorSuggestions.appendChild(div); | |
| }); | |
| indicatorSuggestions.classList.remove('hidden'); | |
| } | |
| function selectIndicator(indicator) { | |
| if (!selectedIndicatorList.includes(indicator)) { | |
| selectedIndicatorList.push(indicator); | |
| indicatorWeights[indicator] = 1; // Default weight | |
| renderSelectedIndicators(); | |
| createWeightSlider(indicator); | |
| } | |
| indicatorSearch.value = ''; | |
| indicatorSuggestions.classList.add('hidden'); | |
| indicatorSearch.focus(); | |
| } | |
| function renderSelectedIndicators() { | |
| selectedIndicators.innerHTML = ''; | |
| selectedIndicatorList.forEach(ind => { | |
| const chip = document.createElement('div'); | |
| chip.className = 'flex items-center bg-green-100 text-green-800 px-3 py-1 rounded-full text-sm indicator-chip'; | |
| const span = document.createElement('span'); | |
| span.textContent = ind; | |
| const button = document.createElement('button'); | |
| button.className = 'ml-2 text-green-600 hover:text-green-900'; | |
| button.innerHTML = '<i class="fas fa-times"></i>'; | |
| button.addEventListener('click', () => removeIndicator(ind)); | |
| chip.appendChild(span); | |
| chip.appendChild(button); | |
| selectedIndicators.appendChild(chip); | |
| }); | |
| } | |
| function removeIndicator(indicator) { | |
| selectedIndicatorList = selectedIndicatorList.filter(ind => ind !== indicator); | |
| delete indicatorWeights[indicator]; | |
| renderSelectedIndicators(); | |
| // Remove corresponding slider | |
| const sliderId = `weight-${indicator.replace(/\s+/g, '-')}`; | |
| const sliderElement = document.getElementById(sliderId); | |
| if (sliderElement) { | |
| sliderElement.parentElement.remove(); | |
| } | |
| // If no indicators left, show placeholder | |
| if (selectedIndicatorList.length === 0) { | |
| weightSliders.innerHTML = '<p class="text-gray-500 text-center py-4">Select indicators to enable weighting</p>'; | |
| } | |
| } | |
| function createWeightSlider(indicator) { | |
| // Remove placeholder if it exists | |
| if (weightSliders.querySelector('p')) { | |
| weightSliders.innerHTML = ''; | |
| } | |
| const sliderId = `weight-${indicator.replace(/\s+/g, '-')}`; | |
| const sliderDiv = document.createElement('div'); | |
| sliderDiv.className = 'space-y-1'; | |
| const labelDiv = document.createElement('div'); | |
| labelDiv.className = 'flex justify-between'; | |
| const label = document.createElement('label'); | |
| label.className = 'text-sm text-gray-700'; | |
| label.textContent = indicator; | |
| label.htmlFor = sliderId; | |
| const valueSpan = document.createElement('span'); | |
| valueSpan.className = 'text-sm text-gray-700'; | |
| valueSpan.id = `${sliderId}-value`; | |
| valueSpan.textContent = '1'; | |
| labelDiv.appendChild(label); | |
| labelDiv.appendChild(valueSpan); | |
| const slider = document.createElement('input'); | |
| slider.type = 'range'; | |
| slider.id = sliderId; | |
| slider.className = 'w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer'; | |
| slider.min = '0'; | |
| slider.max = '5'; | |
| slider.step = '0.1'; | |
| slider.value = '1'; | |
| slider.addEventListener('input', function() { | |
| const value = parseFloat(this.value).toFixed(1); | |
| valueSpan.textContent = value; | |
| indicatorWeights[indicator] = parseFloat(value); | |
| }); | |
| sliderDiv.appendChild(labelDiv); | |
| sliderDiv.appendChild(slider); | |
| weightSliders.appendChild(sliderDiv); | |
| } | |
| function resetAllWeights() { | |
| selectedIndicatorList.forEach(ind => { | |
| indicatorWeights[ind] = 1; | |
| const sliderId = `weight-${ind.replace(/\s+/g, '-')}`; | |
| const slider = document.getElementById(sliderId); | |
| if (slider) { | |
| slider.value = '1'; | |
| document.getElementById(`${sliderId}-value`).textContent = '1'; | |
| } | |
| }); | |
| } | |
| function handleComponentSearch() { | |
| const query = componentSearch.value.toLowerCase(); | |
| if (query.length < 2) { | |
| componentSuggestions.classList.add('hidden'); | |
| return; | |
| } | |
| const matches = components.filter(comp => | |
| comp.toLowerCase().includes(query) && | |
| !selectedComponentList.includes(comp) | |
| ); | |
| if (matches.length === 0) { | |
| componentSuggestions.innerHTML = '<div class="px-4 py-2 text-gray-500">No matching components found</div>'; | |
| componentSuggestions.classList.remove('hidden'); | |
| return; | |
| } | |
| componentSuggestions.innerHTML = ''; | |
| matches.slice(0, 5).forEach(comp => { | |
| const div = document.createElement('div'); | |
| div.className = 'px-4 py-2 hover:bg-gray-100 cursor-pointer'; | |
| div.textContent = comp; | |
| div.addEventListener('click', () => selectComponent(comp)); | |
| componentSuggestions.appendChild(div); | |
| }); | |
| componentSuggestions.classList.remove('hidden'); | |
| } | |
| function selectComponent(component) { | |
| if (!selectedComponentList.includes(component)) { | |
| selectedComponentList.push(component); | |
| renderComponentList(); | |
| } | |
| componentSearch.value = ''; | |
| componentSuggestions.classList.add('hidden'); | |
| componentSearch.focus(); | |
| } | |
| function renderComponentList() { | |
| if (selectedComponentList.length === 0) { | |
| componentList.innerHTML = '<p class="text-gray-500 text-center py-4">Search for components to compare</p>'; | |
| return; | |
| } | |
| componentList.innerHTML = ''; | |
| selectedComponentList.forEach(comp => { | |
| const item = document.createElement | |
| </html> |