From 47e77f9fac1155c536b2b87fec552c7fcce66fa6 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 01 Jun 2026 18:06:34 +0000
Subject: [PATCH] =Timeline block fixes. Next up: adding article schema classes
---
assets/js/admin.js | 394 +++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 306 insertions(+), 88 deletions(-)
diff --git a/assets/js/admin.js b/assets/js/admin.js
index 75bbd7f..7230dc8 100644
--- a/assets/js/admin.js
+++ b/assets/js/admin.js
@@ -1,102 +1,320 @@
document.addEventListener("DOMContentLoaded", function(e) {
- console.log('working');
- // Tabs functionality for settings pages
- let tabs = document.querySelectorAll('.jvb-settings-tab');
+ // Tabs functionality for settings pages
+ let tabs = document.querySelectorAll('.jvb-settings-tab');
- tabs.forEach(tab => {
- tab.addEventListener('click', (e) => {
- removeActiveTab(tabs);
- tab.classList.add('active');
- setActiveSection(tab);
- });
- });
+ tabs.forEach(tab => {
+ tab.addEventListener('click', (e) => {
+ removeActiveTab(tabs);
+ tab.classList.add('active');
+ setActiveSection(tab);
+ });
+ });
- // Check for hash in URL and activate corresponding tab
- if (window.location.hash) {
- var hash = window.location.hash.substring(1);
- document.querySelector('.jvb-settings-tab[data-tab="' + hash + '"]')?.click();
- } else {
- // Activate first tab by default
- document.querySelector('.jvb-settings-tab')?.click();
- }
+ // Check for hash in URL and activate corresponding tab
+ if (window.location.hash) {
+ var hash = window.location.hash.substring(1);
+ document.querySelector('.jvb-settings-tab[data-tab="' + hash + '"]')?.click();
+ } else {
+ // Activate first tab by default
+ document.querySelector('.jvb-settings-tab')?.click();
+ }
- let confirm = document.querySelector('.jvb-confirm-action');
- if (confirm) {
- confirm.addEventListener('click', (e) => {
- if (!window.confirm(confirm.dataset.confirm || 'Are you sure?')) {
- e.preventDefault();
- return false;
- }
- });
- }
+ let confirm = document.querySelector('.jvb-confirm-action');
+ if (confirm) {
+ confirm.addEventListener('click', (e) => {
+ if (!window.confirm(confirm.dataset.confirm || 'Are you sure?')) {
+ e.preventDefault();
+ return false;
+ }
+ });
+ }
- // Admin action buttons
- document.querySelectorAll('a[data-action]').forEach(action => {
- action.addEventListener('click', (e) => {
- e.preventDefault();
- let loader = action.querySelector('.loader');
- loader.classList.add('loading');
-
- let a = action.dataset.action;
-
- fetch(jvbSettings.api, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'X-WP-Nonce': jvbSettings.nonce,
- 'action_nonce': jvbSettings.action
- },
- body: JSON.stringify({
- action: a
- })
- })
- .then(response => {
- if (!response.ok) {
- throw new Error('Network response was not ok');
- }
- return response.json();
- })
- .then(data => {
- if (data.success === true) {
- loader.classList.remove('loading');
- loader.classList.add('loaded');
- setTimeout(() => {
- loader.classList.remove('loaded');
- }, 3000);
- } else {
- throw new Error(data.message || 'Action failed');
- }
- })
- .catch(error => {
- console.error('Error:', error);
- loader.classList.remove('loading');
- // You might want to add an error state class here
- });
- });
- });
+ // Initialize admin actions
+ initAdminActions();
+ initCacheActions();
+ initIconActions();
});
function removeActiveTab(tabs) {
- let active = document.querySelectorAll('.active');
- active.forEach(tab => {
- tab.classList.remove('active');
- if (tab.dataset.tab) {
- setActiveSection(tab, false);
- window.location.hash = tab.dataset.tab;
- }
- });
+ let active = document.querySelectorAll('.active');
+ active.forEach(tab => {
+ tab.classList.remove('active');
+ if (tab.dataset.tab) {
+ setActiveSection(tab, false);
+ window.location.hash = tab.dataset.tab;
+ }
+ });
}
function setActiveSection(tab, set = true) {
- let id = tab.dataset.tab;
- if (!id) return;
+ let id = tab.dataset.tab;
+ if (!id) return;
- let section = document.querySelector('#' + id);
- if (!section) return;
+ let section = document.querySelector('#' + id);
+ if (!section) return;
- if (set) {
- section.classList.add('active');
- } else {
- section.classList.remove('active');
- }
+ if (set) {
+ section.classList.add('active');
+ } else {
+ section.classList.remove('active');
+ }
+}
+
+/**
+ * Initialize admin action buttons (dashboard quick actions)
+ */
+function initAdminActions() {
+ document.querySelectorAll('a[data-action]').forEach(action => {
+ action.addEventListener('click', (e) => {
+ e.preventDefault();
+ let loader = action.querySelector('.loader');
+ loader.classList.add('loading');
+
+ let a = action.dataset.action;
+
+ makeAdminRequest('admin-action', { action: a })
+ .then(data => {
+ if (data.success === true) {
+ loader.classList.remove('loading');
+ loader.classList.add('loaded');
+ setTimeout(() => {
+ loader.classList.remove('loaded');
+ }, 3000);
+ } else {
+ throw new Error(data.message || 'Action failed');
+ }
+ })
+ .catch(error => {
+ console.error('Error:', error);
+ loader.classList.remove('loading');
+ alert('Error: ' + error.message);
+ });
+ });
+ });
+}
+
+/**
+ * Initialize cache management actions
+ */
+function initCacheActions() {
+ const flushAllBtn = document.querySelector('[data-cache-action="flush-all"]');
+ if (flushAllBtn) {
+ flushAllBtn.addEventListener('click', function() {
+ const originalText = this.innerHTML;
+ this.disabled = true;
+ this.innerHTML = 'Flushing...';
+
+ makeAdminRequest('admin-cache', { action: 'flush-all' })
+ .then(data => handleActionResponse(data, this, originalText))
+ .catch(error => handleActionError(error, this, originalText));
+ });
+ }
+
+ document.querySelectorAll('[data-cache-action="flush-cache"]').forEach(btn => {
+ btn.addEventListener('click', function() {
+ const group = this.getAttribute('data-group');
+ const originalText = this.innerHTML;
+ this.disabled = true;
+ this.innerHTML = 'Flushing...';
+
+ makeAdminRequest('admin-cache', { action: 'flush-cache', group: group })
+ .then(data => handleActionResponse(data, this, originalText))
+ .catch(error => handleActionError(error, this, originalText));
+ });
+ });
+}
+
+/**
+ * Initialize icon management actions
+ */
+function initIconActions() {
+ const currentSource = document.getElementById('icon-source-select')?.value || 'icons';
+
+ // Select all checkbox
+ const selectAll = document.getElementById('select-all-versions');
+ if (selectAll) {
+ selectAll.addEventListener('change', function() {
+ document.querySelectorAll('.version-checkbox').forEach(checkbox => {
+ checkbox.checked = this.checked;
+ checkbox.dispatchEvent(new Event('change'));
+ });
+ });
+ }
+
+ // Enable/disable merge button based on selection
+ document.querySelectorAll('.version-checkbox').forEach(checkbox => {
+ checkbox.addEventListener('change', updateMergeButtonState);
+ });
+
+ // Toggle icon list view
+ document.querySelectorAll('.view-icon-list-btn').forEach(btn => {
+ btn.addEventListener('click', function() {
+ const timestamp = this.getAttribute('data-timestamp');
+ const row = document.getElementById('icon-list-' + timestamp);
+ if (row) {
+ row.style.display = row.style.display === 'none' ? '' : 'none';
+ this.textContent = row.style.display === 'none' ? '(view)' : '(hide)';
+ }
+ });
+ });
+
+ // Force refresh button
+ const refreshBtn = document.querySelector('[data-icon-action="refresh-icons"]');
+ if (refreshBtn) {
+ refreshBtn.addEventListener('click', function() {
+ const originalText = this.innerHTML;
+ this.disabled = true;
+ this.innerHTML = 'Regenerating...';
+
+ const source = this.getAttribute('data-source') || currentSource;
+ makeAdminRequest('admin-icons', { action: 'refresh-icons', source: source })
+ .then(data => {
+ handleActionResponse(data, this, originalText);
+ })
+ .catch(error => {
+ handleActionError(error, this, originalText);
+ });
+
+ });
+ }
+
+ // Merge versions button
+ const mergeBtn = document.getElementById('merge-versions-btn');
+ if (mergeBtn) {
+ mergeBtn.addEventListener('click', function() {
+ const checkboxes = document.querySelectorAll('.version-checkbox:checked');
+ const timestamps = Array.from(checkboxes).map(cb => parseInt(cb.value));
+
+ if (timestamps.length < 2) {
+ alert('Please select at least 2 versions to merge');
+ return;
+ }
+
+ if (confirm(`Merge ${timestamps.length} versions? This will create a new CSS file with all unique icons.`)) {
+ const originalText = this.innerHTML;
+ this.disabled = true;
+ this.innerHTML = 'Merging...';
+
+ const source = this.getAttribute('data-source') || currentSource;
+ makeAdminRequest('admin-icons', {
+ action: 'merge-icon-versions',
+ source: source,
+ timestamps: timestamps
+ })
+ .then(data => {
+ handleActionResponse(data, this, originalText);
+ })
+ .catch(error => {
+ handleActionError(error, this, originalText);
+ });
+ }
+ });
+ }
+
+ // Restore version buttons
+ document.querySelectorAll('[data-icon-action="restore-icon-version"]').forEach(btn => {
+ btn.addEventListener('click', function() {
+ const timestamp = parseInt(this.getAttribute('data-timestamp'));
+ const source = this.getAttribute('data-source') || currentSource;
+
+ if (confirm('Restore this icon version? This will reload the page.')) {
+ const originalText = this.innerHTML;
+ this.disabled = true;
+ this.innerHTML = 'Restoring...';
+
+ makeAdminRequest('admin-icons', {
+ action: 'restore-icon-version',
+ source: source,
+ timestamp: timestamp
+ })
+ .then(data => {
+ handleActionResponse(data, this, originalText);
+ })
+ .catch(error => {
+ handleActionError(error, this, originalText);
+ });
+ }
+ });
+ });
+}
+
+/**
+ * Update merge button state based on selected checkboxes
+ */
+function updateMergeButtonState() {
+ const checkedCount = document.querySelectorAll('.version-checkbox:checked').length;
+ const mergeBtn = document.getElementById('merge-versions-btn');
+ if (mergeBtn) {
+ mergeBtn.disabled = checkedCount < 2;
+ }
+}
+
+/**
+ * Make an admin API request
+ * @param {string} endpoint - The API endpoint (without 'jvb/v1/')
+ * @param {object} data - The data to send
+ * @returns {Promise}
+ */
+function makeAdminRequest(endpoint, data = {}) {
+ if (typeof jvbSettings === 'undefined') {
+ console.error('jvbSettings is not defined');
+ return Promise.reject(new Error('jvbSettings is not defined. Scripts may not be loaded correctly.'));
+ }
+
+ console.log('Making request to:', jvbSettings.api + endpoint, 'with data:', data);
+
+ return fetch(jvbSettings.api + endpoint, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-WP-Nonce': jvbSettings.nonce,
+ 'X-Action-Nonce': jvbSettings.action
+ },
+ body: JSON.stringify(data)
+ })
+ .then(response => {
+ console.log('Response status:', response.status);
+ if (!response.ok) {
+ return response.json().then(err => {
+ throw new Error(err.message || 'Network response was not ok');
+ });
+ }
+ return response.json();
+ })
+ .then(data => {
+ console.log('Response data:', data);
+ return data;
+ });
+}
+
+/**
+ * Handle successful action response
+ */
+function handleActionResponse(data, button = null, originalText = null) {
+ if (!data.success) {
+ throw new Error(data.message || 'Unknown error');
+ }
+
+ if (button && originalText) {
+ button.innerHTML = '✓ Success!';
+ setTimeout(() => {
+ button.disabled = false;
+ button.innerHTML = originalText;
+ }, 1500);
+ }
+}
+
+/**
+ * Handle action error
+ */
+function handleActionError(error, button = null, originalText = null) {
+ console.error('Error:', error);
+
+ if (button && originalText) {
+ button.innerHTML = '✗ ' + (error.message || 'Error');
+ setTimeout(() => {
+ button.disabled = false;
+ button.innerHTML = originalText;
+ }, 2000);
+ }
}
--
Gitblit v1.10.0