Skip to content

Commit 7fb54e5

Browse files
author
VuXfi
committed
refactor: update parentId handling to use null instead of 0 across various components
1 parent 4f79571 commit 7fb54e5

File tree

7 files changed

+350
-66
lines changed

7 files changed

+350
-66
lines changed

HISTORY.md

Lines changed: 0 additions & 24 deletions
This file was deleted.

fixture/virtual-catalog/virtualCatalog.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class TestCatalogStorageService {
3939
}
4040

4141
public async buildTree(): Promise<any> {
42-
const rootElements: TestItem[] = await this.findElementsByParentId(0, null);
42+
const rootElements: TestItem[] = await this.findElementsByParentId(null, null);
4343
const buildSubTree = async (elements: TestItem[]): Promise<any[]> => {
4444
const tree = [];
4545
for (const element of elements) {
@@ -70,7 +70,7 @@ class TestCatalogStorageService {
7070
}
7171

7272
public async populateFromTree(tree: any[]): Promise<void> {
73-
const traverseTree = async (node: any, parentId: string | number | null = 0): Promise<void> => {
73+
const traverseTree = async (node: any, parentId: string | number | null = null): Promise<void> => {
7474
const {children, ...itemData} = node;
7575
const item = {...itemData, parentId} as TestItem;
7676
await this.setElement(item.id, item, true);
@@ -239,7 +239,7 @@ export class TestGroup extends AbstractGroup<TestItem> {
239239

240240
protected async dataPreparation(data: any, catalogId: string, sortOrder?: number) {
241241
let storage = StorageHandler.getStorage()
242-
let parentId = data.parentId ? data.parentId : 0; // changed from null to 0
242+
let parentId = data.parentId ? data.parentId : null; // changed from 0 to null
243243
return {
244244
id: uuid(),
245245
name: data.title,
@@ -346,7 +346,7 @@ class TestItemM extends AbstractItem<TestItem> {
346346

347347
protected async dataPreparation(data: any, catalogId: string, sortOrder?: number) {
348348
let storage = StorageHandler.getStorage()
349-
let parentId = data.parentId ? data.parentId : 0; // changed from null to 0
349+
let parentId = data.parentId ? data.parentId : null; // changed from 0 to null
350350
return {
351351
id: uuid(),
352352
modelId: data.record.id,

src/controllers/catalog/Catalog.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {VueCatalog} from "./FrontentCatalogAdapter";
1+
import {FrontendCatalog, FrontendCatalogUtils} from "./FrontendCatalogAdapter";
22
import {Adminizer} from "../../lib/Adminizer";
33

44
export async function catalogController(req: ReqType, res: ResType) {
@@ -40,7 +40,7 @@ export async function catalogController(req: ReqType, res: ResType) {
4040

4141
if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
4242
const data = req.body
43-
const frontendCatalog = new VueCatalog(_catalog);
43+
const frontendCatalog = new FrontendCatalog(_catalog);
4444
if (!frontendCatalog) return res.status(404);
4545

4646
frontendCatalog.setId(id)
@@ -69,7 +69,8 @@ export async function catalogController(req: ReqType, res: ResType) {
6969
})
7070
}
7171
case 'createItem':
72-
return res.json({'data': await frontendCatalog.createItem(data.data, req)})
72+
const createdItem = await frontendCatalog.createItem(data.data, req);
73+
return res.json({'data': FrontendCatalogUtils.normalizeForFrontend(createdItem)})
7374
case 'getChilds':
7475
return res.json({data: await frontendCatalog.getChilds(data.data, req)})
7576
case 'getActions':
@@ -91,7 +92,8 @@ export async function catalogController(req: ReqType, res: ResType) {
9192
case 'getPopUpTemplate':
9293
return res.json({data: await frontendCatalog.getPopUpTemplate(data.actionId, req)})
9394
case 'updateItem':
94-
return res.json({data: await frontendCatalog.updateItem(item, data.modelId, data.data, req)})
95+
const updatedItem = await frontendCatalog.updateItem(item, data.modelId, data.data, req);
96+
return res.json({data: FrontendCatalogUtils.normalizeForFrontend(updatedItem)})
9597
}
9698
break
9799
case 'DELETE':
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
import {AbstractCatalog, Item} from "../../lib/catalog/AbstractCatalog";
2+
3+
4+
interface NodeModel<TDataType> {
5+
text: string;
6+
droppable: boolean;
7+
// isExpanded: boolean;
8+
id: string;
9+
parent: number;
10+
data?: TDataType;
11+
children?: NodeModel<TDataType>[];
12+
13+
isSelected?: boolean;
14+
isVisible?: boolean;
15+
isDraggable?: boolean;
16+
isSelectable?: boolean;
17+
path?: number[];
18+
pathStr?: string;
19+
level?: number;
20+
isFirstChild?: boolean;
21+
isLastChild?: boolean;
22+
}
23+
24+
interface NodeData extends Item {
25+
}
26+
27+
interface obj {
28+
[key: string]: string;
29+
}
30+
31+
interface RequestData {
32+
reqNode: NodeModel<NodeData>[];
33+
reqParent: NodeModel<NodeData>;
34+
_method: string;
35+
}
36+
37+
/**
38+
* @deprecated Now is React
39+
* // TODO: refactor for react name
40+
*/
41+
export class FrontendCatalog {
42+
catalog: AbstractCatalog;
43+
44+
constructor(_catalog: AbstractCatalog) {
45+
this.catalog = _catalog;
46+
}
47+
48+
setId(id: string) {
49+
this.catalog.setId(id);
50+
}
51+
52+
getItemType(type: string) {
53+
return this.catalog.getItemType(type);
54+
}
55+
56+
getAddTemplate(item: any, req: ReqType) {
57+
return this.catalog.getAddTemplate(item, req);
58+
}
59+
60+
61+
getEditTemplate(item: any, id: string | number, req: ReqType, modelId: string | number) {
62+
return this.catalog.getEditTemplate(item, id, req, modelId)
63+
}
64+
65+
getitemTypes() {
66+
return this.catalog.getitemTypes();
67+
}
68+
69+
getLocales(req: ReqType) {
70+
71+
let obj: obj = {
72+
"Delete": "",
73+
"Edit": "",
74+
"create": "",
75+
"Search": "",
76+
"Select Item type": "",
77+
"Select Items": "",
78+
"Save": "",
79+
"No": "",
80+
"Are you sure?": "",
81+
"Yes": "",
82+
"Select Ids": "",
83+
"OR": "",
84+
"Open in a new window": "",
85+
"Visible": "",
86+
"Clean": "",
87+
"Performing an action...": "",
88+
"Action completed": "",
89+
}
90+
obj[this.catalog.name] = ""
91+
for (const actionHandler of this.catalog.actionHandlers) {
92+
obj[actionHandler.name] = ""
93+
}
94+
95+
let messages = obj
96+
97+
let outMessages: obj = {}
98+
for (const mess of Object.keys(messages)) {
99+
outMessages[mess] = req.i18n.__(mess)
100+
}
101+
return {
102+
...outMessages,
103+
}
104+
}
105+
106+
async getActions(items: NodeModel<any>[], type: string) {
107+
let arrItems = []
108+
for (const item of items) {
109+
if (item.data.id === 0) item.data.id = null;
110+
arrItems.push(await this.catalog.find(item.data))
111+
}
112+
console.log(arrItems)
113+
if (type === 'tools') {
114+
return (await this.catalog.getActions(arrItems))?.filter(e => e.displayTool);
115+
} else {
116+
return (await this.catalog.getActions(arrItems))?.filter(e => e.displayContext);
117+
}
118+
}
119+
120+
async handleAction(actionId: string, items: any[], data: any, req: ReqType) {
121+
let arrItems = []
122+
for (const item of items) {
123+
if (item.data.id === 0) item.data.id = null;
124+
arrItems.push(await this.catalog.find(item.data))
125+
}
126+
console.log(arrItems)
127+
return this.catalog.handleAction(actionId, arrItems, data, req);
128+
}
129+
130+
async getPopUpTemplate(actionId: string, req: ReqType) {
131+
return this.catalog.getPopUpTemplate(actionId, req);
132+
}
133+
134+
async getLink(actionId: string) {
135+
return this.catalog.getLink(actionId);
136+
}
137+
138+
//Below are the methods that require action
139+
140+
async getCatalog() {
141+
let rootItems = await this.catalog.getChilds(null);
142+
return FrontendCatalogUtils.arrayToNode(rootItems, this.catalog.getGroupType().type);
143+
}
144+
145+
async createItem(data: any, req: ReqType) {
146+
// TODO It's not clear why it's here.
147+
//data = VueCatalogUtils.refinement(data);
148+
// if (this.catalog.slug !== "navigation") {
149+
// let item = data.record;
150+
// item.parenId = data.parenId;
151+
// return await this.catalog.createItem(item, req);
152+
// } else {
153+
if (data.parentId === 0) data.parentId = null;
154+
return await this.catalog.createItem(data, req);
155+
// }
156+
}
157+
158+
async getChilds(data: any, req: ReqType) {
159+
data = FrontendCatalogUtils.refinement(data);
160+
if (!data || data.id === 0 || data.id === undefined) {
161+
data = { id: null };
162+
}
163+
if (data.id === 0) data.id = null;
164+
return FrontendCatalogUtils.arrayToNode(await this.catalog.getChilds(data.id, undefined, req), this.catalog.getGroupType().type);
165+
}
166+
167+
// Moved into actions
168+
// getCreatedItems(data: any) {
169+
// data = VueCatalogUtils.refinement(data);
170+
// return this.catalog.getChilds(data.id);
171+
// }
172+
173+
async search(s: string, req: ReqType) {
174+
let searchResult = await this.catalog.search(s, undefined, req);
175+
// let itemsTree = AbstractCatalog.buildTree(searchResult);
176+
// console.log(itemsTree)
177+
return FrontendCatalogUtils.treeToNode(searchResult, this.catalog.getGroupType().type);
178+
}
179+
180+
async updateTree(data: RequestData, req: ReqType): Promise<any> {
181+
// console.dir(data, {depth: null})
182+
// return
183+
let reqParent = data.reqParent;
184+
if (reqParent.data.id === 0) reqParent.data.id = null;
185+
186+
// Update all items into parent (for two reason: update parent, updare sorting order)
187+
let sortCount = 0;
188+
for (const childNode of reqParent.children) {
189+
childNode.data.sortOrder = sortCount;
190+
childNode.data.parentId = reqParent.data.id
191+
if (childNode.data.id === 0) childNode.data.id = null;
192+
await this.catalog.updateItem(childNode.data.id, childNode.data.type, childNode.data, req);
193+
sortCount++;
194+
}
195+
return Promise.resolve('ok')
196+
}
197+
198+
199+
async updateItem(item: any, modelId: string, data: any, req: ReqType) {
200+
//TODO It's not clear why it's here.
201+
//data = VueCatalogUtils.refinement(data);
202+
// if (this.catalog.slug !== "navigation") {
203+
// return await this.catalog.updateModelItems(data.modelId, data.type, data.record, req);
204+
// } else {
205+
let normalizedModelId = modelId;
206+
if (normalizedModelId === '0') normalizedModelId = null;
207+
return await this.catalog.updateModelItems(normalizedModelId, item.type, data, req);
208+
// }
209+
}
210+
211+
async deleteItem(item: Item, req: ReqType): Promise<{ ok: boolean }> {
212+
if (item.id === 0) item.id = null;
213+
// Получаем всех непосредственных потомков текущего элемента
214+
const children = await this.catalog.getChilds(item.id, undefined, req);
215+
216+
// Рекурсивно удаляем всех потомков
217+
for (const child of children) {
218+
await this.deleteItem(child, req);
219+
}
220+
221+
// После удаления всех потомков удаляем сам элемент
222+
await this.catalog.deleteItem(item.type, item.id, req);
223+
224+
return {ok: true};
225+
}
226+
}
227+
228+
export class FrontendCatalogUtils {
229+
/**
230+
* Removes unnecessary data from the front
231+
*/
232+
public static refinement<T extends NodeModel<any>>(nodeModel: T) {
233+
return nodeModel.data;
234+
}
235+
236+
/**
237+
* Normalizes data for frontend: replaces null parentId with 0
238+
*/
239+
public static normalizeForFrontend<T extends Item>(item: T): T {
240+
return { ...item, parentId: item.parentId === null ? 0 : item.parentId };
241+
}
242+
243+
public static arrayToNode<T extends Item>(items: T[], groupTypeName: string): NodeModel<T>[] {
244+
return items.map(node => FrontendCatalogUtils.toNode(node, groupTypeName));
245+
}
246+
247+
public static toNode<T extends NodeData>(data: T, groupTypeName: string): NodeModel<T> {
248+
const normalizedData = FrontendCatalogUtils.normalizeForFrontend(data);
249+
return {
250+
data: normalizedData,
251+
droppable: data.type === groupTypeName,
252+
id: data.id as string,
253+
text: data.name,
254+
parent: (data.parentId === null ? 0 : data.parentId as number),
255+
};
256+
}
257+
258+
public static expandTo<T extends NodeData>(frontendCatalogData: NodeModel<T>, theseItemIdsNeedToBeOpened: (string | number)[]): NodeModel<T> {
259+
function expand(node: NodeModel<T>): void {
260+
if (theseItemIdsNeedToBeOpened.includes(node.data.id)) {
261+
// node.isExpanded = true;
262+
}
263+
264+
if (node.children) {
265+
for (const child of node.children) {
266+
expand(child);
267+
}
268+
}
269+
}
270+
271+
theseItemIdsNeedToBeOpened.forEach(id => {
272+
expand(frontendCatalogData);
273+
});
274+
275+
return frontendCatalogData;
276+
}
277+
278+
public static treeToNode(tree: Item[], groupTypeName: string): NodeModel<Item>[] {
279+
function buildNodes(items: Item[]): NodeModel<Item>[] {
280+
return items.map(item => {
281+
const node = FrontendCatalogUtils.toNode(item, groupTypeName);
282+
if (item.childs && item.childs.length > 0) {
283+
// Sort the children before building their nodes
284+
item.childs.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
285+
node.children = buildNodes(item.childs);
286+
// node.isExpanded = !node.droppable;
287+
}
288+
return node;
289+
});
290+
}
291+
292+
return buildNodes(tree);
293+
}
294+
}
295+

0 commit comments

Comments
 (0)