325 lines
15 KiB
JavaScript
325 lines
15 KiB
JavaScript
// Editor entry (ESNext) for Swiss Football blocks.
|
|
// This registers blocks and the shortcode insertion toolbar button.
|
|
|
|
import { registerBlockType } from '@wordpress/blocks';
|
|
import { __ } from '@wordpress/i18n';
|
|
import { InspectorControls, useBlockProps, RawHTML } from '@wordpress/block-editor';
|
|
import { PanelBody, SelectControl, ToggleControl, RangeControl, Spinner } from '@wordpress/components';
|
|
import { useState, useEffect, createElement } from '@wordpress/element';
|
|
import { registerFormatType, applyFormat } from '@wordpress/rich-text';
|
|
import './format-shortcode';
|
|
|
|
function makeAjaxCall(action, data = {}) {
|
|
const root = swiFootEditorData.rest_url.replace(/\/$/, '');
|
|
let url = root;
|
|
const opts = { credentials: 'same-origin', headers: { 'X-WP-Nonce': swiFootEditorData.rest_nonce } };
|
|
|
|
if (action === 'swi_foot_get_teams_for_editor') {
|
|
url += '/teams'; opts.method = 'GET';
|
|
} else if (action === 'swi_foot_get_matches_for_team') {
|
|
url += '/matches?team_id=' + encodeURIComponent(data.team_id || ''); opts.method = 'GET';
|
|
} else if (action === 'swi_foot_get_commons_ids') {
|
|
url += '/commons-ids'; opts.method = 'GET';
|
|
} else {
|
|
return Promise.reject({ error: 'Unknown action' });
|
|
}
|
|
|
|
return fetch(url, opts)
|
|
.then(r => { if (!r.ok) return r.json().then(j => Promise.reject(j)); return r.json(); })
|
|
.then(data => {
|
|
// Transform API response into expected format for editor blocks
|
|
if (action === 'swi_foot_get_teams_for_editor') {
|
|
return {
|
|
success: true,
|
|
data: Array.isArray(data) ? data.map(team => ({
|
|
value: team.teamId,
|
|
label: team.teamName
|
|
})) : []
|
|
};
|
|
} else if (action === 'swi_foot_get_matches_for_team') {
|
|
return {
|
|
success: true,
|
|
data: Array.isArray(data) ? data.map(match => ({
|
|
value: match.matchId,
|
|
label: `${match.teamNameA} vs ${match.teamNameB} (${match.matchDate})`
|
|
})) : []
|
|
};
|
|
}
|
|
return { success: true, data: data };
|
|
});
|
|
}
|
|
|
|
function checkBlockStatus(matchId, endpoint) {
|
|
if (!matchId) return Promise.resolve(null);
|
|
|
|
const root = swiFootEditorData.rest_url.replace(/\/$/, '');
|
|
const url = `${root}/block-status/${matchId}/${endpoint}`;
|
|
const opts = { credentials: 'same-origin', headers: { 'X-WP-Nonce': swiFootEditorData.rest_nonce } };
|
|
|
|
return fetch(url, opts)
|
|
.then(r => r.json())
|
|
.catch(() => null);
|
|
}
|
|
|
|
// Standings block (editor UI only, server renders)
|
|
// Gets team from container context, no user selection needed
|
|
registerBlockType('swi-foot/standings', {
|
|
apiVersion: 3,
|
|
title: __('Swiss Football Standings', 'swi_foot_matchdata'),
|
|
category: 'swi-football',
|
|
supports: { align: true, anchor: true },
|
|
edit: ({ attributes, setAttributes, isSelected }) => {
|
|
const blockProps = useBlockProps({ style: isSelected ? {outline: '2px solid #0073aa'} : {} });
|
|
return (
|
|
<div {...blockProps}>
|
|
<InspectorControls>
|
|
<PanelBody title={__('Settings', 'swi_foot_matchdata')}>
|
|
<p style={{color: '#666', fontSize: '13px'}}>{__('Team is inherited from the container context.', 'swi_foot_matchdata')}</p>
|
|
</PanelBody>
|
|
</InspectorControls>
|
|
<div className="swi-foot-editor-placeholder" style={{padding: '24px', cursor: 'pointer', background: '#f5f5f5', border: '2px solid #ddd', borderRadius: '4px'}}>
|
|
<p><strong>{__('Standings', 'swi_foot_matchdata')}</strong></p>
|
|
<p style={{fontSize: '13px', color: '#666', marginBottom: '0'}}>{__('League standings inherited from team context. ✓ Data will render on the front-end.', 'swi_foot_matchdata')}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
save: () => null
|
|
});
|
|
|
|
// Schedule block (editor UI only, server renders)
|
|
// Gets team from container context, only allows limiting results
|
|
registerBlockType('swi-foot/schedule', {
|
|
apiVersion: 3,
|
|
title: __('Swiss Football Schedule', 'swi_foot_matchdata'),
|
|
category: 'swi-football',
|
|
supports: { align: true, anchor: true },
|
|
attributes: {
|
|
limit: { type: 'number', default: 10 },
|
|
matchFilter: { type: 'string', default: 'all' }
|
|
},
|
|
edit: ({ attributes, setAttributes, isSelected }) => {
|
|
const blockProps = useBlockProps({ style: isSelected ? {outline: '2px solid #0073aa'} : {} });
|
|
const { limit, matchFilter } = attributes;
|
|
|
|
return (
|
|
<div {...blockProps}>
|
|
<InspectorControls>
|
|
<PanelBody title={__('Schedule settings', 'swi_foot_matchdata')}>
|
|
<p style={{color: '#666', fontSize: '13px', marginBottom: '12px'}}>{__('Team is inherited from the container context.', 'swi_foot_matchdata')}</p>
|
|
<RangeControl label={__('Limit', 'swi_foot_matchdata')} value={limit || 10} onChange={val => setAttributes({ limit: val })} min={1} max={20} />
|
|
<SelectControl
|
|
label={__('Match Filter', 'swi_foot_matchdata')}
|
|
value={matchFilter || 'all'}
|
|
options={[
|
|
{ label: __('All Matches', 'swi_foot_matchdata'), value: 'all' },
|
|
{ label: __('Home Matches Only', 'swi_foot_matchdata'), value: 'home' },
|
|
{ label: __('Away Matches Only', 'swi_foot_matchdata'), value: 'away' }
|
|
]}
|
|
onChange={val => setAttributes({ matchFilter: val })}
|
|
/>
|
|
</PanelBody>
|
|
</InspectorControls>
|
|
<div className="swi-foot-editor-placeholder" style={{padding: '24px', cursor: 'pointer', background: '#f5f5f5', border: '2px solid #ddd', borderRadius: '4px'}}>
|
|
<p><strong>{__('Schedule', 'swi_foot_matchdata')}</strong></p>
|
|
<p style={{fontSize: '13px', color: '#666', marginBottom: '0'}}>{__('Team and match schedule inherited from context. ✓ Data will render on the front-end.', 'swi_foot_matchdata')}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
save: ({ attributes }) => null
|
|
});
|
|
|
|
// Match Roster
|
|
// Gets match and team from container context, only allows selecting which side (home/away) and bench option
|
|
registerBlockType('swi-foot/match-roster', {
|
|
apiVersion: 3,
|
|
title: __('Swiss Football Match Roster', 'swi_foot_matchdata'),
|
|
category: 'swi-football',
|
|
supports: { align: true, anchor: true },
|
|
attributes: {
|
|
side: { type: 'string', default: 'home' },
|
|
showStartingSquad: { type: 'boolean', default: true },
|
|
showBench: { type: 'boolean', default: false }
|
|
},
|
|
edit: ({ attributes, setAttributes, isSelected }) => {
|
|
const blockProps = useBlockProps({ style: isSelected ? {outline: '2px solid #0073aa'} : {} });
|
|
const { side, showStartingSquad, showBench } = attributes;
|
|
|
|
return (
|
|
<div {...blockProps}>
|
|
<InspectorControls>
|
|
<PanelBody title={__('Display Options', 'swi_foot_matchdata')}>
|
|
<p style={{color: '#666', fontSize: '13px', marginBottom: '12px'}}>{__('Match and team are inherited from the container context.', 'swi_foot_matchdata')}</p>
|
|
<SelectControl
|
|
label={__('Team Side', 'swi_foot_matchdata')}
|
|
value={side}
|
|
options={[{value:'home',label:__('Home Team', 'swi_foot_matchdata')},{value:'away',label:__('Away Team', 'swi_foot_matchdata')}]}
|
|
onChange={v=>setAttributes({side:v})}
|
|
__next40pxDefaultSize={true}
|
|
__nextHasNoMarginBottom={true}
|
|
/>
|
|
<ToggleControl
|
|
label={__('Show Starting Squad', 'swi_foot_matchdata')}
|
|
checked={showStartingSquad}
|
|
onChange={v=>setAttributes({showStartingSquad:v})}
|
|
__nextHasNoMarginBottom={true}
|
|
/>
|
|
<ToggleControl
|
|
label={__('Show Bench', 'swi_foot_matchdata')}
|
|
checked={showBench}
|
|
onChange={v=>setAttributes({showBench:v})}
|
|
__nextHasNoMarginBottom={true}
|
|
/>
|
|
</PanelBody>
|
|
</InspectorControls>
|
|
<div className="swi-foot-editor-placeholder" style={{padding: '24px', cursor: 'pointer', background: '#f5f5f5', border: '2px solid #ddd', borderRadius: '4px'}}>
|
|
<p><strong>{__('Match Roster', 'swi_foot_matchdata')}</strong></p>
|
|
<p style={{fontSize: '13px', color: '#666', marginBottom: '0'}}>{__('Team and match are inherited from the container context. ✓ Data will render on the front-end.', 'swi_foot_matchdata')}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
save: ({ attributes }) => {
|
|
const { side, showStartingSquad, showBench } = attributes;
|
|
let shortcode = '[swi_foot_roster side="' + side + '"';
|
|
if (showStartingSquad) shortcode += ' starting_squad="true"';
|
|
if (showBench) shortcode += ' bench="true"';
|
|
shortcode += ']';
|
|
return createElement(RawHTML, null, shortcode);
|
|
}
|
|
});
|
|
|
|
// Match Events
|
|
// Gets match and team from container context, only allows setting refresh interval
|
|
registerBlockType('swi-foot/match-events', {
|
|
apiVersion: 3,
|
|
title: __('Swiss Football Match Events', 'swi_foot_matchdata'),
|
|
category: 'swi-football',
|
|
supports: { align: true, anchor: true },
|
|
attributes: {
|
|
refreshInterval: { type: 'number', default: 30 },
|
|
eventOrder: { type: 'string', default: 'dynamic' }
|
|
},
|
|
edit: ({ attributes, setAttributes, isSelected }) => {
|
|
const blockProps = useBlockProps({ style: isSelected ? {outline: '2px solid #0073aa'} : {} });
|
|
const { refreshInterval, eventOrder } = attributes;
|
|
|
|
return (
|
|
<div {...blockProps}>
|
|
<InspectorControls>
|
|
<PanelBody title={__('Display Options', 'swi_foot_matchdata')}>
|
|
<p style={{color: '#666', fontSize: '13px', marginBottom: '12px'}}>{__('Match and team are inherited from the container context.', 'swi_foot_matchdata')}</p>
|
|
<RangeControl
|
|
label={__('Refresh Interval (seconds)', 'swi_foot_matchdata')}
|
|
value={refreshInterval || 30}
|
|
onChange={v=>setAttributes({refreshInterval:v})}
|
|
min={10}
|
|
max={300}
|
|
step={10}
|
|
/>
|
|
<SelectControl
|
|
label={__('Event Order', 'swi_foot_matchdata')}
|
|
value={eventOrder || 'dynamic'}
|
|
onChange={v=>setAttributes({eventOrder:v})}
|
|
options={[
|
|
{ label: __('Dynamic (Newest first while live, chronological after)', 'swi_foot_matchdata'), value: 'dynamic' },
|
|
{ label: __('Newest First', 'swi_foot_matchdata'), value: 'newest_first' },
|
|
{ label: __('Oldest First', 'swi_foot_matchdata'), value: 'oldest_first' }
|
|
]}
|
|
help={__('Dynamic: newest events at top while match is ongoing, chronological (oldest first) after match ends', 'swi_foot_matchdata')}
|
|
__next40pxDefaultSize={true}
|
|
__nextHasNoMarginBottom={true}
|
|
/>
|
|
</PanelBody>
|
|
</InspectorControls>
|
|
<div className="swi-foot-editor-placeholder" style={{padding: '24px', cursor: 'pointer', background: '#f5f5f5', border: '2px solid #ddd', borderRadius: '4px'}}>
|
|
<p><strong>{__('Match Events', 'swi_foot_matchdata')}</strong></p>
|
|
<p style={{fontSize: '13px', color: '#666', marginBottom: '0'}}>{__('Live match events inherited from context. ✓ Will update during match.', 'swi_foot_matchdata')}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
save: ({ attributes }) => {
|
|
const { refreshInterval, eventOrder } = attributes;
|
|
let shortcode = '[swi_foot_events refresh_interval="' + (refreshInterval || 30) + '"';
|
|
if (eventOrder && eventOrder !== 'dynamic') {
|
|
shortcode += ' event_order="' + eventOrder + '"';
|
|
}
|
|
shortcode += ']';
|
|
return createElement(RawHTML, null, shortcode);
|
|
}
|
|
});
|
|
|
|
// Match Bench
|
|
// Gets match from container context, displays team staff and bench players
|
|
registerBlockType('swi-foot/match-bench', {
|
|
apiVersion: 3,
|
|
title: __('Swiss Football Match Bench', 'swi_foot_matchdata'),
|
|
icon: 'clipboard-user',
|
|
category: 'swi-football',
|
|
supports: { align: true, anchor: true },
|
|
attributes: {
|
|
side: { type: 'string', default: 'home' }
|
|
},
|
|
edit: ({ attributes, setAttributes, isSelected }) => {
|
|
const blockProps = useBlockProps({ style: isSelected ? {outline: '2px solid #0073aa'} : {} });
|
|
const { side } = attributes;
|
|
|
|
return (
|
|
<div {...blockProps}>
|
|
<InspectorControls>
|
|
<PanelBody title={__('Display Options', 'swi_foot_matchdata')}>
|
|
<p style={{color: '#666', fontSize: '13px', marginBottom: '12px'}}>{__('Match is inherited from the container context.', 'swi_foot_matchdata')}</p>
|
|
<SelectControl
|
|
label={__('Team Side', 'swi_foot_matchdata')}
|
|
value={side}
|
|
options={[{value:'home',label:__('Home Team', 'swi_foot_matchdata')},{value:'away',label:__('Away Team', 'swi_foot_matchdata')}]}
|
|
onChange={v=>setAttributes({side:v})}
|
|
__next40pxDefaultSize={true}
|
|
__nextHasNoMarginBottom={true}
|
|
/>
|
|
</PanelBody>
|
|
</InspectorControls>
|
|
<div className="swi-foot-editor-placeholder" style={{padding: '24px', cursor: 'pointer', background: '#f5f5f5', border: '2px solid #ddd', borderRadius: '4px'}}>
|
|
<p><strong>{__('Match Bench', 'swi_foot_matchdata')}</strong></p>
|
|
<p style={{fontSize: '13px', color: '#666', marginBottom: '0'}}>{__('Team bench and substitutes inherited from context. ✓ Data will render on the front-end.', 'swi_foot_matchdata')}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
save: ({ attributes }) => {
|
|
const { side } = attributes;
|
|
let shortcode = '[swi_foot_bench side="' + side + '"]';
|
|
return createElement(RawHTML, null, shortcode);
|
|
}
|
|
});
|
|
|
|
// Match Referees
|
|
// Gets match from container context, displays match officials and referees
|
|
registerBlockType('swi-foot/match-referees', {
|
|
apiVersion: 3,
|
|
title: __('Swiss Football Match Referees', 'swi_foot_matchdata'),
|
|
icon: 'whistle',
|
|
category: 'swi-football',
|
|
supports: { align: true, anchor: true },
|
|
edit: ({ attributes, isSelected }) => {
|
|
const blockProps = useBlockProps({ style: isSelected ? {outline: '2px solid #0073aa'} : {} });
|
|
return (
|
|
<div {...blockProps}>
|
|
<InspectorControls>
|
|
<PanelBody title={__('Settings', 'swi_foot_matchdata')}>
|
|
<p style={{color: '#666', fontSize: '13px'}}>{__('Match is inherited from the container context.', 'swi_foot_matchdata')}</p>
|
|
</PanelBody>
|
|
</InspectorControls>
|
|
<div className="swi-foot-editor-placeholder" style={{padding: '24px', cursor: 'pointer', background: '#f5f5f5', border: '2px solid #ddd', borderRadius: '4px'}}>
|
|
<p><strong>{__('Match Referees', 'swi_foot_matchdata')}</strong></p>
|
|
<p style={{fontSize: '13px', color: '#666', marginBottom: '0'}}>{__('Match officials and referees inherited from context. ✓ Data will render on the front-end.', 'swi_foot_matchdata')}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
save: () => null
|
|
});
|
|
|