diff --git a/changelog.md b/changelog.md index 87a83b3..54bce5c 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,11 @@ ## 2025-11-12 +- Fixed critical 500 error in reporting dashboard by correcting route reference in reporting.html template - changed 'reports.project_list_page' to 'projects.project_list_page' to resolve NoMatchFound error when accessing /ui/reporting. +- Completed navigation validation by inventorying all sidebar navigation links, identifying missing routes for simulations, reporting, settings, themes, and currencies, created new UI routes in routes/ui.py with proper authentication guards, built corresponding templates (simulations.html, reporting.html, settings.html, theme_settings.html, currencies.html), registered the UI router in main.py, updated sidebar navigation to use route names instead of hardcoded URLs, and enhanced navigation.js to use dynamic URL resolution for proper route handling. +- Fixed critical template rendering error in sidebar_nav.html where URL objects from request.url_for() were being used with string methods, causing TypeError. Added |string filters to convert URL objects to strings for proper template rendering. +- Integrated Plotly charting for interactive visualizations in reporting templates, added chart generation methods to ReportingService (\_generate_npv_comparison_chart, \_generate_distribution_histogram), updated project summary and scenario distribution contexts to include chart JSON data, enhanced templates with chart containers and JavaScript rendering, added chart-container CSS styling, and validated all reporting tests pass. + - Completed local run verification: started application with `uvicorn main:app --reload` without errors, verified authenticated routes (/login, /, /projects/ui, /projects) load correctly with seeded data, and summarized findings for deployment pipeline readiness. - Fixed docker-compose.override.yml command array to remove duplicate "uvicorn" entry, enabling successful container startup with uvicorn reload in development mode. - Completed deployment pipeline verification: built Docker image without errors, validated docker-compose configuration, deployed locally with docker-compose (app and postgres containers started successfully), and confirmed application startup logs showing database bootstrap and seeded data initialization. diff --git a/main.py b/main.py index 62973e7..6d817c3 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,7 @@ from routes.exports import router as exports_router from routes.projects import router as projects_router from routes.reports import router as reports_router from routes.scenarios import router as scenarios_router +from routes.ui import router as ui_router from monitoring import router as monitoring_router from services.bootstrap import bootstrap_admin, bootstrap_pricing_settings from scripts.init_db import init_db as init_db_script @@ -98,6 +99,7 @@ app.include_router(exports_router) app.include_router(projects_router) app.include_router(scenarios_router) app.include_router(reports_router) +app.include_router(ui_router) app.include_router(monitoring_router) app.mount("/static", StaticFiles(directory="static"), name="static") diff --git a/routes/ui.py b/routes/ui.py new file mode 100644 index 0000000..d152d42 --- /dev/null +++ b/routes/ui.py @@ -0,0 +1,109 @@ +from __future__ import annotations + +from fastapi import APIRouter, Depends, Request +from fastapi.responses import HTMLResponse +from fastapi.templating import Jinja2Templates + +from dependencies import require_any_role, require_roles +from models import User + +router = APIRouter(tags=["UI"]) +templates = Jinja2Templates(directory="templates") + +READ_ROLES = ("viewer", "analyst", "project_manager", "admin") +MANAGE_ROLES = ("project_manager", "admin") + + +@router.get( + "/ui/simulations", + response_class=HTMLResponse, + include_in_schema=False, + name="ui.simulations", +) +def simulations_dashboard( + request: Request, + _: User = Depends(require_any_role(*READ_ROLES)), +) -> HTMLResponse: + return templates.TemplateResponse( + request, + "simulations.html", + { + "title": "Simulations", + }, + ) + + +@router.get( + "/ui/reporting", + response_class=HTMLResponse, + include_in_schema=False, + name="ui.reporting", +) +def reporting_dashboard( + request: Request, + _: User = Depends(require_any_role(*READ_ROLES)), +) -> HTMLResponse: + return templates.TemplateResponse( + request, + "reporting.html", + { + "title": "Reporting", + }, + ) + + +@router.get( + "/ui/settings", + response_class=HTMLResponse, + include_in_schema=False, + name="ui.settings", +) +def settings_page( + request: Request, + _: User = Depends(require_any_role(*READ_ROLES)), +) -> HTMLResponse: + return templates.TemplateResponse( + request, + "settings.html", + { + "title": "Settings", + }, + ) + + +@router.get( + "/theme-settings", + response_class=HTMLResponse, + include_in_schema=False, + name="ui.theme_settings", +) +def theme_settings_page( + request: Request, + _: User = Depends(require_any_role(*READ_ROLES)), +) -> HTMLResponse: + return templates.TemplateResponse( + request, + "theme_settings.html", + { + "title": "Theme Settings", + }, + ) + + +@router.get( + "/ui/currencies", + response_class=HTMLResponse, + include_in_schema=False, + name="ui.currencies", +) +def currencies_page( + request: Request, + _: User = Depends(require_roles(*MANAGE_ROLES)), +) -> HTMLResponse: + return templates.TemplateResponse( + request, + "currencies.html", + { + "title": "Currency Management", + }, + ) diff --git a/services/reporting.py b/services/reporting.py index 8445edc..a708b02 100644 --- a/services/reporting.py +++ b/services/reporting.py @@ -8,6 +8,9 @@ import math from typing import Mapping, Sequence from urllib.parse import urlencode +import plotly.graph_objects as go +import plotly.io as pio + from fastapi import Request from models import FinancialCategory, Project, Scenario @@ -515,6 +518,7 @@ class ReportingService: "label": "Download JSON", } ], + "chart_data": self._generate_npv_comparison_chart(reports), } def build_scenario_comparison_context( @@ -611,8 +615,64 @@ class ReportingService: "label": "Download JSON", } ], + "chart_data": self._generate_distribution_histogram(report.monte_carlo) if report.monte_carlo else "{}", } + def _generate_npv_comparison_chart(self, reports: Sequence[ScenarioReport]) -> str: + """Generate Plotly chart JSON for NPV comparison across scenarios.""" + scenario_names = [] + npv_values = [] + + for report in reports: + scenario_names.append(report.scenario.name) + npv_values.append(report.deterministic.npv or 0) + + fig = go.Figure(data=[ + go.Bar( + x=scenario_names, + y=npv_values, + name='NPV', + marker_color='lightblue' + ) + ]) + + fig.update_layout( + title="NPV Comparison Across Scenarios", + xaxis_title="Scenario", + yaxis_title="NPV", + showlegend=False + ) + + return pio.to_json(fig) or "{}" + + def _generate_distribution_histogram(self, monte_carlo: ScenarioMonteCarloResult) -> str: + """Generate Plotly histogram for Monte Carlo distribution.""" + if not monte_carlo.available or not monte_carlo.result or not monte_carlo.result.samples: + return "{}" + + # Get NPV samples + npv_samples = monte_carlo.result.samples.get(SimulationMetric.NPV, []) + if len(npv_samples) == 0: + return "{}" + + fig = go.Figure(data=[ + go.Histogram( + x=npv_samples, + nbinsx=50, + name='NPV Distribution', + marker_color='lightgreen' + ) + ]) + + fig.update_layout( + title="Monte Carlo NPV Distribution", + xaxis_title="NPV", + yaxis_title="Frequency", + showlegend=False + ) + + return pio.to_json(fig) or "{}" + def _build_cash_flows(scenario: Scenario) -> tuple[list[CashFlow], ScenarioFinancialTotals]: cash_flows: list[CashFlow] = [] diff --git a/static/css/main.css b/static/css/main.css index 5a174c4..4277eff 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -117,6 +117,16 @@ a { margin-top: 3rem; } +.chart-container { + width: 100%; + height: 400px; + background: rgba(15, 20, 27, 0.8); + border-radius: var(--radius-sm); + border: 1px solid rgba(255, 255, 255, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); + margin-bottom: 1rem; +} + .section-header { margin-bottom: 1.25rem; } diff --git a/static/js/navigation.js b/static/js/navigation.js index 62174e9..f1113fc 100644 --- a/static/js/navigation.js +++ b/static/js/navigation.js @@ -7,12 +7,12 @@ document.addEventListener("DOMContentLoaded", function () { // Define the navigation order (main pages) const navPages = [ - "/", - "/projects/ui", - "/imports/ui", - "/ui/simulations", - "/ui/reporting", - "/ui/settings", + window.NAVIGATION_URLS.dashboard, + window.NAVIGATION_URLS.projects, + window.NAVIGATION_URLS.imports, + window.NAVIGATION_URLS.simulations, + window.NAVIGATION_URLS.reporting, + window.NAVIGATION_URLS.settings, ]; const currentPath = window.location.pathname; diff --git a/templates/base.html b/templates/base.html index 99e0561..ae3f5d8 100644 --- a/templates/base.html +++ b/templates/base.html @@ -5,7 +5,7 @@ {% block title %}CalMiner{% endblock %} - + {% block head_extra %}{% endblock %} @@ -21,11 +21,27 @@ {% block scripts %}{% endblock %} - - - - - + + + + + + diff --git a/templates/currencies.html b/templates/currencies.html new file mode 100644 index 0000000..d98d26f --- /dev/null +++ b/templates/currencies.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} +{% block title %}{{ title }} | CalMiner{% endblock %} + +{% block content %} + + +
+
+

