@@ -2,6 +2,22 @@ import React, { useState, useEffect } from 'react'
22import { useEdit } from '../../context/EditContext'
33import { useContent } from '../../context/ContentContext'
44
5+ /**
6+ * A highly flexible, inline editable text component integrated with the CMS.
7+ *
8+ * This component automatically switches between a display state (plain text)
9+ * and an edit state (input/textarea) based on the global `isEditing` context.
10+ * It handles its own internal state during editing and commits changes to the
11+ * global content state on blur.
12+ *
13+ * @param {Object } props
14+ * @param {string } props.section - The CMS content section name (e.g., 'hero', 'footer').
15+ * @param {string } props.field - The specific field path within the section (e.g., 'title', 'features.0.title').
16+ * @param {string } props.value - The initial fallback value if no CMS data exists.
17+ * @param {boolean } [props.multiline=false] - Whether to use a textarea instead of a text input.
18+ * @param {string } [props.className=''] - Additional CSS classes for the wrapper component.
19+ * @param {React.ElementType } [props.as='span'] - The HTML element or component to render as (e.g., 'h1', 'div').
20+ */
521const EditableText = ( {
622 section,
723 field,
@@ -21,29 +37,41 @@ const EditableText = ({
2137 setInternalValue ( value )
2238 } , [ value ] )
2339
40+ /**
41+ * Commits the edited value back to the CMS backend.
42+ *
43+ * Uses a deep-cloning strategy to preserve data integrity when updating
44+ * nested fields (paths like "array.0.field").
45+ */
2446 const handleBlur = async ( ) => {
47+ // Skip update if value hasn't changed
2548 if ( internalValue === currentValue ) return
2649
2750 try {
51+ // Step 1: Fetch current state of the entire section
2852 const sectionData = getSection ( section )
29- // Deep clone to avoid mutating state directly
53+
54+ // Step 2: Deep clone to avoid direct mutation of the global state object
3055 const updatedSection = JSON . parse ( JSON . stringify ( sectionData ) )
3156
57+ // Step 3: Traverse the object tree using the dot-notated field path
3258 const keys = field . split ( '.' )
3359 let target = updatedSection
3460 for ( let i = 0 ; i < keys . length - 1 ; i ++ ) {
61+ // Ensure intermediate objects exist
3562 if ( ! target [ keys [ i ] ] ) target [ keys [ i ] ] = { }
3663 target = target [ keys [ i ] ]
3764 }
65+
66+ // Step 4: Set the new value at the leaf node
3867 target [ keys [ keys . length - 1 ] ] = internalValue
3968
69+ // Step 5: Persist via the content context (API call + State Sync)
4070 await updateSection ( section , updatedSection )
41- // On success, currentValue will update via props eventually,
42- // but for immediate feedback we can set it here too if we want,
43- // though typically we rely on the prop update from parent re-render.
4471 } catch ( err ) {
45- console . error ( 'Failed to save' , err )
46- setInternalValue ( currentValue ) // Revert on error
72+ console . error ( 'CMS: Failed to save changes' , err )
73+ // Revert UI to last known good value on failure
74+ setInternalValue ( currentValue )
4775 }
4876 }
4977
0 commit comments