diff --git a/components/TemplateEditor.tsx b/components/TemplateEditor.tsx index 3381beb..2f57e27 100644 --- a/components/TemplateEditor.tsx +++ b/components/TemplateEditor.tsx @@ -1,3 +1,4 @@ + import React, { useState, useEffect, useCallback } from 'react'; import { EmailTemplate } from '../types'; import { saveTemplate, generateSQL, generateSelectSQL, getTemplates, generateTemplateKey, generateN8nCode } from '../services/storage'; @@ -14,6 +15,18 @@ interface Props { onSave: () => void; } +// Fallback for crypto.randomUUID in non-secure (HTTP) contexts +const generateUUID = () => { + if (typeof crypto !== 'undefined' && crypto.randomUUID) { + return crypto.randomUUID(); + } + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random() * 16 | 0; + const v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +}; + const DEFAULT_HEADER = `

La Mia Azienda

`; @@ -45,7 +58,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => { const [showAiModal, setShowAiModal] = useState(false); const [nameError, setNameError] = useState(''); - // Variable detection logic const detectVariables = useCallback(() => { const regex = /\{\{([\w\d_-]+)\}\}/g; const allText = `${subject} ${header} ${body} ${footer}`; @@ -58,7 +70,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => { detectVariables(); }, [detectVariables]); - // Clear name error when typing useEffect(() => { if (nameError) setNameError(''); }, [name]); @@ -68,29 +79,35 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => { if (!newKey) { setNameError('Il nome del template non può essere vuoto o contenere solo simboli.'); - alert('Il nome del template non può essere vuoto.'); return; } setIsSaving(true); try { + console.log("Inizio processo di salvataggio per:", name); + // Check for duplicates - const allTemplates = await getTemplates(); + let allTemplates: EmailTemplate[] = []; + try { + allTemplates = await getTemplates(); + } catch (err) { + console.error("Errore durante il recupero dei template esistenti:", err); + // Non blocchiamo necessariamente, ma avvisiamo + } + const isDuplicate = allTemplates.some(t => { - // Exclude current template if we are editing if (initialTemplate && t.id === initialTemplate.id) return false; return generateTemplateKey(t.name) === newKey; }); if (isDuplicate) { setNameError('Esiste già un template con questo nome.'); - alert('Un template con questo nome (o ID risultante) esiste già. Per favore scegli un nome univoco.'); setIsSaving(false); return; } const newTemplate: EmailTemplate = { - id: initialTemplate?.id || crypto.randomUUID(), + id: initialTemplate?.id || generateUUID(), name, description, subject, @@ -100,10 +117,14 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => { variables: detectedVars, updatedAt: new Date().toISOString() }; + + console.log("Invio dati al server...", newTemplate); await saveTemplate(newTemplate); + console.log("Salvataggio completato con successo"); onSave(); - } catch (e) { - alert("Impossibile salvare il template. Controlla i log del server."); + } catch (e: any) { + console.error("ERRORE SALVATAGGIO:", e); + alert(`Errore: ${e.message || 'Impossibile salvare il template'}`); } finally { setIsSaving(false); } @@ -158,7 +179,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => { } }; - // Maps internal tab keys to Italian display names const tabNames: Record = { header: 'Testata', body: 'Corpo', @@ -167,7 +187,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => { return (
- {/* Top Bar */}
- {/* Left: Inputs */}
-
- {/* Metadata */}
@@ -241,7 +257,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => {
- {/* Variable Manager */}
Variabili Attive @@ -257,7 +272,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => {
- {/* Tabs */}
{(['header', 'body', 'footer'] as const).map((tab) => (
- {/* Editor Area */}
@@ -291,7 +304,7 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => {
= ({ initialTemplate, onBack, onSave }) => {
- {/* Right: Live Preview */}
@@ -316,17 +328,13 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => { Renderizzato come HTML standard
- {/* Scrollable container: flex-1 ensures it takes available space, overflow-y-auto enables scrolling */}
- {/* mx-auto centers the card without using flexbox on the parent which can cause scroll issues */}
- {/* Simulate Subject Line in Preview */}
Oggetto:

{subject.replace(/\{\{([\w\d_-]+)\}\}/g, (match, p1) => `${match}`)}

- {/* Email Content */}
@@ -335,7 +343,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => {
- {/* SQL Modal */} {showSqlModal && (
@@ -350,7 +357,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => {
- {/* INSERT Section */}
@@ -385,7 +391,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => {
- {/* n8n Code Section */}
)} - {/* AI Modal */} {showAiModal && (
@@ -446,11 +450,6 @@ const Editor: React.FC = ({ initialTemplate, onBack, onSave }) => { {!isGenerating && }
- {!process.env.API_KEY && ( -

- Nota: API_KEY non rilevata nell'ambiente. Questa funzione richiede una chiave API Gemini. -

- )}
)}