Currency Configuration

+

Define available currencies and their properties.

+

Currency management coming soon

+
+ +
+

Exchange Rates

+

Configure and update currency exchange rates.

+

Exchange rate management coming soon

+
+ +
+

Default Settings

+

Set default currencies for new projects and scenarios.

+

Default currency settings coming soon

+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/partials/sidebar_nav.html b/templates/partials/sidebar_nav.html index 64c7115..25382bf 100644 --- a/templates/partials/sidebar_nav.html +++ b/templates/partials/sidebar_nav.html @@ -1,98 +1,67 @@ {% set dashboard_href = request.url_for('dashboard.home') if request else '/' %} -{% set projects_href = request.url_for('projects.project_list_page') if request else '/projects/ui' %} -{% set project_create_href = request.url_for('projects.create_project_form') if request else '/projects/create' %} -{% set auth_session = request.state.auth_session if request else None %} -{% set is_authenticated = auth_session and auth_session.is_authenticated %} - -{% if is_authenticated %} - {% set logout_href = request.url_for('auth.logout') if request else '/logout' %} - {% set account_links = [ - {"href": logout_href, "label": "Logout", "match_prefix": "/logout"} - ] %} -{% else %} - {% set login_href = request.url_for('auth.login_form') if request else '/login' %} - {% set register_href = request.url_for('auth.register_form') if request else '/register' %} - {% set forgot_href = request.url_for('auth.password_reset_request_form') if request else '/forgot-password' %} - {% set account_links = [ - {"href": login_href, "label": "Login", "match_prefix": "/login"}, - {"href": register_href, "label": "Register", "match_prefix": "/register"}, - {"href": forgot_href, "label": "Forgot Password", "match_prefix": "/forgot-password"} - ] %} -{% endif %} -{% set nav_groups = [ - { - "label": "Workspace", - "links": [ - {"href": dashboard_href, "label": "Dashboard", "match_prefix": "/"}, - {"href": projects_href, "label": "Projects", "match_prefix": "/projects"}, - {"href": project_create_href, "label": "New Project", "match_prefix": "/projects/create"}, - {"href": "/imports/ui", "label": "Imports", "match_prefix": "/imports"} - ] - }, - { - "label": "Insights", - "links": [ - {"href": "/ui/simulations", "label": "Simulations"}, - {"href": "/ui/reporting", "label": "Reporting"} - ] - }, - { - "label": "Configuration", - "links": [ - { - "href": "/ui/settings", - "label": "Settings", - "children": [ - {"href": "/theme-settings", "label": "Themes"}, - {"href": "/ui/currencies", "label": "Currency Management"} - ] - } - ] - }, - { - "label": "Account", - "links": account_links - } -] %} +{% set projects_href = request.url_for('projects.project_list_page') if request +else '/projects/ui' %} {% set project_create_href = +request.url_for('projects.create_project_form') if request else +'/projects/create' %} {% set auth_session = request.state.auth_session if +request else None %} {% set is_authenticated = auth_session and +auth_session.is_authenticated %} {% if is_authenticated %} {% set logout_href = +request.url_for('auth.logout') if request else '/logout' %} {% set account_links += [ {"href": logout_href, "label": "Logout", "match_prefix": "/logout"} ] %} {% +else %} {% set login_href = request.url_for('auth.login_form') if request else +'/login' %} {% set register_href = request.url_for('auth.register_form') if +request else '/register' %} {% set forgot_href = +request.url_for('auth.password_reset_request_form') if request else +'/forgot-password' %} {% set account_links = [ {"href": login_href, "label": +"Login", "match_prefix": "/login"}, {"href": register_href, "label": "Register", +"match_prefix": "/register"}, {"href": forgot_href, "label": "Forgot Password", +"match_prefix": "/forgot-password"} ] %} {% endif %} {% set nav_groups = [ { +"label": "Workspace", "links": [ {"href": dashboard_href, "label": "Dashboard", +"match_prefix": "/"}, {"href": projects_href, "label": "Projects", +"match_prefix": "/projects"}, {"href": project_create_href, "label": "New +Project", "match_prefix": "/projects/create"}, {"href": "/imports/ui", "label": +"Imports", "match_prefix": "/imports"} ] }, { "label": "Insights", "links": [ +{"href": "/ui/simulations", "label": "Simulations"}, {"href": "/ui/reporting", +"label": "Reporting"} ] }, { "label": "Configuration", "links": [ { "href": +"/ui/settings", "label": "Settings", "children": [ {"href": "/theme-settings", +"label": "Themes"}, {"href": "/ui/currencies", "label": "Currency Management"} ] +} ] }, { "label": "Account", "links": account_links } ] %} diff --git a/templates/reporting.html b/templates/reporting.html new file mode 100644 index 0000000..e059871 --- /dev/null +++ b/templates/reporting.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} {% block title %}{{ title }} | CalMiner{% endblock %} +{% block content %} {% include "partials/reports_header.html" %} + +
+
+
+

