import { MentionsElement } from "../../types/elements";
import { NumericPropertyInput } from "../ui/NumericPropertyInput";
import { ColorPropertyInput } from "../ui/ColorPropertyInput";
import { getEditorFeatureFlags, getPdfBuilderData } from "../../utils/editorFeatures";
type MentionTemplateOption = {
key: string;
label: string;
text: string;
};
type MentionThemeOption = {
id: string;
name: string;
preview: any;
styles: {
backgroundColor: string;
borderColor: string;
textColor: string;
separatorColor: string;
borderWidth: number;
borderStyle: string;
showBackground?: boolean;
};
};
// Composant Toggle personnalisé
const Toggle = ({
checked,
onChange,
label,
description,
}: {
checked: boolean;
onChange: (checked: boolean) => void;
label: string;
description: string;
}) => (
onChange(!checked)}
style={{
position: "relative",
width: "44px",
height: "24px",
backgroundColor: checked ? "#007bff" : "#ccc",
borderRadius: "12px",
cursor: "pointer",
transition: "background-color 0.2s ease",
border: "none",
}}
>
{description}
);
interface MentionsPropertiesProps {
element: MentionsElement;
onChange: (elementId: string, property: string, value: unknown) => void;
activeTab: {
[key: string]: "fonctionnalites" | "personnalisation" | "positionnement";
};
setActiveTab: (tabs: {
[key: string]: "fonctionnalites" | "personnalisation" | "positionnement";
}) => void;
}
export function MentionsProperties({
element,
onChange,
activeTab,
setActiveTab,
}: MentionsPropertiesProps) {
const mentionsCurrentTab = activeTab[element.id] || "fonctionnalites";
const setMentionsCurrentTab = (
tab: "fonctionnalites" | "personnalisation" | "positionnement",
) => {
setActiveTab({ ...activeTab, [element.id]: tab });
};
const { canUseEditorThemes } = getEditorFeatureFlags();
const customMention: MentionTemplateOption = {
key: "custom",
label: "Personnalisé",
text: "",
};
const medleyMention: MentionTemplateOption = {
key: "medley",
label: "Médley (Combinaison)",
text: "",
};
const freeMentionOptions: MentionTemplateOption[] = [
{
key: "legal",
label: "Mentions légales",
text: "Document établi sous la responsabilité de l'entreprise. Toutes les informations sont confidentielles.",
},
{
key: "payment",
label: "Conditions de paiement",
text: "Paiement dû dans les délais convenus. Tout retard peut entraîner des pénalités.",
},
{
key: "tva",
label: "TVA et mentions fiscales",
text: "TVA non applicable, art. 293 B du CGI. Mention : auto-entrepreneur soumise à l'impôt sur le revenu.",
},
];
const pdfBuilderData = getPdfBuilderData();
const premiumMentionOptions = ((pdfBuilderData.mentionsTemplatesPremium ?? []) as MentionTemplateOption[]);
const allMentionOptions = [customMention, medleyMention, ...freeMentionOptions, ...premiumMentionOptions];
const allMentionKeys = new Set(allMentionOptions.map((mention) => mention.key));
const mentionsThemes: MentionThemeOption[] = [
{
id: "clean",
name: "Propre",
preview: (
),
styles: {
backgroundColor: "#ffffff",
borderColor: "#e5e7eb",
textColor: "#374151",
separatorColor: "#e5e7eb",
borderWidth: 1,
borderStyle: "solid",
showBackground: true,
},
},
{
id: "subtle",
name: "Discret",
preview: (
),
styles: {
backgroundColor: "#f9fafb",
borderColor: "#e5e7eb",
textColor: "#6b7280",
separatorColor: "#cbd5e1",
borderWidth: 1,
borderStyle: "solid",
showBackground: true,
},
},
{
id: "highlighted",
name: "Surligné",
preview: (
),
styles: {
backgroundColor: "#eff6ff",
borderColor: "#bfdbfe",
textColor: "#1d4ed8",
separatorColor: "#60a5fa",
borderWidth: 1,
borderStyle: "solid",
showBackground: true,
},
},
{
id: "elegant",
name: "Élégant",
preview: (
),
styles: {
backgroundColor: "#ffffff",
borderColor: "#ddd6fe",
textColor: "#6d28d9",
separatorColor: "#8b5cf6",
borderWidth: 1,
borderStyle: "solid",
showBackground: true,
},
},
];
const getMedleySeparator = (separatorKey?: string) => {
const separatorMap = {
double_newline: "\n\n",
single_newline: "\n",
dash: " - ",
bullet: " • ",
pipe: " | ",
};
return (
separatorMap[
(separatorKey || "double_newline") as keyof typeof separatorMap
] || "\n\n"
);
};
// Fonction pour générer le contenu des mentions depuis les propriétés d'entreprise
const generateMentionsContent = (props?: any) => {
const config = props || element;
const mentionParts: string[] = [];
const pluginCompany = pdfBuilderData.company || {};
const NOT_SET = "Non indiqué";
const pick = (elementVal: string | undefined, companyVal: string): string => {
const v = (elementVal && elementVal.trim()) ? elementVal.trim() : companyVal;
return v === NOT_SET ? "" : v;
};
// Email
if (config.showEmail !== false) {
const v = pick(config.email, pluginCompany.email || "");
if (v) mentionParts.push(v);
}
// Téléphone
if (config.showPhone !== false) {
const v = pick(config.phone, pluginCompany.phone || "");
if (v) mentionParts.push(v);
}
// Adresse (opt-in : doit être explicitement activé)
if (config.showAddress === true) {
const v = pick(config.address, pluginCompany.address || "");
if (v) mentionParts.push(v);
}
// SIRET
if (config.showSiret !== false) {
const v = pick(config.siret, pluginCompany.siret || "");
if (v) mentionParts.push(`SIRET: ${v}`);
}
// TVA
if (config.showVat !== false) {
const v = pick(config.tva, pluginCompany.vat || "");
if (v) mentionParts.push(`TVA: ${v}`);
}
// RCS (opt-in)
if (config.showRcs === true) {
const v = pick(config.rcs, pluginCompany.rcs || "");
if (v) mentionParts.push(`RCS: ${v}`);
}
// Capital (opt-in)
if (config.showCapital === true) {
const v = pick(config.capital, pluginCompany.capital || "");
if (v) mentionParts.push(`Capital: ${v}`);
}
const separator = config.separator || " • ";
const companyText = mentionParts.join(separator);
const currentMentionType = config.mentionType || element.mentionType || "custom";
const selectedText =
currentMentionType === "medley"
? buildSelectedMentionsContent(config)
: currentMentionType !== "custom"
? predefinedMentions.find((mention) => mention.key === currentMentionType)?.text || ""
: "";
const blockSeparator =
currentMentionType === "medley"
? getMedleySeparator(config.medleySeparator)
: "\n\n";
return [companyText, selectedText].filter(Boolean).join(blockSeparator);
};
const buildSelectedMentionsContent = (config?: any) => {
const source = config || element;
const selectedMentions = source.selectedMentions || [];
if (selectedMentions.length === 0) {
return "";
}
return selectedMentions
.map(
(key: string) =>
predefinedMentions.find((m) => m.key === key)?.text,
)
.filter(Boolean)
.join(getMedleySeparator(source.medleySeparator));
};
// Met à jour une propriété ET régénère le texte (pour le mode infos entreprise)
const handlePropertyChangeWithContentUpdate = (property: string, value: unknown) => {
onChange(element.id, property, value);
const generatedProperties = new Set([
"showEmail",
"showPhone",
"showAddress",
"showSiret",
"showVat",
"showRcs",
"showCapital",
"separator",
"email",
"phone",
"address",
"siret",
"tva",
"rcs",
"capital",
]);
// Les champs d'entreprise doivent toujours régénérer le texte stocké
// pour garder le bloc société + mention sélectionnée synchronisé.
if (generatedProperties.has(property)) {
const updatedConfig = { ...element, [property]: value };
const newText = generateMentionsContent(updatedConfig);
onChange(element.id, "text", newText);
}
};
const predefinedMentions = allMentionOptions;
// Détecter automatiquement le type de mention basé sur le texte actuel
const detectMentionType = () => {
const currentText = element.text || "";
const currentMentionType = element.mentionType || "custom";
// Si un type connu est déjà défini et que ce n'est pas custom, le garder
if (
currentMentionType &&
currentMentionType !== "custom" &&
allMentionKeys.has(currentMentionType)
) {
return currentMentionType;
}
// Pour le medley, vérifier s'il y a des mentions sélectionnées
if (element.selectedMentions && element.selectedMentions.length > 0) {
return "medley";
}
// Sinon, essayer de détecter automatiquement
const matchingMention = predefinedMentions.find(
(mention) =>
mention.key !== "medley" &&
mention.key !== "custom" &&
mention.text === currentText,
);
return matchingMention ? matchingMention.key : "custom";
};
const currentMentionType = detectMentionType();
const currentMentionTheme =
mentionsThemes.find((theme) => theme.id === element.theme) ?? mentionsThemes[0];
const applyMentionTheme = (themeId: string) => {
const theme = mentionsThemes.find((item) => item.id === themeId);
if (!theme) {
return;
}
onChange(element.id, "theme", theme.id);
onChange(element.id, "backgroundColor", theme.styles.backgroundColor);
onChange(element.id, "borderColor", theme.styles.borderColor);
onChange(element.id, "borderWidth", theme.styles.borderWidth);
onChange(element.id, "borderStyle", theme.styles.borderStyle);
onChange(element.id, "textColor", theme.styles.textColor);
onChange(element.id, "separatorColor", theme.styles.separatorColor);
onChange(element.id, "showBackground", theme.styles.showBackground !== false);
};
return (
<>
{/* Système d'onglets pour Mentions */}
{/* Onglet Fonctionnalités */}
{mentionsCurrentTab === "fonctionnalites" && (
<>
{/* Section Propriétés de mentions - Affichage des informations d'entreprise */}
{(
[
{ prop: "showEmail", label: "Email", companyKey: "email", optIn: false },
{ prop: "showPhone", label: "Téléphone", companyKey: "phone", optIn: false },
{ prop: "showAddress", label: "Adresse", companyKey: "address", optIn: true },
{ prop: "showSiret", label: "SIRET", companyKey: "siret", optIn: false },
{ prop: "showVat", label: "TVA", companyKey: "vat", optIn: false },
{ prop: "showRcs", label: "RCS", companyKey: "rcs", optIn: true },
{ prop: "showCapital", label: "Capital", companyKey: "capital", optIn: true },
] as const
).map(({ prop, label, companyKey, optIn }) => {
const companyVal = (pdfBuilderData.company || {})[companyKey] || "";
const hint =
companyVal && companyVal !== "Non indiqué"
? companyVal.length > 32
? companyVal.substring(0, 32) + "…"
: companyVal
: null;
const isChecked = optIn
? (element as any)[prop] === true
: (element as any)[prop] !== false;
return (
);
})}
{/* Aperçu en temps réel du contenu généré */}
{(() => {
const preview = generateMentionsContent();
return preview ? (
Aperçu
{currentMentionTheme.name}
{preview}
) : (
Aucune donnée d'entreprise disponible — configurez vos
informations dans les Paramètres du plugin.
);
})()}
{/* Section Médley - Sélection des mentions à combiner */}
{currentMentionType === "medley" && (
{predefinedMentions
.filter((m) => m.key !== "medley" && m.key !== "custom")
.map((mention) => {
const selectedMentions = element.selectedMentions || [];
const isSelected = selectedMentions.includes(mention.key);
return (
);
})}
{element.selectedMentions?.length ?? 0} mention(s)
sélectionnée(s)
{(element.selectedMentions?.length ?? 0) > 0 && (
• Dimensions ajustables manuellement (avec clipping)
)}
{(element.selectedMentions?.length ?? 0) > 0 && (
)}
)}
onChange(element.id, "showSeparator", e.target.checked)
}
style={{ marginRight: "8px" }}
/>
Ligne de séparation avant les mentions
onChange(element.id, "separatorColor", e.target.value)
}
style={{
width: "100%",
padding: "4px",
border: "1px solid #ccc",
borderRadius: "4px",
cursor: "pointer",
}}
/>
onChange(
element.id,
"separatorWidth",
parseInt(e.target.value) || 1,
)
}
style={{
width: "100%",
padding: "6px",
border: "1px solid #ccc",
borderRadius: "4px",
fontSize: "12px",
}}
/>
{/* Section Affichage du fond */}
Affichage du fond
onChange(element.id, "showBackground", checked)
}
label="Afficher le fond"
description="Affiche un fond coloré derrière les mentions"
/>
>
)}
{/* Onglet Personnalisation */}
{mentionsCurrentTab === "personnalisation" && (
<>
{canUseEditorThemes && (
{mentionsThemes.map((theme) => (
applyMentionTheme(theme.id)}
style={{
cursor: "pointer",
border:
element.theme === theme.id
? "2px solid #007bff"
: "2px solid transparent",
borderRadius: "6px",
padding: "6px",
backgroundColor: "#ffffff",
transition: "all 0.2s ease",
}}
title={theme.name}
>
{theme.name}
{theme.preview}
))}
)}
{/* Section Couleur de fond - visible seulement si showBackground est activé */}
{element.showBackground !== false && (
onChange(element.id, "backgroundColor", value)
}
/>
)}
>
)}
{/* Onglet Positionnement */}
{mentionsCurrentTab === "positionnement" && (
<>
onChange(element.id, "x", value)}
/>
onChange(element.id, "y", value)}
/>
onChange(element.id, "width", value)}
/>
{currentMentionType === "medley" && (
Redimensionner manuellement active le clipping du texte
)}
onChange(element.id, "height", value)}
/>
{currentMentionType === "medley" && (
Redimensionner manuellement active le clipping du texte
)}
onChange(element.id, "padding", value)}
/>
>
)}
>
);
}