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

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

234 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useQuery } from "@tanstack/react-query";
2 import React, { useEffect, useState } from "react";
3 import { DragDropContext, Droppable } from "react-beautiful-dnd";
4 import { getAllUncategorizedFaq, reOrderCategories } from "../utils/function";
5 import { categoryOrdering } from "../utils/reOrder";
6 import Category from "./Category";
7 import UncategorizedFaqList from "./UncategorizedFaqList";
8 import CategoryLoader from "../utils/CategoryLoader";
9 import { useFAQScope } from "../context/ScopeContext";
10
11 const CategoryList = ({
12 isLoading,
13 categoriesLoading,
14 categoriesFetching,
15 allFaqCategories,
16 reFetchCategories,
17 setUpdateCategory,
18 setUncategorizedLoading,
19 setUncategorizedEmpty,
20 setShowFaqAIModalForm,
21 perPage,
22 currentPage,
23 order,
24 highlightGroupId,
25 }) => {
26 const { isProduct, taxonomy } = useFAQScope();
27
28 const {
29 isLoading: uncategorizedFaqLoadingRaw,
30 isFetching: uncategorizedFaqFetching,
31 data: allUncategorizedFaq,
32 refetch: setFetchUncategorizedFaq,
33 } = useQuery(["uncategorizedFaq", taxonomy], getAllUncategorizedFaq, {
34 // Both tabs have an "Uncategorized" bucket now: a Product FAQ can lose its group
35 // (the group is deleted without its FAQs) or be created without one, and it must
36 // stay visible on its own tab rather than surfacing under General. The endpoint
37 // scopes the bucket by taxonomy; the key does the same so the two tabs' results
38 // never bleed into each other.
39 });
40 const uncategorizedFaqLoading = uncategorizedFaqLoadingRaw;
41
42 const [createFaqAt, setCreateFaqAt] = useState(null);
43 const [faqExpanded, setFaqExpanded] = useState(undefined);
44 const [categoryExpanded, setCategoryExpanded] = useState(undefined);
45 const [sortableCategories, setSortableCategories] = useState([]);
46 const [changeFaqSort, setChangeFaqSort] = useState({});
47 const [reFetchFaqForCategory, setReFetchFaqForCategory] = useState(null);
48
49 useEffect(() => {
50 if (!allFaqCategories?.length) {
51 setSortableCategories([]);
52 return;
53 }
54 const sorted = [...allFaqCategories];
55 // For every sort mode the server already returns this page in the right
56 // order (FAQBuilder::faq_category_orderby_meta), so the whole list is
57 // ordered and paginated consistently. Only re-sort in "default" mode,
58 // where drag-and-drop mutates meta.order locally and we need the list to
59 // reflect the new positions immediately without a refetch.
60 if (order === 'default') {
61 sorted.sort((a, b) => (a?.meta?.order[0] ?? 0) - (b?.meta?.order[0] ?? 0));
62 }
63 setSortableCategories(sorted);
64 }, [allFaqCategories, order]);
65
66 useEffect(() => {
67 if (allUncategorizedFaq?.length) {
68 setUncategorizedEmpty(false);
69 } else {
70 setUncategorizedEmpty(true);
71 }
72 }, [allUncategorizedFaq]);
73
74 useEffect(() => {
75 setUncategorizedLoading(uncategorizedFaqLoading);
76 }, [uncategorizedFaqLoading]);
77
78 const onDragEnd = (result) => {
79 if (!result.destination) {
80 return;
81 }
82 // Manual reordering only applies in the "default" order. Any other sort
83 // (A→Z, Most Recent, …) is read-only — the grip stays visible but the
84 // Draggables are disabled, so this is just a safety net.
85 if (order !== 'default') {
86 return;
87 }
88 if (result.type == "category") {
89 const updatedCategories = categoryOrdering(
90 result,
91 sortableCategories,
92 currentPage,
93 perPage
94 );
95 reOrderCategories(updatedCategories);
96 setCategoryExpanded(undefined);
97 setSortableCategories(
98 sortableCategories
99 .map((category) => {
100 return {
101 ...category,
102 meta: {
103 ...category?.meta,
104 order: [
105 updatedCategories.find(
106 (cat) => cat?.id === category?.id
107 )?.updated_position,
108 ],
109 },
110 };
111 })
112 .sort(function (a, b) {
113 return a?.meta?.order[0] - b?.meta?.order[0];
114 })
115 );
116 } else {
117 setChangeFaqSort(result);
118 }
119 };
120
121 return (
122 <>
123 {!isLoading ? (
124 <DragDropContext onDragEnd={onDragEnd}>
125 <div className="bd-faq-ctg-list">
126 <Droppable
127 droppableId="bdCategoryList"
128 type={"category"}
129 >
130 {(provided) => (
131 <div
132 {...provided.droppableProps}
133 ref={provided.innerRef}
134 className="bd-faq-ctg-list-droppable"
135 >
136 {categoriesLoading ? (
137 <CategoryLoader />
138 ) : sortableCategories?.map(
139 (category, index) => (
140 <Category
141 key={category.id}
142 index={index}
143 category={category}
144 order={order}
145 categoriesLoading={
146 categoriesLoading
147 }
148 allFaqCategories={
149 allFaqCategories
150 }
151 reFetchCategories={
152 reFetchCategories
153 }
154 createFaqAt={
155 createFaqAt
156 }
157 setFetchUncategorizedFaq={
158 setFetchUncategorizedFaq
159 }
160 setUpdateCategory={
161 setUpdateCategory
162 }
163 categoryExpanded={
164 categoryExpanded
165 }
166 setCategoryExpanded={
167 setCategoryExpanded
168 }
169 setCreateFaqAt={
170 setCreateFaqAt
171 }
172 faqExpanded={
173 faqExpanded
174 }
175 setFaqExpanded={
176 setFaqExpanded
177 }
178 changeFaqSort={
179 changeFaqSort
180 }
181 setChangeFaqSort={
182 setChangeFaqSort
183 }
184 reFetchFaqForCategory={
185 reFetchFaqForCategory
186 }
187 setReFetchFaqForCategory={
188 setReFetchFaqForCategory
189 }
190 setShowFaqAIModalForm={setShowFaqAIModalForm}
191 highlightGroupId={highlightGroupId}
192 />
193 )
194 )}
195 {provided.placeholder}
196 </div>
197 )}
198 </Droppable>
199 {/* Shown on both tabs — each lists only its own ungrouped FAQs. */}
200 {(allUncategorizedFaq?.length || uncategorizedFaqLoading) ? (
201 <UncategorizedFaqList
202 categoriesLoading={categoriesLoading}
203 allFaqCategories={allFaqCategories}
204 reFetchCategories={reFetchCategories}
205 allUncategorizedFaq={allUncategorizedFaq}
206 setFetchUncategorizedFaq={
207 setFetchUncategorizedFaq
208 }
209 categoryExpanded={categoryExpanded}
210 setCategoryExpanded={setCategoryExpanded}
211 setCreateFaqAt={setCreateFaqAt}
212 faqExpanded={faqExpanded}
213 setFaqExpanded={setFaqExpanded}
214 setReFetchFaqForCategory={
215 setReFetchFaqForCategory
216 }
217 uncategorizedFaqLoading={uncategorizedFaqLoading}
218 uncategorizedFaqFetching={uncategorizedFaqFetching}
219 highlightGroupId={highlightGroupId}
220 />
221 ) : (
222 ""
223 )}
224 </div>
225 </DragDropContext>
226 ) : (
227 ""
228 )}
229 </>
230 );
231 };
232
233 export default React.memo(CategoryList);
234