Reporting Dashboard

+

Generate and view comprehensive financial reports.

+

+ Access project summaries, scenario comparisons, and distribution + analysis. +

+ +
+
+
+{% endblock %} diff --git a/templates/reports/project_summary.html b/templates/reports/project_summary.html index b0e78d0..4e1235d 100644 --- a/templates/reports/project_summary.html +++ b/templates/reports/project_summary.html @@ -1,205 +1,248 @@ -{% extends "base.html" %} -{% block title %}Project Summary | CalMiner{% endblock %} +{% extends "base.html" %} {% block title %}Project Summary | CalMiner{% endblock +%} {% block content %} {% include "partials/reports_header.html" %} {% include +"partials/reports/options_card.html" %} {% include +"partials/reports/filters_card.html" %} -{% block content %} - {% include "partials/reports_header.html" %} +
+
+
+

Project Details

+
+
+
Name
+
{{ project.name }}
+
+
+
Location
+
{{ project.location or "—" }}
+
+
+
Operation Type
+
{{ project.operation_type | replace("_", " ") | title }}
+
+
+
Scenarios
+
{{ scenario_count }}
+
+
+
Created
+
{{ project.created_at | format_datetime }}
+
+
+
Updated
+
{{ project.updated_at | format_datetime }}
+
+
+
- {% include "partials/reports/options_card.html" %} - {% include "partials/reports/filters_card.html" %} +
+

