feat: initial ACRIB WordPress deployment

- WordPress 6.9.4 (es_ES) with Kadence theme
- Homepage: Hero, La Asociación, Pilares, Beneficios, Eventos, Miembros, Hazte Miembro, Contacto
- Brand identity: #13294b navy, #a12932 burgundy, #c69c48 gold
- Fonts: Raleway (headings) + Source Sans 3 (body) + Lato (UI)
- Plugins: Kadence Blocks, Polylang, Contact Form 7
- Custom CSS with full brand styling and responsive layout
- HTTPS enforced via wp-config.php proxy detection
This commit is contained in:
Malin
2026-05-19 19:25:59 +02:00
commit f3ff7b7186
6119 changed files with 1984255 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
import { analyzeWebsite, isSupported } from '@stellarwp/perf-analyzer-client';
import { OPTIMIZE_ROUTE } from './constants';
import apiFetch from '@wordpress/api-fetch';
/**
* Analyze a specific website URL and collect optimization data.
*
* @param {string} url - The URL to analyze.
* @param {number} postId - The post ID for reference.
* @param {string} postPath - The post path.
* @param {string} nonce - The nonce value.
*
* @returns {Promise<*>}
*/
export async function analyzeSite(url, postId, postPath, nonce) {
console.log(`🎯 Analyzing post ${postId}: ${url}`);
if (!isSupported()) {
console.log('❌ Performance analysis not supported in this browser');
throw new Error('Performance analysis not supported in this browser.');
}
try {
let results;
try {
results = await analyzeWebsite(url, true, '[Kadence]', nonce);
} catch (analysisError) {
console.error('❌ Website analysis failed:', analysisError);
throw analysisError;
}
const res = await apiFetch({
path: OPTIMIZE_ROUTE,
method: 'POST',
data: {
post_path: postPath,
post_id: postId,
results,
},
});
console.log(res);
console.log(`✅ Analysis complete for post ID ${postId}:`, results);
// Send a request to generate a hash of the optimized page and don't wait for the results.
// Log any network errors for debugging.
void fetch(url + '?kadence_set_optimizer_hash=1', { credentials: 'omit', keepalive: true }).catch((error) => {
console.error('❌ Failed to generate desktop hash:', error);
});
// Send a request as a fake mobile device to generate the mobile hash.
void fetch(url + '?kadence_set_optimizer_hash=1&kadence_is_mobile=1', {
credentials: 'omit',
keepalive: true,
}).catch((error) => {
console.error('❌ Failed to generate mobile hash:', error);
});
return res;
} catch (error) {
console.error('❌ Analysis failed:', error);
throw error;
}
}
/**
* Delete a post's optimization data.
*
* @param {number} postId The post ID to delete the optimization data for.
* @param {string} postPath The relative path for the post.
*
* @returns {Promise<*>}
*/
export async function removeOptimization(postId, postPath) {
try {
return await apiFetch({
path: OPTIMIZE_ROUTE,
method: 'DELETE',
data: {
post_path: postPath,
post_id: postId,
},
});
} catch (error) {
console.error(`❌ Failed to remove optimization for post ID ${postId}:`, error);
throw error;
}
}

View File

@@ -0,0 +1,35 @@
export const OPTIMIZE_ROUTE = '/kb-optimizer/v1/optimize';
// Get localized strings from WordPress.
const l10n = window.kbOptimizerL10n || {};
// Contains the perf_token.
export const OPTIMIZER_DATA = window.kbOptimizer || {};
// UI State Constants.
// If you update this, match to the Optimizer_Provider.php file.
export const UI_STATES = {
OPTIMIZE: {
class: 'kb-optimize-post',
text: l10n.runOptimizer || 'Run Optimizer',
},
REMOVE: {
class: 'kb-remove-post-optimization',
text: l10n.removeOptimization || 'Remove Optimization',
},
OPTIMIZING: {
class: 'kb-optimizing',
text: l10n.optimizing || 'Optimizing',
},
OPTIMIZED: {
text: l10n.optimized || 'Optimized',
},
NOT_OPTIMIZED: {
text: l10n.notOptimized || 'Not Optimized',
},
EXCLUDED: {
text: l10n.excluded || 'Excluded',
},
NOT_OPTIMIZABLE: {
text: l10n.notOptimizable || 'Not Optimizable',
},
};

