various improvements to make release 1 complete

This commit is contained in:
Reindl David (IT-PTR-CEN2-SL10)
2026-03-27 13:59:28 +01:00
parent 9ea962131a
commit 6b9c3fefa1
37 changed files with 7695 additions and 1803 deletions
+29
View File
@@ -12,6 +12,35 @@
color: #23282d;
}
/* Form actions with buttons */
.swi-foot-form-actions {
display: flex;
gap: 10px;
margin-top: 20px;
margin-bottom: 10px;
}
.swi-foot-form-actions button {
flex-shrink: 0;
}
/* Connection status messages */
#connection-status {
display: inline-block;
margin-left: 15px;
font-weight: 500;
line-height: 1.5;
min-height: 22px;
}
#connection-status.success {
color: #046b3a;
}
#connection-status.error {
color: #d63638;
}
/* Teams grid layout */
.swi-foot-teams-grid {
display: grid;
+96 -37
View File
@@ -2,6 +2,96 @@
jQuery(document).ready(function($) {
'use strict';
// Settings form with Save and Test functionality
$('#swi-foot-settings-form').on('submit', function(e) {
var $form = $(this);
var $status = $('#connection-status');
var action = $('[name="swi_foot_action"]:focus').val() || 'save_only';
e.preventDefault();
// Get form data
var formData = new FormData($form[0]);
// Submit form via WordPress options.php
fetch($form.attr('action'), {
method: 'POST',
credentials: 'same-origin',
body: formData
}).then(function(resp) {
// After form saves successfully, test if requested
if (action === 'save_and_test') {
return test_api_connection($status);
} else {
$('<div class="notice notice-info is-dismissible"><p>Settings saved!</p></div>')
.insertAfter('.wrap h1');
}
}).catch(function(err) {
$('<div class="notice notice-error is-dismissible"><p>Error saving settings: ' + err.message + '</p></div>')
.insertAfter('.wrap h1');
});
});
// Function to test API connection
function test_api_connection($statusElement) {
// Get current field values from form
var apiUrl = $('input[name="swi_foot_api_base_url"]').val();
var username = $('input[name="swi_foot_api_username"]').val();
var password = $('input[name="swi_foot_api_password"]').val();
// Validate credentials first
if (!username || !password) {
$('<div class="notice notice-error is-dismissible"><p>API credentials not configured. Please configure Username and Password.</p></div>')
.insertAfter('.wrap h1');
return;
}
if (!apiUrl) {
$('<div class="notice notice-error is-dismissible"><p>API Base URL not configured.</p></div>')
.insertAfter('.wrap h1');
return;
}
// Step 1: Check if URL is reachable with a HEAD request
var urlTest = apiUrl.replace(/\/$/, '') + '/';
fetch(urlTest, {
method: 'HEAD',
credentials: 'same-origin',
mode: 'no-cors'
}).then(function(resp) {
// Step 2: Make actual API call via REST endpoint
return fetch(swi_foot_ajax.rest_url.replace(/\/$/, '') + '/admin/test-connection', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': swi_foot_ajax.rest_nonce
},
body: JSON.stringify({ test: true })
});
}).then(function(resp) {
return resp.json();
}).then(function(response) {
if (response && response.success) {
$('<div class="notice notice-success is-dismissible"><p>Settings saved and connection test successful!</p></div>')
.insertAfter('.wrap h1');
} else {
var errorMsg = (response.error || 'Connection failed');
var details = response.details ? ': ' + response.details : '';
$('<div class="notice notice-error is-dismissible"><p>' + errorMsg + details + '</p></div>')
.insertAfter('.wrap h1');
}
}).catch(function(err) {
$('<div class="notice notice-error is-dismissible"><p>API URL not reachable: ' + apiUrl + '</p></div>')
.insertAfter('.wrap h1');
});
}
// Handle button clicks to set which action was used
$('button[name="swi_foot_action"]').on('click', function() {
$(this).focus();
});
// Refresh teams functionality
$('#refresh-teams').on('click', function() {
var $button = $(this);
@@ -18,13 +108,13 @@ jQuery(document).ready(function($) {
return resp.json();
}).then(function(response) {
if (response && response.success) {
$status.text('Teams refreshed successfully!').addClass('success');
$status.text('✓ Teams refreshed successfully!').addClass('success');
setTimeout(function() { location.reload(); }, 1500);
} else {
$status.text('Error: ' + (response.error || 'Unknown')).addClass('error');
$status.text('✗ Error: ' + (response.error || 'Unknown')).addClass('error');
}
}).catch(function() {
$status.text('Network error occurred.').addClass('error');
$status.text('✗ Network error occurred.').addClass('error');
}).finally(function() {
$button.prop('disabled', false).text('Refresh Teams List');
});
@@ -48,47 +138,16 @@ jQuery(document).ready(function($) {
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': swi_foot_ajax.rest_nonce }
}).then(function(resp) { return resp.json(); }).then(function(response) {
if (response && response.success) {
$status.text('Cache cleared successfully!').addClass('success');
$status.text('✓ Cache cleared successfully!').addClass('success');
setTimeout(function() { location.reload(); }, 1000);
} else {
$status.text('Error clearing cache.').addClass('error');
$status.text('✗ Error clearing cache.').addClass('error');
}
}).catch(function() {
$status.text('Error clearing cache.').addClass('error');
$status.text('✗ Error clearing cache.').addClass('error');
}).finally(function() {
$button.prop('disabled', false);
});
});
// Test API connection
$('#test-connection').on('click', function() {
var $button = $(this);
var $status = $('#connection-status');
$button.prop('disabled', true).text('Testing...');
$status.text('').removeClass('success error');
fetch(swi_foot_ajax.rest_url.replace(/\/$/, '') + '/admin/test-connection', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': swi_foot_ajax.rest_nonce }
}).then(function(resp) { return resp.json(); }).then(function(response) {
if (response && response.success) {
$status.text('Connection successful!').addClass('success');
} else {
$status.text('Connection failed: ' + (response.error || 'Unknown')).addClass('error');
}
}).catch(function() {
$status.text('Network error occurred.').addClass('error');
}).finally(function() {
$button.prop('disabled', false).text('Test API Connection');
});
});
// Auto-save settings notice
$('form').on('submit', function() {
$('<div class="notice notice-info is-dismissible"><p>Settings saved! The plugin will automatically refresh API tokens as needed.</p></div>')
.insertAfter('.wrap h1');
});
});
+71 -1
View File
@@ -143,7 +143,7 @@
font-weight: 600;
}
/* Error messages */
/* Error messages - for configuration/setup issues */
.swi-foot-error {
color: #721c24;
padding: 12px 16px;
@@ -154,6 +154,39 @@
font-weight: 500;
}
/* Warning messages - yellow style for "data not yet available" (406) */
.swi-foot-data-warning {
color: #856404;
padding: 12px 16px;
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 6px;
margin: 15px 0;
font-weight: 500;
}
/* Unavailable data messages - grey style for "no data available" */
.swi-foot-data-unavailable {
color: #6c757d;
padding: 12px 16px;
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
margin: 15px 0;
font-weight: 500;
}
/* Notice messages - informational style (deprecated, use data-warning or data-unavailable) */
.swi-foot-notice {
color: #6c757d;
padding: 12px 16px;
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
margin: 15px 0;
font-weight: 500;
}
/* Responsive design */
@media (max-width: 768px) {
.swi-foot-table {
@@ -379,3 +412,40 @@
max-height: 80px;
}
}
/* Roster and Bench Player Formatting */
.swi-foot-position {
color: #6c757d;
font-size: 0.9em;
font-weight: 400;
font-style: italic;
}
.swi-foot-role {
color: #6c757d;
font-size: 0.9em;
font-weight: 400;
font-style: italic;
margin-left: 4px;
}
.roster-player-captain {
font-weight: 500;
color: #d4a017;
}
.swi-foot-captain-badge {
display: inline-block;
background-color: #d4a017;
color: #fff;
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
border-radius: 50%;
font-weight: 700;
font-size: 12px;
margin-right: 4px;
vertical-align: middle;
}
+653
View File
@@ -0,0 +1,653 @@
// Swiss Football Gutenberg Blocks - Refactored User Flow
(function (blocks, element, editor, components, i18n, data) {
'use strict';
const { registerBlockType } = blocks;
const { createElement: el, useState, useEffect } = element;
const { InspectorControls } = editor;
const { PanelBody, SelectControl, TextControl, ToggleControl, Spinner, RangeControl } = components;
const { __ } = i18n;
// Helper function to make AJAX calls
function makeAjaxCall(action, data = {}) {
return new Promise((resolve, reject) => {
jQuery.post(swiFootEditorData.ajax_url, {
action: action,
nonce: swiFootEditorData.nonce,
...data
}, function (response) {
if (response && response.success) {
resolve(response);
} else {
reject(response);
}
}).fail(reject);
});
}
// Main Swiss Football Block - Refactored Logic
registerBlockType('swi-foot/team-data', {
title: __('Swiss Football Team Data', 'swi_foot_matchdata'),
description: __('Display team standings or match data with guided selection', 'swi_foot_matchdata'),
icon: 'universal-access-alt',
category: 'widgets',
keywords: [
__('football', 'swi_foot_matchdata'),
__('soccer', 'swi_foot_matchdata'),
__('team', 'swi_foot_matchdata'),
__('swiss', 'swi_foot_matchdata'),
],
attributes: {
selectedTeam: {
type: 'string',
default: ''
},
dataType: {
type: 'string',
default: '' // 'match' or 'stats'
},
selectedMatch: {
type: 'string',
default: ''
},
shortcodeType: {
type: 'string',
default: 'match'
},
format: {
type: 'string',
default: ''
},
separator: {
type: 'string',
default: ':'
}
},
edit: function (props) {
const { attributes, setAttributes } = props;
const { selectedTeam, dataType, selectedMatch, shortcodeType, format, separator } = attributes;
// State for dynamic data
const [teams, setTeams] = useState([]);
const [matches, setMatches] = useState([]);
const [loadingTeams, setLoadingTeams] = useState(true);
const [loadingMatches, setLoadingMatches] = useState(false);
// Load teams on component mount
useEffect(() => {
makeAjaxCall('swi_foot_get_teams_for_editor')
.then(response => {
if (response.success && response.data) {
setTeams(response.data);
}
setLoadingTeams(false);
})
.catch(() => {
setLoadingTeams(false);
});
}, []);
// Load matches when team is selected and dataType is 'match'
useEffect(() => {
if (selectedTeam && dataType === 'match') {
setLoadingMatches(true);
makeAjaxCall('swi_foot_get_matches_for_team', { team_id: selectedTeam })
.then(response => {
if (response.success && response.data) {
setMatches(response.data);
}
setLoadingMatches(false);
})
.catch(() => {
setLoadingMatches(false);
});
} else {
setMatches([]);
}
}, [selectedTeam, dataType]);
// Build shortcode/content preview
let content = '';
if (selectedTeam && dataType === 'stats') {
content = '[swi_foot_standings team_id="' + selectedTeam + '"]';
} else if (selectedTeam && dataType === 'match' && selectedMatch) {
if (selectedMatch === 'current') {
content = '[swi_foot_' + shortcodeType + ' team_id="' + selectedTeam + '" show_current="true"';
} else {
content = '[swi_foot_' + shortcodeType + ' match_id="' + selectedMatch + '"';
}
if (format && (shortcodeType === 'match_date' || shortcodeType === 'match_time')) {
content += ' format="' + format + '"';
}
if (separator !== ':' && shortcodeType === 'match_score') {
content += ' separator="' + separator + '"';
}
content += ']';
}
// Data type options
const dataTypeOptions = [
{ value: '', label: __('Choose what to display...', 'swi_foot_matchdata') },
{ value: 'stats', label: __('Team Statistics (Standings/Ranking)', 'swi_foot_matchdata') },
{ value: 'match', label: __('Match Data', 'swi_foot_matchdata') }
];
// Match data type options
const matchDataOptions = [
{ value: 'match', label: __('Complete Match Information', 'swi_foot_matchdata') },
{ value: 'match_home_team', label: __('Home Team Name Only', 'swi_foot_matchdata') },
{ value: 'match_away_team', label: __('Away Team Name Only', 'swi_foot_matchdata') },
{ value: 'match_date', label: __('Match Date Only', 'swi_foot_matchdata') },
{ value: 'match_time', label: __('Match Time Only', 'swi_foot_matchdata') },
{ value: 'match_venue', label: __('Venue/Location Only', 'swi_foot_matchdata') },
{ value: 'match_score', label: __('Score Only', 'swi_foot_matchdata') },
{ value: 'match_status', label: __('Match Status Only', 'swi_foot_matchdata') },
{ value: 'match_league', label: __('League Name Only', 'swi_foot_matchdata') },
{ value: 'match_round', label: __('Round Number Only', 'swi_foot_matchdata') }
];
return el('div', { className: 'swi-foot-team-data-block' },
el(InspectorControls, {},
el(PanelBody, { title: __('Team Selection', 'swi_foot_matchdata') },
// Step 1: Team selection
loadingTeams ? el(Spinner) : el(SelectControl, {
label: __('1. Select Team', 'swi_foot_matchdata'),
value: selectedTeam,
options: [{ value: '', label: __('Choose a team...', 'swi_foot_matchdata') }].concat(
teams.map(team => ({
value: team.value,
label: team.label
}))
),
onChange: function (value) {
setAttributes({
selectedTeam: value,
dataType: '', // Reset when team changes
selectedMatch: ''
});
}
}),
// Step 2: Data type selection (only show if team is selected)
selectedTeam && el(SelectControl, {
label: __('2. What would you like to display?', 'swi_foot_matchdata'),
value: dataType,
options: dataTypeOptions,
onChange: function (value) {
setAttributes({
dataType: value,
selectedMatch: '' // Reset match when data type changes
});
}
})
),
// Match-specific settings
dataType === 'match' && el(PanelBody, { title: __('Match Settings', 'swi_foot_matchdata') },
// Match selection
loadingMatches ? el(Spinner) : el(SelectControl, {
label: __('3. Select Match', 'swi_foot_matchdata'),
value: selectedMatch,
options: [{ value: '', label: __('Choose a match...', 'swi_foot_matchdata') }].concat(
[{ value: 'current', label: __('Current Match (Next upcoming or recent)', 'swi_foot_matchdata') }],
matches.map(match => ({
value: match.value,
label: match.label
}))
),
onChange: function (value) { setAttributes({ selectedMatch: value }); }
}),
// Match data type (what part of match to show)
selectedMatch && el(SelectControl, {
label: __('4. What match information to display?', 'swi_foot_matchdata'),
value: shortcodeType,
options: matchDataOptions,
onChange: function (value) { setAttributes({ shortcodeType: value }); }
})
),
// Advanced options for match data
dataType === 'match' && selectedMatch && el(PanelBody, { title: __('Advanced Options', 'swi_foot_matchdata'), initialOpen: false },
(shortcodeType === 'match_date' || shortcodeType === 'match_time') && el(SelectControl, {
label: __('Date/Time Format', 'swi_foot_matchdata'),
value: format,
options: shortcodeType === 'match_date' ? [
{ value: '', label: __('Default (WordPress setting)', 'swi_foot_matchdata') },
{ value: 'd.m.Y', label: '31.12.2024 (European)' },
{ value: 'm/d/Y', label: '12/31/2024 (US)' },
{ value: 'F j, Y', label: 'December 31, 2024' },
{ value: 'D, M j', label: 'Tue, Dec 31' }
] : [
{ value: '', label: __('Default (WordPress setting)', 'swi_foot_matchdata') },
{ value: 'H:i', label: '14:30 (24-hour)' },
{ value: 'g:i A', label: '2:30 PM (12-hour)' },
{ value: 'g:i', label: '2:30 (12-hour no AM/PM)' }
],
onChange: function (value) { setAttributes({ format: value }); }
}),
shortcodeType === 'match_score' && el(SelectControl, {
label: __('Score Separator', 'swi_foot_matchdata'),
value: separator,
options: [
{ value: ':', label: '2 : 1 (colon with spaces)' },
{ value: '-', label: '2 - 1 (dash with spaces)' },
{ value: ' vs ', label: '2 vs 1 (versus)' },
{ value: '|', label: '2 | 1 (pipe)' }
],
onChange: function (value) { setAttributes({ separator: value }); }
})
)
),
// Main preview area
el('div', { className: 'swi-foot-preview' },
el('div', {
style: {
padding: '30px',
textAlign: 'center',
background: '#f8f9fa',
border: '2px dashed #ddd',
borderRadius: '8px'
}
},
el('h4', { style: { marginTop: 0 } }, __('Swiss Football Team Data', 'swi_foot_matchdata')),
!selectedTeam && el('p', { style: { color: '#666' } },
__('1. Please select a team to get started', 'swi_foot_matchdata')
),
selectedTeam && !dataType && el('p', { style: { color: '#666' } },
__('2. Please choose what you want to display', 'swi_foot_matchdata')
),
selectedTeam && dataType === 'stats' && el('p', { style: { color: '#28a745' } },
'✓ ' + __('Will display team standings/ranking', 'swi_foot_matchdata')
),
selectedTeam && dataType === 'match' && !selectedMatch && el('p', { style: { color: '#666' } },
__('3. Please select a match', 'swi_foot_matchdata')
),
selectedTeam && dataType === 'match' && selectedMatch && el('div', {},
el('p', { style: { color: '#28a745' } },
'✓ ' + __('Ready to display match data', 'swi_foot_matchdata')
),
el('p', { style: { fontSize: '14px', color: '#666' } },
__('Generated shortcode:', 'swi_foot_matchdata')
),
el('code', {
style: {
display: 'block',
padding: '10px',
background: '#fff',
border: '1px solid #ddd',
borderRadius: '4px',
fontFamily: 'Monaco, Menlo, monospace',
fontSize: '12px',
wordBreak: 'break-all'
}
}, content)
)
)
)
);
},
save: function (props) {
const { attributes } = props;
const { selectedTeam, dataType, selectedMatch, shortcodeType, format, separator } = attributes;
// Build shortcode for saving
let shortcode = '';
if (selectedTeam && dataType === 'stats') {
shortcode = '[swi_foot_standings team_id="' + selectedTeam + '"]';
} else if (selectedTeam && dataType === 'match' && selectedMatch) {
if (selectedMatch === 'current') {
shortcode = '[swi_foot_' + shortcodeType + ' team_id="' + selectedTeam + '" show_current="true"';
} else {
shortcode = '[swi_foot_' + shortcodeType + ' match_id="' + selectedMatch + '"';
}
if (format && (shortcodeType === 'match_date' || shortcodeType === 'match_time')) {
shortcode += ' format="' + format + '"';
}
if (separator !== ':' && shortcodeType === 'match_score') {
shortcode += ' separator="' + separator + '"';
}
shortcode += ']';
}
return shortcode;
}
});
// Match Roster Block with Side Selector and Bench Option
registerBlockType('swi-foot/match-roster', {
title: __('Swiss Football Match Roster', 'swi_foot_matchdata'),
description: __('Display match roster for home or away team, with optional bench players', 'swi_foot_matchdata'),
icon: 'groups',
category: 'widgets',
keywords: [
__('roster', 'swi_foot_matchdata'),
__('players', 'swi_foot_matchdata'),
__('lineup', 'swi_foot_matchdata'),
__('team', 'swi_foot_matchdata'),
],
attributes: {
selectedTeam: {
type: 'string',
default: ''
},
selectedMatch: {
type: 'string',
default: ''
},
side: {
type: 'string',
default: 'home' // home or away
},
withBench: {
type: 'boolean',
default: false
}
},
edit: function (props) {
const { attributes, setAttributes } = props;
const { selectedTeam, selectedMatch, side, withBench } = attributes;
const [teams, setTeams] = wp.element.useState([]);
const [matches, setMatches] = wp.element.useState([]);
const [loadingTeams, setLoadingTeams] = wp.element.useState(true);
const [loadingMatches, setLoadingMatches] = wp.element.useState(false);
// Helper AJAX call
function makeAjaxCall(action, data = {}) {
return new Promise((resolve, reject) => {
jQuery.post(swiFootEditorData.ajax_url, {
action: action,
nonce: swiFootEditorData.nonce,
...data
}, function (response) {
if (response && response.success) {
resolve(response);
} else {
reject(response);
}
}).fail(reject);
});
}
// Load teams
wp.element.useEffect(() => {
setLoadingTeams(true);
makeAjaxCall('swi_foot_get_teams_for_editor')
.then(response => {
if (Array.isArray(response.data)) {
setTeams(response.data);
}
setLoadingTeams(false);
})
.catch(() => setLoadingTeams(false));
}, []);
// Load matches when team changes
wp.element.useEffect(() => {
if (selectedTeam) {
setLoadingMatches(true);
makeAjaxCall('swi_foot_get_matches_for_team', { team_id: selectedTeam })
.then(response => {
if (Array.isArray(response.data)) {
setMatches(response.data);
}
setLoadingMatches(false);
})
.catch(() => setLoadingMatches(false));
}
}, [selectedTeam]);
return el('div', { className: 'swi-foot-roster-block' },
el(InspectorControls, {},
el(PanelBody, { title: __('Match Selection', 'swi_foot_matchdata') },
// Team selector
loadingTeams ? el(Spinner) : el(SelectControl, {
label: __('Select Team', 'swi_foot_matchdata'),
value: selectedTeam,
options: [{ value: '', label: __('Choose a team...', 'swi_foot_matchdata') }].concat(
teams.map(team => ({ value: team.value, label: team.label }))
),
onChange: function (value) {
setAttributes({ selectedTeam: value, selectedMatch: '' });
}
}),
// Match selector (shows after team is selected)
selectedTeam && (loadingMatches ? el(Spinner) : el(SelectControl, {
label: __('Select Match', 'swi_foot_matchdata'),
value: selectedMatch,
options: [{ value: '', label: __('Choose a match...', 'swi_foot_matchdata') }].concat(
[{ value: 'current', label: __('Current Match', 'swi_foot_matchdata') }],
matches.map(match => ({ value: match.value, label: match.label }))
),
onChange: function (value) { setAttributes({ selectedMatch: value }); }
})),
// Side selector (Home/Away)
selectedMatch && el(SelectControl, {
label: __('Select Side', 'swi_foot_matchdata'),
value: side,
options: [
{ value: 'home', label: __('Home Team', 'swi_foot_matchdata') },
{ value: 'away', label: __('Away Team', 'swi_foot_matchdata') }
],
onChange: function (value) { setAttributes({ side: value }); }
}),
// Include bench toggle
selectedMatch && el(ToggleControl, {
label: __('Include Bench Players', 'swi_foot_matchdata'),
help: __('When enabled, bench players will also be displayed', 'swi_foot_matchdata'),
checked: withBench,
onChange: function (value) { setAttributes({ withBench: value }); }
})
)
),
// Block preview
el('div', {
style: {
padding: '30px',
textAlign: 'center',
background: '#f8f9fa',
border: '2px dashed #ddd',
borderRadius: '8px'
}
},
el('h4', { style: { marginTop: 0 } }, __('Match Roster', 'swi_foot_matchdata')),
(!selectedTeam || !selectedMatch) ?
el('p', { style: { color: '#666' } },
__('Please select team, match, side, and bench option', 'swi_foot_matchdata')
)
:
el('p', { style: { color: '#28a745' } },
'✓ ' + __('Roster will be displayed for ' + side + ' team' + (withBench ? ' (with bench)' : ''), 'swi_foot_matchdata')
)
)
);
},
save: function (props) {
const { selectedTeam, selectedMatch, side, withBench } = props.attributes;
if (!selectedTeam || !selectedMatch || !side) return '';
let shortcode = '[swi_foot_roster ';
if (selectedMatch === 'current') {
shortcode += 'team_id="' + selectedTeam + '" show_current="true"';
} else {
shortcode += 'match_id="' + selectedMatch + '"';
}
shortcode += ' side="' + side + '"';
if (withBench) {
shortcode += ' with_bench="true"';
}
shortcode += ']';
return shortcode;
}
});
// Match Events Block
registerBlockType('swi-foot/match-events', {
title: __('Swiss Football Match Events', 'swi_foot_matchdata'),
description: __('Display live match events with auto-refresh', 'swi_foot_matchdata'),
icon: 'list-view',
category: 'widgets',
keywords: [
__('events', 'swi_foot_matchdata'),
__('live', 'swi_foot_matchdata'),
__('timeline', 'swi_foot_matchdata'),
__('match', 'swi_foot_matchdata'),
],
attributes: {
selectedTeam: {
type: 'string',
default: ''
},
selectedMatch: {
type: 'string',
default: ''
},
refreshInterval: {
type: 'number',
default: 30
}
},
edit: function (props) {
const { attributes, setAttributes } = props;
const { selectedTeam, selectedMatch, refreshInterval } = attributes;
const [teams, setTeams] = useState([]);
const [matches, setMatches] = useState([]);
const [loadingTeams, setLoadingTeams] = useState(true);
const [loadingMatches, setLoadingMatches] = useState(false);
// Load teams
useEffect(() => {
makeAjaxCall('swi_foot_get_teams_for_editor')
.then(response => {
if (response.success && response.data) {
setTeams(response.data);
}
setLoadingTeams(false);
})
.catch(() => setLoadingTeams(false));
}, []);
// Load matches when team changes
useEffect(() => {
if (selectedTeam) {
setLoadingMatches(true);
makeAjaxCall('swi_foot_get_matches_for_team', { team_id: selectedTeam })
.then(response => {
if (response.success && response.data) {
setMatches(response.data);
}
setLoadingMatches(false);
})
.catch(() => setLoadingMatches(false));
}
}, [selectedTeam]);
return el('div', { className: 'swi-foot-events-block' },
el(InspectorControls, {},
el(PanelBody, { title: __('Match Selection', 'swi_foot_matchdata') },
loadingTeams ? el(Spinner) : el(SelectControl, {
label: __('Select Team', 'swi_foot_matchdata'),
value: selectedTeam,
options: [{ value: '', label: __('Choose a team...', 'swi_foot_matchdata') }].concat(
teams.map(team => ({ value: team.value, label: team.label }))
),
onChange: function (value) {
setAttributes({ selectedTeam: value, selectedMatch: '' });
}
}),
selectedTeam && (loadingMatches ? el(Spinner) : el(SelectControl, {
label: __('Select Match', 'swi_foot_matchdata'),
value: selectedMatch,
options: [{ value: '', label: __('Choose a match...', 'swi_foot_matchdata') }].concat(
[{ value: 'current', label: __('Current Match', 'swi_foot_matchdata') }],
matches.map(match => ({ value: match.value, label: match.label }))
),
onChange: function (value) { setAttributes({ selectedMatch: value }); }
}))
),
el(PanelBody, { title: __('Auto-Refresh Settings', 'swi_foot_matchdata') },
el(RangeControl, {
label: __('Refresh Interval (seconds)', 'swi_foot_matchdata'),
value: refreshInterval,
onChange: function (value) { setAttributes({ refreshInterval: value }); },
min: 10,
max: 300,
step: 10
})
)
),
el('div', {
style: {
padding: '30px',
textAlign: 'center',
background: '#f8f9fa',
border: '2px dashed #ddd',
borderRadius: '8px'
}
},
el('h4', { style: { marginTop: 0 } }, __('Live Match Events', 'swi_foot_matchdata')),
(!selectedTeam || !selectedMatch) ?
el('p', { style: { color: '#666' } }, __('Please select team and match', 'swi_foot_matchdata')) :
el('div', {},
el('p', { style: { color: '#28a745' } }, '✓ ' + __('Will display live match events', 'swi_foot_matchdata')),
el('p', { style: { fontSize: '12px', color: '#666' } },
__('Auto-refresh every', 'swi_foot_matchdata') + ' ' + refreshInterval + ' ' + __('seconds', 'swi_foot_matchdata'))
)
)
);
},
save: function (props) {
const { attributes } = props;
const { selectedTeam, selectedMatch, refreshInterval } = attributes;
if (!selectedTeam || !selectedMatch) return '';
let shortcode = '[swi_foot_events ';
if (selectedMatch === 'current') {
shortcode += 'team_id="' + selectedTeam + '" show_current="true"';
} else {
shortcode += 'match_id="' + selectedMatch + '"';
}
shortcode += ' refresh_interval="' + refreshInterval + '"]';
return shortcode;
}
});
})(
window.wp.blocks,
window.wp.element,
window.wp.blockEditor,
window.wp.components,
window.wp.i18n,
window.wp.data
);