- Fix AI Assistant timeout issue (frontend: 35s → 50s) - Fix AJAX action name mismatch for categories (categorys → categories) - Fix nonce mismatch (hvac_general_nonce → hvac_ajax_nonce) - Add modal forms for creating new organizers, categories, and venues - Add comprehensive AJAX endpoints with security validation - Implement role-based permissions for category creation - Fix searchable selectors action mapping 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
722 lines
No EOL
29 KiB
JavaScript
722 lines
No EOL
29 KiB
JavaScript
/**
|
|
* HVAC AI Assist JavaScript
|
|
*
|
|
* Handles AI-powered event population modal interface and form integration
|
|
*
|
|
* @package HVAC_Community_Events
|
|
* @since 3.2.0
|
|
*/
|
|
|
|
jQuery(document).ready(function($) {
|
|
'use strict';
|
|
|
|
/**
|
|
* AI Assist functionality object
|
|
*/
|
|
const HVACAIAssist = {
|
|
// Properties
|
|
modal: null,
|
|
isProcessing: false,
|
|
currentInput: '',
|
|
currentInputType: 'auto',
|
|
|
|
// Initialize
|
|
init: function() {
|
|
this.createModal();
|
|
this.bindEvents();
|
|
this.enableAIButton();
|
|
},
|
|
|
|
/**
|
|
* Create the AI modal interface
|
|
*/
|
|
createModal: function() {
|
|
const modalHTML = `
|
|
<div class="hvac-ai-modal" id="hvac-ai-modal" style="display: none;">
|
|
<div class="modal-overlay"></div>
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h3><i class="dashicons dashicons-superhero-alt"></i> AI Event Assistant</h3>
|
|
<button type="button" class="modal-close" id="ai-modal-close">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="ai-intro">
|
|
<p>Let AI help you create your event! Provide any of the following:</p>
|
|
<ul>
|
|
<li><strong>URL:</strong> EventBrite, Facebook Events, or any event webpage</li>
|
|
<li><strong>Text:</strong> Copy/paste from emails, documents, or flyers</li>
|
|
<li><strong>Description:</strong> Brief description with key event details</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="ai-input-section">
|
|
<div class="input-type-tabs">
|
|
<button type="button" class="tab-btn active" data-type="auto">
|
|
<i class="dashicons dashicons-admin-generic"></i> Auto-Detect
|
|
</button>
|
|
<button type="button" class="tab-btn" data-type="url">
|
|
<i class="dashicons dashicons-admin-links"></i> URL
|
|
</button>
|
|
<button type="button" class="tab-btn" data-type="text">
|
|
<i class="dashicons dashicons-media-document"></i> Text
|
|
</button>
|
|
<button type="button" class="tab-btn" data-type="description">
|
|
<i class="dashicons dashicons-edit"></i> Description
|
|
</button>
|
|
</div>
|
|
|
|
<div class="input-area">
|
|
<div class="input-tab-content active" data-type="auto">
|
|
<label for="ai-input-auto">Paste URL, text, or type a description:</label>
|
|
<textarea id="ai-input-auto" placeholder="Paste an EventBrite URL, copy/paste event details, or describe the event you want to create..." rows="6"></textarea>
|
|
</div>
|
|
<div class="input-tab-content" data-type="url">
|
|
<label for="ai-input-url">Event webpage URL:</label>
|
|
<input type="url" id="ai-input-url" placeholder="https://www.eventbrite.com/e/..." />
|
|
<p class="input-help">Works with EventBrite, Facebook Events, Meetup, and most event websites</p>
|
|
</div>
|
|
<div class="input-tab-content" data-type="text">
|
|
<label for="ai-input-text">Copy/paste event information:</label>
|
|
<textarea id="ai-input-text" placeholder="Paste text from emails, flyers, documents..." rows="6"></textarea>
|
|
<p class="input-help">AI will extract event details from any formatted or unformatted text</p>
|
|
</div>
|
|
<div class="input-tab-content" data-type="description">
|
|
<label for="ai-input-description">Describe your event:</label>
|
|
<textarea id="ai-input-description" placeholder="Example: HVAC troubleshooting workshop on March 15th at Johnson Community Center, 2-4 PM, $50 per person..." rows="4"></textarea>
|
|
<p class="input-help">Provide as many details as you can - dates, times, location, cost, etc.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ai-processing" id="ai-processing" style="display: none;">
|
|
<div class="processing-spinner">
|
|
<div class="spinner"></div>
|
|
</div>
|
|
<div class="processing-status">
|
|
<p class="status-message">Analyzing your input...</p>
|
|
<div class="progress-steps">
|
|
<div class="step active" data-step="1">Parsing content</div>
|
|
<div class="step" data-step="2">Extracting details</div>
|
|
<div class="step" data-step="3">Validating data</div>
|
|
<div class="step" data-step="4">Preparing form</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ai-results" id="ai-results" style="display: none;">
|
|
<div class="results-header">
|
|
<h4><i class="dashicons dashicons-yes-alt"></i> Event Information Extracted</h4>
|
|
<div class="confidence-indicator">
|
|
<span class="confidence-label">Confidence:</span>
|
|
<div class="confidence-bar">
|
|
<div class="confidence-fill" style="width: 0%"></div>
|
|
</div>
|
|
<span class="confidence-percent">0%</span>
|
|
</div>
|
|
</div>
|
|
<div class="results-content">
|
|
<div class="result-fields"></div>
|
|
<div class="result-warnings" style="display: none;">
|
|
<h5><i class="dashicons dashicons-warning"></i> Please Review</h5>
|
|
<ul class="warning-list"></ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn-secondary" id="ai-modal-cancel">Cancel</button>
|
|
<button type="button" class="btn-primary" id="ai-process-btn">
|
|
<i class="dashicons dashicons-superhero-alt"></i> Process with AI
|
|
</button>
|
|
<button type="button" class="btn-primary" id="ai-apply-btn" style="display: none;">
|
|
<i class="dashicons dashicons-yes"></i> Apply to Form
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
$('body').append(modalHTML);
|
|
this.modal = $('#hvac-ai-modal');
|
|
},
|
|
|
|
/**
|
|
* Bind event handlers
|
|
*/
|
|
bindEvents: function() {
|
|
const self = this;
|
|
|
|
// AI Assist button click
|
|
$(document).on('click', '#ai-assist-btn', function(e) {
|
|
e.preventDefault();
|
|
if (!$(this).prop('disabled')) {
|
|
self.openModal();
|
|
}
|
|
});
|
|
|
|
// Modal close handlers
|
|
$(document).on('click', '#ai-modal-close, #ai-modal-cancel, .modal-overlay', function() {
|
|
self.closeModal();
|
|
});
|
|
|
|
// Tab switching
|
|
$(document).on('click', '.tab-btn', function() {
|
|
const type = $(this).data('type');
|
|
self.switchTab(type);
|
|
});
|
|
|
|
// Process button
|
|
$(document).on('click', '#ai-process-btn', function() {
|
|
self.processInput();
|
|
});
|
|
|
|
// Apply button
|
|
$(document).on('click', '#ai-apply-btn', function() {
|
|
self.applyToForm();
|
|
});
|
|
|
|
// Input change handlers for validation
|
|
$(document).on('input', '#ai-input-auto, #ai-input-url, #ai-input-text, #ai-input-description', function() {
|
|
self.validateInput();
|
|
});
|
|
|
|
// ESC key handler
|
|
$(document).on('keyup', function(e) {
|
|
if (e.keyCode === 27 && self.modal.is(':visible')) {
|
|
self.closeModal();
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Enable the AI Assist button (remove placeholder status)
|
|
*/
|
|
enableAIButton: function() {
|
|
const $aiBtn = $('#ai-assist-btn');
|
|
$aiBtn.removeClass('placeholder-btn')
|
|
.prop('disabled', false)
|
|
.attr('title', 'AI-powered event creation assistant')
|
|
.text('AI Assist');
|
|
},
|
|
|
|
/**
|
|
* Open the AI modal
|
|
*/
|
|
openModal: function() {
|
|
this.modal.fadeIn(300);
|
|
this.resetModal();
|
|
$('#ai-input-auto').focus();
|
|
},
|
|
|
|
/**
|
|
* Close the AI modal
|
|
*/
|
|
closeModal: function() {
|
|
if (!this.isProcessing) {
|
|
this.modal.fadeOut(300);
|
|
this.resetModal();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Reset modal to initial state
|
|
*/
|
|
resetModal: function() {
|
|
// Reset tabs
|
|
$('.tab-btn').removeClass('active');
|
|
$('.tab-btn[data-type="auto"]').addClass('active');
|
|
$('.input-tab-content').removeClass('active');
|
|
$('.input-tab-content[data-type="auto"]').addClass('active');
|
|
|
|
// Clear inputs
|
|
$('#ai-input-auto, #ai-input-url, #ai-input-text, #ai-input-description').val('');
|
|
|
|
// Hide sections
|
|
$('#ai-processing, #ai-results').hide();
|
|
$('.ai-input-section').show();
|
|
|
|
// Reset buttons
|
|
$('#ai-process-btn').show().prop('disabled', true);
|
|
$('#ai-apply-btn').hide();
|
|
|
|
// Reset progress steps
|
|
$('.progress-steps .step').removeClass('active');
|
|
$('.status-message').text('Analyzing your input...');
|
|
|
|
// Reset confidence indicator
|
|
$('.confidence-fill').css('width', '0%');
|
|
$('.confidence-percent').text('0%');
|
|
$('.confidence-bar').removeClass('confidence-low confidence-medium confidence-high');
|
|
|
|
// Clear results content
|
|
$('.result-fields').html('');
|
|
$('.warning-list').html('');
|
|
$('.result-warnings').hide();
|
|
|
|
// Clear stored data
|
|
this.extractedData = null;
|
|
|
|
// Reset properties
|
|
this.currentInput = '';
|
|
this.currentInputType = 'auto';
|
|
this.isProcessing = false;
|
|
},
|
|
|
|
/**
|
|
* Switch input tabs
|
|
*/
|
|
switchTab: function(type) {
|
|
$('.tab-btn').removeClass('active');
|
|
$(`.tab-btn[data-type="${type}"]`).addClass('active');
|
|
|
|
$('.input-tab-content').removeClass('active');
|
|
$(`.input-tab-content[data-type="${type}"]`).addClass('active');
|
|
|
|
this.currentInputType = type;
|
|
|
|
// Focus on the input field
|
|
$(`#ai-input-${type}`).focus();
|
|
|
|
this.validateInput();
|
|
},
|
|
|
|
/**
|
|
* Validate current input
|
|
*/
|
|
validateInput: function() {
|
|
const input = this.getCurrentInput();
|
|
const $processBtn = $('#ai-process-btn');
|
|
|
|
if (input.length >= 10) {
|
|
$processBtn.prop('disabled', false);
|
|
} else {
|
|
$processBtn.prop('disabled', true);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Get current input value
|
|
*/
|
|
getCurrentInput: function() {
|
|
const activeTab = $('.input-tab-content.active');
|
|
const inputElement = activeTab.find('input, textarea');
|
|
return inputElement.val().trim();
|
|
},
|
|
|
|
/**
|
|
* Process input through AI
|
|
*/
|
|
processInput: function() {
|
|
const input = this.getCurrentInput();
|
|
|
|
if (input.length < 10) {
|
|
this.showError('Please provide at least 10 characters of event information.');
|
|
return;
|
|
}
|
|
|
|
this.isProcessing = true;
|
|
this.currentInput = input;
|
|
|
|
// Show processing UI
|
|
$('.ai-input-section').hide();
|
|
$('#ai-processing').show();
|
|
$('#ai-process-btn').hide();
|
|
|
|
// Start progress animation
|
|
this.animateProgress();
|
|
|
|
// Make AJAX request
|
|
this.makeAIRequest(input, this.currentInputType);
|
|
},
|
|
|
|
/**
|
|
* Animate processing steps
|
|
*/
|
|
animateProgress: function() {
|
|
const isUrl = this.currentInputType === 'url';
|
|
const steps = isUrl ? [
|
|
{ step: 1, message: 'Fetching webpage content...', delay: 500 },
|
|
{ step: 2, message: 'Processing webpage data (this may take up to 40 seconds)...', delay: 3000 },
|
|
{ step: 3, message: 'Extracting event details with AI...', delay: 15000 },
|
|
{ step: 4, message: 'Preparing form data...', delay: 25000 }
|
|
] : [
|
|
{ step: 1, message: 'Analyzing your input...', delay: 500 },
|
|
{ step: 2, message: 'Extracting event details...', delay: 2000 },
|
|
{ step: 3, message: 'Validating information...', delay: 4000 },
|
|
{ step: 4, message: 'Preparing form data...', delay: 6000 }
|
|
];
|
|
|
|
steps.forEach(({ step, message, delay }) => {
|
|
setTimeout(() => {
|
|
if (this.isProcessing) {
|
|
$(`.progress-steps .step[data-step="${step}"]`).addClass('active');
|
|
$('.status-message').text(message);
|
|
}
|
|
}, delay);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Make AJAX request to AI endpoint
|
|
*/
|
|
makeAIRequest: function(input, inputType) {
|
|
const self = this;
|
|
|
|
const requestData = {
|
|
action: 'hvac_ai_populate_event',
|
|
input: input,
|
|
input_type: inputType,
|
|
nonce: hvacAjaxVars.nonce // Assuming nonce is available
|
|
};
|
|
|
|
$.ajax({
|
|
url: hvacAjaxVars.ajaxUrl,
|
|
type: 'POST',
|
|
data: requestData,
|
|
timeout: inputType === 'url' ? 60000 : 50000, // 60 seconds for URLs, 50 for text
|
|
success: function(response) {
|
|
self.handleAISuccess(response);
|
|
},
|
|
error: function(xhr, status, error) {
|
|
self.handleAIError(xhr, status, error);
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Handle successful AI response
|
|
*/
|
|
handleAISuccess: function(response) {
|
|
this.isProcessing = false;
|
|
|
|
if (response.success && response.data && response.data.event_data) {
|
|
this.displayResults(response.data.event_data);
|
|
} else {
|
|
const message = response.data && response.data.message
|
|
? response.data.message
|
|
: 'Unexpected response format from AI service.';
|
|
this.showError(message);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Handle AI request error
|
|
*/
|
|
handleAIError: function(xhr, status, error) {
|
|
this.isProcessing = false;
|
|
|
|
let message = 'AI service temporarily unavailable. Please try again later.';
|
|
|
|
if (status === 'timeout') {
|
|
message = 'Request timed out. The AI might be processing a complex input. Please try with simpler content.';
|
|
} else if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) {
|
|
message = xhr.responseJSON.data.message;
|
|
}
|
|
|
|
this.showError(message);
|
|
},
|
|
|
|
/**
|
|
* Display AI extraction results
|
|
*/
|
|
displayResults: function(eventData) {
|
|
$('#ai-processing').hide();
|
|
$('#ai-results').show();
|
|
|
|
// Update confidence indicator
|
|
const confidence = eventData.confidence && eventData.confidence.overall
|
|
? eventData.confidence.overall
|
|
: 0;
|
|
const confidencePercent = Math.round(confidence * 100);
|
|
|
|
$('.confidence-fill').css('width', confidencePercent + '%');
|
|
$('.confidence-percent').text(confidencePercent + '%');
|
|
|
|
// Color code confidence
|
|
let confidenceClass = 'confidence-low';
|
|
if (confidencePercent >= 80) confidenceClass = 'confidence-high';
|
|
else if (confidencePercent >= 60) confidenceClass = 'confidence-medium';
|
|
|
|
$('.confidence-bar').removeClass('confidence-low confidence-medium confidence-high')
|
|
.addClass(confidenceClass);
|
|
|
|
// Display extracted fields
|
|
this.displayExtractedFields(eventData);
|
|
|
|
// Check for warnings
|
|
this.checkAndDisplayWarnings(eventData);
|
|
|
|
// Show apply button
|
|
$('#ai-apply-btn').show();
|
|
|
|
// Store data for form application
|
|
this.extractedData = eventData;
|
|
},
|
|
|
|
/**
|
|
* Display extracted fields summary
|
|
*/
|
|
displayExtractedFields: function(eventData) {
|
|
const fieldsHtml = [];
|
|
|
|
// Title
|
|
if (eventData.title) {
|
|
fieldsHtml.push(`<div class="result-field">
|
|
<label>Title:</label>
|
|
<span>${this.escapeHtml(eventData.title)}</span>
|
|
</div>`);
|
|
}
|
|
|
|
// Description (truncated for display)
|
|
if (eventData.description) {
|
|
let descriptionPreview = eventData.description;
|
|
// Truncate if too long for modal display
|
|
if (descriptionPreview.length > 200) {
|
|
descriptionPreview = descriptionPreview.substring(0, 200) + '...';
|
|
}
|
|
fieldsHtml.push(`<div class="result-field">
|
|
<label>Description:</label>
|
|
<span>${this.escapeHtml(descriptionPreview)}</span>
|
|
</div>`);
|
|
}
|
|
|
|
// Date and time
|
|
if (eventData.start_date) {
|
|
let dateDisplay = eventData.start_date;
|
|
if (eventData.start_time) {
|
|
dateDisplay += ` at ${eventData.start_time}`;
|
|
}
|
|
fieldsHtml.push(`<div class="result-field">
|
|
<label>Start:</label>
|
|
<span>${this.escapeHtml(dateDisplay)}</span>
|
|
</div>`);
|
|
}
|
|
|
|
if (eventData.end_date) {
|
|
let dateDisplay = eventData.end_date;
|
|
if (eventData.end_time) {
|
|
dateDisplay += ` at ${eventData.end_time}`;
|
|
}
|
|
fieldsHtml.push(`<div class="result-field">
|
|
<label>End:</label>
|
|
<span>${this.escapeHtml(dateDisplay)}</span>
|
|
</div>`);
|
|
}
|
|
|
|
// Venue
|
|
if (eventData.venue_name) {
|
|
let venueDisplay = eventData.venue_name;
|
|
if (eventData.venue_address) {
|
|
venueDisplay += ` (${eventData.venue_address})`;
|
|
}
|
|
fieldsHtml.push(`<div class="result-field">
|
|
<label>Venue:</label>
|
|
<span>${this.escapeHtml(venueDisplay)}</span>
|
|
</div>`);
|
|
}
|
|
|
|
// Cost
|
|
if (eventData.cost !== null && eventData.cost !== undefined) {
|
|
fieldsHtml.push(`<div class="result-field">
|
|
<label>Cost:</label>
|
|
<span>$${eventData.cost}</span>
|
|
</div>`);
|
|
}
|
|
|
|
$('.result-fields').html(fieldsHtml.join(''));
|
|
},
|
|
|
|
/**
|
|
* Check for and display warnings
|
|
*/
|
|
checkAndDisplayWarnings: function(eventData) {
|
|
const warnings = [];
|
|
|
|
// Check confidence levels
|
|
if (eventData.confidence && eventData.confidence.per_field) {
|
|
Object.entries(eventData.confidence.per_field).forEach(([field, confidence]) => {
|
|
if (confidence < 0.7) {
|
|
warnings.push(`${field} information may need review (${Math.round(confidence * 100)}% confidence)`);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Check for missing critical fields
|
|
if (!eventData.title) warnings.push('Event title not found');
|
|
if (!eventData.start_date) warnings.push('Event date not found');
|
|
|
|
if (warnings.length > 0) {
|
|
const warningsHtml = warnings.map(warning => `<li>${this.escapeHtml(warning)}</li>`).join('');
|
|
$('.warning-list').html(warningsHtml);
|
|
$('.result-warnings').show();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Apply extracted data to the event form
|
|
*/
|
|
applyToForm: function() {
|
|
if (!this.extractedData) return;
|
|
|
|
const data = this.extractedData;
|
|
|
|
try {
|
|
// Apply title
|
|
if (data.title) {
|
|
$('#event_title, [name="event_title"]').val(data.title);
|
|
}
|
|
|
|
// Apply description (handle TinyMCE, regular textarea, and rich text editor)
|
|
if (data.description) {
|
|
// Try TinyMCE first if available
|
|
if (typeof tinyMCE !== 'undefined' && tinyMCE.get('event_description')) {
|
|
tinyMCE.get('event_description').setContent(data.description);
|
|
} else {
|
|
// Update the hidden textarea
|
|
$('#event_description, [name="event_description"]').val(data.description);
|
|
|
|
// Also update the visible rich text editor div if it exists
|
|
const $richEditor = $('#event-description-editor');
|
|
if ($richEditor.length && $richEditor.is('[contenteditable]')) {
|
|
$richEditor.html(data.description);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply start date and time (combine into datetime-local format)
|
|
if (data.start_date) {
|
|
let startDateTime = data.start_date;
|
|
if (data.start_time) {
|
|
startDateTime += 'T' + data.start_time;
|
|
} else {
|
|
// Default to 9:00 AM if no time specified
|
|
startDateTime += 'T09:00';
|
|
}
|
|
$('#event_start_datetime, [name="event_start_datetime"]').val(startDateTime);
|
|
}
|
|
|
|
// Apply end date and time (combine into datetime-local format)
|
|
if (data.end_date) {
|
|
let endDateTime = data.end_date;
|
|
if (data.end_time) {
|
|
endDateTime += 'T' + data.end_time;
|
|
} else {
|
|
// Default to 5:00 PM if no time specified
|
|
endDateTime += 'T17:00';
|
|
}
|
|
$('#event_end_datetime, [name="event_end_datetime"]').val(endDateTime);
|
|
}
|
|
|
|
// Apply cost
|
|
if (data.cost !== null && data.cost !== undefined) {
|
|
$('#event_cost, [name="event_cost"]').val(data.cost);
|
|
}
|
|
|
|
// Apply capacity
|
|
if (data.capacity) {
|
|
$('#event_capacity, [name="event_capacity"]').val(data.capacity);
|
|
}
|
|
|
|
// Apply URL
|
|
if (data.url) {
|
|
$('#event_url, [name="event_url"]').val(data.url);
|
|
}
|
|
|
|
// Apply venue fields (flatter structure)
|
|
if (data.venue_name) {
|
|
$('#venue_name, [name="venue_name"]').val(data.venue_name);
|
|
}
|
|
if (data.venue_address) {
|
|
$('#venue_address, [name="venue_address"]').val(data.venue_address);
|
|
}
|
|
if (data.venue_city) {
|
|
$('#venue_city, [name="venue_city"]').val(data.venue_city);
|
|
}
|
|
if (data.venue_state) {
|
|
$('#venue_state, [name="venue_state"]').val(data.venue_state);
|
|
}
|
|
if (data.venue_zip) {
|
|
$('#venue_zip, [name="venue_zip"]').val(data.venue_zip);
|
|
}
|
|
|
|
// Apply organizer fields (flatter structure)
|
|
if (data.organizer_name) {
|
|
$('#organizer_name, [name="organizer_name"]').val(data.organizer_name);
|
|
}
|
|
if (data.organizer_email) {
|
|
$('#organizer_email, [name="organizer_email"]').val(data.organizer_email);
|
|
}
|
|
if (data.organizer_phone) {
|
|
$('#organizer_phone, [name="organizer_phone"]').val(data.organizer_phone);
|
|
}
|
|
|
|
// Apply website URL
|
|
if (data.website) {
|
|
$('#website, [name="website"]').val(data.website);
|
|
}
|
|
|
|
// Apply event URL
|
|
if (data.event_url) {
|
|
$('#event_url, [name="event_url"]').val(data.event_url);
|
|
}
|
|
|
|
// Apply timezone (if provided)
|
|
if (data.timezone) {
|
|
$('#event_timezone, [name="event_timezone"]').val(data.timezone);
|
|
}
|
|
|
|
// Apply event image (only if >= 200x200px)
|
|
if (data.event_image_url) {
|
|
$('#event_image_url, [name="event_image_url"]').val(data.event_image_url);
|
|
}
|
|
|
|
// Trigger autosave if available
|
|
if (typeof performAutoSave === 'function') {
|
|
setTimeout(performAutoSave, 1000);
|
|
}
|
|
|
|
// Close modal and show success message
|
|
this.closeModal();
|
|
this.showSuccess('Event information applied successfully! Please review and adjust as needed before submitting.');
|
|
|
|
} catch (error) {
|
|
console.error('Error applying AI data to form:', error);
|
|
this.showError('Error applying data to form. Please try filling the fields manually.');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Show error message
|
|
*/
|
|
showError: function(message) {
|
|
// Reset processing UI
|
|
$('#ai-processing').hide();
|
|
$('.ai-input-section').show();
|
|
$('#ai-process-btn').show();
|
|
this.isProcessing = false;
|
|
|
|
// Show error in modal or as alert
|
|
alert('Error: ' + message);
|
|
},
|
|
|
|
/**
|
|
* Show success message
|
|
*/
|
|
showSuccess: function(message) {
|
|
// You might want to show this in a nicer way, like a toast notification
|
|
alert(message);
|
|
},
|
|
|
|
/**
|
|
* Escape HTML for safe display
|
|
*/
|
|
escapeHtml: function(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
};
|
|
|
|
// Initialize when document is ready
|
|
HVACAIAssist.init();
|
|
}); |