@omnsight/osint-entity-components 0.2.5 → 0.2.7

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 (79) hide show
  1. package/dist/index.js +8 -8
  2. package/dist/index.mjs +1429 -1146
  3. package/package.json +23 -2
  4. package/src/App.tsx +397 -141
  5. package/src/assets/icons/generated/boxicons-file-report.tsx +20 -0
  6. package/src/assets/icons/generated/bx-plus-medical.tsx +8 -0
  7. package/src/assets/icons/generated/bx-run.tsx +8 -0
  8. package/src/assets/icons/generated/fa-solid-fist-raised.tsx +8 -0
  9. package/src/assets/icons/generated/fluent-emoji-high-contrast-ballot-box-with-ballot.tsx +8 -0
  10. package/src/assets/icons/generated/glyphs-handshake-bold.tsx +8 -0
  11. package/src/assets/icons/generated/icon-park-currency.tsx +8 -0
  12. package/src/assets/icons/generated/icon-park-great-wall.tsx +8 -0
  13. package/src/assets/icons/generated/iconoir-commodity.tsx +8 -0
  14. package/src/assets/icons/generated/lsicon-work-order-abnormal-outline.tsx +8 -0
  15. package/src/assets/icons/generated/material-symbols-light-drone.tsx +8 -0
  16. package/src/assets/icons/generated/material-symbols-satellite-alt.tsx +8 -0
  17. package/src/assets/icons/generated/mdi-anchor.tsx +19 -0
  18. package/src/assets/icons/generated/ph-mask-happy-fill.tsx +8 -0
  19. package/src/assets/icons/generated/streamline-ultimate-meeting-remote-bold.tsx +19 -0
  20. package/src/assets/icons/generated/tabler-barrier-block.tsx +8 -0
  21. package/src/assets/icons/generated/typcn-flash.tsx +8 -0
  22. package/src/avatars/layouts/AvatarDropdown.tsx +1 -1
  23. package/src/forms/BaseForm.tsx +138 -0
  24. package/src/forms/EditableAttributes.tsx +131 -0
  25. package/src/forms/EventForm/EditableForm.tsx +78 -0
  26. package/src/forms/EventForm/EditingForm.tsx +401 -0
  27. package/src/forms/EventForm/IconFormSection.tsx +54 -0
  28. package/src/forms/EventForm/StaticForm.tsx +272 -0
  29. package/src/forms/EventForm/index.ts +1 -0
  30. package/src/forms/InsightForm/EditableForm.tsx +70 -0
  31. package/src/forms/InsightForm/EditingForm.tsx +79 -0
  32. package/src/forms/InsightForm/StaticForm.tsx +139 -0
  33. package/src/forms/InsightForm/index.ts +1 -0
  34. package/src/forms/MonitoringSourceForm/EditableForm.tsx +59 -0
  35. package/src/forms/MonitoringSourceForm/EditingForm.tsx +192 -0
  36. package/src/forms/MonitoringSourceForm/StaticForm.tsx +107 -0
  37. package/src/forms/MonitoringSourceForm/index.ts +1 -0
  38. package/src/forms/OrganizationForm/EditableForm.tsx +74 -0
  39. package/src/forms/OrganizationForm/EditingForm.tsx +177 -0
  40. package/src/forms/OrganizationForm/IconFormSection.tsx +60 -0
  41. package/src/forms/OrganizationForm/StaticForm.tsx +209 -0
  42. package/src/forms/OrganizationForm/index.ts +1 -0
  43. package/src/forms/PersonForm/EditableForm.tsx +74 -0
  44. package/src/forms/PersonForm/EditingForm.tsx +187 -0
  45. package/src/forms/PersonForm/IconFormSection.tsx +54 -0
  46. package/src/forms/PersonForm/StaticForm.tsx +202 -0
  47. package/src/forms/PersonForm/index.ts +1 -0
  48. package/src/forms/RelationForm/EditableForm.tsx +74 -0
  49. package/src/forms/RelationForm/EditingForm.tsx +147 -0
  50. package/src/forms/RelationForm/StaticForm.tsx +182 -0
  51. package/src/forms/RelationForm/index.ts +1 -0
  52. package/src/forms/SourceForm/EditableForm.tsx +74 -0
  53. package/src/forms/SourceForm/EditingForm.tsx +199 -0
  54. package/src/forms/SourceForm/IconFormSection.tsx +54 -0
  55. package/src/forms/SourceForm/StaticForm.tsx +209 -0
  56. package/src/forms/SourceForm/index.ts +1 -0
  57. package/src/forms/WebsiteForm/EditableForm.tsx +74 -0
  58. package/src/forms/WebsiteForm/EditingForm.tsx +216 -0
  59. package/src/forms/WebsiteForm/IconFormSection.tsx +54 -0
  60. package/src/forms/WebsiteForm/StaticForm.tsx +225 -0
  61. package/src/forms/WebsiteForm/index.ts +1 -0
  62. package/src/forms/accessLevel.ts +48 -0
  63. package/src/forms/index.ts +8 -0
  64. package/src/icons/Event/Select.tsx +7 -6
  65. package/src/icons/Event/icons.ts +112 -4
  66. package/src/icons/Organization/Select.tsx +7 -6
  67. package/src/icons/Person/Select.tsx +7 -6
  68. package/src/icons/Source/Select.tsx +7 -6
  69. package/src/icons/Website/Select.tsx +9 -8
  70. package/src/icons/index.ts +1 -0
  71. package/src/inputs/CountrySelect.tsx +45 -0
  72. package/src/inputs/CustomDatePicker.tsx +51 -0
  73. package/src/inputs/CustomDateTimePicker.tsx +51 -0
  74. package/src/inputs/RangeDatePicker.tsx +99 -0
  75. package/src/inputs/TimezoneSelect.tsx +20 -0
  76. package/src/inputs/index.ts +5 -0
  77. package/src/locales/en.json +153 -1
  78. package/src/locales/zh.json +153 -1
  79. package/src/main.tsx +20 -4
