adminizer 4.3.0-build.118 → 4.3.0-build.120
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/controllers/catalog/Catalog.js +21 -20
- package/controllers/catalog/FrontendCatalogAdapter.d.ts +82 -0
- package/controllers/catalog/FrontendCatalogAdapter.js +228 -0
- package/controllers/catalog/FrontentCatalogAdapter.d.ts +2 -2
- package/controllers/catalog/FrontentCatalogAdapter.js +27 -10
- package/interfaces/adminpanelConfig.d.ts +0 -1
- package/lib/Adminizer.d.ts +2 -0
- package/lib/Adminizer.js +2 -8
- package/lib/catalog/AbstractCatalog.d.ts +2 -0
- package/lib/catalog/AbstractCatalog.js +5 -4
- package/lib/catalog/Navigation.d.ts +8 -5
- package/lib/catalog/Navigation.js +48 -34
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FrontendCatalog, FrontendCatalogUtils } from "./FrontendCatalogAdapter";
|
|
2
2
|
import { Adminizer } from "../../lib/Adminizer";
|
|
3
3
|
export async function catalogController(req, res) {
|
|
4
4
|
const slug = req.params.slug;
|
|
@@ -34,7 +34,7 @@ export async function catalogController(req, res) {
|
|
|
34
34
|
}
|
|
35
35
|
if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
|
|
36
36
|
const data = req.body;
|
|
37
|
-
const frontendCatalog = new
|
|
37
|
+
const frontendCatalog = new FrontendCatalog(_catalog);
|
|
38
38
|
if (!frontendCatalog)
|
|
39
39
|
return res.status(404);
|
|
40
40
|
frontendCatalog.setId(id);
|
|
@@ -46,24 +46,24 @@ export async function catalogController(req, res) {
|
|
|
46
46
|
return res.json(await frontendCatalog.getAddTemplate(item, req));
|
|
47
47
|
case 'getEditTemplate':
|
|
48
48
|
return res.json(await frontendCatalog.getEditTemplate(item, data.id, req, data.modelId));
|
|
49
|
-
case 'getCatalog':
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
49
|
+
case 'getCatalog': {
|
|
50
|
+
const __catalog = await frontendCatalog.getCatalog();
|
|
51
|
+
return res.json({
|
|
52
|
+
items: frontendCatalog.getitemTypes(),
|
|
53
|
+
catalog: {
|
|
54
|
+
nodes: __catalog,
|
|
55
|
+
movingGroupsRootOnly: _catalog.movingGroupsRootOnly ?? false,
|
|
56
|
+
catalogName: _catalog.name,
|
|
57
|
+
catalogId: _catalog.id,
|
|
58
|
+
catalogSlug: _catalog.slug,
|
|
59
|
+
idList: idList
|
|
60
|
+
},
|
|
61
|
+
toolsActions: await frontendCatalog.getActions([], 'tools')
|
|
62
|
+
});
|
|
63
|
+
}
|
|
65
64
|
case 'createItem':
|
|
66
|
-
|
|
65
|
+
const createdItem = await frontendCatalog.createItem(data.data, req);
|
|
66
|
+
return res.json({ 'data': FrontendCatalogUtils.normalizeForFrontend(createdItem) });
|
|
67
67
|
case 'getChilds':
|
|
68
68
|
return res.json({ data: await frontendCatalog.getChilds(data.data, req) });
|
|
69
69
|
case 'getActions':
|
|
@@ -85,7 +85,8 @@ export async function catalogController(req, res) {
|
|
|
85
85
|
case 'getPopUpTemplate':
|
|
86
86
|
return res.json({ data: await frontendCatalog.getPopUpTemplate(data.actionId, req) });
|
|
87
87
|
case 'updateItem':
|
|
88
|
-
|
|
88
|
+
const updatedItem = await frontendCatalog.updateItem(item, data.modelId, data.data, req);
|
|
89
|
+
return res.json({ data: FrontendCatalogUtils.normalizeForFrontend(updatedItem) });
|
|
89
90
|
}
|
|
90
91
|
break;
|
|
91
92
|
case 'DELETE':
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { AbstractCatalog, Item } from "../../lib/catalog/AbstractCatalog";
|
|
2
|
+
interface NodeModel<TDataType> {
|
|
3
|
+
text: string;
|
|
4
|
+
droppable: boolean;
|
|
5
|
+
id: string;
|
|
6
|
+
parent: number;
|
|
7
|
+
data?: TDataType;
|
|
8
|
+
children?: NodeModel<TDataType>[];
|
|
9
|
+
isSelected?: boolean;
|
|
10
|
+
isVisible?: boolean;
|
|
11
|
+
isDraggable?: boolean;
|
|
12
|
+
isSelectable?: boolean;
|
|
13
|
+
path?: number[];
|
|
14
|
+
pathStr?: string;
|
|
15
|
+
level?: number;
|
|
16
|
+
isFirstChild?: boolean;
|
|
17
|
+
isLastChild?: boolean;
|
|
18
|
+
}
|
|
19
|
+
interface NodeData extends Item {
|
|
20
|
+
}
|
|
21
|
+
interface RequestData {
|
|
22
|
+
reqNode: NodeModel<NodeData>[];
|
|
23
|
+
reqParent: NodeModel<NodeData>;
|
|
24
|
+
_method: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Now is React
|
|
28
|
+
* // TODO: refactor for react name
|
|
29
|
+
*/
|
|
30
|
+
export declare class FrontendCatalog {
|
|
31
|
+
catalog: AbstractCatalog;
|
|
32
|
+
constructor(_catalog: AbstractCatalog);
|
|
33
|
+
setId(id: string): void;
|
|
34
|
+
getItemType(type: string): import("../../lib/catalog/AbstractCatalog").BaseItem<Item>;
|
|
35
|
+
getAddTemplate(item: any, req: ReqType): Promise<{
|
|
36
|
+
type: "component" | "navigation.group" | "navigation.link" | "model";
|
|
37
|
+
data: any;
|
|
38
|
+
}>;
|
|
39
|
+
getEditTemplate(item: any, id: string | number, req: ReqType, modelId: string | number): Promise<{
|
|
40
|
+
type: "component" | "navigation.group" | "navigation.link" | "model";
|
|
41
|
+
data: any;
|
|
42
|
+
}>;
|
|
43
|
+
getitemTypes(): {
|
|
44
|
+
type: string;
|
|
45
|
+
name: string;
|
|
46
|
+
isGroup: boolean;
|
|
47
|
+
allowedRoot: boolean;
|
|
48
|
+
icon: string;
|
|
49
|
+
actionHandlers: import("../../lib/catalog/AbstractCatalog").ActionHandler[];
|
|
50
|
+
}[];
|
|
51
|
+
getLocales(req: ReqType): {
|
|
52
|
+
[key: string]: string;
|
|
53
|
+
};
|
|
54
|
+
getActions(items: NodeModel<any>[], type: string): Promise<import("../../lib/catalog/AbstractCatalog").ActionHandler[]>;
|
|
55
|
+
handleAction(actionId: string, items: any[], data: any, req: ReqType): Promise<string | void>;
|
|
56
|
+
getPopUpTemplate(actionId: string, req: ReqType): Promise<string>;
|
|
57
|
+
getLink(actionId: string): Promise<string>;
|
|
58
|
+
getCatalog(): Promise<NodeModel<Item>[]>;
|
|
59
|
+
createItem(data: any, req: ReqType): Promise<any>;
|
|
60
|
+
getChilds(data: any, req: ReqType): Promise<NodeModel<Item>[]>;
|
|
61
|
+
search(s: string, req: ReqType): Promise<NodeModel<Item>[]>;
|
|
62
|
+
updateTree(data: RequestData, req: ReqType): Promise<any>;
|
|
63
|
+
updateItem(item: any, modelId: string, data: any, req: ReqType): Promise<any>;
|
|
64
|
+
deleteItem(item: Item, req: ReqType): Promise<{
|
|
65
|
+
ok: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
}
|
|
68
|
+
export declare class FrontendCatalogUtils {
|
|
69
|
+
/**
|
|
70
|
+
* Removes unnecessary data from the front
|
|
71
|
+
*/
|
|
72
|
+
static refinement<T extends NodeModel<any>>(nodeModel: T): any;
|
|
73
|
+
/**
|
|
74
|
+
* Normalizes data for frontend: replaces null parentId with 0
|
|
75
|
+
*/
|
|
76
|
+
static normalizeForFrontend<T extends Item>(item: T): T;
|
|
77
|
+
static arrayToNode<T extends Item>(items: T[], groupTypeName: string): NodeModel<T>[];
|
|
78
|
+
static toNode<T extends NodeData>(data: T, groupTypeName: string): NodeModel<T>;
|
|
79
|
+
static expandTo<T extends NodeData>(frontendCatalogData: NodeModel<T>, theseItemIdsNeedToBeOpened: (string | number)[]): NodeModel<T>;
|
|
80
|
+
static treeToNode(tree: Item[], groupTypeName: string): NodeModel<Item>[];
|
|
81
|
+
}
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated Now is React
|
|
3
|
+
* // TODO: refactor for react name
|
|
4
|
+
*/
|
|
5
|
+
export class FrontendCatalog {
|
|
6
|
+
catalog;
|
|
7
|
+
constructor(_catalog) {
|
|
8
|
+
this.catalog = _catalog;
|
|
9
|
+
}
|
|
10
|
+
setId(id) {
|
|
11
|
+
this.catalog.setId(id);
|
|
12
|
+
}
|
|
13
|
+
getItemType(type) {
|
|
14
|
+
return this.catalog.getItemType(type);
|
|
15
|
+
}
|
|
16
|
+
getAddTemplate(item, req) {
|
|
17
|
+
return this.catalog.getAddTemplate(item, req);
|
|
18
|
+
}
|
|
19
|
+
getEditTemplate(item, id, req, modelId) {
|
|
20
|
+
return this.catalog.getEditTemplate(item, id, req, modelId);
|
|
21
|
+
}
|
|
22
|
+
getitemTypes() {
|
|
23
|
+
return this.catalog.getitemTypes();
|
|
24
|
+
}
|
|
25
|
+
getLocales(req) {
|
|
26
|
+
let obj = {
|
|
27
|
+
"Delete": "",
|
|
28
|
+
"Edit": "",
|
|
29
|
+
"create": "",
|
|
30
|
+
"Search": "",
|
|
31
|
+
"Select Item type": "",
|
|
32
|
+
"Select Items": "",
|
|
33
|
+
"Save": "",
|
|
34
|
+
"No": "",
|
|
35
|
+
"Are you sure?": "",
|
|
36
|
+
"Yes": "",
|
|
37
|
+
"Select Ids": "",
|
|
38
|
+
"OR": "",
|
|
39
|
+
"Open in a new window": "",
|
|
40
|
+
"Visible": "",
|
|
41
|
+
"Clean": "",
|
|
42
|
+
"Performing an action...": "",
|
|
43
|
+
"Action completed": "",
|
|
44
|
+
};
|
|
45
|
+
obj[this.catalog.name] = "";
|
|
46
|
+
for (const actionHandler of this.catalog.actionHandlers) {
|
|
47
|
+
obj[actionHandler.name] = "";
|
|
48
|
+
}
|
|
49
|
+
let messages = obj;
|
|
50
|
+
let outMessages = {};
|
|
51
|
+
for (const mess of Object.keys(messages)) {
|
|
52
|
+
outMessages[mess] = req.i18n.__(mess);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
...outMessages,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async getActions(items, type) {
|
|
59
|
+
let arrItems = [];
|
|
60
|
+
for (const item of items) {
|
|
61
|
+
if (item.data.id === 0)
|
|
62
|
+
item.data.id = null;
|
|
63
|
+
arrItems.push(await this.catalog.find(item.data));
|
|
64
|
+
}
|
|
65
|
+
console.log(arrItems);
|
|
66
|
+
if (type === 'tools') {
|
|
67
|
+
return (await this.catalog.getActions(arrItems))?.filter(e => e.displayTool);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return (await this.catalog.getActions(arrItems))?.filter(e => e.displayContext);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async handleAction(actionId, items, data, req) {
|
|
74
|
+
let arrItems = [];
|
|
75
|
+
for (const item of items) {
|
|
76
|
+
if (item.data.id === 0)
|
|
77
|
+
item.data.id = null;
|
|
78
|
+
arrItems.push(await this.catalog.find(item.data));
|
|
79
|
+
}
|
|
80
|
+
console.log(arrItems);
|
|
81
|
+
return this.catalog.handleAction(actionId, arrItems, data, req);
|
|
82
|
+
}
|
|
83
|
+
async getPopUpTemplate(actionId, req) {
|
|
84
|
+
return this.catalog.getPopUpTemplate(actionId, req);
|
|
85
|
+
}
|
|
86
|
+
async getLink(actionId) {
|
|
87
|
+
return this.catalog.getLink(actionId);
|
|
88
|
+
}
|
|
89
|
+
//Below are the methods that require action
|
|
90
|
+
async getCatalog() {
|
|
91
|
+
let rootItems = await this.catalog.getChilds(null);
|
|
92
|
+
return FrontendCatalogUtils.arrayToNode(rootItems, this.catalog.getGroupType().type);
|
|
93
|
+
}
|
|
94
|
+
async createItem(data, req) {
|
|
95
|
+
// TODO It's not clear why it's here.
|
|
96
|
+
//data = VueCatalogUtils.refinement(data);
|
|
97
|
+
// if (this.catalog.slug !== "navigation") {
|
|
98
|
+
// let item = data.record;
|
|
99
|
+
// item.parenId = data.parenId;
|
|
100
|
+
// return await this.catalog.createItem(item, req);
|
|
101
|
+
// } else {
|
|
102
|
+
if (data.parentId === 0)
|
|
103
|
+
data.parentId = null;
|
|
104
|
+
return await this.catalog.createItem(data, req);
|
|
105
|
+
// }
|
|
106
|
+
}
|
|
107
|
+
async getChilds(data, req) {
|
|
108
|
+
data = FrontendCatalogUtils.refinement(data);
|
|
109
|
+
if (!data || data.id === 0 || data.id === undefined) {
|
|
110
|
+
data = { id: null };
|
|
111
|
+
}
|
|
112
|
+
if (data.id === 0)
|
|
113
|
+
data.id = null;
|
|
114
|
+
return FrontendCatalogUtils.arrayToNode(await this.catalog.getChilds(data.id, undefined, req), this.catalog.getGroupType().type);
|
|
115
|
+
}
|
|
116
|
+
// Moved into actions
|
|
117
|
+
// getCreatedItems(data: any) {
|
|
118
|
+
// data = VueCatalogUtils.refinement(data);
|
|
119
|
+
// return this.catalog.getChilds(data.id);
|
|
120
|
+
// }
|
|
121
|
+
async search(s, req) {
|
|
122
|
+
let searchResult = await this.catalog.search(s, undefined, req);
|
|
123
|
+
// let itemsTree = AbstractCatalog.buildTree(searchResult);
|
|
124
|
+
// console.log(itemsTree)
|
|
125
|
+
return FrontendCatalogUtils.treeToNode(searchResult, this.catalog.getGroupType().type);
|
|
126
|
+
}
|
|
127
|
+
async updateTree(data, req) {
|
|
128
|
+
// console.dir(data, {depth: null})
|
|
129
|
+
// return
|
|
130
|
+
let reqParent = data.reqParent;
|
|
131
|
+
if (reqParent.data.id === 0)
|
|
132
|
+
reqParent.data.id = null;
|
|
133
|
+
// Update all items into parent (for two reason: update parent, updare sorting order)
|
|
134
|
+
let sortCount = 0;
|
|
135
|
+
for (const childNode of reqParent.children) {
|
|
136
|
+
childNode.data.sortOrder = sortCount;
|
|
137
|
+
childNode.data.parentId = reqParent.data.id;
|
|
138
|
+
if (childNode.data.id === 0)
|
|
139
|
+
childNode.data.id = null;
|
|
140
|
+
await this.catalog.updateItem(childNode.data.id, childNode.data.type, childNode.data, req);
|
|
141
|
+
sortCount++;
|
|
142
|
+
}
|
|
143
|
+
return Promise.resolve('ok');
|
|
144
|
+
}
|
|
145
|
+
async updateItem(item, modelId, data, req) {
|
|
146
|
+
//TODO It's not clear why it's here.
|
|
147
|
+
//data = VueCatalogUtils.refinement(data);
|
|
148
|
+
// if (this.catalog.slug !== "navigation") {
|
|
149
|
+
// return await this.catalog.updateModelItems(data.modelId, data.type, data.record, req);
|
|
150
|
+
// } else {
|
|
151
|
+
let normalizedModelId = modelId;
|
|
152
|
+
if (normalizedModelId === '0')
|
|
153
|
+
normalizedModelId = null;
|
|
154
|
+
return await this.catalog.updateModelItems(normalizedModelId, item.type, data, req);
|
|
155
|
+
// }
|
|
156
|
+
}
|
|
157
|
+
async deleteItem(item, req) {
|
|
158
|
+
if (item.id === 0)
|
|
159
|
+
item.id = null;
|
|
160
|
+
// Получаем всех непосредственных потомков текущего элемента
|
|
161
|
+
const children = await this.catalog.getChilds(item.id, undefined, req);
|
|
162
|
+
// Рекурсивно удаляем всех потомков
|
|
163
|
+
for (const child of children) {
|
|
164
|
+
await this.deleteItem(child, req);
|
|
165
|
+
}
|
|
166
|
+
// После удаления всех потомков удаляем сам элемент
|
|
167
|
+
await this.catalog.deleteItem(item.type, item.id, req);
|
|
168
|
+
return { ok: true };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export class FrontendCatalogUtils {
|
|
172
|
+
/**
|
|
173
|
+
* Removes unnecessary data from the front
|
|
174
|
+
*/
|
|
175
|
+
static refinement(nodeModel) {
|
|
176
|
+
return nodeModel.data;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Normalizes data for frontend: replaces null parentId with 0
|
|
180
|
+
*/
|
|
181
|
+
static normalizeForFrontend(item) {
|
|
182
|
+
return { ...item, parentId: item.parentId === null ? 0 : item.parentId };
|
|
183
|
+
}
|
|
184
|
+
static arrayToNode(items, groupTypeName) {
|
|
185
|
+
return items.map(node => FrontendCatalogUtils.toNode(node, groupTypeName));
|
|
186
|
+
}
|
|
187
|
+
static toNode(data, groupTypeName) {
|
|
188
|
+
const normalizedData = FrontendCatalogUtils.normalizeForFrontend(data);
|
|
189
|
+
return {
|
|
190
|
+
data: normalizedData,
|
|
191
|
+
droppable: data.type === groupTypeName,
|
|
192
|
+
id: data.id,
|
|
193
|
+
text: data.name,
|
|
194
|
+
parent: (data.parentId === null ? 0 : data.parentId),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
static expandTo(frontendCatalogData, theseItemIdsNeedToBeOpened) {
|
|
198
|
+
function expand(node) {
|
|
199
|
+
if (theseItemIdsNeedToBeOpened.includes(node.data.id)) {
|
|
200
|
+
// node.isExpanded = true;
|
|
201
|
+
}
|
|
202
|
+
if (node.children) {
|
|
203
|
+
for (const child of node.children) {
|
|
204
|
+
expand(child);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
theseItemIdsNeedToBeOpened.forEach(id => {
|
|
209
|
+
expand(frontendCatalogData);
|
|
210
|
+
});
|
|
211
|
+
return frontendCatalogData;
|
|
212
|
+
}
|
|
213
|
+
static treeToNode(tree, groupTypeName) {
|
|
214
|
+
function buildNodes(items) {
|
|
215
|
+
return items.map(item => {
|
|
216
|
+
const node = FrontendCatalogUtils.toNode(item, groupTypeName);
|
|
217
|
+
if (item.childs && item.childs.length > 0) {
|
|
218
|
+
// Sort the children before building their nodes
|
|
219
|
+
item.childs.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
|
220
|
+
node.children = buildNodes(item.childs);
|
|
221
|
+
// node.isExpanded = !node.droppable;
|
|
222
|
+
}
|
|
223
|
+
return node;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return buildNodes(tree);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -27,7 +27,7 @@ interface RequestData {
|
|
|
27
27
|
* @deprecated Now is React
|
|
28
28
|
* // TODO: refactor for react name
|
|
29
29
|
*/
|
|
30
|
-
export declare class
|
|
30
|
+
export declare class FrontendCatalog {
|
|
31
31
|
catalog: AbstractCatalog;
|
|
32
32
|
constructor(_catalog: AbstractCatalog);
|
|
33
33
|
setId(id: string): void;
|
|
@@ -65,7 +65,7 @@ export declare class VueCatalog {
|
|
|
65
65
|
ok: boolean;
|
|
66
66
|
}>;
|
|
67
67
|
}
|
|
68
|
-
export declare class
|
|
68
|
+
export declare class FrontendCatalogUtils {
|
|
69
69
|
/**
|
|
70
70
|
* Removes unnecessary data from the front
|
|
71
71
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @deprecated Now is React
|
|
3
3
|
* // TODO: refactor for react name
|
|
4
4
|
*/
|
|
5
|
-
export class
|
|
5
|
+
export class FrontendCatalog {
|
|
6
6
|
catalog;
|
|
7
7
|
constructor(_catalog) {
|
|
8
8
|
this.catalog = _catalog;
|
|
@@ -58,6 +58,8 @@ export class VueCatalog {
|
|
|
58
58
|
async getActions(items, type) {
|
|
59
59
|
let arrItems = [];
|
|
60
60
|
for (const item of items) {
|
|
61
|
+
if (item.data.id === 0)
|
|
62
|
+
item.data.id = null;
|
|
61
63
|
arrItems.push(await this.catalog.find(item.data));
|
|
62
64
|
}
|
|
63
65
|
console.log(arrItems);
|
|
@@ -71,6 +73,8 @@ export class VueCatalog {
|
|
|
71
73
|
async handleAction(actionId, items, data, req) {
|
|
72
74
|
let arrItems = [];
|
|
73
75
|
for (const item of items) {
|
|
76
|
+
if (item.data.id === 0)
|
|
77
|
+
item.data.id = null;
|
|
74
78
|
arrItems.push(await this.catalog.find(item.data));
|
|
75
79
|
}
|
|
76
80
|
console.log(arrItems);
|
|
@@ -84,8 +88,8 @@ export class VueCatalog {
|
|
|
84
88
|
}
|
|
85
89
|
//Below are the methods that require action
|
|
86
90
|
async getCatalog() {
|
|
87
|
-
let rootItems = await this.catalog.getChilds(
|
|
88
|
-
return
|
|
91
|
+
let rootItems = await this.catalog.getChilds(null);
|
|
92
|
+
return FrontendCatalogUtils.arrayToNode(rootItems, this.catalog.getGroupType().type);
|
|
89
93
|
}
|
|
90
94
|
async createItem(data, req) {
|
|
91
95
|
// TODO It's not clear why it's here.
|
|
@@ -95,12 +99,16 @@ export class VueCatalog {
|
|
|
95
99
|
// item.parenId = data.parenId;
|
|
96
100
|
// return await this.catalog.createItem(item, req);
|
|
97
101
|
// } else {
|
|
102
|
+
if (data.parentId === 0)
|
|
103
|
+
data.parentId = null;
|
|
98
104
|
return await this.catalog.createItem(data, req);
|
|
99
105
|
// }
|
|
100
106
|
}
|
|
101
107
|
async getChilds(data, req) {
|
|
102
|
-
data =
|
|
103
|
-
|
|
108
|
+
data = FrontendCatalogUtils.refinement(data);
|
|
109
|
+
if (data.id === 0)
|
|
110
|
+
data.id = null;
|
|
111
|
+
return FrontendCatalogUtils.arrayToNode(await this.catalog.getChilds(data.id, undefined, req), this.catalog.getGroupType().type);
|
|
104
112
|
}
|
|
105
113
|
// Moved into actions
|
|
106
114
|
// getCreatedItems(data: any) {
|
|
@@ -111,17 +119,21 @@ export class VueCatalog {
|
|
|
111
119
|
let searchResult = await this.catalog.search(s, undefined, req);
|
|
112
120
|
// let itemsTree = AbstractCatalog.buildTree(searchResult);
|
|
113
121
|
// console.log(itemsTree)
|
|
114
|
-
return
|
|
122
|
+
return FrontendCatalogUtils.treeToNode(searchResult, this.catalog.getGroupType().type);
|
|
115
123
|
}
|
|
116
124
|
async updateTree(data, req) {
|
|
117
125
|
// console.dir(data, {depth: null})
|
|
118
126
|
// return
|
|
119
127
|
let reqParent = data.reqParent;
|
|
128
|
+
if (reqParent.data.id === 0)
|
|
129
|
+
reqParent.data.id = null;
|
|
120
130
|
// Update all items into parent (for two reason: update parent, updare sorting order)
|
|
121
131
|
let sortCount = 0;
|
|
122
132
|
for (const childNode of reqParent.children) {
|
|
123
133
|
childNode.data.sortOrder = sortCount;
|
|
124
134
|
childNode.data.parentId = reqParent.data.id;
|
|
135
|
+
if (childNode.data.id === 0)
|
|
136
|
+
childNode.data.id = null;
|
|
125
137
|
await this.catalog.updateItem(childNode.data.id, childNode.data.type, childNode.data, req);
|
|
126
138
|
sortCount++;
|
|
127
139
|
}
|
|
@@ -133,10 +145,15 @@ export class VueCatalog {
|
|
|
133
145
|
// if (this.catalog.slug !== "navigation") {
|
|
134
146
|
// return await this.catalog.updateModelItems(data.modelId, data.type, data.record, req);
|
|
135
147
|
// } else {
|
|
136
|
-
|
|
148
|
+
let normalizedModelId = modelId;
|
|
149
|
+
if (normalizedModelId === '0')
|
|
150
|
+
normalizedModelId = null;
|
|
151
|
+
return await this.catalog.updateModelItems(normalizedModelId, item.type, data, req);
|
|
137
152
|
// }
|
|
138
153
|
}
|
|
139
154
|
async deleteItem(item, req) {
|
|
155
|
+
if (item.id === 0)
|
|
156
|
+
item.id = null;
|
|
140
157
|
// Получаем всех непосредственных потомков текущего элемента
|
|
141
158
|
const children = await this.catalog.getChilds(item.id, undefined, req);
|
|
142
159
|
// Рекурсивно удаляем всех потомков
|
|
@@ -148,7 +165,7 @@ export class VueCatalog {
|
|
|
148
165
|
return { ok: true };
|
|
149
166
|
}
|
|
150
167
|
}
|
|
151
|
-
export class
|
|
168
|
+
export class FrontendCatalogUtils {
|
|
152
169
|
/**
|
|
153
170
|
* Removes unnecessary data from the front
|
|
154
171
|
*/
|
|
@@ -156,7 +173,7 @@ export class VueCatalogUtils {
|
|
|
156
173
|
return nodeModel.data;
|
|
157
174
|
}
|
|
158
175
|
static arrayToNode(items, groupTypeName) {
|
|
159
|
-
return items.map(node =>
|
|
176
|
+
return items.map(node => FrontendCatalogUtils.toNode(node, groupTypeName));
|
|
160
177
|
}
|
|
161
178
|
static toNode(data, groupTypeName) {
|
|
162
179
|
return {
|
|
@@ -186,7 +203,7 @@ export class VueCatalogUtils {
|
|
|
186
203
|
static treeToNode(tree, groupTypeName) {
|
|
187
204
|
function buildNodes(items) {
|
|
188
205
|
return items.map(item => {
|
|
189
|
-
const node =
|
|
206
|
+
const node = FrontendCatalogUtils.toNode(item, groupTypeName);
|
|
190
207
|
if (item.childs && item.childs.length > 0) {
|
|
191
208
|
// Sort the children before building their nodes
|
|
192
209
|
item.childs.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
package/lib/Adminizer.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { CatalogHandler } from "./catalog/CatalogHandler";
|
|
|
15
15
|
import { NotificationHandler } from './notifications/NotificationHandler';
|
|
16
16
|
import { INotification } from "../interfaces/types";
|
|
17
17
|
import { MediaManagerHandler } from "./media-manager/MediaManagerHandler";
|
|
18
|
+
import { StorageServices } from "./catalog/Navigation";
|
|
18
19
|
export declare class Adminizer {
|
|
19
20
|
/**
|
|
20
21
|
* If you convey this default Middleware, it will add it to the very top of the router,
|
|
@@ -36,6 +37,7 @@ export declare class Adminizer {
|
|
|
36
37
|
controlsHandler: ControlsHandler;
|
|
37
38
|
catalogHandler: CatalogHandler;
|
|
38
39
|
mediaManagerHandler: MediaManagerHandler;
|
|
40
|
+
storageServices: StorageServices;
|
|
39
41
|
jwtSecret: string;
|
|
40
42
|
static logger: winston.Logger;
|
|
41
43
|
constructor(ormAdapters: AbstractAdapter[]);
|
package/lib/Adminizer.js
CHANGED
|
@@ -54,6 +54,7 @@ export class Adminizer {
|
|
|
54
54
|
controlsHandler;
|
|
55
55
|
catalogHandler;
|
|
56
56
|
mediaManagerHandler;
|
|
57
|
+
storageServices;
|
|
57
58
|
// Constants
|
|
58
59
|
jwtSecret = process.env.JWT_SECRET ?? uuid();
|
|
59
60
|
static logger = winston.createLogger({
|
|
@@ -208,14 +209,7 @@ export class Adminizer {
|
|
|
208
209
|
}
|
|
209
210
|
}
|
|
210
211
|
await bindDashboardWidgets(this);
|
|
211
|
-
|
|
212
|
-
this._emitter.on('isSeeding', async () => {
|
|
213
|
-
await bindNavigation(this);
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
await bindNavigation(this);
|
|
218
|
-
}
|
|
212
|
+
await bindNavigation(this);
|
|
219
213
|
bindMediaManager(this);
|
|
220
214
|
await bindAccessRights(this);
|
|
221
215
|
if (I18n.appendLocale) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Adminizer } from "../Adminizer";
|
|
2
|
+
import { StorageServices } from "./Navigation";
|
|
2
3
|
/**
|
|
3
4
|
* Interface `Item` describes the data that the UI will operate on
|
|
4
5
|
* This is a common interface for all data that is linked to the catalog
|
|
@@ -24,6 +25,7 @@ export type _Item_ = {
|
|
|
24
25
|
* General Item structure that will be available for all elements, including groups
|
|
25
26
|
*/
|
|
26
27
|
export declare abstract class BaseItem<T extends Item> {
|
|
28
|
+
storageServices?: StorageServices;
|
|
27
29
|
abstract readonly type: string;
|
|
28
30
|
/**
|
|
29
31
|
* Used for infer T
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* General Item structure that will be available for all elements, including groups
|
|
3
3
|
*/
|
|
4
4
|
export class BaseItem {
|
|
5
|
+
storageServices;
|
|
5
6
|
/**
|
|
6
7
|
* Array of all global contexts, which will appear for all elements
|
|
7
8
|
*/
|
|
@@ -253,7 +254,7 @@ export class AbstractCatalog {
|
|
|
253
254
|
* Method for getting group elements
|
|
254
255
|
*/
|
|
255
256
|
getitemTypes() {
|
|
256
|
-
return this.itemTypes.map(({ adminizer, ...rest }) => rest);
|
|
257
|
+
return this.itemTypes.map(({ adminizer, storageServices, ...rest }) => rest);
|
|
257
258
|
}
|
|
258
259
|
;
|
|
259
260
|
async search(s, hasExtras = true, req) {
|
|
@@ -268,7 +269,7 @@ export class AbstractCatalog {
|
|
|
268
269
|
const extras = await this.getChilds(item.id, undefined, req);
|
|
269
270
|
accumulator.push(...extras);
|
|
270
271
|
}
|
|
271
|
-
if (item.parentId ===
|
|
272
|
+
if (item.parentId === null)
|
|
272
273
|
return item;
|
|
273
274
|
const parentItem = await groupType._find(item.parentId, this.id);
|
|
274
275
|
if (parentItem) {
|
|
@@ -284,7 +285,7 @@ export class AbstractCatalog {
|
|
|
284
285
|
foundItems = foundItems.concat(items);
|
|
285
286
|
}
|
|
286
287
|
for (const item of foundItems) {
|
|
287
|
-
if (item.parentId !==
|
|
288
|
+
if (item.parentId !== null) { // changed from 0 to null
|
|
288
289
|
await buildTreeUpwards(item, hasExtras);
|
|
289
290
|
}
|
|
290
291
|
}
|
|
@@ -310,7 +311,7 @@ export class AbstractCatalog {
|
|
|
310
311
|
itemMap[item.id] = item;
|
|
311
312
|
});
|
|
312
313
|
items.forEach(item => {
|
|
313
|
-
if (item.parentId ===
|
|
314
|
+
if (item.parentId === null) {
|
|
314
315
|
tree.push(item);
|
|
315
316
|
}
|
|
316
317
|
else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AbstractCatalog, ActionHandler, Item } from "./AbstractCatalog";
|
|
2
2
|
import { NavigationConfig } from "../../interfaces/adminpanelConfig";
|
|
3
3
|
import { Adminizer } from "../Adminizer";
|
|
4
|
-
|
|
4
|
+
interface NavItem extends Item {
|
|
5
5
|
urlPath?: string;
|
|
6
6
|
modelId?: string | number;
|
|
7
7
|
targetBlank?: boolean;
|
|
@@ -27,10 +27,11 @@ export declare class StorageService {
|
|
|
27
27
|
search(s: string, type: string): Promise<NavItem[]>;
|
|
28
28
|
}
|
|
29
29
|
export declare class StorageServices {
|
|
30
|
-
protected
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
protected storages: StorageService[];
|
|
31
|
+
constructor();
|
|
32
|
+
add(storage: StorageService): void;
|
|
33
|
+
get(id: string): StorageService;
|
|
34
|
+
getAll(): StorageService[];
|
|
34
35
|
}
|
|
35
36
|
export declare class Navigation extends AbstractCatalog {
|
|
36
37
|
readonly name: string;
|
|
@@ -38,6 +39,8 @@ export declare class Navigation extends AbstractCatalog {
|
|
|
38
39
|
readonly icon: string;
|
|
39
40
|
readonly actionHandlers: ActionHandler[];
|
|
40
41
|
idList: string[];
|
|
42
|
+
storageServices: StorageServices;
|
|
41
43
|
constructor(adminizer: Adminizer, config: NavigationConfig);
|
|
42
44
|
getIdList(): Promise<string[]>;
|
|
43
45
|
}
|
|
46
|
+
export {};
|
|
@@ -30,7 +30,7 @@ export class StorageService {
|
|
|
30
30
|
return this.id;
|
|
31
31
|
}
|
|
32
32
|
async buildTree() {
|
|
33
|
-
const rootElements = await this.findElementsByParentId(
|
|
33
|
+
const rootElements = await this.findElementsByParentId(null, null);
|
|
34
34
|
const buildSubTree = async (elements) => {
|
|
35
35
|
const tree = [];
|
|
36
36
|
for (const element of elements) {
|
|
@@ -56,9 +56,9 @@ export class StorageService {
|
|
|
56
56
|
return tree;
|
|
57
57
|
}
|
|
58
58
|
async populateFromTree(tree) {
|
|
59
|
-
const traverseTree = async (node, parentId =
|
|
59
|
+
const traverseTree = async (node, parentId = null) => {
|
|
60
60
|
const { children, ...itemData } = node;
|
|
61
|
-
const item = { ...itemData, parentId };
|
|
61
|
+
const item = { ...itemData, parentId: itemData.parentId === 0 ? null : itemData.parentId };
|
|
62
62
|
await this.setElement(item.id, item, true);
|
|
63
63
|
if (children && children.length > 0) {
|
|
64
64
|
for (const child of children) {
|
|
@@ -109,6 +109,8 @@ export class StorageService {
|
|
|
109
109
|
async findElementsByParentId(parentId, type) {
|
|
110
110
|
const elements = [];
|
|
111
111
|
for (const item of this.storageMap.values()) {
|
|
112
|
+
if (parentId === 0)
|
|
113
|
+
parentId = null;
|
|
112
114
|
if (type === null && item.parentId === parentId) {
|
|
113
115
|
elements.push(item);
|
|
114
116
|
continue;
|
|
@@ -138,14 +140,16 @@ export class StorageService {
|
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
export class StorageServices {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
storages = [];
|
|
144
|
+
constructor() {
|
|
145
|
+
}
|
|
146
|
+
add(storage) {
|
|
143
147
|
this.storages.push(storage);
|
|
144
148
|
}
|
|
145
|
-
|
|
149
|
+
get(id) {
|
|
146
150
|
return this.storages.find(storage => storage.getId() === id);
|
|
147
151
|
}
|
|
148
|
-
|
|
152
|
+
getAll() {
|
|
149
153
|
return this.storages;
|
|
150
154
|
}
|
|
151
155
|
}
|
|
@@ -155,17 +159,21 @@ export class Navigation extends AbstractCatalog {
|
|
|
155
159
|
icon = "box";
|
|
156
160
|
actionHandlers = [];
|
|
157
161
|
idList = [];
|
|
162
|
+
storageServices;
|
|
158
163
|
constructor(adminizer, config) {
|
|
159
164
|
let items = [];
|
|
165
|
+
const storageServices = new StorageServices();
|
|
160
166
|
for (const configElement of config.items) {
|
|
161
|
-
items.push(new NavigationItem(adminizer, configElement.title, configElement.model, config.model, configElement.urlPath));
|
|
167
|
+
items.push(new NavigationItem(adminizer, configElement.title, configElement.model, config.model, configElement.urlPath, storageServices));
|
|
162
168
|
}
|
|
163
|
-
items.push(new NavigationGroup(adminizer, config.groupField));
|
|
164
|
-
items.push(new LinkItem(adminizer));
|
|
169
|
+
items.push(new NavigationGroup(adminizer, config.groupField, storageServices));
|
|
170
|
+
items.push(new LinkItem(adminizer, storageServices));
|
|
171
|
+
super(adminizer, items);
|
|
172
|
+
this.storageServices = storageServices;
|
|
173
|
+
adminizer.storageServices = storageServices;
|
|
165
174
|
for (const section of config.sections) {
|
|
166
|
-
|
|
175
|
+
this.storageServices.add(new StorageService(adminizer, section, config.model));
|
|
167
176
|
}
|
|
168
|
-
super(adminizer, items);
|
|
169
177
|
this.movingGroupsRootOnly = config.movingGroupsRootOnly;
|
|
170
178
|
this.idList = config.sections ?? [];
|
|
171
179
|
}
|
|
@@ -183,7 +191,7 @@ class NavigationItem extends AbstractItem {
|
|
|
183
191
|
actionHandlers = [];
|
|
184
192
|
urlPath;
|
|
185
193
|
adminizer;
|
|
186
|
-
constructor(adminizer, name, model, navigationModel, urlPath) {
|
|
194
|
+
constructor(adminizer, name, model, navigationModel, urlPath, storageServices) {
|
|
187
195
|
super();
|
|
188
196
|
this.name = name;
|
|
189
197
|
this.navigationModel = navigationModel;
|
|
@@ -193,9 +201,10 @@ class NavigationItem extends AbstractItem {
|
|
|
193
201
|
let configModel = adminizer.config.models[this.model];
|
|
194
202
|
this.icon = configModel?.icon ?? 'file_present';
|
|
195
203
|
this.adminizer = adminizer;
|
|
204
|
+
this.storageServices = storageServices;
|
|
196
205
|
}
|
|
197
206
|
async create(data, catalogId) {
|
|
198
|
-
let storage =
|
|
207
|
+
let storage = this.storageServices.get(catalogId);
|
|
199
208
|
let storageData = null;
|
|
200
209
|
if (data._method === 'select') {
|
|
201
210
|
// Direct call by model adapter
|
|
@@ -213,9 +222,11 @@ class NavigationItem extends AbstractItem {
|
|
|
213
222
|
return await storage.setElement(data.id, storageData);
|
|
214
223
|
}
|
|
215
224
|
async dataPreparation(data, catalogId, sortOrder) {
|
|
216
|
-
let storage =
|
|
225
|
+
let storage = this.storageServices.get(catalogId);
|
|
217
226
|
let urlPath = eval('`' + this.urlPath + '`');
|
|
218
|
-
let parentId = data.parentId ? data.parentId :
|
|
227
|
+
let parentId = data.parentId ? data.parentId : null; // changed from 0 to null
|
|
228
|
+
if (parentId === 0)
|
|
229
|
+
parentId = null;
|
|
219
230
|
return {
|
|
220
231
|
id: uuid(),
|
|
221
232
|
modelId: data.record.id,
|
|
@@ -231,7 +242,7 @@ class NavigationItem extends AbstractItem {
|
|
|
231
242
|
};
|
|
232
243
|
}
|
|
233
244
|
async updateModelItems(modelId, data, catalogId) {
|
|
234
|
-
let storage =
|
|
245
|
+
let storage = this.storageServices.get(catalogId);
|
|
235
246
|
let items = await storage.findElementByModelId(modelId);
|
|
236
247
|
let urlPath = eval('`' + this.urlPath + '`');
|
|
237
248
|
let response = [];
|
|
@@ -247,15 +258,15 @@ class NavigationItem extends AbstractItem {
|
|
|
247
258
|
return response[0];
|
|
248
259
|
}
|
|
249
260
|
async update(itemId, data, catalogId) {
|
|
250
|
-
let storage =
|
|
261
|
+
let storage = this.storageServices.get(catalogId);
|
|
251
262
|
return await storage.setElement(itemId, data);
|
|
252
263
|
}
|
|
253
264
|
async deleteItem(itemId, catalogId) {
|
|
254
|
-
let storage =
|
|
265
|
+
let storage = this.storageServices.get(catalogId);
|
|
255
266
|
return await storage.removeElementById(itemId);
|
|
256
267
|
}
|
|
257
268
|
async find(itemId, catalogId) {
|
|
258
|
-
let storage =
|
|
269
|
+
let storage = this.storageServices.get(catalogId);
|
|
259
270
|
return await storage.findElementById(itemId);
|
|
260
271
|
}
|
|
261
272
|
/**
|
|
@@ -288,7 +299,7 @@ class NavigationItem extends AbstractItem {
|
|
|
288
299
|
};
|
|
289
300
|
}
|
|
290
301
|
async getChilds(parentId, catalogId) {
|
|
291
|
-
let storage =
|
|
302
|
+
let storage = this.storageServices.get(catalogId);
|
|
292
303
|
return await storage.findElementsByParentId(parentId, this.type);
|
|
293
304
|
}
|
|
294
305
|
async getEditTemplate(id, catalogId, req, modelId) {
|
|
@@ -300,7 +311,7 @@ class NavigationItem extends AbstractItem {
|
|
|
300
311
|
});
|
|
301
312
|
}
|
|
302
313
|
async search(s, catalogId) {
|
|
303
|
-
let storage =
|
|
314
|
+
let storage = this.storageServices.get(catalogId);
|
|
304
315
|
return await storage.search(s, this.type);
|
|
305
316
|
}
|
|
306
317
|
}
|
|
@@ -309,13 +320,14 @@ class NavigationGroup extends AbstractGroup {
|
|
|
309
320
|
name = "Group";
|
|
310
321
|
groupField;
|
|
311
322
|
adminizer;
|
|
312
|
-
constructor(adminizer, groupField) {
|
|
323
|
+
constructor(adminizer, groupField, storageServices) {
|
|
313
324
|
super();
|
|
314
325
|
this.groupField = groupField;
|
|
315
326
|
this.adminizer = adminizer;
|
|
327
|
+
this.storageServices = storageServices;
|
|
316
328
|
}
|
|
317
329
|
async create(data, catalogId) {
|
|
318
|
-
let storage =
|
|
330
|
+
let storage = this.storageServices.get(catalogId);
|
|
319
331
|
let storageData = await this.dataPreparation(data, catalogId);
|
|
320
332
|
delete data.name;
|
|
321
333
|
delete data.parentId;
|
|
@@ -323,8 +335,10 @@ class NavigationGroup extends AbstractGroup {
|
|
|
323
335
|
return await storage.setElement(storageData.id, storageData);
|
|
324
336
|
}
|
|
325
337
|
async dataPreparation(data, catalogId, sortOrder) {
|
|
326
|
-
let storage =
|
|
327
|
-
let parentId = data.parentId ? data.parentId :
|
|
338
|
+
let storage = this.storageServices.get(catalogId);
|
|
339
|
+
let parentId = data.parentId ? data.parentId : null; // changed from 0 to null
|
|
340
|
+
if (parentId === 0)
|
|
341
|
+
parentId = null;
|
|
328
342
|
return {
|
|
329
343
|
id: uuid(),
|
|
330
344
|
name: data.name,
|
|
@@ -338,19 +352,19 @@ class NavigationGroup extends AbstractGroup {
|
|
|
338
352
|
};
|
|
339
353
|
}
|
|
340
354
|
async deleteItem(itemId, catalogId) {
|
|
341
|
-
let storage =
|
|
355
|
+
let storage = this.storageServices.get(catalogId);
|
|
342
356
|
return await storage.removeElementById(itemId);
|
|
343
357
|
}
|
|
344
358
|
async find(itemId, catalogId) {
|
|
345
|
-
let storage =
|
|
359
|
+
let storage = this.storageServices.get(catalogId);
|
|
346
360
|
return await storage.findElementById(itemId);
|
|
347
361
|
}
|
|
348
362
|
async update(itemId, data, catalogId) {
|
|
349
|
-
let storage =
|
|
363
|
+
let storage = this.storageServices.get(catalogId);
|
|
350
364
|
return await storage.setElement(itemId, data);
|
|
351
365
|
}
|
|
352
366
|
async updateModelItems(modelId, data, catalogId) {
|
|
353
|
-
let storage =
|
|
367
|
+
let storage = this.storageServices.get(catalogId);
|
|
354
368
|
return await storage.setElement(modelId, data);
|
|
355
369
|
}
|
|
356
370
|
getAddTemplate(req) {
|
|
@@ -404,11 +418,11 @@ class NavigationGroup extends AbstractGroup {
|
|
|
404
418
|
});
|
|
405
419
|
}
|
|
406
420
|
async getChilds(parentId, catalogId) {
|
|
407
|
-
let storage =
|
|
421
|
+
let storage = this.storageServices.get(catalogId);
|
|
408
422
|
return await storage.findElementsByParentId(parentId, this.type);
|
|
409
423
|
}
|
|
410
424
|
async search(s, catalogId) {
|
|
411
|
-
let storage =
|
|
425
|
+
let storage = this.storageServices.get(catalogId);
|
|
412
426
|
return await storage.search(s, this.type);
|
|
413
427
|
}
|
|
414
428
|
}
|
|
@@ -418,8 +432,8 @@ class LinkItem extends NavigationGroup {
|
|
|
418
432
|
name = 'Link';
|
|
419
433
|
type = 'link';
|
|
420
434
|
isGroup = false;
|
|
421
|
-
constructor(adminizer) {
|
|
422
|
-
super(adminizer, []);
|
|
435
|
+
constructor(adminizer, storageServices) {
|
|
436
|
+
super(adminizer, [], storageServices);
|
|
423
437
|
}
|
|
424
438
|
getAddTemplate(req) {
|
|
425
439
|
let type = 'navigation.link';
|