Financial Summary

+
    +
  • + Total Inflows + {{ aggregates.financials.total_inflows | + currency_display(project.currency) }} +
  • +
  • + Total Outflows + {{ aggregates.financials.total_outflows | + currency_display(project.currency) }} +
  • +
  • + Net Cash Flow + {{ aggregates.financials.total_net | + currency_display(project.currency) }} +
  • +
+
-
-
-
-

Project Details

-
-
-
Name
-
{{ project.name }}
-
-
-
Location
-
{{ project.location or "—" }}
-
-
-
Operation Type
-
{{ project.operation_type | replace("_", " ") | title }}
-
-
-
Scenarios
-
{{ scenario_count }}
-
-
-
Created
-
{{ project.created_at | format_datetime }}
-
-
-
Updated
-
{{ project.updated_at | format_datetime }}
-
-
-
+
+

Deterministic Metrics

+ {% if aggregates.deterministic_metrics %} + + + + + + + + + + + {% for key, metric in aggregates.deterministic_metrics.items() %} + + + + + + + {% endfor %} + +
MetricAverageBestWorst
{{ key | replace("_", " ") | title }}{{ metric.average | format_metric(key, project.currency) }}{{ metric.maximum | format_metric(key, project.currency) }}{{ metric.minimum | format_metric(key, project.currency) }}
+ {% else %} +