View File

@@ -0,0 +1,178 @@
import { __, sprintf } from '@wordpress/i18n';
import { addAction } from '@wordpress/hooks';
import { dispatch, select } from '@wordpress/data';
import { getPathAndQueryString } from '@wordpress/url';
import { analyzeSite, removeOptimization } from './analyzer.js';
import { OPTIMIZER_DATA, UI_STATES } from './constants.js';
import { createNotice, NOTICE_TYPES } from '@kadence-bundled/admin-notices';
import { POST_SAVED_HOOK } from '../../../../../../src/extension/post-saved-event/constants';
import { updateLinkState, getPostTitle, animateDots } from './ui-manager.js';
import { META_KEY, STATUS_EXCLUDED } from '../meta/constants';
/**
* Handle the post-save hook for automatic optimization.
*/
export function setupPostSaveHandler() {
addAction(POST_SAVED_HOOK, 'kadence', async ({ post, permalink, suffix = '' }) => {
console.log('Post Saved', post, permalink, suffix);
if (!permalink) {
console.error('❌ No URL found for optimization. This is likely not a public post.');
return;
}
const postId = post.id;
const postUrl = permalink;
const postPath = getPathAndQueryString(permalink);
const nonce = OPTIMIZER_DATA.token;
// This is a public post without a pretty rewrite rule.
if (postPath.includes('?')) {
console.error('❌ Unable to optimize. This is a public post, but has no rewrite rules.');
return;
}
const meta = select('core/editor').getEditedPostAttribute('meta');
if (meta !== undefined && meta[META_KEY] === STATUS_EXCLUDED) {
console.warn(`⚠️ Post ID ${postId} was excluded from optimization via post meta.`);
return;
}
console.log('🚀 Starting optimization...', { postId, postUrl, postPath });
console.log(
'%c' + 'Warnings are expected!',
'color: #edd144; -webkit-text-stroke: 0.5px black; font-size: 28px; font-weight: bold;'
);
try {
const response = await analyzeSite(postUrl, postId, postPath, nonce);
console.log(response);
dispatch('core/notices').createSuccessNotice(__('Kadence Optimization Data Updated.', 'kadence-blocks'), {
type: 'snackbar',
});
// Force Gutenberg to reload the post + meta from the server so the View Optimized Page component can display in real-time.
dispatch('core').invalidateResolution('getEntityRecord', ['postType', post.type, post.id]);
} catch (error) {
console.error('❌ Optimization failed:', error);
dispatch('core/notices').createErrorNotice(
__('Failed to update Kadence Optimization Data.', 'kadence-blocks'),
{
type: 'snackbar',
}
);
}
});
}
/**
* Handle optimize link click from the post list table.
*
* @param {Event} event - The click event
*/
export async function handleOptimizeClick(event) {
event.preventDefault();
const postUrl = event.target.dataset.postUrl;
const postPath = event.target.dataset.postPath;
if (!postUrl) {
console.error('❌ No URL found for optimization');
return;
}
if (!postPath) {
console.error('❌ No Post Path found for optimization');
return;
}
const postId = parseInt(event.target.dataset.postId, 10);
const nonce = event.target.dataset.nonce;
console.log('🚀 Starting optimization...', { postId, postUrl });
console.log(
'%c' + 'Warnings are expected!',
'color: #edd144; -webkit-text-stroke: 0.5px black; font-size: 28px; font-weight: bold;'
);
// Show "Optimizing..." state with animated dots.
updateLinkState(event.target, UI_STATES.OPTIMIZING);
const animationInterval = animateDots(event.target);
const postTitle = getPostTitle(event.target, postId);
try {
const response = await analyzeSite(postUrl, postId, postPath, nonce);
console.log(response);
// Clear the animation interval.
clearInterval(animationInterval);
// Update link state to show "Remove Optimization".
updateLinkState(event.target, UI_STATES.REMOVE);
createNotice(
sprintf(
// translators: %s: The post title or post ID.
__('🎉 "%s" is now Optimized!', 'kadence-blocks'),
postTitle
)
);
} catch (error) {
console.error('❌ Optimization failed:', error);
// Clear the animation interval.
clearInterval(animationInterval);
// Reset to original state.
updateLinkState(event.target, UI_STATES.OPTIMIZE);
createNotice(
// translators: %s: The post title or post ID.
sprintf(__('Optimization failed for "%s"', 'kadence-blocks'), postTitle),
NOTICE_TYPES.ERROR,
true,
error
);
}
}
/**
* Handle remove optimization link click in the post list table.
*
* @param {Event} event - The click event
*/
export async function handleRemoveOptimizationClick(event) {
event.preventDefault();
const postId = parseInt(event.target.dataset.postId, 10);
const postPath = event.target.dataset.postPath;
console.log('Removing optimization...', { postId, postPath });
try {
const response = await removeOptimization(postId, postPath);
console.log(response);
// Update link state to show "Run Optimizer".
updateLinkState(event.target, UI_STATES.OPTIMIZE);
const postTitle = getPostTitle(event.target, postId);
createNotice(
sprintf(
// translators: %s: The post title or post ID.
__('Optimization data removed for "%s".', 'kadence-blocks'),
postTitle
)
);
} catch (error) {
console.error('❌ Failed to remove optimization:', error);
createNotice(__('An error occurred', 'kadence-blocks'), NOTICE_TYPES.ERROR, true, error);
}
}

