@hrbolek/uoisfrontend-template 0.6.2 → 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 +17 -17
- package/dist/es/index.js +38 -31
- package/dist/umd/index.js +31 -31
- package/package.json +1 -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
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react"
|
|
2
|
+
import {
|
|
3
|
+
assignEventsToWeeks,
|
|
4
|
+
buildAcademicWeeks,
|
|
5
|
+
formatDate,
|
|
6
|
+
formatWeek,
|
|
7
|
+
getContrastTextColor,
|
|
8
|
+
normalizeEvent,
|
|
9
|
+
rangesIntersect,
|
|
10
|
+
toIsoDate
|
|
11
|
+
} from "./_utils"
|
|
12
|
+
import { applyIntervalTransition, diffEvents } from "./calendarReducer"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Kontejner pro horizontální layout týdnů (scrollovatelný grid).
|
|
17
|
+
*
|
|
18
|
+
* Používá flexbox:
|
|
19
|
+
* - děti jsou řazeny v řádku
|
|
20
|
+
* - umožňuje horizontální scroll (`overflow-auto`)
|
|
21
|
+
*
|
|
22
|
+
* Vhodné pro:
|
|
23
|
+
* - hlavičku týdnů (WeekHeader)
|
|
24
|
+
* - řádky planneru (PlanRow)
|
|
25
|
+
*
|
|
26
|
+
* @param {{
|
|
27
|
+
* children: React.ReactNode
|
|
28
|
+
* }} props
|
|
29
|
+
*
|
|
30
|
+
* @returns {JSX.Element}
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* <WeekGrid>
|
|
34
|
+
* <WeekGridItem />
|
|
35
|
+
* <WeekGridItem />
|
|
36
|
+
* </WeekGrid>
|
|
37
|
+
*/
|
|
38
|
+
export const WeekGrid = ({ children }) => {
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
className="d-flex gap-1 overflow-auto"
|
|
42
|
+
style={{
|
|
43
|
+
width: "100%",
|
|
44
|
+
paddingBottom: "4px"
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
{children}
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Základní buňka gridu pro jeden týden.
|
|
54
|
+
*
|
|
55
|
+
* Poskytuje:
|
|
56
|
+
* - pevnou minimální šířku a výšku
|
|
57
|
+
* - relativní pozici (pro absolutně pozicované prvky uvnitř)
|
|
58
|
+
* - tooltip s informacemi o týdnu
|
|
59
|
+
*
|
|
60
|
+
* Slouží jako wrapper pro:
|
|
61
|
+
* - ClickableBox (interaktivní obsah)
|
|
62
|
+
* - WeekHeader (popisky týdne)
|
|
63
|
+
*
|
|
64
|
+
* @param {{
|
|
65
|
+
* children: React.ReactNode,
|
|
66
|
+
* week: {
|
|
67
|
+
* labelFull: string,
|
|
68
|
+
* firstHalfStart: Date,
|
|
69
|
+
* firstHalfEnd: Date,
|
|
70
|
+
* secondHalfStart: Date,
|
|
71
|
+
* secondHalfEnd: Date
|
|
72
|
+
* }
|
|
73
|
+
* }} props
|
|
74
|
+
*
|
|
75
|
+
* @returns {JSX.Element}
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* <WeekGridItem week={week}>
|
|
79
|
+
* <ClickableBox ... />
|
|
80
|
+
* </WeekGridItem>
|
|
81
|
+
*/
|
|
82
|
+
export const WeekGridItem = ({ children, title, minWidth = 40, height = 69 }) => {
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
className="position-relative border rounded overflow-hidden user-select-none"
|
|
86
|
+
style={{
|
|
87
|
+
minWidth: `${minWidth}px`,
|
|
88
|
+
height: `${height}px`,
|
|
89
|
+
textAlign: "center",
|
|
90
|
+
flex: "1 0 auto"
|
|
91
|
+
}}
|
|
92
|
+
title={title}
|
|
93
|
+
>
|
|
94
|
+
{children}
|
|
95
|
+
</div>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const academicYear_ = {
|
|
100
|
+
year: 2023,
|
|
101
|
+
startWeek: 35,
|
|
102
|
+
endWeek: 42
|
|
103
|
+
}
|
|
104
|
+
const GREY = "#777777";
|
|
105
|
+
export const WeekHeaderContent = ({ academicYear = academicYear_ }) => {
|
|
106
|
+
const { year: academicStartYear, startWeek, endWeek } = academicYear
|
|
107
|
+
const weeks = useMemo(
|
|
108
|
+
() => buildAcademicWeeks(academicStartYear, startWeek, endWeek),
|
|
109
|
+
[academicYear]
|
|
110
|
+
);
|
|
111
|
+
// return <div>hi</div>
|
|
112
|
+
const link = () => "#"
|
|
113
|
+
return (
|
|
114
|
+
<>
|
|
115
|
+
{weeks.map((week) => (
|
|
116
|
+
<WeekGridItem
|
|
117
|
+
key={week.id}
|
|
118
|
+
title={`${week.labelFull}\n1. polovina: ${formatDate(week.firstHalfStart)} až ${formatDate(week.firstHalfEnd)}\n2. polovina: ${formatDate(week.secondHalfStart)} až ${formatDate(week.secondHalfEnd)}`}
|
|
119
|
+
>
|
|
120
|
+
<div
|
|
121
|
+
className="position-absolute fw-bold"
|
|
122
|
+
style={{
|
|
123
|
+
left: "3px",
|
|
124
|
+
top: "2px",
|
|
125
|
+
zIndex: 2,
|
|
126
|
+
fontSize: "10px",
|
|
127
|
+
textAlign: "center",
|
|
128
|
+
// lineHeight: 1,
|
|
129
|
+
// color: leftTextColor,
|
|
130
|
+
// pointerEvents: "none"
|
|
131
|
+
}}
|
|
132
|
+
>
|
|
133
|
+
<a href={link(week)}>{week.label}</a><br />
|
|
134
|
+
{formatWeek(week)}
|
|
135
|
+
</div>
|
|
136
|
+
</WeekGridItem>
|
|
137
|
+
))}
|
|
138
|
+
</>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
export const WeekHeader = ({ academicYear = academicYear_ }) => {
|
|
142
|
+
return (
|
|
143
|
+
<WeekGrid>
|
|
144
|
+
<WeekHeaderContent academicYear={academicYear} />
|
|
145
|
+
</WeekGrid>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
const WeekTop = ({ textColor, backgroundColor, text, onClick }) => {
|
|
151
|
+
return (<>
|
|
152
|
+
<div
|
|
153
|
+
className="position-absolute"
|
|
154
|
+
onClick={onClick}
|
|
155
|
+
style={{
|
|
156
|
+
inset: 0,
|
|
157
|
+
backgroundColor: backgroundColor,
|
|
158
|
+
clipPath: "polygon(0 0, 100% 0, 0 100%)",
|
|
159
|
+
cursor: "pointer"
|
|
160
|
+
}}
|
|
161
|
+
/>
|
|
162
|
+
<div
|
|
163
|
+
className="position-absolute fw-bold"
|
|
164
|
+
style={{
|
|
165
|
+
left: "3px",
|
|
166
|
+
top: "2px",
|
|
167
|
+
zIndex: 2,
|
|
168
|
+
fontSize: "10px",
|
|
169
|
+
lineHeight: 1,
|
|
170
|
+
color: textColor,
|
|
171
|
+
pointerEvents: "none"
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
{text}
|
|
175
|
+
</div>
|
|
176
|
+
</>)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const WeekBottom = ({ textColor, backgroundColor, text, onClick }) => {
|
|
180
|
+
return (<>
|
|
181
|
+
<div
|
|
182
|
+
className="position-absolute"
|
|
183
|
+
onClick={onClick}
|
|
184
|
+
style={{
|
|
185
|
+
inset: 0,
|
|
186
|
+
backgroundColor: backgroundColor,
|
|
187
|
+
clipPath: "polygon(100% 0, 100% 100%, 0 100%)",
|
|
188
|
+
cursor: "pointer"
|
|
189
|
+
}}
|
|
190
|
+
/>
|
|
191
|
+
|
|
192
|
+
<div
|
|
193
|
+
className="position-absolute fw-bold text-end"
|
|
194
|
+
style={{
|
|
195
|
+
right: "3px",
|
|
196
|
+
bottom: "2px",
|
|
197
|
+
zIndex: 2,
|
|
198
|
+
fontSize: "10px",
|
|
199
|
+
lineHeight: 1,
|
|
200
|
+
color: textColor,
|
|
201
|
+
pointerEvents: "none"
|
|
202
|
+
}}
|
|
203
|
+
>
|
|
204
|
+
{text}
|
|
205
|
+
</div>
|
|
206
|
+
</>)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const ClickableBox = ({ week, height = 46, onClick = () => null }) => {
|
|
210
|
+
const weekEvents = [...week.firstHalfEvents, ...week.secondHalfEvents];
|
|
211
|
+
|
|
212
|
+
const topEvent = weekEvents.find(
|
|
213
|
+
event => rangesIntersect(
|
|
214
|
+
week.firstHalfStart,
|
|
215
|
+
week.firstHalfEnd,
|
|
216
|
+
event.startDateObj,
|
|
217
|
+
event.endDateObj
|
|
218
|
+
)
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const bottomEvent = weekEvents.find(
|
|
222
|
+
event => rangesIntersect(
|
|
223
|
+
week.secondHalfStart,
|
|
224
|
+
week.secondHalfEnd,
|
|
225
|
+
event.startDateObj,
|
|
226
|
+
event.endDateObj
|
|
227
|
+
)
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
const topColor = topEvent?.type?.color || GREY;
|
|
231
|
+
const bottomColor = bottomEvent?.type?.color || GREY;
|
|
232
|
+
const topText = topEvent?.type?.abbreviation || "";
|
|
233
|
+
const bottomText = bottomEvent?.type?.abbreviation || "";
|
|
234
|
+
|
|
235
|
+
const topTextColor = getContrastTextColor(topColor);
|
|
236
|
+
const bottomTextColor = getContrastTextColor(bottomColor);
|
|
237
|
+
|
|
238
|
+
const handleHalfClick = (half) => {
|
|
239
|
+
const isTop = half === "top";
|
|
240
|
+
|
|
241
|
+
onClick({
|
|
242
|
+
half,
|
|
243
|
+
weekId: week.id,
|
|
244
|
+
startDate: toIsoDate(isTop ? week.firstHalfStart : week.secondHalfStart),
|
|
245
|
+
endDate: toIsoDate(isTop ? week.firstHalfEnd : week.secondHalfEnd),
|
|
246
|
+
event: isTop ? topEvent : bottomEvent
|
|
247
|
+
});
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<WeekGridItem
|
|
252
|
+
height={height}
|
|
253
|
+
key={week.id}
|
|
254
|
+
title={`${week.labelFull}\n1. polovina: ${formatDate(week.firstHalfStart)} až ${formatDate(week.firstHalfEnd)}\n2. polovina: ${formatDate(week.secondHalfStart)} až ${formatDate(week.secondHalfEnd)}`}
|
|
255
|
+
>
|
|
256
|
+
<FlexContainer>
|
|
257
|
+
<FlexRow>
|
|
258
|
+
<WeekTop
|
|
259
|
+
textColor={topTextColor}
|
|
260
|
+
backgroundColor={topColor}
|
|
261
|
+
text={topText}
|
|
262
|
+
onClick={() => handleHalfClick("top")}
|
|
263
|
+
/>
|
|
264
|
+
<WeekBottom
|
|
265
|
+
textColor={bottomTextColor}
|
|
266
|
+
backgroundColor={bottomColor}
|
|
267
|
+
text={bottomText}
|
|
268
|
+
onClick={() => handleHalfClick("bottom")}
|
|
269
|
+
/>
|
|
270
|
+
</FlexRow>
|
|
271
|
+
</FlexContainer>
|
|
272
|
+
</WeekGridItem>
|
|
273
|
+
);
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const eventTypes = [
|
|
277
|
+
{ id: "69ec2b0b-a39d-40df-9cea-e295b36749c9", name: "Semestr", abbreviation: "S", color: "#7f7f7f" },
|
|
278
|
+
|
|
279
|
+
{ id: "ac3238a2-a3ca-4f4b-a56b-8ac7c3953aff", name: "výuka - zimní semestr", abbreviation: "V-ZS", color: "#ffffff" },
|
|
280
|
+
{ id: "78a6f015-b8f4-49c8-b218-7861454cb8e9", name: "výuka - letní semestr", abbreviation: "V-LS", color: "#ffffff" },
|
|
281
|
+
|
|
282
|
+
{ id: "6fef77f1-a580-4e13-a088-30368a95af2f", name: "řádná dovolená", abbreviation: "ŘD", color: "#d9a441" },
|
|
283
|
+
{ id: "7e4187e5-b219-4332-b50e-54411541bba6", name: "zkouškové období", abbreviation: "Z", color: "#f28c28" },
|
|
284
|
+
{ id: "79c71457-b926-449a-b227-a3461bf1df4c", name: "příprava v poli - teorie", abbreviation: "PT", color: "#8bc34a" },
|
|
285
|
+
{ id: "539c45b2-629c-43ca-87cb-db753e7bbab9", name: "příprava v poli - praxe", abbreviation: "PP", color: "#7cb342" },
|
|
286
|
+
{ id: "83d0a942-6456-4f35-bce8-37c9a08cb966", name: "rezerva", abbreviation: "R", color: "#ff1f1f" },
|
|
287
|
+
|
|
288
|
+
{ id: "5f147c3c-b307-4334-a339-6f4c83f1dad7", name: "intenzivní kurz AJ", abbreviation: "AJ", color: "#ffff33" },
|
|
289
|
+
{ id: "48ff1e53-83c8-48d6-84e6-b63e4f5fa60d", name: "kurz TV", abbreviation: "TV", color: "#7a6000" },
|
|
290
|
+
{ id: "1af8ea7e-b280-4202-8efe-1df22763081e", name: "odborná praxe", abbreviation: "OP", color: "#bdbdbd" },
|
|
291
|
+
{ id: "0b624b6c-ed72-4316-b070-8e09f73e531c", name: "stáž na systematizovaném místě", abbreviation: "SSM", color: "#d9d9d9" },
|
|
292
|
+
{ id: "cd309340-cae0-4ec2-8adc-f4c61dc5f023", name: "letecká AJ", abbreviation: "LA", color: "#ffff66" },
|
|
293
|
+
{ id: "0f5713ca-7134-4c6a-99a0-7166c689dbfb", name: "vyřazení", abbreviation: "V", color: "#ff1f1f" },
|
|
294
|
+
{ id: "7edccbad-3c70-49ec-b5ca-b744bec8d234", name: "aplikované vojenské technologie", abbreviation: "AVT", color: "#00b0f0" },
|
|
295
|
+
|
|
296
|
+
{ id: "d9494b47-b46c-4ba4-a900-1443751cabce", name: "příprava na SZZ", abbreviation: "PSZZ", color: "#a64ac9" },
|
|
297
|
+
{ id: "eb86a21b-04e2-419c-a3a3-5f21603d349b", name: "SZZ", abbreviation: "SZZ", color: "#8e24aa" },
|
|
298
|
+
{ id: "4d0d3e3f-4d45-4fc4-95f5-612905cf5823", name: "kurz SERE, případně LV, CANI, PARA", abbreviation: "SERE", color: "#9fd3f2" },
|
|
299
|
+
{ id: "4560fca2-533b-45c1-945c-16dcf0935126", name: "zkouškové období / letecký výcvik", abbreviation: "ŘD/LV", color: "#d4af37" },
|
|
300
|
+
{ id: "3d5c8255-9abc-4d3e-aa25-b3984e5ce1a1", name: "diplomový projekt", abbreviation: "DP", color: "#d9d9d9" }
|
|
301
|
+
];
|
|
302
|
+
|
|
303
|
+
const events_ = [
|
|
304
|
+
{ id: "8969d826-bb56-4a26-b3cd-82554cee8897", startDate: "2025-08-25", endDate: "2025-12-21", typeId: "ac3238a2-a3ca-4f4b-a56b-8ac7c3953aff" },
|
|
305
|
+
{ id: "d6ad5dad-43d8-4ccf-b4bc-35fe30357f1e", startDate: "2025-12-22", endDate: "2026-01-04", typeId: "6fef77f1-a580-4e13-a088-30368a95af2f" },
|
|
306
|
+
{ id: "e3e44b78-aa94-49a8-a0e9-a0cb73a3f871", startDate: "2026-01-05", endDate: "2026-01-25", typeId: "7e4187e5-b219-4332-b50e-54411541bba6" },
|
|
307
|
+
{ id: "c689f2bd-0703-42f7-a456-1f5eaa6dc968", startDate: "2026-02-02", endDate: "2026-03-01", typeId: "78a6f015-b8f4-49c8-b218-7861454cb8e9" },
|
|
308
|
+
{ id: "f95385c6-c89e-4a1b-8c45-c039f6ae7253", startDate: "2026-03-02", endDate: "2026-03-22", typeId: "79c71457-b926-449a-b227-a3461bf1df4c" },
|
|
309
|
+
{ id: "ede4e6a5-742c-4848-8238-e0de940c6b52", startDate: "2026-03-23", endDate: "2026-04-12", typeId: "539c45b2-629c-43ca-87cb-db753e7bbab9" },
|
|
310
|
+
{ id: "ca98c5bd-657c-474a-a1d8-334189fce221", startDate: "2026-04-13", endDate: "2026-05-17", typeId: "d9494b47-b46c-4ba4-a900-1443751cabce" },
|
|
311
|
+
{ id: "1d96403b-c421-4d3b-9123-54368789273a", startDate: "2026-05-18", endDate: "2026-05-31", typeId: "eb86a21b-04e2-419c-a3a3-5f21603d349b" },
|
|
312
|
+
{ id: "ac437892-86d5-495d-bd0d-7e511c777c5d", startDate: "2026-06-01", endDate: "2026-06-21", typeId: "4d0d3e3f-4d45-4fc4-95f5-612905cf5823" },
|
|
313
|
+
{ id: "b6b0288d-ddbf-48a7-8aa7-060215cf9dd2", startDate: "2026-06-22", endDate: "2026-09-20", typeId: "3d5c8255-9abc-4d3e-aa25-b3984e5ce1a1" }
|
|
314
|
+
];
|
|
315
|
+
|
|
316
|
+
const enrichEvent = (event) => {
|
|
317
|
+
return {
|
|
318
|
+
...event,
|
|
319
|
+
"type": {
|
|
320
|
+
...eventTypes.find(t => t.id === event.typeId)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export const FlexRow = ({ children }) => {
|
|
326
|
+
return (
|
|
327
|
+
<div
|
|
328
|
+
className="d-flex gap-1 align-items-center"
|
|
329
|
+
style={{ flexWrap: "nowrap" }}
|
|
330
|
+
>
|
|
331
|
+
{children}
|
|
332
|
+
</div>
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export const FlexContainer = ({ children }) => {
|
|
337
|
+
return (
|
|
338
|
+
<div
|
|
339
|
+
style={{
|
|
340
|
+
overflowX: "auto",
|
|
341
|
+
overflowY: "hidden",
|
|
342
|
+
width: "100%",
|
|
343
|
+
}}
|
|
344
|
+
>
|
|
345
|
+
<div
|
|
346
|
+
style={{
|
|
347
|
+
display: "inline-flex",
|
|
348
|
+
flexDirection: "column",
|
|
349
|
+
gap: "4px",
|
|
350
|
+
minWidth: "max-content",
|
|
351
|
+
}}
|
|
352
|
+
>
|
|
353
|
+
{children}
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export const Tools = ({ selected, onSelect }) => {
|
|
360
|
+
return (<>
|
|
361
|
+
{eventTypes.map(current_tool => (
|
|
362
|
+
<button
|
|
363
|
+
key={current_tool.id}
|
|
364
|
+
style={{
|
|
365
|
+
backgroundColor: current_tool?.color,
|
|
366
|
+
border: current_tool == selected ? "3px solid black" : null
|
|
367
|
+
}}
|
|
368
|
+
className="btn btn-sm"
|
|
369
|
+
onClick={() => onSelect(current_tool)}
|
|
370
|
+
>
|
|
371
|
+
{current_tool.name}
|
|
372
|
+
</button>
|
|
373
|
+
))}
|
|
374
|
+
</>)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export const PlanRow = ({ item, academicYear = academicYear_ }) => {
|
|
378
|
+
//predpoklada se, ze item ma subevents, pokud ne, tak se pouziji demodata, nevhodne pro produkci
|
|
379
|
+
const { subevents = events_ } = item
|
|
380
|
+
// const subevents=events_
|
|
381
|
+
//dekompozice akademickeho roku,
|
|
382
|
+
// const { year: academicStartYear, startWeek, endWeek } = academicYear;
|
|
383
|
+
const { year: academicStartYear, startWeek, endWeek } = {
|
|
384
|
+
year: 2025,
|
|
385
|
+
startWeek: 35,
|
|
386
|
+
endWeek: 42
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
//tydny dopocitane z akademickeho roku, automaticky se porepocitaji pri zmene pouzitych parametru
|
|
390
|
+
const weeks = useMemo(
|
|
391
|
+
() => buildAcademicWeeks(academicStartYear, startWeek, endWeek),
|
|
392
|
+
[academicStartYear, startWeek, endWeek]
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
//aktualni nastroj pro klikani
|
|
396
|
+
const [tool, setTool] = useState(eventTypes.find(t => t?.id === "7e4187e5-b219-4332-b50e-54411541bba6"))
|
|
397
|
+
//poznamenani si udalosti, jejich datove osetreni, toto je lokalni cache
|
|
398
|
+
const [events, setEvents] = useState(subevents.map(normalizeEvent));
|
|
399
|
+
|
|
400
|
+
//prirazeni udalosti tydnum, pozor na multiplicity
|
|
401
|
+
const weeksWithEvents = useMemo(
|
|
402
|
+
() => assignEventsToWeeks(weeks, events.map(normalizeEvent).map(enrichEvent)),
|
|
403
|
+
[weeks, events]
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const handleIntervalClick = ({ startDate, endDate }) =>
|
|
407
|
+
setEvents(prev => applyIntervalTransition(prev, startDate, endDate, tool));
|
|
408
|
+
|
|
409
|
+
// poznamenani si udalosti, jejich datove osetreni
|
|
410
|
+
// diff(old, events) je definice neulozenych zmen
|
|
411
|
+
// lze proves debounced synchronizaci s backendem, tj. bezpecne ulozeni dat na server
|
|
412
|
+
// po ulozeni se nactena data pomoci setOld tady aktualizuji,
|
|
413
|
+
// zmeny udelane behem teto aktualizace je mozne opet ulozit
|
|
414
|
+
const [old, setOld] = useState([...events])
|
|
415
|
+
const { added, updated, deleted } = diffEvents(old, events)
|
|
416
|
+
|
|
417
|
+
return (<>
|
|
418
|
+
<FlexContainer>
|
|
419
|
+
<FlexRow>
|
|
420
|
+
<Tools selected={tool} onSelect={setTool} />
|
|
421
|
+
</FlexRow>
|
|
422
|
+
<FlexRow>
|
|
423
|
+
<WeekHeaderContent academicYear={academicYear} />
|
|
424
|
+
</FlexRow>
|
|
425
|
+
|
|
426
|
+
<FlexRow>
|
|
427
|
+
{weeksWithEvents.map((week) => (
|
|
428
|
+
<ClickableBox
|
|
429
|
+
key={week.id}
|
|
430
|
+
week={week}
|
|
431
|
+
onClick={handleIntervalClick}
|
|
432
|
+
/>
|
|
433
|
+
))}
|
|
434
|
+
</FlexRow>
|
|
435
|
+
|
|
436
|
+
</FlexContainer>
|
|
437
|
+
|
|
438
|
+
<pre>
|
|
439
|
+
<b>inserted</b><br />
|
|
440
|
+
{JSON.stringify(added, null, 2)}
|
|
441
|
+
</pre>
|
|
442
|
+
|
|
443
|
+
<pre>
|
|
444
|
+
<b>updated</b><br />
|
|
445
|
+
{JSON.stringify(updated, null, 2)}
|
|
446
|
+
</pre>
|
|
447
|
+
|
|
448
|
+
<pre>
|
|
449
|
+
<b>removed</b><br />
|
|
450
|
+
{JSON.stringify(deleted, null, 2)}
|
|
451
|
+
</pre>
|
|
452
|
+
|
|
453
|
+
{/* <Row>
|
|
454
|
+
<Col>
|
|
455
|
+
<pre>
|
|
456
|
+
<b>Old list</b><br />
|
|
457
|
+
{JSON.stringify(old.map(enrichEvent), null, 2)}
|
|
458
|
+
</pre>
|
|
459
|
+
</Col>
|
|
460
|
+
<Col>
|
|
461
|
+
<pre>
|
|
462
|
+
<b>New list</b><br />
|
|
463
|
+
{JSON.stringify(events.map(enrichEvent), null, 2)}
|
|
464
|
+
</pre>
|
|
465
|
+
</Col>
|
|
466
|
+
</Row> */}
|
|
467
|
+
|
|
468
|
+
</>
|
|
469
|
+
);
|
|
470
|
+
};
|