assets_js_controllers_email-template-editor-controller.js

import { Controller } from "@hotwired/stimulus"
import EasyMDE from "easymde"

/**
 * Email Template Editor Controller
 * 
 * Extends markdown editor functionality with variable insertion support for email templates.
 * Provides buttons to insert available variables into the template.
 * 
 * Usage:
 * <div data-controller="email-template-editor"
 *      data-email-template-editor-variables-value='[{"name":"userName","description":"User name"}]'>
 *   <textarea data-email-template-editor-target="editor"></textarea>
 *   <div data-email-template-editor-target="variableButtons"></div>
 * </div>
 * 
 * Values:
 * - variables: Array of available variables [{name, description}]
 * - placeholder: Placeholder text for the editor
 * - minHeight: Minimum height of the editor
 */
class EmailTemplateEditorController extends Controller {
    static targets = ["editor", "variableButtons"]
    
    static values = {
        variables: { type: Array, default: [] },
        placeholder: { type: String, default: "Enter email template..." },
        minHeight: { type: String, default: "400px" }
    }

    initialize() {
        this.editor = null
    }

    connect() {
        // Initialize EasyMDE on the textarea
        this.editor = new EasyMDE({
            element: this.editorTarget,
            placeholder: this.placeholderValue,
            minHeight: this.minHeightValue,
            spellChecker: false,
            status: ["lines", "words", "cursor"],
            toolbar: this.buildToolbar(),
            forceSync: true,
            autosave: {
                enabled: false
            },
            previewRender: (plainText) => {
                return this.renderPreview(plainText);
            }
        })

        // Render variable insertion buttons if we have variables
        if (this.hasVariableButtonsTarget && this.variablesValue.length > 0) {
            this.renderVariableButtons()
        }

        console.log('Email template editor initialized with', this.variablesValue.length, 'variables')
    }

    disconnect() {
        if (this.editor) {
            this.editor.toTextArea()
            this.editor = null
        }
    }

    /**
     * Build custom toolbar with variable insertion button
     */
    buildToolbar() {
        const toolbar = [
            "bold",
            "italic",
            "heading",
            "|",
            "quote",
            "unordered-list",
            "ordered-list",
            "|",
            "link",
            "|",
            "preview",
            "side-by-side",
            "fullscreen",
            "|",
        ];

        // Add custom variable insertion button
        if (this.variablesValue.length > 0) {
            toolbar.push({
                name: "insert-variable",
                action: (editor) => {
                    this.showVariableMenu(editor)
                },
                className: "fa fa-code",
                title: "Insert Variable",
            });
        }

        toolbar.push("guide");

        return toolbar;
    }

    /**
     * Show variable insertion menu
     */
    showVariableMenu(editor) {
        // Get cursor position
        const cm = editor.codemirror;
        const cursor = cm.getCursor();
        
        // Create a simple prompt with variable options
        const varNames = this.variablesValue.map(v => v.name).join(', ');
        const selectedVar = prompt(`Available variables:\n${varNames}\n\nEnter variable name to insert:`);
        
        if (selectedVar) {
            const variable = this.variablesValue.find(v => v.name === selectedVar);
            if (variable) {
                cm.replaceSelection(`{{${variable.name}}}`);
            } else {
                alert('Invalid variable name');
            }
        }
    }

    /**
     * Render variable insertion buttons
     */
    renderVariableButtons() {
        const container = this.variableButtonsTarget;
        container.innerHTML = '<div class="mb-2"><strong>Available Variables:</strong> Click to insert</div>';
        
        const buttonGroup = document.createElement('div');
        buttonGroup.className = 'btn-group flex-wrap mb-3';
        buttonGroup.setAttribute('role', 'group');
        
        this.variablesValue.forEach(variable => {
            const btn = document.createElement('button');
            btn.type = 'button';
            btn.className = 'btn btn-sm btn-outline-primary';
            btn.textContent = `{{${variable.name}}}`;
            btn.title = variable.description || variable.name;
            btn.addEventListener('click', (e) => {
                e.preventDefault();
                this.insertVariable(variable.name);
            });
            
            buttonGroup.appendChild(btn);
        });
        
        container.appendChild(buttonGroup);

        // Add syntax help
        const helpText = document.createElement('div');
        helpText.className = 'alert alert-info small';
        helpText.innerHTML = '<strong>Syntax:</strong> Use <code>{{variableName}}</code> or <code>${variableName}</code> to insert variables. They will be replaced with actual values when the email is sent.';
        container.appendChild(helpText);
    }

    /**
     * Insert a variable at the current cursor position
     */
    insertVariable(variableName) {
        if (!this.editor) return;
        
        const cm = this.editor.codemirror;
        const doc = cm.getDoc();
        const cursor = doc.getCursor();
        
        doc.replaceRange(`{{${variableName}}}`, cursor);
        
        // Move cursor after the inserted text
        cm.focus();
    }

    /**
     * Render preview with variable highlighting
     */
    renderPreview(plainText) {
        // Convert markdown to HTML
        let html = this.editor.markdown(plainText);
        
        // Highlight variables in the preview
        html = html.replace(/\{\{([^}]+)\}\}/g, '<span class="badge bg-primary">{{$1}}</span>');
        html = html.replace(/\$\{([^}]+)\}/g, '<span class="badge bg-success">${$1}</span>');
        
        return html;
    }

    /**
     * Get the editor content
     */
    getValue() {
        return this.editor ? this.editor.value() : '';
    }

    /**
     * Set the editor content
     */
    setValue(value) {
        if (this.editor) {
            this.editor.value(value);
        }
    }
}

// Add to global controllers registry
if (!window.Controllers) {
    window.Controllers = {};
}
window.Controllers["email-template-editor"] = EmailTemplateEditorController;