feat: vsetky sablony cenoviek z novych SVG (zakladne, appkaci, vernostna karta)

- 22 sablon: Super cena / Super vyhodne / Zakladna v 3 farbach so SK aj bez,
  Appkaci (SK / bez), Vernostna karta (so zlavou / bez); orezane na viditelny
  obsah, rovnaka mierka (sirka = viewBox x 130 / 80,1)
- nove premenne {zlava_app}, {jednotkova_cena_app}, {cena_bez_karty},
  {zlava_bez_karty}, {jednotkova_cena_s_kartou}, {jednotkova_cena_bez_karty};
  zvisle natiahnutie textu (scaleY), viac nazvov pisma, napis zlava len pri zlave
- Vernostna karta je predvolena cenovka sekcie Vernostne zlavy
- produkty pri nacitani dostanu aktualnu verziu svojej sablony
- import: zlava pre appkacov a s vernostnou kartou z poznamky, jednotkova cena
  pre appkacov prepocitana, sirka cenovky podla sablony
- okraj stitkov predajni podla mierky cenovky
- postup prevodu v scripts/badges

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub_HostacnyandClaude Opus 5.5 committed 2026-10-22 10:36:37 +02:00
1 parent 50af94f954
commit af35334688
13 files changed
+300 -107

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
/* eslint-disable @typescript-eslint/no-explicit-any -- pomocný skript nad JSON výstupmi z prevodu */
// Zostavenie šablón cenoviek z výstupu convert-illustrator-svg.py (public/__svg/conv.json) a vrstiev
// zmeraných v prehliadači (scripts/badges/layers.json, vytvára measure.js). Zapíše public/__svg/rows.json
// (riadky tabuľky badges) a public/__svg/compare.html (porovnanie s originálmi).
// Spustenie: npx tsx --tsconfig tsconfig.json scripts/badges/build-templates.ts [filter názvu] [mierka]
import fs from 'fs';
import { renderCleanBadgeSvg, badgeFromRow } from '../../src/lib/badgeUtils';
const conv = JSON.parse(fs.readFileSync('public/__svg/conv.json', 'utf8'));
const L = JSON.parse(fs.readFileSync('scripts/badges/layers.json', 'utf8'));
const rows = L.map(([file, name, tags, crop, width, layers]: any) => {
const c = conv.find((x: any) => x.file === file);
return {
file, name,
svg_raw: `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="${crop.join(' ')}">${c.inner}</svg>`,
layers_config: {
width, align: 'center', paddingTop: 4, paddingBottom: 4, importTags: tags,
layers: layers.map(([text, al, x, y, fontSize, fill, font, ifs, ioy, iox, ls, sy]: any, i: number) => ({
id: `l-${c.key}-${i}`, type: 'text', text, align: { c: 'center', l: 'left', r: 'right' }[al as 'c'], x, y, fontSize,
fill: fill === 'w' ? '#ffffff' : fill,
fontFamily: font === 'A' ? 'Aptly' : 'korolev-condensed, Korolev Condensed', fontSource: 'adobe', fontWeight: font === 'A' ? '700' : font.slice(1),
...(ifs !== '' ? { indexFontSize: ifs, indexOffsetY: ioy, indexOffsetX: iox, longPriceScale: 0.8 } : {}),
...(ls !== '' ? { letterSpacing: ls } : {}), ...(sy !== '' ? { scaleY: sy } : {})
})).map((l: any, _i: number, all: any[]) => {
// nápis „zľava“ len pri vyplnenej zľave – patrí k najbližšej premennej {zlava…}
if (l.text !== 'zľava') return l;
const d = all.filter((x: any) => /^\{zlava/.test(x.text)).sort((a: any, b: any) => Math.abs(a.y - l.y) - Math.abs(b.y - l.y))[0];
return d ? { ...l, showIfFilled: d.text } : l;
})
}
};
});
fs.writeFileSync('public/__svg/rows.json', JSON.stringify(rows));
const sample = { priceMain: '0', priceSub: '00', discountText: '00%', unitPrice: 'jednotková cena 000 EUR/kg',
appPriceMain: '0', appPriceSub: '00', appDiscount: '00%', appUnitPrice: 'jednotková cena 000 EUR/kg',
cardPriceMain: '0', cardPriceSub: '00', cardDiscount: '00%', cardUnitPrice: 'jednotková cena 000 EUR/kg',
noCardPriceMain: '0', noCardPriceSub: '00', noCardDiscount: '00%', noCardUnitPrice: 'jednotková cena 000 EUR/kg' };
const only = process.argv[2] ? new RegExp(process.argv[2]) : null;
const scale = Number(process.argv[3] || 1);
let html = '<!doctype html><html><head><link rel="stylesheet" href="https://use.typekit.net/loe7muj.css"></head><body style="background:#333;color:#fff;font:11px sans-serif;margin:8px"><div style="display:flex;flex-wrap:wrap;gap:12px">';
for (const r of rows) {
if (only && !only.test(r.name)) continue;
const b = badgeFromRow({ id: r.file, name: r.name, svg_raw: r.svg_raw, layers_config: r.layers_config });
const [x, y, w, h] = r.svg_raw.match(/viewBox="([^"]+)"/)![1].split(' ').map(Number);
const px = Math.round(r.layers_config.width * scale);
html += `<figure style="margin:0"><div style="display:flex;gap:4px;align-items:flex-start"><img src="/__svg/${r.file.replace(' ', '_')}#svgView(viewBox(${[x, y, w, h].join(',')}))" width="${px}" height="${Math.round(px * h / w)}" style="background:#555"><div style="width:${px}px;background:#555">${renderCleanBadgeSvg(b, sample as any, px)}</div></div><figcaption>${r.name} · ${r.layers_config.width}px</figcaption></figure>`;
}
fs.writeFileSync('public/__svg/compare.html', html + '</div></body></html>');
console.log(rows.length, 'ok');