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 / UncategorizedFaq.js

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

277 lines 10.2 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 React, { useEffect, useState } from "react";
3 import { toast } from "react-toastify";
4 import Swal from "sweetalert2";
5 import { createFAQ, deleteFaq, faqStatusChange, scrollExpandedIntoView } from "../utils/function";
6 import EditFaq from "./EditFaq";
7 import { useFAQ } from "../../components/FAQContext";
8 import { useFAQScope } from "../context/ScopeContext";
9 import { useQueryClient } from "@tanstack/react-query";
10
11 // icons
12 import { ReactComponent as Delete } from "./../assets/images/delete.svg";
13 import { ReactComponent as DownArrow } from "./../assets/images/down-arrow.svg";
14 import { ReactComponent as Duplicate } from "./../assets/images/duplicate.svg";
15 import { ReactComponent as Edit } from "./../assets/images/edit.svg";
16 import { ReactComponent as Magic } from "./../assets/images/magic.svg";
17 import FaqAIModalForm from "./FaqAIModalForm";
18
19 const UncategorizedFaq = ({
20 data,
21 categoriesLoading,
22 allFaqCategories,
23 reFetchCategories,
24 reFetchFaqList,
25 setCreateFaqAt,
26 faqExpanded,
27 setFaqExpanded,
28 setReFetchFaqForCategory,
29 settings,
30 }) => {
31 const [faqStatus, setFaqStatus] = useState(data?.post_status);
32 const [showFaqAIModalForm, setShowFaqAIModalForm] = useState(false);
33 const { bumpStats, highlightFaqId, setHighlightFaqId } = useFAQ();
34 const { taxonomy } = useFAQScope();
35 const queryClient = useQueryClient();
36
37 // Flash this row when it's the FAQ just created in the Uncategorized panel.
38 const [flashing, setFlashing] = useState(false);
39 const isHighlighted =
40 highlightFaqId != null && String(highlightFaqId) === String(data?.ID);
41 useEffect(() => {
42 if (!isHighlighted) return;
43 setFlashing(true);
44 scrollExpandedIntoView(`faq-${data?.ID}`);
45 setHighlightFaqId(null);
46 }, [isHighlighted]);
47 useEffect(() => {
48 if (!flashing) return;
49 const t = setTimeout(() => setFlashing(false), 2000);
50 return () => clearTimeout(t);
51 }, [flashing]);
52
53 useEffect(() => {
54 if (faqExpanded !== undefined) {
55 setCreateFaqAt(null);
56 }
57 }, [faqExpanded]);
58
59 const handleUpdate = (post) => {
60 setFaqExpanded(post?.ID);
61 };
62
63 // Called by EditFaq after a save. If the FAQ was moved into a group it no
64 // longer belongs in the Uncategorized bucket — drop it from the cache (like
65 // delete) instead of reloading the whole panel. Otherwise refresh in place.
66 const handleEditFaqRefetch = (selectedCategory) => {
67 if (selectedCategory && selectedCategory !== "not-set") {
68 queryClient.setQueryData(
69 ["uncategorizedFaq", taxonomy],
70 (old) => (old || []).filter((faq) => faq?.ID !== data?.ID)
71 );
72 } else if (typeof reFetchFaqList === "function") {
73 reFetchFaqList();
74 }
75 };
76
77 const handleExpanded = (post) => {
78 if (faqExpanded === post?.ID) {
79 setFaqExpanded(undefined);
80 } else {
81 setFaqExpanded(post?.ID);
82 scrollExpandedIntoView(`faq-${post?.ID}`);
83 }
84 };
85
86 // useEffect(() => {
87 // setIsMagicIcon(true);
88 // if (faqExpanded !== undefined && faqExpanded === data?.ID) {
89 // if (isMagicIcon) {
90 // setShowFaqAIModalForm(true);
91 // }
92 // }
93 // }, [faqExpanded]);
94
95 const handleDuplicate = () => {
96 Swal.fire({
97 title: __("Duplicate this FAQ?", "betterdocs"),
98 icon: "info",
99 showConfirmButton: true,
100 showCancelButton:true,
101 confirmButtonText: __('Yes', 'betterdocs'),
102 cancelButtonText: __('No', 'betterdocs'),
103 customClass: {
104 popup: 'betterdocs-bulk-action-popup'
105 }
106 }).then((result) => {
107 if (result.isConfirmed) {
108 createFAQ({
109 term_id: null,
110 post_title: "Copy " + data?.post_title,
111 post_content: data?.post_content,
112 })
113 .then((res) => {
114 if (res) {
115 toast.success(__("FAQ duplicated.", "betterdocs"));
116 reFetchFaqList();
117 bumpStats();
118 }
119 })
120 .catch((err) => {
121 err?.name
122 ? toast.error(`${err?.name} :${err?.message}`)
123 : toast.error(`${err?.message}`);
124 });
125 }
126 });
127 };
128
129 const handleDelete = (post_id) => {
130 Swal.fire({
131 title: __("Delete this FAQ?", "betterdocs"),
132 icon: "warning",
133 showCancelButton: true,
134 confirmButtonText: __("Yes, delete it!", "betterdocs"),
135 cancelButtonText: __("Cancel", "betterdocs"),
136 customClass: {
137 popup: 'betterdocs-bulk-action-popup'
138 }
139 }).then((result) => {
140 if (result.isConfirmed) {
141 deleteFaq(post_id)
142 .then(() => {
143 toast.success(__("FAQ deleted.", "betterdocs"));
144 // Just drop the deleted FAQ from the cached list (like
145 // the group FaqList) — no full refetch of the bucket.
146 queryClient.setQueryData(
147 ["uncategorizedFaq", taxonomy],
148 (old) => (old || []).filter((faq) => faq?.ID !== post_id)
149 );
150 bumpStats();
151 })
152 .catch((err) => {
153 err?.name
154 ? toast.error(`${err?.name} :${err?.message}`)
155 : toast.error(`${err?.message}`);
156 });
157 }
158 });
159 };
160
161 const handleFaqStatus = (post_id) => {
162 faqStatusChange(post_id, faqStatus === "publish" ? "draft" : "publish")
163 .then(() => {
164 setFaqStatus(faqStatus === "publish" ? "draft" : "publish");
165 // Refresh stat counts (published/draft) after the status flip.
166 reFetchCategories && reFetchCategories();
167 bumpStats();
168 faqStatus === "publish"
169 ? toast.error(__("FAQ Disabled.", "betterdocs"))
170 : toast.success(__("FAQ Enabled.", "betterdocs"));
171 })
172 .catch((err) => {
173 err?.name
174 ? toast.error(`${err?.name} :${err?.message}`)
175 : toast.error(`${err?.message}`);
176 });
177 };
178
179 return (
180 <div
181 className={`bd-faq${flashing ? " bd-faq--highlight" : ""}`}
182 data-bd-expand-id={`faq-${data?.ID}`}
183 >
184 <div className="bd-faq-head">
185 <h3
186 className="bd-faq-title"
187 onClick={() => handleExpanded(data)}
188 >
189 {data?.post_title}
190 </h3>
191 <label
192 className="bd-faq-toggler"
193 onClick={() => handleFaqStatus(data?.ID)}
194 >
195 <input
196 type="checkbox"
197 checked={faqStatus === "publish" ? true : false}
198 className="checkbox"
199 />
200 <span className="knobs"></span>
201 <span className="layer"></span>
202 </label>
203 <button
204 type="button"
205 className="bd-faq-button-reset bd-faq-action-button"
206 onClick={() => handleUpdate(data)}
207 >
208 <Edit />
209 </button>
210 {settings && (settings?.enable_faq_write_with_ai ?? true) && (
211 <button
212 type="button"
213 className="bd-faq-button-reset bd-faq-action-button magic-icon"
214 onClick={() => {
215 handleUpdate(data);
216 setShowFaqAIModalForm(true);
217 }}
218 >
219 <Magic />
220 </button>
221 )}
222
223 <button
224 type="button"
225 className="bd-faq-button-reset bd-faq-action-button"
226 onClick={() => handleDuplicate()}
227 >
228 <Duplicate />
229 </button>
230 <button
231 type="button"
232 className="bd-faq-button-reset bd-faq-action-button delete-icon"
233 onClick={() => handleDelete(data?.ID)}
234 >
235 <Delete />
236 </button>
237 <button
238 type="button"
239 className={`bd-faq-accordion-icon ${
240 faqExpanded === data?.ID ? "active" : ""
241 }`}
242 onClick={() => {
243 handleExpanded(data);
244 }}
245 >
246 <DownArrow />
247 </button>
248 </div>
249 <div
250 className={`bd-faq-body ${
251 faqExpanded === data?.ID ? "active" : ""
252 }`}
253 >
254 <div className="bd-faq-body-inner">
255 {
256 <EditFaq
257 faq={data}
258 reFetchFaqList={handleEditFaqRefetch}
259 categoriesLoading={categoriesLoading}
260 allFaqCategories={allFaqCategories}
261 reFetchCategories={reFetchCategories}
262 faqExpanded={faqExpanded}
263 setFaqExpanded={setFaqExpanded}
264 setReFetchFaqForCategory={setReFetchFaqForCategory}
265 showFaqAIModalForm={showFaqAIModalForm}
266 setShowFaqAIModalForm={setShowFaqAIModalForm}
267 settings={settings}
268 />
269 }
270 </div>
271 </div>
272 </div>
273 );
274 };
275
276 export default UncategorizedFaq;
277