403Webshell
Server IP : 162.214.67.83  /  Your IP : 216.73.216.67
Web Server : Apache
System : Linux dedi-13542965.clustter.com.br 5.14.0-687.20.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Jun 30 06:22:49 EDT 2026 x86_64
User : pjacortinas ( 1096)
PHP Version : 8.3.32
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/pjacortinas/www/wp-content/plugins/formlayer/assets/js/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/pjacortinas/www/wp-content/plugins/formlayer/assets/js/admin.js
jQuery(document).ready(function($){

	// Toast Notification
	function show_toast(message, type = 'success') {
		let toast = $('<div>')
			.addClass('formlayer-toast')
			.addClass(type) 
			.html(`<span class="dashicons dashicons-${type === 'success' ? 'yes' : 'warning'}"></span> ${message}`);

		$('body').append(toast); 

		toast.fadeIn(300).delay(3000).fadeOut(300, function () {
			toast.remove();
		});
	}

	/**
	 * FormLayer Admin SPA Tab Handling
	 */
	function handle_tabs(){
		let hash_full = window.location.hash.trim().replace('#', '');
		let hash = hash_full.split('/')[0];
		let $nav = $('#formlayer-main-nav');
		
		// Handle hash or query param
		if(!hash){
			let url_params = new URLSearchParams(window.location.search);
			hash = url_params.get('tab') || 'forms';
		}

		let $target_tab = $('#formlayer-tab-' + hash),
    $nav_item = $nav.find(`a[data-tab="${hash}"]`);

		if($target_tab.length){
			// Hide all tabs
			$('.formlayer-tab-content').hide();
			// Show target tab
			$target_tab.fadeIn(200);

			// Update nav state
			$nav.find('.formlayer-tab-item').removeClass('active');
			$nav_item.addClass('active');

			// Special handling for builder layout
			if(hash === 'formbuilder'){
				$('.formlayer-admin-wrapper').addClass('formlayer-builder-active');
			}else{
				$('.formlayer-admin-wrapper').removeClass('formlayer-builder-active');
				
				// Remove form_id from URL
				const url = new URL(window.location.href);
				if(url.searchParams.has('form_id')){
					url.searchParams.delete('form_id');
					window.history.replaceState({}, document.title, url.toString());
					if(typeof formlayer_admin !== 'undefined'){
						formlayer_admin.form_id = 0;
					}
				}
			}
		}else if(hash === 'forms'){
			// Fallback for empty hash or 'forms'
			$('.formlayer-tab-content').hide();
			$('#formlayer-tab-forms').show();
			$nav.find('.formlayer-tab-item').removeClass('active');
			$nav.find('a[data-tab="forms"]').addClass('active');
		}
	}

	// Tab change listener
	$(window).on('hashchange', function(){
		handle_tabs();
	});

	// Add New Form
	$('#formlayer-add-new-form').on('click', function(e){
		e.preventDefault();
		state.view = 'templates';
		state.fields = [];
		state.form_title = 'Untitled Form';
		formlayer_admin.form_id = 0;
		window.location.hash = 'formbuilder';
		render();
	});

	// Initial tab load
	handle_tabs();

	// Move all modals to body to escape transformed containers and center correctly
	$('.formlayer-modal-overlay').appendTo('body');
	
	// Select All handler
	$('#formlayer-select-all').on('change', function(){
		$('.formlayer-row-cb').prop('checked', $(this).prop('checked'));
	});

	$('.formlayer-row-cb').on('change', function(){
		if($('.formlayer-row-cb:checked').length === $('.formlayer-row-cb').length){
			$('#formlayer-select-all').prop('checked', true);
		}else{
			$('#formlayer-select-all').prop('checked', false);
		}
	});

	// Bulk Action handler
	$('#formlayer-apply-bulk').on('click', function(e){
		e.preventDefault();
		let action = $('#formlayer-bulk-action').val();
		let selected_ids = $('.formlayer-row-cb:checked').map(function(){
			return $(this).val();
		}).get();

		if(!action){
			show_toast('Please select an action.', 'error');
			return;
		}

		if(selected_ids.length === 0){
			show_toast('Please select at least one form.', 'error');
			return;
		}

		if(!confirm(`Are you sure you want to ${action} selected forms?`)){
			return;
		}

		let $btn = $(this);
		$btn.prop('disabled', true).text('Applying...');

		$.post(formlayer_admin.ajax_url, {
			action: 'formlayer_bulk_action',
			bulk_action: action,
			ids: selected_ids,
			nonce: formlayer_admin.nonce
		}, function(response){
			if(response.success){
				location.reload();
			}else{
				show_toast(response.data.message || 'Error applying bulk action', 'error');
				$btn.prop('disabled', false).text('Apply');
			}
		});
	});

	// Save Settings handler
	$('#formlayer-save-settings').on('click', function(e){
		e.preventDefault();
		let $btn = $(this);
		$status = $('#formlayer-settings-status'),
		original_text = $btn.text();
		
		let settings = {};
		$('input[name], select[name], textarea[name]').each(function(){
			let name = $(this).attr('name');
			if($(this).attr('type') === 'checkbox'){
				settings[name] = $(this).is(':checked') ? '1' : '0';
			}else{
				settings[name] = $(this).val();
			}
		});

		$btn.prop('disabled', true).text('Saving...');
		if($status.length) $status.removeClass('success error').empty();

		$.post(formlayer_admin.ajax_url, {
			action: 'formlayer_save_settings',
			settings: settings,
			nonce: formlayer_admin.nonce
		}, function(response){
			if(response.success){
				show_toast('Settings saved successfully!', 'success');
				$btn.prop('disabled', false).text(original_text);
			}else{
				show_toast(response.data.message || 'Error saving settings', 'error');
				$btn.prop('disabled', false).text(original_text);
			}
		});
	});

	// Captcha Tab Switching
	$('.captcha-tab-item').on('click', function(){
		let target = $(this).data('target');
		$('.captcha-tab-item').removeClass('active');
		$(this).addClass('active');
		
		$('.captcha-pane').removeClass('active');
		$(`#pane-${target}`).addClass('active');
	});

	// Delete form handler
	$('.formlayer-delete-form').on('click', function(e){
		e.preventDefault();
		var form_id = $(this).data('form-id');
		if(!form_id) return;
		
		if(!confirm('Are you sure you want to delete this form? This action cannot be undone.')){
			return;
		}
		
		var $btn = $(this);
		$btn.css('opacity', '0.5');
		
		$.post(formlayer_admin.ajax_url, {
			action: 'formlayer_delete_form',
			form_id: form_id,
			nonce: formlayer_admin.nonce
		}, function(response){
			if(response.success){
				show_toast('Form deleted successfully!', 'success');
				$btn.closest('tr').fadeOut(300, function(){
					$(this).remove();
					// If no rows left, show "No forms found"
					let $tbody = $('#formlayer-tab-forms .formlayer-table tbody');
					if ($tbody.find('tr').length === 0) {
						$tbody.html(`
							<tr>
								<td colspan="7" style="text-align:center; padding:60px 0; color:var(--formlayer-text-muted);">
									<div class="formlayer-empty-title">No forms found</div>
									<p style="margin:0;">Create your first form to start collecting data.</p>
								</td>
							</tr>
						`);
					}
				});
			}else{
				show_toast(response.data.message || 'Error deleting form', 'error');
				$btn.css('opacity', '1');
			}
		});
	});

	let $app = $('#formlayer-formbuilder-app');
	if(!$app.length) return;

	// State management
	let state = {
		view: (formlayer_admin.form_id && formlayer_admin.form_id !== '0') ? 'builder' : 'templates',
		template_category: 'all',
		template_search: '',
		field_search: '',
		display_id: null,
		form_title: 'Untitled Form',
		form_settings: {
			notifications: {
				enabled: true,
				to_email: '{admin_email}',
				reply_to: '',
				from_name: 'FormLayer',
				from_email: '{admin_email}',
				bcc: '',
				subject: 'New Form Submission',
				message: 'You have a new submission:\n\n{all_fields}'
			},
			confirmations: {
				type: 'message',
				message: 'Thank you for your submission!',
				redirect_url: '',
				hide_form: true
			},
			integrations: {},
			custom_css: ''
		},
		active_tab: 'editor',
		sidebar_tab: 'input-fields',
		fields: [],
		selected_field_id: null,
		categories: formlayer_admin.categories || [
			{ id: 'general', label: 'General Fields', open: true },
			{ id: 'advanced', label: 'Advanced Fields', open: false },
			{ id: 'premium', label: 'Premium Fields', open: false }
		],
		field_types: formlayer_admin.fieldTypes || [
			{ type: 'name', label: 'Name Fields', icon: 'dashicons-admin-users', category: 'general' },
			{ type: 'email', label: 'Email', icon: 'dashicons-email', category: 'general' },
			{ type: 'text', label: 'Simple Text', icon: 'dashicons-edit', category: 'general' },
			{ type: 'mask', label: 'Mask Input', icon: 'dashicons-shield', category: 'general' },
			{ type: 'textarea', label: 'Text Area', icon: 'dashicons-editor-alignleft', category: 'general' },
			{ type: 'address', label: 'Address Fields', icon: 'dashicons-location', category: 'general' },
			{ type: 'country', label: 'Country List', icon: 'dashicons-flag', category: 'general' },
			{ type: 'number', label: 'Numeric Field', icon: 'dashicons-editor-ol', category: 'general' },
			{ type: 'dropdown', label: 'Dropdown', icon: 'dashicons-arrow-down-alt2', category: 'general' },
			{ type: 'radio', label: 'Radio Field', icon: 'dashicons-marker', category: 'general' },
			{ type: 'checkbox', label: 'Checkbox', icon: 'dashicons-yes', category: 'general' },
			{ type: 'multiple', label: 'Multiple Choice', icon: 'dashicons-list-view', category: 'general' },
			{ type: 'url', label: 'Website URL', icon: 'dashicons-admin-site', category: 'general' },
			{ type: 'date', label: 'Time & Date', icon: 'dashicons-calendar-alt', category: 'general' },
			{ type: 'section', label: 'Section Break', icon: 'dashicons-minus', category: 'general' },
			{ type: 'file', label: 'File Upload', icon: 'dashicons-upload', category: 'premium' },
			{ type: 'image', label: 'Image Upload', icon: 'dashicons-format-image', category: 'premium' },
			{ type: 'phone', label: 'Phone Number', icon: 'dashicons-phone', category: 'premium' },
			{ type: 'rating', label: 'Ratings', icon: 'dashicons-star-filled', category: 'advanced' },
			{ type: 'hidden', label: 'Hidden Field', icon: 'dashicons-visibility-faint', category: 'advanced' },
			{ type: 'password', label: 'Password', icon: 'dashicons-lock', category: 'advanced' },
			{ type: 'captcha', label: 'Captcha (Spam Protection)', icon: 'dashicons-shield', category: 'advanced' },
			{ type: 'terms', label: 'Terms & Conditions', icon: 'dashicons-media-text', category: 'advanced' },
			{ type: 'gdpr', label: 'GDPR Agreement', icon: 'dashicons-shield', category: 'advanced' },
			{ type: 'submit', label: 'Submit Button', icon: 'dashicons-plus-alt', category: 'advanced' }
		],
		templates: formlayer_admin.templates || []
	};

	// Template engine helper
	function generate_unique_name_attr(base_name, ignore_id = null) {
		let slug = (base_name || '').toLowerCase().replace(/[^a-z0-9]/g, '_').replace(/_+/g, '_').replace(/^_|_$/g, '');
		if (!slug) slug = 'field';
		
		let is_unique = false;
		let name_attr = slug;
		let counter = 1;
		
		while (!is_unique) {
			let exists = state.fields.some(function(f) { 
				return f.id !== ignore_id && (f.name_attr === name_attr || (!f.name_attr && ('field_' + f.id) === name_attr)); 
			});
			if (!exists) {
				is_unique = true;
			} else {
				name_attr = slug + '_' + counter;
				counter++;
			}
		}
		return name_attr;
	}

	const utils = {
		tmpl: function(key, data = {}) {
			let html = formlayer_admin.html_templates[key] || '';
			Object.keys(data).forEach(k => {
				const val = data[k] === undefined || data[k] === null ? '' : data[k];
				html = html.replace(new RegExp(`{{${k}}}`, 'g'), val);
			});
			return html;
		}
	};


	function load_form(id){
		$.ajax({
			url: formlayer_admin.ajax_url,
			method: 'GET',
			data: {
				action: 'formlayer_load_form',
				nonce: formlayer_admin.nonce,
				form_id: id
			},
			success: function(response){
				if(typeof response === 'string'){
					try{ response = JSON.parse(response); } catch(e){}
				}
				if(response.success && response.data.form_data){
					const data = response.data.form_data;
					state.form_title = data.title || 'Untitled Form';
					state.fields = data.fields || [];
					state.form_settings = data.settings || state.form_settings;
					state.display_id = response.data.display_id;
				}else if(!response.success){
					console.error('Form load error:', response);
				}
				render();
			},
			error: function(jqXHR, textStatus, errorThrown){
				console.error("AJAX Error details:", textStatus, errorThrown, jqXHR.responseText);
				if(textStatus !== 'parsererror'){
					show_toast('Failed to load form. ' + textStatus, 'error');
				}
				render();
			}
		});
	}

	function render(){
		let current_hash = window.location.hash.replace('#', ''),
		current_tab = current_hash.split('/')[0],
		is_editing = (formlayer_admin.form_id && formlayer_admin.form_id !== '0'),
		should_update_hash = (current_tab === 'formbuilder' || (is_editing && !current_tab));

		if(state.view === 'templates'){
			if(should_update_hash){
				window.location.hash = 'formbuilder';
			}
			$('#formlayer-builder-view').hide();
			$('#formlayer-templates-view').show();
			render_templates();
		}else{
			if(should_update_hash){
				window.location.hash = 'formbuilder/customize';
			}
			$('#formlayer-templates-view').hide();
			$('#formlayer-builder-view').show();
			render_builder();
		}
	}

	function render_templates(){
		let categories = [
			{ id: 'all', label: 'All Templates', icon: 'dashicons-admin-page' },
			{ id: 'general', label: 'General', icon: 'dashicons-forms' },
			{ id: 'education', label: 'Education', icon: 'dashicons-welcome-learn-more' },
			{ id: 'marketing', label: 'Marketing', icon: 'dashicons-share' },
			{ id: 'crm', label: 'Sales & CRM', icon: 'dashicons-admin-users' },
			{ id: 'feedback', label: 'User Feedback', icon: 'dashicons-awards' },
			{ id: 'hr', label: 'HR & Talent', icon: 'dashicons-businessman' },
			{ id: 'it', label: 'IT & Technical', icon: 'dashicons-laptop' },
			{ id: 'finance', label: 'Commercial & Finance', icon: 'dashicons-money-alt' }
		];

		let search_term = state.template_search.toLowerCase();
		
		let filtered = state.templates.filter(function(t){
			let matches_cat = (state.template_category === 'all' || t.cat === state.template_category || t.id === 'scratch');
			let matches_search = t.title.toLowerCase().includes(search_term) || 
								 t.desc.toLowerCase().includes(search_term);
			return matches_cat && matches_search;
		});

		// Update Sidebar with Counts
		let cats_html = categories.map(function(cat){
			let count = state.templates.filter(function(t){ 
				let is_cat = (cat.id === 'all' ? t.id !== 'scratch' : t.cat === cat.id);
				let is_search = t.title.toLowerCase().includes(search_term) || t.desc.toLowerCase().includes(search_term);
				return is_cat && is_search;
			}).length;

			return `
				<li class="${state.template_category === cat.id ? 'active' : ''}" data-cat="${cat.id}">
					<div class="formlayer-template-cat-item">
						<span><span class="dashicons ${cat.icon}"></span> ${cat.label}</span>
					</div>
				</li>
			`;
		}).join('');
		$('#formlayer-template-cats-list').html(cats_html);

		let grid_html = filtered.length > 0 ? filtered.map(function(t){
			let badge = '';
			let is_pro = !!t.is_pro;
			let pro_active = !!formlayer_admin.is_pro;

			if(t.id === 'scratch'){
				badge = '<div class="template-header-badge" style="background:var(--fl-primary); color:#fff;">Starter</div>';
			}else if(is_pro && !pro_active){
				badge = '<div class="template-header-badge" style="background:#f59e0b; color:#fff;">PRO</div>';
			}else if(!is_pro){
				badge = `<div class="template-header-badge">${t.cat ? t.cat.charAt(0).toUpperCase() + t.cat.slice(1) : 'General'}</div>`;
			}

			let button_html = (is_pro && !pro_active) 
				? `<button class="formlayer-btn-upgrade-pro formlayer-btn-upgrade-pro-el">Upgrade to PRO</button>`
				: `<button class="formlayer-btn-use-template">Use Template</button>`;

			return utils.tmpl('template_card', {
				id: t.id,
				locked_class: is_pro && !pro_active ? 'is-pro-locked' : '',
				badge_html: badge,
				icon: t.icon,
				title: t.title,
				desc: t.desc,
				button_html: button_html
			});
		}).join('') : utils.tmpl('no_templates_found');
		$('#formlayer-templates-grid').html(grid_html);
	}

	function render_builder(){
		let shortcode = state.display_id ? `[formlayer id="${state.display_id}"]` : (formlayer_admin.form_id && formlayer_admin.form_id !== '0' ? `[formlayer id="${formlayer_admin.form_id}"]` : 'Save First....');
		
		$('#formlayer-builder-title').text(state.form_title);
		$('#formlayer-shortcode-val').val(shortcode);

		// Render Canvas Fields
		let canvas_html = state.fields.length === 0 ? render_empty_state() : render_fields();
		$('#formlayer-dropzone').html(canvas_html);

		// Render Sidebar Panes
		render_sidebar_content();
	}

	function render_empty_state(){
		return utils.tmpl('empty_state');
	}

	function refresh_canvas(){
		let canvas_html = state.fields.length === 0 ? render_empty_state() : render_fields();
		$('#formlayer-dropzone').html(canvas_html);
	}

	function render_fields(){
		return state.fields.map(function(field, index){
			let label_placement = field.label_placement || 'top',
			label_style = field.style_label_color ? `style="color:${field.style_label_color};"` : '',
			input_style = field.style_border_radius ? `style="border-radius:${field.style_border_radius}px;"` : '';
			
			let label_html = (!['submit', 'section', 'terms', 'gdpr'].includes(field.type) && label_placement !== 'hidden') ? utils.tmpl('field_label', {
				label_style: label_style,
				label: field.label || '',
				required_mark: field.required ? '<span class="required">*</span>' : ''
			}) : '';

			let actions_html = utils.tmpl('field_actions', {
				move_up_disabled: index === 0 ? 'disabled' : '',
				move_down_disabled: index === state.fields.length - 1 ? 'disabled' : ''
			});

			return utils.tmpl('field_instance', {
				id: field.id,
				active_class: state.selected_field_id === field.id ? 'active' : '',
				label_placement: label_placement,
				container_class: field.container_class || '',
				label_html: label_html,
				input_style: input_style,
				input_html: render_field_input(field),
				help_html: field.help_text ? `<div class="formlayer-field-help">${field.help_text}</div>` : '',
				actions_html: actions_html
			});
		}).join('');
	}

	function render_field_input(field){
		const options = Array.isArray(field.options) ? field.options : (typeof field.options === 'string' ? field.options.split('\n') : ['Option 1', 'Option 2', 'Option 3']);
		const normalized_options = options.map(opt => {
			if(typeof opt === 'string') return { label: opt, value: opt, default: false };
			return opt;
		});
		
		switch(field.type){
			case 'textarea': {
				const rows = field.rows || 3;
				const cols = field.cols || '';
				const t_style = cols ? `width:${cols}px !important;` : 'width:100%;';
				return `<textarea placeholder="${field.placeholder || ''}" rows="${rows}" cols="${cols}" style="${t_style}" disabled>${field.default_value || field.value || ''}</textarea>`;
			}
			case 'dropdown':
				return `
				<select class="formlayer-builder-select" disabled style="width:100%;">
					<option value="">${field.placeholder || 'Select Option'}</option>
					${normalized_options.map(function(opt){ 
						const is_selected = (field.default_value === opt.value || opt.default);
						return `<option ${is_selected ? 'selected' : ''}>${opt.label}</option>`; 
					}).join('')}
				</select>`;
			case 'radio':
				return `
				<div class="formlayer-options-preview">
					${normalized_options.map(function(opt, i){
						const is_checked = (field.default_value === opt.value || opt.default);
						return `
						<label class="formlayer-option-row">
							<input type="radio" name="preview_${field.id}" ${is_checked ? 'checked' : ''} disabled> <span>${opt.label}</span>
						</label>
					`; }).join('')}
				</div>`;
			case 'checkbox':
			case 'multiple':
				return `
				<div class="formlayer-options-preview">
					${normalized_options.map(function(opt, i){
						const is_checked = (Array.isArray(field.default_value) ? field.default_value.includes(opt.value) : (field.default_value === opt.value || opt.default));
						return `
						<label class="formlayer-option-row">
							<input type="checkbox" ${is_checked ? 'checked' : ''} disabled> <span>${opt.label}</span>
						</label>
					`; }).join('')}
				</div>`;
			case 'hidden':
				return utils.tmpl('hidden_field_preview', { value: field.value || '(No Value)' });
			case 'password':
				return `<input type="password" placeholder="${field.placeholder || '********'}" disabled>`;
			case 'terms':
				return `
					<div class="formlayer-terms-wrap">
						<input type="checkbox" disabled> 
						<span class="formlayer-terms-label">${field.terms_label || 'I agree to the <a href="#">Terms & Conditions</a>'}</span>
					</div>`;
			case 'gdpr':
				return `
					<div class="formlayer-gdpr-wrap">
						<input type="checkbox" disabled> 
						<div style="line-height:1.4;">
							<div class="formlayer-gdpr-label" style="font-size:14px; color:#475569;">${field.gdpr_label || 'Accept GDPR Policy'}</div>
							${field.gdpr_description ? `<div style="color:#64748b; font-size:12px; margin-top:4px;">${field.gdpr_description}</div>` : ''}
						</div>
					</div>`;
			case 'captcha': {
				const isPro = formlayer_admin && formlayer_admin.is_pro;
				const prov_label = { 
					hcaptcha: 'hCaptcha', 
					turnstile: isPro ? 'Cloudflare Turnstile' : 'Turnstile (Pro)', 
					recaptcha: isPro ? 'Google reCAPTCHA' : 'reCAPTCHA (Pro)' 
				}[field.captcha_provider || 'hcaptcha'];
				const theme_label = field.captcha_theme ? field.captcha_theme.charAt(0).toUpperCase() + field.captcha_theme.slice(1) : 'Global Default';
				return utils.tmpl('captcha_preview', { provider: prov_label + ' (' + theme_label + ')' });
			}
			case 'image':
			case 'file':
			case 'camera': {
				const btn_style = `background: ${field.file_btn_bg || '#5525d6'} !important; color: ${field.file_btn_color || '#ffffff'} !important;`;
				return utils.tmpl('file_upload_box', {
					btn_style: btn_style,
					btn_text: 'Choose File',
					chosen_text: 'No file chosen'
				});
			}
			case 'richtext':
				return utils.tmpl('richtext_preview');
			case 'name': {
				let sub_fields = '';
				if(field.enable_first_name !== false) sub_fields += utils.tmpl('sub_field_preview', { type:'text', placeholder: field.placeholder_first || 'First Name', label: field.label_first || 'First Name' });
				if(field.enable_middle_name) sub_fields += utils.tmpl('sub_field_preview', { type:'text', placeholder: field.placeholder_middle || 'Middle Name', label: field.label_middle || 'Middle Name' });
				if(field.enable_last_name !== false) sub_fields += utils.tmpl('sub_field_preview', { type:'text', placeholder: field.placeholder_last || 'Last Name', label: field.label_last || 'Last Name' });
				return utils.tmpl('name_fields_preview', { sub_fields: sub_fields });
			}
			case 'address': {
				let sub_fields = '';
				if(field.enable_street !== false) sub_fields += utils.tmpl('sub_field_preview', { full_width_class:'full-width', type:'text', placeholder: field.placeholder_street || 'Street Address', label: field.label_street || 'Street Address' });
				if(field.enable_city !== false) sub_fields += utils.tmpl('sub_field_preview', { type:'text', placeholder: field.placeholder_city || 'City', label: field.label_city || 'City' });
				if(field.enable_state !== false) sub_fields += utils.tmpl('sub_field_preview', { type:'text', placeholder: field.placeholder_state || 'State / Province', label: field.label_state || 'State / Province' });
				if(field.enable_zip !== false) sub_fields += utils.tmpl('sub_field_preview', { type:'text', placeholder: field.placeholder_zip || 'Zip / Postal Code', label: field.label_zip || 'Zip / Postal Code' });
				if(field.enable_country !== false) {
					sub_fields += `
					<div class="formlayer-sub-field">
						<select class="formlayer-builder-select" disabled style="width:100%;">
							<option>${field.placeholder_country || 'Select Country'}</option>
							<option>United States</option>
							<option>United Kingdom</option>
							<option>Canada</option>
						</select>
						<div class="formlayer-sub-label">${field.label_country || 'Country'}</div>
					</div>`;
				}
				return utils.tmpl('address_grid_preview', { sub_fields: sub_fields });
			}
			case 'number':
				return `<input type="number" placeholder="${field.placeholder || ''}" min="${field.min || ''}" max="${field.max || ''}" disabled>`;
			case 'phone':
				return `
				<div class="formlayer-input-icon-wrap" style="position:relative;">
					<span class="dashicons dashicons-phone" style="position:absolute; left:12px; top:50%; transform:translateY(-50%); color:#94a3b8; font-size:18px;"></span>
					<input type="tel" placeholder="${field.placeholder || 'Phone Number'}" style="padding-left:40px !important;" disabled>
				</div>`;
			case 'mask':
				return `<input type="text" placeholder="${field.placeholder || '(+1) 000-0000'}" disabled>`;
			case 'country':
				return `
				<div class="formlayer-select-wrap">
					<span class="dashicons dashicons-admin-site"></span>
					<select class="formlayer-builder-select" disabled style="width:100%;">
						<option>${field.placeholder || 'Select Country'}</option>
						<option ${field.default_value === 'US' || field.default_value === 'United States' ? 'selected' : ''}>United States</option>
						<option ${field.default_value === 'GB' || field.default_value === 'United Kingdom' ? 'selected' : ''}>United Kingdom</option>
						<option ${field.default_value === 'CA' || field.default_value === 'Canada' ? 'selected' : ''}>Canada</option>
						<option ${field.default_value === 'AU' || field.default_value === 'Australia' ? 'selected' : ''}>Australia</option>
						<option ${field.default_value === 'DE' || field.default_value === 'Germany' ? 'selected' : ''}>Germany</option>
						<option ${field.default_value === 'FR' || field.default_value === 'France' ? 'selected' : ''}>France</option>
					</select>
				</div>`;
			case 'date':
				return `
				<div class="formlayer-date-wrap">
					<input type="date" disabled>
					<span class="dashicons dashicons-calendar-alt"></span>
				</div>`;
			case 'rating':
				return `
				<div class="formlayer-rating-preview">
					${[1,2,3,4,5].map(function(i){ return `<span class="dashicons dashicons-star-filled" style="color:#e2e8f0; font-size:24px; width:24px; height:24px;"></span>`; }).join('')}
				</div>`;
			case 'submit': {
				let align = field.btn_align || 'left',
				size = field.btn_size || 'md',
				bg = field.btn_bg_color || '',
				txt = field.btn_text_color || '',
				bg_h = field.btn_bg_hover || '',
				txt_h = field.btn_text_hover || '',
				rad = field.style_border_radius ? field.style_border_radius + 'px' : '';
				
				let b_style = '';
				if(bg) b_style += `background:${bg} !important;`;
				if(txt) b_style += `color:${txt} !important;`;
				if(rad) b_style += `border-radius:${rad} !important;`;
				
				let btn_id = `btn-preview-${field.id}`;
				let hover_style = '';
				if(bg_h || txt_h) {
					hover_style = `<style>#${btn_id}:hover { 
						${bg_h ? `background: ${bg_h} !important;` : ''} 
						${txt_h ? `color: ${txt_h} !important;` : ''} 
					}</style>`;
				}

				let size_classes = { sm: 'btn-sm', md: 'btn-md', lg: 'btn-lg' },
				align_classes = { left: 'align-left', center: 'align-center', right: 'align-right', full: 'align-full' };
				
				return `${hover_style}
				<div class="formlayer-submit-preview ${align_classes[align]}">
					<button id="${btn_id}" class="formlayer-submit-btn ${size_classes[size]}" type="button" style="${b_style}">${field.label || 'Submit'}</button>
				</div>`;
			}
			case 'section':
				return `
				<div style="border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; margin-bottom: 15px;">
					<h3 style="margin:0; font-size:18px; color:#1e293b;">${field.label || 'Section Title'}</h3>
					${field.help_text ? `<p style="margin:5px 0 0 0; font-size:13px; color:#64748b;">${field.help_text}</p>` : ''}
				</div>`;
			default:
				return `<input type="text" placeholder="${field.placeholder || ''}" value="${field.default_value || field.value || ''}" disabled>`;
		}
	}

	function render_sidebar_content(){
		$('.formlayer-sidebar-tab').removeClass('active');
		$(`.formlayer-sidebar-tab[data-tab="${state.sidebar_tab}"]`).addClass('active');
		
		$('.formlayer-sidebar-pane').removeClass('active');
		if(state.sidebar_tab === 'input-fields'){
			$('#formlayer-pane-fields').addClass('active');
			render_fields_palette();
		}else{
			$('#formlayer-pane-customization').addClass('active');
			render_advanced_options();
		}
	}

	function render_fields_palette(){
		let search_term = (state.field_search || '').toLowerCase();
		
		let html = state.categories.map(function(cat){
			let fields = state.field_types.filter(function(f){ 
				let matches_cat = f.category === cat.id;
				let matches_search = !search_term || f.label.toLowerCase().includes(search_term) || f.type.toLowerCase().includes(search_term);
				return matches_cat && matches_search;
			});

			if(fields.length === 0 && search_term) return '';

			let fields_html = fields.map(function(f){
				return utils.tmpl('palette_field', {
					type: f.type,
					icon: f.icon || 'dashicons-admin-customizer',
					label: f.label
				});
			}).join('');

			return utils.tmpl('sidebar_category', {
				open_class: (cat.open || search_term) ? 'open' : '',
				id: cat.id,
				label: cat.label,
				fields_html: fields_html
			});
		}).join('');
		
		let final_html = html || `<div class="formlayer-no-results">No fields found for "${search_term}"</div>`;
		$('#formlayer-builder-categories').html(final_html);
	}

	function render_advanced_options(){
		let field = state.fields.find(function(f){ return f.id === state.selected_field_id; });
		if(!field){
			$('#formlayer-pane-customization').html('<div class="formlayer-no-field-selected">Select a field to customize</div>');
			return;
		}

		let accordion_open = state.accordion_open || 'general';

		let general_content = '';
		
		// Admin Label
		general_content += utils.tmpl('control_group', {
			label: 'Admin Field Label',
			info_html: utils.tmpl('info_icon', { title: 'Used only in the admin panel' }),
			input_html: `<input type="text" class="formlayer-input-full" data-prop="label" value="${field.label || ''}" placeholder="Name">`
		});

		// Placeholder
		if(['text', 'email', 'number', 'textarea', 'password', 'url', 'address', 'tel', 'mask', 'country'].includes(field.type)){
			general_content += utils.tmpl('control_group', {
				label: 'Placeholder',
				input_html: `<input type="text" class="formlayer-input-full" data-prop="placeholder" value="${field.placeholder || ''}" placeholder="${field.type === 'country' ? 'Select Country' : 'Placeholder text'}">`
			});
		}

		// Label Placement
		if(!['gdpr', 'submit'].includes(field.type)){
			general_content += utils.tmpl('control_group', {
				label: 'Label Placement',
				info_html: utils.tmpl('info_icon', { title: 'Control where the label appears relative to the input' }),
				input_html: `
					<select class="formlayer-input-full" data-prop="label_placement">
						<option value="top" ${field.label_placement === 'top' ? 'selected' : ''}>Top</option>
						<option value="left" ${field.label_placement === 'left' ? 'selected' : ''}>Left</option>
						<option value="right" ${field.label_placement === 'right' ? 'selected' : ''}>Right</option>
						<option value="hidden" ${field.label_placement === 'hidden' ? 'selected' : ''}>Hidden</option>
					</select>`
			});
		}

		// Options Manager
		if(['dropdown', 'radio', 'checkbox', 'multiple'].includes(field.type)){
			const options_html = (field.options || []).map((opt, idx) => {
				const is_obj = typeof opt === 'object';
				return utils.tmpl('option_edit_row', {
					index: idx,
					default_checked: (is_obj ? opt.default : false) ? 'checked' : '',
					label: is_obj ? opt.label : opt,
					value: is_obj ? opt.value : opt
				});
			}).join('') + `<button id="formlayer-btn-add-option" class="formlayer-btn-outline" style="width:100%; margin-top:10px;"><span class="dashicons dashicons-plus"></span> Add Option</button>`;

			general_content += utils.tmpl('control_group', {
				label: 'Field Options',
				input_html: `<div class="formlayer-options-manager">${options_html}</div>`
			});
		}

		// Submit Button specifics
		if(field.type === 'submit'){
			general_content += utils.tmpl('control_group', {
				label: 'Button Alignment',
				input_html: `
					<select class="formlayer-input-full" data-prop="btn_align">
						<option value="left" ${field.btn_align === 'left' ? 'selected' : ''}>Left</option>
						<option value="center" ${field.btn_align === 'center' ? 'selected' : ''}>Center</option>
						<option value="right" ${field.btn_align === 'right' ? 'selected' : ''}>Right</option>
						<option value="full" ${field.btn_align === 'full' ? 'selected' : ''}>Full Width</option>
					</select>`
			});
			general_content += utils.tmpl('control_group', {
				label: 'Button Size',
				input_html: `
					<select class="formlayer-input-full" data-prop="btn_size">
						<option value="sm" ${field.btn_size === 'sm' ? 'selected' : ''}>Small</option>
						<option value="md" ${field.btn_size === 'md' ? 'selected' : ''}>Medium</option>
						<option value="lg" ${field.btn_size === 'lg' ? 'selected' : ''}>Large</option>
					</select>`
			});
		}

		// GDPR specific
		if(field.type === 'gdpr'){
			general_content += utils.tmpl('control_group', {
				label: 'GDPR Label',
				input_html: `<input type="text" class="formlayer-input-full" data-prop="gdpr_label" value="${field.gdpr_label || 'Accept GDPR Policy'}">`
			});
			general_content += utils.tmpl('control_group', {
				label: 'Policy Description',
				input_html: `<textarea class="formlayer-input-full" data-prop="gdpr_description" style="height:60px;">${field.gdpr_description || ''}</textarea>`
			});
		}

		// Terms specific
		if(field.type === 'terms'){
			general_content += utils.tmpl('control_group', {
				label: 'Terms Label',
				input_html: `<input type="text" class="formlayer-input-full" data-prop="terms_label" value="${field.terms_label || 'I agree to the <a href=\'#\'>Terms & Conditions</a>'}">`
			});
		}

		// Default Value
		if(!['submit', 'section', 'terms', 'gdpr'].includes(field.type)){
			general_content += utils.tmpl('control_group', {
				label: 'Default Value',
				input_html: `<input type="text" class="formlayer-input-full" data-prop="default_value" value="${field.default_value || field.value || ''}" placeholder="Initial value">`
			});
		}

		// Container Class
		general_content += utils.tmpl('control_group', {
			label: 'Container CSS Class',
			input_html: `<input type="text" class="formlayer-input-full" data-prop="container_class" value="${field.container_class || ''}" placeholder="e.g. half-width">`
		});

		// Help Text
		general_content += utils.tmpl('control_group', {
			label: 'Help Message',
			input_html: `<textarea class="formlayer-input-full" data-prop="help_text" style="height:60px;" placeholder="Brief info for users">${field.help_text || ''}</textarea>`
		});

		// Style Customization Section
		let style_html = '<div class="formlayer-divider formlayer-style-divider">Style Customization</div>';
		
		style_html += `
			<div class="formlayer-control-group">
				<div class="formlayer-flex-center-between" style="margin-bottom: 8px;">
					<label class="formlayer-control-label" style="margin-bottom:0 !important;">Label Color</label>
					<input type="color" class="formlayer-input-color" data-prop="style_label_color" value="${field.style_label_color || '#334155'}" style="width:30px; height:30px;">
				</div>
				${['file', 'image', 'camera'].includes(field.type) ? `
				<div class="formlayer-flex-center-between" style="margin-bottom: 8px;">
					<label class="formlayer-control-label" style="margin-bottom:0 !important;">Button Background</label>
					<input type="color" class="formlayer-input-color" data-prop="file_btn_bg" value="${field.file_btn_bg || '#5525d6'}" style="width:30px; height:30px;">
				</div>
				<div style="display:flex; align-items:center; justify-content:space-between;">
					<label class="formlayer-control-label" style="margin-bottom:0 !important;">Button Text Color</label>
					<input type="color" class="formlayer-input-color" data-prop="file_btn_color" value="${field.file_btn_color || '#ffffff'}" style="width:30px; height:30px;">
				</div>` : ''}
			</div>`;

		const general_accordion = utils.tmpl('sidebar_accordion', {
			open_class: accordion_open === 'general' ? 'open' : '',
			id: 'general',
			title: (field.label || 'Field') + ' Settings',
			content_html: general_content + style_html
		});

		let advanced_content = '';

		// Field Name / Merge Tag
		advanced_content += utils.tmpl('control_group', {
			label: 'Name Attribute / Merge Tag',
			info_html: utils.tmpl('info_icon', { title: 'Use {field_name} in Email Notifications to output this field\'s value' }),
			input_html: `<input type="text" class="formlayer-input-full" data-prop="name_attr" value="${field.name_attr || generate_unique_name_attr(field.label, field.id)}" placeholder="${generate_unique_name_attr(field.label, field.id)}">`
		});

		if(field.type === 'name'){
			advanced_content += `
				<div class="formlayer-control-group">
					<label class="formlayer-control-label">Enable Fields</label>
					<div style="display:grid; grid-template-columns: 1fr; gap:8px;">
						<label class="formlayer-flex-center-gap8" style="font-size:13px;"><input type="checkbox" data-prop="enable_first_name" ${field.enable_first_name !== false ? 'checked' : ''}> First Name</label>
						<label class="formlayer-flex-center-gap8" style="font-size:13px;"><input type="checkbox" data-prop="enable_middle_name" ${field.enable_middle_name ? 'checked' : ''}> Middle Name</label>
						<label class="formlayer-flex-center-gap8" style="font-size:13px;"><input type="checkbox" data-prop="enable_last_name" ${field.enable_last_name !== false ? 'checked' : ''}> Last Name</label>
					</div>
				</div>
				<div class="formlayer-control-group">
					<label class="formlayer-control-label">Sub Labels</label>
					<input type="text" class="formlayer-input-full" data-prop="label_first" value="${field.label_first || 'First Name'}" style="margin-bottom:5px;">
					<input type="text" class="formlayer-input-full" data-prop="label_middle" value="${field.label_middle || 'Middle Name'}" style="margin-bottom:5px;">
					<input type="text" class="formlayer-input-full" data-prop="label_last" value="${field.label_last || 'Last Name'}">
				</div>`;
		}

		if(field.type === 'address'){
			advanced_content += `
				<div class="formlayer-control-group">
					<label class="formlayer-control-label">Enable Fields</label>
					<div style="display:grid; grid-template-columns: 1fr; gap:8px;">
						<label class="formlayer-flex-center-gap8" style="font-size:13px;"><input type="checkbox" data-prop="enable_street" ${field.enable_street !== false ? 'checked' : ''}> Street Address</label>
						<label class="formlayer-flex-center-gap8" style="font-size:13px;"><input type="checkbox" data-prop="enable_city" ${field.enable_city !== false ? 'checked' : ''}> City</label>
						<label class="formlayer-flex-center-gap8" style="font-size:13px;"><input type="checkbox" data-prop="enable_state" ${field.enable_state !== false ? 'checked' : ''}> State / Province</label>
						<label class="formlayer-flex-center-gap8" style="font-size:13px;"><input type="checkbox" data-prop="enable_zip" ${field.enable_zip !== false ? 'checked' : ''}> Zip / Postal Code</label>
						<label class="formlayer-flex-center-gap8" style="font-size:13px;"><input type="checkbox" data-prop="enable_country" ${field.enable_country !== false ? 'checked' : ''}> Country List</label>
					</div>
				</div>`;
		}

		if(field.type === 'number'){
			advanced_content += `
				<div class="formlayer-control-group" style="display:grid; grid-template-columns: 1fr 1fr; gap:10px;">
					<div>
						<label class="formlayer-control-label">Min Value</label>
						<input type="number" class="formlayer-input-full" data-prop="min" value="${field.min || ''}">
					</div>
					<div>
						<label class="formlayer-control-label">Max Value</label>
						<input type="number" class="formlayer-input-full" data-prop="max" value="${field.max || ''}">
					</div>
				</div>`;
		}

		if(field.type === 'textarea'){
			advanced_content += utils.tmpl('control_group', {
				label: 'Rows (Height)',
				input_html: `<input type="number" class="formlayer-input-full" data-prop="rows" value="${field.rows || 3}">`
			});
		}

		if(field.type === 'date'){
			advanced_content += utils.tmpl('control_group', {
				label: 'Date Format',
				input_html: `
					<select class="formlayer-input-full" data-prop="date_format">
						<option value="Y-m-d" ${field.date_format === 'Y-m-d' ? 'selected' : ''}>YYYY-MM-DD</option>
						<option value="d/m/Y" ${field.date_format === 'd/m/Y' ? 'selected' : ''}>DD/MM/YYYY</option>
						<option value="m/d/Y" ${field.date_format === 'm/d/Y' ? 'selected' : ''}>MM/DD/YYYY</option>
					</select>`
			});
		}

		if(field.type === 'captcha'){
			const isPro = formlayer_admin && formlayer_admin.is_pro;
			advanced_content += utils.tmpl('control_group', {
				label: 'Provider',
				input_html: `
					<select class="formlayer-input-full" data-prop="captcha_provider">
						<option value="hcaptcha" ${field.captcha_provider === 'hcaptcha' ? 'selected' : ''}>hCaptcha</option>
						<option value="turnstile" ${!isPro ? 'disabled' : ''} ${field.captcha_provider === 'turnstile' ? 'selected' : ''}>${!isPro ? 'Turnstile (Pro)' : 'Cloudflare Turnstile'}</option>
						<option value="recaptcha" ${!isPro ? 'disabled' : ''} ${field.captcha_provider === 'recaptcha' ? 'selected' : ''}>${!isPro ? 'reCAPTCHA (Pro)' : 'reCAPTCHA v2'}</option>
					</select>`
			});
			advanced_content += utils.tmpl('control_group', {
				label: 'Theme',
				input_html: `
					<select class="formlayer-input-full" data-prop="captcha_theme">
						<option value="light" ${field.captcha_theme === 'light' ? 'selected' : ''}>Light</option>
						<option value="dark" ${field.captcha_theme === 'dark' ? 'selected' : ''}>Dark</option>
					</select>`
			});
		}

		if(field.type === 'submit'){
			advanced_content += `
				<div class="formlayer-control-group">
					<label class="formlayer-control-label">Button Styling</label>
					<div style="display:grid; grid-template-columns: 1fr 1fr; gap:10px;">
						<div>
							<label style="font-size:11px; color:#64748b; display:block; margin-bottom:4px;">Background</label>
							<input type="color" class="formlayer-input-color" data-prop="btn_bg_color" value="${field.btn_bg_color || '#5525d6'}" style="width:100%; height:32px;">
						</div>
						<div>
							<label style="font-size:11px; color:#64748b; display:block; margin-bottom:4px;">Text</label>
							<input type="color" class="formlayer-input-color" data-prop="btn_text_color" value="${field.btn_text_color || '#ffffff'}" style="width:100%; height:32px;">
						</div>
						<div>
							<label style="font-size:11px; color:#64748b; display:block; margin-bottom:4px;">Hover Bg</label>
							<input type="color" class="formlayer-input-color" data-prop="btn_bg_hover" value="${field.btn_bg_hover || '#441eb1'}" style="width:100%; height:32px;">
						</div>
						<div>
							<label style="font-size:11px; color:#64748b; display:block; margin-bottom:4px;">Hover Text</label>
							<input type="color" class="formlayer-input-color" data-prop="btn_text_hover" value="${field.btn_text_hover || '#ffffff'}" style="width:100%; height:32px;">
						</div>
					</div>
				</div>
				<div class="formlayer-control-group">
					<label class="formlayer-control-label">Border Radius (px)</label>
					<input type="number" class="formlayer-input-full" data-prop="style_border_radius" value="${field.style_border_radius || '10'}" min="0" max="50">
				</div>`;
		}

		advanced_content += `
			<div class="formlayer-control-group">
				<label class="formlayer-control-label" style="display:flex; align-items:center; gap:8px;">
					<input type="checkbox" data-prop="required" ${field.required ? 'checked' : ''}>
					Required Field
				</label>
			</div>`;

		const advanced_accordion = utils.tmpl('sidebar_accordion', {
			open_class: accordion_open === 'advanced' ? 'open' : '',
			id: 'advanced',
			title: 'Advanced',
			content_html: advanced_content
		});

		$('#formlayer-pane-customization').html(`<div class="formlayer-customization-form">${general_accordion}${advanced_accordion}</div>`);
	}

	// Helper functions for Form Settings & Entries
	function sync_settings_to_ui(){
		const s = state.form_settings;
		$('#form-setting-notif-enabled').prop('checked', s.notifications.enabled);
		$('#form-setting-notif-email').val(s.notifications.to_email);
		$('#form-setting-notif-replyto').val(s.notifications.reply_to || '');
		$('#form-setting-notif-fromname').val(s.notifications.from_name || '');
		$('#form-setting-notif-fromemail').val(s.notifications.from_email || '');
		$('#form-setting-notif-bcc').val(s.notifications.bcc || '');
		$('#form-setting-notif-subject').val(s.notifications.subject);
		$('#form-setting-notif-format').val(s.notifications.format || 'html').trigger('change');
		$('#form-setting-notif-message').val(s.notifications.message);
		$('#form-setting-conf-type').val(s.confirmations.type).trigger('change');
		$('#form-setting-conf-message').val(s.confirmations.message);
		$('#form-setting-conf-url').val(s.confirmations.redirect_url);
		$('#form-setting-conf-hide').prop('checked', s.confirmations.hide_form !== false);
		if(s.integrations){
			const ints = ['slack', 'mailchimp', 'sheets', 'notion', 'trello', 'discord'];
			ints.forEach(function(slug){
				const data = s.integrations[slug] || {};
				$(`#form-setting-int-${slug}-enabled`).prop('checked', !!data.enabled).trigger('change');
				if(slug === 'slack' || slug === 'discord') $(`#form-setting-int-${slug}-webhook`).val(data.webhook || '');
				if(slug === 'mailchimp') $(`#form-setting-int-mailchimp-list`).val(data.list_id || '');
				if(slug === 'sheets') {
					$(`#form-setting-int-sheets-id`).val(data.spreadsheet_id || '');
					$(`#form-setting-int-sheets-name`).val(data.sheet_name || '');
				}
				if(slug === 'notion') $(`#form-setting-int-notion-db`).val(data.database_id || '');
				if(slug === 'trello') $(`#form-setting-int-trello-list`).val(data.list_id || '');
			});
		}
		$('#form-setting-custom-css').val(s.custom_css);
		
		// Render Merge Tags
		let tags_html = `<span class="formlayer-badge-tag" data-tag="{all_fields}">{all_fields}</span>`;
		tags_html += `<span class="formlayer-badge-tag" data-tag="{admin_email}">{admin_email}</span>`;
		tags_html += `<span class="formlayer-badge-tag" data-tag="{form_title}">{form_title}</span>`;
		tags_html += `<span class="formlayer-badge-tag" data-tag="{site_url}">{site_url}</span>`;
		
		state.fields.forEach(function(f){
			if(['submit', 'section', 'gdpr', 'terms'].includes(f.type)) return;
			if (!f.name_attr) {
				f.name_attr = generate_unique_name_attr(f.label, f.id);
			}
			let name = f.name_attr;
			let label = f.label || name;
			tags_html += `<span class="formlayer-badge-tag" data-tag="{${name}}" title="${label}">{${name}}</span>`;
		});
		
		$('#formlayer-dynamic-merge-tags').html(tags_html);
	}

	function sync_ui_to_settings(){
		state.form_settings.notifications.enabled = $('#form-setting-notif-enabled').is(':checked');
		state.form_settings.notifications.to_email = $('#form-setting-notif-email').val();
		state.form_settings.notifications.reply_to = $('#form-setting-notif-replyto').val();
		state.form_settings.notifications.from_name = $('#form-setting-notif-fromname').val();
		state.form_settings.notifications.from_email = $('#form-setting-notif-fromemail').val();
		state.form_settings.notifications.bcc = $('#form-setting-notif-bcc').val();
		state.form_settings.notifications.subject = $('#form-setting-notif-subject').val();
		state.form_settings.notifications.format = $('#form-setting-notif-format').val();
		state.form_settings.notifications.message = $('#form-setting-notif-message').val();
		state.form_settings.confirmations.type = $('#form-setting-conf-type').val();
		state.form_settings.confirmations.message = $('#form-setting-conf-message').val();
		state.form_settings.confirmations.redirect_url = $('#form-setting-conf-url').val();
		state.form_settings.confirmations.hide_form = $('#form-setting-conf-hide').is(':checked');
		state.form_settings.integrations = state.form_settings.integrations || {};
		['slack', 'mailchimp', 'sheets', 'notion', 'trello', 'discord'].forEach(function(slug){
			const data = { enabled: $(`#form-setting-int-${slug}-enabled`).is(':checked') };
			if(slug === 'slack' || slug === 'discord') data.webhook = $(`#form-setting-int-${slug}-webhook`).val();
			if(slug === 'mailchimp') data.list_id = $(`#form-setting-int-mailchimp-list`).val();
			if(slug === 'sheets') {
				data.spreadsheet_id = $(`#form-setting-int-sheets-id`).val();
				data.sheet_name = $(`#form-setting-int-sheets-name`).val();
			}
			if(slug === 'notion') data.database_id = $(`#form-setting-int-notion-db`).val();
			if(slug === 'trello') data.list_id = $(`#form-setting-int-trello-list`).val();
			state.form_settings.integrations[slug] = data;
		});
		state.form_settings.custom_css = $('#form-setting-custom-css').val();
	}

	function save_form(){
		const $btn = $('.formlayer-btn-save');
		const original_text = $btn.text();
		
		if(state.active_tab === 'settings'){
			sync_ui_to_settings();
		}

		$btn.prop('disabled', true).text('Saving...');

		const data = {
			action: 'formlayer_save_form',
			nonce: formlayer_admin.nonce,
			form_id: formlayer_admin.form_id,
			title: state.form_title,
			fields: JSON.stringify(state.fields),
			settings: JSON.stringify(state.form_settings)
		};

		$.post(formlayer_admin.ajax_url, data, function(response){
			if(response.success){
				let is_new = (!formlayer_admin.form_id || formlayer_admin.form_id === '0');
				if(is_new){
					formlayer_admin.form_id = response.data.form_id;
					state.display_id = response.data.display_id;
				}
				show_toast('Form saved successfully!', 'success');
				
				// Dynamically add or update row in forms table without reload
				if(response.data.row_html){
					let $tbody = $('#formlayer-tab-forms .formlayer-table tbody');
					if(is_new){
						// Remove empty state row if it exists
						if($tbody.find('.formlayer-empty-title').length){
							$tbody.empty();
						}
						// Prepend the new form row
						$tbody.prepend(response.data.row_html);
					}else{
						// Replace existing row
						let $existing_row = $tbody.find('.formlayer-row-cb[value="' + response.data.display_id + '"]').closest('tr');
						if($existing_row.length){
							$existing_row.replaceWith(response.data.row_html);
						}
					}
				}
				
				render();
			}else{
				show_toast(response.data.message || 'Error saving form', 'error');
			}
			$btn.prop('disabled', false).text(original_text);
		});
	}

	// Template Search
	$('#formlayer-template-search').on('input', function(){
		state.template_search = $(this).val();
		render_templates();
	});

	// Template Category Switch
	$('#formlayer-template-cats-list').on('click', 'li', function(){
		state.template_category = $(this).data('cat');
		render_templates();
	});

	// Field Search
	$('.formlayer-fields-search').on('input', function(){
		state.field_search = $(this).val();
		render_fields_palette();
	});

	// Field Search Keyboard Shortcut (/)
	$(document).on('keydown', function(e){
		if(e.key === '/' && !$(e.target).is('input, textarea, select')){
			e.preventDefault();
			$('.formlayer-fields-search').focus();
		}
	});

	// Template Card Click
	$('#formlayer-templates-grid').on('click', '.formlayer-template-card', function(){
		if($(this).hasClass('is-pro-locked')) return;
		const id = $(this).data('id');
		const t = state.templates.find(function(tmp){ return tmp.id === id; });
		if(t){
			state.form_title = t.title === 'Start from Scratch' ? 'Untitled Form' : t.title;
			state.fields = t.fields || [];
			state.form_settings = t.settings || state.form_settings;
			state.view = 'builder';
			render();
		}
	});

	// Back to Templates
	$('.formlayer-btn-back-templates').on('click', function(){
		if(confirm('Are you sure you want to go back? Unsaved changes will be lost.')){
			state.view = 'templates';
			state.fields = [];
			formlayer_admin.form_id = 0;
			render();
		}
	});

	// Sidebar Tab
	$('.formlayer-sidebar-tab').on('click', function(){
		state.sidebar_tab = $(this).data('tab');
		render_sidebar_content();
	});

	// Category Header Toggle
	$('#formlayer-builder-categories').on('click', '.formlayer-category-header', function(){
		const $cat = $(this).closest('.formlayer-category');
		const cat_id = $cat.data('cat');
		const cat = state.categories.find(function(c){ return c.id === cat_id; });
		if(cat){
			cat.open = !cat.open;
			$cat.toggleClass('open');
		}
	});

	// Palette Field Click
	$('#formlayer-builder-categories').on('click', '.formlayer-palette-field', function(){
		const type = $(this).data('type');
		const field_type = state.field_types.find(function(f){ return f.type === type; });
		if(field_type){
			const new_field = {
				id: 'f' + Date.now(),
				type: type,
				label: field_type.label,
				required: false,
				options: ['Option 1', 'Option 2', 'Option 3']
			};
			new_field.name_attr = generate_unique_name_attr(new_field.label, new_field.id);
			state.fields.push(new_field);
			state.selected_field_id = new_field.id;
			render();
		}
	});

	// Field Instance Click
	$('#formlayer-dropzone').on('click', '.formlayer-field-instance', function(e){
		if($(e.target).closest('.formlayer-field-actions').length) return;
		state.selected_field_id = $(this).data('id');
		render();
	});

	// Field Delete
	$('#formlayer-dropzone').on('click', '.formlayer-field-delete', function(){
		const id = $(this).closest('.formlayer-field-instance').data('id');
		state.fields = state.fields.filter(function(f){ return f.id !== id; });
		if(state.selected_field_id === id) state.selected_field_id = null;
		render();
	});

	// Field Clone
	$('#formlayer-dropzone').on('click', '.formlayer-field-clone', function(){
		const id = $(this).closest('.formlayer-field-instance').data('id');
		const idx = state.fields.findIndex(function(f){ return f.id === id; });
		if(idx !== -1){
			const cloned = JSON.parse(JSON.stringify(state.fields[idx]));
			cloned.id = 'f' + Date.now();
			cloned.name_attr = generate_unique_name_attr(cloned.label, cloned.id);
			state.fields.splice(idx + 1, 0, cloned);
			render();
		}
	});

	// Field Move Up
	$('#formlayer-dropzone').on('click', '.formlayer-field-move-up', function(){
		const id = $(this).closest('.formlayer-field-instance').data('id');
		const idx = state.fields.findIndex(function(f){ return f.id === id; });
		if(idx > 0){
			const temp = state.fields[idx];
			state.fields[idx] = state.fields[idx - 1];
			state.fields[idx - 1] = temp;
			render();
		}
	});

	// Field Move Down
	$('#formlayer-dropzone').on('click', '.formlayer-field-move-down', function(){
		const id = $(this).closest('.formlayer-field-instance').data('id');
		const idx = state.fields.findIndex(function(f){ return f.id === id; });
		if(idx !== -1 && idx < state.fields.length - 1){
			const temp = state.fields[idx];
			state.fields[idx] = state.fields[idx + 1];
			state.fields[idx + 1] = temp;
			render();
		}
	});

	// Customization Form Inputs
	$('#formlayer-pane-customization').on('input change', '.formlayer-customization-form input, .formlayer-customization-form select, .formlayer-customization-form textarea', function(){
		let prop = $(this).data('prop');
		if(!prop) return;
		let field = state.fields.find(function(f){ return f.id === state.selected_field_id; });
		if(field){
			if($(this).attr('type') === 'checkbox'){
				field[prop] = $(this).is(':checked');
			}else{
				field[prop] = $(this).val();
			}
			refresh_canvas();
		}
	});

	// Accordion Header
	$('#formlayer-pane-customization').on('click', '.formlayer-accordion-header', function(){
		let $acc = $(this).closest('.formlayer-accordion');
		state.accordion_open = $acc.data('accordion');
		$('.formlayer-accordion').removeClass('open');
		$acc.addClass('open');
	});

	// Add Option Button
	$('#formlayer-pane-customization').on('click', '#formlayer-btn-add-option', function(){
		let field = state.fields.find(function(f){ return f.id === state.selected_field_id; });
		if(field){
			field.options = field.options || [];
			field.options.push({ label: 'New Option', value: 'new_option', default: false });
			render_advanced_options();
			refresh_canvas();
		}
	});

	// Remove Option Button
	$('#formlayer-pane-customization').on('click', '.formlayer-btn-remove-option', function(){
		let idx = $(this).closest('.formlayer-option-edit-row').data('index'),
		field = state.fields.find(function(f){ return f.id === state.selected_field_id; });
		if(field && field.options){
			field.options.splice(idx, 1);
			render_advanced_options();
			refresh_canvas();
		}
	});

	// Option Row Inputs
	$('#formlayer-pane-customization').on('input change', '.formlayer-option-label, .formlayer-option-value, .formlayer-option-default', function(){
		let $row = $(this).closest('.formlayer-option-edit-row'),
		idx = $row.data('index'),
		field = state.fields.find(function(f){ return f.id === state.selected_field_id; });
		if(field && field.options){
			let opt = field.options[idx];
			if(typeof opt === 'string'){
				field.options[idx] = { label: opt, value: opt, default: false };
			}
			
			if($(this).hasClass('formlayer-option-label')) field.options[idx].label = $(this).val();
			if($(this).hasClass('formlayer-option-value')) field.options[idx].value = $(this).val();
			if($(this).hasClass('formlayer-option-default')){
				const is_checked = $(this).is(':checked');
				if(field.type === 'radio' || field.type === 'dropdown'){
					field.options.forEach(o => o.default = false);
					field.default_value = is_checked ? field.options[idx].value : '';
				}
				field.options[idx].default = is_checked;
			}
			refresh_canvas();
		}
	});

	// Save Button
	$('.formlayer-btn-save').on('click', function(){
		save_form();
	});

	// Builder Tab Switch
	$('.formlayer-builder-tab').on('click', function(){
		state.active_tab = $(this).data('tab');
		$('.formlayer-builder-tab').removeClass('active');
		$(this).addClass('active');
		
		$('.formlayer-builder-pane').hide();
		$(`#formlayer-pane-${state.active_tab}`).show();
		
		if(state.active_tab === 'settings') sync_settings_to_ui();
		if(state.active_tab === 'entries' && window.formlayerAdminPro && typeof window.formlayerAdminPro.load_entries === 'function') {
			window.formlayerAdminPro.load_entries();
		}
	});

	// Form Settings Modal Open
	$('#formlayer-btn-form-settings').on('click', function(){
		sync_settings_to_ui();
		$('#formlayer-form-settings-modal').addClass('active');
	});

	// Modal Close
	$('.formlayer-modal-close, .formlayer-modal-overlay').on('click', function(e){
		if($(e.target).hasClass('formlayer-modal-overlay') || $(e.target).hasClass('formlayer-modal-close')){
			$('.formlayer-modal-overlay').removeClass('active');
		}
	});

	// Settings Section Switching
	$('.formlayer-modal-sidebar').on('click', 'li', function(){
		let section = $(this).data('section');
		$('.formlayer-modal-sidebar li').removeClass('active');
		$(this).addClass('active');
		
		$('.formlayer-settings-section').hide();
		$(`#formlayer-settings-${section}`).show();
	});

	// Apply Settings
	$('#formlayer-settings-apply-btn').on('click', function(){
		sync_ui_to_settings();
		$('#formlayer-form-settings-modal').removeClass('active');
	});

	// Device Toggles
	$('.formlayer-device-btn').on('click', function(){
		let device = $(this).data('device');
		$('.formlayer-device-btn').removeClass('active');
		$(this).addClass('active');
		
		$('.formlayer-canvas-inner').removeClass('desktop tablet mobile').addClass(device);
	});

	// Fullscreen Toggle
	$('.formlayer-fullscreen-toggle').on('click', function(){
		$('.formlayer-admin-wrapper').toggleClass('formlayer-builder-fullscreen');
	});

	// Insert Merge Tag
	$('#formlayer-dynamic-merge-tags').on('click', '.formlayer-badge-tag', function(){
		let tag = $(this).data('tag');
		let $textarea = $('#form-setting-notif-message');
		let pos = $textarea.prop('selectionStart');
		let val = $textarea.val();
		$textarea.val(val.substring(0, pos) + tag + val.substring(pos));
		$textarea.prop('selectionStart', pos + tag.length);
		$textarea.prop('selectionEnd', pos + tag.length);
		$textarea.focus();
		// Trigger change to sync state later if needed
		$textarea.trigger('input');
	});

	// Copy Shortcode
	$('.formlayer-copy-shortcode').on('click', function(){
		let $input = $('#formlayer-shortcode-val');
		$input.select();
		document.execCommand('copy');
		
		let original_text = $(this).html();
		$(this).html('<span class="dashicons dashicons-yes"></span> Copied!');
		setTimeout(() => {
			$(this).html(original_text);
		}, 2000);
	});

	// Copy Shortcode from List
	$('.formlayer-copy-shortcode-list').on('click', function (e){
		e.preventDefault();
		let shortcode = $(this).data('shortcode');
		if(!shortcode) return;

		let $temp = $('<input>');
		$('body').append($temp);
		$temp.val(shortcode).select();
		document.execCommand('copy');
		$temp.remove();

		let $icon = $(this).find('.dashicons');
		$icon.removeClass('dashicons-admin-page').addClass('dashicons-yes');
		show_toast('Shortcode copied to clipboard!', 'success');

		setTimeout(() => {
			$icon.removeClass('dashicons-yes').addClass('dashicons-admin-page');
		}, 2000);
	});

	// Edit Title
	$('#formlayer-edit-title-btn').on('click', function(){
		$('#formlayer-builder-title').hide();
		$('#formlayer-builder-title-input').val(state.form_title).show().focus();
		$(this).hide();
	});

	$('#formlayer-builder-title-input').on('blur', function(){
		let new_title = $(this).val().trim() || 'Untitled Form';
		state.form_title = new_title;
		$('#formlayer-builder-title').text(new_title).show();
		$(this).hide();
		$('#formlayer-edit-title-btn').show();
	});

	$('#formlayer-builder-title-input').on('keypress', function(e){
		if(e.which === 13){
			$(this).blur();
		}
	});

	// Confirmation Type Toggle
	$('#form-setting-conf-type').on('change', function(){
		if($(this).val() === 'message'){
			$('#conf-group-message').show();
			$('#conf-group-redirect').hide();
		}else{
			$('#conf-group-message').hide();
			$('#conf-group-redirect').show();
		}
	});

	// Integration Toggle
	$('input[id^="form-setting-int-"][id$="-enabled"]').on('change', function(){
		let slug = $(this).attr('id').replace('form-setting-int-', '').replace('-enabled', '');
		if($(this).is(':checked')){
			$(`#${slug}-integration-fields`).show();
		}else{
			$(`#${slug}-integration-fields`).hide();
		}
	});

	// Initialize
	if(formlayer_admin.form_id && formlayer_admin.form_id !== '0'){
		load_form(formlayer_admin.form_id);
	}else{
		render();
	}

});

Youez - 2016 - github.com/yon3zu
LinuXploit