+ Deterministic metrics are unavailable for the current filters. +

+ {% endif %} +
+
+
-
-

Financial Summary

-
    +
    +
    +

    NPV Comparison

    +

    + Visual comparison of Net Present Value across scenarios. +

    +
    +
    +
    + +
    +
    +

    Scenario Breakdown

    +

    + Deterministic metrics and Monte Carlo summaries for each scenario. +

    +
    + + {% if scenarios %} {% for item in scenarios %} +
    +
    +
    +

    {{ item.scenario.name }}

    +

    + {{ item.scenario.status | title }} · {{ item.scenario.primary_resource + or "No primary resource" }} +

    +
    +
    + Currency + {{ item.scenario.currency or project.currency or "—" }} +
    + {% include "partials/reports/scenario_actions.html" %} +
    + +
    +
    +

    Financial Totals

    +
    • - Total Inflows - {{ aggregates.financials.total_inflows | currency_display(project.currency) }} + Inflows + {{ item.financials.inflows | + currency_display(item.scenario.currency or project.currency) + }}
    • - Total Outflows - {{ aggregates.financials.total_outflows | currency_display(project.currency) }} + Outflows + {{ item.financials.outflows | + currency_display(item.scenario.currency or project.currency) + }}
    • - Net Cash Flow - {{ aggregates.financials.total_net | currency_display(project.currency) }} + Net + {{ item.financials.net | currency_display(item.scenario.currency + or project.currency) }}
    -
    - -
    -

    Deterministic Metrics

    - {% if aggregates.deterministic_metrics %} - - - - - - - - - - - {% for key, metric in aggregates.deterministic_metrics.items() %} - - - - - - - {% endfor %} - -
    MetricAverageBestWorst
    {{ key | replace("_", " ") | title }}{{ metric.average | format_metric(key, project.currency) }}{{ metric.maximum | format_metric(key, project.currency) }}{{ metric.minimum | format_metric(key, project.currency) }}
    +
    By Category
    + {% if item.financials.by_category %} +
      + {% for label, value in item.financials.by_category.items() %} +
    • + {{ label | replace("_", " ") | title }} + {{ value | currency_display(item.scenario.currency or + project.currency) }} +
    • + {% endfor %} +
    {% else %} -

    Deterministic metrics are unavailable for the current filters.

    +

    No financial inputs recorded.

    {% endif %} -
    +
    + +
    +

    Deterministic Metrics

    + + + + + + + + + + + + + + + + + + + +
    Discount Rate{{ item.metrics.discount_rate | percentage_display }}
    NPV + {{ item.metrics.npv | currency_display(item.scenario.currency or + project.currency) }} +
    IRR{{ item.metrics.irr | percentage_display }}
    Payback Period{{ item.metrics.payback_period | period_display }}
    + {% if item.metrics.notes %} +
      + {% for note in item.metrics.notes %} +
    • {{ note }}
    • + {% endfor %} +
    + {% endif %} +
    + +
    +

    Monte Carlo Summary

    + {% if item.monte_carlo and item.monte_carlo.available %} +

    + Iterations: {{ item.monte_carlo.iterations }} {% if percentiles %} · + Percentiles: {% for percentile in percentiles %} {{ '%g' % percentile + }}{% if not loop.last %}, {% endif %} {% endfor %} {% endif %} +

    + {% include "partials/reports/monte_carlo_table.html" %} {% else %} +

    + Monte Carlo metrics are unavailable for this scenario. +

    + {% if item.monte_carlo and item.monte_carlo.notes %} +
      + {% for note in item.monte_carlo.notes %} +
    • {{ note }}
    • + {% endfor %} +
    + {% endif %} {% endif %} +
