@vonaffenfels/contentful-teasermanager 1.2.0 → 1.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vonaffenfels/contentful-teasermanager",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "prepublish": "yarn run build",
@@ -70,7 +70,7 @@
70
70
  "classnames": "^2.3.2",
71
71
  "clean-webpack-plugin": "^3.0.0",
72
72
  "cloudinary": "^1.25.1",
73
- "contentful-management": "^7.17.0",
73
+ "contentful-management": "^11",
74
74
  "copy-webpack-plugin": "^8.1.1",
75
75
  "cross-env": "^7.0.3",
76
76
  "css-loader": "^5.2.4",
@@ -98,9 +98,10 @@
98
98
  },
99
99
  "dependencies": {
100
100
  "@vonaffenfels/slate-editor": "^1.2.0",
101
+ "contentful-resolve-response": "^1.9.2",
101
102
  "webpack": "5.88.2"
102
103
  },
103
- "gitHead": "fe9b4ce59e9b4dfae924c580f3a1247c180f4d9b",
104
+ "gitHead": "b06b79ed032fecf1dfeef2e7e312eb0d42f2e84e",
104
105
  "publishConfig": {
105
106
  "access": "public"
106
107
  }
@@ -10,6 +10,7 @@ import {
10
10
  DndContext, useDraggable, useDroppable,
11
11
  PointerSensor, useSensor, useSensors,
12
12
  } from '@dnd-kit/core';
13
+ import contentfulResolveResponse from "contentful-resolve-response";
13
14
  import {
14
15
  useEffect, useState,
15
16
  } from "react";
