@centreon/ui 23.10.39 → 23.10.41
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/package.json +3 -1
- package/src/Listing/Cell/DataCell.tsx +16 -4
- package/src/Listing/Cell/index.tsx +82 -2
- package/src/Listing/Listing.cypress.spec.tsx +120 -0
- package/src/Listing/Listing.styles.ts +2 -2
- package/src/Listing/ListingDocs.mdx +61 -0
- package/src/Listing/Row/Row.tsx +5 -2
- package/src/Listing/index.stories.tsx +104 -31
- package/src/Listing/index.tsx +103 -33
- package/src/Listing/models.ts +1 -0
- package/src/Listing/tableAtoms.ts +2 -0
- package/src/Panel/index.tsx +1 -1
- package/src/ThemeProvider/index.tsx +0 -24
- package/src/ThemeProvider/palettes.ts +36 -6
- package/src/api/useMutationQuery/index.ts +16 -6
- package/src/components/Form/AccessRights/AccessRights.resource.ts +45 -0
- package/src/components/Form/AccessRights/AccessRightsForm.stories.tsx +59 -0
- package/src/components/Form/AccessRights/AccessRightsForm.styles.ts +21 -0
- package/src/components/Form/AccessRights/AccessRightsForm.tsx +67 -0
- package/src/components/Form/AccessRights/AccessRightsFormActions.tsx +80 -0
- package/src/components/Form/AccessRights/Input/AddAction.tsx +31 -0
- package/src/components/Form/AccessRights/Input/ContactAccessRightInput.stories.tsx +54 -0
- package/src/components/Form/AccessRights/Input/ContactAccessRightInput.tsx +72 -0
- package/src/components/Form/AccessRights/Input/ContactAccessRightsInput.styles.ts +22 -0
- package/src/components/Form/AccessRights/Input/ContactInputField.tsx +105 -0
- package/src/components/Form/AccessRights/Input/RoleInputField.tsx +29 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsList.stories.tsx +97 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsList.styles.ts +71 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsList.tsx +51 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsListItem.stories.tsx +116 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsListItem.tsx +118 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsListItemSkeleton.tsx +26 -0
- package/src/components/Form/AccessRights/List/ContactAccessRightsListSkeleton.tsx +28 -0
- package/src/components/Form/AccessRights/Stats/AccessRightsStats.styles.ts +18 -0
- package/src/components/Form/AccessRights/Stats/AccessRightsStats.tsx +41 -0
- package/src/components/Form/AccessRights/__fixtures__/contactAccessRight.mock.ts +52 -0
- package/src/components/Form/AccessRights/common/GroupLabel.styles.ts +18 -0
- package/src/components/Form/AccessRights/common/GroupLabel.tsx +15 -0
- package/src/components/Form/AccessRights/common/Input.styles.ts +48 -0
- package/src/components/Form/AccessRights/common/RoleInputSelect.styles.ts +11 -0
- package/src/components/Form/AccessRights/common/RoleInputSelect.tsx +57 -0
- package/src/components/Form/AccessRights/index.ts +3 -0
- package/src/components/Form/AccessRights/useAccessRightsForm.test.tsx +531 -0
- package/src/components/Form/AccessRights/useAccessRightsForm.tsx +282 -0
- package/src/components/Form/AccessRights/useAccessRightsForm.utils.ts +41 -0
- package/src/components/Form/Dashboard/DashboardForm.styles.ts +1 -7
- package/src/components/Form/Dashboard/DashboardForm.tsx +9 -12
- package/src/components/Form/Form.resource.ts +1 -0
- package/src/components/Form/Form.styles.ts +17 -0
- package/src/components/Form/{Dashboard/DashboardFormActions.tsx → FormActions.tsx} +17 -16
- package/src/components/Form/index.ts +1 -0
- package/src/components/Header/PageHeader/PageHeader.stories.tsx +9 -0
- package/src/components/List/Item/Avatar.tsx +21 -0
- package/src/components/List/Item/AvatarSkeleton.tsx +17 -0
- package/src/components/List/Item/ListItem.stories.tsx +41 -0
- package/src/components/List/Item/ListItem.styles.ts +57 -0
- package/src/components/List/Item/ListItem.tsx +32 -0
- package/src/components/List/Item/Text.tsx +25 -0
- package/src/components/List/Item/TextSkeleton.tsx +22 -0
- package/src/components/List/Item/index.ts +14 -0
- package/src/components/List/List.stories.tsx +70 -0
- package/src/components/List/List.styles.ts +8 -0
- package/src/components/List/List.tsx +19 -0
- package/src/components/List/index.ts +6 -0
- package/src/components/Modal/ModalActions.tsx +4 -1
- package/src/components/index.ts +1 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/useInfiniteScrollListing.ts +106 -0
package/src/Listing/index.tsx
CHANGED
|
@@ -18,14 +18,17 @@ import {
|
|
|
18
18
|
map,
|
|
19
19
|
not,
|
|
20
20
|
pick,
|
|
21
|
+
pluck,
|
|
21
22
|
prop,
|
|
22
23
|
propEq,
|
|
24
|
+
reduce,
|
|
23
25
|
reject,
|
|
24
26
|
slice,
|
|
25
27
|
subtract,
|
|
26
28
|
uniqBy
|
|
27
29
|
} from 'ramda';
|
|
28
30
|
import { useTranslation } from 'react-i18next';
|
|
31
|
+
import { useAtomValue } from 'jotai';
|
|
29
32
|
|
|
30
33
|
import { Box, LinearProgress, Table, TableBody } from '@mui/material';
|
|
31
34
|
|
|
@@ -50,10 +53,11 @@ import ListingRow from './Row/Row';
|
|
|
50
53
|
import { labelNoResultFound } from './translatedLabels';
|
|
51
54
|
import useResizeObserver from './useResizeObserver';
|
|
52
55
|
import useStyleTable from './useStyleTable';
|
|
53
|
-
import { loadingIndicatorHeight,
|
|
56
|
+
import { loadingIndicatorHeight, useListingStyles } from './Listing.styles';
|
|
54
57
|
import { EmptyResult } from './EmptyResult/EmptyResult';
|
|
55
58
|
import { SkeletonLoader } from './Row/SkeletonLoaderRows';
|
|
56
59
|
import { ListingHeader } from './Header';
|
|
60
|
+
import { subItemsPivotsAtom } from './tableAtoms';
|
|
57
61
|
|
|
58
62
|
const getVisibleColumns = ({
|
|
59
63
|
columnConfiguration,
|
|
@@ -114,6 +118,13 @@ export interface Props<TRow> {
|
|
|
114
118
|
selectedRows?: Array<TRow>;
|
|
115
119
|
sortField?: string;
|
|
116
120
|
sortOrder?: SortOrder;
|
|
121
|
+
subItems?: {
|
|
122
|
+
canCheckSubItems: boolean;
|
|
123
|
+
enable: boolean;
|
|
124
|
+
labelCollapse: string;
|
|
125
|
+
labelExpand: string;
|
|
126
|
+
rowProperty: string;
|
|
127
|
+
};
|
|
117
128
|
totalRows?: number;
|
|
118
129
|
viewMode?: ListingVariant;
|
|
119
130
|
viewerModeConfiguration?: ViewerModeConfiguration;
|
|
@@ -160,7 +171,14 @@ const Listing = <TRow extends { id: RowId }>({
|
|
|
160
171
|
viewMode = ListingVariant.compact,
|
|
161
172
|
widthToMoveTablePagination,
|
|
162
173
|
getHighlightRowCondition,
|
|
163
|
-
viewerModeConfiguration
|
|
174
|
+
viewerModeConfiguration,
|
|
175
|
+
subItems = {
|
|
176
|
+
canCheckSubItems: false,
|
|
177
|
+
enable: false,
|
|
178
|
+
labelCollapse: 'Collapse',
|
|
179
|
+
labelExpand: 'Expand',
|
|
180
|
+
rowProperty: ''
|
|
181
|
+
}
|
|
164
182
|
}: Props<TRow>): JSX.Element => {
|
|
165
183
|
const currentVisibleColumns = getVisibleColumns({
|
|
166
184
|
columnConfiguration,
|
|
@@ -172,13 +190,6 @@ const Listing = <TRow extends { id: RowId }>({
|
|
|
172
190
|
viewMode
|
|
173
191
|
});
|
|
174
192
|
|
|
175
|
-
const { classes, theme } = useStyles({
|
|
176
|
-
dataStyle,
|
|
177
|
-
getGridTemplateColumn,
|
|
178
|
-
limit,
|
|
179
|
-
rows,
|
|
180
|
-
viewMode
|
|
181
|
-
});
|
|
182
193
|
const { t } = useTranslation();
|
|
183
194
|
|
|
184
195
|
const [tableTopOffset, setTableTopOffset] = React.useState(0);
|
|
@@ -192,6 +203,37 @@ const Listing = <TRow extends { id: RowId }>({
|
|
|
192
203
|
const containerRef = React.useRef<HTMLDivElement>();
|
|
193
204
|
const actionBarRef = React.useRef<HTMLDivElement>();
|
|
194
205
|
|
|
206
|
+
const subItemsPivots = useAtomValue(subItemsPivotsAtom);
|
|
207
|
+
|
|
208
|
+
const rowsToDisplay = React.useMemo(
|
|
209
|
+
() =>
|
|
210
|
+
subItems?.enable
|
|
211
|
+
? reduce<TRow, Array<TRow>>(
|
|
212
|
+
(acc, row): Array<TRow> => {
|
|
213
|
+
if (
|
|
214
|
+
row[subItems.rowProperty] &&
|
|
215
|
+
subItemsPivots.includes(row.id)
|
|
216
|
+
) {
|
|
217
|
+
return [...acc, row, ...row[subItems.rowProperty]];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return [...acc, row];
|
|
221
|
+
},
|
|
222
|
+
[],
|
|
223
|
+
rows
|
|
224
|
+
)
|
|
225
|
+
: rows,
|
|
226
|
+
[rows, subItemsPivots, subItems]
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
const { classes, theme } = useListingStyles({
|
|
230
|
+
dataStyle,
|
|
231
|
+
getGridTemplateColumn,
|
|
232
|
+
limit,
|
|
233
|
+
rows: rowsToDisplay,
|
|
234
|
+
viewMode
|
|
235
|
+
});
|
|
236
|
+
|
|
195
237
|
useResizeObserver({
|
|
196
238
|
onResize: () => {
|
|
197
239
|
setTableTopOffset(getCumulativeOffset(containerRef.current));
|
|
@@ -422,6 +464,19 @@ const Listing = <TRow extends { id: RowId }>({
|
|
|
422
464
|
|
|
423
465
|
const areColumnsEditable = not(isNil(onSelectColumns));
|
|
424
466
|
|
|
467
|
+
const allSubItemIds = React.useMemo(
|
|
468
|
+
() =>
|
|
469
|
+
reduce<TRow | number, Array<string | number>>(
|
|
470
|
+
(acc, row) => [
|
|
471
|
+
...acc,
|
|
472
|
+
...pluck('id', row[subItems?.rowProperty || ''] || [])
|
|
473
|
+
],
|
|
474
|
+
[],
|
|
475
|
+
rows
|
|
476
|
+
),
|
|
477
|
+
[rows, subItems]
|
|
478
|
+
);
|
|
479
|
+
|
|
425
480
|
return (
|
|
426
481
|
<>
|
|
427
482
|
{loading && rows.length > 0 && (
|
|
@@ -495,14 +550,16 @@ const Listing = <TRow extends { id: RowId }>({
|
|
|
495
550
|
component="div"
|
|
496
551
|
onMouseLeave={clearHoveredRow}
|
|
497
552
|
>
|
|
498
|
-
{
|
|
553
|
+
{rowsToDisplay.map((row, index) => {
|
|
499
554
|
const isRowSelected = isSelected(row);
|
|
500
|
-
|
|
501
555
|
const isRowHovered = equals(hoveredRowId, getId(row));
|
|
556
|
+
const isSubItem = allSubItemIds.includes(row.id);
|
|
502
557
|
|
|
503
558
|
return (
|
|
504
559
|
<ListingRow
|
|
505
|
-
checkable={
|
|
560
|
+
checkable={
|
|
561
|
+
checkable && (!isSubItem || subItems.canCheckSubItems)
|
|
562
|
+
}
|
|
506
563
|
columnConfiguration={columnConfiguration}
|
|
507
564
|
columnIds={columns.map(prop('id'))}
|
|
508
565
|
disableRowCondition={disableRowCondition}
|
|
@@ -519,6 +576,7 @@ const Listing = <TRow extends { id: RowId }>({
|
|
|
519
576
|
row={row}
|
|
520
577
|
rowColorConditions={rowColorConditions}
|
|
521
578
|
shiftKeyDownRowPivot={shiftKeyDownRowPivot}
|
|
579
|
+
subItemsPivots={subItemsPivots}
|
|
522
580
|
tabIndex={-1}
|
|
523
581
|
viewMode={viewMode}
|
|
524
582
|
visibleColumns={visibleColumns}
|
|
@@ -528,28 +586,37 @@ const Listing = <TRow extends { id: RowId }>({
|
|
|
528
586
|
onFocus={(): void => hoverRow(row)}
|
|
529
587
|
onMouseOver={(): void => hoverRow(row)}
|
|
530
588
|
>
|
|
531
|
-
{checkable &&
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
589
|
+
{checkable &&
|
|
590
|
+
(!isSubItem || subItems.canCheckSubItems ? (
|
|
591
|
+
<Cell
|
|
592
|
+
align="left"
|
|
593
|
+
className={classes.checkbox}
|
|
594
|
+
disableRowCondition={disableRowCondition}
|
|
595
|
+
isRowHovered={isRowHovered}
|
|
596
|
+
row={row}
|
|
597
|
+
rowColorConditions={rowColorConditions}
|
|
598
|
+
onClick={(event): void => selectRow(event, row)}
|
|
599
|
+
>
|
|
600
|
+
<Checkbox
|
|
601
|
+
checked={isRowSelected}
|
|
602
|
+
disabled={
|
|
603
|
+
disableRowCheckCondition(row) ||
|
|
604
|
+
disableRowCondition(row)
|
|
605
|
+
}
|
|
606
|
+
inputProps={{
|
|
607
|
+
'aria-label': `Select row ${getId(row)}`
|
|
608
|
+
}}
|
|
609
|
+
/>
|
|
610
|
+
</Cell>
|
|
611
|
+
) : (
|
|
612
|
+
<Cell
|
|
613
|
+
align="left"
|
|
614
|
+
disableRowCondition={disableRowCondition}
|
|
615
|
+
isRowHovered={isRowHovered}
|
|
616
|
+
row={row}
|
|
617
|
+
rowColorConditions={rowColorConditions}
|
|
550
618
|
/>
|
|
551
|
-
|
|
552
|
-
)}
|
|
619
|
+
))}
|
|
553
620
|
|
|
554
621
|
{visibleColumns.map((column) => (
|
|
555
622
|
<DataCell
|
|
@@ -559,8 +626,11 @@ const Listing = <TRow extends { id: RowId }>({
|
|
|
559
626
|
isRowHovered={isRowHovered}
|
|
560
627
|
isRowSelected={isRowSelected}
|
|
561
628
|
key={`${getId(row)}-${column.id}`}
|
|
629
|
+
labelCollapse={subItems.labelCollapse}
|
|
630
|
+
labelExpand={subItems.labelExpand}
|
|
562
631
|
row={row}
|
|
563
632
|
rowColorConditions={rowColorConditions}
|
|
633
|
+
subItemsRowProperty={subItems?.rowProperty}
|
|
564
634
|
viewMode={viewMode}
|
|
565
635
|
/>
|
|
566
636
|
))}
|
package/src/Listing/models.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface Column {
|
|
|
19
19
|
compact?: boolean;
|
|
20
20
|
disablePadding?: boolean;
|
|
21
21
|
disabled?: boolean;
|
|
22
|
+
displaySubItemsCaret?: boolean;
|
|
22
23
|
getColSpan?: (isSelected) => number | undefined;
|
|
23
24
|
getFormattedString?: (row) => string | null;
|
|
24
25
|
getHiddenCondition?: (isSelected) => boolean;
|
package/src/Panel/index.tsx
CHANGED
|
@@ -148,30 +148,6 @@ export const getTheme = (mode: ThemeMode): ThemeOptions => ({
|
|
|
148
148
|
},
|
|
149
149
|
MuiCssBaseline: {
|
|
150
150
|
styleOverrides: (theme) => `
|
|
151
|
-
::-webkit-scrollbar {
|
|
152
|
-
height: ${theme.spacing(1)};
|
|
153
|
-
width: ${theme.spacing(1)};
|
|
154
|
-
background-color: ${theme.palette.background.default};
|
|
155
|
-
}
|
|
156
|
-
::-webkit-scrollbar-thumb {
|
|
157
|
-
background-color: ${
|
|
158
|
-
equals(mode, 'dark')
|
|
159
|
-
? theme.palette.divider
|
|
160
|
-
: theme.palette.text.disabled
|
|
161
|
-
};
|
|
162
|
-
border-radius: ${theme.spacing(0.5)};
|
|
163
|
-
}
|
|
164
|
-
::-webkit-scrollbar-thumb:hover {
|
|
165
|
-
background-color: ${theme.palette.primary.main};
|
|
166
|
-
}
|
|
167
|
-
* {
|
|
168
|
-
scrollbar-color: ${
|
|
169
|
-
equals(mode, 'dark')
|
|
170
|
-
? theme.palette.divider
|
|
171
|
-
: theme.palette.text.disabled
|
|
172
|
-
} ${theme.palette.background.default};
|
|
173
|
-
scrollbar-width: thin;
|
|
174
|
-
}
|
|
175
151
|
html {
|
|
176
152
|
margin: 0;
|
|
177
153
|
padding: 0;
|
|
@@ -18,6 +18,7 @@ declare module '@mui/material/styles/createPalette' {
|
|
|
18
18
|
declare module '@mui/material/styles' {
|
|
19
19
|
interface Palette {
|
|
20
20
|
background: TypeBackground;
|
|
21
|
+
chip: TypeChip;
|
|
21
22
|
header: TypeHeader;
|
|
22
23
|
layout: TypeLayout;
|
|
23
24
|
menu: TypeMenu;
|
|
@@ -36,6 +37,7 @@ declare module '@mui/material/styles' {
|
|
|
36
37
|
warning: string;
|
|
37
38
|
}
|
|
38
39
|
interface PaletteOptions {
|
|
40
|
+
chip: TypeChip;
|
|
39
41
|
header: TypeHeader;
|
|
40
42
|
layout: TypeLayout;
|
|
41
43
|
menu: TypeMenu;
|
|
@@ -113,6 +115,16 @@ declare module '@mui/material/styles' {
|
|
|
113
115
|
};
|
|
114
116
|
};
|
|
115
117
|
}
|
|
118
|
+
|
|
119
|
+
interface TypeChip {
|
|
120
|
+
color: {
|
|
121
|
+
error: string;
|
|
122
|
+
info: string;
|
|
123
|
+
neutral: string;
|
|
124
|
+
success: string;
|
|
125
|
+
warning: string;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
116
128
|
}
|
|
117
129
|
|
|
118
130
|
declare module '@mui/material/Button' {
|
|
@@ -152,6 +164,15 @@ export const lightPalette: PaletteOptions = {
|
|
|
152
164
|
paper: '#FFFFFF',
|
|
153
165
|
tooltip: '#434E5B'
|
|
154
166
|
},
|
|
167
|
+
chip: {
|
|
168
|
+
color: {
|
|
169
|
+
error: '#FF6666',
|
|
170
|
+
info: '#1588D1',
|
|
171
|
+
neutral: BaseTokens.colorGrey300,
|
|
172
|
+
success: '#88B922',
|
|
173
|
+
warning: '#FD9B27'
|
|
174
|
+
}
|
|
175
|
+
},
|
|
155
176
|
divider: '#E3E3E3',
|
|
156
177
|
error: {
|
|
157
178
|
contrastText: '#000',
|
|
@@ -193,7 +214,7 @@ export const lightPalette: PaletteOptions = {
|
|
|
193
214
|
background: {
|
|
194
215
|
active: BaseTokens.colorBlue50,
|
|
195
216
|
default: 'transparent',
|
|
196
|
-
hover: BaseTokens.
|
|
217
|
+
hover: BaseTokens.colorGrey100
|
|
197
218
|
},
|
|
198
219
|
color: {
|
|
199
220
|
active: BaseTokens.colorBlue400,
|
|
@@ -208,7 +229,7 @@ export const lightPalette: PaletteOptions = {
|
|
|
208
229
|
background: {
|
|
209
230
|
active: BaseTokens.colorBlue50,
|
|
210
231
|
default: 'transparent',
|
|
211
|
-
hover: BaseTokens.
|
|
232
|
+
hover: BaseTokens.colorGrey100
|
|
212
233
|
},
|
|
213
234
|
color: {
|
|
214
235
|
active: BaseTokens.colorBlue400,
|
|
@@ -282,6 +303,15 @@ export const darkPalette: PaletteOptions = {
|
|
|
282
303
|
paper: '#212121',
|
|
283
304
|
tooltip: '#AAB4C0'
|
|
284
305
|
},
|
|
306
|
+
chip: {
|
|
307
|
+
color: {
|
|
308
|
+
error: '#D60101',
|
|
309
|
+
info: '#1CA9F4',
|
|
310
|
+
neutral: BaseTokens.colorGrey700,
|
|
311
|
+
success: '#5F8118',
|
|
312
|
+
warning: '#C55400'
|
|
313
|
+
}
|
|
314
|
+
},
|
|
285
315
|
divider: '#666666',
|
|
286
316
|
error: {
|
|
287
317
|
contrastText: '#fff',
|
|
@@ -321,9 +351,9 @@ export const darkPalette: PaletteOptions = {
|
|
|
321
351
|
background: BaseTokens.colorGrey950,
|
|
322
352
|
button: {
|
|
323
353
|
background: {
|
|
324
|
-
active: BaseTokens.
|
|
354
|
+
active: BaseTokens.colorBlue900,
|
|
325
355
|
default: 'transparent',
|
|
326
|
-
hover: BaseTokens.
|
|
356
|
+
hover: BaseTokens.colorGrey900
|
|
327
357
|
},
|
|
328
358
|
color: {
|
|
329
359
|
active: BaseTokens.colorBlue600,
|
|
@@ -336,9 +366,9 @@ export const darkPalette: PaletteOptions = {
|
|
|
336
366
|
},
|
|
337
367
|
item: {
|
|
338
368
|
background: {
|
|
339
|
-
active: BaseTokens.
|
|
369
|
+
active: BaseTokens.colorBlue900,
|
|
340
370
|
default: 'transparent',
|
|
341
|
-
hover: BaseTokens.
|
|
371
|
+
hover: BaseTokens.colorGrey900
|
|
342
372
|
},
|
|
343
373
|
color: {
|
|
344
374
|
active: BaseTokens.colorBlue400,
|
|
@@ -26,7 +26,7 @@ export type UseMutationQueryProps<T> = {
|
|
|
26
26
|
decoder?: JsonDecoder.Decoder<T>;
|
|
27
27
|
defaultFailureMessage?: string;
|
|
28
28
|
fetchHeaders?: HeadersInit;
|
|
29
|
-
getEndpoint: (
|
|
29
|
+
getEndpoint: (_meta?: Record<string, unknown> | unknown) => string;
|
|
30
30
|
httpCodesBypassErrorSnackbar?: Array<number>;
|
|
31
31
|
method: Method;
|
|
32
32
|
} & Omit<UseMutationOptions<T>, 'mutationFn'>;
|
|
@@ -45,17 +45,22 @@ const useMutationQuery = <T extends object>({
|
|
|
45
45
|
defaultFailureMessage,
|
|
46
46
|
fetchHeaders,
|
|
47
47
|
httpCodesBypassErrorSnackbar = [],
|
|
48
|
-
method
|
|
48
|
+
method,
|
|
49
|
+
onMutate,
|
|
50
|
+
onError
|
|
49
51
|
}: UseMutationQueryProps<T>): UseMutationQueryState<T> => {
|
|
50
52
|
const { showErrorMessage } = useSnackbar();
|
|
51
53
|
|
|
52
54
|
const queryData = useMutation<T | ResponseError>(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
// @ts-expect-error useMutation / useMutationQuery is not typed correctly
|
|
56
|
+
(_payload: Record<string, unknown> | null): Promise<T | ResponseError> => {
|
|
57
|
+
const { _meta, ...payload } = _payload || {};
|
|
58
|
+
|
|
59
|
+
return customFetch<T>({
|
|
55
60
|
catchError,
|
|
56
61
|
decoder,
|
|
57
62
|
defaultFailureMessage,
|
|
58
|
-
endpoint: getEndpoint(
|
|
63
|
+
endpoint: getEndpoint(_meta),
|
|
59
64
|
headers: new Headers({
|
|
60
65
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
61
66
|
...fetchHeaders
|
|
@@ -63,7 +68,12 @@ const useMutationQuery = <T extends object>({
|
|
|
63
68
|
isMutation: true,
|
|
64
69
|
method,
|
|
65
70
|
payload
|
|
66
|
-
})
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
onError,
|
|
75
|
+
onMutate
|
|
76
|
+
}
|
|
67
77
|
);
|
|
68
78
|
|
|
69
79
|
const manageError = (): void => {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type ContactAccessRightState =
|
|
2
|
+
| 'added'
|
|
3
|
+
| 'updated'
|
|
4
|
+
| 'removed'
|
|
5
|
+
| 'unchanged';
|
|
6
|
+
|
|
7
|
+
export type ContactAccessRightStateResource = {
|
|
8
|
+
contactAccessRight: ContactAccessRightResource;
|
|
9
|
+
state: ContactAccessRightState;
|
|
10
|
+
stateHistory: Array<ContactAccessRightState>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type ContactAccessRightResource = {
|
|
14
|
+
contact: ContactResource | ContactGroupResource | null;
|
|
15
|
+
role: RoleResource['role'];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ContactResource = {
|
|
19
|
+
email?: string;
|
|
20
|
+
id?: number | string;
|
|
21
|
+
name: string;
|
|
22
|
+
type: 'contact';
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const isContactResource = (
|
|
26
|
+
resource: ContactResource | ContactGroupResource | null
|
|
27
|
+
): resource is ContactResource => {
|
|
28
|
+
return resource?.type === 'contact';
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type ContactGroupResource = {
|
|
32
|
+
id?: number | string;
|
|
33
|
+
name: string;
|
|
34
|
+
type: 'contact_group';
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const isContactGroupResource = (
|
|
38
|
+
resource: ContactResource | ContactGroupResource | null
|
|
39
|
+
): resource is ContactResource => {
|
|
40
|
+
return resource?.type === 'contact_group';
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type RoleResource = {
|
|
44
|
+
role: 'viewer' | 'editor' | string;
|
|
45
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { AccessRightsForm } from './AccessRightsForm';
|
|
4
|
+
import { Default as DefaultContactAccessRightsListStory } from './List/ContactAccessRightsList.stories';
|
|
5
|
+
import { Default as DefaultContactAccessRightInputStory } from './Input/ContactAccessRightInput.stories';
|
|
6
|
+
import { ContactAccessRightsListProps } from './List/ContactAccessRightsList';
|
|
7
|
+
import { ContactAccessRightInputProps } from './Input/ContactAccessRightInput';
|
|
8
|
+
import {
|
|
9
|
+
contactAccessRightsMock,
|
|
10
|
+
contactsAndGroupsMock
|
|
11
|
+
} from './__fixtures__/contactAccessRight.mock';
|
|
12
|
+
|
|
13
|
+
const meta: Meta<typeof AccessRightsForm> = {
|
|
14
|
+
component: AccessRightsForm
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default meta;
|
|
18
|
+
type Story = StoryObj<typeof AccessRightsForm>;
|
|
19
|
+
|
|
20
|
+
export const Default: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
initialValues: contactAccessRightsMock(12),
|
|
23
|
+
labels: {
|
|
24
|
+
actions: {
|
|
25
|
+
cancel: 'Cancel',
|
|
26
|
+
copyLink: 'Copy link',
|
|
27
|
+
copyLinkMessages: {
|
|
28
|
+
error: 'Unable to copy link',
|
|
29
|
+
success: 'Link copied'
|
|
30
|
+
},
|
|
31
|
+
submit: 'Update'
|
|
32
|
+
},
|
|
33
|
+
input: {
|
|
34
|
+
...DefaultContactAccessRightInputStory.args?.labels
|
|
35
|
+
} as ContactAccessRightInputProps['labels'],
|
|
36
|
+
list: {
|
|
37
|
+
...DefaultContactAccessRightsListStory.args?.labels
|
|
38
|
+
} as ContactAccessRightsListProps['labels'],
|
|
39
|
+
stats: {
|
|
40
|
+
added: 'added',
|
|
41
|
+
removed: 'removed',
|
|
42
|
+
updated: 'updated'
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
options: {
|
|
46
|
+
contacts: contactsAndGroupsMock(25),
|
|
47
|
+
roles: [{ role: 'viewer' }, { role: 'editor' }]
|
|
48
|
+
},
|
|
49
|
+
resourceLink: 'https://app.centreon.com/resource/1'
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const AsEmptyState: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
labels: Default.args?.labels,
|
|
56
|
+
options: Default.args?.options,
|
|
57
|
+
resourceLink: Default.args?.resourceLink
|
|
58
|
+
}
|
|
59
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { makeStyles } from 'tss-react/mui';
|
|
2
|
+
|
|
3
|
+
const useStyles = makeStyles()((theme) => ({
|
|
4
|
+
accessRightsForm: {
|
|
5
|
+
display: 'flex',
|
|
6
|
+
flexDirection: 'column',
|
|
7
|
+
gap: theme.spacing(6),
|
|
8
|
+
|
|
9
|
+
maxWidth: '520px',
|
|
10
|
+
paddingTop: theme.spacing(3),
|
|
11
|
+
|
|
12
|
+
width: '100%'
|
|
13
|
+
},
|
|
14
|
+
accessRightsFormList: {
|
|
15
|
+
display: 'flex',
|
|
16
|
+
flexDirection: 'column',
|
|
17
|
+
gap: theme.spacing(1)
|
|
18
|
+
}
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
export { useStyles };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useStyles } from './AccessRightsForm.styles';
|
|
4
|
+
import {
|
|
5
|
+
AccessRightsFormProvider,
|
|
6
|
+
AccessRightsFormProviderProps
|
|
7
|
+
} from './useAccessRightsForm';
|
|
8
|
+
import {
|
|
9
|
+
ContactAccessRightInput,
|
|
10
|
+
ContactAccessRightInputProps
|
|
11
|
+
} from './Input/ContactAccessRightInput';
|
|
12
|
+
import {
|
|
13
|
+
ContactAccessRightsList,
|
|
14
|
+
ContactAccessRightsListProps
|
|
15
|
+
} from './List/ContactAccessRightsList';
|
|
16
|
+
import {
|
|
17
|
+
AccessRightsFormActions,
|
|
18
|
+
AccessRightsFormActionsProps
|
|
19
|
+
} from './AccessRightsFormActions';
|
|
20
|
+
import {
|
|
21
|
+
AccessRightsStats,
|
|
22
|
+
AccessRightsStatsProps
|
|
23
|
+
} from './Stats/AccessRightsStats';
|
|
24
|
+
|
|
25
|
+
export type AccessRightsFormProps = {
|
|
26
|
+
labels: AccessRightsFormLabels;
|
|
27
|
+
onCancel?: AccessRightsFormActionsProps['onCancel'];
|
|
28
|
+
resourceLink: string;
|
|
29
|
+
} & Pick<
|
|
30
|
+
AccessRightsFormProviderProps,
|
|
31
|
+
'initialValues' | 'onSubmit' | 'options' | 'loadingStatus'
|
|
32
|
+
>;
|
|
33
|
+
|
|
34
|
+
export type AccessRightsFormLabels = {
|
|
35
|
+
actions: AccessRightsFormActionsProps['labels'];
|
|
36
|
+
input: ContactAccessRightInputProps['labels'];
|
|
37
|
+
list: ContactAccessRightsListProps['labels'];
|
|
38
|
+
stats: AccessRightsStatsProps['labels'];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const AccessRightsForm = ({
|
|
42
|
+
labels,
|
|
43
|
+
onCancel,
|
|
44
|
+
resourceLink,
|
|
45
|
+
...providerProps
|
|
46
|
+
}: AccessRightsFormProps): ReactElement => {
|
|
47
|
+
const { classes } = useStyles();
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<AccessRightsFormProvider {...providerProps}>
|
|
51
|
+
<div className={classes.accessRightsForm}>
|
|
52
|
+
<ContactAccessRightInput labels={labels.input} />
|
|
53
|
+
<span className={classes.accessRightsFormList}>
|
|
54
|
+
<ContactAccessRightsList labels={labels.list} />
|
|
55
|
+
<AccessRightsStats labels={labels.stats} />
|
|
56
|
+
</span>
|
|
57
|
+
<AccessRightsFormActions
|
|
58
|
+
labels={labels.actions}
|
|
59
|
+
resourceLink={resourceLink}
|
|
60
|
+
onCancel={onCancel}
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
</AccessRightsFormProvider>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export { AccessRightsForm };
|