PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.5
4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 All 203 releases
betterdocs / react-src / admin / settings / fields / BetterRepeater.js

BetterRepeater.js in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.5, at react-src/admin/settings/fields/BetterRepeater.js

296 lines 15.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import React, { useCallback, useMemo, useEffect, useState } from 'react'
2 import { ReactSortable } from "react-sortablejs";
3 import { v4 } from "uuid";
4 import { executeChange, useBuilderContext, GenericField, isArray } from 'quickbuilder';
5 import BetterRepeaterField from './BetterRepeaterField';
6 import ReactModal from "react-modal";
7 import BetterRepeaterValueShow from './BetterRepeaterValueShow';
8 import { __ } from '@wordpress/i18n';
9 import CloseIcon from '../components/CloseIcon';
10
11
12 const BetterRepeater = (props) => {
13 const { name: fieldName, value: fieldValue, placeholder_img, button, _fields, visible_fields, empty_rules_message } = props;
14 const [isOpen, setIsOpen] = useState(false);
15 const [isIntegrationModalOpen, setIsIntegrationModalOpen] = useState(false);
16 const [isEditNecessaryModalOpen, setIsEditCookieInfoModalOpen] = useState(false);
17 const builderContext = useBuilderContext();
18 const [localMemoizedValue, setLocalMemoizedValue] = useState(builderContext.values?.[fieldName]);
19 const [activeEditItem, setActiveEditItem] = useState("");
20 const [lastEditState, setLastEditState] = useState({});
21
22 useEffect(() => {
23 if (builderContext.values?.[fieldName] != undefined) {
24 setLocalMemoizedValue(builderContext.values?.[fieldName]);
25 }
26 }, [builderContext.values?.[fieldName]]);
27
28 const handleChange = (event, index) => {
29 if (event.persist) {
30 event.persist();
31 }
32 const { field, val: value } = executeChange(event);
33 builderContext.setFieldValue([fieldName, index, field], value);
34 }
35
36 const handleRemove = useCallback((index) => {
37 let lValue = [...localMemoizedValue];
38 lValue.splice(index, 1)
39 builderContext.setFieldValue(fieldName, lValue);
40 }, [localMemoizedValue])
41
42 const handleClone = useCallback((index) => {
43 let lValue = [...localMemoizedValue];
44 if (lValue.length > 0) {
45 let indexedCopy = lValue?.[index] || {};
46 if(indexedCopy?.title){
47 indexedCopy = {...indexedCopy, title: (indexedCopy.title + ' - Copy')}
48 }
49 if(indexedCopy?.post_title){
50 indexedCopy = {...indexedCopy, post_title: (indexedCopy.post_title + ' - Copy')}
51 }
52 if(indexedCopy?.username){
53 indexedCopy = {...indexedCopy, username: (indexedCopy.username + ' - Copy')}
54 }
55 if(indexedCopy?.plugin_theme_name){
56 indexedCopy = {...indexedCopy, plugin_theme_name: (indexedCopy.plugin_theme_name + ' - Copy')}
57 }
58 indexedCopy = {...indexedCopy, index: v4(), isCollapsed: false};
59 builderContext.setFieldValue([fieldName, localMemoizedValue.length], indexedCopy);
60 }
61 }, [localMemoizedValue])
62
63
64 useEffect(() => {
65 if (localMemoizedValue == undefined || localMemoizedValue == '') {
66 setLocalMemoizedValue([]);
67 } else{
68 // setLocalMemoizedValue((items) => items.map((item) => {
69 // return {...item, index: v4()};
70 // }))
71 }
72 }, [])
73
74 const handleButtonClick = () => {
75 let fieldKeys = Object.keys(_fields);
76 let newFieldKeysWithDefaultValues = {};
77 fieldKeys?.map((key) => {
78 newFieldKeysWithDefaultValues[key] = '';
79 });
80 builderContext.setFieldValue(fieldName, [...localMemoizedValue, {index: v4(), ...newFieldKeysWithDefaultValues}]);
81 setIsOpen(true);
82 }
83
84 const _handleButtonClick = () => {
85 setIsIntegrationModalOpen(true);
86 }
87
88
89 let editItem = localMemoizedValue?.find((value) => ( value?.index == activeEditItem));
90 let modalLockCondition = ( localMemoizedValue != undefined && editItem == undefined && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.access_control_rule?.length > 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_restrict_doc_category?.length > 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_restrict_roles?.length > 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_permission_mode?.length > 0 ) ||
91 ( localMemoizedValue != undefined && editItem == undefined && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.access_control_rule_kb?.length > 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_restrict_multiple_kb?.toString()?.length > 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_restrict_roles_kb?.length > 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_permission_mode_kb?.length > 0 ) ||
92 ( editItem != undefined && editItem?.access_control_rule?.length > 0 && editItem?.control_access_restrict_doc_category?.length > 0 && editItem?.control_access_restrict_roles?.length > 0 && editItem?.control_access_permission_mode?.length > 0 ) ||
93 ( editItem != undefined && editItem?.access_control_rule_kb?.length > 0 && editItem?.control_access_restrict_multiple_kb?.toString()?.length > 0 && editItem?.control_access_restrict_roles_kb?.length > 0 && editItem?.control_access_permission_mode_kb?.length > 0 )
94 ? '' : ' locked';
95
96
97 function closeiconremove(e) {
98 if( activeEditItem?.length == 0 ) { // if this is not edit more remove modal, active edit item
99 setIsOpen(false);
100 setActiveEditItem("");
101 handleRemove(localMemoizedValue?.length - 1); // remove the recently added fresh modal if cross is clicked
102 } else {
103 let index = localMemoizedValue?.findIndex((value) => {
104 return value?.index == lastEditState?.index;
105 });
106
107 if( index != undefined ) {
108 localMemoizedValue[index] = {...lastEditState};
109 }
110
111 builderContext.setFieldValue(fieldName, [...localMemoizedValue]);
112 setIsOpen(false);
113 setActiveEditItem("");
114 }
115 }
116
117 useEffect(() => {
118 if( activeEditItem?.length > 0 ) {
119 let foundItem = localMemoizedValue?.find((value) => value?.index == activeEditItem);
120 if( foundItem != undefined ) {
121 setLastEditState({...foundItem}); //preserve the last edit state
122 }
123 } else {
124 setLastEditState({}); //revert the last edit state, when edit mode is disabled
125 }
126 }, [activeEditItem]);
127
128 return (
129 <>
130 {empty_rules_message != undefined && localMemoizedValue?.length == 0 && <div className='wprf-repeater-no-rules-message-wrapper'>
131 <div className='wprf-repeater-no-rules-message-wrapper-secondary-wrapper'>
132 {empty_rules_message?.icon_url?.length > 0 && <img className="wprf-repeater-rules-image" src={empty_rules_message?.icon_url}></img>}
133 {empty_rules_message?.heading?.length > 0 && <h3 className='wprf-repeater-rules-heading'>{empty_rules_message?.heading}</h3>}
134 {empty_rules_message?.subtitle?.length > 0 && <p className='wprf-repeater-rules-subtitle'>{empty_rules_message?.subtitle}</p>}
135 </div>
136 </div>}
137 <div className={`wprf-repeater-control${localMemoizedValue?.length == 0 ? ' no-rules' : ''}`}>
138 {localMemoizedValue?.length > 0 ? (
139 button?.position == 'top' &&
140 <div className="wprf-repeater-label" onClick={handleButtonClick}>
141 <button className="wprf-repeater-button"
142 onClick={handleButtonClick}>
143 {button?.label}
144 </button>
145 </div>
146 ) : (
147 <div className="wprf-repeater-empty">
148 {button?.position == 'top' &&
149 <div className="wprf-repeater-label" onClick={handleButtonClick}>
150 <button className="wprf-repeater-button"
151 onClick={handleButtonClick}>
152 {button?.label}
153 </button>
154 </div>
155 }
156 </div>
157 )
158 }
159 {
160 localMemoizedValue && localMemoizedValue?.length > 0 &&
161 <div className="wprf-repeater-content">
162 {
163 localMemoizedValue.map((value, index) => {
164 return ( isOpen && localMemoizedValue?.length - 1 == index && ! activeEditItem ) ? '' : <BetterRepeaterValueShow
165 isEditMode={lastEditState != undefined && value?.index == lastEditState?.index ? true : false}
166 editState={lastEditState != undefined ? lastEditState : {}}
167 isDefault={value?.default ? true : false}
168 isCollapsed={value?.isCollapsed}
169 key={value?.index || index}
170 fields={_fields}
171 index={index}
172 parent={fieldName}
173 clone={handleClone}
174 remove={handleRemove}
175 onChange={(event) => handleChange(event, index)}
176 visible_fields={visible_fields}
177 setIsOpen={() => {
178 setIsOpen(true);
179 setActiveEditItem(value?.index);
180 }}
181 />
182 })
183 }
184 </div>
185 }
186 { button?.position == 'bottom' &&
187 <div className="wprf-repeater-label" onClick={handleButtonClick}>
188 {button?.icon_url?.length > 0 && <img className="repeater-button-icon-wrapper" src={button?.icon_url}/>}
189 <button className="wprf-repeater-button"
190 onClick={handleButtonClick}>
191 {button?.label}
192 </button>
193 </div>
194 }
195 <ReactModal
196 isOpen={isOpen}
197 ariaHideApp={false}
198 overlayClassName={`betterdocs-access-control-wrapper-overlay`}
199 className={'betterdocs-access-control-wrapper'}
200 style={{
201 overlay: {
202 zIndex: 99999,
203 }
204 }}
205 >
206 <>
207 <div className="wprf-modal-preview-header" onClick={closeiconremove}>
208 <CloseIcon onClick={closeiconremove}/>
209 {/* <button onClick={() => {
210 setIsOpen(false);
211 setActiveEditItem("");
212 }}>
213 <CloseIcon />
214 </button> */}
215 </div>
216 <div className="wprf-modal-table-wrapper wpsp-better-repeater-fields">
217 <span className='repeater-heading'>{ activeEditItem ? __( 'Edit Rule','betterdocs' ) : __( 'Create New Rule','betterdocs' ) }</span>
218 <p className='repeater-subtitle'>{__('Set custom rules to control who can view, edit, or manage specific documentation based on user roles.', 'betterdocs')}</p>
219 { localMemoizedValue && localMemoizedValue?.length > 0 &&
220 <div className="wprf-repeater-content">
221 { activeEditItem &&
222 localMemoizedValue.map((value, index) => {
223 let parentIndex = [...props.parentIndex, 'fields', index];
224 if( value?.index == activeEditItem ) {
225 return <BetterRepeaterField
226 isCollapsed={value?.isCollapsed}
227 key={value?.index || index}
228 fields={_fields}
229 index={index}
230 parent={fieldName}
231 clone={handleClone}
232 remove={handleRemove}
233 onChange={(event) => handleChange(event, index)}
234 parentIndex={parentIndex}
235 />
236 }
237 })
238 }
239 { !activeEditItem &&
240 localMemoizedValue.map((value, index) => {
241 if( localMemoizedValue?.length == (index + 1) ) {
242 let parentIndex = [...props.parentIndex, 'fields', index];
243 return <BetterRepeaterField
244 isCollapsed={value?.isCollapsed}
245 key={value?.index || index}
246 fields={_fields}
247 index={index}
248 parent={fieldName}
249 clone={handleClone}
250 remove={handleRemove}
251 onChange={(event) => handleChange(event, index)}
252 parentIndex={parentIndex}
253 />
254 }
255 })
256 }
257 </div>
258 }
259 <div className="wprf-modal-preview-footer">
260 <button className={`wpsp-btn wpsp-btn-preview-update${modalLockCondition}`} onClick={() => {
261 if( ( activeEditItem?.length == 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.access_control_rule?.length == 0 ) ||
262 ( activeEditItem?.length == 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_restrict_doc_category?.length == 0 ) ||
263 ( activeEditItem?.length == 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_restrict_roles?.length == 0 ) ||
264 ( activeEditItem?.length == 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_permission_mode?.length == 0 ) ||
265 ( activeEditItem?.length == 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.access_control_rule_kb?.length == 0 ) ||
266 ( activeEditItem?.length == 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_restrict_multiple_kb?.toString()?.length == 0 ) ||
267 ( activeEditItem?.length == 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_restrict_roles_kb?.length == 0 ) ||
268 ( activeEditItem?.length == 0 && builderContext.values?.[fieldName][localMemoizedValue?.length - 1]?.control_access_permission_mode_kb?.length == 0 )
269 ) {
270 return;
271 }
272
273 if( ( activeEditItem?.length > 0 && editItem?.access_control_rule?.length == 0 ) ||
274 ( activeEditItem?.length > 0 && editItem?.control_access_restrict_doc_category?.length == 0 ) ||
275 ( activeEditItem?.length > 0 && editItem?.control_access_restrict_roles?.length == 0 ) ||
276 ( activeEditItem?.length > 0 && editItem?.control_access_permission_mode?.length == 0 ) ||
277 ( activeEditItem?.length > 0 && editItem?.access_control_rule_kb?.length == 0 ) ||
278 ( activeEditItem?.length > 0 && editItem?.control_access_restrict_multiple_kb?.toString()?.length == 0 ) ||
279 ( activeEditItem?.length > 0 && editItem?.control_access_restrict_roles_kb?.length == 0 ) ||
280 ( activeEditItem?.length > 0 && editItem?.control_access_permission_mode_kb?.length == 0 ) ) {
281 return;
282 }
283 setIsOpen(false);
284 setActiveEditItem("");
285 }}> { activeEditItem ? __( 'Update','betterdocs' ) : __( 'Save','betterdocs' ) }</button>
286 </div>
287 </div>
288 </>
289 </ReactModal>
290 </div>
291 </>
292 )
293 }
294
295 export default BetterRepeater;
296