@griddo/ax 10.4.36 → 10.5.0

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.
Files changed (38) hide show
  1. package/package.json +2 -2
  2. package/src/api/checkgroups.tsx +1 -1
  3. package/src/api/structuredData.tsx +109 -18
  4. package/src/components/Fields/AsyncCheckGroup/index.tsx +56 -19
  5. package/src/components/Fields/AsyncCheckGroup/style.tsx +21 -4
  6. package/src/components/Fields/CheckField/style.tsx +1 -1
  7. package/src/components/Fields/NoteField/style.tsx +1 -1
  8. package/src/components/Fields/ReferenceField/AutoPanel/AutoItem/index.tsx +58 -20
  9. package/src/components/Fields/ReferenceField/AutoPanel/AutoItem/style.tsx +20 -1
  10. package/src/components/Fields/ReferenceField/Context/index.tsx +9 -9
  11. package/src/components/LanguageMenu/index.tsx +1 -1
  12. package/src/components/Tag/index.tsx +3 -2
  13. package/src/components/Tag/style.tsx +5 -4
  14. package/src/containers/StructuredData/actions.tsx +169 -82
  15. package/src/containers/StructuredData/constants.tsx +2 -2
  16. package/src/containers/StructuredData/interfaces.tsx +16 -9
  17. package/src/containers/StructuredData/reducer.tsx +11 -7
  18. package/src/containers/StructuredData/utils.tsx +2 -2
  19. package/src/global.d.ts +0 -1
  20. package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/index.tsx +10 -3
  21. package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/style.tsx +12 -6
  22. package/src/modules/Categories/CategoriesList/BulkHeader/index.tsx +21 -2
  23. package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +131 -63
  24. package/src/modules/Categories/CategoriesList/CategoryItem/style.tsx +67 -7
  25. package/src/modules/Categories/CategoriesList/CategoryNav/index.tsx +2 -8
  26. package/src/modules/Categories/CategoriesList/CategoryPanel/Form/index.tsx +88 -33
  27. package/src/modules/Categories/CategoriesList/CategoryPanel/index.tsx +130 -56
  28. package/src/modules/Categories/CategoriesList/CategoryPanel/style.tsx +1 -1
  29. package/src/modules/Categories/CategoriesList/atoms.tsx +46 -19
  30. package/src/modules/Categories/CategoriesList/helpers.tsx +116 -0
  31. package/src/modules/Categories/CategoriesList/hooks.tsx +61 -0
  32. package/src/modules/Categories/CategoriesList/index.tsx +283 -97
  33. package/src/modules/Categories/CategoriesList/style.tsx +54 -2
  34. package/src/modules/Categories/CategoriesList/utils.tsx +34 -14
  35. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +1 -1
  36. package/src/modules/Users/UserList/hooks.tsx +5 -4
  37. package/src/types/index.tsx +64 -3
  38. package/tsconfig.paths.json +0 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "10.4.36",
4
+ "version": "10.5.0",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Carlos Torres <carlos.torres@secuoyas.com>",
@@ -232,5 +232,5 @@
232
232
  "publishConfig": {
233
233
  "access": "public"
234
234
  },
235
- "gitHead": "558c5aeb4f2dac9a9cd2fba1cdad2aa70ea5f9da"
235
+ "gitHead": "245671251ee67a3d95f10705ddab42178208a5c5"
236
236
  }
@@ -27,7 +27,7 @@ const getCheckGroupItems = async (
27
27
 
28
28
  const allLanguagesQuery = allLanguages ? "&allLanguages=true" : "";
29
29
 
30
- SERVICES.GET_STRUCTURED_DATA.dynamicUrl = `${host}${prefix}${siteId}${suffix}/${source}/checkgroup?order=title-ASC${allLanguagesQuery}`;
30
+ SERVICES.GET_STRUCTURED_DATA.dynamicUrl = `${host}${prefix}${siteId}${suffix}/${source}/checkgroup?groupingCategories=on&order=title-ASC${allLanguagesQuery}`;
31
31
 
32
32
  return sendRequest(SERVICES.GET_STRUCTURED_DATA);
33
33
  };
@@ -1,6 +1,6 @@
1
1
  import { template } from "./config";
2
2
  import { IServiceConfig, sendRequest, sendInitialRequest } from "./utils";