@@ -61,7 +62,8 @@ const LogicEditorInner = ({
61
62
  }) => {
62
63
  const contentfulClient = getContentfulClient();
63
64
  const [loading, setLoading] = useState(false);
64
- const [data, setData] = useState([]);
65
+ const [tags, setTags] = useState([]);
66
+ const [categories, setCategories] = useState([]);
65
67
  const [searchQuery, setSearchQuery] = useState("");
66
68
  const [addTimepoint, setAddTimepoint] = useState(7);
67
69
  const debouncedSearch = useDebounce(searchQuery, 500);
@@ -102,14 +104,48 @@ const LogicEditorInner = ({
102
104
  "sys.id",
103
105
  ].join(","),
104
106
  };
107
+ const paramsNavigation = {
108
+ limit: 1,
109
+ content_type: "navigation",
110
+ include: 2,
111
+ locale,
112
+ "fields.portal": portal,
113
+ "fields.type": "main",
114
+ };
105
115
 
106
116
  if (debouncedSearch) {
107
117
  params["fields.title[match]"] = debouncedSearch;
108
118
  }
109
119
 
110
- contentfulClient.getEntries(params).then((response) => {
120
+ Promise.all([
121
+ contentfulClient.getEntries(params),
122
+ contentfulClient.getEntries(paramsNavigation).then(async (response) => {
123
+ if (!response?.items?.[0]) {
124
+ return [];
125
+ }
126
+
127
+ const references = await sdk.cma.entry.references({entryId: response.items[0].sys.id}, {include: 7});
128
+ const resolved = contentfulResolveResponse(references);
129
+ const allCategories = {};
130
+
131
+ for (const category of resolved) {
132
+ if (!category?.fields?.children?.de) {
133
+ continue;
134
+ }
135
+
136
+ const children = category.fields.children.de || [];
137
+
138
+ for (const child of children) {
139
+ getAllTagsFromCategory(child, allCategories);
140
+ }
141
+ }
142
+
143
+ return Object.values(allCategories);
144
+ }),
145
+ ]).then(([tags, categories]) => {
111
146
  setLoading(false);
112
- setData(response);
147
+ setTags(tags.items);
148
+ setCategories(categories);
113
149
  });
114
150
  }, [debouncedSearch, portal, locale]);
115
151
 
@@ -117,7 +153,7 @@ const LogicEditorInner = ({
117
153
  const orTags = selectedTags.filter((t) => t.type === "or");
118
154
 
119
155
  return <div className="flex flex-col gap-2 p-4">
120
- <div className="sticky top-0 z-10 flex w-full flex-col gap-4 border-b border-gray-200 bg-white py-4">
156
+ <div className="z-10 flex w-full flex-col gap-4 border-b border-gray-200 bg-white py-4">
121
157
  <div className="flex w-full flex-row items-center gap-2" style={{minHeight: 36}}>
122
158
  <SectionHeading style={{marginBottom: 0}}>Gewählte Zeitpunkte:</SectionHeading>
123
159
  <div className="flex flex-row items-center gap-2">
@@ -159,53 +195,55 @@ const LogicEditorInner = ({
159
195
  </div>
160
196
  <div className="flex w-full flex-row items-center gap-2" style={{minHeight: 36}}>
161
197
  <SectionHeading style={{marginBottom: 0}}>Gewählte Kategorien & Tags:</SectionHeading>
162
- <div className="grid w-full grid-cols-2 gap-4">
163
- <div>
164
- <SectionHeading style={{marginBottom: 0}}>Oder</SectionHeading>
198
+ <div className="grid grid-cols-2 gap-4">
199
+ <div className="flex flex-col gap-2">
200
+ <div>
201
+ <SectionHeading style={{marginBottom: 0}}>Oder</SectionHeading>
202
+ </div>
203
+ <div
204
+ ref={setOrNodeRef}
205
+ style={{
206
+ minHeight: 36,
207
+ backgroundColor: isOrOver ? "rgba(0, 255, 0, 0.3)" : "transparent",
208
+ }}
209
+ className="flex h-full w-full flex-row flex-wrap gap-2"
210
+ >
211
+ {orTags.map((tag) => {
212
+ return <DraggablePill
213
+ key={tag.id}
214
+ id={tag.id}
215
+ variant="active"
216
+ label={(`${tag.label}`)}
217
+ onClose={() => {
218
+ setSelectedTags(selectedTags.filter((t) => t.id !== tag.id));
219
+ }}
220
+ />;
221
+ })}
222
+ </div>
165
223
  </div>
166
- <div>
224
+ <div className="flex flex-col gap-2">
167
225
  <SectionHeading style={{marginBottom: 0}}>Und</SectionHeading>
168
- </div>
169
- <div
170
- ref={setOrNodeRef}
171
- style={{
172
- minHeight: 36,
173
- backgroundColor: isOrOver ? "rgba(0, 255, 0, 0.3)" : "transparent",
174
- }}
175
- className="flex w-full flex-row flex-wrap gap-2"
176
- >
177
- {orTags.map((tag) => {
178
- return <DraggablePill
179
- key={tag.id}
180
- id={tag.id}
181
- variant="active"
182
- label={(`${tag.label}`)}
183
- onClose={() => {
184
- setSelectedTags(selectedTags.filter((t) => t.id !== tag.id));
185
- }}
186
- />;
187
- })}
188
- </div>
189
- <div
190
- ref={setAndNodeRef}
191
- style={{
192
- minHeight: 36,
193
- backgroundColor: isAndOver ? "rgba(0, 255, 0, 0.3)" : "transparent",
194
- }}
195
- className="flex w-full flex-row flex-wrap gap-2"
196
- >
197
- {andTags.map((tag) => {
198
- return <DraggablePill
199
- key={tag.id}
200
- id={tag.id}
201
- testId={tag.id}
202
- variant="active"
203
- label={(`${tag.label}`)}
204
- onClose={() => {
205
- setSelectedTags(selectedTags.filter((t) => t.id !== tag.id));
206
- }}
207
- />;
208
- })}
226
+ <div
227
+ ref={setAndNodeRef}
228
+ style={{
229
+ minHeight: 36,
230
+ backgroundColor: isAndOver ? "rgba(0, 255, 0, 0.3)" : "transparent",
231
+ }}
232
+ className="flex h-full w-full flex-row flex-wrap gap-2"
233
+ >
234
+ {andTags.map((tag) => {
235
+ return <DraggablePill
236
+ key={tag.id}
237
+ id={tag.id}
238
+ testId={tag.id}
239
+ variant="active"
240
+ label={(`${tag.label}`)}
241
+ onClose={() => {
242
+ setSelectedTags(selectedTags.filter((t) => t.id !== tag.id));
243
+ }}
244
+ />;
245
+ })}
246
+ </div>
209
247
  </div>
210
248
  </div>
211
249
  </div>
@@ -217,28 +255,73 @@ const LogicEditorInner = ({
217
255
  />
218
256
  </div>
219
257
  </div>
220
- <div className="grid grid-cols-3 gap-4">
221
- {data?.items?.map((tag) => {
222
- return (
223
- <Checkbox
224
- key={tag.sys.id}
225
- isChecked={!!selectedTags.find((t) => t.id === tag.sys.id)}
226
- onChange={(e) => {
227
- if (e.target.checked) {
228
- setSelectedTags([...selectedTags, {
229
- label: tag.fields.title[locale],
230
- id: tag.sys.id,
231
- type: "or",
232
- }]);
233
- } else {
234
- setSelectedTags(selectedTags.filter((t) => t.id !== tag.sys.id));
235
- }
236
- }}
237
- >
238
- {tag.fields.title[locale]}
239
- </Checkbox>
240
- );
241
- })}
258
+ <div className="grid grid-cols-2 gap-4 overflow-y-auto">
259
+ <div className="flex flex-col gap-4">
260
+ <div>
261
+ <SectionHeading style={{marginBottom: 0}}>Kategorien</SectionHeading>
262
+ </div>
263
+ <div className="grid grid-cols-3 gap-4">
264
+ {categories?.map((category) => {
265
+ return (
266
+ <Checkbox
267
+ key={category.id}
268
+ isChecked={selectedTags.some((t) => category.tags.some((c) => c.id === t.id))}
269
+ onChange={(e) => {
270
+ let newTags = [...selectedTags];
271
+
272
+ if (e.target.checked) {
273
+ for (const tag of category.tags) {
274
+ if (!selectedTags.find((t) => t.id === tag.id)) {
275
+ newTags.push({
276
+ label: tag.label,
277
+ id: tag.id,
278
+ type: "or",
279
+ });
280
+ }
281
+ }
282
+ } else {
283
+ for (const tag of category.tags) {
284
+ newTags = newTags.filter((t) => t.id !== tag.id);
285
+ }
286
+ }
287
+
288
+ setSelectedTags(newTags);
289
+ }}
290
+ >
291
+ {category.title} ({selectedTags.filter((t) => category.tags.some((c) => c.id === t.id)).length}/{category.tags.length})
292
+ </Checkbox>
293
+ );
294
+ })}
295
+ </div>
296
+ </div>
297
+ <div className="flex flex-col gap-4">
298
+ <div>
299
+ <SectionHeading style={{marginBottom: 0}}>Tags</SectionHeading>
300
+ </div>
301
+ <div className="grid grid-cols-3 gap-4">
302
+ {tags?.map((tag) => {
303
+ return (
304
+ <Checkbox
305
+ key={tag.sys.id}
306
+ isChecked={!!selectedTags.find((t) => t.id === tag.sys.id)}
307
+ onChange={(e) => {
308
+ if (e.target.checked) {
309
+ setSelectedTags([...selectedTags, {
310
+ label: tag.fields.title[locale],
311
+ id: tag.sys.id,
312
+ type: "or",
313
+ }]);
314
+ } else {
315
+ setSelectedTags(selectedTags.filter((t) => t.id !== tag.sys.id));
316
+ }
317
+ }}
318
+ >
319
+ {tag.fields.title[locale]}
320
+ </Checkbox>
321
+ );
322
+ })}
323
+ </div>
324
+ </div>
242
325
  </div>
243
326
  <div className="sticky bottom-0 flex w-full flex-row justify-between gap-2 border-t border-gray-200 bg-white py-4">
244
327
  <Button
@@ -259,6 +342,62 @@ const LogicEditorInner = ({
259
342
  </div>;
260
343
  };
261
344
 
345
+ function getAllTagsFromCategory(categoryNode, allCategories = {}) {
346
+ if (!categoryNode || !categoryNode.fields?.children) {
347
+ return allCategories;
348
+ }
349
+
350
+ const children = categoryNode.fields.children.de || [];
351
+
352
+ allCategories[categoryNode.sys.id] = {
353
+ title: categoryNode.fields.title.de,
354
+ tags: [],
355
+ id: categoryNode.sys.id,
356
+ };
357
+
358
+ if (categoryNode?.fields?.tags?.de) {
359
+ for (const tag of categoryNode.fields.tags.de) {
360
+ if (!tag?.fields?.title) {
361
+ continue;
362
+ }
363
+
364
+ if (!allCategories[categoryNode.sys.id].tags.find((c) => c.id === tag.sys.id)) {
365
+ allCategories[categoryNode.sys.id].tags.push({
366
+ label: tag.fields.title.de,
367
+ id: tag.sys.id,
368
+ });
369
+ }
370
+ }
371
+ }
372
+
373
+ for (const child of children) {
374
+ if (!child || !child.fields) {
375
+ continue;
376
+ }
377
+
378
+ if (child?.fields?.tags?.de) {
379
+ for (const tag of child.fields.tags.de) {
380
+ if (!allCategories[categoryNode.sys.id].tags.find((c) => c.id === tag.sys.id)) {
381
+ if (!tag?.fields?.title) {
382
+ continue;
383
+ }
384
+
385
+ allCategories[categoryNode.sys.id].tags.push({
386
+ label: tag.fields.title.de,
387
+ id: tag.sys.id,
388
+ });
389
+ }
390
+ }
391
+ }
392
+
393
+ getAllTagsFromCategory(child, allCategories);
394
+ }
395
+
396
+ allCategories[categoryNode.sys.id].tags = [...new Set(allCategories[categoryNode.sys.id].tags)];
397
+
398
+ return allCategories;
399
+ }
400
+
262
401
 
263
402
  function DraggablePill({
264
403
  id, ...props
@@ -276,19 +415,21 @@ function DraggablePill({
276
415
  };
277
416
 
278
417
  return (
279
- <Pill
280
- dragHandleComponent={
281
- <DragHandle
282
- label="Reorder item"
283
- variant="transparent"
284
- {...attributes}
285
- {...listeners}
286
- />
287
- }
288
- isDraggable
289
- ref={setNodeRef}
290
- style={style}
291
- {...props}
292
- />
418
+ <div>
419
+ <Pill
420
+ dragHandleComponent={
421
+ <DragHandle
422
+ label="Reorder item"
423
+ variant="transparent"
424
+ {...attributes}
425
+ {...listeners}
426
+ />
427
+ }
428
+ isDraggable
429
+ ref={setNodeRef}
430
+ style={style}
431
+ {...props}
432
+ />
433
+ </div>
293
434
  );
294
435
  }