View File

@@ -0,0 +1,13 @@
import { initOptimizer } from './initializer.js';
import { initBulkActions } from '../bulk-actions';
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
initOptimizer();
initBulkActions();
});
} else {
initOptimizer();
initBulkActions();
}

View File

@@ -0,0 +1,19 @@
import { setupPostSaveHandler, handleOptimizeClick, handleRemoveOptimizationClick } from './event-handlers.js';
/**
* Initialize the optimizer.
*/
export function initOptimizer() {
// Set up the post-save handler for automatic optimization.
setupPostSaveHandler();
// Add click event listeners to all optimize links on the post list table.
document.addEventListener('click', async function (event) {
// Check if the clicked element is an optimize link.
if (event.target.classList.contains('kb-optimize-post')) {
await handleOptimizeClick(event);
} else if (event.target.classList.contains('kb-remove-post-optimization')) {
await handleRemoveOptimizationClick(event);
}
});
}

View File

@@ -0,0 +1,53 @@
import { UI_STATES } from './constants.js';
/**
* Update the optimizer link state in the post list table.
*
* @param {HTMLElement} link - The link element to update.
* @param {{class: string, text: string}} state - The state to set (OPTIMIZE, REMOVE, or OPTIMIZING).
*/
export function updateLinkState(link, state) {
// Remove all state classes.
link.className = link.className
.replace(UI_STATES.OPTIMIZE.class, '')
.replace(UI_STATES.REMOVE.class, '')
.replace(UI_STATES.OPTIMIZING.class, '')
.trim();
// Add the new state class.
link.className += ' ' + state.class;
// Update text content.
link.textContent = state.text;
}
/**
* Get the post title from the row containing the given element in the post list table.
*
* @param {HTMLElement} element - The element to find the row for.
* @param {number} postId - The post ID as fallback.
*
* @returns {string} The post title or fallback text.
*/
export function getPostTitle(element, postId) {
const row = element.closest('tr');
const rowTitle = row ? row.querySelector('.row-title')?.textContent?.trim() : null;
return rowTitle || `Post ID: ${postId}`;
}
/**
* Animate the dots in the "Optimizing" text.
*
* @param {HTMLElement} link - The link element to animate.
*/
export function animateDots(link) {
let dots = 0;
const baseText = link.textContent;
return setInterval(() => {
dots = (dots + 1) % 4;
const dotsText = '.'.repeat(dots);
link.textContent = baseText + dotsText;
}, 500);
}