3
- import { IGetStructuredDataParams } from "@ax/types";
3
+ import { ICategoryGroupParams, IGetStructuredDataParams, IOrderCategoryParams } from "@ax/types";
4
4
  import { AxiosResponse } from "axios";
5
5
 
6
6
  const SERVICES: { [key: string]: IServiceConfig } = {
@@ -84,9 +84,39 @@ const SERVICES: { [key: string]: IServiceConfig } = {
84
84
  endpoint: "/translations/structured_data_content/",
85
85
  method: "POST",
86
86
  },
87
+ CREATE_GROUP: {
88
+ ...template,
89
+ endpoint: "/categories/group",
90
+ method: "POST",
91
+ },
92
+ GET_GROUP: {
93
+ ...template,
94
+ endpoint: "/categories/group/",
95
+ method: "GET",
96
+ },
97
+ EDIT_GROUP: {
98
+ ...template,
99
+ endpoint: "/categories/group/",
100
+ method: "PUT",
101
+ },
102
+ DELETE_GROUP: {
103
+ ...template,
104
+ endpoint: ["/categories/", "/site/", "/group/"],
105
+ method: "DELETE",
106
+ },
107
+ DELETE_GROUP_BULK: {
108
+ ...template,
109
+ endpoint: ["/categories/", "/site/", "/group/bulk"],
110
+ method: "DELETE",
111
+ },
112
+ ORDER_CATEGORY: {
113
+ ...template,
114
+ endpoint: "/categories/order/",
115
+ method: "PUT",
116
+ },
87
117
  };
88
118
 