@@ -0,0 +1,272 @@
1
+ import { useState, type PropsWithChildren, type CSSProperties } from "react";
2
+ import {
3
+ CalendarDaysIcon,
4
+ ChevronDownIcon,
5
+ MapPinIcon,
6
+ UserIcon,
7
+ } from "@heroicons/react/24/outline";
8
+ import {
9
+ Box,
10
+ Collapse,
11
+ Divider,
12
+ Group,
13
+ Stack,
14
+ Title,
15
+ UnstyledButton,
16
+ rem,
17
+ Text,
18
+ Badge,
19
+ Select,
20
+ Tooltip,
21
+ } from "@mantine/core";
22
+ import {
23
+ type Event,
24
+ type Source,
25
+ type Permissive,
26
+ } from "omni-osint-crud-client";
27
+ import { useTranslation } from "react-i18next";
28
+ import countries from "i18n-iso-countries";
29
+ import { Controller } from "react-hook-form";
30
+ import { EditableAttributes } from "../EditableAttributes";
31
+ import { BaseForm } from "../BaseForm";
32
+ import { EventIcon } from "@omnsight/osint-entity-components/icons";
33
+ import {
34
+ SourceLink,
35
+ AvatarSpan,
36
+ } from "@omnsight/osint-entity-components/avatars";
37
+ import {
38
+ getAccessLevel,
39
+ getRoles,
40
+ useReadOptions,
41
+ useWriteOptions,
42
+ } from "../accessLevel";
43
+
44
+ interface Props extends PropsWithChildren {
45
+ event: Event;
46
+ sources?: Source[];
47
+ isAdmin?: boolean;
48
+ onUpdate?: (data: Permissive) => void;
49
+ onClose?: () => void;
50
+ onDoubleClick: () => void;
51
+ editModeEnabled: boolean;
52
+ exitButton?: React.ReactNode;
53
+ style?: CSSProperties;
54
+ }
55
+
56
+ export const StaticForm: React.FC<Props> = ({
57
+ event,
58
+ sources = [],
59
+ isAdmin = false,
60
+ onUpdate,
61
+ onClose,
62
+ onDoubleClick,
63
+ editModeEnabled,
64
+ exitButton,
65
+ children,
66
+ style,
67
+ }) => {
68
+ const { t, i18n } = useTranslation();
69
+ const [attributesOpen, setAttributesOpen] = useState(false);
70
+ const readOptions = useReadOptions(isAdmin);
71
+ const writeOptions = useWriteOptions();
72
+
73
+ return (
74
+ <BaseForm<Event>
75
+ style={style}
76
+ icon={<EventIcon event={event} />}
77
+ title={event.title || t("placeholder.title")}
78
+ titleRight={
79
+ sources && (
80
+ <AvatarSpan showEmptyAvatar={false}>
81
+ {sources.map((source) => (
82
+ <SourceLink key={source._id || source._key} data={source} />
83
+ ))}
84
+ </AvatarSpan>
85
+ )
86
+ }
87
+ onlyShowEditOnDirty={true}
88
+ onClose={() => onClose?.()}
89
+ defaultValues={{
90
+ ...event,
91
+ location: {
92
+ ...event.location,
93
+ sub_locality: event?.location?.sub_locality || "",
94
+ sub_administrative_area:
95
+ event?.location?.sub_administrative_area || "",
96
+ },
97
+ }}
98
+ onUpdate={onUpdate}
99
+ exitButton={exitButton}
100
+ >
101
+ {({ control, formState: { errors } }) => {
102
+ return (
103
+ <Stack
104
+ pos="relative"
105
+ gap="xs"
106
+ style={{
107
+ cursor: editModeEnabled ? "pointer" : "default",
108
+ }}
109
+ onDoubleClick={onDoubleClick}
110
+ >
111
+ <Group gap={4}>
112
+ <Text size="sm" c="dimmed">
113
+ {t("placeholder.type")}:
114
+ </Text>
115
+ <Text size="sm">{event.type}</Text>
116
+ </Group>
117
+
118
+ <Group gap="xs" c="dimmed">
119
+ <CalendarDaysIcon style={{ width: rem(18), height: rem(18) }} />
120
+ <Text size="sm">
121
+ {event.happened_at
122
+ ? new Date(event.happened_at * 1000).toLocaleDateString()
123
+ : t("placeholder.date")}
124
+ </Text>
125
+ </Group>
126
+
127
+ <Group gap="xs" c="dimmed" align="flex-start" wrap="nowrap">
128
+ <MapPinIcon
129
+ style={{
130
+ width: rem(18),
131
+ height: rem(18),
132
+ flexShrink: 0,
133
+ marginTop: rem(2),
134
+ }}
135
+ />
136
+ <Stack gap={0} style={{ flex: 1 }}>
137
+ <Text size="sm">
138
+ {[
139
+ event.location?.address,
140
+ event.location?.sub_locality,
141
+ event.location?.locality,
142
+ event.location?.sub_administrative_area,
143
+ event.location?.administrative_area,
144
+ event.location?.country_code
145
+ ? countries.getName(
146
+ event.location.country_code,
147
+ i18n.language.startsWith("zh") ? "zh" : "en",
148
+ { select: "official" },
149
+ )
150
+ : undefined,
151
+ event.location?.postal_code,
152
+ ]
153
+ .filter(Boolean)
154
+ .join(", ") || t("placeholder.address")}
155
+ </Text>
156
+ <Group>
157
+ <Text size="sm">
158
+ Lat: {event.location?.latitude ?? "N/A"}
159
+ </Text>
160
+ <Text size="sm">
161
+ Lon: {event.location?.longitude ?? "N/A"}
162
+ </Text>
163
+ </Group>
164
+ </Stack>
165
+ </Group>
166
+
167
+ <Text size="sm">
168
+ {event.description ||
169
+ t("components.forms.EventForm.eventDescription")}
170
+ </Text>
171
+
172
+ <Group gap="xs">
173
+ {(event.tags || []).map((tag) => (
174
+ <Badge key={tag}>{tag}</Badge>
175
+ ))}
176
+ </Group>
177
+
178
+ {onUpdate && (
179
+ <Group gap="xs" w="100%">
180
+ <Box
181
+ style={{
182
+ flex: 1,
183
+ display: "flex",
184
+ alignItems: "center",
185
+ justifyContent: "center",
186
+ }}
187
+ >
188
+ <Tooltip label={event.owner?.toUpperCase()[0] || ""}>
189
+ <UserIcon style={{ width: rem(18), height: rem(18) }} />
190
+ </Tooltip>
191
+ </Box>
192
+ <Box
193
+ style={{
194
+ flex: 3,
195
+ display: "flex",
196
+ }}
197
+ >
198
+ {t("placeholder.accessLabel")}:
199
+ </Box>
200
+ <Controller
201
+ name="read"
202
+ control={control}
203
+ rules={{ required: t("common.required") }}
204
+ render={({ field }) => {
205
+ return (
206
+ <Box style={{ flex: 4 }}>
207
+ <Select
208
+ value={getAccessLevel(field.value ?? [])}
209
+ onChange={(value) => field.onChange(getRoles(value))}
210
+ placeholder={t("placeholder.readAccess")}
211
+ data={readOptions}
212
+ clearable
213
+ error={errors.read?.message}
214
+ />
215
+ </Box>
216
+ );
217
+ }}
218
+ />
219
+ <Controller
220
+ name="write"
221
+ control={control}
222
+ rules={{ required: t("common.required") }}
223
+ render={({ field }) => {
224
+ return (
225
+ <Box style={{ flex: 4 }}>
226
+ <Select
227
+ value={getAccessLevel(field.value ?? [])}
228
+ onChange={(value) => field.onChange(getRoles(value))}
229
+ placeholder={t("placeholder.writeAccess")}
230
+ data={writeOptions}
231
+ clearable
232
+ error={errors.write?.message}
233
+ />
234
+ </Box>
235
+ );
236
+ }}
237
+ />
238
+ </Group>
239
+ )}
240
+
241
+ {children}
242
+
243
+ <Divider my="sm" />
244
+
245
+ <UnstyledButton onClick={() => setAttributesOpen((o) => !o)}>
246
+ <Group justify="space-between">
247
+ <Title order={5}>{t("placeholder.attributes")}</Title>
248
+ <ChevronDownIcon
249
+ style={{
250
+ width: 16,
251
+ transform: attributesOpen
252
+ ? "rotate(180deg)"
253
+ : "rotate(0deg)",
254
+ transition: "transform 200ms ease",
255
+ }}
256
+ />
257
+ </Group>
258
+ </UnstyledButton>
259
+
260
+ <Collapse in={attributesOpen}>
261
+ <EditableAttributes
262
+ value={event.attributes || {}}
263
+ isEditing={false}
264
+ onChange={() => {}}
265
+ />
266
+ </Collapse>
267
+ </Stack>
268
+ );
269
+ }}
270
+ </BaseForm>
271
+ );
272
+ };
@@ -0,0 +1 @@
1
+ export { EventForm } from './EditableForm';
@@ -0,0 +1,70 @@
1
+ import { useState, type PropsWithChildren } from "react";
2
+ import type { OsintView, Permissive } from "omni-osint-crud-client";
3
+ import { EditingForm } from "./EditingForm";
4
+ import { StaticForm } from "./StaticForm";
5
+
6
+ interface Props extends PropsWithChildren {
7
+ insight: OsintView;
8
+ isAdmin?: boolean;
9
+ onSubmit?: (data: OsintView) => void;
10
+ onUpdate?: (data: Partial<OsintView>) => void;
11
+ onUpdatePermissive?: (data: Permissive) => void;
12
+ onClose?: () => void;
13
+ exitButton?: React.ReactNode;
14
+ }
15
+
16
+ export const InsightForm: React.FC<Props> = ({
17
+ insight,
18
+ isAdmin = false,
19
+ onSubmit,
20
+ onUpdate,
21
+ onUpdatePermissive,
22
+ onClose,
23
+ exitButton,
24
+ children,
25
+ }) => {
26
+ const [isEditing, setIsEditing] = useState(onSubmit !== undefined || false);
27
+
28
+ if (
29
+ onSubmit !== undefined &&
30
+ (onUpdate !== undefined || onUpdatePermissive !== undefined)
31
+ ) {
32
+ throw new Error(
33
+ "onSubmit cannot be defined at the same time with onUpdate or onUpdatePermissive",
34
+ );
35
+ }
36
+
37
+ const handlClose = () => {
38
+ if (onUpdate !== undefined) {
39
+ setIsEditing(false);
40
+ }
41
+ onClose?.();
42
+ };
43
+
44
+ const handleDoubleClick = () => {
45
+ if (onUpdate !== undefined) {
46
+ setIsEditing(true);
47
+ }
48
+ };
49
+
50
+ return isEditing ? (
51
+ <EditingForm
52
+ insight={insight}
53
+ onSubmit={onSubmit}
54
+ onUpdate={onUpdate}
55
+ onClose={handlClose}
56
+ />
57
+ ) : (
58
+ <StaticForm
59
+ insight={insight}
60
+ isAdmin={isAdmin}
61
+ onUpdate={onUpdatePermissive}
62
+ onClose={handlClose}
63
+ onDoubleClick={handleDoubleClick}
64
+ exitButton={exitButton || <></>}
65
+ editModeEnabled={onUpdate !== undefined}
66
+ >
67
+ {children}
68
+ </StaticForm>
69
+ );
70
+ };
@@ -0,0 +1,79 @@
1
+ import React from 'react';
2
+ import { Stack, Text, Textarea, TextInput } from '@mantine/core';
3
+ import type { OsintView } from 'omni-osint-crud-client';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Controller } from 'react-hook-form';
6
+ import { BaseForm } from '../BaseForm';
7
+
8
+ interface Props {
9
+ insight: OsintView;
10
+ onSubmit?: (data: OsintView) => void;
11
+ onUpdate?: (data: Partial<OsintView>) => void;
12
+ onClose?: () => void;
13
+ }
14
+
15
+ export const EditingForm: React.FC<Props> = ({
16
+ insight,
17
+ onSubmit,
18
+ onUpdate,
19
+ onClose,
20
+ }) => {
21
+ const { t } = useTranslation();
22
+
23
+ const handlClose = () => {
24
+ onClose?.();
25
+ };
26
+
27
+ return (
28
+ <BaseForm<OsintView>
29
+ title={t('components.forms.InsightForm.title')}
30
+ onClose={handlClose}
31
+ onlyShowEditOnDirty={false}
32
+ defaultValues={insight}
33
+ onSubmit={onSubmit}
34
+ onUpdate={onUpdate}
35
+ >
36
+ {({ control, formState: { errors } }) => (
37
+ <Stack
38
+ pos="relative"
39
+ gap="xs"
40
+ style={{ cursor: 'default' }}
41
+ >
42
+ <Text size="sm" fw={500}>
43
+ {t('placeholder.title')}
44
+ </Text>
45
+ <Controller
46
+ name="name"
47
+ control={control}
48
+ rules={{ required: t('common.required') }}
49
+ render={({ field }) => (
50
+ <TextInput
51
+ {...field}
52
+ autoFocus
53
+ value={field.value || ''}
54
+ placeholder={`${t('placeholder.enter')}${t('placeholder.title')}...`}
55
+ error={errors.name?.message}
56
+ />
57
+ )}
58
+ />
59
+
60
+ <Text size="sm">{t('placeholder.description')}</Text>
61
+ <Controller
62
+ name="description"
63
+ control={control}
64
+ rules={{ required: t('common.required') }}
65
+ render={({ field }) => (
66
+ <Textarea
67
+ {...field}
68
+ autosize
69
+ value={field.value || ''}
70
+ placeholder={`${t('placeholder.enter')}${t('placeholder.description')}...`}
71
+ error={errors.description?.message}
72
+ />
73
+ )}
74
+ />
75
+ </Stack>
76
+ )}
77
+ </BaseForm>
78
+ );
79
+ };
@@ -0,0 +1,139 @@
1
+ import React, { type PropsWithChildren } from "react";
2
+ import { Group, rem, Stack, Text, Tooltip, Box, Select } from "@mantine/core";
3
+ import { UserIcon } from "@heroicons/react/24/outline";
4
+ import type { OsintView, Permissive } from "omni-osint-crud-client";
5
+ import { useTranslation } from "react-i18next";
6
+ import { Controller } from "react-hook-form";
7
+ import {
8
+ getAccessLevel,
9
+ getRoles,
10
+ useReadOptions,
11
+ useWriteOptions,
12
+ } from "../accessLevel";
13
+ import { BaseForm } from "../BaseForm";
14
+
15
+ interface Props extends PropsWithChildren {
16
+ insight: OsintView;
17
+ isAdmin?: boolean;
18
+ onUpdate?: (data: Permissive) => void;
19
+ onClose?: () => void;
20
+ onDoubleClick: () => void;
21
+ editModeEnabled: boolean;
22
+ exitButton?: React.ReactNode;
23
+ }
24
+
25
+ export const StaticForm: React.FC<Props> = ({
26
+ insight,
27
+ isAdmin = false,
28
+ onUpdate,
29
+ onClose,
30
+ onDoubleClick,
31
+ editModeEnabled,
32
+ exitButton,
33
+ children,
34
+ }) => {
35
+ const { t } = useTranslation();
36
+
37
+ const handlClose = () => {
38
+ onClose?.();
39
+ };
40
+
41
+ const readOptions = useReadOptions(isAdmin);
42
+ const writeOptions = useWriteOptions();
43
+
44
+ return (
45
+ <BaseForm<OsintView>
46
+ title={insight.name || t("placeholder.title")}
47
+ onClose={handlClose}
48
+ defaultValues={insight}
49
+ onUpdate={onUpdate}
50
+ exitButton={exitButton}
51
+ onlyShowEditOnDirty={true}
52
+ >
53
+ {({ control, formState: { errors } }) => (
54
+ <Stack
55
+ pos="relative"
56
+ gap="xs"
57
+ style={{ cursor: editModeEnabled ? "pointer" : "default" }}
58
+ onDoubleClick={onDoubleClick}
59
+ >
60
+ <Text
61
+ size="sm"
62
+ style={{
63
+ color: insight.description
64
+ ? "var(--mantine-color-text)"
65
+ : "var(--mantine-color-dimmed)",
66
+ fontStyle: insight.description ? "normal" : "italic",
67
+ }}
68
+ >
69
+ {insight.description || t("placeholder.description")}
70
+ </Text>
71
+ {children}
72
+
73
+ {onUpdate && (
74
+ <Group gap="xs" w="100%">
75
+ <Box
76
+ style={{
77
+ flex: 1,
78
+ display: "flex",
79
+ alignItems: "center",
80
+ justifyContent: "center",
81
+ }}
82
+ >
83
+ <Tooltip label={insight.owner?.toUpperCase()[0] || ""}>
84
+ <UserIcon style={{ width: rem(18), height: rem(18) }} />
85
+ </Tooltip>
86
+ </Box>
87
+ <Box
88
+ style={{
89
+ flex: 3,
90
+ display: "flex",
91
+ }}
92
+ >
93
+ {t("placeholder.accessLabel")}:
94
+ </Box>
95
+ <Controller
96
+ name="read"
97
+ control={control}
98
+ rules={{ required: t("common.required") }}
99
+ render={({ field }) => {
100
+ return (
101
+ <Box style={{ flex: 4 }}>
102
+ <Select
103
+ value={getAccessLevel(field.value ?? [])}
104
+ onChange={(value) => field.onChange(getRoles(value))}
105
+ placeholder={t("placeholder.readAccess")}
106
+ data={readOptions}
107
+ clearable
108
+ error={errors.read?.message}
109
+ />
110
+ </Box>
111
+ );
112
+ }}
113
+ />
114
+ <Controller
115
+ name="write"
116
+ control={control}
117
+ rules={{ required: t("common.required") }}
118
+ render={({ field }) => {
119
+ return (
120
+ <Box style={{ flex: 4 }}>
121
+ <Select
122
+ value={getAccessLevel(field.value ?? [])}
123
+ onChange={(value) => field.onChange(getRoles(value))}
124
+ placeholder={t("placeholder.writeAccess")}
125
+ data={writeOptions}
126
+ clearable
127
+ error={errors.write?.message}
128
+ />
129
+ </Box>
130
+ );
131
+ }}
132
+ />
133
+ </Group>
134
+ )}
135
+ </Stack>
136
+ )}
137
+ </BaseForm>
138
+ );
139
+ };
@@ -0,0 +1 @@
1
+ export { InsightForm } from './EditableForm';
@@ -0,0 +1,59 @@
1
+ import { useState, type PropsWithChildren } from "react";
2
+ import type { MonitoringSource } from "omni-monitoring-client";
3
+ import { EditingForm } from "./EditingForm";
4
+ import { StaticForm } from "./StaticForm";
5
+
6
+ interface Props extends PropsWithChildren {
7
+ source: MonitoringSource;
8
+ onSubmit?: (data: MonitoringSource) => void;
9
+ onUpdate?: (data: Partial<MonitoringSource>) => void;
10
+ onClose?: () => void;
11
+ exitButton?: React.ReactNode;
12
+ }
13
+
14
+ export const MonitoringSourceForm: React.FC<Props> = ({
15
+ source,
16
+ onSubmit,
17
+ onUpdate,
18
+ onClose,
19
+ exitButton,
20
+ children,
21
+ }) => {
22
+ const [isEditing, setIsEditing] = useState(onSubmit !== undefined || false);
23
+
24
+ if (onSubmit !== undefined && onUpdate !== undefined) {
25
+ throw new Error("onSubmit cannot be defined at the same time with onUpdate");
26
+ }
27
+
28
+ const handlClose = () => {
29
+ if (onUpdate !== undefined) {
30
+ setIsEditing(false);
31
+ }
32
+ onClose?.();
33
+ };
34
+
35
+ const handleDoubleClick = () => {
36
+ if (onUpdate !== undefined) {
37
+ setIsEditing(true);
38
+ }
39
+ };
40
+
41
+ return isEditing ? (
42
+ <EditingForm
43
+ source={source}
44
+ onSubmit={onSubmit}
45
+ onUpdate={onUpdate}
46
+ onClose={handlClose}
47
+ />
48
+ ) : (
49
+ <StaticForm
50
+ source={source}
51
+ onClose={handlClose}
52
+ onDoubleClick={handleDoubleClick}
53
+ exitButton={exitButton || <></>}
54
+ editModeEnabled={onUpdate !== undefined}
55
+ >
56
+ {children}
57
+ </StaticForm>
58
+ );
59
+ };