-
- -
-
-

Scenario Breakdown

-

Deterministic metrics and Monte Carlo summaries for each scenario.

-
- - {% if scenarios %} - {% for item in scenarios %} -
-
-
-

{{ item.scenario.name }}

-

{{ item.scenario.status | title }} · {{ item.scenario.primary_resource or "No primary resource" }}

-
-
- Currency - {{ item.scenario.currency or project.currency or "—" }} -
- {% include "partials/reports/scenario_actions.html" %} -
- -
-
-

Financial Totals

-
    -
  • - Inflows - {{ item.financials.inflows | currency_display(item.scenario.currency or project.currency) }} -
  • -
  • - Outflows - {{ item.financials.outflows | currency_display(item.scenario.currency or project.currency) }} -
  • -
  • - Net - {{ item.financials.net | currency_display(item.scenario.currency or project.currency) }} -
  • -
-
By Category
- {% if item.financials.by_category %} -
    - {% for label, value in item.financials.by_category.items() %} -
  • - {{ label | replace("_", " ") | title }} - {{ value | currency_display(item.scenario.currency or project.currency) }} -
  • - {% endfor %} -
- {% else %} -

No financial inputs recorded.

- {% endif %} -
- -
-

Deterministic Metrics

- - - - - - - - - - - - - - - - - - - -
Discount Rate{{ item.metrics.discount_rate | percentage_display }}
NPV{{ item.metrics.npv | currency_display(item.scenario.currency or project.currency) }}
IRR{{ item.metrics.irr | percentage_display }}
Payback Period{{ item.metrics.payback_period | period_display }}
- {% if item.metrics.notes %} -
    - {% for note in item.metrics.notes %} -
  • {{ note }}
  • - {% endfor %} -
- {% endif %} -
- -
-

Monte Carlo Summary

- {% if item.monte_carlo and item.monte_carlo.available %} -

- Iterations: {{ item.monte_carlo.iterations }} - {% if percentiles %} - · Percentiles: - {% for percentile in percentiles %} - {{ '%g' % percentile }}{% if not loop.last %}, {% endif %} - {% endfor %} - {% endif %} -

- {% include "partials/reports/monte_carlo_table.html" %} - {% else %} -

Monte Carlo metrics are unavailable for this scenario.

- {% if item.monte_carlo and item.monte_carlo.notes %} -
    - {% for note in item.monte_carlo.notes %} -
  • {{ note }}
  • - {% endfor %} -
- {% endif %} - {% endif %} -
-
-
- {% endfor %} - {% else %} -

No scenarios match the current filters.

