refactor: kod sablony cenovky vznika z nazvu
Kod pre hromadny import = nazov sablony bez diakritiky, velkymi pismenami, medzery a interpunkcia -> _ (napr. Super cena so zlavou -> SUPER_CENA_SO_ZLAVOU). Samostatne pole Kod v editore je nahradene zobrazenim kodu pri nazve s upozornenim, ak rovnaky kod ma ina sablona. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4510e4f999
commit
905170f66e
4 files changed
+20
-30
No files matched your search
@@ -7,7 +7,7 @@
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { BadgeTemplate, BadgeLayer } from '@/types/newsletter';
|
||||
import { supabase } from '@/lib/supabase';
|
||||
import { renderCleanBadgeSvg, BadgeProductData, BadgeRow, badgeFromRow, normalizeBadgeCode } from '@/lib/badgeUtils';
|
||||
import { renderCleanBadgeSvg, BadgeProductData, BadgeRow, badgeFromRow, badgeCode } from '@/lib/badgeUtils';
|
||||
import {
|
||||
Tag,
|
||||
Plus,
|
||||
@@ -45,7 +45,6 @@ function toBadgeDbPayload(badge: BadgeTemplate) {
|
||||
svg_raw: badge.svgRaw,
|
||||
layers_config: {
|
||||
layers: badge.layersConfig,
|
||||
code: badge.code ? normalizeBadgeCode(badge.code) : null,
|
||||
width: badge.width || 110,
|
||||
align: badge.align || 'center',
|
||||
paddingTop: badge.paddingTop ?? 4,
|
||||
@@ -275,9 +274,9 @@ export const BadgeEditorModal: React.FC<BadgeEditorModalProps> = ({
|
||||
|
||||
const isDirty = !!badge && JSON.stringify(badge) !== savedJson;
|
||||
// Kód pre import musí byť jedinečný
|
||||
const codeTaken =
|
||||
!!badge?.code &&
|
||||
badges.some((b) => b.id !== badge.id && b.code && normalizeBadgeCode(b.code) === normalizeBadgeCode(badge.code));
|
||||
// Kód pre hromadný import vzniká z názvu – dve šablóny nesmú mať rovnaký
|
||||
const code = badge ? badgeCode(badge) : '';
|
||||
const codeTaken = !!code && badges.some((b) => b.id !== badge?.id && badgeCode(b) === code);
|
||||
const layer = badge?.layersConfig.find((l) => l.id === selectedId) || null;
|
||||
const vb = useMemo(() => viewBoxOf(badge?.svgRaw), [badge?.svgRaw]);
|
||||
const canvasWidth = Math.round(300 * zoom);
|
||||
@@ -648,19 +647,13 @@ export const BadgeEditorModal: React.FC<BadgeEditorModalProps> = ({
|
||||
/>
|
||||
)}
|
||||
{badge && (
|
||||
<label className="flex items-center gap-1 shrink-0" title="Kód pre hromadný import: v stĺpci cenovky v CSV/JSON/XML zadajte tento kód">
|
||||
<span className="text-[10px] font-bold text-gray-500">Kód</span>
|
||||
<input
|
||||
value={badge.code || ''}
|
||||
onChange={(e) => commit({ ...badge, code: e.target.value.toUpperCase() }, false)}
|
||||
onBlur={() => badge.code && commit({ ...badge, code: normalizeBadgeCode(badge.code) }, false)}
|
||||
placeholder={normalizeBadgeCode(badge.name) || 'KOD'}
|
||||
className={`w-32 text-[11px] font-mono uppercase border rounded-lg px-1.5 py-1 ${
|
||||
codeTaken ? 'border-red-400 bg-red-50' : 'border-gray-200'
|
||||
}`}
|
||||
/>
|
||||
{codeTaken && <span className="text-[10px] font-bold text-red-600">už používa iná šablóna</span>}
|
||||
</label>
|
||||
<span
|
||||
className={`shrink-0 text-[10px] font-mono rounded-md px-1.5 py-0.5 ${codeTaken ? 'text-red-700 bg-red-50 border border-red-300' : 'text-gray-500 bg-gray-100'}`}
|
||||
title="Kód pre hromadný import (vzniká z názvu): v stĺpci cenovky v CSV/JSON/XML zadajte tento kód alebo názov"
|
||||
>
|
||||
{code || '—'}
|
||||
{codeTaken && ' · rovnaký kód má iná šablóna'}
|
||||
</span>
|
||||
)}
|
||||
{isDirty && <span className="text-[10px] font-bold text-amber-600 bg-amber-50 border border-amber-200 rounded-full px-2 py-0.5">Neuložené</span>}
|
||||
{savedFlash && <span className="text-[10px] font-bold text-emerald-600 flex items-center gap-1"><Check className="w-3 h-3" /> Uložené</span>}
|
||||
|
||||
@@ -4,7 +4,7 @@ import React, { useState } from 'react';
|
||||
import Papa from 'papaparse';
|
||||
import { XMLParser } from 'fast-xml-parser';
|
||||
import { BadgeTemplate, ProductItem } from '@/types/newsletter';
|
||||
import { findBadgeByCode, normalizeBadgeCode } from '@/lib/badgeUtils';
|
||||
import { findBadgeByCode, badgeCode } from '@/lib/badgeUtils';
|
||||
import { Upload, Check, AlertCircle, FileText, X } from 'lucide-react';
|
||||
import { supabase } from '@/lib/supabase';
|
||||
|
||||
@@ -305,9 +305,9 @@ export const ProductImportModal: React.FC<ImportModalProps> = ({
|
||||
<p className="text-[11px] text-gray-500 mt-1">
|
||||
Kódy šablón:{' '}
|
||||
{availableBadges.length
|
||||
? availableBadges.map((b) => normalizeBadgeCode(b.code || b.name)).join(', ')
|
||||
? availableBadges.map(badgeCode).join(', ')
|
||||
: 'zatiaľ žiadne šablóny'}
|
||||
{' '}(kód nastavíte v Editore cenoviek; funguje aj názov šablóny)
|
||||
{' '}(kód vzniká z názvu šablóny; v súbore môže byť kód aj presný názov)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ export interface BadgeRow {
|
||||
| BadgeLayer[]
|
||||
| {
|
||||
layers?: BadgeLayer[];
|
||||
code?: string | null;
|
||||
width?: number;
|
||||
align?: BadgeTemplate['align'];
|
||||
paddingTop?: number;
|
||||
@@ -30,7 +29,6 @@ export function badgeFromRow(d: BadgeRow): BadgeTemplate {
|
||||
return {
|
||||
id: d.id,
|
||||
name: d.name,
|
||||
code: cfg?.code || undefined,
|
||||
svgRaw: d.svg_raw || '',
|
||||
layersConfig: cfg ? cfg.layers || [] : (d.layers_config as BadgeLayer[]) || [],
|
||||
width: cfg?.width || 110,
|
||||
@@ -40,7 +38,8 @@ export function badgeFromRow(d: BadgeRow): BadgeTemplate {
|
||||
};
|
||||
}
|
||||
|
||||
// Kód šablóny: veľké písmená bez diakritiky, medzery a pomlčky → _ (napr. "Super cena" → SUPER_CENA)
|
||||
// Kód šablóny pre hromadný import = názov šablóny upravený na kód:
|
||||
// bez diakritiky, veľké písmená, medzery a interpunkcia → _ (napr. „Super cena – 1+1“ → SUPER_CENA_1_1)
|
||||
export function normalizeBadgeCode(value: string | null | undefined): string {
|
||||
return String(value || '')
|
||||
.normalize('NFD')
|
||||
@@ -51,14 +50,13 @@ export function normalizeBadgeCode(value: string | null | undefined): string {
|
||||
.replace(/^_+|_+$/g, '');
|
||||
}
|
||||
|
||||
// Šablóna podľa hodnoty zo súboru: najprv kód, potom názov šablóny
|
||||
export const badgeCode = (b: Pick<BadgeTemplate, 'name'>) => normalizeBadgeCode(b.name);
|
||||
|
||||
// Šablóna podľa hodnoty zo súboru (kód alebo názov – oboje sa porovná po úprave na kód)
|
||||
export function findBadgeByCode(badges: BadgeTemplate[], value: string | null | undefined): BadgeTemplate | undefined {
|
||||
const wanted = normalizeBadgeCode(value);
|
||||
if (!wanted) return undefined;
|
||||
return (
|
||||
badges.find((b) => b.code && normalizeBadgeCode(b.code) === wanted) ||
|
||||
badges.find((b) => normalizeBadgeCode(b.name) === wanted)
|
||||
);
|
||||
return badges.find((b) => badgeCode(b) === wanted);
|
||||
}
|
||||
|
||||
export interface BadgeProductData {
|
||||
|
||||
@@ -219,7 +219,6 @@ export interface BadgeLayer {
|
||||
export interface BadgeTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
code?: string; // kód pre hromadný import (napr. SUPER_CENA) – stĺpec v CSV/JSON/XML určí šablónu
|
||||
svgBackgroundUrl?: string;
|
||||
svgRaw?: string;
|
||||
layersConfig: BadgeLayer[];
|
||||
|
||||
Reference in new issue
Block a user