89
- const getData = (token: string | null, siteID?: number | null) => {
119
+ const getData = (token: string | null, siteID?: number | null): Promise<AxiosResponse> => {
90
120
  const { host, endpoint } = SERVICES.GET_DATA;
91
121
 
92
122
  if (siteID) {
@@ -102,7 +132,7 @@ const getData = (token: string | null, siteID?: number | null) => {
102
132
  }
103
133
  };
104
134
 
105
- const getDataContent = (dataContentId: number, langID: number | null) => {
135
+ const getDataContent = (dataContentId: number, langID: number | null): Promise<AxiosResponse> => {
106
136
  const { host, endpoint } = SERVICES.GET_DATA_CONTENT;
107
137
 
108
138
  const config = langID ? { lang: langID } : {};
@@ -112,7 +142,7 @@ const getDataContent = (dataContentId: number, langID: number | null) => {
112
142
  return sendRequest(SERVICES.GET_DATA_CONTENT, {}, config);
113
143
  };
114
144
 
115
- const getDataContents = (params: IGetStructuredDataParams, siteID?: number | null) => {
145
+ const getDataContents = (params: IGetStructuredDataParams, siteID?: number | null): Promise<AxiosResponse> => {
116
146
  const { host, endpoint } = SERVICES.GET_DATA_CONTENTS;
117
147
  const {
118
148
  dataID,
@@ -125,12 +155,14 @@ const getDataContents = (params: IGetStructuredDataParams, siteID?: number | nul
125
155
  filterQuery,
126
156
  relatedFields = false,
127
157
  order,
128
- lang
158
+ lang,
159
+ groupingCategories,
129
160
  } = params;
130
161
 
131
162
  const url = siteID ? `${host}/site/${siteID}${endpoint}` : `${host}${endpoint}`;
132
163
 
133
164
  SERVICES.GET_DATA_CONTENTS.dynamicUrl = `${url}${dataID}?page=${page}&itemsPerPage=${itemsPerPage}&pagination=${pagination}&deleted=${deleted}&includeDraft=${include_draft}`;
165
+ if (groupingCategories) SERVICES.GET_DATA_CONTENTS.dynamicUrl += `&groupingCategories=on`;
134
166
  if (relatedFields) SERVICES.GET_DATA_CONTENTS.dynamicUrl += `&relatedFields=true`;
135
167
  if (query && query.trim() !== "") SERVICES.GET_DATA_CONTENTS.dynamicUrl += `&query=${query}`;
136
168
  if (order) SERVICES.GET_DATA_CONTENTS.dynamicUrl += `&order=${order}`;
@@ -140,14 +172,14 @@ const getDataContents = (params: IGetStructuredDataParams, siteID?: number | nul
140
172
  ...(lang && { lang }),
141
173
  };
142
174
 
143
- return sendRequest(SERVICES.GET_DATA_CONTENTS, null, dataHeader);
175
+ return sendRequest(SERVICES.GET_DATA_CONTENTS, null, dataHeader);
144
176
  };
145
177
 
146
- const createDataContent = (data: any) => {
178
+ const createDataContent = (data: any): Promise<AxiosResponse> => {
147
179
  return sendRequest(SERVICES.CREATE_DATA_CONTENT, { ...data });
148
180
  };
149
181
 
150
- const updateDataContent = (dataContentId: number, data: any) => {
182
+ const updateDataContent = (dataContentId: number, data: any): Promise<AxiosResponse> => {
151
183
  const { host, endpoint } = SERVICES.UPDATE_DATA_CONTENT;
152
184
 
153
185
  SERVICES.UPDATE_DATA_CONTENT.dynamicUrl = `${host}${endpoint}${dataContentId}`;
@@ -155,14 +187,14 @@ const updateDataContent = (dataContentId: number, data: any) => {
155
187
  return sendRequest(SERVICES.UPDATE_DATA_CONTENT, { ...data });
156
188
  };
157
189
 
158
- const deleteDataContent = async (dataContentId: number) => {
190
+ const deleteDataContent = async (dataContentId: number): Promise<AxiosResponse> => {
159
191
  const { host, endpoint } = SERVICES.DELETE_DATA_CONTENT;
160
192
 
161
193
  SERVICES.DELETE_DATA_CONTENT.dynamicUrl = `${host}${endpoint}${dataContentId}`;
162
194
  return sendRequest(SERVICES.DELETE_DATA_CONTENT);
163
195
  };
164
196
 
165
- const getDataContentBulk = (dataContentIDs: number[], siteID?: number) => {
197
+ const getDataContentBulk = (dataContentIDs: number[], siteID?: number): Promise<AxiosResponse> => {
166
198
  const { host, endpoint } = SERVICES.GET_DATA_CONTENT_BULK;
167
199
  const url = siteID ? `${host}/site/${siteID}${endpoint}` : `${host}${endpoint}`;
168
200
  const ids = dataContentIDs.join(",");
@@ -170,11 +202,11 @@ const getDataContentBulk = (dataContentIDs: number[], siteID?: number) => {
170
202
  return sendRequest(SERVICES.GET_DATA_CONTENT_BULK);
171
203
  };
172
204
 
173
- const deleteDataContentBulk = (dataContentID: number[]) => {
205
+ const deleteDataContentBulk = (dataContentID: number[]): Promise<AxiosResponse> => {
174
206
  return sendRequest(SERVICES.DELETE_DATA_CONTENT_BULK, { ids: dataContentID });
175
207
  };
176
208
 
177
- const restoreDataContent = (dataContentId: number) => {
209
+ const restoreDataContent = (dataContentId: number): Promise<AxiosResponse> => {
178
210
  const {
179
211
  host,
180
212
  endpoint: [prefix, suffix],
@@ -185,11 +217,11 @@ const restoreDataContent = (dataContentId: number) => {
185
217
  return sendRequest(SERVICES.RESTORE_DATA_CONTENT);
186
218
  };
187
219
 
188
- const restoreDataContentBulk = (dataContentId: number[]) => {
220
+ const restoreDataContentBulk = (dataContentId: number[]): Promise<AxiosResponse> => {
189
221
  return sendRequest(SERVICES.RESTORE_DATA_CONTENT_BULK, { ids: dataContentId });
190
222
  };
191
223
 
192
- const getDistributorContent = async (siteID: number | string, data: any) => {
224
+ const getDistributorContent = async (siteID: number | string, data: any): Promise<AxiosResponse> => {
193
225
  const { host, endpoint } = SERVICES.GET_DISTRIBUTOR_CONTENT;
194
226
  const [prefix, suffix] = endpoint;
195
227
 
@@ -198,7 +230,7 @@ const getDistributorContent = async (siteID: number | string, data: any) => {
198
230
  return sendRequest(SERVICES.GET_DISTRIBUTOR_CONTENT, { ...data });
199
231
  };
200
232
 
201
- const getDataTitles = async (data: string[]) => {
233
+ const getDataTitles = async (data: string[]): Promise<AxiosResponse> => {
202
234
  const { host, endpoint } = SERVICES.GET_DATA_TITLES;
203
235
 
204
236
  const dataString = data.join(",");
@@ -207,7 +239,7 @@ const getDataTitles = async (data: string[]) => {
207
239
  return sendRequest(SERVICES.GET_DATA_TITLES);
208
240
  };
209
241
 
210
- const setDataStatus = (dataContentId: number, status: string) => {
242
+ const setDataStatus = (dataContentId: number, status: string): Promise<AxiosResponse> => {
211
243
  const { host, endpoint } = SERVICES.SET_DATA_STATUS;
212
244
 
213
245
  SERVICES.SET_DATA_STATUS.dynamicUrl = `${host}${endpoint}${dataContentId}/${status}`;
@@ -215,7 +247,7 @@ const setDataStatus = (dataContentId: number, status: string) => {
215
247
  return sendRequest(SERVICES.SET_DATA_STATUS);
216
248
  };
217
249
 
218
- const setDataStatusBulk = (dataContentID: number[], status: string) => {
250
+ const setDataStatusBulk = (dataContentID: number[], status: string): Promise<AxiosResponse> => {
219
251
  const { host, endpoint } = SERVICES.SET_DATA_STATUS_BULK;
220
252
  const [prefix, suffix] = endpoint;
221
253
 
@@ -236,13 +268,66 @@ const getContentTypes = (siteID: number | null, lang?: number): Promise<AxiosRes
236
268
  return sendRequest(SERVICES.GET_CONTENT_TYPES, null, dataHeader);
237
269
  };
238
270
 
239
- const getDataContentTranslation = async (data: any, langID: number) => {
271
+ const getDataContentTranslation = async (data: any, langID: number): Promise<AxiosResponse> => {
240
272
  const { host, endpoint } = SERVICES.CONTENT_AI_TRANSLATION;
241
273
  SERVICES.CONTENT_AI_TRANSLATION.dynamicUrl = `${host}${endpoint}${langID}`;
242
274
 
243
275
  return sendRequest(SERVICES.CONTENT_AI_TRANSLATION, { ...data });
244
276
  };
245
277
 
278
+ const createGroup = (data: ICategoryGroupParams): Promise<AxiosResponse> => {
279
+ return sendRequest(SERVICES.CREATE_GROUP, { ...data });
280
+ };
281
+
282
+ const getGroup = (groupId: number): Promise<AxiosResponse> => {
283
+ const { host, endpoint } = SERVICES.GET_GROUP;
284
+ SERVICES.GET_GROUP.dynamicUrl = `${host}${endpoint}${groupId}`;
285
+ return sendRequest(SERVICES.GET_GROUP);
286
+ };
287
+
288
+ const updateGroup = (groupID: number, data: ICategoryGroupParams): Promise<AxiosResponse> => {
289
+ const { host, endpoint } = SERVICES.EDIT_GROUP;
290
+ SERVICES.EDIT_GROUP.dynamicUrl = `${host}${endpoint}${groupID}`;
291
+
292
+ return sendRequest(SERVICES.EDIT_GROUP, { ...data });
293
+ };
294
+
295
+ const deleteGroup = (
296
+ structuredDataID: string,
297
+ siteID: number | "global",
298
+ groupID: number,
299
+ deleteChildren: boolean
300
+ ): Promise<AxiosResponse> => {
301
+ const { host, endpoint } = SERVICES.DELETE_GROUP;
302
+ const [prefix, infix, suffix] = endpoint;
303
+
304
+ const children = deleteChildren === false ? "?deleteChildren=off" : "";
305
+
306
+ SERVICES.DELETE_GROUP.dynamicUrl = `${host}${prefix}${structuredDataID}${infix}${siteID}${suffix}${groupID}${children}`;
307
+
308
+ return sendRequest(SERVICES.DELETE_GROUP);
309
+ };
310
+
311
+ const deleteGroupBulk = (
312
+ structuredDataID: string,
313
+ siteID: number | "global",
314
+ groupID: number[],
315
+ deleteChildren: boolean
316
+ ): Promise<AxiosResponse> => {
317
+ const { host, endpoint } = SERVICES.DELETE_GROUP_BULK;
318
+ const [prefix, infix, suffix] = endpoint;
319
+
320
+ const children = deleteChildren === false ? "?deleteChildren=off" : "";
321
+
322
+ SERVICES.DELETE_GROUP_BULK.dynamicUrl = `${host}${prefix}${structuredDataID}${infix}${siteID}${suffix}${children}`;
323
+
324
+ return sendRequest(SERVICES.DELETE_GROUP_BULK, { groups: groupID });
325
+ };
326
+
327
+ const orderCategory = (data: IOrderCategoryParams): Promise<AxiosResponse> => {
328
+ return sendRequest(SERVICES.ORDER_CATEGORY, { ...data });
329
+ };
330
+
246
331
  export default {
247
332
  getData,
248
333
  getDataContent,
@@ -260,4 +345,10 @@ export default {
260
345
  getContentTypes,
261
346
  getDataContentBulk,
262
347
  getDataContentTranslation,
348
+ createGroup,
349
+ updateGroup,
350
+ deleteGroup,
351
+ deleteGroupBulk,
352
+ orderCategory,
353
+ getGroup,
263
354
  };
@@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react";
2
2
 
3
3
  import { checkgroups } from "@ax/api";
4
4
  import { isReqOk } from "@ax/helpers";
5
- import CheckField from "@ax/components/Fields/CheckField";
6
5
  import { ISite, ICheck } from "@ax/types";
7
6
 
8
7
  import * as S from "./style";
@@ -15,6 +14,17 @@ const AsyncCheckGroup = (props: IAsyncCheckGroup): JSX.Element => {
15
14
 
16
15
  const isCheckValue = (object: any): object is ICheckValue => "value" in object;
17
16
 
17
+ const getAllIds = (results: ICheckValue[]) => {
18
+ return results.reduce((acc: number[], current) => {
19
+ acc.push(current.value);
20
+ const children = getAllIds(current.children || []);
21
+ if (children.length) {
22
+ acc = [...acc, ...children];
23
+ }
24
+ return acc;
25
+ }, []);
26
+ };
27
+
18
28
  useEffect((): any => {
19
29
  let isSubscribed = true;
20
30
 
@@ -38,7 +48,7 @@ const AsyncCheckGroup = (props: IAsyncCheckGroup): JSX.Element => {
38
48
  setOptions(result);
39
49
  if (safeValue.length > 0 && isCheckValue(safeValue[0])) {
40
50
  // clean array of old values and set translations
41
- const resultIDs = result && result.map((opt: ICheckValue) => opt.value);
51
+ const resultIDs = result && getAllIds(result);
42
52
  const fixedState = safeValue.reduce((acc: ICheckValue[], current: ICheckValue) => {
43
53
  if (resultIDs.includes(current.value)) {
44
54
  return [...acc, current];
@@ -60,8 +70,22 @@ const AsyncCheckGroup = (props: IAsyncCheckGroup): JSX.Element => {
60
70
  .catch((apiError) => console.log(apiError));
61
71
 
62
72
  return () => (isSubscribed = false);
73
+ // eslint-disable-next-line react-hooks/exhaustive-deps
63
74
  }, [site, source, allLanguages]);
64
75
 
76
+ const filterOptions = (options: ICheckValue[], ids: number[]) => {
77
+ return options.reduce((acc: ICheckValue[], current) => {
78
+ if (ids.includes(current.value)) {
79
+ acc.push(current);
80
+ }
81
+ const children = filterOptions(current.children || [], ids);
82
+ if (children.length) {
83
+ acc = [...acc, ...children];
84
+ }
85
+ return acc;
86
+ }, []);
87
+ };
88
+
65
89
  const handleChange = (newValue: ICheck) => {
66
90
  const arrayIds = safeValue.map((e: any) => (typeof e === "number" ? e : e.value));
67
91
 
@@ -76,7 +100,7 @@ const AsyncCheckGroup = (props: IAsyncCheckGroup): JSX.Element => {
76
100
  newArray = [...arrayIds, newValue.value];
77
101
  }
78
102
 
79
- const newArrayObject = options.filter((e: ICheckValue) => newArray.includes(e.value));
103
+ const newArrayObject = filterOptions(options, newArray);
80
104
 
81
105
  onChange(newArrayObject);
82
106
  error && handleValidation && handleValidation(newArrayObject);
@@ -85,22 +109,30 @@ const AsyncCheckGroup = (props: IAsyncCheckGroup): JSX.Element => {
85
109
  const isChecked = (id: number): boolean =>
86
110
  !!safeValue.find((e: ICheckValue | number) => (typeof e === "number" ? e === id : e.value === id));
87
111
 
88
- const checks =
89
- options &&
90
- options.map((item: any) => (
91
- <CheckField
92
- key={item.name}
93
- onChange={handleChange}
94
- checked={isChecked(item.value)}
95
- value={item.value}
96
- title={item.title}
97
- name={item.name}
98
- disabled={item.disabled || disabled}
99
- error={item.error}
100
- />
101
- ));
102
-
103
- return <S.FieldGroup full={fullHeight}>{checks}</S.FieldGroup>;
112
+ const renderChecks = (options: ICheckValue[]): JSX.Element[] => {
113
+ return options.map((item) => {
114
+ return (
115
+ <React.Fragment key={`${item.name}${item.value}`}>
116
+ {item.type === "group" ? (
117
+ <S.GroupTitle>{item.title}</S.GroupTitle>
118
+ ) : (
119
+ <S.StyledCheckField
120
+ onChange={handleChange}
121
+ checked={isChecked(item.value)}
122
+ value={item.value}
123
+ title={item.title}
124
+ name={item.name}
125
+ disabled={item.disabled || disabled}
126
+ error={item.error}
127
+ />
128
+ )}
129
+ {item.children && <S.GroupWrapper>{renderChecks(item.children)}</S.GroupWrapper>}
130
+ </React.Fragment>
131
+ );
132
+ });
133
+ };
134
+
135
+ return <S.FieldGroup full={fullHeight}>{renderChecks(options)}</S.FieldGroup>;
104
136
  };
105
137
 
106
138
  interface ICheckValue {
@@ -108,6 +140,11 @@ interface ICheckValue {
108
140
  name: string;
109
141
  title: string;
110
142
  dataLanguages?: { id: number; language: number }[];
143
+ type: "category" | "group";
144
+ children?: ICheckValue[];
145
+ selectable?: boolean;
146
+ disabled?: boolean;
147
+ error?: boolean;
111
148
  }
112
149
 
113
150
  export interface IAsyncCheckGroup {
@@ -1,12 +1,29 @@
1
+ import React from "react";
1
2
  import styled from "styled-components";
3
+ import CheckField from "../CheckField";
2
4
 
3
- export const FieldGroup = styled.div<{ full?: boolean }>`
5
+ const FieldGroup = styled.div<{ full?: boolean }>`
4
6
  width: 100%;
5
7
  max-width: calc(${(p) => p.theme.spacing.xl} * 5);
6
8
  max-height: ${(p) => (p.full ? "auto" : `calc(${p.theme.spacing.l} * 3)`)};
7
9
  overflow: auto;
8
10
  margin-bottom: ${(p) => p.theme.spacing.xxs};
9
- & > div {
10
- margin-bottom: ${p => p.theme.spacing.xxs}
11
- }
12
11
  `;
12
+
13
+ const GroupWrapper = styled.div`
14
+ padding-left: ${(p) => p.theme.spacing.s};
15
+ padding-bottom: ${(p) => p.theme.spacing.xxs};
16
+ `;
17
+
18
+ const GroupTitle = styled.div`
19
+ ${(p) => p.theme.textStyle.fieldContent};
20
+ color: ${(p) => p.theme.color.textMediumEmphasis};
21
+ font-style: italic;
22
+ margin-bottom: ${(p) => p.theme.spacing.xs};
23
+ `;
24
+
25
+ const StyledCheckField = styled((props) => <CheckField {...props} />)`
26
+ margin-bottom: ${(p) => p.theme.spacing.xxs};
27
+ `;
28
+
29
+ export { FieldGroup, GroupWrapper, GroupTitle, StyledCheckField };
@@ -14,7 +14,7 @@ const Label = styled.label<{ disabled: boolean | undefined; inversed: boolean; h
14
14
  ? p.theme.color.interactiveDisabled
15
15
  : p.inversed
16
16
  ? p.theme.color.textHighEmphasisInverse
17
- : p.theme.color.textMediumEmphasis};
17
+ : p.theme.color.textHighEmphasis};
18
18
  `;
19
19
 
20
20
  const CheckMark = styled.span<{
@@ -7,7 +7,7 @@ const Wrapper = styled.div`
7
7
  `;
8
8
 
9
9
  const Title = styled.div`
10
- ${(p) => p.theme.textStyle.headingXS};
10
+ ${(p) => p.theme.textStyle.fieldLabel};
11
11
  color: ${(p) => p.theme.color.textMediumEmphasis};
12
12
  margin-bottom: ${(p) => p.theme.spacing.xs};
13
13
  `;
@@ -3,7 +3,7 @@ import React, { useEffect, useState } from "react";
3
3
  import { structuredData, selects } from "@ax/api";
4
4
  import { isReqOk } from "@ax/helpers";
5
5
  import { IGetStructuredDataParams, IDataSource, ISite, IStructuredData } from "@ax/types";
6
- import { Button, CheckField, FieldsBehavior, IconAction, Select, SearchField, Tag, Tooltip } from "@ax/components";
6
+ import { Button, FieldsBehavior, IconAction, Select, SearchField, Tag, Tooltip } from "@ax/components";
7
7
  import { useCategoryColors } from "@ax/hooks";
8
8
 
9
9
  import { ISource, IFilter } from "../../Context";
@@ -68,6 +68,7 @@ const AutoItem = (props: IProps): JSX.Element => {
68
68
  deleted: false,
69
69
  include_draft: false,
70
70
  order: "alpha-asc",
71
+ groupingCategories: true,
71
72
  };
72
73
 
73
74
  const site = getIsGlobal(newValue) ? null : siteID;
@@ -117,9 +118,15 @@ const AutoItem = (props: IProps): JSX.Element => {
117
118
  }
118
119
  };
119
120
 
121
+ const find = (options: IOption[], id: number): IOption | undefined => {
122
+ let result;
123
+ options.some((o) => (result = o.id === id ? o : find(o.children || [], id)));
124
+ return result;
125
+ };
126
+
120
127
  const handleCheckChange = (checkValue: any) => {
121
128
  if (checkValue.isChecked) {
122
- const newFilter: any = state.options.find((f: any) => checkValue.value === f.id);
129
+ const newFilter = find(state.options, checkValue.value);
123
130
  if (newFilter) {
124
131
  const filterCategory = state.categories.find((cat: ICategory) => cat.value === newFilter.structuredData);
125
132
  const selectedCategories =
@@ -207,10 +214,21 @@ const AutoItem = (props: IProps): JSX.Element => {
207
214
  addFilter(updatedSource);
208
215
  };
209
216
 
217
+ const filterOptions = (options: IOption[], query: string) => {
218
+ return options.reduce((acc: IOption[], current) => {
219
+ if (current.content.title.toLowerCase().indexOf(query.toLowerCase()) !== -1) {
220
+ acc.push(current);
221
+ }
222
+ const children = filterOptions(current.children || [], query);
223
+ if (children.length) {
224
+ acc = [...acc, ...children];
225
+ }
226
+ return acc;
227
+ }, []);
228
+ };
229
+
210
230
  const handleSearchChange = (query: string) => {
211
- const filteredOptions = state.options.filter(
212
- (option) => option.content.title.toLowerCase().indexOf(query.toLowerCase()) !== -1
213
- );
231
+ const filteredOptions = query.trim() !== "" ? filterOptions(state.options, query) : state.options;
214
232
  setState({ ...state, filteredOptions });
215
233
  };
216
234
 
@@ -228,6 +246,28 @@ const AutoItem = (props: IProps): JSX.Element => {
228
246
  { value: "AND", label: "AND" },
229
247
  ];
230
248
 
249
+ const renderChecks = (options: IOption[]): JSX.Element[] => {
250
+ return options.map((item) => {
251
+ return (
252
+ <React.Fragment key={item.id}>
253
+ {item.type === "group" ? (
254
+ <S.GroupTitle>{item.content.title}</S.GroupTitle>
255
+ ) : (
256
+ <S.StyledCheckField
257
+ key={item.id}
258
+ title={item.content.title}
259
+ name={item.id}
260
+ value={item.id}
261
+ checked={isChecked(item.id)}
262
+ onChange={handleCheckChange}
263
+ />
264
+ )}
265
+ {item.children && <S.GroupWrapper>{renderChecks(item.children)}</S.GroupWrapper>}
266
+ </React.Fragment>
267
+ );
268
+ });
269
+ };
270
+
231
271
  return (
232
272
  <S.TypeContainer key={source.id}>
233
273
  <S.TypeWrapper data-testid="item-type-wrapper">
@@ -321,19 +361,7 @@ const AutoItem = (props: IProps): JSX.Element => {
321
361
  <SearchField onChange={handleSearchChange} searchOnEnter={false} size="XS" />
322
362
  </S.SearchWrapper>
323
363
  )}
324
- <S.ChecksWrapper>
325
- {state.filteredOptions &&
326
- state.filteredOptions.map((option: any) => (
327
- <CheckField
328
- key={option.content.title}
329
- title={option.content.title}
330
- name={option.id}
331
- value={option.id}
332
- checked={isChecked(option.id)}
333
- onChange={handleCheckChange}
334
- />
335
- ))}
336
- </S.ChecksWrapper>
364
+ <S.ChecksWrapper>{state.filteredOptions && renderChecks(state.filteredOptions)}</S.ChecksWrapper>
337
365
  </S.ActionsFilterWrapper>
338
366
  </S.TypeContainer>
339
367
  );
@@ -341,12 +369,12 @@ const AutoItem = (props: IProps): JSX.Element => {
341
369
 
342
370
  interface IState {
343
371
  selected: string;
344
- options: any[];
372
+ options: IOption[];
345
373
  isOpen: boolean;
346
374
  isAdvancedOpen: boolean;
347
375
  categories: ICategory[];
348
376
  selectedCategories: ICategory[];
349
- filteredOptions: any[];
377
+ filteredOptions: IOption[];
350
378
  }
351
379
 
352
380
  interface ICategory {
@@ -354,6 +382,16 @@ interface ICategory {
354
382
  label: string;
355
383
  }
356
384
 
385
+ interface IOption {
386
+ id: number;
387
+ content: { title: string; code: string };
388
+ dataLanguages?: { id: number; language: number }[];
389
+ type: "category" | "group";
390
+ children?: IOption[];
391
+ selectable?: boolean;
392
+ structuredData: string;
393
+ }
394
+
357
395
  interface IProps {
358
396
  source: IDataSource;
359
397
  canDelete: boolean;
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import styled from "styled-components";
3
- import { ActionMenu } from "@ax/components";
3
+ import { ActionMenu, CheckField } from "@ax/components";
4
4
 
5
5
  const ActionsWrapper = styled.div`
6
6
  position: absolute;
@@ -86,6 +86,22 @@ const ClearWrapper = styled.div`
86
86
  margin-top: ${(p) => p.theme.spacing.xs};
87
87
  `;
88
88
 
89
+ const StyledCheckField = styled((props) => <CheckField {...props} />)`
90
+ margin-bottom: ${(p) => p.theme.spacing.xxs};
91
+ `;
92
+
93
+ const GroupWrapper = styled.div`
94
+ padding-left: ${(p) => p.theme.spacing.s};
95
+ padding-bottom: ${(p) => p.theme.spacing.xxs};
96
+ `;
97
+
98
+ const GroupTitle = styled.div`
99
+ ${(p) => p.theme.textStyle.fieldContent};
100
+ color: ${(p) => p.theme.color.textMediumEmphasis};
101
+ font-style: italic;
102
+ margin-bottom: ${(p) => p.theme.spacing.xs};
103
+ `;
104
+
89
105
  export {
90
106
  IconWrapper,
91
107
  TypeContainer,
@@ -101,4 +117,7 @@ export {
101
117
  ActionsWrapper,
102
118
  SearchWrapper,
103
119
  ClearWrapper,
120
+ StyledCheckField,
121
+ GroupTitle,
122
+ GroupWrapper,
104
123
  };
@@ -100,15 +100,15 @@ export interface IReferenceState {
100
100
 
101
101
  export interface IRefField {
102
102
  mode?: string;
103
- order?: string;
104
- fixed?: number[];
105
- sources?: ISource[];
106
- quantity?: number;
107
- fullRelations?: boolean;
108
- allLanguages?: boolean;
109
- preferenceLanguage?: boolean;
110
- lang?: number;
111
- site?: number;
103
+ order?: string;
104
+ fixed?: number[];
105
+ sources?: ISource[];
106
+ quantity?: number;
107
+ fullRelations?: boolean;
108
+ allLanguages?: boolean;
109
+ preferenceLanguage?: boolean;
110
+ lang?: number;
111
+ site?: number;
112
112
  }
113
113
 
114
114
  export interface IFilter {
@@ -1,9 +1,9 @@
1
1
  import React from "react";
2
2
 
3
3
  import { Flag, Icon } from "@ax/components";
4
+ import { ILanguage } from "@ax/types";
4
5
 
5
6
  import * as S from "./style";
6
- import { ILanguage } from "@ax/types";
7
7
 
8
8
  const LanguageMenu = (props: ILanguageMenuProps): JSX.Element => {
9
9
  const { availableLanguages, language, setLanguage, isInAppBar, currentLanguages } = props;