PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.7.2
AI Builder – Generate pages, blocks, images & translate with AI v2.7.2
2.8.0 2.7.10 2.7.9 2.7.8 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 All 123 releases
ai-builder / assets / js / multi-page.js

multi-page.js in AI Builder – Generate pages, blocks, images & translate with AI 2.7.2, at assets/js/multi-page.js

779 lines 30.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 let currentPageCount = 1;
2 let currentContentType = 'page';
3 let generationInProgress = false;
4 let generatedPages = [];
5 let isPremiumUser = false;
6
7 function showMessage(message, type = "info") {
8 console.log(`Showing message: ${message} (type: ${type})`);
9
10 let container = document.getElementById("message-container");
11
12 // Si le container n'existe pas, le créer
13 if (!container) {
14 container = document.createElement("div");
15 container.id = "message-container";
16 const multiPageContainer = document.querySelector(".ai-multi-page-container");
17 if (multiPageContainer) {
18 multiPageContainer.appendChild(container);
19 } else {
20 console.error('Multi-page container not found');
21 return;
22 }
23 }
24
25 container.innerHTML = `<div class="ai-message ${type}">${message}</div>`;
26 container.scrollIntoView({ behavior: "smooth" });
27
28 // Auto-hide after 5 seconds for info messages
29 if (type === "info") {
30 setTimeout(() => {
31 if (container && container.parentNode) {
32 container.innerHTML = '';
33 }
34 }, 5000);
35 }
36 }
37
38
39 function getCreditsPageUrl() {
40 const base = (typeof aiBuilderVars !== 'undefined' && aiBuilderVars.adminBaseUrl)
41 ? aiBuilderVars.adminBaseUrl
42 : '/wp-admin/';
43 return `${base}admin.php?page=aibui-credits`;
44 }
45
46 function renderPremiumWarning(message) {
47 const warningContainer = document.getElementById('premium-warning-container');
48 if (!warningContainer) {
49 return;
50 }
51
52 warningContainer.innerHTML = `
53 <div class="ai-premium-warning">
54 <strong>${message}</strong>
55 <a class="ai-primary-btn" href="${getCreditsPageUrl()}">Go to Credits</a>
56 </div>
57 `;
58 }
59
60 function disableMultiPageAccess(message) {
61 const content = document.querySelector('.ai-multi-page-content');
62 if (content) {
63 content.classList.add('ai-locked');
64 const interactiveElements = content.querySelectorAll('button, select, textarea, input');
65 interactiveElements.forEach((element) => {
66 element.setAttribute('disabled', 'disabled');
67 element.setAttribute('aria-disabled', 'true');
68 });
69 }
70
71 renderPremiumWarning(message);
72 showMessage(message, 'error');
73 }
74
75 async function ensurePremiumAccess() {
76 try {
77 // Create AbortController for timeout
78 const controller = new AbortController();
79 const timeoutId = setTimeout(() => controller.abort(), 20000); // 10 second timeout
80
81 const tokenResponse = await fetch(ajaxurl, {
82 method: "POST",
83 headers: { "Content-Type": "application/x-www-form-urlencoded" },
84 body: "action=aibui_get_token&nonce=" + aiBuilderVars.nonce,
85 signal: controller.signal,
86 });
87
88 if (!tokenResponse.ok) {
89 if (tokenResponse.status === 500) {
90 disableMultiPageAccess("Server error: The request took too long. Please refresh the page and try again.");
91 } else {
92 disableMultiPageAccess("Unable to verify authentication. Please refresh the page.");
93 }
94 return false;
95 }
96
97 clearTimeout(timeoutId);
98
99 const tokenData = await tokenResponse.json();
100 if (!tokenData.success || !tokenData.data || !tokenData.data.token) {
101 disableMultiPageAccess("Please sign in to access the Multi Page Generator.");
102 return false;
103 }
104
105 const jwtToken = tokenData.data.token;
106 const response = await fetch(`${window.config.apiUrl}/user/profile`, {
107 method: "GET",
108 headers: {
109 Authorization: `Bearer ${jwtToken}`,
110 "Content-Type": "application/json",
111 },
112 });
113
114 if (!response.ok) {
115 disableMultiPageAccess("Unable to verify your subscription. Please refresh the page.");
116 return false;
117 }
118
119 const profile = await response.json();
120 const user = profile?.user || profile?.data || profile;
121 const plan = (user?.plan || '').toLowerCase();
122
123 if (plan !== 'premium' && plan !== 'creator') {
124 disableMultiPageAccess("Multi Page Generator is available for Premium subscribers only.");
125 return false;
126 }
127
128 isPremiumUser = true;
129 return true;
130 } catch (error) {
131 if (typeof timeoutId !== 'undefined' && timeoutId) {
132 clearTimeout(timeoutId);
133 }
134 console.error("Error verifying premium access:", error);
135
136 // Handle timeout/abort errors
137 if (error.name === 'AbortError' || error.message.includes('timeout')) {
138 disableMultiPageAccess("Request timeout: The server took too long to respond. Please check your connection and try again.");
139 } else {
140 disableMultiPageAccess("Unable to verify your subscription at this time. Please try again later.");
141 }
142 return false;
143 }
144 }
145
146 function setLoading(buttonId, isLoading) {
147 const button = document.getElementById(buttonId);
148
149 if (!button) {
150 console.error(`Button with id '${buttonId}' not found`);
151 return;
152 }
153
154 let loading = button.querySelector(".ai-loading");
155
156 // Si l'élément loading n'existe pas, le créer
157 if (!loading) {
158 loading = document.createElement("span");
159 loading.className = "ai-loading";
160 loading.style.display = "none";
161 button.appendChild(loading);
162 }
163
164 // Stocker le texte original dans un attribut data si pas déjà fait
165 if (!button.dataset.originalText) {
166 button.dataset.originalText = button.textContent.trim();
167 }
168
169 if (isLoading) {
170 // Ensure layout centers spinner and text
171 button.style.display = 'inline-flex';
172 button.style.alignItems = 'center';
173 button.style.justifyContent = 'center';
174
175 loading.style.display = "inline-block";
176 loading.style.marginRight = '8px';
177
178 button.disabled = true;
179 button.textContent = "";
180 button.appendChild(loading);
181 const textSpan = document.createElement('span');
182 textSpan.className = 'ai-loading-text';
183 textSpan.style.marginLeft = '4px';
184 textSpan.textContent = button.dataset.originalText;
185 button.appendChild(textSpan);
186 } else {
187 loading.style.display = "none";
188 button.disabled = false;
189 button.style.display = '';
190 button.style.alignItems = '';
191 button.style.justifyContent = '';
192 button.textContent = button.dataset.originalText;
193 }
194 }
195
196 function updateTotalCost() {
197 const pageCountSelect = document.getElementById('page-count');
198 if (!pageCountSelect) {
199 console.error('Page count select element not found');
200 return;
201 }
202
203 const pageCount = parseInt(pageCountSelect.value);
204 const totalCost = pageCount * 75;
205
206 const totalCostElement = document.getElementById('total-cost');
207 if (!totalCostElement) {
208 console.error('Total cost element not found');
209 return;
210 }
211
212 totalCostElement.textContent = `${totalCost} credits`;
213 }
214
215 function generatePagePrompts() {
216 if (!isPremiumUser) {
217 showMessage('Premium subscription required to use the Multi Page Generator.', 'error');
218 return;
219 }
220
221 const pageCount = parseInt(document.getElementById('page-count').value);
222 const container = document.getElementById('pages-prompts-container');
223
224 container.innerHTML = '';
225
226 for (let i = 1; i <= pageCount; i++) {
227 const pageDiv = document.createElement('div');
228 pageDiv.className = 'ai-page-prompt-item';
229 pageDiv.innerHTML = `
230 <div class="ai-form-group">
231 <label for="page-${i}-prompt">Page ${i} Prompt</label>
232 <textarea
233 id="page-${i}-prompt"
234 name="page_${i}_prompt"
235 placeholder="Describe what you want for page ${i}..."
236 maxlength="1500"
237 required
238 ></textarea>
239 <div class="ai-char-counter">
240 <span id="page-${i}-counter">0</span>/1500 characters
241 </div>
242 </div>
243 `;
244 container.appendChild(pageDiv);
245
246 // Ajouter le listener pour le compteur de caractères
247 const textarea = pageDiv.querySelector('textarea');
248 const counter = pageDiv.querySelector('.ai-char-counter span');
249
250 textarea.addEventListener('input', function () {
251 counter.textContent = this.value.length;
252 if (this.value.length > 1400) {
253 counter.style.color = '#ff6b6b';
254 } else if (this.value.length > 1200) {
255 counter.style.color = '#ffa726';
256 } else {
257 counter.style.color = '#666';
258 }
259 });
260 }
261
262 document.getElementById('pages-config-section').style.display = 'block';
263 document.getElementById('generate-pages-btn').style.display = 'block';
264 }
265
266 function validatePrompts() {
267 const pageCount = parseInt(document.getElementById('page-count').value);
268
269 for (let i = 1; i <= pageCount; i++) {
270 const prompt = document.getElementById(`page-${i}-prompt`).value.trim();
271 if (!prompt) {
272 showMessage(`Please enter a prompt for page ${i}`, 'error');
273 return false;
274 }
275 if (prompt.length > 1500) {
276 showMessage(`Prompt for page ${i} is too long (max 1500 characters)`, 'error');
277 return false;
278 }
279 }
280
281 return true;
282 }
283
284 async function generatePages() {
285 if (!isPremiumUser) {
286 showMessage('Premium subscription required to use the Multi Page Generator.', 'error');
287 return;
288 }
289
290 if (generationInProgress) return;
291
292 if (!validatePrompts()) return;
293
294 generationInProgress = true;
295 setLoading('generate-pages-btn', true);
296
297 const pageCount = parseInt(document.getElementById('page-count').value);
298 const contentType = document.getElementById('content-type').value;
299
300 // Collect prompts
301 const prompts = [];
302 for (let i = 1; i <= pageCount; i++) {
303 prompts.push(document.getElementById(`page-${i}-prompt`).value.trim());
304 }
305
306 // Show progress section
307 document.getElementById('generation-progress-section').style.display = 'block';
308 const progressContainer = document.getElementById('generation-progress');
309 progressContainer.innerHTML = '';
310
311 // Create progress items
312 for (let i = 1; i <= pageCount; i++) {
313 const progressItem = document.createElement('div');
314 progressItem.className = 'ai-progress-item';
315 progressItem.id = `progress-${i}`;
316 progressItem.innerHTML = `
317 <div class="ai-progress-header">
318 <span class="ai-progress-title">Page ${i}</span>
319 <span class="ai-progress-status" id="status-${i}">Waiting...</span>
320 </div>
321 <div class="ai-progress-bar">
322 <div class="ai-progress-fill" id="fill-${i}"></div>
323 </div>
324 `;
325 progressContainer.appendChild(progressItem);
326 }
327
328 // Generate pages one by one
329 const results = [];
330
331 for (let i = 0; i < pageCount; i++) {
332 const pageNumber = i + 1;
333 const statusElement = document.getElementById(`status-${pageNumber}`);
334 const fillElement = document.getElementById(`fill-${pageNumber}`);
335
336 try {
337 statusElement.textContent = 'Generating...';
338 statusElement.style.color = '#ffa726';
339 fillElement.style.width = '0%';
340 fillElement.style.backgroundColor = '#ffa726';
341
342 const result = await generateSinglePage(prompts[i], contentType, pageNumber);
343
344 if (result.success) {
345 statusElement.textContent = 'Completed';
346 statusElement.style.color = '#4caf50';
347 fillElement.style.width = '100%';
348 fillElement.style.backgroundColor = '#4caf50';
349 results.push(result);
350 } else {
351 statusElement.textContent = result.error || 'Failed';
352 statusElement.style.color = '#f44336';
353 fillElement.style.backgroundColor = '#f44336';
354 results.push({ success: false, error: result.error });
355 }
356 } catch (error) {
357 console.error(`Error generating page ${pageNumber}:`, error);
358 statusElement.textContent = 'Error';
359 statusElement.style.color = '#f44336';
360 fillElement.style.backgroundColor = '#f44336';
361 results.push({ success: false, error: error.message });
362 }
363
364 // Small delay between pages
365 if (i < pageCount - 1) {
366 await new Promise(resolve => setTimeout(resolve, 1000));
367 }
368 }
369
370 // Show results
371 showResults(results);
372
373 generationInProgress = false;
374 setLoading('generate-pages-btn', false);
375 }
376
377 // Fonction pour simuler la barre de progression
378 function simulateProgress(fillElement, durationMs = 45000) {
379 let currentProgress = 0;
380 const targetProgress = 90;
381 const startTime = Date.now();
382 let progressInterval;
383 let slowProgressInterval;
384 let isPhase2 = false;
385
386 // Phase 1: 0% à 90% en 30 secondes de manière saccadée
387 progressInterval = setInterval(() => {
388 if (isPhase2) return;
389
390 const elapsed = Date.now() - startTime;
391 const baseProgress = Math.min((elapsed / durationMs) * targetProgress, targetProgress);
392
393 // Ajouter des variations saccadées (uniquement positives pour ne pas reculer)
394 const jitter = Math.random() * 2; // Entre 0% et 2%
395 currentProgress = Math.min(baseProgress + jitter, targetProgress);
396
397 // S'assurer que la progression ne recule jamais
398 const currentWidth = parseFloat(fillElement.style.width) || 0;
399 currentProgress = Math.max(currentProgress, currentWidth);
400
401 fillElement.style.width = currentProgress + '%';
402
403 // Quand on arrive à 90%, passer à la phase lente
404 if (currentProgress >= targetProgress) {
405 clearInterval(progressInterval);
406 isPhase2 = true;
407
408 // Phase 2: Avancer très lentement de 90% vers ~95%
409 slowProgressInterval = setInterval(() => {
410 currentProgress = Math.min(currentProgress + 0.05, 95);
411 fillElement.style.width = currentProgress + '%';
412 }, 1000); // +0.05% toutes les secondes (très lent)
413 }
414 }, 200); // Mise à jour toutes les 200ms pour un effet fluide mais saccadé
415
416 // Fonction pour compléter à 100%
417 return {
418 complete: () => {
419 clearInterval(progressInterval);
420 clearInterval(slowProgressInterval);
421 fillElement.style.width = '100%';
422 }
423 };
424 }
425
426 async function generateSinglePage(prompt, contentType, pageNumber) {
427 if (!isPremiumUser) {
428 return { success: false, error: 'Premium subscription required' };
429 }
430
431 // Démarrer la simulation de progression
432 const fillElement = document.getElementById(`fill-${pageNumber}`);
433 const progressSimulator = simulateProgress(fillElement);
434
435 let timeoutId;
436 try {
437 // Get JWT token with timeout
438 const controller = new AbortController();
439 timeoutId = setTimeout(() => controller.abort(), 20000); // 10 second timeout
440
441 const tokenResponse = await fetch(ajaxurl, {
442 method: "POST",
443 headers: { "Content-Type": "application/x-www-form-urlencoded" },
444 body: "action=aibui_get_token&nonce=" + aiBuilderVars.nonce,
445 signal: controller.signal,
446 });
447
448 clearTimeout(timeoutId);
449
450 if (!tokenResponse.ok) {
451 if (tokenResponse.status === 500) {
452 progressSimulator.complete();
453 throw new Error("Server error: The request took too long or encountered an error. Please try again.");
454 }
455 progressSimulator.complete();
456 throw new Error(`HTTP error! status: ${tokenResponse.status}`);
457 }
458
459 const tokenData = await tokenResponse.json();
460
461 if (!tokenData.success || !tokenData.data.token) {
462 progressSimulator.complete();
463 throw new Error("No authentication token found");
464 }
465
466 const jwtToken = tokenData.data.token;
467
468 // Call the AI API (JSON blocks response)
469 const response = await fetch(`${window.config.apiUrl}/ai-transform-page/v2-page-generation`, {
470 method: "POST",
471 headers: {
472 "Content-Type": "application/json",
473 Authorization: `Bearer ${jwtToken}`,
474 },
475 body: JSON.stringify({
476 userPrompt: prompt,
477 pageContent: "", // Kept for compatibility
478 wooCommerceInstalled: !!(typeof aiBuilderVars !== 'undefined' && aiBuilderVars.wooCommerceInstalled && aiBuilderVars.wooCommerceInstalled !== '0'),
479 activeThemeName: (typeof aiBuilderVars !== 'undefined' && aiBuilderVars.activeThemeName) ? aiBuilderVars.activeThemeName : '',
480 sitePlugins: (typeof aiBuilderVars !== 'undefined' && Array.isArray(aiBuilderVars.sitePlugins)) ? aiBuilderVars.sitePlugins.slice(0, 15) : [],
481 wordpressVersion: (typeof aiBuilderVars !== 'undefined' && aiBuilderVars.wordpressVersion) ? String(aiBuilderVars.wordpressVersion) : '',
482 }),
483 });
484
485 const data = await response.json();
486
487 if (response.status === 402) {
488 progressSimulator.complete();
489 return { success: false, error: "Not enough credits" };
490 }
491
492 if (data.error === "not-enough-credits") {
493 progressSimulator.complete();
494 return { success: false, error: "Not enough credits" };
495 }
496
497 if (!data.pageContent) {
498 progressSimulator.complete();
499 return { success: false, error: "No content generated" };
500 }
501 // Save generation (Pending review)
502 const generationPayload = {
503 id: `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
504 title: data.postTitle || `Generated Page ${pageNumber}`,
505 metaDesc: data.postMetaDesc || '',
506 cssContent: data.cssContent || '',
507 jsContent: data.jsContent || '',
508 blocksJson: Array.isArray(data.pageContent) ? data.pageContent : [],
509 };
510 const saveRes = await fetch(ajaxurl, {
511 method: "POST",
512 headers: { "Content-Type": "application/x-www-form-urlencoded" },
513 body: `action=aibui_save_generation&nonce=${aiBuilderVars.nonce}&payload=${encodeURIComponent(JSON.stringify(generationPayload))}`,
514 });
515 const saveData = await saveRes.json();
516 progressSimulator.complete();
517 if (!saveData.success) {
518 return { success: false, error: saveData.data || 'Failed to save generation' };
519 }
520 return {
521 success: true,
522 generation: saveData.data,
523 pageTitle: generationPayload.title,
524 };
525
526 } catch (error) {
527 if (timeoutId) {
528 clearTimeout(timeoutId);
529 }
530 console.error("Error generating page:", error);
531 progressSimulator.complete();
532
533 // Handle timeout/abort errors
534 if (error.name === 'AbortError' || error.message.includes('timeout')) {
535 return { success: false, error: "Request timeout: The server took too long to respond. Please check your connection and try again." };
536 }
537
538 return { success: false, error: error.message };
539 }
540 }
541
542 function showResults(results) {
543 const resultsSection = document.getElementById('results-section');
544 const resultsList = document.getElementById('generated-pages-list');
545
546 resultsSection.style.display = 'block';
547 resultsList.innerHTML = '';
548
549 let successCount = 0;
550
551 results.forEach((result, index) => {
552 const pageNumber = index + 1;
553 const resultItem = document.createElement('div');
554 resultItem.className = 'ai-result-item';
555
556 if (result.success && result.generation) {
557 successCount++;
558 const gen = result.generation;
559 const base = (typeof aiBuilderVars !== 'undefined' && aiBuilderVars.adminBaseUrl) ? aiBuilderVars.adminBaseUrl : '/wp-admin/';
560 const applyUrl = `${base}post-new.php?post_type=${currentContentType}&aibui_gen_id=${encodeURIComponent(gen.id)}`;
561 resultItem.innerHTML = `
562 <div class="ai-result-success">
563 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
564 <path d="M9 12L11 14L15 10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
565 <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
566 </svg>
567 <div class="ai-result-content">
568 <h4>Page ${pageNumber}: ${gen.title}</h4>
569 <div class="ai-actions">
570 <span class="ai-status">Status: Pending review</span>
571 <a href="${applyUrl}" class="ai-page-link" target="_blank" rel="noopener">Open and create page</a>
572 </div>
573 </div>
574 </div>
575 `;
576 } else {
577 resultItem.innerHTML = `
578 <div class="ai-result-error">
579 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
580 <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
581 <path d="M15 9L9 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
582 <path d="M9 9L15 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
583 </svg>
584 <div class="ai-result-content">
585 <h4>Page ${pageNumber}: Failed</h4>
586 <p class="ai-error-message">${result.error}</p>
587 </div>
588 </div>
589 `;
590 }
591
592 resultsList.appendChild(resultItem);
593 });
594
595 // Show summary message
596 if (successCount > 0) {
597 showMessage(`Successfully generated ${successCount} out of ${results.length} pages!`, 'success');
598 } else {
599 showMessage('No pages were generated successfully.', 'error');
600 }
601 }
602
603 function resetForm() {
604 document.getElementById('multi-page-form').reset();
605 document.getElementById('pages-config-section').style.display = 'none';
606 document.getElementById('generation-progress-section').style.display = 'none';
607 document.getElementById('results-section').style.display = 'none';
608 document.getElementById('generate-pages-btn').style.display = 'none';
609 updateTotalCost();
610 }
611
612 // Event listeners
613 document.addEventListener("DOMContentLoaded", function () {
614 (async () => {
615 console.log('Multi-page generator loaded');
616
617 // Debug: Check if config is loaded
618 console.log('window.config:', window.config);
619 console.log('window.config.apiUrl:', window.config?.apiUrl);
620
621 if (!window.config || !window.config.apiUrl) {
622 console.warn('Config not loaded, using fallback configuration');
623 // Fallback configuration
624 window.config = {
625 apiUrl: "https://api.wordpress-ai-builder.com/api"
626 };
627 console.log('Using fallback config:', window.config);
628 }
629
630 // Debug: Check if CSS is loaded
631 const testElement = document.createElement('div');
632 testElement.className = 'ai-multi-page-container';
633 testElement.style.display = 'none';
634 document.body.appendChild(testElement);
635 const computedStyle = window.getComputedStyle(testElement);
636 console.log('CSS loaded:', computedStyle.fontFamily !== '');
637 document.body.removeChild(testElement);
638
639 // Debug: Check if AJAX variables are available
640 console.log('ajaxurl:', window.ajaxurl);
641 console.log('aiBuilderVars:', window.aiBuilderVars);
642
643 if (!window.ajaxurl || !window.aiBuilderVars) {
644 console.error('AJAX variables not loaded!');
645 showMessage('AJAX configuration not loaded. Please refresh the page.', 'error');
646 return;
647 }
648
649 const hasPremiumAccess = await ensurePremiumAccess();
650 if (!hasPremiumAccess) {
651 return;
652 }
653
654 // Debug: Check if elements exist
655 const pageCountSelect = document.getElementById('page-count');
656 const totalCostElement = document.getElementById('total-cost');
657 const startBtn = document.getElementById('start-generation-btn');
658
659 console.log('Page count select found:', !!pageCountSelect);
660 console.log('Total cost element found:', !!totalCostElement);
661 console.log('Start button found:', !!startBtn);
662
663 if (pageCountSelect) {
664 // Page count change
665 pageCountSelect.addEventListener('change', function () {
666 console.log('Page count changed to:', this.value);
667 updateTotalCost();
668 });
669 } else {
670 console.error('Page count select element not found');
671 }
672
673 // Content type change
674 const contentTypeSelect = document.getElementById('content-type');
675 if (contentTypeSelect) {
676 contentTypeSelect.addEventListener('change', function () {
677 currentContentType = this.value;
678 console.log('Content type changed to:', this.value);
679 });
680 }
681
682 // Start generation button
683 if (startBtn) {
684 startBtn.addEventListener('click', function (e) {
685 e.preventDefault();
686 console.log('Start generation button clicked');
687 generatePagePrompts();
688 });
689 } else {
690 console.error('Start generation button not found');
691 }
692
693 // Generate pages button
694 const generateBtn = document.getElementById('generate-pages-btn');
695 if (generateBtn) {
696 generateBtn.addEventListener('click', generatePages);
697 }
698
699 // Generate more button
700 const generateMoreBtn = document.getElementById('generate-more-btn');
701 if (generateMoreBtn) {
702 generateMoreBtn.addEventListener('click', resetForm);
703 }
704
705 // Initialize
706 console.log('Initializing total cost...');
707 updateTotalCost();
708
709 // Load existing generations history and render sections
710 (async function loadGenerationsHistory() {
711 try {
712 const res = await fetch(ajaxurl, {
713 method: 'POST',
714 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
715 body: `action=aibui_get_generations&nonce=${aiBuilderVars.nonce}`,
716 });
717 const data = await res.json();
718 if (!data.success) return;
719 const gens = Array.isArray(data.data) ? data.data : [];
720
721 let history = document.getElementById('history-section');
722 if (!history) {
723 history = document.createElement('div');
724 history.id = 'history-section';
725 history.innerHTML = `
726 <h3 style="margin-top:24px;">Generations history</h3>
727 <div class="ai-history">
728 <div class="ai-history-pending">
729 <h4>Pending review</h4>
730 <div id="history-pending-list"></div>
731 </div>
732 <div class="ai-history-applied" style="margin-top:16px;">
733 <h4>Applied</h4>
734 <div id="history-applied-list"></div>
735 </div>
736 </div>`;
737 const container = document.querySelector('.ai-multi-page-container') || document.body;
738 container.appendChild(history);
739 }
740 const pendingList = document.getElementById('history-pending-list');
741 const appliedList = document.getElementById('history-applied-list');
742 if (!pendingList || !appliedList) return;
743 pendingList.innerHTML = '';
744 appliedList.innerHTML = '';
745
746 const adminBase = (typeof aiBuilderVars !== 'undefined' && aiBuilderVars.adminBaseUrl) ? aiBuilderVars.adminBaseUrl : '/wp-admin/';
747
748 gens.forEach((g) => {
749 const wrap = document.createElement('div');
750 wrap.className = 'ai-history-item';
751 wrap.style.cssText = 'display:flex;align-items:center;padding:10px;border:1px solid #e5e7eb;border-radius:6px;margin-bottom:8px;gap:12px;';
752 const title = document.createElement('div');
753 title.innerHTML = `<strong>${g.title || 'Untitled'}</strong><br><small>${g.createdAt || ''}</small>`;
754 const actions = document.createElement('div');
755 actions.style.cssText = 'margin-left:auto;display:flex;align-items:center;gap:10px;justify-content:flex-end;';
756
757 if (g.applied && g.pageId) {
758 const editUrl = `${adminBase}post.php?post=${encodeURIComponent(g.pageId)}&action=edit`;
759 actions.innerHTML = `<span class="ai-status">Status: Applied</span>
760 <a href="${editUrl}" target="_blank" rel="noopener" class="ai-page-link">Open page</a>`;
761 wrap.appendChild(title);
762 wrap.appendChild(actions);
763 appliedList.appendChild(wrap);
764 } else {
765 const applyUrl = `${adminBase}post-new.php?post_type=${currentContentType}&aibui_gen_id=${encodeURIComponent(g.id)}`;
766 actions.innerHTML = `<span class="ai-status">Status: Pending review</span>
767 <a href="${applyUrl}" target="_blank" rel="noopener" class="ai-page-link">Open and create page</a>`;
768 wrap.appendChild(title);
769 wrap.appendChild(actions);
770 pendingList.appendChild(wrap);
771 }
772 });
773 } catch (e) {
774 // silent
775 }
776 })();
777 })();
778 });
779