Add bulk term generation functionality and enhance logging
- Introduced a new JavaScript file for handling bulk term generation in the admin interface. - Implemented AJAX requests for generating terms and handling responses with appropriate logging. - Enhanced the Groq_AI_Ajax_Controller to support new options for term generation, including origin and force parameters. - Improved error handling and logging for term generation events. - Updated the user interface to reflect the status of term generation and provide feedback to the user.
This commit is contained in:
@@ -1,212 +0,0 @@
|
||||
(function () {
|
||||
const data = window.GroqAICategoryBulk || {};
|
||||
const startButton = document.getElementById('groq-ai-bulk-generate');
|
||||
const stopButton = document.getElementById('groq-ai-bulk-cancel');
|
||||
const statusField = document.getElementById('groq-ai-bulk-status');
|
||||
const logList = document.getElementById('groq-ai-bulk-log');
|
||||
|
||||
if (!startButton || !data.ajaxUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
let queue = [];
|
||||
let totalCount = 0;
|
||||
let processed = 0;
|
||||
let successes = 0;
|
||||
let isRunning = false;
|
||||
let abortRequested = false;
|
||||
|
||||
function formatString(template, values) {
|
||||
if (!template) {
|
||||
return '';
|
||||
}
|
||||
let autoIndex = 0;
|
||||
return template.replace(/%(\d+\$)?[sd]/g, (match, position) => {
|
||||
let valueIndex;
|
||||
if (position) {
|
||||
valueIndex = parseInt(position, 10) - 1;
|
||||
} else {
|
||||
valueIndex = autoIndex;
|
||||
autoIndex += 1;
|
||||
}
|
||||
const replacement = values[valueIndex];
|
||||
return typeof replacement === 'undefined' ? '' : String(replacement);
|
||||
});
|
||||
}
|
||||
|
||||
function setStatus(message, type) {
|
||||
if (!statusField) {
|
||||
return;
|
||||
}
|
||||
statusField.textContent = message || '';
|
||||
statusField.dataset.status = type || '';
|
||||
}
|
||||
|
||||
function appendLog(message, type) {
|
||||
if (!logList || !message) {
|
||||
return;
|
||||
}
|
||||
const item = document.createElement('li');
|
||||
item.textContent = message;
|
||||
item.dataset.status = type || '';
|
||||
logList.appendChild(item);
|
||||
}
|
||||
|
||||
function resetLog() {
|
||||
if (!logList) {
|
||||
return;
|
||||
}
|
||||
logList.innerHTML = '';
|
||||
}
|
||||
|
||||
function toggleButtons(running) {
|
||||
isRunning = running;
|
||||
startButton.disabled = running;
|
||||
if (stopButton) {
|
||||
stopButton.hidden = !running;
|
||||
}
|
||||
}
|
||||
|
||||
function getPendingTerms() {
|
||||
if (!Array.isArray(data.terms)) {
|
||||
return [];
|
||||
}
|
||||
return data.terms.filter((term) => !term.processed);
|
||||
}
|
||||
|
||||
function updateRow(termId, words) {
|
||||
const row = document.querySelector('[data-groq-ai-term-id="' + termId + '"]');
|
||||
if (!row) {
|
||||
return;
|
||||
}
|
||||
row.classList.remove('groq-ai-term-missing');
|
||||
row.classList.add('groq-ai-term-updated');
|
||||
const wordCell = row.querySelector('.groq-ai-word-count');
|
||||
if (wordCell) {
|
||||
wordCell.textContent = String(typeof words === 'number' ? words : wordCell.textContent);
|
||||
}
|
||||
}
|
||||
|
||||
function finish(state) {
|
||||
const summaryTemplate =
|
||||
state === 'done'
|
||||
? data.strings && data.strings.statusDone
|
||||
: state === 'stopped'
|
||||
? data.strings && data.strings.statusStopped
|
||||
: '';
|
||||
|
||||
const summary = summaryTemplate
|
||||
? formatString(summaryTemplate, [successes])
|
||||
: '';
|
||||
|
||||
const statusType = state === 'done' ? 'success' : state === 'stopped' ? 'info' : '';
|
||||
setStatus(summary, statusType);
|
||||
toggleButtons(false);
|
||||
queue = [];
|
||||
totalCount = 0;
|
||||
abortRequested = false;
|
||||
}
|
||||
|
||||
function processNext() {
|
||||
if (abortRequested) {
|
||||
finish('stopped');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue.length) {
|
||||
finish('done');
|
||||
return;
|
||||
}
|
||||
|
||||
const term = queue.shift();
|
||||
const position = processed + 1;
|
||||
const progressTemplate = data.strings && data.strings.statusProgress;
|
||||
if (progressTemplate) {
|
||||
setStatus(formatString(progressTemplate, [position, totalCount, term.name || '']), 'loading');
|
||||
}
|
||||
|
||||
const payload = new URLSearchParams();
|
||||
payload.append('action', 'groq_ai_bulk_generate_terms');
|
||||
payload.append('nonce', data.nonce || '');
|
||||
payload.append('taxonomy', data.taxonomy || 'product_cat');
|
||||
payload.append('term_id', term.id);
|
||||
|
||||
fetch(data.ajaxUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
},
|
||||
body: payload.toString(),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((json) => {
|
||||
if (!json.success) {
|
||||
const errorMessage = (json.data && json.data.message) || 'Onbekende fout';
|
||||
appendLog(formatString((data.strings && data.strings.logError) || '%1$s: %2$s', [term.name || term.id, errorMessage]), 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
term.processed = true;
|
||||
successes += 1;
|
||||
const words = json.data && typeof json.data.words !== 'undefined' ? json.data.words : 0;
|
||||
updateRow(term.id, words);
|
||||
appendLog(formatString((data.strings && data.strings.logSuccess) || '%1$s gevuld.', [term.name || term.id, words]), 'success');
|
||||
})
|
||||
.catch((error) => {
|
||||
appendLog(
|
||||
formatString((data.strings && data.strings.logError) || '%1$s: %2$s', [term.name || term.id, error && error.message ? error.message : 'Onbekende fout']),
|
||||
'error'
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
processed += 1;
|
||||
if (abortRequested) {
|
||||
finish('stopped');
|
||||
} else {
|
||||
processNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startBulk() {
|
||||
if (isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pending = getPendingTerms();
|
||||
if (!pending.length) {
|
||||
setStatus((data.strings && data.strings.statusEmpty) || '', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
queue = pending.slice();
|
||||
totalCount = queue.length;
|
||||
processed = 0;
|
||||
successes = 0;
|
||||
abortRequested = false;
|
||||
resetLog();
|
||||
toggleButtons(true);
|
||||
setStatus((data.strings && data.strings.statusIdle) || '', 'info');
|
||||
processNext();
|
||||
}
|
||||
|
||||
startButton.addEventListener('click', startBulk);
|
||||
|
||||
if (stopButton) {
|
||||
stopButton.addEventListener('click', () => {
|
||||
if (!isRunning) {
|
||||
return;
|
||||
}
|
||||
const confirmation = ! (data.strings && data.strings.confirmStop)
|
||||
? window.confirm('Stoppen?')
|
||||
: window.confirm(data.strings.confirmStop);
|
||||
if (confirmation) {
|
||||
abortRequested = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.terms) || !data.terms.length) {
|
||||
setStatus((data.strings && data.strings.statusEmpty) || '', 'info');
|
||||
}
|
||||
})();
|
||||
288
assets/js/term-bulk.js
Normal file
288
assets/js/term-bulk.js
Normal file
@@ -0,0 +1,288 @@
|
||||
(function () {
|
||||
const data = window.GroqAITermBulk || {};
|
||||
const startButton = document.getElementById('groq-ai-bulk-generate');
|
||||
const stopButton = document.getElementById('groq-ai-bulk-cancel');
|
||||
const statusField = document.getElementById('groq-ai-bulk-status');
|
||||
const logList = document.getElementById('groq-ai-bulk-log');
|
||||
|
||||
if (!data.ajaxUrl || !startButton || !statusField || !logList) {
|
||||
return;
|
||||
}
|
||||
|
||||
const strings = data.strings || {};
|
||||
const allowRegenerate = !!data.allowRegenerate;
|
||||
const terms = (Array.isArray(data.terms) ? data.terms : [])
|
||||
.map((term) => {
|
||||
const id = parseInt(term.id, 10);
|
||||
if (!Number.isFinite(id)) {
|
||||
return null;
|
||||
}
|
||||
const words = typeof term.words === 'number' ? term.words : parseInt(term.words, 10) || 0;
|
||||
const hasDescription = !!term.hasDescription;
|
||||
return {
|
||||
id,
|
||||
name: term.name || '',
|
||||
slug: term.slug || '',
|
||||
count: typeof term.count === 'number' ? term.count : parseInt(term.count, 10) || 0,
|
||||
words,
|
||||
hasDescription,
|
||||
needsGeneration: !hasDescription,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const termMap = new Map();
|
||||
terms.forEach((term) => termMap.set(term.id, term));
|
||||
|
||||
let queue = [];
|
||||
let totalCount = 0;
|
||||
let processed = 0;
|
||||
let successes = 0;
|
||||
let isRunning = false;
|
||||
let abortRequested = false;
|
||||
|
||||
function formatString(template, values) {
|
||||
if (!template) {
|
||||
return '';
|
||||
}
|
||||
let autoIndex = 0;
|
||||
return template.replace(/%(\d+\$)?[sd]/g, (match, position) => {
|
||||
let valueIndex;
|
||||
if (position) {
|
||||
valueIndex = parseInt(position, 10) - 1;
|
||||
} else {
|
||||
valueIndex = autoIndex;
|
||||
autoIndex += 1;
|
||||
}
|
||||
const replacement = values[valueIndex];
|
||||
return typeof replacement === 'undefined' ? '' : String(replacement);
|
||||
});
|
||||
}
|
||||
|
||||
function setStatus(message, type) {
|
||||
statusField.textContent = message || '';
|
||||
statusField.dataset.status = type || '';
|
||||
}
|
||||
|
||||
function appendLog(message, type) {
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
const item = document.createElement('li');
|
||||
item.textContent = message;
|
||||
item.dataset.status = type || '';
|
||||
logList.appendChild(item);
|
||||
}
|
||||
|
||||
function resetLog() {
|
||||
logList.innerHTML = '';
|
||||
}
|
||||
|
||||
function toggleButtons(running) {
|
||||
isRunning = running;
|
||||
startButton.disabled = running;
|
||||
if (stopButton) {
|
||||
stopButton.hidden = !running;
|
||||
}
|
||||
}
|
||||
|
||||
function getPendingTerms() {
|
||||
return terms.filter((term) => term.needsGeneration);
|
||||
}
|
||||
|
||||
function updateRow(term) {
|
||||
const row = document.querySelector('[data-groq-ai-term-id="' + term.id + '"]');
|
||||
if (!row) {
|
||||
return;
|
||||
}
|
||||
row.classList.remove('groq-ai-term-missing');
|
||||
row.classList.add('groq-ai-term-updated');
|
||||
const wordCell = row.querySelector('.groq-ai-word-count');
|
||||
if (wordCell) {
|
||||
wordCell.textContent = String(term.words);
|
||||
}
|
||||
}
|
||||
|
||||
function markTermCompleted(term, words) {
|
||||
term.hasDescription = true;
|
||||
term.needsGeneration = false;
|
||||
if (Number.isFinite(words)) {
|
||||
term.words = words;
|
||||
}
|
||||
updateRow(term);
|
||||
}
|
||||
|
||||
function finish(state) {
|
||||
const summaryTemplate = state === 'done' ? strings.statusDone : state === 'stopped' ? strings.statusStopped : '';
|
||||
const summary = summaryTemplate ? formatString(summaryTemplate, [successes]) : '';
|
||||
const statusType = state === 'done' ? 'success' : state === 'stopped' ? 'info' : '';
|
||||
setStatus(summary, statusType);
|
||||
toggleButtons(false);
|
||||
queue = [];
|
||||
totalCount = 0;
|
||||
processed = 0;
|
||||
successes = 0;
|
||||
abortRequested = false;
|
||||
}
|
||||
|
||||
function sendRequest(term, options = {}) {
|
||||
const payload = new URLSearchParams();
|
||||
payload.append('action', 'groq_ai_bulk_generate_terms');
|
||||
payload.append('nonce', data.nonce || '');
|
||||
payload.append('taxonomy', data.taxonomy || '');
|
||||
payload.append('term_id', term.id);
|
||||
if (options.force) {
|
||||
payload.append('force', '1');
|
||||
}
|
||||
|
||||
return fetch(data.ajaxUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
},
|
||||
body: payload.toString(),
|
||||
}).then((response) => response.json());
|
||||
}
|
||||
|
||||
function handleResponse(term, json, context) {
|
||||
if (!json || !json.success) {
|
||||
const errorMessage = (json && json.data && json.data.message) || 'Onbekende fout';
|
||||
appendLog(formatString(strings.logError || '%1$s: %2$s', [term.name || term.id, errorMessage]), 'error');
|
||||
if (context === 'single') {
|
||||
setStatus(formatString(strings.regenerateError || '%1$s mislukt: %2$s', [term.name || term.id, errorMessage]), 'error');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const words = json.data && typeof json.data.words !== 'undefined' ? parseInt(json.data.words, 10) : term.words;
|
||||
markTermCompleted(term, Number.isFinite(words) ? words : term.words);
|
||||
appendLog(formatString(strings.logSuccess || '%1$s gevuld.', [term.name || term.id, term.words]), 'success');
|
||||
if (context === 'single') {
|
||||
setStatus(formatString(strings.regenerateDone || '%s is bijgewerkt.', [term.name || term.id]), 'success');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function processNext() {
|
||||
if (abortRequested) {
|
||||
finish('stopped');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue.length) {
|
||||
finish('done');
|
||||
return;
|
||||
}
|
||||
|
||||
const term = queue.shift();
|
||||
const progressTemplate = strings.statusProgress;
|
||||
if (progressTemplate) {
|
||||
setStatus(formatString(progressTemplate, [processed + 1, totalCount, term.name || '']), 'loading');
|
||||
}
|
||||
|
||||
sendRequest(term)
|
||||
.then((json) => {
|
||||
if (handleResponse(term, json, 'bulk')) {
|
||||
successes += 1;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
appendLog(
|
||||
formatString(strings.logError || '%1$s: %2$s', [term.name || term.id, error && error.message ? error.message : 'Onbekende fout']),
|
||||
'error'
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
processed += 1;
|
||||
if (abortRequested) {
|
||||
finish('stopped');
|
||||
} else {
|
||||
processNext();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startBulk() {
|
||||
if (isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pending = getPendingTerms();
|
||||
if (!pending.length) {
|
||||
setStatus(strings.statusEmpty || '', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
queue = pending.slice();
|
||||
totalCount = queue.length;
|
||||
processed = 0;
|
||||
successes = 0;
|
||||
abortRequested = false;
|
||||
resetLog();
|
||||
toggleButtons(true);
|
||||
if (strings.statusIdle) {
|
||||
setStatus(strings.statusIdle, 'info');
|
||||
}
|
||||
processNext();
|
||||
}
|
||||
|
||||
startButton.addEventListener('click', startBulk);
|
||||
|
||||
if (stopButton) {
|
||||
stopButton.addEventListener('click', () => {
|
||||
if (!isRunning) {
|
||||
return;
|
||||
}
|
||||
const confirmation = strings.confirmStop ? window.confirm(strings.confirmStop) : window.confirm('Stoppen?');
|
||||
if (confirmation) {
|
||||
abortRequested = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (allowRegenerate) {
|
||||
const buttons = document.querySelectorAll('.groq-ai-regenerate-term');
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener('click', () => {
|
||||
if (isRunning) {
|
||||
setStatus(strings.regenerateBlocked || '', 'error');
|
||||
return;
|
||||
}
|
||||
const termId = parseInt(button.getAttribute('data-term-id'), 10);
|
||||
const term = termMap.get(termId);
|
||||
if (!term) {
|
||||
setStatus('Onbekende term.', 'error');
|
||||
return;
|
||||
}
|
||||
if (strings.confirmRegenerate) {
|
||||
const confirmed = window.confirm(formatString(strings.confirmRegenerate, [term.name || term.id]));
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
button.classList.add('is-busy');
|
||||
button.disabled = true;
|
||||
if (strings.regenerateProgress) {
|
||||
setStatus(formatString(strings.regenerateProgress, [term.name || term.id]), 'loading');
|
||||
}
|
||||
sendRequest(term, { force: true })
|
||||
.then((json) => {
|
||||
handleResponse(term, json, 'single');
|
||||
})
|
||||
.catch((error) => {
|
||||
const message = error && error.message ? error.message : 'Onbekende fout';
|
||||
appendLog(formatString(strings.logError || '%1$s: %2$s', [term.name || term.id, message]), 'error');
|
||||
setStatus(formatString(strings.regenerateError || '%1$s mislukt: %2$s', [term.name || term.id, message]), 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
button.disabled = false;
|
||||
button.classList.remove('is-busy');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (getPendingTerms().length === 0) {
|
||||
setStatus(strings.statusEmpty || '', 'info');
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user