@evenicanpm/admin-integrate 1.1.0 → 1.2.2

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 (114) hide show
  1. package/documents/Process/fragments.ts +13 -0
  2. package/documents/Process/list.ts +27 -0
  3. package/documents/ProcessSchedule/fragments.ts +24 -0
  4. package/documents/ProcessSchedule/read.ts +12 -0
  5. package/documents/ProcessScheduleGroup/fragments.ts +29 -0
  6. package/documents/ProcessScheduleGroup/read.ts +12 -0
  7. package/documents/ScheduledProcess/fragments.ts +24 -0
  8. package/documents/ScheduledProcess/list.ts +27 -0
  9. package/package.json +21 -17
  10. package/src/api/Integrations/queries/get-process.query.ts +48 -0
  11. package/src/api/Integrations/queries/get-process.server.ts +9 -0
  12. package/src/api/Integrations/queries/index.ts +1 -0
  13. package/src/api/dashboard/queries/get-process-executions.query.ts +33 -36
  14. package/src/api/dashboard/queries/get-process-executions.server.ts +4 -4
  15. package/src/api/scheduler/queries/get-process-schedule-group-details.query.ts +45 -0
  16. package/src/api/scheduler/queries/get-process-schedule-group-details.server.ts +14 -0
  17. package/src/api/scheduler/queries/get-process-schedule.query.ts +45 -0
  18. package/src/api/scheduler/queries/get-process-schedule.server.ts +14 -0
  19. package/src/api/scheduler/queries/get-scheduled-processes.query.ts +45 -0
  20. package/src/api/scheduler/queries/get-scheduled-processes.server.ts +11 -0
  21. package/src/api/scheduler/queries/index.ts +3 -0
  22. package/src/components/breadcrumbs/breadcrumbs.tsx +23 -26
  23. package/src/components/breadcrumbs/index.ts +1 -1
  24. package/src/components/breadcrumbs/types.tsx +4 -0
  25. package/src/components/button/outlined-icon-button.tsx +26 -38
  26. package/src/components/core/date-time-popover.tsx +32 -0
  27. package/src/components/core/edited-typography.tsx +20 -0
  28. package/src/components/core/index.ts +3 -0
  29. package/src/components/core/loading.tsx +6 -6
  30. package/src/components/core/title-edit.tsx +122 -0
  31. package/src/components/dashboard-list/dashboard-list-row.tsx +114 -115
  32. package/src/components/dashboard-list/dashboard-list.tsx +119 -119
  33. package/src/components/data-table/table-header.tsx +85 -81
  34. package/src/components/data-table/table-pagination.tsx +27 -27
  35. package/src/components/data-table/table-row.tsx +14 -14
  36. package/src/components/data-table/table-skeleton.tsx +34 -34
  37. package/src/components/execution/execution-filter.ts +22 -22
  38. package/src/components/execution/execution-status-icon.tsx +16 -17
  39. package/src/components/execution/execution-tag.tsx +81 -80
  40. package/src/components/footer/footer.tsx +34 -34
  41. package/src/components/header/dashboard-list-header.tsx +64 -63
  42. package/src/components/header/execution-details-header.tsx +102 -129
  43. package/src/components/header/header.tsx +27 -27
  44. package/src/components/header/index.ts +1 -0
  45. package/src/components/header/integration-details-header.tsx +99 -0
  46. package/src/components/icons/up-down.tsx +15 -15
  47. package/src/components/integration-list/index.ts +2 -0
  48. package/src/components/integration-list/integration-filter.ts +21 -0
  49. package/src/components/integration-list/integration-status-icon.tsx +33 -0
  50. package/src/components/integration-list/integration-tag.tsx +77 -0
  51. package/src/components/integration-view/edges/default-edge.tsx +18 -17
  52. package/src/components/integration-view/elk-layout-options.ts +26 -26
  53. package/src/components/integration-view/elk-types.ts +16 -12
  54. package/src/components/integration-view/flow-types.tsx +5 -5
  55. package/src/components/integration-view/integration-view.tsx +132 -130
  56. package/src/components/integration-view/nodes/base-node.tsx +50 -0
  57. package/src/components/integration-view/nodes/connection-node.tsx +8 -8
  58. package/src/components/integration-view/nodes/entry-node.tsx +8 -8
  59. package/src/components/integration-view/nodes/group-node.tsx +47 -6
  60. package/src/components/integration-view/nodes/task-node.tsx +58 -13
  61. package/src/components/integration-view/temp-data/initialElements.tsx +224 -0
  62. package/src/components/integration-view/types.ts +6 -6
  63. package/src/components/layouts/main-layout.tsx +13 -13
  64. package/src/components/layouts/root-container.tsx +25 -25
  65. package/src/components/link-cards/link-card.tsx +23 -23
  66. package/src/components/link-cards/styles.ts +15 -15
  67. package/src/components/list-filter/date-filter.tsx +41 -45
  68. package/src/components/list-filter/date-range-filter.tsx +78 -77
  69. package/src/components/list-filter/list-filter-types.ts +38 -38
  70. package/src/components/list-filter/list-filter.tsx +363 -354
  71. package/src/components/list-filter/multi-select-filter.tsx +94 -95
  72. package/src/components/list-filter/select-filter.tsx +67 -64
  73. package/src/components/scheduler/day-selector.tsx +174 -0
  74. package/src/components/scheduler/day-tag.tsx +73 -0
  75. package/src/components/scheduler/index.ts +4 -0
  76. package/src/components/scheduler/schedule-filter.ts +25 -0
  77. package/src/components/scheduler/schedule-icon.tsx +87 -0
  78. package/src/components/scheduler/scheduler-types.ts +40 -0
  79. package/src/hooks/use-breadcrumbs.ts +31 -31
  80. package/src/hooks/use-recurrence.tsx +13 -0
  81. package/src/hooks/use-timestamp.tsx +21 -21
  82. package/src/pages/dashboard/dashboard-link-cards.tsx +99 -99
  83. package/src/pages/dashboard/dashboard-list-sections.tsx +130 -125
  84. package/src/pages/dashboard/dashboard.tsx +6 -6
  85. package/src/pages/execution-details/execution-details.tsx +23 -29
  86. package/src/pages/executions/executions-list/executions-list-row.tsx +101 -104
  87. package/src/pages/executions/executions-list/executions-list-table.tsx +45 -53
  88. package/src/pages/executions/executions-list/executions-list.tsx +219 -212
  89. package/src/pages/executions/executions.tsx +67 -72
  90. package/src/pages/integrations/integration-list/index.ts +1 -0
  91. package/src/pages/integrations/integration-list/integration-list-row.tsx +147 -0
  92. package/src/pages/integrations/integration-list/integration-list-table.tsx +78 -0
  93. package/src/pages/integrations/integration-list/integration-list.tsx +262 -0
  94. package/src/pages/integrations/integrations.tsx +83 -28
  95. package/src/pages/scheduler/schedule-drawer/edited/edited-types.ts +17 -0
  96. package/src/pages/scheduler/schedule-drawer/edited/index.ts +1 -0
  97. package/src/pages/scheduler/schedule-drawer/index.ts +1 -0
  98. package/src/pages/scheduler/schedule-drawer/schedule-details-integrations.tsx +227 -0
  99. package/src/pages/scheduler/schedule-drawer/schedule-details-main.tsx +292 -0
  100. package/src/pages/scheduler/schedule-drawer/schedule-details-name.tsx +77 -0
  101. package/src/pages/scheduler/schedule-drawer/schedule-details-skeleton.tsx +64 -0
  102. package/src/pages/scheduler/schedule-drawer/schedule-details.tsx +42 -0
  103. package/src/pages/scheduler/schedule-drawer/schedule-drawer-buttons.tsx +72 -0
  104. package/src/pages/scheduler/schedule-drawer/schedule-drawer-group.tsx +103 -0
  105. package/src/pages/scheduler/schedule-drawer/schedule-drawer-legacy.tsx +94 -0
  106. package/src/pages/scheduler/schedule-drawer/schedule-drawer.tsx +101 -0
  107. package/src/pages/scheduler/schedule-drawer/sortable-integration-row.tsx +93 -0
  108. package/src/pages/scheduler/scheduler.tsx +106 -30
  109. package/src/pages/scheduler/schedules-list/index.ts +1 -0
  110. package/src/pages/scheduler/schedules-list/schedules-list-row.tsx +235 -0
  111. package/src/pages/scheduler/schedules-list/schedules-list-table.tsx +74 -0
  112. package/src/pages/scheduler/schedules-list/schedules-list.tsx +280 -0
  113. package/tsconfig.json +29 -26
  114. package/src/components/integration-view/temp-data/initialElements.ts +0 -238