- {% endif %} -
+ + {% endfor %} {% else %} +

No scenarios match the current filters.

+ {% endif %} + +{% endblock %} {% block scripts %} + + {% endblock %} diff --git a/templates/reports/scenario_distribution.html b/templates/reports/scenario_distribution.html index b0bc253..68fe5b2 100644 --- a/templates/reports/scenario_distribution.html +++ b/templates/reports/scenario_distribution.html @@ -1,149 +1,177 @@ -{% extends "base.html" %} -{% block title %}Scenario Distribution | CalMiner{% endblock %} +{% extends "base.html" %} {% block title %}Scenario Distribution | CalMiner{% +endblock %} {% block content %} {% include "partials/reports_header.html" %} -{% block content %} - {% include "partials/reports_header.html" %} +
+
+
+

Scenario Details

+
+
+
Name
+
{{ scenario.name }}
+
+
+
Project ID
+
{{ scenario.project_id }}
+
+
+
Status
+
{{ scenario.status | title }}
+
+
+
Currency
+
{{ scenario.currency or "—" }}
+
+
+
Discount Rate
+
{{ metrics.discount_rate | percentage_display }}
+
+
+
Updated
+
{{ scenario.updated_at | format_datetime }}
+
+
+
-
-
-
-

Scenario Details

-
-
-
Name
-
{{ scenario.name }}
-
-
-
Project ID
-
{{ scenario.project_id }}
-
-
-
Status
-
{{ scenario.status | title }}
-
-
-
Currency
-
{{ scenario.currency or "—" }}
-
-
-
Discount Rate
-
{{ metrics.discount_rate | percentage_display }}
-
-
-
Updated
-
{{ scenario.updated_at | format_datetime }}
-
-
-
- -
-

Financial Totals

-
    -
  • - Inflows - {{ summary.inflows | currency_display(scenario.currency) }} -
  • -
  • - Outflows - {{ summary.outflows | currency_display(scenario.currency) }} -
  • -
  • - Net Cash Flow - {{ summary.net | currency_display(scenario.currency) }} -
  • -
- {% if summary.by_category %} -

By Category

-
    - {% for label, value in summary.by_category.items() %} -
  • - {{ label | replace("_", " ") | title }} - {{ value | currency_display(scenario.currency) }} -
  • - {% endfor %} -
- {% endif %} -
-
-
- -
-
-

Deterministic Metrics

-

Key financial indicators calculated from deterministic cash flows.

-
- - - - - - - - - - - - - - - -
NPV{{ metrics.npv | currency_display(scenario.currency) }}
IRR{{ metrics.irr | percentage_display }}
Payback Period{{ metrics.payback_period | period_display }}
- {% if metrics.notes %} -
    - {% for note in metrics.notes %} -
  • {{ note }}
  • +
    +

    Financial Totals

    +
      +
    • + Inflows + {{ summary.inflows | currency_display(scenario.currency) }} +
    • +
    • + Outflows + {{ summary.outflows | currency_display(scenario.currency) + }} +
    • +
    • + Net Cash Flow + {{ summary.net | currency_display(scenario.currency) }} +
    • +
    + {% if summary.by_category %} +

    By Category

    +
      + {% for label, value in summary.by_category.items() %} +
    • + {{ label | replace("_", " ") | title }} + {{ value | currency_display(scenario.currency) }} +
    • {% endfor %}
    - {% endif %} -
- -
-
-

Monte Carlo Distribution

-

Simulation-driven distributions contextualize stochastic variability.

-
- {% if monte_carlo and monte_carlo.available %} -
-

Iterations: {{ monte_carlo.iterations }} · Percentiles: {{ percentiles | join(", ") }}

