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,43 @@
import { animateDots } from '../optimizer/ui-manager.js';
/**
* Manages animation intervals for bulk operations.
*/
export class AnimationManager {
constructor() {
this.intervals = new Map();
}
/**
* Start animation for a post link.
*
* @param {number} postId - Post ID.
* @param {HTMLElement} link - Link element.
*/
start(postId, link) {
const interval = animateDots(link);
this.intervals.set(postId, interval);
}
/**
* Stop animation for a post.
*
* @param {number} postId - Post ID.
*/
stop(postId) {
const interval = this.intervals.get(postId);
if (interval) {
clearInterval(interval);
this.intervals.delete(postId);
}
}
/**
* Stop all animations.
*/
stopAll() {
this.intervals.forEach((interval) => clearInterval(interval));
this.intervals.clear();
}
}

View File

@@ -0,0 +1,36 @@
import apiFetch from '@wordpress/api-fetch';
import { POSTS_METADATA_ROUTE, BULK_DELETE_ROUTE } from './constants.js';
/**
* Fetch metadata for multiple posts.
*
* @param {number[]} postIds - Array of post IDs.
*
* @returns {Promise<{data: Array, errors: Array}>}
*/
export async function fetchPostsMetadata(postIds) {
return await apiFetch({
path: POSTS_METADATA_ROUTE,
method: 'POST',
data: { post_ids: postIds },
});
}
/**
* Bulk delete optimization data for multiple posts.
*
* @param {number[]} postIds - Array of post IDs.
* @param {string[]} postPaths - Array of post paths.
*
* @returns {Promise<{data: {successful: Array, failed: Array}}>}
*/
export async function bulkDeleteOptimization(postIds, postPaths) {
return await apiFetch({
path: BULK_DELETE_ROUTE,
method: 'DELETE',
data: {
post_ids: postIds,
post_paths: postPaths,
},
});
}

View File

@@ -0,0 +1,2 @@
export const POSTS_METADATA_ROUTE = '/kb-optimizer/v1/optimize/posts-metadata';
export const BULK_DELETE_ROUTE = '/kb-optimizer/v1/optimize/bulk/delete';

View File

@@ -0,0 +1,126 @@
import { __, _n, sprintf } from '@wordpress/i18n';
import { analyzeSite } from '../../optimizer/analyzer.js';
import { OPTIMIZER_DATA, UI_STATES } from '../../optimizer/constants.js';
import { createNotice, createDismissibleNotice, NOTICE_TYPES } from '@kadence-bundled/admin-notices';
import { AnimationManager } from '../animation-manager.js';
import { updatePostLinkState } from '../link-manager.js';
import { fetchPostsMetadata } from '../api.js';
import { initializeBulkUI } from '../ui.js';
/**
* Handle errors from metadata fetch.
*
* @param {Array} errors - Array of error objects.
* @param {AnimationManager} animationManager - Animation manager instance.
*/
function handleMetadataErrors(errors, animationManager) {
if (!errors || errors.length === 0) {
return;
}
errors.forEach((error) => {
createNotice(`Post ID ${error.post_id}: ${error.message}`, NOTICE_TYPES.ERROR, true);
animationManager.stop(error.post_id);
updatePostLinkState(error.post_id, UI_STATES.OPTIMIZE);
});
}
/**
* Handle bulk optimization action.
*
* @param {number[]} postIds - Array of post IDs to optimize.
*
* @returns {Promise<void>}
*/
export async function handleBulkOptimize(postIds) {
console.log('🚀 Starting bulk optimization for posts:', postIds);
const animationManager = new AnimationManager();
initializeBulkUI(postIds, animationManager);
const processingNotice = createDismissibleNotice(
sprintf(
// translators: %d: Number of posts being optimized.
_n('Optimizing %d post…', 'Optimizing %d posts…', postIds.length, 'kadence-blocks'),
postIds.length
),
NOTICE_TYPES.INFO
);
try {
const response = await fetchPostsMetadata(postIds);
const { data: postsData, errors } = response;
handleMetadataErrors(errors, animationManager);
const results = {
successful: [],
failed: [],
};
for (const postData of postsData) {
console.log(`🎯 Optimizing post ${postData.post_id}...`);
try {
await analyzeSite(postData.url, postData.post_id, postData.post_path, OPTIMIZER_DATA.token);
results.successful.push(postData.post_id);
animationManager.stop(postData.post_id);
updatePostLinkState(postData.post_id, UI_STATES.REMOVE);
} catch (error) {
console.error(`❌ Failed to optimize post ${postData.post_id}:`, error);
results.failed.push(postData.post_id);
createNotice(`Post ID ${postData.post_id}: ${error.message}`, NOTICE_TYPES.ERROR, true);
animationManager.stop(postData.post_id);
updatePostLinkState(postData.post_id, UI_STATES.OPTIMIZE);
}
}
if (results.successful.length > 0) {
createNotice(
sprintf(
// translators: %d: Number of posts optimized.
_n(
'🎉 %d post optimized successfully!',
'🎉 %d posts optimized successfully!',
results.successful.length,
'kadence-blocks'
),
results.successful.length
),
NOTICE_TYPES.SUCCESS
);
}
if (results.failed.length > 0) {
createNotice(
sprintf(
// translators: %d: Number of posts that failed.
_n(
'Failed to optimize %d post.',
'Failed to optimize %d posts.',
results.failed.length,
'kadence-blocks'
),
results.failed.length
),
NOTICE_TYPES.ERROR,
true
);
}
processingNotice?.remove();
} catch (error) {
console.error('❌ Bulk optimization failed:', error);
processingNotice?.remove();
createNotice(
__('Failed to fetch post data for bulk optimization.', 'kadence-blocks'),
NOTICE_TYPES.ERROR,
true,
error
);
}
}