@@ -1,39 +1,94 @@
1
- import React from "react";
1
+ import React, { useState } from "react";
2
2
  import { useTranslations } from "next-intl";
3
3
 
4
4
  // MUI
5
- import { Theme, useTheme } from "@mui/material";
6
5
  import { Box } from "@mui/material";
7
6
 
7
+ // API
8
+ import { getProcesses } from "@evenicanpm/admin-integrate/api/Integrations/queries";
9
+ import { ProcessInput } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
10
+
8
11
  // Custom components
9
12
  import {
10
- IntegrateBreadcrumbs,
11
- Crumb,
13
+ IntegrateBreadcrumbs,
14
+ Crumb,
12
15
  } from "@evenicanpm/admin-integrate/components/breadcrumbs";
16
+ import { Head } from "@evenicanpm/admin-integrate/components/data-table/table-header";
17
+ import IntegrationList from "./integration-list";
13
18
 
14
19
  export default function Integrations() {
15
- const t = useTranslations("Integrate");
16
-
17
- const theme = useTheme();
18
- const styles = getStyles(theme);
19
-
20
- const breadcrumbs: Crumb[] = [
21
- {
22
- name: t("Header.dashboard"),
23
- url: "/e4integrate",
24
- },
25
- {
26
- name: t("Header.integrations"),
27
- },
28
- ];
29
-
30
- return (
31
- <Box>
32
- <IntegrateBreadcrumbs breadcrumbs={breadcrumbs} />
33
- </Box>
34
- );
35
- }
20
+ const t = useTranslations("Integrate");
21
+ const l = useTranslations("Integrate.IntegrationList.ListHeader");
22
+
23
+ const breadcrumbs: Crumb[] = [
24
+ {
25
+ name: t("Header.dashboard"),
26
+ url: "/e4integrate",
27
+ },
28
+ {
29
+ name: t("Header.integrations"),
30
+ },
31
+ ];
32
+
33
+ const heading: Head[] = [
34
+ {
35
+ id: "name",
36
+ label: l("integrationName"),
37
+ align: "left",
38
+ width: "30%",
39
+ sortable: true,
40
+ },
41
+ {
42
+ id: "id",
43
+ label: l("id"),
44
+ align: "left",
45
+ width: "15%",
46
+ },
47
+ {
48
+ id: "isScheduled",
49
+ label: l("status"),
50
+ align: "center",
51
+ width: "30%",
52
+ },
53
+ {
54
+ id: "modified",
55
+ label: l("lastModified"),
56
+ align: "center",
57
+ width: "15%",
58
+ sortable: true,
59
+ },
60
+ {
61
+ id: "view",
62
+ label: l("actions"),
63
+ align: "center",
64
+ width: "5%",
65
+ },
66
+ ];
36
67
 
