PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.1.5
AI Builder – Generate pages, blocks, images & translate with AI v2.1.5
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 2.3.10 All 122 releases
ai-builder / assets / js / pattern-translation.js

pattern-translation.js in AI Builder – Generate pages, blocks, images & translate with AI 2.1.5, at assets/js/pattern-translation.js

270 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global aibuiTranslation, ajaxurl */
2 (function () {
3 const { data, notices } = window.wp || {};
4 if (!data || !notices) {
5 console.log('[AI Builder] wp.data or wp.notices not available');
6 return;
7 }
8
9 const { select, subscribe } = data;
10 const notificationStore = data.dispatch('core/notices');
11 let button;
12 let modal;
13 let unsubscribe;
14
15 // Get languages from localized script
16 const languages = aibuiTranslation?.languages || {
17 en: 'English',
18 fr: 'French',
19 es: 'Spanish',
20 de: 'German',
21 it: 'Italian',
22 };
23
24 function createModal() {
25 if (modal) return;
26
27 modal = document.createElement('div');
28 modal.className = 'aibui-pattern-translate-modal';
29 modal.innerHTML = `
30 <div class="aibui-pattern-translate-modal-backdrop"></div>
31 <div class="aibui-pattern-translate-modal-content">
32 <h2>✨ Translate with AI</h2>
33 <p>Select the target language for this template part:</p>
34 <select class="aibui-pattern-translate-lang-select">
35 ${Object.entries(languages)
36 .map(([code, name]) => `<option value="${code}">${name}</option>`)
37 .join('')}
38 </select>
39 <div class="aibui-pattern-translate-modal-actions">
40 <button type="button" class="components-button is-secondary aibui-pattern-translate-cancel">Cancel</button>
41 <button type="button" class="components-button is-primary aibui-pattern-translate-confirm">Translate</button>
42 </div>
43 <div class="aibui-pattern-translate-loading" style="display:none;">
44 <span class="spinner is-active"></span>
45 <span>Translating...</span>
46 </div>
47 </div>
48 `;
49 document.body.appendChild(modal);
50
51 // Event listeners
52 modal.querySelector('.aibui-pattern-translate-modal-backdrop').addEventListener('click', closeModal);
53 modal.querySelector('.aibui-pattern-translate-cancel').addEventListener('click', closeModal);
54 modal.querySelector('.aibui-pattern-translate-confirm').addEventListener('click', confirmTranslate);
55 }
56
57 function openModal() {
58 createModal();
59 modal.style.display = 'flex';
60 }
61
62 function closeModal() {
63 if (modal) {
64 modal.style.display = 'none';
65 // Reset loading state
66 modal.querySelector('.aibui-pattern-translate-loading').style.display = 'none';
67 modal.querySelector('.aibui-pattern-translate-modal-actions').style.display = 'flex';
68 }
69 }
70
71 function ensureButton() {
72 if (!button) {
73 button = document.createElement('button');
74 button.type = 'button';
75 button.className =
76 'components-button is-primary aibui-floating-translate-button';
77 button.textContent = '✨ Translate with AI';
78 button.addEventListener('click', openModal);
79 document.body.appendChild(button);
80 }
81 }
82
83 function getEditedPostContext() {
84 const store = select('core/edit-site');
85 if (!store || typeof store.getEditedPostId !== 'function') {
86 return {};
87 }
88 const rawId = store.getEditedPostId();
89 const postType = typeof store.getEditedPostType === 'function'
90 ? store.getEditedPostType()
91 : null;
92
93 console.log('[AI Builder] getEditedPostContext:', { rawId, postType });
94
95 return {
96 id: rawId,
97 type: postType,
98 };
99 }
100
101 async function confirmTranslate() {
102 const { id, type } = getEditedPostContext();
103 console.log('[AI Builder] confirmTranslate called with:', { id, type });
104
105 if (!id || type !== 'wp_template_part') {
106 notificationStore.createNotice(
107 'error',
108 'Please open a template part (header/footer) to translate.',
109 { type: 'snackbar' }
110 );
111 closeModal();
112 return;
113 }
114
115 const langSelect = modal.querySelector('.aibui-pattern-translate-lang-select');
116 const targetLang = langSelect.value;
117
118 // Show loading
119 modal.querySelector('.aibui-pattern-translate-modal-actions').style.display = 'none';
120 modal.querySelector('.aibui-pattern-translate-loading').style.display = 'flex';
121
122 const params = new URLSearchParams();
123 params.append('action', 'aibui_translate_template_part');
124 params.append('nonce', aibuiTranslation.nonce);
125 params.append('template_part_id', id); // This is the slug like "theme//slug"
126 params.append('target_lang', targetLang);
127
128 console.log('[AI Builder] Sending AJAX request:', {
129 action: 'aibui_translate_template_part',
130 template_part_id: id,
131 target_lang: targetLang,
132 });
133
134 try {
135 const response = await fetch(aibuiTranslation.ajaxurl || ajaxurl, {
136 method: 'POST',
137 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
138 body: params.toString(),
139 });
140 const json = await response.json();
141 console.log('[AI Builder] AJAX response:', json);
142
143 closeModal();
144
145 if (json.success) {
146 notificationStore.createNotice(
147 'success',
148 aibuiTranslation?.messages?.success ||
149 'Translation created. Opening draft...',
150 { type: 'snackbar' }
151 );
152 if (json.data?.edit_url) {
153 window.open(json.data.edit_url, '_blank');
154 }
155 } else {
156 notificationStore.createNotice(
157 'error',
158 json.data?.message ||
159 aibuiTranslation?.messages?.error ||
160 'Translation failed.',
161 { type: 'snackbar' }
162 );
163 }
164 } catch (error) {
165 console.error('[AI Builder] AJAX error:', error);
166 closeModal();
167 notificationStore.createNotice(
168 'error',
169 aibuiTranslation?.messages?.error || 'Network error while translating.',
170 { type: 'snackbar' }
171 );
172 }
173 }
174
175 function updateButtonState() {
176 ensureButton();
177 if (!button) return;
178
179 const { id, type } = getEditedPostContext();
180 const isPart = id && type === 'wp_template_part';
181 button.disabled = !isPart;
182 button.style.display = isPart ? '' : 'none';
183 }
184
185 document.addEventListener('DOMContentLoaded', () => {
186 console.log('[AI Builder] Pattern translation script loaded');
187 ensureButton();
188 updateButtonState();
189
190 if (!unsubscribe) {
191 unsubscribe = subscribe(() => {
192 updateButtonState();
193 });
194 }
195 });
196
197 // Styles
198 const style = document.createElement('style');
199 style.textContent = `
200 .aibui-floating-translate-button {
201 position: fixed;
202 bottom: 24px;
203 right: 24px;
204 z-index: 9999;
205 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
206 }
207 .aibui-pattern-translate-modal {
208 display: none;
209 position: fixed;
210 top: 0;
211 left: 0;
212 right: 0;
213 bottom: 0;
214 z-index: 100000;
215 align-items: center;
216 justify-content: center;
217 }
218 .aibui-pattern-translate-modal-backdrop {
219 position: absolute;
220 top: 0;
221 left: 0;
222 right: 0;
223 bottom: 0;
224 background: rgba(0, 0, 0, 0.5);
225 }
226 .aibui-pattern-translate-modal-content {
227 position: relative;
228 background: #fff;
229 padding: 24px;
230 border-radius: 8px;
231 min-width: 320px;
232 max-width: 400px;
233 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
234 }
235 .aibui-pattern-translate-modal-content h2 {
236 margin: 0 0 12px;
237 font-size: 18px;
238 }
239 .aibui-pattern-translate-modal-content p {
240 margin: 0 0 16px;
241 color: #555;
242 }
243 .aibui-pattern-translate-lang-select {
244 width: 100%;
245 padding: 8px 12px;
246 font-size: 14px;
247 border: 1px solid #ccc;
248 border-radius: 4px;
249 margin-bottom: 16px;
250 }
251 .aibui-pattern-translate-modal-actions {
252 display: flex;
253 gap: 8px;
254 justify-content: flex-end;
255 }
256 .aibui-pattern-translate-loading {
257 display: flex;
258 align-items: center;
259 gap: 8px;
260 justify-content: center;
261 padding: 8px 0;
262 }
263 .aibui-pattern-translate-loading .spinner {
264 float: none;
265 margin: 0;
266 }
267 `;
268 document.head.appendChild(style);
269 })();
270