// customize-shared.jsx — shared model/validation + content-type & social metadata // used by the two redesign proposals (Parcours guidé / Atelier visuel). // Exports to window: czMakeUnit, czUnitErrors, czResize, CZ_TYPES, CZ_SOCIAL, CzIcon, czLabel function czIsEmail(v) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test((v || "").trim()); } function czIsPhone(v) { return /^[+0-9 ().-]{6,}$/.test((v || "").trim()); } function czIsUrl(v) { return /^https?:\/\/.+\..+/.test((v || "").trim()); } function czMakeUnit() { return { contentType: "contact", firstName: "", lastName: "", phone: "", email: "", instagram: "", snapchat: "", tiktok: "", message: "", url: "", text: "", plate: "white", code: "black", }; } function czUnitErrors(u, t) { const e = {}; if (u.contentType === "contact") { if (!u.firstName.trim()) e.firstName = t("err_required"); if (!u.lastName.trim()) e.lastName = t("err_required"); if (!czIsPhone(u.phone)) e.phone = u.phone ? t("err_phone") : t("err_required"); if (!czIsEmail(u.email)) e.email = u.email ? t("err_email") : t("err_required"); } else if (u.contentType === "text") { if (!u.text.trim()) e.text = t("err_required"); } else if (u.contentType === "url") { if (!czIsUrl(u.url)) e.url = u.url ? t("err_url") : t("err_required"); } return e; } function czResize(units, count) { const next = units.slice(0, count); while (next.length < count) next.push(czMakeUnit()); return next; } // FR/EN copy local to the proposals (keeps i18n.jsx untouched). const CZ_STR = { fr: { step_model: "Votre pack", step_type: "Que contient le QR ?", step_info: "Vos informations", step_colors: "Vos couleurs", step_model_sub: "Combien de porte-clés", step_type_sub: "Choisissez ce qui s'affiche au scan", step_info_sub: "Elles seront encodées dans le QR", step_colors_sub: "Plaque et motif du code", continue: "Continuer", edit: "Modifier", done: "Validé", of: "sur", unit: "Porte-clé", plate: "Plaque", code: "Code", social_title: "Réseaux sociaux", social_sub: "Accessibles d'un tap depuis votre fiche contact", optional: "facultatif", studio_kicker: "Atelier", studio_title: "Composez votre porte-clé", live: "Aperçu en direct", scan_me: "Scannez-moi", recap: "Récapitulatif", add: "Ajouter au panier", copy_all: "Appliquer à tous", filling: "à compléter", complete: "complet", type_help_contact: "Nom, téléphone, email et réseaux.", type_help_text: "Un message libre.", type_help_url: "Un lien web.", }, en: { step_model: "Your pack", step_type: "What's in the QR?", step_info: "Your details", step_colors: "Your colors", step_model_sub: "How many keychains", step_type_sub: "Pick what shows on scan", step_info_sub: "Encoded into your QR", step_colors_sub: "Plate and code pattern", continue: "Continue", edit: "Edit", done: "Done", of: "of", unit: "Keychain", plate: "Plate", code: "Code", social_title: "Social media", social_sub: "One tap away from your contact card", optional: "optional", studio_kicker: "Studio", studio_title: "Compose your keychain", live: "Live preview", scan_me: "Scan me", recap: "Summary", add: "Add to cart", copy_all: "Apply to all", filling: "to fill in", complete: "complete", type_help_contact: "Name, phone, email and socials.", type_help_text: "A free message.", type_help_url: "A web link.", }, }; function czLabel(lang, key) { return (CZ_STR[lang] || CZ_STR.fr)[key] || key; } // content types with icons (24x24 stroke paths) const CZ_TYPES = [ { id: "contact", icon: "contact" }, { id: "text", icon: "text" }, { id: "url", icon: "url" }, ]; // social channels — representative glyphs + brand-ish tints (not exact logos) const CZ_SOCIAL = [ { id: "instagram", label: "Instagram", tint: "#E1306C", icon: "instagram", prefix: "instagram.com/" }, { id: "snapchat", label: "Snapchat", tint: "#F7B500", icon: "snapchat", prefix: "snapchat.com/add/" }, { id: "tiktok", label: "TikTok", tint: "#111111", icon: "tiktok", prefix: "tiktok.com/@" }, ]; function CzIcon({ name, size = 22, color = "currentColor", stroke = 1.8 }) { const common = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round" }; const P = (d, extra) => React.createElement("path", Object.assign({ d }, extra || {})); let kids; switch (name) { case "contact": kids = [P("M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2", { key: 1 }), React.createElement("circle", { key: 2, cx: 9, cy: 7, r: 4 }), P("M22 21v-2a4 4 0 0 0-3-3.87", { key: 3 }), P("M16 3.13a4 4 0 0 1 0 7.75", { key: 4 })]; break; case "text": kids = [P("M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", { key: 1 }), P("M8 9h8", { key: 2 }), P("M8 13h5", { key: 3 })]; break; case "url": kids = [P("M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71", { key: 1 }), P("M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71", { key: 2 })]; break; case "instagram": kids = [React.createElement("rect", { key: 1, x: 2, y: 2, width: 20, height: 20, rx: 5 }), React.createElement("circle", { key: 2, cx: 12, cy: 12, r: 4 }), React.createElement("circle", { key: 3, cx: 17.5, cy: 6.5, r: 0.6, fill: color })]; break; case "snapchat": kids = [P("M12 3c2.5 0 4 1.8 4 4.2 0 1.1-.1 2 .1 2.6.3.7 1.2.6 1.8 1 .3.5-.4 1-1.4 1.4-.5.2-.6.4-.5.9.4 1.6 2.4 2.2 2.4 2.8 0 .5-1.3.5-2 .9-.4.2-.2.9-.7 1.1-.6.2-1.3-.4-2.4-.1-1 .3-1.6 1.4-2.8 1.4s-1.8-1.1-2.8-1.4c-1.1-.3-1.8.3-2.4.1-.5-.2-.3-.9-.7-1.1-.7-.4-2-.4-2-.9 0-.6 2-1.2 2.4-2.8.1-.5 0-.7-.5-.9-1-.4-1.7-.9-1.4-1.4.6-.4 1.5-.3 1.8-1 .2-.6.1-1.5.1-2.6C8 4.8 9.5 3 12 3z", { key: 1 })]; break; case "tiktok": kids = [P("M9 12a4 4 0 1 0 4 4V4c.5 2.3 2.3 4 4.5 4.2", { key: 1 })]; break; case "check": kids = [P("M20 6 9 17l-5-5", { key: 1 })]; break; case "scan": kids = [P("M3 7V5a2 2 0 0 1 2-2h2", { key: 1 }), P("M17 3h2a2 2 0 0 1 2 2v2", { key: 2 }), P("M21 17v2a2 2 0 0 1-2 2h-2", { key: 3 }), P("M7 21H5a2 2 0 0 1-2-2v-2", { key: 4 }), P("M7 12h10", { key: 5 })]; break; default: kids = []; } return React.createElement("svg", common, kids); } Object.assign(window, { czMakeUnit, czUnitErrors, czResize, czIsEmail, czIsPhone, czIsUrl, CZ_TYPES, CZ_SOCIAL, CzIcon, czLabel });