{% extends "layouts/base.twig" %} {% block title %}{{ translations.benchmark_observations ?? 'Benchmark: Observations' }}{% endblock %} {% block head %} {{ parent() }} {% endblock %} {% block page_heading %}

{{ translations.benchmark ?? 'Benchmark' }} — {{ translations.observations ?? 'Observations' }}

{{ translations.benchmark_observations_subtitle ?? 'Manage observation snapshots and compare competitor pricing over time.' }}

{% endblock %} {% block content %} {% set obsCount = observations|length %} {% set lastObsAt = (obsCount > 0) ? (observations[0].getObservedAt() ?? null) : null %}
{# =========================== HEADER / COMPETITOR SUMMARY =========================== #}

{{ competitor.getName() ?? '-' }}

{{ competitor.isActive() ? (translations.active ?? 'Active') : (translations.inactive ?? 'Inactive') }} {% if competitor.getType() %} {{ competitor.getType() }} {% endif %} {% if competitor.getCity() %} {{ competitor.getCity() }} {% endif %} {% if competitor.getDistanceKm() %} {{ competitor.getDistanceKm() }} km {% endif %}
{{ translations.total_observations ?? 'Total observations' }}: {{ obsCount }} {% if lastObsAt %} {{ translations.last_observation ?? 'Last observation' }}: {{ lastObsAt }} {% endif %}
{% if competitor.getNotes() %}
{{ translations.notes ?? 'Notes' }}
{{ competitor.getNotes() }}
{% endif %}
{{ translations.back_to_competitors ?? 'Back to competitors' }}
{{ translations.search_observations_hint ?? 'Filter observations instantly.' }}
{# ✅ Bez matched/unmatched: nowe filtry #}
{# =========================== LIST OBSERVATIONS =========================== #}
{% if observations is empty %}
{{ translations.no_observations_yet ?? 'No observations yet. Create your first snapshot.' }}
{% else %}
{% for o in observations %} {% set itemsCount = o.getItemsCount() ?? (o.getItems() is iterable ? (o.getItems()|length) : 0) %} {% set note = o.getNotes() ?? '' %} {% set searchText = ((o.getObservedAt() ?? '') ~ ' ' ~ (note ?? ''))|lower %} {# ===== Stats we can derive even if backend doesn’t provide aggregates ===== #} {% set promoCount = 0 %} {% set pricedCount = 0 %} {% set sumPrice = 0 %} {% set sumDiscountPerc = 0 %} {% set discountCount = 0 %} {% if o.getItems() is iterable %} {% for it in o.getItems() %} {% set p = it.getPrice() %} {% if p is not null %} {% set pricedCount = pricedCount + 1 %} {% set sumPrice = sumPrice + p %} {% endif %} {% if it.isPromo() %} {% set promoCount = promoCount + 1 %} {% set pp = it.getPromoPrice() %} {% if p is not null and pp is not null and p > 0 %} {% set sumDiscountPerc = sumDiscountPerc + (((p - pp) / p) * 100) %} {% set discountCount = discountCount + 1 %} {% endif %} {% endif %} {% endfor %} {% else %} {# fallback: if you later add o.getPromoCount()/getPricedCount()/getAvgPrice - use those #} {% set promoCount = o.getPromoCount() ?? 0 %} {% set pricedCount = o.getPricedCount() ?? 0 %} {% set sumPrice = 0 %} {% endif %} {% set avgPrice = (pricedCount > 0) ? (sumPrice / pricedCount) : 0 %} {% set avgDiscount = (discountCount > 0) ? (sumDiscountPerc / discountCount) : 0 %} {% set promoShare = (itemsCount > 0) ? ((promoCount / itemsCount) * 100) : 0 %} {% set priceCompleteness = (itemsCount > 0) ? ((pricedCount / itemsCount) * 100) : 0 %} {% set promoShareInt = promoShare|round(0, 'floor') %} {% set priceCompletenessInt = priceCompleteness|round(0, 'floor') %} {% set avgDiscountInt = avgDiscount|round(0, 'floor') %}
{{ o.getObservedAt() ?? '-' }}
{% if o.getClonedFromObservationId() %} {{ translations.cloned ?? 'Cloned' }} {% endif %} {% if promoCount > 0 %} {{ translations.promotions ?? 'Promos' }}: {{ promoCount }} {% endif %} {% if priceCompletenessInt < 100 %} {{ translations.missing_prices ?? 'Missing prices' }} {% endif %}
{% if note %}
{{ note }}
{% endif %}
{{ translations.items ?? 'Items' }}
{{ itemsCount }}
{{ translations.avg_price ?? 'Avg price' }}
{% if pricedCount > 0 %} {{ avgPrice|number_format(2, '.', '') }} {% else %} - {% endif %}
{{ translations.promotions ?? 'Promos' }}
{{ promoShareInt }}%
{{ translations.avg_discount ?? 'Avg discount' }}
{% if discountCount > 0 %} {{ avgDiscountInt }}% {% else %} - {% endif %}
{# Price completeness bar (przydatniejsze niż match coverage) #}
{{ translations.price_completeness ?? 'Price completeness' }} {{ pricedCount }}/{{ itemsCount }}
{{ translations.created_at ?? 'Created' }}: {{ o.getCreatedAt() ?? '-' }}
{% endfor %}
{% endif %}
{# ========================== MODAL: CREATE OBSERVATION ========================== #} {% endblock %} {% block scripts %} {% endblock %}