@@ -14,20 +14,30 @@ import { useTaskCompletion } from '../../hooks/useTaskCompletion';
1414/**
1515 * OnboardTask component.
1616 *
17- * @param {Object } props - Component props.
18- * @param {Object } props.task - Task data.
19- * @param {Object } props.config - Wizard configuration.
20- * @param {Function } props.onComplete - Callback when task is completed.
17+ * @param {Object } props - Component props.
18+ * @param {Object } props.task - Task data.
19+ * @param {Object } props.config - Wizard configuration.
20+ * @param {Function } props.onComplete - Callback when task is completed.
21+ * @param {Function } props.onOpenChange - Callback when task open state changes.
22+ * @param {boolean } props.forceOpen - If true, render in open state.
23+ * @param {boolean } props.disableActionButton - If true, disable the template's action button by default.
2124 * @return {JSX.Element } OnboardTask component.
2225 */
23- export default function OnboardTask ( { task, config, onComplete } ) {
26+ export default function OnboardTask ( {
27+ task,
28+ config,
29+ onComplete,
30+ onOpenChange,
31+ forceOpen = false ,
32+ disableActionButton = false ,
33+ } ) {
2434 const { ajaxUrl, nonce } = config ;
25- const { completeTask, isCompleting } = useTaskCompletion ( {
35+ const { completeTask } = useTaskCompletion ( {
2636 ajaxUrl,
2737 nonce,
2838 } ) ;
2939
30- const [ isOpen , setIsOpen ] = useState ( false ) ;
40+ const [ isOpen , setIsOpen ] = useState ( forceOpen ) ;
3141 const [ isCompleted , setIsCompleted ] = useState ( false ) ;
3242 const [ formValues , setFormValues ] = useState ( { } ) ;
3343 const taskContentRef = useRef ( null ) ;
@@ -86,6 +96,9 @@ export default function OnboardTask( { task, config, onComplete } ) {
8696 try {
8797 await completeTask ( task . task_id , formValues ) ;
8898 setIsCompleted ( true ) ;
99+ // Close the task view and return to task list.
100+ setIsOpen ( false ) ;
101+ onOpenChange ?. ( false ) ;
89102 onComplete ?. ( task . task_id ) ;
90103 } catch ( error ) {
91104 console . error ( 'Failed to complete task:' , error ) ;
@@ -97,42 +110,20 @@ export default function OnboardTask( { task, config, onComplete } ) {
97110 */
98111 const handleOpen = ( ) => {
99112 setIsOpen ( true ) ;
113+ onOpenChange ?. ( true ) ;
100114 } ;
101115
102116 /**
103117 * Handle close task.
104118 */
105119 const handleClose = ( ) => {
106120 setIsOpen ( false ) ;
121+ onOpenChange ?. ( false ) ;
107122 } ;
108123
109124 if ( isOpen ) {
110125 return (
111126 < div className = "prpl-task-content-active" ref = { taskContentRef } >
112- < div className = "prpl-task-buttons" >
113- < button
114- type = "button"
115- className = "prpl-btn prpl-task-close-btn"
116- onClick = { handleClose }
117- >
118- < span className = "dashicons dashicons-arrow-left-alt2" > </ span >
119- { config ?. l10n ?. backToRecommendations ||
120- __ (
121- 'Back to recommendations' ,
122- 'progress-planner'
123- ) }
124- </ button >
125- < button
126- type = "button"
127- className = "prpl-complete-task-btn"
128- onClick = { handleComplete }
129- disabled = { isCompleting }
130- >
131- { isCompleting
132- ? __ ( 'Completing…' , 'progress-planner' )
133- : __ ( 'Complete' , 'progress-planner' ) }
134- </ button >
135- </ div >
136127 < div className = "prpl-task-form" >
137128 { isLoadingTemplate && (
138129 < div className = "prpl-spinner" >
@@ -196,16 +187,99 @@ export default function OnboardTask( { task, config, onComplete } ) {
196187 tabIndex = { - 1 }
197188 ref = { ( el ) => {
198189 if ( el && templateHtml ) {
199- // Re-initialize file upload handlers after template is rendered.
200- const fileInputs =
201- el . querySelectorAll (
202- 'input[type="file"]'
190+ // Prevent duplicate button creation on re-renders.
191+ if (
192+ el . querySelector ( '.prpl-task-buttons' )
193+ ) {
194+ return ;
195+ }
196+
197+ const actionBtn = el . querySelector (
198+ '.prpl-complete-task-btn'
199+ ) ;
200+
201+ if ( actionBtn ) {
202+ // Create button wrapper like develop branch does.
203+ const buttonWrapper =
204+ document . createElement ( 'div' ) ;
205+ buttonWrapper . className =
206+ 'prpl-task-buttons' ;
207+
208+ // Create close button.
209+ const closeBtn =
210+ document . createElement ( 'button' ) ;
211+ closeBtn . type = 'button' ;
212+ closeBtn . className =
213+ 'prpl-btn prpl-task-close-btn' ;
214+ closeBtn . innerHTML =
215+ '<span class="dashicons dashicons-arrow-left-alt2"></span> ' +
216+ ( config ?. l10n
217+ ?. backToRecommendations ||
218+ 'Back to recommendations' ) ;
219+ closeBtn . addEventListener (
220+ 'click' ,
221+ handleClose
203222 ) ;
204- // File upload handling will be done by existing JavaScript if available.
205- // eslint-disable-next-line no-unused-vars
206- fileInputs . forEach ( ( ) => {
207- // File inputs are handled by existing event listeners.
208- } ) ;
223+
224+ // Insert wrapper before action button, then move buttons into it.
225+ actionBtn . parentNode . insertBefore (
226+ buttonWrapper ,
227+ actionBtn
228+ ) ;
229+ buttonWrapper . appendChild ( closeBtn ) ;
230+ buttonWrapper . appendChild ( actionBtn ) ;
231+
232+ // Disable action button by default if requested.
233+ if ( disableActionButton ) {
234+ actionBtn . disabled = true ;
235+ actionBtn . classList . add (
236+ 'prpl-btn-disabled'
237+ ) ;
238+
239+ // Enable button when user makes a selection.
240+ const enableButton = ( ) => {
241+ actionBtn . disabled = false ;
242+ actionBtn . classList . remove (
243+ 'prpl-btn-disabled'
244+ ) ;
245+ } ;
246+
247+ // Watch for form input changes.
248+ const inputs = el . querySelectorAll (
249+ 'input, select, textarea'
250+ ) ;
251+ inputs . forEach ( ( input ) => {
252+ input . addEventListener (
253+ 'change' ,
254+ enableButton
255+ ) ;
256+ input . addEventListener (
257+ 'input' ,
258+ enableButton
259+ ) ;
260+ } ) ;
261+
262+ // Watch for file uploads.
263+ const fileInputs =
264+ el . querySelectorAll (
265+ 'input[type="file"]'
266+ ) ;
267+ fileInputs . forEach (
268+ ( fileInput ) => {
269+ fileInput . addEventListener (
270+ 'change' ,
271+ enableButton
272+ ) ;
273+ }
274+ ) ;
275+
276+ // Watch for custom events (e.g., from media uploader).
277+ el . addEventListener (
278+ 'prpl-task-input-changed' ,
279+ enableButton
280+ ) ;
281+ }
282+ }
209283 }
210284 } }
211285 />
0 commit comments