PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.2.0
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.2.0
2.4.1 2.4.0 2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.2 2.2.1 2.2.0 2.1.2 2.1.1 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 All 68 releases
better-payment / src / campaign-builder / components / SettingsPanel.js

SettingsPanel.js in Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More 2.2.0, at src/campaign-builder/components/SettingsPanel.js

262 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __ } from '@wordpress/i18n';
2 import { getElement } from '../fields/FieldRegistry';
3
4 /**
5 * Right panel — campaign-level settings when nothing is selected,
6 * or schema-driven element settings when an element is selected.
7 *
8 * Element settings controls are generated from the PHP settingsSchema
9 * (localized via betterPaymentCampaignData.elements), so adding a new
10 * control to PHP automatically adds it here without touching this file.
11 */
12 export default function SettingsPanel( { meta, layout, selectedElementId, selectedColumnId, dispatch } ) {
13
14 let selectedElement = null;
15 if ( selectedElementId && layout?.columns ) {
16 for ( const col of layout.columns ) {
17 const found = col.elements.find( ( el ) => el.id === selectedElementId );
18 if ( found ) { selectedElement = found; break; }
19 }
20 }
21
22 if ( selectedElement ) {
23 const def = getElement( selectedElement.type );
24 return (
25 <div className="bp-cb-settings-panel">
26 <div className="bp-cb-settings-panel__header">
27 <span className={ `dashicons dashicons-${ def?.icon || 'block-default' }` } />
28 <h3 className="bp-cb-panel-heading">
29 { def?.label || __( 'Element Settings', 'better-payment' ) }
30 </h3>
31 </div>
32 <ElementSettings
33 element={ selectedElement }
34 columnId={ selectedColumnId }
35 schema={ def?.settingsSchema || [] }
36 meta={ meta }
37 dispatch={ dispatch }
38 />
39 </div>
40 );
41 }
42
43 return (
44 <div className="bp-cb-settings-panel">
45 <h3 className="bp-cb-panel-heading">{ __( 'Campaign Settings', 'better-payment' ) }</h3>
46 <CampaignMetaSettings meta={ meta } dispatch={ dispatch } />
47 </div>
48 );
49 }
50
51 // ── Schema-driven element settings ────────────────────────────────────────────
52
53 function ElementSettings( { element, columnId, schema, meta, dispatch } ) {
54 const updateElement = ( patch ) =>
55 dispatch( { type: 'UPDATE_ELEMENT_SETTINGS', elementId: element.id, settings: patch } );
56 const updateMeta = ( patch ) =>
57 dispatch( { type: 'UPDATE_META', payload: patch } );
58 const s = element.settings || {};
59
60 if ( schema.length === 0 ) {
61 return <p className="bp-cb-hint">{ __( 'No settings for this element.', 'better-payment' ) }</p>;
62 }
63
64 return (
65 <div className="bp-cb-settings-form">
66 { schema.map( ( control ) => {
67 const isMeta = !! control.metaKey;
68 const value = isMeta ? ( meta?.[ control.metaKey ] ?? '' ) : s[ control.key ];
69 const onChange = isMeta
70 ? ( val ) => updateMeta( { [ control.metaKey ]: val } )
71 : ( val ) => updateElement( { [ control.key ]: val } );
72 return (
73 <SettingsControl
74 key={ control.key }
75 control={ control }
76 value={ value }
77 onChange={ onChange }
78 />
79 );
80 } ) }
81 </div>
82 );
83 }
84
85 const ALIGN_OPTIONS = [
86 {
87 value: 'left',
88 title: 'Align left',
89 icon: (
90 <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
91 <rect x="2" y="4" width="16" height="2" rx="1" fill="currentColor"/>
92 <rect x="2" y="9" width="11" height="2" rx="1" fill="currentColor"/>
93 <rect x="2" y="14" width="14" height="2" rx="1" fill="currentColor"/>
94 </svg>
95 ),
96 },
97 {
98 value: 'center',
99 title: 'Align center',
100 icon: (
101 <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
102 <rect x="2" y="4" width="16" height="2" rx="1" fill="currentColor"/>
103 <rect x="4.5" y="9" width="11" height="2" rx="1" fill="currentColor"/>
104 <rect x="3" y="14" width="14" height="2" rx="1" fill="currentColor"/>
105 </svg>
106 ),
107 },
108 {
109 value: 'right',
110 title: 'Align right',
111 icon: (
112 <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
113 <rect x="2" y="4" width="16" height="2" rx="1" fill="currentColor"/>
114 <rect x="7" y="9" width="11" height="2" rx="1" fill="currentColor"/>
115 <rect x="4" y="14" width="14" height="2" rx="1" fill="currentColor"/>
116 </svg>
117 ),
118 },
119 ];
120
121 function SettingsControl( { control, value, onChange } ) {
122 const { key, label, type, placeholder, min, max, rows, options } = control;
123 const resolvedPlaceholder = placeholder || '';
124
125 return (
126 <div className="bp-cb-control">
127 { type !== 'toggle' && (
128 <label className="bp-cb-field-label" htmlFor={ `ctrl-${ key }` }>
129 { label }
130 </label>
131 ) }
132
133 { type === 'align' && (
134 <div className="bp-cb-align-group">
135 { ALIGN_OPTIONS.map( ( opt ) => (
136 <button
137 key={ opt.value }
138 type="button"
139 title={ opt.title }
140 className={ `bp-cb-align-btn${ ( value || 'left' ) === opt.value ? ' is-active' : '' }` }
141 onClick={ () => onChange( opt.value ) }
142 >
143 { opt.icon }
144 </button>
145 ) ) }
146 </div>
147 ) }
148
149 { type === 'text' && (
150 <input
151 id={ `ctrl-${ key }` }
152 type="text"
153 value={ value ?? '' }
154 placeholder={ resolvedPlaceholder }
155 onChange={ ( e ) => onChange( e.target.value ) }
156 />
157 ) }
158
159 { type === 'number' && (
160 <input
161 id={ `ctrl-${ key }` }
162 type="number"
163 value={ value ?? '' }
164 min={ min }
165 max={ max }
166 onChange={ ( e ) => onChange( parseInt( e.target.value, 10 ) ) }
167 />
168 ) }
169
170 { type === 'color' && (
171 <input
172 id={ `ctrl-${ key }` }
173 type="color"
174 value={ value || '#6b63f6' }
175 onChange={ ( e ) => onChange( e.target.value ) }
176 />
177 ) }
178
179 { type === 'toggle' && (
180 <label className="bp-cb-toggle-label" htmlFor={ `ctrl-${ key }` }>
181 <input
182 id={ `ctrl-${ key }` }
183 type="checkbox"
184 checked={ !! value }
185 onChange={ ( e ) => onChange( e.target.checked ) }
186 />
187 <span>{ label }</span>
188 </label>
189 ) }
190
191 { type === 'select' && (
192 <select
193 id={ `ctrl-${ key }` }
194 value={ value ?? '' }
195 onChange={ ( e ) => onChange( e.target.value ) }
196 >
197 { ( options || [] ).map( ( opt ) => (
198 <option key={ opt.value } value={ opt.value }>{ opt.label }</option>
199 ) ) }
200 </select>
201 ) }
202
203 { type === 'textarea' && (
204 <textarea
205 id={ `ctrl-${ key }` }
206 rows={ rows || 4 }
207 value={ value ?? '' }
208 placeholder={ placeholder || '' }
209 onChange={ ( e ) => onChange( e.target.value ) }
210 />
211 ) }
212 </div>
213 );
214 }
215
216 // ── Campaign meta settings (campaign-level, shown when nothing selected) ──────
217
218 function CampaignMetaSettings( { meta, dispatch } ) {
219 const update = ( patch ) => dispatch( { type: 'UPDATE_META', payload: patch } );
220
221 return (
222 <div className="bp-cb-settings-form">
223
224 <label className="bp-cb-field-label">{ __( 'Goal Amount', 'better-payment' ) }</label>
225 <input
226 type="number" min="0" step="1"
227 value={ meta.bpc_goal_amount }
228 onChange={ ( e ) => update( { bpc_goal_amount: e.target.value } ) }
229 />
230
231 <label className="bp-cb-field-label">{ __( 'End Date', 'better-payment' ) }</label>
232 <input
233 type="date"
234 value={ meta.bpc_end_date }
235 onChange={ ( e ) => update( { bpc_end_date: e.target.value } ) }
236 onClick={ ( e ) => e.target.showPicker?.() }
237 />
238
239 <label className="bp-cb-field-label">{ __( 'Donation Page', 'better-payment' ) }</label>
240 <input
241 type="number" min="0"
242 placeholder={ __( 'Page ID', 'better-payment' ) }
243 value={ meta.bpc_form_page_id || '' }
244 onChange={ ( e ) => update( { bpc_form_page_id: parseInt( e.target.value, 10 ) || 0 } ) }
245 />
246
247 <label className="bp-cb-field-label">{ __( 'Primary Color', 'better-payment' ) }</label>
248 <input
249 type="color"
250 value={ meta.bpc_color_primary || '#6b63f6' }
251 onChange={ ( e ) => update( { bpc_color_primary: e.target.value } ) }
252 />
253
254 <label className="bp-cb-field-label">{ __( 'Status', 'better-payment' ) }</label>
255 <select value={ meta.bpc_status } onChange={ ( e ) => update( { bpc_status: e.target.value } ) }>
256 <option value="active">{ __( 'Active', 'better-payment' ) }</option>
257 <option value="ended">{ __( 'Ended', 'better-payment' ) }</option>
258 </select>
259 </div>
260 );
261 }
262