@centreon/ui 24.10.12 → 24.10.13

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 (153) hide show
  1. package/package.json +3 -2
  2. package/public/mockServiceWorker.js +1 -1
  3. package/src/Button/Icon/index.tsx +3 -1
  4. package/src/Dashboard/Dashboard.styles.ts +3 -4
  5. package/src/Dashboard/DashboardLayout.stories.tsx +1 -1
  6. package/src/Dashboard/Grid.tsx +11 -17
  7. package/src/Dashboard/Layout.tsx +27 -56
  8. package/src/Dialog/UnsavedChanges/index.tsx +15 -13
  9. package/src/Dialog/UnsavedChanges/translatedLabels.ts +15 -13
  10. package/src/Form/Form.tsx +0 -1
  11. package/src/Form/Inputs/Autocomplete.tsx +1 -1
  12. package/src/Form/Inputs/ConnectedAutocomplete.tsx +5 -2
  13. package/src/Form/Inputs/Grid.tsx +7 -1
  14. package/src/Form/Inputs/Radio.tsx +1 -1
  15. package/src/Form/Inputs/Switch.tsx +1 -1
  16. package/src/Form/Inputs/Text.tsx +1 -1
  17. package/src/Form/Inputs/index.tsx +25 -24
  18. package/src/Form/Inputs/models.ts +2 -0
  19. package/src/Graph/BarChart/BarChart.cypress.spec.tsx +3 -3
  20. package/src/Graph/BarChart/BarChart.tsx +24 -31
  21. package/src/Graph/BarChart/BarGroup.tsx +32 -59
  22. package/src/Graph/BarChart/BarStack.tsx +64 -13
  23. package/src/Graph/BarChart/MemoizedGroup.tsx +123 -0
  24. package/src/Graph/BarChart/ResponsiveBarChart.tsx +21 -7
  25. package/src/Graph/BarStack/BarStack.cypress.spec.tsx +87 -9
  26. package/src/Graph/BarStack/BarStack.stories.tsx +13 -4
  27. package/src/Graph/BarStack/BarStack.styles.ts +57 -33
  28. package/src/Graph/BarStack/Graph.tsx +173 -0
  29. package/src/Graph/BarStack/GraphAndLegend.tsx +117 -0
  30. package/src/Graph/BarStack/ResponsiveBarStack.tsx +61 -168
  31. package/src/Graph/BarStack/constants.ts +5 -0
  32. package/src/Graph/BarStack/models.ts +0 -1
  33. package/src/Graph/BarStack/useGraphAndLegend.ts +84 -0
  34. package/src/Graph/BarStack/useResponsiveBarStack.ts +73 -97
  35. package/src/Graph/Chart/Chart.cypress.spec.tsx +14 -26
  36. package/src/Graph/Chart/Chart.stories.tsx +1 -1
  37. package/src/Graph/Chart/Chart.tsx +53 -37
  38. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +3 -3
  39. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/useTickGraph.ts +19 -6
  40. package/src/Graph/Chart/Legend/Legend.styles.ts +25 -11
  41. package/src/Graph/Chart/Legend/index.tsx +6 -24
  42. package/src/Graph/Chart/index.tsx +34 -43
  43. package/src/Graph/Chart/models.ts +0 -1
  44. package/src/Graph/Chart/useChartData.ts +19 -1
  45. package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +20 -2
  46. package/src/Graph/HeatMap/model.ts +6 -2
  47. package/src/Graph/Legend/Legend.styles.ts +10 -0
  48. package/src/Graph/Legend/Legend.tsx +6 -1
  49. package/src/Graph/SingleBar/ResponsiveSingleBar.tsx +9 -10
  50. package/src/Graph/SingleBar/ThresholdLine.tsx +6 -6
  51. package/src/Graph/Text/Text.styles.ts +2 -2
  52. package/src/Graph/Text/Text.tsx +23 -10
  53. package/src/Graph/Timeline/ResponsiveTimeline.tsx +152 -0
  54. package/src/Graph/Timeline/Timeline.cypress.spec.tsx +148 -0
  55. package/src/Graph/Timeline/Timeline.stories.tsx +91 -0
  56. package/src/Graph/Timeline/Timeline.tsx +28 -0
  57. package/src/Graph/Timeline/index.ts +1 -0
  58. package/src/Graph/Timeline/models.ts +20 -0
  59. package/src/Graph/Timeline/timeline.styles.ts +11 -0
  60. package/src/Graph/Timeline/translatedLabel.ts +6 -0
  61. package/src/Graph/Timeline/useTimeline.ts +90 -0
  62. package/src/Graph/Tree/Links.tsx +2 -2
  63. package/src/Graph/Tree/Tree.tsx +2 -2
  64. package/src/Graph/Tree/constants.ts +1 -1
  65. package/src/Graph/common/Axes/index.tsx +1 -1
  66. package/src/Graph/common/Axes/useAxisY.ts +8 -4
  67. package/src/Graph/common/BaseChart/BaseChart.tsx +3 -12
  68. package/src/Graph/common/BaseChart/ChartSvgWrapper.tsx +12 -4
  69. package/src/Graph/common/BaseChart/Header/index.tsx +3 -1
  70. package/src/Graph/common/BaseChart/useComputeBaseChartDimensions.ts +23 -11
  71. package/src/Graph/common/BaseChart/useComputeYAxisMaxCharacters.ts +92 -0
  72. package/src/Graph/common/models.ts +7 -8
  73. package/src/Graph/common/timeSeries/index.test.ts +1 -1
  74. package/src/Graph/common/timeSeries/index.ts +56 -29
  75. package/src/Graph/common/timeSeries/models.ts +2 -0
  76. package/src/Graph/common/utils.ts +51 -3
  77. package/src/Graph/index.ts +4 -1
  78. package/src/Graph/mockedData/lastDayWithNullValues.json +6 -6
  79. package/src/Graph/mockedData/pingServiceLinesBars.json +47 -47
  80. package/src/Icon/DowntimeIcon.tsx +8 -1
  81. package/src/Icon/FlappingIcon.tsx +22 -0
  82. package/src/Icon/index.ts +1 -0
  83. package/src/InputField/Select/Autocomplete/Connected/Multi/index.test.tsx +21 -1
  84. package/src/InputField/Select/Autocomplete/Connected/index.test.tsx +2 -2
  85. package/src/InputField/Select/Autocomplete/Connected/index.tsx +52 -15
  86. package/src/InputField/Select/Autocomplete/Multi/index.stories.tsx +19 -0
  87. package/src/InputField/Select/Autocomplete/Multi/index.tsx +8 -5
  88. package/src/InputField/Select/Autocomplete/index.tsx +79 -54
  89. package/src/InputField/Text/index.tsx +6 -4
  90. package/src/InputField/translatedLabels.ts +2 -0
  91. package/src/Listing/ActionBar/index.tsx +1 -1
  92. package/src/Listing/Listing.styles.ts +3 -3
  93. package/src/Listing/index.tsx +40 -37
  94. package/src/Listing/models.ts +0 -8
  95. package/src/Listing/useStyleTable.ts +58 -32
  96. package/src/MultiSelectEntries/index.tsx +2 -0
  97. package/src/PopoverMenu/index.tsx +2 -9
  98. package/src/SortableItems/index.tsx +0 -1
  99. package/src/ThemeProvider/index.tsx +1 -1
  100. package/src/ThemeProvider/palettes.ts +6 -0
  101. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +2 -3
  102. package/src/TimePeriods/DateTimePickerInput.tsx +3 -1
  103. package/src/api/buildListingEndpoint/getSearchQueryParameterValue.ts +7 -1
  104. package/src/api/buildListingEndpoint/models.ts +1 -0
  105. package/src/api/customFetch.ts +4 -1
  106. package/src/api/models.ts +9 -0
  107. package/src/api/useGraphQuery/index.ts +117 -20
  108. package/src/api/useGraphQuery/models.ts +1 -0
  109. package/src/api/useMutationQuery/index.ts +1 -1
  110. package/src/components/DataTable/DataTable.styles.ts +1 -1
  111. package/src/components/DataTable/EmptyState/DataTableEmptyState.styles.ts +2 -1
  112. package/src/components/DataTable/EmptyState/DataTableEmptyState.tsx +4 -1
  113. package/src/components/DataTable/Item/DataTableItem.styles.ts +28 -2
  114. package/src/components/DataTable/Item/DataTableItem.tsx +19 -4
  115. package/src/components/Form/FormActions.tsx +21 -12
  116. package/src/components/Layout/AreaIndicator.tsx +1 -1
  117. package/src/components/Layout/PageLayout/PageLayout.styles.ts +2 -7
  118. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +0 -1
  119. package/src/components/Zoom/Zoom.tsx +9 -2
  120. package/src/components/Zoom/ZoomContent.tsx +143 -136
  121. package/src/components/Zoom/models.ts +18 -15
  122. package/src/components/Zoom/useMinimap.ts +5 -8
  123. package/src/components/Zoom/useZoom.ts +3 -3
  124. package/src/index.ts +2 -0
  125. package/src/utils/index.ts +1 -0
  126. package/src/utils/useLocale/index.ts +9 -0
  127. package/src/utils/useLocale/useLocale.cypress.spec.tsx +38 -0
  128. package/src/utils/useLocaleDateTimeFormat/index.ts +4 -2
  129. package/src/utils/usePluralizedTranslation.ts +2 -3
  130. package/src/Graph/common/timeSeries/index.test.ts-E +0 -622
  131. package/src/components/CrudPage/Actions/Actions.styles.ts +0 -16
  132. package/src/components/CrudPage/Actions/Actions.tsx +0 -24
  133. package/src/components/CrudPage/Actions/AddButton.tsx +0 -23
  134. package/src/components/CrudPage/Actions/Filters.tsx +0 -25
  135. package/src/components/CrudPage/Actions/Search.tsx +0 -31
  136. package/src/components/CrudPage/Actions/useSearch.tsx +0 -24
  137. package/src/components/CrudPage/Columns/Actions.tsx +0 -88
  138. package/src/components/CrudPage/CrudPage.cypress.spec.tsx +0 -559
  139. package/src/components/CrudPage/CrudPage.stories.tsx +0 -278
  140. package/src/components/CrudPage/CrudPageRoot.tsx +0 -142
  141. package/src/components/CrudPage/DeleteModal.tsx +0 -77
  142. package/src/components/CrudPage/Form/AddModal.tsx +0 -35
  143. package/src/components/CrudPage/Form/Buttons.tsx +0 -98
  144. package/src/components/CrudPage/Form/UpdateModal.tsx +0 -60
  145. package/src/components/CrudPage/Listing.tsx +0 -63
  146. package/src/components/CrudPage/atoms.ts +0 -30
  147. package/src/components/CrudPage/hooks/useDeleteItem.ts +0 -53
  148. package/src/components/CrudPage/hooks/useGetItem.ts +0 -36
  149. package/src/components/CrudPage/hooks/useGetItems.ts +0 -67
  150. package/src/components/CrudPage/hooks/useListingQueryKey.ts +0 -31
  151. package/src/components/CrudPage/index.tsx +0 -7
  152. package/src/components/CrudPage/models.ts +0 -118
  153. package/src/components/CrudPage/utils.ts +0 -4