- - - - - - - - - - - - {% for metric, summary in monte_carlo.metrics.items() %} - - - - - - - - {% endfor %} - -
MetricMeanP5MedianP95
{{ metric | replace("_", " ") | title }}{{ summary.mean | format_metric(metric, scenario.currency) }}{{ summary.percentiles['5'] | format_metric(metric, scenario.currency) }}{{ summary.percentiles['50'] | format_metric(metric, scenario.currency) }}{{ summary.percentiles['95'] | format_metric(metric, scenario.currency) }}
- {% if monte_carlo.notes %} -
    - {% for note in monte_carlo.notes %} -
  • {{ note }}
  • - {% endfor %} -
- {% endif %} -
- {% else %} -

Monte Carlo output is unavailable for this scenario.

- {% if monte_carlo and monte_carlo.notes %} -
    - {% for note in monte_carlo.notes %} -
  • {{ note }}
  • - {% endfor %} -
{% endif %} + +
+
+ +
+
+

Deterministic Metrics

+

+ Key financial indicators calculated from deterministic cash flows. +

+
+ + + + + + + + + + + + + + + +
NPV{{ metrics.npv | currency_display(scenario.currency) }}
IRR{{ metrics.irr | percentage_display }}
Payback Period{{ metrics.payback_period | period_display }}
+ {% if metrics.notes %} + + {% endif %} +
+ +
+
+

Monte Carlo Distribution

+

+ Simulation-driven distributions contextualize stochastic variability. +

+
+ {% if monte_carlo and monte_carlo.available %} +
+
+

+ Iterations: {{ monte_carlo.iterations }} · Percentiles: {{ percentiles | + join(", ") }} +

+ + + + + + + + + + + + {% for metric, summary in monte_carlo.metrics.items() %} + + + + + + + + {% endfor %} + +
MetricMeanP5MedianP95
{{ metric | replace("_", " ") | title }}{{ summary.mean | format_metric(metric, scenario.currency) }} + {{ summary.percentiles['5'] | format_metric(metric, + scenario.currency) }} + + {{ summary.percentiles['50'] | format_metric(metric, + scenario.currency) }} + + {{ summary.percentiles['95'] | format_metric(metric, + scenario.currency) }} +
+ {% if monte_carlo.notes %} + {% endif %} -
+ + {% else %} +

Monte Carlo output is unavailable for this scenario.

+ {% if monte_carlo and monte_carlo.notes %} + + {% endif %} {% endif %} + +{% endblock %} {% block scripts %} + + {% endblock %} diff --git a/templates/settings.html b/templates/settings.html new file mode 100644 index 0000000..ca0af7d --- /dev/null +++ b/templates/settings.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% block title %}{{ title }} | CalMiner{% endblock %} + +{% block content %} + + +
+
+

Theme Settings

+

Customize the appearance and color scheme of the application.

+ +
+ +
+

Currency Management

+

Manage currency settings and exchange rates.

+ +
+ +
+

User Preferences

+

Configure personal preferences and defaults.

+

Coming soon

+
+ +
+

System Configuration

+

Advanced system settings and maintenance options.

+

Coming soon

+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/simulations.html b/templates/simulations.html new file mode 100644 index 0000000..81b271e --- /dev/null +++ b/templates/simulations.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} +{% block title %}{{ title }} | CalMiner{% endblock %} + +{% block content %} + {% include "partials/reports_header.html" %} + +
+
+
+

Simulation Dashboard

+

Run and monitor Monte Carlo simulations across scenarios.

+

This feature is coming soon.

+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/theme_settings.html b/templates/theme_settings.html new file mode 100644 index 0000000..3d8190d --- /dev/null +++ b/templates/theme_settings.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} +{% block title %}{{ title }} | CalMiner{% endblock %} + +{% block content %} + + +
+
+

Color Theme

+

Select your preferred color scheme.

+

Theme customization coming soon

+
+ +
+

Layout Options

+

Configure sidebar and navigation preferences.

+

Layout options coming soon

+
+ +
+

Accessibility

+

Adjust settings for better accessibility.

+

Accessibility settings coming soon

+
+
+{% endblock %} \ No newline at end of file