@hrbolek/uoisfrontend-template 0.6.1 → 0.6.3
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/dist/cjs/index.js +447 -0
- package/dist/es/index.js +11062 -0
- package/dist/umd/index.js +447 -0
- package/package.json +5 -1
- package/src/Base/Components/Link.jsx +13 -8
- package/src/Base/Mutations/General.jsx +3 -1
- package/src/Base/Mutations/Update.jsx +5 -0
- package/src/EventGQLModel/Components/A4Plan/A4Plan.jsx +363 -0
- package/src/EventGQLModel/Components/A4Plan/index.js +1 -0
- package/src/EventGQLModel/Components/CardCapsule.jsx +43 -0
- package/src/EventGQLModel/Components/Children.jsx +31 -0
- package/src/EventGQLModel/Components/ConfirmEdit.jsx +61 -0
- package/src/EventGQLModel/Components/Filter.jsx +14 -0
- package/src/EventGQLModel/Components/LargeCard.jsx +54 -0
- package/src/EventGQLModel/Components/Link.jsx +55 -0
- package/src/EventGQLModel/Components/LiveEdit.jsx +111 -0
- package/src/EventGQLModel/Components/MediumCard.jsx +39 -0
- package/src/EventGQLModel/Components/MediumContent.jsx +96 -0
- package/src/EventGQLModel/Components/MediumEditableContent.jsx +35 -0
- package/src/EventGQLModel/Components/Plan/PlanRow.jsx +470 -0
- package/src/EventGQLModel/Components/Plan/_utils.js +971 -0
- package/src/EventGQLModel/Components/Plan/calendarReducer.js +535 -0
- package/src/EventGQLModel/Components/Plan/index.js +3 -0
- package/src/EventGQLModel/Components/Table.jsx +7 -0
- package/src/EventGQLModel/Components/index.js +15 -0
- package/src/EventGQLModel/Mutations/Create.jsx +202 -0
- package/src/EventGQLModel/Mutations/Delete.jsx +173 -0
- package/src/EventGQLModel/Mutations/InteractiveMutations.jsx +30 -0
- package/src/EventGQLModel/Mutations/Update.jsx +147 -0
- package/src/EventGQLModel/Mutations/helpers.jsx +7 -0
- package/src/EventGQLModel/Pages/PageBase.jsx +56 -0
- package/src/EventGQLModel/Pages/PageCreateItem.jsx +28 -0
- package/src/EventGQLModel/Pages/PageDeleteItem.jsx +16 -0
- package/src/EventGQLModel/Pages/PageNavbar.jsx +160 -0
- package/src/EventGQLModel/Pages/PagePlan.jsx +42 -0
- package/src/EventGQLModel/Pages/PageReadItem.jsx +11 -0
- package/src/EventGQLModel/Pages/PageReadItemEx.jsx +42 -0
- package/src/EventGQLModel/Pages/PageSubevents.jsx +43 -0
- package/src/EventGQLModel/Pages/PageUpdateItem.jsx +14 -0
- package/src/EventGQLModel/Pages/PageVector.jsx +80 -0
- package/src/EventGQLModel/Pages/RouterSegment.jsx +82 -0
- package/src/EventGQLModel/Pages/index.js +2 -0
- package/src/EventGQLModel/Queries/DeleteAsyncAction.jsx +32 -0
- package/src/EventGQLModel/Queries/Fragments.jsx +123 -0
- package/src/EventGQLModel/Queries/InsertAsyncAction.jsx +40 -0
- package/src/EventGQLModel/Queries/ReadAsyncAction.jsx +44 -0
- package/src/EventGQLModel/Queries/ReadPageAsyncAction.jsx +13 -0
- package/src/EventGQLModel/Queries/ReadSubEventsAsyncAction.jsx +44 -0
- package/src/EventGQLModel/Queries/SearchAsyncAction.jsx +16 -0
- package/src/EventGQLModel/Queries/UpdateAsyncAction.jsx +40 -0
- package/src/EventGQLModel/Queries/index.js +6 -0
- package/src/EventGQLModel/Scalars/ScalarAttribute.jsx +54 -0
- package/src/EventGQLModel/Scalars/TemplateScalarAttribute.jsx +88 -0
- package/src/EventGQLModel/Scalars/index.js +1 -0
- package/src/EventGQLModel/Vectors/TemplateVectorsAttribute.jsx +326 -0
- package/src/EventGQLModel/Vectors/VectorAttribute.jsx +56 -0
- package/src/EventGQLModel/Vectors/index.js +1 -0
- package/src/EventGQLModel/WhatToDo.md +44 -0
- package/src/EventGQLModel/index.js +71 -0
- package/src/GroupGQLModel/Mutations/Create.jsx +8 -2
- package/src/GroupGQLModel/Mutations/Delete.jsx +8 -2
- package/src/GroupGQLModel/Mutations/Update.jsx +8 -8
- package/src/GroupGQLModel/Queries/Fragments.jsx +17 -1
- package/src/GroupGQLModel/Scalars/RBACObject.jsx +17 -5
- package/src/GroupGQLModel/Vectors/GroupMemberships.jsx +1 -1
- package/src/UserGQLModel/Components/MediumContent.jsx +9 -3
- package/src/UserGQLModel/Queries/Fragments.jsx +6 -0
- package/src/_Template/WhatToDo.md +1 -1
- package/index.html +0 -104
- package/vite.config.js +0 -47
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* calendarReducer.js
|
|
3
|
+
* Minimalistický reducer pro klikací rozvrh nad intervaly v celých dnech
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} Event
|
|
8
|
+
* @property {string} id
|
|
9
|
+
* @property {string} startDate - YYYY-MM-DD
|
|
10
|
+
* @property {string} endDate - YYYY-MM-DD
|
|
11
|
+
* @property {string} typeId
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} SelectedType
|
|
16
|
+
* @property {string} id
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {Object} State
|
|
21
|
+
* @property {Event[]} events
|
|
22
|
+
* @property {SelectedType|null} selectedType
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Vrátí datum posunuté o zadaný počet dní.
|
|
27
|
+
* @param {string} dayStr - Datum ve formátu YYYY-MM-DD.
|
|
28
|
+
* @param {number} amount - Počet dní k posunutí.
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
const addDays = (dayStr, amount) => {
|
|
32
|
+
const d = new Date(dayStr + "T00:00:00");
|
|
33
|
+
d.setDate(d.getDate() + amount);
|
|
34
|
+
const y = d.getFullYear();
|
|
35
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
36
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
37
|
+
return `${y}-${m}-${day}`;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Vrátí interval ve správném pořadí od menšího do většího data.
|
|
42
|
+
* @param {string} startDate
|
|
43
|
+
* @param {string} endDate
|
|
44
|
+
* @returns {{startDate: string, endDate: string}}
|
|
45
|
+
*/
|
|
46
|
+
export const normalizeRange = (startDate, endDate) => {
|
|
47
|
+
return startDate <= endDate
|
|
48
|
+
? { startDate, endDate }
|
|
49
|
+
: { startDate: endDate, endDate: startDate };
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Zjistí, zda se event překrývá s intervalem.
|
|
54
|
+
* @param {Event} event
|
|
55
|
+
* @param {string} startDate
|
|
56
|
+
* @param {string} endDate
|
|
57
|
+
* @returns {boolean}
|
|
58
|
+
*/
|
|
59
|
+
export const overlapsRange = (event, startDate, endDate) => {
|
|
60
|
+
return event.startDate <= endDate && startDate <= event.endDate;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Zjistí, zda event plně obsahuje interval.
|
|
65
|
+
* @param {Event} event
|
|
66
|
+
* @param {string} startDate
|
|
67
|
+
* @param {string} endDate
|
|
68
|
+
* @returns {boolean}
|
|
69
|
+
*/
|
|
70
|
+
export const containsRange = (event, startDate, endDate) => {
|
|
71
|
+
return event.startDate <= startDate && endDate <= event.endDate;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Vytvoří nové unikátní ID.
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
export const makeId = () => crypto.randomUUID();
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Seřadí eventy podle začátku a konce.
|
|
82
|
+
* @param {Event[]} events
|
|
83
|
+
* @returns {Event[]}
|
|
84
|
+
*/
|
|
85
|
+
export const sortEvents = (events) => {
|
|
86
|
+
return [...events].sort((a, b) => {
|
|
87
|
+
if (a.startDate !== b.startDate) {
|
|
88
|
+
return a.startDate.localeCompare(b.startDate);
|
|
89
|
+
}
|
|
90
|
+
return a.endDate.localeCompare(b.endDate);
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Odebere z eventu zadaný interval.
|
|
96
|
+
* Může vrátit:
|
|
97
|
+
* - []
|
|
98
|
+
* - [event]
|
|
99
|
+
* - [leftPart, rightPart]
|
|
100
|
+
*
|
|
101
|
+
* @param {Event} event
|
|
102
|
+
* @param {string} startDate
|
|
103
|
+
* @param {string} endDate
|
|
104
|
+
* @returns {Event[]}
|
|
105
|
+
*/
|
|
106
|
+
export const removeRangeFromEvent = (event, startDate, endDate) => {
|
|
107
|
+
if (!overlapsRange(event, startDate, endDate)) {
|
|
108
|
+
return [event];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const cutsLeft = startDate <= event.startDate;
|
|
112
|
+
const cutsRight = endDate >= event.endDate;
|
|
113
|
+
|
|
114
|
+
// interval smaže celý event
|
|
115
|
+
if (cutsLeft && cutsRight) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ukrojí levou část eventu
|
|
120
|
+
if (cutsLeft) {
|
|
121
|
+
return [
|
|
122
|
+
{
|
|
123
|
+
...event,
|
|
124
|
+
startDate: addDays(endDate, 1)
|
|
125
|
+
}
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ukrojí pravou část eventu
|
|
130
|
+
if (cutsRight) {
|
|
131
|
+
return [
|
|
132
|
+
{
|
|
133
|
+
...event,
|
|
134
|
+
endDate: addDays(startDate, -1)
|
|
135
|
+
}
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// interval je uvnitř eventu => rozdělení na dva eventy
|
|
140
|
+
return [
|
|
141
|
+
{
|
|
142
|
+
...event,
|
|
143
|
+
endDate: addDays(startDate, -1)
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
...event,
|
|
147
|
+
id: makeId(),
|
|
148
|
+
startDate: addDays(endDate, 1)
|
|
149
|
+
}
|
|
150
|
+
];
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Sloučí sousední nebo překrývající se eventy stejného typu
|
|
155
|
+
* a zachová stabilní identitu původních eventů.
|
|
156
|
+
*
|
|
157
|
+
* Pokud se spojuje původní event a synteticky vytvořený event,
|
|
158
|
+
* přednost dostane ID původního eventu.
|
|
159
|
+
*
|
|
160
|
+
* Neměněné eventy vrací ve stejné referenci.
|
|
161
|
+
*
|
|
162
|
+
* @param {Event[]} events
|
|
163
|
+
* @returns {Event[]}
|
|
164
|
+
*/
|
|
165
|
+
export const normalizeEvents = (events) => {
|
|
166
|
+
const sorted = sortEvents(events);
|
|
167
|
+
const result = [];
|
|
168
|
+
|
|
169
|
+
for (const event of sorted) {
|
|
170
|
+
const last = result[result.length - 1];
|
|
171
|
+
|
|
172
|
+
if (
|
|
173
|
+
last &&
|
|
174
|
+
last.typeId === event.typeId &&
|
|
175
|
+
addDays(last.endDate, 1) >= event.startDate
|
|
176
|
+
) {
|
|
177
|
+
const lastSynthetic = !!last._isSynthetic;
|
|
178
|
+
const eventSynthetic = !!event._isSynthetic;
|
|
179
|
+
|
|
180
|
+
const nextId =
|
|
181
|
+
lastSynthetic && !eventSynthetic
|
|
182
|
+
? event.id
|
|
183
|
+
: last.id;
|
|
184
|
+
|
|
185
|
+
const nextSynthetic =
|
|
186
|
+
lastSynthetic && !eventSynthetic
|
|
187
|
+
? event._isSynthetic
|
|
188
|
+
: last._isSynthetic;
|
|
189
|
+
|
|
190
|
+
const nextStartDate =
|
|
191
|
+
event.startDate < last.startDate
|
|
192
|
+
? event.startDate
|
|
193
|
+
: last.startDate;
|
|
194
|
+
|
|
195
|
+
const nextEndDate =
|
|
196
|
+
event.endDate > last.endDate
|
|
197
|
+
? event.endDate
|
|
198
|
+
: last.endDate;
|
|
199
|
+
|
|
200
|
+
const changed =
|
|
201
|
+
nextId !== last.id ||
|
|
202
|
+
nextSynthetic !== last._isSynthetic ||
|
|
203
|
+
nextStartDate !== last.startDate ||
|
|
204
|
+
nextEndDate !== last.endDate;
|
|
205
|
+
|
|
206
|
+
if (changed) {
|
|
207
|
+
result[result.length - 1] = {
|
|
208
|
+
...last,
|
|
209
|
+
id: nextId,
|
|
210
|
+
startDate: nextStartDate,
|
|
211
|
+
endDate: nextEndDate,
|
|
212
|
+
_isSynthetic: nextSynthetic
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
result.push(event);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
let hasSynthetic = false;
|
|
221
|
+
for (const event of result) {
|
|
222
|
+
if (event._isSynthetic !== undefined) {
|
|
223
|
+
hasSynthetic = true;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!hasSynthetic) {
|
|
229
|
+
return result;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return result.map((event) => {
|
|
233
|
+
if (event._isSynthetic === undefined) {
|
|
234
|
+
return event;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const { _isSynthetic, ...cleanEvent } = event;
|
|
238
|
+
return cleanEvent;
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Vrátí průnik dvou intervalů.
|
|
244
|
+
*
|
|
245
|
+
* @param {{startDate: string, endDate: string}} a
|
|
246
|
+
* @param {{startDate: string, endDate: string}} b
|
|
247
|
+
* @returns {{startDate: string, endDate: string} | null}
|
|
248
|
+
*/
|
|
249
|
+
export const intersectRanges = (a, b) => {
|
|
250
|
+
const startDate = a.startDate > b.startDate ? a.startDate : b.startDate;
|
|
251
|
+
const endDate = a.endDate < b.endDate ? a.endDate : b.endDate;
|
|
252
|
+
|
|
253
|
+
if (startDate > endDate) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return { startDate, endDate };
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Odečte jeden interval od jednoho segmentu.
|
|
262
|
+
*
|
|
263
|
+
* @param {{startDate: string, endDate: string}} segment
|
|
264
|
+
* @param {{startDate: string, endDate: string}} cut
|
|
265
|
+
* @returns {{startDate: string, endDate: string}[]}
|
|
266
|
+
*/
|
|
267
|
+
export const subtractRange = (segment, cut) => {
|
|
268
|
+
const intersection = intersectRanges(segment, cut);
|
|
269
|
+
|
|
270
|
+
if (!intersection) {
|
|
271
|
+
return [segment];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const result = [];
|
|
275
|
+
|
|
276
|
+
if (segment.startDate < intersection.startDate) {
|
|
277
|
+
result.push({
|
|
278
|
+
startDate: segment.startDate,
|
|
279
|
+
endDate: addDays(intersection.startDate, -1)
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (intersection.endDate < segment.endDate) {
|
|
284
|
+
result.push({
|
|
285
|
+
startDate: addDays(intersection.endDate, 1),
|
|
286
|
+
endDate: segment.endDate
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return result;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Odečte více intervalů od jednoho základního intervalu.
|
|
295
|
+
*
|
|
296
|
+
* @param {{startDate: string, endDate: string}} baseRange
|
|
297
|
+
* @param {{startDate: string, endDate: string}[]} cuts
|
|
298
|
+
* @returns {{startDate: string, endDate: string}[]}
|
|
299
|
+
*/
|
|
300
|
+
export const subtractRanges = (baseRange, cuts) => {
|
|
301
|
+
let segments = [baseRange];
|
|
302
|
+
|
|
303
|
+
for (const cut of cuts) {
|
|
304
|
+
segments = segments.flatMap((segment) => subtractRange(segment, cut));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return segments;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Aplikuje vybraný typ na interval.
|
|
312
|
+
*
|
|
313
|
+
* Chování:
|
|
314
|
+
* - v částech intervalu, kde už je selectedType, se tento typ odebere
|
|
315
|
+
* - v ostatních částech intervalu se selectedType přidá
|
|
316
|
+
*
|
|
317
|
+
* Nově vytvořené segmenty označuje jako syntetické, aby při merge
|
|
318
|
+
* šlo zachovat ID původních eventů.
|
|
319
|
+
*
|
|
320
|
+
* Neměněné eventy ponechává ve stejné referenci.
|
|
321
|
+
*
|
|
322
|
+
* @param {Event[]} events
|
|
323
|
+
* @param {string} startDate
|
|
324
|
+
* @param {string} endDate
|
|
325
|
+
* @param {{id: string}|null} selectedType
|
|
326
|
+
* @returns {Event[]}
|
|
327
|
+
*/
|
|
328
|
+
export const applyIntervalTransition = (
|
|
329
|
+
events,
|
|
330
|
+
startDate,
|
|
331
|
+
endDate,
|
|
332
|
+
selectedType
|
|
333
|
+
) => {
|
|
334
|
+
if (!selectedType) return events;
|
|
335
|
+
|
|
336
|
+
const range = normalizeRange(startDate, endDate);
|
|
337
|
+
|
|
338
|
+
const selectedTypeOverlaps = events
|
|
339
|
+
.filter(
|
|
340
|
+
(event) =>
|
|
341
|
+
event.typeId === selectedType.id &&
|
|
342
|
+
overlapsRange(event, range.startDate, range.endDate)
|
|
343
|
+
)
|
|
344
|
+
.map((event) =>
|
|
345
|
+
intersectRanges(
|
|
346
|
+
{ startDate: event.startDate, endDate: event.endDate },
|
|
347
|
+
range
|
|
348
|
+
)
|
|
349
|
+
)
|
|
350
|
+
.filter(Boolean);
|
|
351
|
+
|
|
352
|
+
const rangesToAdd = subtractRanges(range, selectedTypeOverlaps);
|
|
353
|
+
|
|
354
|
+
const withoutClickedRange = events.flatMap((event) => {
|
|
355
|
+
const parts = removeRangeFromEvent(
|
|
356
|
+
event,
|
|
357
|
+
range.startDate,
|
|
358
|
+
range.endDate
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
if (parts.length === 1 && parts[0] === event) {
|
|
362
|
+
return parts;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (event._isSynthetic === undefined) {
|
|
366
|
+
return parts;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return parts.map((part) => {
|
|
370
|
+
if (part._isSynthetic === event._isSynthetic) {
|
|
371
|
+
return part;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
...part,
|
|
376
|
+
_isSynthetic: event._isSynthetic
|
|
377
|
+
};
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
if (rangesToAdd.length === 0 && withoutClickedRange.length === events.length) {
|
|
382
|
+
let same = true;
|
|
383
|
+
for (let i = 0; i < events.length; i++) {
|
|
384
|
+
if (events[i] !== withoutClickedRange[i]) {
|
|
385
|
+
same = false;
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (same) {
|
|
390
|
+
return events;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const next = [
|
|
395
|
+
...withoutClickedRange,
|
|
396
|
+
...rangesToAdd.map((rangeToAdd) => ({
|
|
397
|
+
id: makeId(),
|
|
398
|
+
startDate: rangeToAdd.startDate,
|
|
399
|
+
endDate: rangeToAdd.endDate,
|
|
400
|
+
typeId: selectedType.id,
|
|
401
|
+
_isSynthetic: true
|
|
402
|
+
}))
|
|
403
|
+
];
|
|
404
|
+
|
|
405
|
+
return normalizeEvents(next);
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Výchozí stav reduceru.
|
|
410
|
+
* @type {State}
|
|
411
|
+
*/
|
|
412
|
+
export const initialState = {
|
|
413
|
+
events: [],
|
|
414
|
+
selectedType: null
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Reducer kompatibilní s Redux i React useReducer.
|
|
419
|
+
*
|
|
420
|
+
* Akce:
|
|
421
|
+
* - SET_SELECTED_TYPE
|
|
422
|
+
* - SET_EVENTS
|
|
423
|
+
* - APPLY_INTERVAL
|
|
424
|
+
*
|
|
425
|
+
* @param {State} state
|
|
426
|
+
* @param {{type: string, payload?: any}} action
|
|
427
|
+
* @returns {State}
|
|
428
|
+
*/
|
|
429
|
+
export const calendarReducer = (state = initialState, action) => {
|
|
430
|
+
switch (action.type) {
|
|
431
|
+
case "SET_SELECTED_TYPE":
|
|
432
|
+
return {
|
|
433
|
+
...state,
|
|
434
|
+
selectedType: action.payload
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
case "SET_EVENTS":
|
|
438
|
+
return {
|
|
439
|
+
...state,
|
|
440
|
+
events: normalizeEvents(action.payload)
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
case "APPLY_INTERVAL":
|
|
444
|
+
return {
|
|
445
|
+
...state,
|
|
446
|
+
events: applyIntervalTransition(
|
|
447
|
+
state.events,
|
|
448
|
+
action.payload.startDate,
|
|
449
|
+
action.payload.endDate,
|
|
450
|
+
state.selectedType
|
|
451
|
+
)
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
default:
|
|
455
|
+
return state;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* @typedef {Object} Event
|
|
461
|
+
* @property {string} id
|
|
462
|
+
* @property {string} startDate - Datum začátku ve formátu YYYY-MM-DD
|
|
463
|
+
* @property {string} endDate - Datum konce ve formátu YYYY-MM-DD
|
|
464
|
+
* @property {string} typeId - Identifikátor typu eventu
|
|
465
|
+
*/
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* @typedef {Object} UpdatedEvent
|
|
469
|
+
* @property {Event} before - Původní verze eventu
|
|
470
|
+
* @property {Event} after - Nová verze eventu
|
|
471
|
+
*/
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* @typedef {Object} EventsDiff
|
|
475
|
+
* @property {Event[]} added - Eventy, které jsou nové (existují pouze v nextEvents)
|
|
476
|
+
* @property {UpdatedEvent[]} updated - Eventy, které existují v obou stavech,
|
|
477
|
+
* ale změnily některé vlastnosti (startDate, endDate, typeId)
|
|
478
|
+
* @property {Event[]} deleted - Eventy, které byly odstraněny (existují pouze v prevEvents)
|
|
479
|
+
*/
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Porovná dva seznamy eventů a vrátí rozdíly ve formě přidaných,
|
|
483
|
+
* upravených a smazaných eventů.
|
|
484
|
+
*
|
|
485
|
+
* Porovnání probíhá podle `id`:
|
|
486
|
+
* - pokud `id` existuje pouze v `nextEvents` → event je přidaný (`added`)
|
|
487
|
+
* - pokud `id` existuje pouze v `prevEvents` → event je smazaný (`deleted`)
|
|
488
|
+
* - pokud `id` existuje v obou, ale změnily se hodnoty
|
|
489
|
+
* (`startDate`, `endDate`, `typeId`) → event je upravený (`updated`)
|
|
490
|
+
*
|
|
491
|
+
* Funkce NEPOROVNÁVÁ jiné vlastnosti než:
|
|
492
|
+
* - startDate
|
|
493
|
+
* - endDate
|
|
494
|
+
* - typeId
|
|
495
|
+
*
|
|
496
|
+
* @param {Event[]} prevEvents - Předchozí stav eventů
|
|
497
|
+
* @param {Event[]} nextEvents - Nový stav eventů
|
|
498
|
+
* @returns {EventsDiff} Objekt obsahující seznam přidaných, upravených a smazaných eventů
|
|
499
|
+
*/
|
|
500
|
+
export const diffEvents = (prevEvents, nextEvents) => {
|
|
501
|
+
const prevMap = new Map(prevEvents.map(e => [e.id, e]));
|
|
502
|
+
const nextMap = new Map(nextEvents.map(e => [e.id, e]));
|
|
503
|
+
|
|
504
|
+
const added = [];
|
|
505
|
+
const updated = [];
|
|
506
|
+
const deleted = [];
|
|
507
|
+
|
|
508
|
+
for (const [id, prev] of prevMap) {
|
|
509
|
+
const next = nextMap.get(id);
|
|
510
|
+
|
|
511
|
+
if (!next) {
|
|
512
|
+
deleted.push(prev);
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (
|
|
517
|
+
prev.startDate !== next.startDate ||
|
|
518
|
+
prev.endDate !== next.endDate ||
|
|
519
|
+
prev.typeId !== next.typeId
|
|
520
|
+
) {
|
|
521
|
+
updated.push({
|
|
522
|
+
before: prev,
|
|
523
|
+
after: next
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
for (const [id, next] of nextMap) {
|
|
529
|
+
if (!prevMap.has(id)) {
|
|
530
|
+
added.push(next);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return { added, updated, deleted };
|
|
535
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
export * from './CardCapsule'
|
|
3
|
+
export * from './Children'
|
|
4
|
+
export * from './LargeCard'
|
|
5
|
+
export * from './Link'
|
|
6
|
+
export * from './MediumContent'
|
|
7
|
+
export * from './MediumCard'
|
|
8
|
+
|
|
9
|
+
export * from './MediumEditableContent'
|
|
10
|
+
export * from './LiveEdit'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export * from './ConfirmEdit'
|
|
14
|
+
|
|
15
|
+
export * from './Plan'
|