@@ -1,278 +0,0 @@
1
- import { FormControlLabel, Switch, Typography } from '@mui/material';
2
- import { Meta, StoryObj } from '@storybook/react';
3
- import { atom, useAtom, useSetAtom } from 'jotai';
4
- import { http, HttpResponse } from 'msw';
5
- import { identity, isNil, prop } from 'ramda';
6
- import { ChangeEvent, useEffect } from 'react';
7
- import { CrudPage } from '.';
8
- import { SnackbarProvider } from '../../';
9
- import { Column, ColumnType } from '../../Listing/models';
10
-
11
- interface Item {
12
- id: number;
13
- name: string;
14
- description: string;
15
- subItems: Array<{ id: number; name: string }>;
16
- }
17
-
18
- interface Filters {
19
- hasDescription: boolean;
20
- isEven: boolean;
21
- }
22
-
23
- const generateItems = (count: number) =>
24
- Array(count)
25
- .fill(0)
26
- .map((_, idx) => ({
27
- id: idx,
28
- name: `Item ${idx}`,
29
- description: `Description ${idx}`,
30
- subItems: [{ id: 1, name: 'SubItem' }]
31
- }));
32
-
33
- const mockedListing = {
34
- result: generateItems(30),
35
- meta: {
36
- page: 1,
37
- total: 60,
38
- limit: 30
39
- }
40
- };
41
-
42
- const filtersAtom = atom<Filters>({
43
- hasDescription: true,
44
- isEven: false
45
- });
46
-
47
- const getSearchParameters = ({ filters }) => ({
48
- conditions: [
49
- {
50
- field: 'hasDescription',
51
- values: {
52
- $in: filters.hasDescription
53
- }
54
- },
55
- {
56
- field: 'isEven',
57
- values: {
58
- $in: filters.isEven
59
- }
60
- }
61
- ]
62
- });
63
-
64
- const labels = {
65
- title: 'Items',
66
- welcome: {
67
- title: 'Welcome to the items page',
68
- description: 'This page handles item'
69
- },
70
- actions: {
71
- create: 'Create item'
72
- },
73
- listing: {
74
- search: 'Search'
75
- }
76
- };
77
-
78
- const columns: Array<Column> = [
79
- {
80
- type: ColumnType.string,
81
- id: 'name',
82
- label: 'Name',
83
- getFormattedString: prop('name'),
84
- displaySubItemsCaret: true
85
- },
86
- {
87
- type: ColumnType.string,
88
- id: 'description',
89
- label: 'Description',
90
- getFormattedString: prop('description')
91
- }
92
- ];
93
-
94
- const Filters = () => {
95
- const [filters, setFilters] = useAtom(filtersAtom);
96
-
97
- const change =
98
- (property: string) => (event: ChangeEvent<HTMLInputElement>) => {
99
- setFilters((current) => ({
100
- ...current,
101
- [property]: event.target.checked
102
- }));
103
- };
104
-
105
- return (
106
- <>
107
- <FormControlLabel
108
- control={
109
- <Switch
110
- checked={filters.hasDescription}
111
- onChange={change('hasDescription')}
112
- />
113
- }
114
- label="Has description"
115
- />
116
- <FormControlLabel
117
- control={
118
- <Switch checked={filters.isEven} onChange={change('isEven')} />
119
- }
120
- label="Is even"
121
- />
122
- </>
123
- );
124
- };
125
-
126
- const args = {
127
- baseEndpoint: '/listing',
128
- filtersAtom,
129
- getSearchParameters,
130
- labels,
131
- columns,
132
- filters: <Filters />,
133
- form: {
134
- getItem: {
135
- baseEndpoint: (id) => `/item/${id}`,
136
- adapter: identity,
137
- itemQueryKey: 'item'
138
- },
139
- Form: ({ initialValues }) => {
140
- const [askBeforeCloseForm, setAskBeforeCloseFormModal] = useAtom(
141
- CrudPage.askBeforeCloseFormModalAtom
142
- );
143
- const setOpenFormModal = useSetAtom(CrudPage.openFormModalAtom);
144
-
145
- useEffect(() => {
146
- if (!askBeforeCloseForm) {
147
- return;
148
- }
149
-
150
- setOpenFormModal(null);
151
- setAskBeforeCloseFormModal(false);
152
- }, [askBeforeCloseForm]);
153
-
154
- return (
155
- <Typography>
156
- This is a placeholder for the form
157
- <br />
158
- Initial values: {JSON.stringify(initialValues)}
159
- </Typography>
160
- );
161
- },
162
- labels: {
163
- add: {
164
- title: 'Add item',
165
- cancel: 'Cancel',
166
- confirm: 'Add'
167
- },
168
- update: {
169
- title: 'Update item',
170
- cancel: 'Cancel',
171
- confirm: 'Update'
172
- }
173
- }
174
- },
175
- deleteItem: {
176
- deleteEndpoint: (item) =>
177
- !isNil(item?.parent)
178
- ? `/delete/${item?.parent?.id}/subItems/${item?.id}`
179
- : `/delete/${item?.id}`,
180
- labels: {
181
- successMessage: (item) =>
182
- !isNil(item?.parent) ? 'Sub item deleted' : 'Item deleted',
183
- confirm: 'Delete',
184
- cancel: 'Cancel',
185
- title: (item) =>
186
- !isNil(item?.parent) ? 'Delete sub item' : 'Delete item',
187
- description: (item) =>
188
- !isNil(item?.parent) ? (
189
- <Typography>
190
- The sub item <strong>{item?.name}</strong> from the item{' '}
191
- <strong>{item?.parent?.name}</strong> will be deleted
192
- </Typography>
193
- ) : (
194
- <Typography>
195
- The item <strong>{item?.name}</strong> will be deleted
196
- </Typography>
197
- )
198
- }
199
- }
200
- };
201
-
202
- const meta: Meta<typeof CrudPage<Item, Filters, Item, Item>> = {
203
- args,
204
- component: CrudPage<Item, Filters, Item, Item>,
205
- parameters: {
206
- msw: {
207
- handlers: [
208
- http.get('**/listing**', () => {
209
- return HttpResponse.json(mockedListing);
210
- }),
211
- http.get('**/item**', () => {
212
- return HttpResponse.json({
213
- id: 0,
214
- name: 'Item 0',
215
- description: 'Description 0',
216
- subItems: [{ id: 1, name: 'SubItem' }]
217
- });
218
- }),
219
- http.delete('**/delete**', () => {
220
- return HttpResponse.json({});
221
- })
222
- ]
223
- }
224
- },
225
- render: (args) => {
226
- return (
227
- <SnackbarProvider>
228
- <div style={{ height: '90vh' }}>
229
- <CrudPage<Item, Filters, Item, Item> {...args} />
230
- </div>
231
- </SnackbarProvider>
232
- );
233
- }
234
- };
235
-
236
- export default meta;
237
- type Story = StoryObj<typeof CrudPage<Item, Filters>>;
238
-
239
- export const Default: Story = {
240
- args: {
241
- queryKeyName: 'default'
242
- },
243
- parameters: {
244
- msw: {
245
- handlers: [
246
- http.get('**/listing**', () => {
247
- return HttpResponse.json({
248
- result: [],
249
- meta: {
250
- page: 1,
251
- total: 60,
252
- limit: 30
253
- }
254
- });
255
- })
256
- ]
257
- }
258
- }
259
- };
260
-
261
- export const WithItems: Story = {
262
- args: {
263
- queryKeyName: 'withItems'
264
- }
265
- };
266
-
267
- export const WithSubItem: Story = {
268
- args: {
269
- queryKeyName: 'subItems',
270
- subItems: {
271
- canCheckSubItems: false,
272
- enable: true,
273
- getRowProperty: () => 'subItems',
274
- labelExpand: 'Expand',
275
- labelCollapse: 'Collapse'
276
- }
277
- }
278
- };
@@ -1,142 +0,0 @@
1
- import { useSetAtom } from 'jotai';
2
- import { equals } from 'ramda';
3
- import { useCallback, useRef } from 'react';
4
- import PageSkeleton from '../../PageSkeleton';
5
- import { DataTable } from '../DataTable';
6
- import { PageHeader } from '../Header';
7
- import { PageLayout } from '../Layout';
8
- import DeleteModal from './DeleteModal';
9
- import AddModal from './Form/AddModal';
10
- import UpdateModal from './Form/UpdateModal';
11
- import Listing from './Listing';
12
- import {
13
- canDeleteSubItemsAtom,
14
- formLabelButtonsAtom,
15
- openFormModalAtom
16
- } from './atoms';
17
- import { useGetItems } from './hooks/useGetItems';
18
- import type { CrudPageRootProps } from './models';
19
-
20
- export const CrudPageRoot = <
21
- TData extends { id: number; name: string },
22
- TFilters,
23
- TItem extends { id: number; name: string },
24
- TItemForm
25
- >({
26
- labels,
27
- decoder,
28
- queryKeyName,
29
- filtersAtom,
30
- getSearchParameters,
31
- baseEndpoint,
32
- columns,
33
- subItems,
34
- filters,
35
- deleteItem,
36
- form
37
- }: CrudPageRootProps<TData, TFilters, TItem, TItemForm>): JSX.Element => {
38
- const previousCanDeleteSubItemRef = useRef<boolean | undefined>();
39
- const previousFormLabelButtonsRef = useRef<unknown | null>(null);
40
- const { isDataEmpty, hasItems, isLoading, items, total } = useGetItems<
41
- TData,
42
- TFilters
43
- >({
44
- queryKeyName,
45
- filtersAtom,
46
- decoder,
47
- getSearchParameters,
48
- baseEndpoint
49
- });
50
-
51
- const setOpenFormModal = useSetAtom(openFormModalAtom);
52
- const setCanDeleteSubItem = useSetAtom(canDeleteSubItemsAtom);
53
- const setFormLabelButton = useSetAtom(formLabelButtonsAtom);
54
-
55
- if (
56
- !equals(previousCanDeleteSubItemRef.current, subItems?.canDeleteSubItems)
57
- ) {
58
- setCanDeleteSubItem(subItems?.canDeleteSubItems ?? true);
59
- previousCanDeleteSubItemRef.current = subItems?.canDeleteSubItems;
60
- }
61
-
62
- if (!equals(previousFormLabelButtonsRef.current, form.labels)) {
63
- setFormLabelButton({
64
- add: {
65
- cancel: form.labels.add.cancel,
66
- confirm: form.labels.add.confirm
67
- },
68
- update: {
69
- cancel: form.labels.update.cancel,
70
- confirm: form.labels.update.confirm
71
- }
72
- });
73
- previousFormLabelButtonsRef.current = form.labels;
74
- }
75
-
76
- const add = useCallback(() => setOpenFormModal('add'), []);
77
-
78
- if (isLoading && !hasItems) {
79
- return <PageSkeleton displayHeaderAndNavigation={false} />;
80
- }
81
-
82
- return (
83
- <PageLayout>
84
- <PageLayout.Header>
85
- <PageHeader>
86
- <PageHeader.Title title={labels.title} />
87
- </PageHeader>
88
- </PageLayout.Header>
89
- <PageLayout.Body>
90
- <>
91
- <DataTable
92
- isEmpty={isDataEmpty}
93
- variant={isDataEmpty ? 'grid' : 'listing'}
94
- >
95
- {isDataEmpty && !isLoading ? (
96
- <DataTable.EmptyState
97
- aria-label="create"
98
- data-testid="create-agent-configuration"
99
- labels={{
100
- title: labels.welcome.title,
101
- description: labels.welcome.description,
102
- actions: labels?.actions
103
- }}
104
- onCreate={add}
105
- />
106
- ) : (
107
- <Listing
108
- total={total}
109
- isLoading={isLoading}
110
- rows={items}
111
- columns={columns}
112
- subItems={subItems}
113
- labels={{
114
- add: labels.actions.create,
115
- search: labels.listing.search
116
- }}
117
- filters={filters}
118
- />
119
- )}
120
- </DataTable>
121
- <DeleteModal<TData>
122
- listingQueryKey={queryKeyName}
123
- deleteEndpoint={deleteItem.deleteEndpoint}
124
- labels={deleteItem.labels}
125
- modalSize={deleteItem.modalSize}
126
- />
127
- <AddModal
128
- title={form.labels.add.title}
129
- Form={form.Form}
130
- modalSize={form.modalSize}
131
- />
132
- <UpdateModal<TItem, TItemForm>
133
- title={form.labels.update.title}
134
- Form={form.Form}
135
- modalSize={form.modalSize}
136
- {...form.getItem}
137
- />
138
- </>
139
- </PageLayout.Body>
140
- </PageLayout>
141
- );
142
- };
@@ -1,77 +0,0 @@
1
- import { Box, CircularProgress, Typography } from '@mui/material';
2
- import { useAtom } from 'jotai';
3
- import { useCallback, useRef } from 'react';
4
- import { Button } from '../Button';
5
- import { Modal } from '../Modal';
6
- import { itemToDeleteAtom } from './atoms';
7
- import { useDeleteItem } from './hooks/useDeleteItem';
8
- import { DeleteItem, ItemToDelete } from './models';
9
- import { isAFunction } from './utils';
10
-
11
- const DeleteModal = <TData extends { id: number; name: string }>({
12
- labels,
13
- deleteEndpoint,
14
- listingQueryKey,
15
- modalSize = 'medium'
16
- }: DeleteItem & {
17
- listingQueryKey: string;
18
- }): JSX.Element => {
19
- const itemToDeleteRef = useRef<ItemToDelete | null>(null);
20
-
21
- const [itemToDelete, setItemToDelete] = useAtom(itemToDeleteAtom);
22
-
23
- const { isMutating, deleteItem } = useDeleteItem({
24
- deleteEndpoint,
25
- listingQueryKey,
26
- successMessage: labels.successMessage
27
- });
28
-
29
- const isOpen = Boolean(itemToDelete);
30
-
31
- const close = useCallback(() => {
32
- setItemToDelete(null);
33
- }, []);
34
-
35
- const confirm = useCallback(() => {
36
- deleteItem(itemToDeleteRef.current).then(close);
37
- }, [itemToDeleteRef.current]);
38
-
39
- if (isOpen) {
40
- itemToDeleteRef.current = itemToDelete;
41
- }
42
-
43
- return (
44
- <Modal open={isOpen} onClose={close} size={modalSize}>
45
- <Modal.Header>
46
- {isAFunction(labels.title)
47
- ? labels.title(itemToDeleteRef.current as TData)
48
- : labels.title}
49
- </Modal.Header>
50
- <Modal.Body>
51
- <Typography>
52
- {isAFunction(labels.description)
53
- ? labels.description(itemToDeleteRef.current as TData)
54
- : labels.description}
55
- </Typography>
56
- </Modal.Body>
57
- <Box
58
- sx={{
59
- display: 'flex',
60
- flexDirection: 'row',
61
- gap: 2,
62
- justifyContent: 'flex-end'
63
- }}
64
- >
65
- {isMutating && <CircularProgress size={20} />}
66
- <Button variant="ghost" onClick={close} disabled={isMutating}>
67
- {labels.cancel}
68
- </Button>
69
- <Button isDanger onClick={confirm} disabled={isMutating}>
70
- {labels.confirm}
71
- </Button>
72
- </Box>
73
- </Modal>
74
- );
75
- };
76
-
77
- export default DeleteModal;
@@ -1,35 +0,0 @@
1
- import { useAtomValue, useSetAtom } from 'jotai';
2
- import { equals, isNotNil } from 'ramda';
3
- import { useCallback, useMemo } from 'react';
4
- import { Modal } from '../../Modal';
5
- import { askBeforeCloseFormModalAtom, openFormModalAtom } from '../atoms';
6
- import Buttons from './Buttons';
7
-
8
- const AddModal = ({ title, Form, modalSize = 'medium' }): JSX.Element => {
9
- const setAskBeforeCloseFormModal = useSetAtom(askBeforeCloseFormModalAtom);
10
-
11
- const openFormModal = useAtomValue(openFormModalAtom);
12
-
13
- const isModalOpen = useMemo(
14
- () => isNotNil(openFormModal) && equals('add', openFormModal),
15
- [openFormModal]
16
- );
17
-
18
- const openAskBeforeClose = useCallback(
19
- () => setAskBeforeCloseFormModal(true),
20
- []
21
- );
22
-
23
- return (
24
- <>
25
- <Modal open={isModalOpen} onClose={openAskBeforeClose} size={modalSize}>
26
- <Modal.Header>{title}</Modal.Header>
27
- <Modal.Body>
28
- <Form Buttons={Buttons} />
29
- </Modal.Body>
30
- </Modal>
31
- </>
32
- );
33
- };
34
-
35
- export default AddModal;
@@ -1,98 +0,0 @@
1
- import { UnsavedChangesDialog } from '@centreon/ui';
2
- import SaveIcon from '@mui/icons-material/Save';
3
- import { Box, CircularProgress } from '@mui/material';
4
- import { useFormikContext } from 'formik';
5
- import { useAtom, useAtomValue } from 'jotai';
6
- import { equals } from 'ramda';
7
- import { useCallback, useEffect, useMemo } from 'react';
8
- import { Button } from '../../Button';
9
- import {
10
- askBeforeCloseFormModalAtom,
11
- formLabelButtonsAtom,
12
- openFormModalAtom
13
- } from '../atoms';
14
-
15
- const Buttons = (): JSX.Element => {
16
- const [askBeforeCloseForm, setAskBeforeCloseFormModal] = useAtom(
17
- askBeforeCloseFormModalAtom
18
- );
19
- const [openFormModal, setOpenFormModal] = useAtom(openFormModalAtom);
20
- const labels = useAtomValue(formLabelButtonsAtom);
21
-
22
- const { isValid, dirty, isSubmitting, submitForm } = useFormikContext();
23
-
24
- const isSubmitDisabled = useMemo(
25
- () => !dirty || !isValid || isSubmitting,
26
- [dirty, isValid, isSubmitting]
27
- );
28
- const cancelLabel = useMemo(
29
- () =>
30
- equals(openFormModal, 'add') ? labels.add.cancel : labels.update.cancel,
31
- [labels, openFormModal]
32
- );
33
- const confirmLabel = useMemo(
34
- () =>
35
- equals(openFormModal, 'add') ? labels.add.confirm : labels.update.confirm,
36
- [labels, openFormModal]
37
- );
38
-
39
- const discard = useCallback(() => {
40
- setAskBeforeCloseFormModal(false);
41
- setOpenFormModal(null);
42
- }, []);
43
-
44
- const close = useCallback(() => {
45
- if (dirty) {
46
- setAskBeforeCloseFormModal(true);
47
- return;
48
- }
49
- setOpenFormModal(null);
50
- setAskBeforeCloseFormModal(false);
51
- }, [dirty]);
52
-
53
- const submitAndClose = useCallback(() => {
54
- submitForm();
55
- setAskBeforeCloseFormModal(false);
56
- }, []);
57
-
58
- const closeAskBeforeCloseModal = useCallback(() => {
59
- setAskBeforeCloseFormModal(false);
60
- }, []);
61
-
62
- useEffect(() => {
63
- if (!askBeforeCloseForm || dirty) {
64
- return;
65
- }
66
-
67
- close();
68
- }, [askBeforeCloseForm, dirty]);
69
-
70
- return (
71
- <>
72
- <Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end' }}>
73
- {isSubmitting && <CircularProgress size={24} />}
74
- <Button variant="ghost" onClick={close}>
75
- {cancelLabel}
76
- </Button>
77
- <Button
78
- disabled={isSubmitDisabled}
79
- iconVariant="start"
80
- icon={<SaveIcon />}
81
- onClick={submitForm}
82
- >
83
- {confirmLabel}
84
- </Button>
85
- </Box>
86
- <UnsavedChangesDialog
87
- isSubmitting={isSubmitting}
88
- isValidForm={isValid}
89
- saveChanges={submitAndClose}
90
- closeDialog={closeAskBeforeCloseModal}
91
- discardChanges={discard}
92
- dialogOpened={askBeforeCloseForm && dirty}
93
- />
94
- </>
95
- );
96
- };
97
-
98
- export default Buttons;
@@ -1,60 +0,0 @@
1
- import { useAtomValue, useSetAtom } from 'jotai';
2
- import { equals, isNotNil } from 'ramda';
3
- import { useCallback, useMemo } from 'react';
4
- import { Modal } from '../..';
5
- import { askBeforeCloseFormModalAtom, openFormModalAtom } from '../atoms';
6
- import { useGetItem } from '../hooks/useGetItem';
7
- import { Form as FormModel, GetItem } from '../models';
8
- import Buttons from './Buttons';
9
-
10
- const UpdateModal = <TItem extends { id: number; name: string }, TItemForm>({
11
- decoder,
12
- baseEndpoint,
13
- itemQueryKey,
14
- adapter,
15
- Form,
16
- title,
17
- modalSize = 'medium'
18
- }: GetItem<TItem, TItemForm> &
19
- Pick<FormModel<TItem, TItemForm>, 'Form' | 'modalSize'> & {
20
- title: string;
21
- }) => {
22
- const setAskBeforeCloseFormModal = useSetAtom(askBeforeCloseFormModalAtom);
23
-
24
- const openFormModal = useAtomValue(openFormModalAtom);
25
-
26
- const { initialValues, isLoading } = useGetItem({
27
- id: openFormModal,
28
- decoder,
29
- baseEndpoint,
30
- itemQueryKey,
31
- adapter
32
- });
33
-
34
- const isModalOpen = useMemo(
35
- () => isNotNil(openFormModal) && !equals('add', openFormModal),
36
- [openFormModal]
37
- );
38
-
39
- const openAskBeforeClose = useCallback(
40
- () => setAskBeforeCloseFormModal(true),
41
- []
42
- );
43
-
44
- return (
45
- <>
46
- <Modal open={isModalOpen} onClose={openAskBeforeClose} size={modalSize}>
47
- <Modal.Header>{title}</Modal.Header>
48
- <Modal.Body>
49
- <Form
50
- initialValues={initialValues}
51
- Buttons={Buttons}
52
- isLoading={isLoading}
53
- />
54
- </Modal.Body>
55
- </Modal>
56
- </>
57
- );
58
- };
59
-
60
- export default UpdateModal;