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 / faq-builder / components / Faq.js

Faq.js in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.5, at react-src/admin/faq-builder/components/Faq.js

291 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { decodeEntities } from "@wordpress/html-entities";
2 import { __ } from "@wordpress/i18n";
3 import React, { useEffect, useState } from "react";
4 import { Draggable } from "react-beautiful-dnd";
5 import { toast } from "react-toastify";
6 import Swal from "sweetalert2";
7 import { createFAQ, deleteFaq, faqStatusChange, scrollExpandedIntoView } from "../utils/function";
8 import EditFaq from "./EditFaq";
9 import { useFAQ } from "../../components/FAQContext";
10 import { useFAQScope } from "../context/ScopeContext";
11
12 // icons
13 import { ReactComponent as Grip } from "./../assets/images/grip.svg";
14 import { ReactComponent as Delete } from "./../assets/images/delete.svg";
15 import { ReactComponent as DownArrow } from "./../assets/images/down-arrow.svg";
16 import { ReactComponent as Duplicate } from "./../assets/images/duplicate.svg";
17 import { ReactComponent as Edit } from "./../assets/images/edit.svg";
18 import { ReactComponent as Magic } from "./../assets/images/magic.svg";
19
20 const Faq = ({
21 data,
22 index,
23 category,
24 categoriesLoading,
25 allFaqCategories,
26 reFetchCategories,
27 reFetchFaqList,
28 setCreateFaqAt,
29 faqExpanded,
30 setFaqExpanded,
31 setReFetchFaqForCategory,
32 settings,
33 order
34 }) => {
35 const [faqStatus, setFaqStatus] = useState(data?.status);
36 const [showFaqAIModalForm, setShowFaqAIModalForm] = useState(false);
37 const { bumpStats, highlightFaqId, setHighlightFaqId } = useFAQ();
38 const { taxonomy } = useFAQScope();
39
40 // Flash this row when it's the FAQ that was just created in / moved into the
41 // group. Consume the signal immediately (one-shot) and run a local 2s flash
42 // so the animation completes even after highlightFaqId is cleared.
43 const [flashing, setFlashing] = useState(false);
44 const isHighlighted =
45 highlightFaqId != null && String(highlightFaqId) === String(data?.id);
46 useEffect(() => {
47 if (!isHighlighted) return;
48 setFlashing(true);
49 scrollExpandedIntoView(`faq-${data?.id}`);
50 setHighlightFaqId(null);
51 }, [isHighlighted]);
52 useEffect(() => {
53 if (!flashing) return;
54 const t = setTimeout(() => setFlashing(false), 2000);
55 return () => clearTimeout(t);
56 }, [flashing]);
57
58 useEffect(() => {
59 setFaqStatus(data?.status);
60 }, [data]);
61
62
63 useEffect(() => {
64 if (faqExpanded !== undefined) {
65 setCreateFaqAt(null);
66 }
67 }, [faqExpanded]);
68
69 const handleUpdate = (post) => {
70 setFaqExpanded(post?.id);
71 };
72
73 const handleExpanded = (post) => {
74 if (faqExpanded === post?.id) {
75 setFaqExpanded(undefined);
76 } else {
77 setFaqExpanded(post?.id);
78 scrollExpandedIntoView(`faq-${post?.id}`);
79 }
80 };
81
82 const handleDuplicate = () => {
83 Swal.fire({
84 title: __("Duplicate this FAQ?", "betterdocs"),
85 icon: "info",
86 showConfirmButton: true,
87 showCancelButton:true,
88 confirmButtonText: __('Yes', 'betterdocs'),
89 cancelButtonText: __('No', 'betterdocs'),
90 customClass: {
91 popup: 'betterdocs-bulk-action-popup'
92 }
93 }).then((result) => {
94 if (result.isConfirmed) {
95 createFAQ({
96 term_id: category,
97 post_title: "Copy " + data?.title?.rendered,
98 post_content: data?.content?.rendered,
99 }, taxonomy)
100 .then((res) => {
101 if (res) {
102 reFetchFaqList();
103 reFetchCategories();
104 bumpStats();
105 toast.success(__("FAQ duplicated.", "betterdocs"));
106 }
107 })
108 .catch((err) => {
109 err?.name
110 ? toast.error(`${err?.name} :${err?.message}`)
111 : toast.error(`${err?.message}`);
112 });
113 }
114 });
115 };
116
117 const handleDelete = (post_id) => {
118 Swal.fire({
119 title: __("Delete this FAQ?", "betterdocs"),
120 icon: "warning",
121 showCancelButton: true,
122 confirmButtonText: __("Yes, delete it!", "betterdocs"),
123 cancelButtonText: __("Cancel", "betterdocs"),
124 customClass: {
125 popup: 'betterdocs-bulk-action-popup'
126 }
127 }).then((result) => {
128 if (result.isConfirmed) {
129 deleteFaq(post_id)
130 .then(() => {
131 reFetchFaqList();
132 reFetchCategories();
133 bumpStats();
134 toast.success(__("FAQ deleted.", "betterdocs"));
135 })
136 .catch((err) => {
137 err?.name
138 ? toast.error(`${err?.name} :${err?.message}`)
139 : toast.error(`${err?.message}`);
140 });
141 }
142 });
143 };
144
145 const handleFaqStatus = (post_id) => {
146 faqStatusChange(post_id, faqStatus === "publish" ? "draft" : "publish")
147 .then(() => {
148 setFaqStatus(faqStatus === "publish" ? "draft" : "publish");
149 // Refresh group + stat counts (published/draft) after the status flip.
150 reFetchCategories();
151 bumpStats();
152 faqStatus === "publish"
153 ? toast.error(__("FAQ Disabled.", "betterdocs"))
154 : toast.success(__("FAQ Enabled.", "betterdocs"));
155 })
156 .catch((err) => {
157 err?.name
158 ? toast.error(`${err?.name} :${err?.message}`)
159 : toast.error(`${err?.message}`);
160 });
161 };
162
163 const getItemStyle = (isDragging, draggableStyle) => ({
164 boxShadow: isDragging && "0 0 5px rgba(0, 0, 0, 0.5)",
165 ...draggableStyle,
166 });
167
168 return (
169 <>
170 <Draggable key={data?.id} draggableId={data?.id.toString()} index={index} isDragDisabled={order !== 'default'}>
171 {(provided, snapshot) => (
172 <div
173 className={`bd-faq${flashing ? " bd-faq--highlight" : ""}`}
174 data-bd-expand-id={`faq-${data?.id}`}
175 ref={provided.innerRef}
176 {...provided.draggableProps}
177 style={getItemStyle(
178 snapshot.isDragging,
179 provided.draggableProps.style
180 )}
181 >
182 <div className="bd-faq-head">
183 <button
184 type="button"
185 className="bd-faq-button-reset bd-faq-drag-button"
186 {...provided.dragHandleProps}
187 >
188 <Grip />
189 </button>
190 <div
191 className="bd-faq-title-wrap"
192 onClick={() => handleExpanded(data)}
193 style={{ cursor: "pointer" }}
194 >
195 <h3 className="bd-faq-title">
196 {decodeEntities(data?.title?.rendered)}
197 </h3>
198 {faqStatus === "draft" && (
199 <span className="bd-faq-status-badge">
200 {__("Drafted", "betterdocs")}
201 </span>
202 )}
203 </div>
204
205 <label
206 className="bd-faq-toggler"
207 >
208 <input
209 type="checkbox"
210 checked={faqStatus === "publish"}
211 onChange={() => handleFaqStatus(data?.id)}
212 className="checkbox"
213 />
214 <span className="knobs"></span>
215 <span className="layer"></span>
216 </label>
217 <button
218 type="button"
219 className="bd-faq-button-reset bd-faq-action-button"
220 onClick={() => handleUpdate(data)}
221 >
222 <Edit />
223 </button>
224 {
225 settings && (settings?.enable_faq_write_with_ai ?? true) && (
226 <button
227 type="button"
228 className="bd-faq-button-reset bd-faq-action-button magic-icon"
229 onClick={() => {
230 handleUpdate(data);
231 setShowFaqAIModalForm(true);
232 }}
233 >
234 <Magic />
235 </button>
236 )
237 }
238 <button
239 type="button"
240 className="bd-faq-button-reset bd-faq-action-button"
241 onClick={() => handleDuplicate()}
242 >
243 <Duplicate />
244 </button>
245 <button
246 type="button"
247 className="bd-faq-button-reset bd-faq-action-button delete-icon"
248 onClick={() => handleDelete(data?.id)}
249 >
250 <Delete />
251 </button>
252 <button
253 type="button"
254 className={`bd-faq-accordion-icon ${faqExpanded === data?.id ? "active" : ""
255 }`}
256 onClick={() => handleExpanded(data)}
257 >
258 <DownArrow />
259 </button>
260 </div>
261 <div
262 className={`bd-faq-body ${faqExpanded === data?.id ? "active" : ""
263 }`}
264 >
265 <div className="bd-faq-body-inner">
266 {
267 <EditFaq
268 categoriesLoading={categoriesLoading}
269 allFaqCategories={allFaqCategories}
270 reFetchCategories={reFetchCategories}
271 faq={data}
272 reFetchFaqList={reFetchFaqList}
273 faqExpanded={faqExpanded}
274 setFaqExpanded={setFaqExpanded}
275 setReFetchFaqForCategory={setReFetchFaqForCategory}
276 showFaqAIModalForm={showFaqAIModalForm}
277 setShowFaqAIModalForm={setShowFaqAIModalForm}
278 settings={settings}
279 />
280 }
281 </div>
282 </div>
283 </div>
284 )}
285 </Draggable>
286 </>
287 );
288 };
289
290 export default Faq;
291