View File

@@ -0,0 +1,117 @@
import { __, _n, sprintf } from '@wordpress/i18n';
import { UI_STATES } from '../../optimizer/constants.js';
import { createNotice, createDismissibleNotice, NOTICE_TYPES } from '@kadence-bundled/admin-notices';
import { AnimationManager } from '../animation-manager.js';
import { updatePostLinkState } from '../link-manager.js';
import { fetchPostsMetadata, bulkDeleteOptimization } from '../api.js';
import { initializeBulkUI } from '../ui.js';
/**
* Handle bulk remove optimization action.
*
* @param {number[]} postIds - Array of post IDs to remove optimization from.
*
* @returns {Promise<void>}
*/
export async function handleBulkRemoveOptimization(postIds) {
console.log('🗑️ Removing optimization for posts:', postIds);
const animationManager = new AnimationManager();
initializeBulkUI(postIds, animationManager);
const processingNotice = createDismissibleNotice(
sprintf(
// translators: %d: Number of posts having optimization removed.
_n(
'Removing optimization for %d post…',
'Removing optimization for %d posts…',
postIds.length,
'kadence-blocks'
),
postIds.length
),
NOTICE_TYPES.INFO
);
try {
const metadataResponse = await fetchPostsMetadata(postIds);
const { data: postsData, errors: metadataErrors } = metadataResponse;
if (metadataErrors && metadataErrors.length > 0) {
metadataErrors.forEach((error) => {
createNotice(`Post ID ${error.post_id}: ${error.message}`, NOTICE_TYPES.ERROR, true);
animationManager.stop(error.post_id);
updatePostLinkState(error.post_id, UI_STATES.REMOVE);
});
}
if (postsData.length > 0) {
const postIdsToDelete = postsData.map((post) => post.post_id);
const postPathsToDelete = postsData.map((post) => post.post_path);
console.log(`Removing optimization for ${postIdsToDelete.length} posts...`);
const deleteResponse = await bulkDeleteOptimization(postIdsToDelete, postPathsToDelete);
const { data: results } = deleteResponse;
results.successful.forEach((postId) => {
animationManager.stop(postId);
updatePostLinkState(postId, UI_STATES.OPTIMIZE);
});
if (results.failed && results.failed.length > 0) {
results.failed.forEach((failure) => {
createNotice(`Post ID ${failure.post_id}: ${failure.message}`, NOTICE_TYPES.ERROR, true);
animationManager.stop(failure.post_id);
updatePostLinkState(failure.post_id, UI_STATES.REMOVE);
});
}
if (results.successful.length > 0) {
createNotice(
sprintf(
// translators: %d: Number of posts.
_n(
'Removed optimization for %d post.',
'Removed optimization for %d posts.',
results.successful.length,
'kadence-blocks'
),
results.successful.length
),
NOTICE_TYPES.SUCCESS
);
}
if (results.failed && results.failed.length > 0) {
createNotice(
sprintf(
// translators: %d: Number of posts that failed.
_n(
'Failed to remove optimization for %d post.',
'Failed to remove optimization for %d posts.',
results.failed.length,
'kadence-blocks'
),
results.failed.length
),
NOTICE_TYPES.ERROR,
true
);
}
}
processingNotice?.remove();
} catch (error) {
console.error('❌ Bulk remove optimization failed:', error);
processingNotice?.remove();
createNotice(
__('Failed to fetch post data for bulk operation.', 'kadence-blocks'),
NOTICE_TYPES.ERROR,
true,
error
);
}
}

