"""Administrarea sabloanelor de eticheta (designer vizual).""" from __future__ import annotations from typing import Any from flask import (Blueprint, Response, flash, jsonify, redirect, render_template, request, url_for) from .. import designer from ..db import all_settings from ..labels import print_payload, sample_package, sample_recipe from ..repositories import ( ValidationError, create_template, delete_template, get_template, list_label_fields, list_templates, save_template_design, update_template, ) bp = Blueprint("label_templates", __name__, url_prefix="/sabloane") @bp.get("") def index(): return render_template("templates/list.html", templates=list_templates()) @bp.post("//stergere") def remove(template_id: int): try: delete_template(template_id) flash("Sabloul a fost sters.", "success") except ValidationError as exc: flash(str(exc), "error") return redirect(url_for("label_templates.index")) @bp.get("//previzualizare") def preview(template_id: int): template = get_template(template_id) if template is None: flash("Sabloul nu exista.", "error") return redirect(url_for("label_templates.index")) demo = sample_package() payload = print_payload(demo, template, all_settings(), sample_recipe()) return render_template("label_print.html", payload=payload, auto=False, preview=True) # --------------------------------------------------------------------------- # Designer vizual # --------------------------------------------------------------------------- def _designer_context(template: dict) -> dict: width = float(template.get("width_mm") or 100) height = float(template.get("height_mm") or 60) design = designer.load_design(template.get("design_json") or "", width, height) if design is None: design = designer.default_design(width, height) custom_rows = list_label_fields() demo_fields = designer.apply_custom_fields(designer.demo_fields(), custom_rows) fields = [ {"key": key, "label": label, "sample": sample} for key, label, sample in designer.FIELD_CATALOG ] fields += designer.custom_field_catalog(custom_rows, demo_fields) return { "template": template, "design": design, "fields": fields, "demo_fields": demo_fields, "demo_items": designer.demo_items(), "block_types": designer.BLOCK_TYPES, "safe_mm": designer.SAFE_MARGIN_MM, "custom_count": len(custom_rows), "has_design": bool(template.get("design_json")), } @bp.get("//designer") def designer_page(template_id: int): template = get_template(template_id) if template is None: flash("Sabloul nu exista.", "error") return redirect(url_for("label_templates.index")) return render_template("templates/designer.html", **_designer_context(template)) @bp.post("//designer") def designer_save(template_id: int): template = get_template(template_id) if template is None: return jsonify({"ok": False, "error": "Sabloul nu exista."}), 404 data = request.get_json(silent=True) or {} try: design = designer.validate_design( data.get("design"), float(template.get("width_mm") or 100), float(template.get("height_mm") or 60), ) except ValidationError as exc: return jsonify({"ok": False, "error": str(exc)}), 400 save_template_design(template_id, designer.dump_design(design)) # Dimensiunea din designer devine dimensiunea sablonului. width = float(design["width_mm"]) height = float(design["height_mm"]) if (width, height) != (float(template["width_mm"]), float(template["height_mm"])): update_template(template_id, { "name": template["name"], "width_mm": width, "height_mm": height, "body_html": template["body_html"] or " ", "css": template["css"], "design_json": designer.dump_design(design), "is_default": template["is_default"], }) html, css = designer.render_preview(design, list_label_fields()) return jsonify({"ok": True, "blocks": len(design["blocks"]), "html": html, "css": css}) @bp.post("//designer/stergere") @bp.post("//designer/reset") def designer_reset(template_id: int): """Reseteaza designul la varianta implicita (nu lasă șablonul fără etichetă).""" template = get_template(template_id) if template is None: flash("Sabloul nu exista.", "error") return redirect(url_for("label_templates.index")) width = float(template.get("width_mm") or 100) height = float(template.get("height_mm") or 60) design = designer.default_design(width, height) save_template_design(template_id, designer.dump_design(design)) flash("Designul a fost resetat la varianta implicita.", "success") return redirect(url_for("label_templates.designer_page", template_id=template_id)) @bp.get("/designer/qr.svg") def designer_qr(): """Cod QR inline pentru previzualizarea din editor.""" data = (request.args.get("data") or "")[:500] svg = designer.qr_svg(data) return Response(svg, mimetype="image/svg+xml", headers={"Cache-Control": "public, max-age=3600"}) @bp.post("/designer/nou") def designer_create(): """Creeaza un sablon nou cu designer, cu numele ales de utilizator.""" width = _size_or_default(request.form.get("width_mm"), 100.0) height = _size_or_default(request.form.get("height_mm"), 60.0) design = designer.default_design(width, height) name = (request.form.get("name") or "").strip()[:80] existing = {t["name"].strip().lower() for t in list_templates()} if name: if name.lower() in existing: flash(f"Exista deja un sablon numit {name!r}. Alege alt nume.", "error") return redirect(url_for("label_templates.index")) else: # Fara nume: generam unul disponibil, ca sa nu blocam crearea. name = "Sablon designer" base = name counter = 2 while name.lower() in existing: name = f"{base} {counter}" counter += 1 template_id = create_template({ "name": name, "width_mm": design["width_mm"], "height_mm": design["height_mm"], "body_html": " ", "css": designer.DESIGN_CSS, "design_json": designer.dump_design(design), "is_default": request.form.get("is_default"), }) flash(f"Sablonul {name} a fost creat.", "success") return redirect(url_for("label_templates.designer_page", template_id=template_id)) def _size_or_default(value: Any, default: float) -> float: try: number = float(str(value).replace(",", ".")) except (TypeError, ValueError): return default return number if 10 <= number <= 500 else default @bp.post("//redenumire") def rename(template_id: int): """Schimba numele sablonului (din pagina de designer sau din lista).""" template = get_template(template_id) if template is None: flash("Sabloul nu exista.", "error") return redirect(url_for("label_templates.index")) name = (request.form.get("name") or "").strip() if not name: flash("Numele sablonului este obligatoriu.", "error") elif name == template["name"]: pass else: try: update_template(template_id, {**template, "name": name}) flash(f"Sabloul a fost redenumit in {name}.", "success") except ValidationError as exc: flash(str(exc), "error") if request.form.get("next") == "designer": return redirect(url_for("label_templates.designer_page", template_id=template_id)) return redirect(url_for("label_templates.index"))