effective price is only highlighted if changed

This commit is contained in:
Priec
2026-06-22 11:10:12 +02:00
parent bf8f8e54c9
commit 088fcb60a1
6 changed files with 210 additions and 54 deletions

View File

@@ -0,0 +1,97 @@
{% extends "admin/base.html" %}
{% import "macros/ui.html" as ui %}
{% block title %}{{ t(key="set-negotiated-price", lang=lang | default(value='sk')) }}{% endblock title %}
{% block crumb %}{{ t(key="admin-customers", lang=lang | default(value='sk')) }}{% endblock crumb %}
{% block content %}
<div class="flex items-center justify-between gap-3">
<div>
<div class="flex items-center gap-2">
<h1 class="text-2xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ product.name }}</h1>
{{ ui::badge(label=t(key="negotiated-price", lang=lang | default(value='sk')), variant="info") }}
</div>
<p class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ customer.name }}</p>
</div>
{{ ui::button(variant="outline-secondary", label=t(key="back", lang=lang | default(value='sk')), href="/admin/customers/" ~ customer.id, size="px-3 py-2 text-sm") }}
</div>
{% if error %}
<div class="mt-4 max-w-md">{{ ui::alert_danger(message=t(key=error, lang=lang | default(value='sk'))) }}</div>
{% endif %}
<form method="post" action="/admin/customers/{{ customer.id }}/prices/{{ product.id }}"
x-data="{
price: '{{ negotiated }}',
regular: {{ product.regular_cents }},
num(v) { let n = parseFloat(String(v).replace(',', '.')); return isFinite(n) ? n : null; },
get afterCents() { let f = this.num(this.price); return f === null ? null : Math.round(f * 100); },
money(c) { return (c / 100).toFixed(2); },
get valid() { let a = this.afterCents; return a !== null && a > 0; }
}"
class="mt-6 max-w-md space-y-5 rounded-radius border-2 border-secondary/60 bg-surface p-6 dark:border-secondary-dark/60 dark:bg-surface-dark-alt">
{{ ui::csrf_field() }}
<p class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="negotiated-price-hint", lang=lang | default(value='sk')) }}</p>
<!-- reference prices -->
<div class="space-y-2 rounded-radius bg-surface-alt px-4 py-3 dark:bg-surface-dark/40">
<div class="flex items-center justify-between gap-3 text-sm">
<span class="text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="price", lang=lang | default(value='sk')) }}</span>
<span class="tabular-nums text-on-surface-strong dark:text-on-surface-dark-strong">{{ product.regular_price }} {{ product.currency }}</span>
</div>
<div class="flex items-center justify-between gap-3 text-sm">
<span class="text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="business-price", lang=lang | default(value='sk')) }}</span>
<span class="tabular-nums {% if product.business_reduced %}font-medium text-danger{% else %}text-on-surface-strong dark:text-on-surface-dark-strong{% endif %}">{{ product.business_price }} {{ product.currency }}</span>
</div>
<div class="flex items-center justify-between gap-3 text-sm">
<span class="text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="effective-price", lang=lang | default(value='sk')) }}</span>
<span class="tabular-nums font-medium {% if product.effective_differs %}text-primary dark:text-primary-dark{% else %}text-on-surface-strong dark:text-on-surface-dark-strong{% endif %}">{{ product.effective_price }} {{ product.currency }}</span>
</div>
</div>
<!-- negotiated price input -->
<div class="space-y-1.5">
<label for="price" class="text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="negotiated-price", lang=lang | default(value='sk')) }}</label>
{{ ui::input(name="price", id="price", value=negotiated, placeholder="0.00", attrs='inputmode="decimal" x-model="price"') }}
</div>
<!-- live preview -->
<div x-show="afterCents !== null" x-cloak
class="space-y-2 rounded-radius border border-outline bg-surface-alt px-4 py-3 dark:border-outline-dark dark:bg-surface-dark/40">
<div class="flex items-center justify-between gap-3">
<span class="text-sm text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="negotiated-price", lang=lang | default(value='sk')) }}</span>
<span class="text-lg font-semibold tabular-nums" :class="valid ? 'text-secondary dark:text-secondary-dark' : 'text-on-surface/40 dark:text-on-surface-dark/40'">
<span x-text="money(afterCents)"></span> {{ product.currency }}
</span>
</div>
<p x-show="!valid" class="text-xs text-danger">{{ t(key="discount-must-be-positive", lang=lang | default(value='sk')) }}</p>
</div>
<div class="flex flex-wrap gap-3 pt-2">
{{ ui::button(variant="secondary", label=t(key="save", lang=lang | default(value='sk')), type="submit") }}
{% if has_negotiated %}
{{ ui::button(variant="outline-danger", label=t(key="remove", lang=lang | default(value='sk')), type="submit", attrs=`formaction="/admin/customers/` ~ customer.id ~ `/prices/` ~ product.id ~ `/remove" onclick="return confirm('` ~ t(key="negotiated-remove-confirm", lang=lang | default(value='sk')) ~ `')"`) }}
{% endif %}
</div>
</form>
{% if collision %}
<!-- collision resolution: two assigned profiles cover this product -->
<section class="mt-4 max-w-md rounded-radius border border-warning/60 bg-surface p-6 dark:bg-surface-dark-alt">
<div class="flex items-center gap-2">
<h2 class="text-lg font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="automated-price", lang=lang | default(value='sk')) }}</h2>
{{ ui::badge(label=t(key="collision", lang=lang | default(value='sk')), variant="warning") }}
</div>
<form method="post" action="/admin/customers/{{ customer.id }}/resolutions/{{ product.id }}" class="mt-3 flex items-center gap-2">
{{ ui::csrf_field() }}
<select name="profile_id" class="rounded-radius border border-outline bg-surface-alt px-2 py-1.5 text-sm dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark">
{% for c in covering %}
<option value="{{ c.id }}" {% if c.id == auto_profile_id %}selected{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
{{ ui::button(label=t(key="resolve", lang=lang | default(value='sk')), type="submit", size="px-3 py-1.5 text-sm") }}
</form>
</section>
{% endif %}
{% endblock content %}

View File

@@ -49,10 +49,9 @@
<thead class="{{ ui::thead_cls() }}">
<tr>
{{ ui::th(label=t(key="product", lang=lang | default(value='sk'))) }}
{{ ui::th(label=t(key="public-price", lang=lang | default(value='sk'))) }}
{{ ui::th(label=t(key="automated-price", lang=lang | default(value='sk'))) }}
{{ ui::th(label=t(key="negotiated-price", lang=lang | default(value='sk'))) }}
{{ ui::th(label=t(key="effective-price", lang=lang | default(value='sk')), align="text-right") }}
{{ ui::th(label=t(key="business-price", lang=lang | default(value='sk'))) }}
{{ ui::th(label=t(key="effective-price", lang=lang | default(value='sk'))) }}
{{ ui::th(label=t(key="actions", lang=lang | default(value='sk')), align="text-right") }}
</tr>
</thead>
<tbody class="{{ ui::tbody_cls() }}">
@@ -60,46 +59,28 @@
<tr class="{{ ui::row_cls() }}">
<td class="px-4 py-3 font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ product.name }}</td>
<td class="px-4 py-3 tabular-nums">
{% if product.on_public_sale %}
<span class="font-medium text-danger">{{ product.public_price }} {{ product.currency }}</span>
{% if product.business_reduced %}
<span class="font-medium text-danger">{{ product.business_price }} {{ product.currency }}</span>
<span class="ml-1 text-xs text-on-surface/50 line-through dark:text-on-surface-dark/50">{{ product.regular_price }}</span>
{% else %}
{{ product.public_price }} {{ product.currency }}
{{ product.business_price }} {{ product.currency }}
{% endif %}
</td>
<td class="px-4 py-3 tabular-nums">
{% if product.auto_price %}
<div>{{ product.auto_price }} {{ product.currency }}</div>
{% if product.collision %}
<div class="mt-1">{{ ui::badge(label=t(key="collision", lang=lang | default(value='sk')), variant="warning") }}</div>
<form method="post" action="/admin/customers/{{ customer.id }}/resolutions/{{ product.product_id }}" class="mt-1 flex items-center gap-1">
{{ ui::csrf_field() }}
<select name="profile_id" class="rounded-radius border border-outline bg-surface-alt px-2 py-1 text-xs dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark">
{% for c in product.covering %}
<option value="{{ c.id }}" {% if c.id == product.auto_profile_id %}selected{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
{{ ui::button(label=t(key="resolve", lang=lang | default(value='sk')), type="submit", size="px-2 py-1 text-xs") }}
</form>
{% elif product.auto_profile_name %}
<div class="text-xs text-on-surface/60 dark:text-on-surface-dark/60">{{ product.auto_profile_name }}</div>
{% endif %}
{% else %}
<span class="text-on-surface/40 dark:text-on-surface-dark/40"></span>
{% endif %}
<span class="font-medium {% if product.effective_differs %}text-primary dark:text-primary-dark{% else %}text-on-surface-strong dark:text-on-surface-dark-strong{% endif %}">{{ product.effective_price }} {{ product.currency }}</span>
{% if product.collision %}<span class="ml-1">{{ ui::badge(label=t(key="collision", lang=lang | default(value='sk')), variant="warning") }}</span>{% endif %}
</td>
<td class="px-4 py-3">
<form method="post" action="/admin/customers/{{ customer.id }}/prices/{{ product.product_id }}" class="flex items-center gap-2">
{{ ui::csrf_field() }}
{{ ui::input(name="price", value=product.manual_price | default(value=""), placeholder="0.00", width="w-28", attrs='inputmode="decimal"') }}
{{ ui::button(label=t(key="save", lang=lang | default(value='sk')), type="submit", size="px-3 py-1.5 text-xs") }}
{% if product.manual_price %}
{{ ui::button(variant="outline-danger", label=t(key="remove", lang=lang | default(value='sk')), type="submit", size="px-3 py-1.5 text-xs", attrs='formaction="/admin/customers/' ~ customer.id ~ '/prices/' ~ product.product_id ~ '/remove"') }}
<div class="flex flex-wrap justify-end gap-2">
{{ ui::button(variant="outline-secondary", label=t(key="set-negotiated-price", lang=lang | default(value='sk')), href="/admin/customers/" ~ customer.id ~ "/prices/" ~ product.product_id ~ "/edit", size="px-3 py-1.5 text-xs") }}
{% if product.has_negotiated %}
<form method="post" action="/admin/customers/{{ customer.id }}/prices/{{ product.product_id }}/remove"
onsubmit="return confirm('{{ t(key="negotiated-remove-confirm", lang=lang | default(value='sk')) }}')">
{{ ui::csrf_field() }}
{{ ui::button(variant="outline-danger", label=t(key="remove", lang=lang | default(value='sk')), type="submit", size="px-3 py-1.5 text-xs") }}
</form>
{% endif %}
</form>
</td>
<td class="px-4 py-3 text-right tabular-nums">
<span class="font-medium {% if product.is_business %}text-primary dark:text-primary-dark{% else %}text-on-surface-strong dark:text-on-surface-dark-strong{% endif %}">{{ product.effective_price }} {{ product.currency }}</span>
</div>
</td>
</tr>
{% endfor %}