37
- const getStyles = (theme: Theme) => {
38
- return {};
39
- };
68
+ // TODO: initialize input based on url query params
69
+ const [input, setInput] = useState<ProcessInput>({
70
+ skip: 0,
71
+ sort: "modified",
72
+ sortDesc: true,
73
+ top: 10,
74
+ integrationTags: null,
75
+ search: null,
76
+ });
77
+
78
+ const { data, isLoading } = getProcesses.useData({
79
+ input: input,
80
+ });
81
+
82
+ return (
83
+ <Box>
84
+ <IntegrateBreadcrumbs breadcrumbs={breadcrumbs} />
85
+ <IntegrationList
86
+ heading={heading}
87
+ data={data}
88
+ loading={isLoading}
89
+ input={input}
90
+ setInput={setInput}
91
+ />
92
+ </Box>
93
+ );
94
+ }
@@ -0,0 +1,17 @@
1
+ export type Edited = {
2
+ name: boolean;
3
+ enabled: boolean;
4
+ days: boolean;
5
+ startDate: boolean;
6
+ recurrence: boolean;
7
+ integrations: boolean;
8
+ };
9
+
10
+ export const initialEdited: Edited = {
11
+ name: false,
12
+ enabled: false,
13
+ days: false,
14
+ startDate: false,
15
+ recurrence: false,
16
+ integrations: false,
17
+ };
@@ -0,0 +1 @@
1
+ export * from "./edited-types";
@@ -0,0 +1 @@
1
+ export { default } from "./schedule-drawer";
@@ -0,0 +1,227 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { useTranslations } from "next-intl";
3
+
4
+ // Dnd
5
+ import {
6
+ DndContext,
7
+ closestCenter,
8
+ KeyboardSensor,
9
+ PointerSensor,
10
+ useSensor,
11
+ useSensors,
12
+ DragEndEvent,
13
+ } from "@dnd-kit/core";
14
+ import {
15
+ arrayMove,
16
+ SortableContext,
17
+ sortableKeyboardCoordinates,
18
+ verticalListSortingStrategy,
19
+ } from "@dnd-kit/sortable";
20
+ import { restrictToFirstScrollableAncestor } from "@dnd-kit/modifiers";
21
+
22
+ // Api
23
+ import {
24
+ GetProcessScheduleGroupDetailsQuery,
25
+ ProcessScheduleGroupDetailsUpdate,
26
+ GetProcessScheduleByIdQuery,
27
+ } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
28
+
29
+ // MUI
30
+ import { Theme, useTheme } from "@mui/material";
31
+ import {
32
+ Box,
33
+ Typography,
34
+ Stack,
35
+ TableContainer,
36
+ Table,
37
+ TableBody,
38
+ Button,
39
+ } from "@mui/material";
40
+ import { Add } from "@mui/icons-material";
41
+
42
+ // Custom components
43
+ import { TableHeader } from "@evenicanpm/admin-integrate/components/data-table";
44
+ import { EditedTypography } from "@evenicanpm/admin-integrate/components/core";
45
+
46
+ // Local components
47
+ import SortableIntegrationRow from "./sortable-integration-row";
48
+
49
+ // Types
50
+ import { Edited } from "./edited";
51
+ import { Head } from "@evenicanpm/admin-integrate/components/data-table/table-header";
52
+
53
+ export type Process = {
54
+ id: string;
55
+ sort: number;
56
+ name: string;
57
+ processIcon: string;
58
+ };
59
+
60
+ interface Props {
61
+ data: GetProcessScheduleGroupDetailsQuery | GetProcessScheduleByIdQuery;
62
+ details: ProcessScheduleGroupDetailsUpdate;
63
+ setDetails: (newDetails: ProcessScheduleGroupDetailsUpdate) => void;
64
+ edited: Edited;
65
+ setEdited: (newEdited: Edited) => void;
66
+ readOnly?: boolean;
67
+ }
68
+
69
+ export default function ScheduleDetailsIntegrations(props: Props) {
70
+ const t = useTranslations("Integrate.Scheduler");
71
+
72
+ const theme = useTheme();
73
+ const styles = getStyles(theme);
74
+
75
+ const [items, setItems] = useState<Process[]>([]);
76
+
77
+ const sensors = useSensors(
78
+ useSensor(PointerSensor),
79
+ useSensor(KeyboardSensor, {
80
+ coordinateGetter: sortableKeyboardCoordinates,
81
+ }),
82
+ );
83
+
84
+ useEffect(() => {
85
+ if (props.data) {
86
+ if ("processScheduleGroupDetails" in props.data) {
87
+ setItems(
88
+ (props.data.processScheduleGroupDetails?.processSchedule
89
+ ?.processes as Process[]) || [],
90
+ );
91
+ } else if ("processScheduleById" in props.data) {
92
+ setItems([props.data.processScheduleById?.process as Process]);
93
+ }
94
+ }
95
+ }, [props.data]);
96
+
97
+ function handleDragEnd(event: DragEndEvent) {
98
+ const { active, over } = event;
99
+
100
+ if (over && active.id !== over.id) {
101
+ setItems((items) => {
102
+ const oldIndex = items.findIndex((process) => process.id === active.id);
103
+ const newIndex = items.findIndex((process) => process.id === over.id);
104
+
105
+ return arrayMove(items, oldIndex, newIndex);
106
+ });
107
+
108
+ props.setDetails({
109
+ ...props.details,
110
+ processes: items,
111
+ });
112
+ if (!props.edited.integrations) {
113
+ props.setEdited({
114
+ ...props.edited,
115
+ integrations: true,
116
+ });
117
+ }
118
+ }
119
+ }
120
+
121
+ // @ts-ignore
122
+ const handleDeleteIntegration = (item: Process) => {
123
+ // TODO: delete integration
124
+ };
125
+
126
+ let heading: Head[] = [
127
+ {
128
+ id: "drag",
129
+ label: "",
130
+ align: "right",
131
+ width: "1%",
132
+ },
133
+ {
134
+ id: "name",
135
+ label: t("integration"),
136
+ align: "left",
137
+ width: "92%",
138
+ },
139
+ {
140
+ id: "actions",
141
+ label: t("ListHeader.actions"),
142
+ align: "right",
143
+ width: "7%",
144
+ },
145
+ ];
146
+
147
+ return (
148
+ <Box sx={styles.main}>
149
+ {items.length > 0 ? (
150
+ <>
151
+ <Box display="flex" justifyContent="space-between">
152
+ <Stack direction="row" spacing={1} alignItems="center">
153
+ <Typography variant="body1">
154
+ {t("scheduledIntegrations")}
155
+ </Typography>
156
+ <EditedTypography edited={props.edited.integrations} />
157
+ </Stack>
158
+ {!props.readOnly && (
159
+ <Button
160
+ color="info"
161
+ variant="contained"
162
+ sx={{ fontSize: theme.typography.caption.fontSize }}
163
+ startIcon={<Add />}
164
+ >
165
+ Add
166
+ </Button>
167
+ )}
168
+ </Box>
169
+ <Box sx={styles.list}>
170
+ <DndContext
171
+ sensors={sensors}
172
+ collisionDetection={closestCenter}
173
+ onDragEnd={handleDragEnd}
174
+ modifiers={[restrictToFirstScrollableAncestor]}
175
+ >
176
+ <SortableContext
177
+ items={items}
178
+ strategy={verticalListSortingStrategy}
179
+ >
180
+ <TableContainer>
181
+ <Table>
182
+ <TableHeader
183
+ heading={heading}
184
+ orderBy={"sort"}
185
+ order={"asc"}
186
+ />
187
+ <TableBody>
188
+ {items.map((item) => (
189
+ <SortableIntegrationRow
190
+ key={item.id}
191
+ item={item}
192
+ heading={heading}
193
+ handleDeleteIntegration={handleDeleteIntegration}
194
+ readOnly={props.readOnly}
195
+ />
196
+ ))}
197
+ </TableBody>
198
+ </Table>
199
+ </TableContainer>
200
+ </SortableContext>
201
+ </DndContext>
202
+ </Box>
203
+ </>
204
+ ) : (
205
+ <></>
206
+ )}
207
+ </Box>
208
+ );
209
+ }
210
+
211
+ const getStyles = (theme: Theme) => {
212
+ return {
213
+ main: {
214
+ padding: theme.spacing(2, 3),
215
+
216
+ maxHeight: "50vh",
217
+ overflow: "auto",
218
+ },
219
+ list: {
220
+ margin: theme.spacing(1, 0),
221
+ borderColor: theme.palette.grey[200],
222
+ borderWidth: theme.spacing(0.25),
223
+ borderStyle: "solid",
224
+ borderRadius: "1%",
225
+ },
226
+ };
227
+ };
@@ -0,0 +1,292 @@
1
+ import { useTranslations } from "next-intl";
2
+ import { useState } from "react";
3
+
4
+ // Api
5
+ import { ProcessScheduleGroupDetailsUpdate } from "@evenicanpm/admin-core/api/e4/graphqlRequestSdk";
6
+
7
+ // MUI
8
+ import { Theme, useTheme } from "@mui/material";
9
+ import {
10
+ Box,
11
+ Stack,
12
+ Typography,
13
+ Switch,
14
+ TextField,
15
+ Select,
16
+ MenuItem,
17
+ SelectChangeEvent,
18
+ } from "@mui/material";
19
+ import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
20
+ import { InfoOutlined } from "@mui/icons-material";
21
+
22
+ // Custom components
23
+ import { DaySelector } from "@evenicanpm/admin-integrate/components/scheduler";
24
+ import {
25
+ DateTimePopover,
26
+ EditedTypography,
27
+ } from "@evenicanpm/admin-integrate/components/core";
28
+
29
+ // Types
30
+ import { Edited } from "./edited";
31
+ import { IsDay } from "@evenicanpm/admin-integrate/components/scheduler";
32
+
33
+ interface Props {
34
+ details: ProcessScheduleGroupDetailsUpdate;
35
+ setDetails: (newDetails: ProcessScheduleGroupDetailsUpdate) => void;
36
+ edited: Edited;
37
+ setEdited: (newEdited: Edited) => void;
38
+ readOnly?: boolean;
39
+ }
40
+
41
+ export default function ScheduleDetailsMain(props: Props) {
42
+ const t = useTranslations("Integrate.Scheduler");
43
+
44
+ const theme = useTheme();
45
+ const styles = getStyles(theme);
46
+
47
+ const [dateError, setDateError] = useState<string | null>(null);
48
+
49
+ const [interval, setInterval] = useState<string>(
50
+ props.details.interval?.toString() || "1",
51
+ );
52
+ const [intervalError, setIntervalError] = useState<string | null>(null);
53
+
54
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
55
+ const open = Boolean(anchorEl);
56
+
57
+ const handlePopoverOpen = (event: React.MouseEvent<HTMLElement>) => {
58
+ setAnchorEl(event.currentTarget);
59
+ };
60
+
61
+ const handlePopoverClose = () => {
62
+ setAnchorEl(null);
63
+ };
64
+
65
+ const handleEnabledChange = () => {
66
+ props.setDetails({
67
+ ...props.details,
68
+ isDisabled: !props.details.isDisabled,
69
+ });
70
+ if (!props.edited.enabled) {
71
+ props.setEdited({
72
+ ...props.edited,
73
+ enabled: true,
74
+ });
75
+ }
76
+ };
77
+
78
+ const handleDayChange = (newDays: IsDay) => {
79
+ props.setDetails({
80
+ ...props.details,
81
+ ...newDays,
82
+ });
83
+ if (!props.edited.days) {
84
+ props.setEdited({
85
+ ...props.edited,
86
+ days: true,
87
+ });
88
+ }
89
+ };
90
+
91
+ const handleStartDateChange = (newStartDate: string | null) => {
92
+ if (newStartDate) {
93
+ try {
94
+ const date = new Date(newStartDate);
95
+ props.setDetails({
96
+ ...props.details,
97
+ startDate: date.toISOString(),
98
+ });
99
+ setDateError(null);
100
+ if (!props.edited.startDate) {
101
+ props.setEdited({
102
+ ...props.edited,
103
+ startDate: true,
104
+ });
105
+ }
106
+ } catch {
107
+ setDateError(t("invalidDate"));
108
+ }
109
+ } else {
110
+ setDateError(t("invalidDate"));
111
+ }
112
+ };
113
+
114
+ const handleStartDateError = (newError: string | null) => {
115
+ setDateError(newError ? t("invalidDate") : null);
116
+ };
117
+
118
+ const handleIntervalChange = (event: React.ChangeEvent<HTMLInputElement>) => {
119
+ setInterval(event.target.value);
120
+ const newInterval = Number(event.target.value);
121
+
122
+ if (!isNaN(newInterval)) {
123
+ props.setDetails({
124
+ ...props.details,
125
+ interval: newInterval,
126
+ });
127
+ if (!props.edited.recurrence) {
128
+ props.setEdited({
129
+ ...props.edited,
130
+ recurrence: true,
131
+ });
132
+ }
133
+ setIntervalError(null);
134
+ } else {
135
+ setIntervalError(t("invalidInterval"));
136
+ }
137
+ };
138
+
139
+ const handleIntervalUnitChange = (event: SelectChangeEvent) => {
140
+ props.setDetails({
141
+ ...props.details,
142
+ intervalUnitId: event.target.value,
143
+ });
144
+ if (!props.edited.recurrence) {
145
+ props.setEdited({
146
+ ...props.edited,
147
+ recurrence: true,
148
+ });
149
+ }
150
+ };
151
+
152
+ return (
153
+ <Box sx={styles.main}>
154
+ <Stack spacing={2}>
155
+ {/* Enabled/disabled switch */}
156
+ <Box>
157
+ <Stack direction="row" spacing={1} alignItems="flex-end">
158
+ <Typography variant="body1">
159
+ {props.details.isDisabled ? t("disabled") : t("enabled")}
160
+ </Typography>
161
+ <EditedTypography edited={props.edited.enabled} />
162
+ </Stack>
163
+ <Switch
164
+ color="info"
165
+ checked={!props.details.isDisabled}
166
+ onChange={handleEnabledChange}
167
+ disabled={props.readOnly}
168
+ />
169
+ </Box>
170
+
171
+ {/* Day of the week selector */}
172
+ <Box>
173
+ <Stack direction="row" spacing={1} alignItems="flex-end">
174
+ <Typography variant="body1">{t("daysOfTheWeek")}</Typography>
175
+ <EditedTypography edited={props.edited.days} />
176
+ </Stack>
177
+ <DaySelector
178
+ days={props.details as IsDay}
179
+ setDays={handleDayChange}
180
+ disabled={props.readOnly}
181
+ />
182
+ </Box>
183
+
184
+ {/* Start date */}
185
+ <Box>
186
+ <Stack direction="row" spacing={1} alignItems="flex-end">
187
+ <Typography variant="body1">{t("startDate")}</Typography>
188
+ <Box
189
+ onMouseEnter={handlePopoverOpen}
190
+ onMouseLeave={handlePopoverClose}
191
+ display="flex"
192
+ alignItems="flex-end"
193
+ sx={{
194
+ fontSize: theme.typography.h5.fontSize,
195
+ paddingBottom: theme.spacing(0.25),
196
+ }}
197
+ >
198
+ <InfoOutlined color="disabled" fontSize="inherit" />
199
+ </Box>
200
+ <EditedTypography edited={props.edited.startDate} />
201
+ </Stack>
202
+ <DateTimePicker
203
+ sx={styles.startDatePicker}
204
+ value={props.details.startDate}
205
+ onChange={handleStartDateChange}
206
+ onError={handleStartDateError}
207
+ views={["year", "month", "day", "hours", "minutes", "seconds"]}
208
+ slotProps={{
209
+ textField: {
210
+ helperText: dateError || "",
211
+ },
212
+ }}
213
+ disabled={props.readOnly}
214
+ />
215
+ <DateTimePopover
216
+ dateIsoString={props.details.startDate}
217
+ sx={{ pointerEvents: "none" }}
218
+ open={open}
219
+ anchorEl={anchorEl}
220
+ anchorOrigin={{
221
+ vertical: "center",
222
+ horizontal: "right",
223
+ }}
224
+ transformOrigin={{
225
+ vertical: "center",
226
+ horizontal: "left",
227
+ }}
228
+ onClose={handlePopoverClose}
229
+ disableRestoreFocus
230
+ />
231
+ </Box>
232
+
233
+ {/* Recurrence */}
234
+ <Box>
235
+ <Stack direction="row" spacing={1} alignItems="flex-end">
236
+ <Typography variant="body1">{t("recurrence")}</Typography>
237
+ <EditedTypography edited={props.edited.recurrence} />
238
+ </Stack>
239
+ <Stack direction="row" spacing={1} alignItems="flex-start">
240
+ <TextField
241
+ value={interval}
242
+ onChange={handleIntervalChange}
243
+ error={intervalError !== null}
244
+ helperText={intervalError || ""}
245
+ slotProps={{
246
+ htmlInput: {
247
+ sx: {
248
+ textAlign: "right",
249
+ },
250
+ },
251
+ }}
252
+ sx={styles.recurrenceInterval}
253
+ disabled={props.readOnly}
254
+ />
255
+ <Select
256
+ value={props.details.intervalUnitId || "1"}
257
+ onChange={handleIntervalUnitChange}
258
+ sx={styles.recurrenceUnit}
259
+ disabled={props.readOnly}
260
+ >
261
+ <MenuItem value={"1"}>{t("minutes")}</MenuItem>
262
+ <MenuItem value={"2"}>{t("hours")}</MenuItem>
263
+ </Select>
264
+ </Stack>
265
+ </Box>
266
+ </Stack>
267
+ </Box>
268
+ );
269
+ }
270
+
271
+ const getStyles = (theme: Theme) => {
272
+ return {
273
+ main: {
274
+ margin: theme.spacing(1, 0),
275
+ },
276
+ startDatePicker: {
277
+ width: "fit-content",
278
+ },
279
+ recurrenceInterval: {
280
+ maxWidth: theme.spacing(15),
281
+ "& .MuiInputBase-input": {
282
+ padding: theme.spacing(1),
283
+ },
284
+ },
285
+ recurrenceUnit: {
286
+ minWidth: theme.spacing(12),
287
+ "& .MuiSelect-select": {
288
+ padding: theme.spacing(1),
289
+ },
290
+ },
291
+ };
292
+ };