View File

@@ -0,0 +1,40 @@
import { handleBulkOptimize } from './handlers/optimize.js';
import { handleBulkRemoveOptimization } from './handlers/remove-optimization.js';
import { getSelectedPostIds } from './link-manager.js';
/**
* Initialize bulk action handlers.
*/
export function initBulkActions() {
const bulkActionForm = document.querySelector('#posts-filter');
if (!bulkActionForm) {
return;
}
bulkActionForm.addEventListener('submit', async (event) => {
// Only handle bulk actions when the Apply button (doaction) is clicked.
if (!event.submitter || event.submitter.id !== 'doaction') {
return;
}
const actionSelect = document.querySelector('#bulk-action-selector-top');
const action = actionSelect ? actionSelect.value : null;
if (action === 'kb_optimize_posts' || action === 'kb_optimize_remove') {
event.preventDefault();
const postIds = getSelectedPostIds();
if (postIds.length === 0) {
return;
}
if (action === 'kb_optimize_posts') {
await handleBulkOptimize(postIds);
} else if (action === 'kb_optimize_remove') {
await handleBulkRemoveOptimization(postIds);
}
}
});
}

View File

@@ -0,0 +1,30 @@
import { updateLinkState } from '../optimizer/ui-manager.js';
/**
* Find and update link state for a post.
*
* @param {number} postId - Post ID.
* @param {{class: string, text: string}} state - UI state to set (e.g., UI_STATES.OPTIMIZE).
*
* @returns {HTMLElement|null} The link element if found.
*/
export function updatePostLinkState(postId, state) {
const link = document.querySelector(`a[data-post-id="${postId}"]`);
if (link) {
updateLinkState(link, state);
}
return link;
}
/**
* Get all selected post IDs from checkboxes.
*
* @returns {number[]} Array of selected post IDs.
*/
export function getSelectedPostIds() {
const checkboxes = document.querySelectorAll('input[name="post[]"]:checked');
return Array.from(checkboxes).map((checkbox) => parseInt(checkbox.value, 10));
}

View File

@@ -0,0 +1,19 @@
import { UI_STATES } from '../optimizer/constants.js';
import { AnimationManager } from './animation-manager.js';
import { updatePostLinkState } from './link-manager.js';
/**
* Initialize UI state for bulk operation.
*
* @param {number[]} postIds - Array of post IDs.
* @param {AnimationManager} animationManager - Animation manager instance.
*/
export function initializeBulkUI(postIds, animationManager) {
postIds.forEach((postId) => {
const link = updatePostLinkState(postId, UI_STATES.OPTIMIZING);
if (link) {
animationManager.start(postId, link);
}
});
}