@vonaffenfels/contentful-teasermanager 1.1.69 → 1.2.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.
- package/package.json +6 -5
- package/src/components/Contentful/Dialog/LogicEditor.js +294 -0
- package/src/components/Contentful/Dialog/NewestArticles.js +36 -16
- package/src/components/Contentful/Dialog.js +36 -13
- package/src/components/Contentful/EntryEditor.js +62 -5
- package/src/components/Teasermanager.js +49 -17
- package/src/components/Teasermanager.module.css +33 -1
- package/dist/_base_slate-editor_src_dev_testComponents_TestStory2_js.js +0 -94
- package/dist/_base_slate-editor_src_dev_testComponents_TestStory3_js.js +0 -94
- package/dist/_base_slate-editor_src_dev_testComponents_TestStory_js.js +0 -94
- package/dist/index.html +0 -12
- package/dist/index.js +0 -64938
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vonaffenfels/contentful-teasermanager",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"prepublish": "yarn run build",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
17
17
|
"@babel/preset-env": "^7.13.15",
|
|
18
18
|
"@babel/preset-react": "^7.13.13",
|
|
19
|
-
"@contentful/app-sdk": "^
|
|
19
|
+
"@contentful/app-sdk": "^4.29.5",
|
|
20
20
|
"@contentful/f36-components": "^4.50.2",
|
|
21
21
|
"@contentful/field-editor-single-line": "^0.14.1",
|
|
22
22
|
"@contentful/field-editor-test-utils": "^0.11.1",
|
|
@@ -57,13 +57,14 @@
|
|
|
57
57
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
|
58
58
|
"@babel/preset-env": "^7.13.15",
|
|
59
59
|
"@babel/preset-react": "^7.13.13",
|
|
60
|
-
"@contentful/app-sdk": "^
|
|
60
|
+
"@contentful/app-sdk": "^4.29.5",
|
|
61
61
|
"@contentful/f36-components": "^4.50.2",
|
|
62
62
|
"@contentful/field-editor-single-line": "^0.14.1",
|
|
63
63
|
"@contentful/field-editor-test-utils": "^0.11.1",
|
|
64
64
|
"@contentful/forma-36-fcss": "^0.3.1",
|
|
65
65
|
"@contentful/forma-36-react-components": "^3.88.3",
|
|
66
66
|
"@contentful/forma-36-tokens": "^0.10.1",
|
|
67
|
+
"@dnd-kit/core": "^6.3.1",
|
|
67
68
|
"@svgr/webpack": "^5.5.0",
|
|
68
69
|
"babel-loader": "^8.2.2",
|
|
69
70
|
"classnames": "^2.3.2",
|
|
@@ -96,10 +97,10 @@
|
|
|
96
97
|
"webpack-dev-server": "^4.0.0-beta.2"
|
|
97
98
|
},
|
|
98
99
|
"dependencies": {
|
|
99
|
-
"@vonaffenfels/slate-editor": "^1.
|
|
100
|
+
"@vonaffenfels/slate-editor": "^1.2.0",
|
|
100
101
|
"webpack": "5.88.2"
|
|
101
102
|
},
|
|
102
|
-
"gitHead": "
|
|
103
|
+
"gitHead": "fe9b4ce59e9b4dfae924c580f3a1247c180f4d9b",
|
|
103
104
|
"publishConfig": {
|
|
104
105
|
"access": "public"
|
|
105
106
|
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TextInput,
|
|
3
|
+
Pill,
|
|
4
|
+
SectionHeading,
|
|
5
|
+
Checkbox,
|
|
6
|
+
Button,
|
|
7
|
+
DragHandle,
|
|
8
|
+
} from "@contentful/f36-components";
|
|
9
|
+
import {
|
|
10
|
+
DndContext, useDraggable, useDroppable,
|
|
11
|
+
PointerSensor, useSensor, useSensors,
|
|
12
|
+
} from '@dnd-kit/core';
|
|
13
|
+
import {
|
|
14
|
+
useEffect, useState,
|
|
15
|
+
} from "react";
|
|
16
|
+
import useDebounce from "../../../hooks/useDebounce";
|
|
17
|
+
import {getContentfulClient} from "../../../lib/contentfulClient";
|
|
18
|
+
import {PlusIcon} from "@contentful/f36-icons";
|
|
19
|
+
import {CSS} from "@dnd-kit/utilities";
|
|
20
|
+
|
|
21
|
+
export const LogicEditor = (props) => {
|
|
22
|
+
const [selectedTags, setSelectedTags] = useState([]);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setSelectedTags(props?.logicData?.tags || []);
|
|
26
|
+
}, [props?.logicData?.tags]);
|
|
27
|
+
|
|
28
|
+
const handleDragEnd = (event) => {
|
|
29
|
+
const {
|
|
30
|
+
over,
|
|
31
|
+
active,
|
|
32
|
+
} = event;
|
|
33
|
+
|
|
34
|
+
if (over) {
|
|
35
|
+
const draggedTagIndex = selectedTags.findIndex(tag => tag.id === active.id);
|
|
36
|
+
if (draggedTagIndex !== -1) {
|
|
37
|
+
const updatedTags = [...selectedTags];
|
|
38
|
+
updatedTags[draggedTagIndex] = {
|
|
39
|
+
...updatedTags[draggedTagIndex],
|
|
40
|
+
type: over.id, // "and" or "or"
|
|
41
|
+
};
|
|
42
|
+
setSelectedTags(updatedTags);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return <DndContext onDragEnd={handleDragEnd}>
|
|
48
|
+
<LogicEditorInner {...props} selectedTags={selectedTags} setSelectedTags={setSelectedTags} />
|
|
49
|
+
</DndContext>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const LogicEditorInner = ({
|
|
53
|
+
onSave = () => alert("onSave missing"),
|
|
54
|
+
sdk,
|
|
55
|
+
logicData = {
|
|
56
|
+
timepoints: [7, 12, 18],
|
|
57
|
+
tags: [],
|
|
58
|
+
},
|
|
59
|
+
selectedTags,
|
|
60
|
+
setSelectedTags,
|
|
61
|
+
}) => {
|
|
62
|
+
const contentfulClient = getContentfulClient();
|
|
63
|
+
const [loading, setLoading] = useState(false);
|
|
64
|
+
const [data, setData] = useState([]);
|
|
65
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
66
|
+
const [addTimepoint, setAddTimepoint] = useState(7);
|
|
67
|
+
const debouncedSearch = useDebounce(searchQuery, 500);
|
|
68
|
+
const [selectedTimepoints, setSelectedTimepoints] = useState([]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
setSelectedTimepoints(logicData?.timepoints || []);
|
|
71
|
+
}, [logicData?.timepoints]);
|
|
72
|
+
const {
|
|
73
|
+
setNodeRef: setOrNodeRef,
|
|
74
|
+
isOver: isOrOver,
|
|
75
|
+
node: orNode,
|
|
76
|
+
} = useDroppable({id: 'or'});
|
|
77
|
+
const {
|
|
78
|
+
setNodeRef: setAndNodeRef,
|
|
79
|
+
isOver: isAndOver,
|
|
80
|
+
node: andNode,
|
|
81
|
+
} = useDroppable({id: "and"});
|
|
82
|
+
|
|
83
|
+
const {
|
|
84
|
+
portal,
|
|
85
|
+
locale = "de",
|
|
86
|
+
} = sdk.parameters.invocation;
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
setLoading(true);
|
|
90
|
+
const params = {
|
|
91
|
+
limit: 100,
|
|
92
|
+
skip: 0,
|
|
93
|
+
content_type: "tags",
|
|
94
|
+
locale,
|
|
95
|
+
order: "-sys.updatedAt",
|
|
96
|
+
"fields.portal": portal,
|
|
97
|
+
select: [
|
|
98
|
+
"fields.title",
|
|
99
|
+
"fields.isCategory",
|
|
100
|
+
"fields.hidden",
|
|
101
|
+
"sys.publishedAt",
|
|
102
|
+
"sys.id",
|
|
103
|
+
].join(","),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if (debouncedSearch) {
|
|
107
|
+
params["fields.title[match]"] = debouncedSearch;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
contentfulClient.getEntries(params).then((response) => {
|
|
111
|
+
setLoading(false);
|
|
112
|
+
setData(response);
|
|
113
|
+
});
|
|
114
|
+
}, [debouncedSearch, portal, locale]);
|
|
115
|
+
|
|
116
|
+
const andTags = selectedTags.filter((t) => t.type === "and");
|
|
117
|
+
const orTags = selectedTags.filter((t) => t.type === "or");
|
|
118
|
+
|
|
119
|
+
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">
|
|
121
|
+
<div className="flex w-full flex-row items-center gap-2" style={{minHeight: 36}}>
|
|
122
|
+
<SectionHeading style={{marginBottom: 0}}>Gewählte Zeitpunkte:</SectionHeading>
|
|
123
|
+
<div className="flex flex-row items-center gap-2">
|
|
124
|
+
{selectedTimepoints.map((timepoint) => {
|
|
125
|
+
return <Pill
|
|
126
|
+
key={timepoint}
|
|
127
|
+
draggable={false}
|
|
128
|
+
testId="pill-item"
|
|
129
|
+
variant="active"
|
|
130
|
+
label={`${timepoint} Uhr`}
|
|
131
|
+
onClose={() => {
|
|
132
|
+
setSelectedTimepoints(selectedTimepoints.filter((t) => t !== timepoint));
|
|
133
|
+
}}
|
|
134
|
+
/>;
|
|
135
|
+
})}
|
|
136
|
+
<TextInput
|
|
137
|
+
type="number"
|
|
138
|
+
min={1}
|
|
139
|
+
max={24}
|
|
140
|
+
value={addTimepoint}
|
|
141
|
+
onChange={(e) => {
|
|
142
|
+
setAddTimepoint(e.target.value);
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
<SectionHeading style={{marginBottom: 0}}>Uhr</SectionHeading>
|
|
146
|
+
<Button
|
|
147
|
+
variant="secondary"
|
|
148
|
+
onClick={() => {
|
|
149
|
+
if (selectedTimepoints.includes(addTimepoint)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
setSelectedTimepoints([...selectedTimepoints, addTimepoint]);
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
<PlusIcon />
|
|
157
|
+
</Button>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
<div className="flex w-full flex-row items-center gap-2" style={{minHeight: 36}}>
|
|
161
|
+
<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>
|
|
165
|
+
</div>
|
|
166
|
+
<div>
|
|
167
|
+
<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
|
+
})}
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
<div className="flex w-full flex-row gap-2">
|
|
213
|
+
<TextInput
|
|
214
|
+
placeholder="Suche nach Kategorie oder Tag"
|
|
215
|
+
value={searchQuery}
|
|
216
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
</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
|
+
})}
|
|
242
|
+
</div>
|
|
243
|
+
<div className="sticky bottom-0 flex w-full flex-row justify-between gap-2 border-t border-gray-200 bg-white py-4">
|
|
244
|
+
<Button
|
|
245
|
+
variant="positive"
|
|
246
|
+
type="button"
|
|
247
|
+
onClick={() => onSave({
|
|
248
|
+
tags: selectedTags,
|
|
249
|
+
timepoints: selectedTimepoints,
|
|
250
|
+
})}>Speichern</Button>
|
|
251
|
+
<Button
|
|
252
|
+
variant="negative"
|
|
253
|
+
type="button"
|
|
254
|
+
onClick={() => onSave({
|
|
255
|
+
tags: [],
|
|
256
|
+
timepoints: [],
|
|
257
|
+
})}>Logik löschen</Button>
|
|
258
|
+
</div>
|
|
259
|
+
</div>;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
function DraggablePill({
|
|
264
|
+
id, ...props
|
|
265
|
+
}) {
|
|
266
|
+
const {
|
|
267
|
+
attributes,
|
|
268
|
+
listeners,
|
|
269
|
+
setNodeRef,
|
|
270
|
+
transform,
|
|
271
|
+
transition,
|
|
272
|
+
} = useDraggable({id});
|
|
273
|
+
const style = {
|
|
274
|
+
transform: CSS.Translate.toString(transform),
|
|
275
|
+
transition,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
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
|
+
/>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
EntityList, Pagination, Spinner, Select, Autocomplete, TextInput,
|
|
3
|
+
} from "@contentful/f36-components";
|
|
2
4
|
import {getContentfulClient} from "../../../lib/contentfulClient";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
+
import {
|
|
6
|
+
useEffect, useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
import format from "date-fns/format";
|
|
5
9
|
import useDebounce from "../../../hooks/useDebounce";
|
|
6
10
|
import classNames from "classnames";
|
|
7
11
|
|
|
@@ -12,7 +16,10 @@ export const NewestArticles = ({
|
|
|
12
16
|
onEntryClick = () => alert("onEntryClick missing"),
|
|
13
17
|
getArticleThumbnailUrl = (article) => alert("getArticleThumbnailUrl missing"),
|
|
14
18
|
}) => {
|
|
15
|
-
const {
|
|
19
|
+
const {
|
|
20
|
+
portal,
|
|
21
|
+
slotId,
|
|
22
|
+
} = sdk.parameters.invocation;
|
|
16
23
|
const contentfulClient = getContentfulClient();
|
|
17
24
|
const limit = 25;
|
|
18
25
|
const [searchQuery, setSearchQuery] = useState("");
|
|
@@ -20,7 +27,10 @@ export const NewestArticles = ({
|
|
|
20
27
|
const [enabledPortals, setEnabledPortals] = useState([portal]);
|
|
21
28
|
const [selectedPortal, setSelectedPortal] = useState(portal);
|
|
22
29
|
const [loading, setLoading] = useState(true);
|
|
23
|
-
const [data, setData] = useState({
|
|
30
|
+
const [data, setData] = useState({
|
|
31
|
+
items: [],
|
|
32
|
+
total: 0,
|
|
33
|
+
});
|
|
24
34
|
const [page, setPage] = useState(0);
|
|
25
35
|
const [selectedAuthor, setSelectedAuthor] = useState(null);
|
|
26
36
|
const [authors, setAuthors] = useState([]);
|
|
@@ -51,6 +61,16 @@ export const NewestArticles = ({
|
|
|
51
61
|
content_type: "article",
|
|
52
62
|
order: "-fields.teaserDate",
|
|
53
63
|
"fields.portal": selectedPortal,
|
|
64
|
+
select: [
|
|
65
|
+
"fields.title",
|
|
66
|
+
"fields.authors",
|
|
67
|
+
"fields.teaserDate",
|
|
68
|
+
"fields.image",
|
|
69
|
+
"fields.date",
|
|
70
|
+
"fields.portal",
|
|
71
|
+
"sys.publishedAt",
|
|
72
|
+
"sys.id",
|
|
73
|
+
].join(","),
|
|
54
74
|
};
|
|
55
75
|
|
|
56
76
|
if (searchQuery) {
|
|
@@ -65,7 +85,7 @@ export const NewestArticles = ({
|
|
|
65
85
|
setLoading(false);
|
|
66
86
|
setData(response);
|
|
67
87
|
});
|
|
68
|
-
}, [debouncedSearch, selectedAuthor,selectedPortal, limit, page]);
|
|
88
|
+
}, [debouncedSearch, selectedAuthor, selectedPortal, limit, page]);
|
|
69
89
|
|
|
70
90
|
useEffect(() => {
|
|
71
91
|
setAuthorsLoading(true);
|
|
@@ -77,7 +97,7 @@ export const NewestArticles = ({
|
|
|
77
97
|
};
|
|
78
98
|
|
|
79
99
|
if (debouncedAuthorSearch) {
|
|
80
|
-
params["fields.name[match]"] = debouncedAuthorSearch
|
|
100
|
+
params["fields.name[match]"] = debouncedAuthorSearch;
|
|
81
101
|
} else {
|
|
82
102
|
setSelectedAuthor(null);
|
|
83
103
|
}
|
|
@@ -97,15 +117,15 @@ export const NewestArticles = ({
|
|
|
97
117
|
}, [authors, selectedAuthor]);
|
|
98
118
|
|
|
99
119
|
return (
|
|
100
|
-
<div className="
|
|
120
|
+
<div className="flex flex-col space-y-4 p-4">
|
|
101
121
|
<div className="flex space-x-2">
|
|
102
|
-
<div className="
|
|
122
|
+
<div className="grow">
|
|
103
123
|
<TextInput
|
|
104
124
|
width={"full"}
|
|
105
125
|
placeholder={"Suche nach Titel"}
|
|
106
126
|
onChange={(e) => setSearchQuery(e.target.value)}/>
|
|
107
127
|
</div>
|
|
108
|
-
<div className="
|
|
128
|
+
<div className="grow-0">
|
|
109
129
|
<Autocomplete
|
|
110
130
|
isLoading={authorsLoading}
|
|
111
131
|
onSelectItem={item => setSelectedAuthor(item?.sys?.id)}
|
|
@@ -119,11 +139,11 @@ export const NewestArticles = ({
|
|
|
119
139
|
noMatchesMessage={'Kein Autor gefunden'}
|
|
120
140
|
>
|
|
121
141
|
{(options) => {
|
|
122
|
-
return options.map(option => <span key={option.value}>{option.label}</span>)
|
|
142
|
+
return options.map(option => <span key={option.value}>{option.label}</span>);
|
|
123
143
|
}}
|
|
124
144
|
</Autocomplete>
|
|
125
145
|
</div>
|
|
126
|
-
<div className="
|
|
146
|
+
<div className="grow-0">
|
|
127
147
|
<Select value={selectedPortal} onChange={e => setSelectedPortal(e.target.value)}>
|
|
128
148
|
{Object.keys(portals).filter(v => enabledPortals.includes(v)).sort().map(optionValue => <Select.Option
|
|
129
149
|
key={optionValue}
|
|
@@ -135,7 +155,7 @@ export const NewestArticles = ({
|
|
|
135
155
|
{loading && <Spinner/>}
|
|
136
156
|
{!loading && data.items.map((entry) => {
|
|
137
157
|
const isCurrentlySelectedInOtherSlot = !! Object.keys(slotState || {}).find(slot => slotState[slot] === entry.sys.id && slot !== slotId);
|
|
138
|
-
const isCurrentlySelectedInSlot =
|
|
158
|
+
const isCurrentlySelectedInSlot = !!Object.keys(slotState || {}).find(slot => slotState[slot] === entry.sys.id && slot === slotId);
|
|
139
159
|
|
|
140
160
|
let status = "draft";
|
|
141
161
|
if (entry.sys.publishedAt) {
|
|
@@ -146,7 +166,7 @@ export const NewestArticles = ({
|
|
|
146
166
|
if (isCurrentlySelectedInSlot) {
|
|
147
167
|
description = "Aktuell ausgewählt";
|
|
148
168
|
} else if (isCurrentlySelectedInOtherSlot) {
|
|
149
|
-
description = "Aktuell in anderem Teaser ausgewählt"
|
|
169
|
+
description = "Aktuell in anderem Teaser ausgewählt";
|
|
150
170
|
}
|
|
151
171
|
|
|
152
172
|
return <div
|
|
@@ -165,7 +185,7 @@ export const NewestArticles = ({
|
|
|
165
185
|
description={description}
|
|
166
186
|
status={status}
|
|
167
187
|
/>
|
|
168
|
-
</div
|
|
188
|
+
</div>;
|
|
169
189
|
})}
|
|
170
190
|
</EntityList>
|
|
171
191
|
<Pagination
|
|
@@ -176,4 +196,4 @@ export const NewestArticles = ({
|
|
|
176
196
|
/>
|
|
177
197
|
</div>
|
|
178
198
|
);
|
|
179
|
-
}
|
|
199
|
+
};
|
|
@@ -3,6 +3,7 @@ import React, {
|
|
|
3
3
|
} from 'react';
|
|
4
4
|
import {NewestArticles} from "./Dialog/NewestArticles";
|
|
5
5
|
import {NoAccess} from "../NoAccess";
|
|
6
|
+
import {LogicEditor} from './Dialog/LogicEditor';
|
|
6
7
|
|
|
7
8
|
const Dialog = ({
|
|
8
9
|
sdk,
|
|
@@ -10,9 +11,20 @@ const Dialog = ({
|
|
|
10
11
|
getArticleThumbnailUrl,
|
|
11
12
|
}) => {
|
|
12
13
|
const [slotState, setSlotState] = useState({});
|
|
14
|
+
const [logicData, setLogicData] = useState({});
|
|
13
15
|
|
|
14
16
|
const selectEntry = (entry) => {
|
|
15
|
-
sdk.close(
|
|
17
|
+
sdk.close({
|
|
18
|
+
entry,
|
|
19
|
+
isArticleResponse: true,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const onSave = (data) => {
|
|
24
|
+
sdk.close({
|
|
25
|
+
data,
|
|
26
|
+
isLogicResponse: true,
|
|
27
|
+
});
|
|
16
28
|
};
|
|
17
29
|
|
|
18
30
|
const loadSlotStateForPage = async () => {
|
|
@@ -28,30 +40,41 @@ const Dialog = ({
|
|
|
28
40
|
console.error(response.message);
|
|
29
41
|
}
|
|
30
42
|
|
|
31
|
-
return response
|
|
43
|
+
return response || {};
|
|
32
44
|
} catch (e) {
|
|
33
45
|
console.error(e);
|
|
34
46
|
}
|
|
35
47
|
};
|
|
36
48
|
|
|
37
49
|
useEffect(() => {
|
|
38
|
-
loadSlotStateForPage().then(
|
|
50
|
+
loadSlotStateForPage().then(({
|
|
51
|
+
data = {},
|
|
52
|
+
logicData = {},
|
|
53
|
+
}) => {
|
|
54
|
+
setSlotState(data);
|
|
55
|
+
setLogicData(logicData[sdk.parameters.invocation.slotId]);
|
|
56
|
+
});
|
|
39
57
|
}, [sdk.parameters.invocation]);
|
|
40
58
|
|
|
41
59
|
return <div style={{
|
|
42
60
|
backgroundColor: "#FFFFFF",
|
|
43
61
|
minHeight: "100vh",
|
|
44
62
|
}}>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
{sdk.parameters.invocation.isLogicEditor ?
|
|
64
|
+
<LogicEditor
|
|
65
|
+
sdk={sdk}
|
|
66
|
+
onSave={onSave}
|
|
67
|
+
logicData={logicData}
|
|
68
|
+
portals={portals}
|
|
69
|
+
/> :
|
|
70
|
+
<NewestArticles
|
|
71
|
+
sdk={sdk}
|
|
72
|
+
onEntryClick={selectEntry}
|
|
73
|
+
slotState={slotState}
|
|
74
|
+
getArticleThumbnailUrl={getArticleThumbnailUrl}
|
|
75
|
+
portals={portals}
|
|
76
|
+
/>
|
|
77
|
+
}
|
|
55
78
|
</div>;
|
|
56
79
|
};
|
|
57
80
|
|
|
@@ -42,8 +42,11 @@ const Entry = ({sdk}) => {
|
|
|
42
42
|
slotId: slotId,
|
|
43
43
|
entryId: sdk.entry.getSys().id,
|
|
44
44
|
},
|
|
45
|
-
}).then(async (
|
|
46
|
-
|
|
45
|
+
}).then(async ({
|
|
46
|
+
entry,
|
|
47
|
+
isLogicResponse,
|
|
48
|
+
} = {}) => {
|
|
49
|
+
if (!entry || isLogicResponse) {
|
|
47
50
|
return;
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -69,12 +72,61 @@ const Entry = ({sdk}) => {
|
|
|
69
72
|
});
|
|
70
73
|
};
|
|
71
74
|
|
|
75
|
+
const onSlotClickLogic = (slotId, currentDate) => {
|
|
76
|
+
const entryId = sdk.entry.getSys().id;
|
|
77
|
+
return sdk.dialogs.openCurrentApp({
|
|
78
|
+
title: "Positionslogik bearbeiten",
|
|
79
|
+
width: "fullWidth",
|
|
80
|
+
position: "top",
|
|
81
|
+
allowHeightOverflow: true,
|
|
82
|
+
minHeight: "500px",
|
|
83
|
+
shouldCloseOnOverlayClick: true,
|
|
84
|
+
shouldCloseOnEscapePress: true,
|
|
85
|
+
parameters: {
|
|
86
|
+
portal,
|
|
87
|
+
slotId: slotId,
|
|
88
|
+
entryId: entryId,
|
|
89
|
+
isLogicEditor: true,
|
|
90
|
+
date: currentDate,
|
|
91
|
+
},
|
|
92
|
+
}).then(async ({
|
|
93
|
+
data: {
|
|
94
|
+
timepoints,
|
|
95
|
+
tags,
|
|
96
|
+
} = {},
|
|
97
|
+
isArticleResponse,
|
|
98
|
+
} = {}) => {
|
|
99
|
+
if (!timepoints || !tags || isArticleResponse) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log(`Selected timepoints ${timepoints.map(v => `${v} Uhr`).join(", ")} and tags ${tags.map(v => `${v.label} (${v.id})`).join(", ")} for page ${entryId} with portal ${portal} for slot ${slotId}`);
|
|
104
|
+
|
|
105
|
+
const {apiRoot} = sdk.parameters.instance;
|
|
106
|
+
try {
|
|
107
|
+
await fetch(`${apiRoot}/api/saveLogicForSlot`, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
body: JSON.stringify({
|
|
110
|
+
"project": portal,
|
|
111
|
+
"slot": slotId,
|
|
112
|
+
"page": entryId,
|
|
113
|
+
"timepoints": timepoints,
|
|
114
|
+
"tags": tags,
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.error(e);
|
|
119
|
+
alert("Fehler beim speichern!");
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
72
124
|
const removeSlotData = async (slotId, currentDate) => {
|
|
73
125
|
console.log(`Removing slot data for page ${sdk.entry.getSys().id} with portal ${portal} for slot ${slotId} and date ${currentDate}`);
|
|
74
126
|
|
|
75
127
|
try {
|
|
76
128
|
const apiRoot = sdk.parameters.instance.apiRoot;
|
|
77
|
-
|
|
129
|
+
await fetch(`${apiRoot}/api/saveStateForSlot`, {
|
|
78
130
|
method: "POST",
|
|
79
131
|
body: JSON.stringify({
|
|
80
132
|
"project": portal,
|
|
@@ -100,7 +152,10 @@ const Entry = ({sdk}) => {
|
|
|
100
152
|
console.error(response.message);
|
|
101
153
|
}
|
|
102
154
|
|
|
103
|
-
return response
|
|
155
|
+
return response || {
|
|
156
|
+
data: {},
|
|
157
|
+
logicData: {},
|
|
158
|
+
};
|
|
104
159
|
} catch (e) {
|
|
105
160
|
console.error(e);
|
|
106
161
|
}
|
|
@@ -133,7 +188,9 @@ const Entry = ({sdk}) => {
|
|
|
133
188
|
loadSlotStateForPage={loadSlotStateForPage}
|
|
134
189
|
removeSlotData={removeSlotData}
|
|
135
190
|
loadTimelineStateForPage={loadTimelineStateForPage}
|
|
136
|
-
onSlotClick={onSlotClick}
|
|
191
|
+
onSlotClick={onSlotClick}
|
|
192
|
+
onSlotClickLogic={onSlotClickLogic}
|
|
193
|
+
/>;
|
|
137
194
|
};
|
|
138
195
|
|
|
139
196
|
export default Entry;
|