feat(5.16+5.17): tipografie/antet branded + tipuri cont, planuri si enforcement
PRD 5.16 — propagare design finalizata (system font stack, fara IBM Plex self-hostat): - US-001/002/008: tokeni --font-ui/--font-mono (system stack) + scala --fs-*; zero @font-face si zero /static/fonts/; landing aliniat la acelasi stack - US-003: RAR online = dot compact in antet + meniu burger; banda rosie DOAR pe blocat (invariant zero-silent-failures pastrat) - US-010: antet "ROMFAST AUTOPASS" + nume service + /login brandeit 2 coloane + badge plan; meniu burger cu separatoare; gate strict pe is_authenticated - US-011: selector tema pill icon+eticheta (reuse THEMES) - US-004/005/006/007: bug-fix editor prestatii (picker cod+denumire, add_extra in mod operatii, cod ales se salveaza fara "+", Renunta inchide via closest) - US-012/013: landing Autentificare->/login; wizard import colapsat + 4 pasi pe tokeni - fix VERIFY E2E: contoare duplicate pe 390px (inline display:flex batea @media) -> CSS + test-lock PRD 5.17 — tipuri de cont + trial Pro 30z + enforcement DUR: - US-001/002/008: accounts.tier + trial_until (migrare aditiva defensiva); app/plans.py sursa unica (PLANS, FREE_MONTHLY_LIMIT=60, effective_tier(now injectabil), monthly_usage, CONSUMED_STATUSES); create_account trial Pro 30z; CLI set-tier (protejat id=1, audit) - US-003/004/005: enforce volum 60/luna INAINTE de build_key pe ambele canale (PLAN_LIMITA_LUNARA, 3 niveluri + log_event); gate API Pro+ (PLAN_FARA_API 403 actionabil); valideaza/nomenclator raman permise; downgrade lazy; flag AUTOPASS_ENFORCE_PLANS (kill-switch) - US-006: badge plan antet + linie burger + consum N/60 + warn>=80% + 6 stari + copy RO pluralizat + banner one-time trial->Gratuit + pagina Cont Regresie: 1380 passed, 0 failed, 1 deselected (live). E2E browser pe 390/1280 confirmat. Backend trimitere (worker/masina stari/idempotenta/contract RAR) NEATINS. Lucrul 5.18 (corpus kNN) ramane separat, necomis. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
{# prima_inregistrare poate veni din context (web_upload_import) sau derivat din sample_rows #}
|
||||
{%- set prima_inreg = prima_inregistrare if prima_inregistrare is defined else (sample_rows[0] if sample_rows else none) -%}
|
||||
<div class="card">
|
||||
<h2 style="font-size:15px; margin:0 0 12px;">
|
||||
<h2 style="font-size:var(--fs-md); margin:0 0 12px;">
|
||||
Mapare coloane —
|
||||
<span class="muted" style="font-weight:400;">{{ filename or ("import #" ~ import_id) }}</span>
|
||||
</h2>
|
||||
@@ -20,19 +20,19 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<p class="muted" style="margin:0 0 12px; font-size:13px;">
|
||||
<p class="muted" style="margin:0 0 12px; font-size:var(--fs-sm);">
|
||||
Asociaza fiecare coloana din fisier cu campul canonic corespunzator.
|
||||
Maparea se retine automat pentru fisiere cu acelasi antet.
|
||||
</p>
|
||||
|
||||
{# Tabel orizontal preview: antet + prima inregistrare (US-003) #}
|
||||
{# Tabel orizontal preview: antet + prima inregistrare (compatibilitate teste) #}
|
||||
<div class="tablewrap" style="margin-bottom:16px;">
|
||||
<table class="preview-antet" style="border-collapse:collapse; font-size:12px; width:100%; min-width:max-content;">
|
||||
<table class="preview-antet" style="border-collapse:collapse; font-size:var(--fs-xs); width:100%; min-width:max-content;">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for col in columns %}
|
||||
<th style="padding:4px 10px; text-align:left; background:var(--card); border:1px solid var(--line);
|
||||
white-space:nowrap; font-weight:600; font-size:12px; color:var(--ink);">
|
||||
white-space:nowrap; font-weight:600; font-size:var(--fs-xs); color:var(--ink);">
|
||||
{{ col }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
@@ -44,7 +44,7 @@
|
||||
{% for col in columns %}
|
||||
{%- set val = prima_inreg.get(col, '') | string -%}
|
||||
<td style="padding:4px 10px; border:1px solid var(--line); white-space:nowrap;
|
||||
font-size:11px; color:var(--muted); max-width:160px; overflow:hidden; text-overflow:ellipsis;"
|
||||
font-size:var(--fs-xs); color:var(--muted); max-width:160px; overflow:hidden; text-overflow:ellipsis;"
|
||||
title="{{ val }}">
|
||||
{{ val[:40] }}{% if val | length > 40 %}…{% endif %}
|
||||
</td>
|
||||
@@ -53,7 +53,7 @@
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="{{ columns | length }}"
|
||||
style="padding:6px 10px; border:1px solid var(--line); font-size:12px;
|
||||
style="padding:6px 10px; border:1px solid var(--line); font-size:var(--fs-xs);
|
||||
color:var(--muted); font-style:italic; text-align:center;">
|
||||
Antet fara randuri de date
|
||||
</td>
|
||||
@@ -69,7 +69,7 @@
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token or '' }}">
|
||||
|
||||
<div style="margin-bottom:8px; display:flex; align-items:center; gap:10px; flex-wrap:wrap;">
|
||||
<label for="format-data" style="font-size:13px; color:var(--muted);">
|
||||
<label for="format-data" style="font-size:var(--fs-sm); color:var(--muted);">
|
||||
Format data
|
||||
</label>
|
||||
<input type="text" id="format-data" name="format_data"
|
||||
@@ -77,66 +77,97 @@
|
||||
placeholder="ex: DD.MM.YYYY"
|
||||
style="max-width:160px;"
|
||||
aria-describedby="format-data-hint">
|
||||
<span id="format-data-hint" class="muted" style="font-size:12px;">
|
||||
<span id="format-data-hint" class="muted" style="font-size:var(--fs-xs);">
|
||||
sau YYYY-MM-DD, MM/DD/YYYY etc.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{% for col in columns %}
|
||||
{%- set sugg = fuzzy_suggestions.get(col, []) -%}
|
||||
{%- set best = sugg[0].camp_canonic if sugg else '' -%}
|
||||
<input type="hidden" name="colname" value="{{ col }}">
|
||||
<div class="maprow">
|
||||
<div class="mapcol grow">
|
||||
<div><strong>{{ col }}</strong></div>
|
||||
{% if sugg %}
|
||||
<div class="muted" style="font-size:12px; margin-top:2px;">
|
||||
sugestie: <span class="sugg">{{ sugg[0].camp_canonic }}
|
||||
({{ sugg[0].score | round | int }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{%- set ns = namespace(samples=[]) -%}
|
||||
{%- for row in sample_rows -%}
|
||||
{%- if row.get(col) is not none and row.get(col) != '' -%}
|
||||
{%- set ns.samples = ns.samples + [row[col] | string] -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
{% if ns.samples %}
|
||||
<div class="muted" style="font-size:11px; margin-top:2px;">
|
||||
ex: {{ ns.samples[:2] | join(", ") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="mapcol" style="min-width:200px;">
|
||||
<label for="canon-{{ loop.index }}"
|
||||
style="display:block; font-size:12px; color:var(--muted); margin-bottom:2px;">
|
||||
Camp canonic
|
||||
</label>
|
||||
<select id="canon-{{ loop.index }}" name="canon">
|
||||
<option value="">— ignorat —</option>
|
||||
{% for field_key, field_label in canonical_fields %}
|
||||
<option value="{{ field_key }}"
|
||||
{% if field_key == best %}selected{% endif %}>
|
||||
{{ field_key }} — {{ field_label }}
|
||||
</option>
|
||||
{# Tabel mapare: coloana din fisier | exemplu | camp RAR (mockup 5.16 / US-013) #}
|
||||
<div class="tablewrap" style="margin-bottom:16px;">
|
||||
<table style="border-collapse:collapse; width:100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="font-size:var(--fs-xs); width:34%; padding:6px 10px; text-align:left;
|
||||
background:var(--card2); border-bottom:1px solid var(--line); color:var(--muted);
|
||||
font-weight:600; text-transform:uppercase; letter-spacing:.04em;">
|
||||
Coloana din fisier
|
||||
</th>
|
||||
<th style="font-size:var(--fs-xs); width:28%; padding:6px 10px; text-align:left;
|
||||
background:var(--card2); border-bottom:1px solid var(--line); color:var(--muted);
|
||||
font-weight:600; text-transform:uppercase; letter-spacing:.04em;">
|
||||
Exemplu
|
||||
</th>
|
||||
<th style="font-size:var(--fs-xs); padding:6px 10px; text-align:left;
|
||||
background:var(--card2); border-bottom:1px solid var(--line); color:var(--muted);
|
||||
font-weight:600; text-transform:uppercase; letter-spacing:.04em;">
|
||||
Camp RAR
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for col in columns %}
|
||||
{%- set sugg = fuzzy_suggestions.get(col, []) -%}
|
||||
{%- set best = sugg[0].camp_canonic if sugg else '' -%}
|
||||
{%- set ns = namespace(samples=[]) -%}
|
||||
{%- for row in sample_rows -%}
|
||||
{%- if row.get(col) is not none and row.get(col) != '' -%}
|
||||
{%- set ns.samples = ns.samples + [row[col] | string] -%}
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
<tr style="border-bottom:1px solid var(--line);">
|
||||
<td style="padding:9px 10px; vertical-align:top;">
|
||||
<input type="hidden" name="colname" value="{{ col }}">
|
||||
<strong style="font-family:var(--font-mono); font-size:var(--fs-sm);">{{ col }}</strong>
|
||||
{% if sugg %}
|
||||
<div class="muted" style="font-size:var(--fs-xs); margin-top:3px;">
|
||||
sugestie: <span class="sugg">{{ sugg[0].camp_canonic }}
|
||||
({{ sugg[0].score | round | int }}%)</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="padding:9px 10px; vertical-align:top;">
|
||||
{% if ns.samples %}
|
||||
<span style="font-family:var(--font-mono); font-size:var(--fs-xs); color:var(--muted);">
|
||||
{{ ns.samples[:2] | join(", ") }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="muted" style="font-size:var(--fs-xs);">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="padding:9px 10px; vertical-align:top;">
|
||||
<label for="canon-{{ loop.index }}"
|
||||
style="display:block; font-size:var(--fs-xs); color:var(--muted); margin-bottom:3px;">
|
||||
Camp canonic
|
||||
</label>
|
||||
<select id="canon-{{ loop.index }}" name="canon"
|
||||
style="width:100%; font-size:var(--fs-base); min-height:38px;">
|
||||
<option value="">— ignorat —</option>
|
||||
{% for field_key, field_label in canonical_fields %}
|
||||
<option value="{{ field_key }}"
|
||||
{% if field_key == best %}selected{% endif %}>
|
||||
{{ field_key }} — {{ field_label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div style="margin-top:16px; display:flex; align-items:center; gap:12px; flex-wrap:wrap;">
|
||||
<button type="submit"
|
||||
{% if not prima_inreg %}disabled aria-disabled="true"{% endif %}
|
||||
style="min-height:44px; padding:10px 24px; font-size:14px;{% if not prima_inreg %} opacity:0.5; cursor:not-allowed;{% endif %}">
|
||||
style="min-height:44px; padding:10px 24px; font-size:var(--fs-md);{% if not prima_inreg %} opacity:0.5; cursor:not-allowed;{% endif %}">
|
||||
Salveaza si continua la preview
|
||||
</button>
|
||||
{% if not prima_inreg %}
|
||||
<span style="font-size:12px; color:var(--err);">
|
||||
<span style="font-size:var(--fs-xs); color:var(--err);">
|
||||
Fisierul nu contine randuri de date — incarca un fisier cu cel putin o inregistrare.
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="muted" style="font-size:12px;">
|
||||
<span class="muted" style="font-size:var(--fs-xs);">
|
||||
maparea se retine pentru fisiere cu acelasi antet
|
||||
</span>
|
||||
{% endif %}
|
||||
@@ -144,7 +175,7 @@
|
||||
</form>
|
||||
|
||||
<div style="margin-top:12px;">
|
||||
<a href="/" class="muted" style="font-size:13px;">Incarca alt fisier</a>
|
||||
<a href="/" class="muted" style="font-size:var(--fs-sm);">Incarca alt fisier</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user