@oneuptime/common 9.5.7 → 9.5.9
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/Models/DatabaseModels/Alert.ts +8 -9
- package/Models/DatabaseModels/Incident.ts +5 -5
- package/Models/DatabaseModels/IncidentTemplate.ts +4 -3
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -1
- package/Models/DatabaseModels/UserOnCallLog.ts +1 -1
- package/Models/DatabaseModels/UserPush.ts +2 -1
- package/Server/API/UserPushAPI.ts +51 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770833704656-MigrationName.ts +156 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770834237090-MigrationName.ts +119 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/UserAuthorization.ts +14 -9
- package/Server/Services/AlertEpisodeFeedService.ts +50 -0
- package/Server/Services/AlertEpisodeInternalNoteService.ts +162 -0
- package/Server/Services/AlertEpisodeMemberService.ts +7 -0
- package/Server/Services/AlertEpisodeOwnerTeamService.ts +186 -0
- package/Server/Services/AlertEpisodeOwnerUserService.ts +180 -0
- package/Server/Services/AlertEpisodeService.ts +68 -0
- package/Server/Services/AlertEpisodeStateTimelineService.ts +5 -0
- package/Server/Services/AlertService.ts +3 -0
- package/Server/Services/IncidentEpisodeFeedService.ts +50 -0
- package/Server/Services/IncidentEpisodeInternalNoteService.ts +163 -0
- package/Server/Services/IncidentEpisodeMemberService.ts +7 -0
- package/Server/Services/IncidentEpisodeOwnerTeamService.ts +189 -0
- package/Server/Services/IncidentEpisodeOwnerUserService.ts +183 -0
- package/Server/Services/IncidentEpisodePublicNoteService.ts +8 -0
- package/Server/Services/IncidentEpisodeService.ts +91 -12
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +5 -0
- package/Server/Services/IncidentService.ts +5 -0
- package/Server/Services/PushNotificationService.ts +129 -27
- package/Server/Services/UserNotificationRuleService.ts +13 -3
- package/Server/Services/UserPushService.ts +2 -1
- package/Server/Services/WorkspaceNotificationRuleService.ts +20 -0
- package/Server/Utils/PushNotificationUtil.ts +56 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +1 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +7 -6
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +1 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +7 -6
- package/Server/Utils/Workspace/Slack/Actions/Alert.ts +17 -0
- package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +27 -12
- package/Server/Utils/Workspace/Slack/Actions/Incident.ts +17 -0
- package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +86 -28
- package/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.ts +6 -6
- package/Server/Utils/Workspace/Slack/Slack.ts +49 -0
- package/Server/Utils/Workspace/WorkspaceMessages/Alert.ts +2 -1
- package/Server/Utils/Workspace/WorkspaceMessages/AlertEpisode.ts +3 -1
- package/Server/Utils/Workspace/WorkspaceMessages/Incident.ts +2 -1
- package/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.ts +3 -1
- package/Types/Permission.ts +641 -0
- package/Types/PushNotification/PushDeviceType.ts +7 -0
- package/Types/PushNotification/PushNotificationRequest.ts +3 -1
- package/UI/Components/Detail/Detail.tsx +13 -4
- package/UI/Components/Detail/Field.ts +2 -2
- package/UI/Components/Dropdown/Dropdown.tsx +38 -7
- package/UI/Components/Forms/BasicForm.tsx +35 -5
- package/UI/Components/Forms/Fields/PermissionPicker.tsx +261 -0
- package/UI/Components/Forms/Types/Field.ts +5 -3
- package/UI/Components/ModelDelete/ModelDelete.tsx +4 -1
- package/UI/Components/ModelDetail/CardModelDetail.tsx +4 -0
- package/UI/Components/ModelDetail/ModelDetail.tsx +4 -1
- package/UI/Components/Page/ModelPage.tsx +4 -1
- package/UI/Utils/Permission.ts +29 -6
- package/build/dist/Models/DatabaseModels/Alert.js +8 -8
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +5 -5
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +3 -3
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +2 -1
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Server/API/UserPushAPI.js +34 -3
- package/build/dist/Server/API/UserPushAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770833704656-MigrationName.js +63 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770833704656-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237090-MigrationName.js +46 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237090-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +10 -4
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeFeedService.js +33 -0
- package/build/dist/Server/Services/AlertEpisodeFeedService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeInternalNoteService.js +132 -0
- package/build/dist/Server/Services/AlertEpisodeInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeMemberService.js +7 -0
- package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeOwnerTeamService.js +163 -0
- package/build/dist/Server/Services/AlertEpisodeOwnerTeamService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeOwnerUserService.js +156 -0
- package/build/dist/Server/Services/AlertEpisodeOwnerUserService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeService.js +53 -0
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +4 -0
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +3 -5
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeFeedService.js +33 -0
- package/build/dist/Server/Services/IncidentEpisodeFeedService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeInternalNoteService.js +132 -0
- package/build/dist/Server/Services/IncidentEpisodeInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +7 -0
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeOwnerTeamService.js +163 -0
- package/build/dist/Server/Services/IncidentEpisodeOwnerTeamService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeOwnerUserService.js +156 -0
- package/build/dist/Server/Services/IncidentEpisodeOwnerUserService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +8 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +72 -10
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +4 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +5 -5
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +77 -21
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +12 -9
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +2 -1
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +16 -0
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Utils/PushNotificationUtil.js +32 -8
- package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +7 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +7 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +16 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +25 -9
- package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +16 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +71 -25
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.js +6 -6
- package/build/dist/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +40 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Alert.js +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/AlertEpisode.js +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Incident.js +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.js +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceMessages/IncidentEpisode.js.map +1 -1
- package/build/dist/Types/Permission.js +637 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/PushNotification/PushDeviceType.js +8 -0
- package/build/dist/Types/PushNotification/PushDeviceType.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +7 -1
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +17 -2
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +17 -3
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/PermissionPicker.js +129 -0
- package/build/dist/UI/Components/Forms/Fields/PermissionPicker.js.map +1 -0
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js +2 -1
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js +2 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
- package/build/dist/UI/Components/Page/ModelPage.js +2 -1
- package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
- package/build/dist/UI/Utils/Permission.js +17 -4
- package/build/dist/UI/Utils/Permission.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import PushDeviceType from "./PushDeviceType";
|
|
2
|
+
|
|
1
3
|
interface PushNotificationRequest {
|
|
2
4
|
devices: Array<{
|
|
3
5
|
token: string;
|
|
@@ -19,7 +21,7 @@ interface PushNotificationRequest {
|
|
|
19
21
|
clickAction?: string;
|
|
20
22
|
url?: string;
|
|
21
23
|
};
|
|
22
|
-
deviceType:
|
|
24
|
+
deviceType: PushDeviceType;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export default PushNotificationRequest;
|
|
@@ -4,7 +4,7 @@ import CodeBlock from "../CodeBlock/CodeBlock";
|
|
|
4
4
|
import ColorViewer from "../ColorViewer/ColorViewer";
|
|
5
5
|
import CopyableButton from "../CopyableButton/CopyableButton";
|
|
6
6
|
import DictionaryOfStringsViewer from "../Dictionary/DictionaryOfStingsViewer";
|
|
7
|
-
import { DropdownOption } from "../Dropdown/Dropdown";
|
|
7
|
+
import { DropdownOption, DropdownOptionGroup } from "../Dropdown/Dropdown";
|
|
8
8
|
import HiddenText from "../HiddenText/HiddenText";
|
|
9
9
|
import MarkdownViewer from "../Markdown.tsx/LazyMarkdownViewer";
|
|
10
10
|
import ObjectIDView from "../ObjectID/ObjectIDView";
|
|
@@ -72,13 +72,13 @@ const Detail: DetailFunction = <T extends GenericObject>(
|
|
|
72
72
|
|
|
73
73
|
type GetDropdownViewerFunction = (
|
|
74
74
|
data: string,
|
|
75
|
-
options: Array<DropdownOption>,
|
|
75
|
+
options: Array<DropdownOption | DropdownOptionGroup>,
|
|
76
76
|
placeholder: string,
|
|
77
77
|
) => ReactElement;
|
|
78
78
|
|
|
79
79
|
const getDropdownViewer: GetDropdownViewerFunction = (
|
|
80
80
|
data: string,
|
|
81
|
-
options: Array<DropdownOption>,
|
|
81
|
+
options: Array<DropdownOption | DropdownOptionGroup>,
|
|
82
82
|
placeholder: string,
|
|
83
83
|
): ReactElement => {
|
|
84
84
|
if (!options) {
|
|
@@ -87,7 +87,16 @@ const Detail: DetailFunction = <T extends GenericObject>(
|
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
const
|
|
90
|
+
const flatOptions: Array<DropdownOption> = options.flatMap(
|
|
91
|
+
(item: DropdownOption | DropdownOptionGroup) => {
|
|
92
|
+
if ("options" in item && Array.isArray(item.options)) {
|
|
93
|
+
return item.options;
|
|
94
|
+
}
|
|
95
|
+
return [item as DropdownOption];
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const selectedOption: DropdownOption | undefined = flatOptions.find(
|
|
91
100
|
(i: DropdownOption) => {
|
|
92
101
|
return i.value === data;
|
|
93
102
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import AlignItem from "../../Types/AlignItem";
|
|
2
|
-
import { DropdownOption } from "../Dropdown/Dropdown";
|
|
2
|
+
import { DropdownOption, DropdownOptionGroup } from "../Dropdown/Dropdown";
|
|
3
3
|
import FieldType from "../Types/FieldType";
|
|
4
4
|
import { Size } from "./FieldLabel";
|
|
5
5
|
import Route from "../../../Types/API/Route";
|
|
@@ -18,7 +18,7 @@ export interface FieldBase<T> {
|
|
|
18
18
|
description?: string | ReactElement;
|
|
19
19
|
fieldTitleSize?: Size | undefined;
|
|
20
20
|
fieldType?: FieldType;
|
|
21
|
-
dropdownOptions?: Array<DropdownOption> | undefined;
|
|
21
|
+
dropdownOptions?: Array<DropdownOption | DropdownOptionGroup> | undefined;
|
|
22
22
|
colSpan?: number | undefined;
|
|
23
23
|
alignItem?: AlignItem | undefined;
|
|
24
24
|
contentClassName?: string | undefined;
|
|
@@ -35,8 +35,13 @@ export interface DropdownOption {
|
|
|
35
35
|
color?: Color;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export interface
|
|
38
|
+
export interface DropdownOptionGroup {
|
|
39
|
+
label: string;
|
|
39
40
|
options: Array<DropdownOption>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ComponentProps {
|
|
44
|
+
options: Array<DropdownOption | DropdownOptionGroup>;
|
|
40
45
|
initialValue?: undefined | DropdownOption | Array<DropdownOption>;
|
|
41
46
|
onClick?: undefined | (() => void);
|
|
42
47
|
placeholder?: undefined | string;
|
|
@@ -61,6 +66,25 @@ const Dropdown: FunctionComponent<ComponentProps> = (
|
|
|
61
66
|
const uniqueId: string = useId();
|
|
62
67
|
const errorId: string = `dropdown-error-${uniqueId}`;
|
|
63
68
|
|
|
69
|
+
const isDropdownOptionGroup: (
|
|
70
|
+
item: DropdownOption | DropdownOptionGroup,
|
|
71
|
+
) => item is DropdownOptionGroup = (
|
|
72
|
+
item: DropdownOption | DropdownOptionGroup,
|
|
73
|
+
): item is DropdownOptionGroup => {
|
|
74
|
+
return (
|
|
75
|
+
"options" in item && Array.isArray((item as DropdownOptionGroup).options)
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const flatOptions: Array<DropdownOption> = props.options.flatMap(
|
|
80
|
+
(item: DropdownOption | DropdownOptionGroup) => {
|
|
81
|
+
if (isDropdownOptionGroup(item)) {
|
|
82
|
+
return item.options;
|
|
83
|
+
}
|
|
84
|
+
return [item];
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
|
|
64
88
|
type GetDropdownOptionFromValueFunctionProps =
|
|
65
89
|
| undefined
|
|
66
90
|
| DropdownValue
|
|
@@ -103,13 +127,14 @@ const Dropdown: FunctionComponent<ComponentProps> = (
|
|
|
103
127
|
!Array.isArray(item) &&
|
|
104
128
|
(typeof item === "string" || typeof item === "number")
|
|
105
129
|
) {
|
|
106
|
-
const option: DropdownOption | undefined
|
|
107
|
-
|
|
130
|
+
const option: DropdownOption | undefined = flatOptions.find(
|
|
131
|
+
(option: DropdownOption) => {
|
|
108
132
|
return option.value === item;
|
|
109
|
-
}
|
|
133
|
+
},
|
|
134
|
+
);
|
|
110
135
|
|
|
111
136
|
if (option) {
|
|
112
|
-
options.push(option
|
|
137
|
+
options.push(option);
|
|
113
138
|
}
|
|
114
139
|
}
|
|
115
140
|
}
|
|
@@ -121,9 +146,9 @@ const Dropdown: FunctionComponent<ComponentProps> = (
|
|
|
121
146
|
!Array.isArray(value) &&
|
|
122
147
|
(typeof value === "string" || typeof value === "number")
|
|
123
148
|
) {
|
|
124
|
-
return
|
|
149
|
+
return flatOptions.find((option: DropdownOption) => {
|
|
125
150
|
return option.value === value;
|
|
126
|
-
})
|
|
151
|
+
});
|
|
127
152
|
}
|
|
128
153
|
|
|
129
154
|
return value as DropdownOption | Array<DropdownOption>;
|
|
@@ -569,6 +594,12 @@ const Dropdown: FunctionComponent<ComponentProps> = (
|
|
|
569
594
|
|
|
570
595
|
return "px-3 py-2 text-sm text-gray-700";
|
|
571
596
|
},
|
|
597
|
+
group: () => {
|
|
598
|
+
return "py-1";
|
|
599
|
+
},
|
|
600
|
+
groupHeading: () => {
|
|
601
|
+
return "px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500";
|
|
602
|
+
},
|
|
572
603
|
noOptionsMessage: () => {
|
|
573
604
|
return "px-3 py-2 text-sm text-gray-500";
|
|
574
605
|
},
|
|
@@ -4,7 +4,11 @@ import Alert, { AlertType } from "../Alerts/Alert";
|
|
|
4
4
|
import Button, { ButtonStyleType } from "../Button/Button";
|
|
5
5
|
import ButtonTypes from "../Button/ButtonTypes";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
DropdownOption,
|
|
9
|
+
DropdownOptionGroup,
|
|
10
|
+
DropdownValue,
|
|
11
|
+
} from "../Dropdown/Dropdown";
|
|
8
12
|
import ErrorMessage from "../ErrorMessage/ErrorMessage";
|
|
9
13
|
import FormField from "./Fields/FormField";
|
|
10
14
|
import FormSummary from "./FormSummary";
|
|
@@ -301,7 +305,7 @@ const BasicForm: ForwardRefExoticComponent<any> = forwardRef(
|
|
|
301
305
|
setIsDropdownOptionsLoading(true);
|
|
302
306
|
// If a dropdown has fetch optiosn then we need to fetch them
|
|
303
307
|
try {
|
|
304
|
-
const options: Array<DropdownOption> =
|
|
308
|
+
const options: Array<DropdownOption | DropdownOptionGroup> =
|
|
305
309
|
await item.fetchDropdownOptions(refCurrentValue.current);
|
|
306
310
|
item.dropdownOptions = options;
|
|
307
311
|
} catch (err) {
|
|
@@ -501,8 +505,21 @@ const BasicForm: ForwardRefExoticComponent<any> = forwardRef(
|
|
|
501
505
|
field.fieldType === FormFieldSchemaType.Dropdown &&
|
|
502
506
|
(values as any)[fieldName]
|
|
503
507
|
) {
|
|
508
|
+
const flatDropdownOptions: Array<DropdownOption> =
|
|
509
|
+
field.dropdownOptions?.flatMap(
|
|
510
|
+
(item: DropdownOption | DropdownOptionGroup) => {
|
|
511
|
+
if (
|
|
512
|
+
"options" in item &&
|
|
513
|
+
Array.isArray((item as DropdownOptionGroup).options)
|
|
514
|
+
) {
|
|
515
|
+
return (item as DropdownOptionGroup).options;
|
|
516
|
+
}
|
|
517
|
+
return [item as DropdownOption];
|
|
518
|
+
},
|
|
519
|
+
) || [];
|
|
520
|
+
|
|
504
521
|
const dropdownOption: DropdownOption | undefined =
|
|
505
|
-
|
|
522
|
+
flatDropdownOptions.find((option: DropdownOption) => {
|
|
506
523
|
let valueToCompare: DropdownValue = (values as any)[fieldName];
|
|
507
524
|
|
|
508
525
|
if ((valueToCompare as any) instanceof ObjectID) {
|
|
@@ -519,8 +536,21 @@ const BasicForm: ForwardRefExoticComponent<any> = forwardRef(
|
|
|
519
536
|
field.fieldType === FormFieldSchemaType.MultiSelectDropdown &&
|
|
520
537
|
(values as any)[fieldName]
|
|
521
538
|
) {
|
|
539
|
+
const flatDropdownOptions: Array<DropdownOption> =
|
|
540
|
+
field.dropdownOptions?.flatMap(
|
|
541
|
+
(item: DropdownOption | DropdownOptionGroup) => {
|
|
542
|
+
if (
|
|
543
|
+
"options" in item &&
|
|
544
|
+
Array.isArray((item as DropdownOptionGroup).options)
|
|
545
|
+
) {
|
|
546
|
+
return (item as DropdownOptionGroup).options;
|
|
547
|
+
}
|
|
548
|
+
return [item as DropdownOption];
|
|
549
|
+
},
|
|
550
|
+
) || [];
|
|
551
|
+
|
|
522
552
|
const dropdownOptions: Array<DropdownOption> =
|
|
523
|
-
|
|
553
|
+
flatDropdownOptions.filter((option: DropdownOption) => {
|
|
524
554
|
let valueToCompare: Array<DropdownValue> = [
|
|
525
555
|
...(values as any)[fieldName],
|
|
526
556
|
];
|
|
@@ -534,7 +564,7 @@ const BasicForm: ForwardRefExoticComponent<any> = forwardRef(
|
|
|
534
564
|
});
|
|
535
565
|
|
|
536
566
|
return valueToCompare.includes(option.value);
|
|
537
|
-
})
|
|
567
|
+
});
|
|
538
568
|
|
|
539
569
|
(values as any)[fieldName] = dropdownOptions.map(
|
|
540
570
|
(option: DropdownOption) => {
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import Permission, {
|
|
2
|
+
PermissionGroup,
|
|
3
|
+
PermissionHelper,
|
|
4
|
+
PermissionProps,
|
|
5
|
+
} from "../../../../Types/Permission";
|
|
6
|
+
import React, {
|
|
7
|
+
FunctionComponent,
|
|
8
|
+
ReactElement,
|
|
9
|
+
useEffect,
|
|
10
|
+
useMemo,
|
|
11
|
+
useState,
|
|
12
|
+
} from "react";
|
|
13
|
+
|
|
14
|
+
export interface ComponentProps {
|
|
15
|
+
onChange: (value: Permission | null) => void;
|
|
16
|
+
initialValue?: Permission | undefined;
|
|
17
|
+
placeholder?: string | undefined;
|
|
18
|
+
onFocus?: (() => void) | undefined;
|
|
19
|
+
tabIndex?: number | undefined;
|
|
20
|
+
onBlur?: (() => void) | undefined;
|
|
21
|
+
error?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PermissionPicker: FunctionComponent<ComponentProps> = (
|
|
25
|
+
props: ComponentProps,
|
|
26
|
+
): ReactElement => {
|
|
27
|
+
const [selectedPermission, setSelectedPermission] =
|
|
28
|
+
useState<Permission | null>(props.initialValue || null);
|
|
29
|
+
const [activeGroup, setActiveGroup] = useState<PermissionGroup | null>(null);
|
|
30
|
+
const [searchQuery, setSearchQuery] = useState<string>("");
|
|
31
|
+
|
|
32
|
+
const allPermissions: Array<PermissionProps> = useMemo(() => {
|
|
33
|
+
return PermissionHelper.getTenantPermissionProps();
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
const groupedPermissions: Map<
|
|
37
|
+
PermissionGroup,
|
|
38
|
+
Array<PermissionProps>
|
|
39
|
+
> = useMemo(() => {
|
|
40
|
+
const map: Map<PermissionGroup, Array<PermissionProps>> = new Map();
|
|
41
|
+
for (const perm of allPermissions) {
|
|
42
|
+
if (!map.has(perm.group)) {
|
|
43
|
+
map.set(perm.group, []);
|
|
44
|
+
}
|
|
45
|
+
map.get(perm.group)!.push(perm);
|
|
46
|
+
}
|
|
47
|
+
return map;
|
|
48
|
+
}, [allPermissions]);
|
|
49
|
+
|
|
50
|
+
const groups: Array<PermissionGroup> = useMemo(() => {
|
|
51
|
+
return Array.from(groupedPermissions.keys());
|
|
52
|
+
}, [groupedPermissions]);
|
|
53
|
+
|
|
54
|
+
// Auto-select the group for the initial value
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (props.initialValue && !activeGroup) {
|
|
57
|
+
const match: PermissionProps | undefined = allPermissions.find(
|
|
58
|
+
(p: PermissionProps) => {
|
|
59
|
+
return p.permission === props.initialValue;
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
if (match) {
|
|
63
|
+
setActiveGroup(match.group);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}, [props.initialValue, allPermissions, activeGroup]);
|
|
67
|
+
|
|
68
|
+
// Default to first group if none selected
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!activeGroup && groups.length > 0 && !props.initialValue) {
|
|
71
|
+
setActiveGroup(groups[0]!);
|
|
72
|
+
}
|
|
73
|
+
}, [activeGroup, groups, props.initialValue]);
|
|
74
|
+
|
|
75
|
+
const isSearching: boolean = searchQuery.trim().length > 0;
|
|
76
|
+
const lowerQuery: string = searchQuery.toLowerCase();
|
|
77
|
+
|
|
78
|
+
const filteredPermissions: Array<PermissionProps> = useMemo(() => {
|
|
79
|
+
if (!isSearching) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
return allPermissions.filter((p: PermissionProps) => {
|
|
83
|
+
return (
|
|
84
|
+
p.title.toLowerCase().includes(lowerQuery) ||
|
|
85
|
+
p.description.toLowerCase().includes(lowerQuery)
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
}, [allPermissions, isSearching, lowerQuery]);
|
|
89
|
+
|
|
90
|
+
const searchMatchCountByGroup: Map<PermissionGroup, number> = useMemo(() => {
|
|
91
|
+
const counts: Map<PermissionGroup, number> = new Map();
|
|
92
|
+
if (!isSearching) {
|
|
93
|
+
return counts;
|
|
94
|
+
}
|
|
95
|
+
for (const perm of filteredPermissions) {
|
|
96
|
+
counts.set(perm.group, (counts.get(perm.group) || 0) + 1);
|
|
97
|
+
}
|
|
98
|
+
return counts;
|
|
99
|
+
}, [filteredPermissions, isSearching]);
|
|
100
|
+
|
|
101
|
+
const visiblePermissions: Array<PermissionProps> = useMemo(() => {
|
|
102
|
+
if (isSearching) {
|
|
103
|
+
if (activeGroup) {
|
|
104
|
+
return filteredPermissions.filter((p: PermissionProps) => {
|
|
105
|
+
return p.group === activeGroup;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return filteredPermissions;
|
|
109
|
+
}
|
|
110
|
+
if (!activeGroup) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
return groupedPermissions.get(activeGroup) || [];
|
|
114
|
+
}, [isSearching, activeGroup, filteredPermissions, groupedPermissions]);
|
|
115
|
+
|
|
116
|
+
type HandlePermissionClickFunction = (perm: PermissionProps) => void;
|
|
117
|
+
|
|
118
|
+
const handlePermissionClick: HandlePermissionClickFunction = (
|
|
119
|
+
perm: PermissionProps,
|
|
120
|
+
): void => {
|
|
121
|
+
setSelectedPermission(perm.permission);
|
|
122
|
+
props.onChange(perm.permission);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
type GetGroupCountFunction = (group: PermissionGroup) => number;
|
|
126
|
+
|
|
127
|
+
const getGroupCount: GetGroupCountFunction = (
|
|
128
|
+
group: PermissionGroup,
|
|
129
|
+
): number => {
|
|
130
|
+
if (isSearching) {
|
|
131
|
+
return searchMatchCountByGroup.get(group) || 0;
|
|
132
|
+
}
|
|
133
|
+
return groupedPermissions.get(group)?.length || 0;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div
|
|
138
|
+
tabIndex={props.tabIndex}
|
|
139
|
+
onFocus={props.onFocus}
|
|
140
|
+
onBlur={props.onBlur}
|
|
141
|
+
>
|
|
142
|
+
<div
|
|
143
|
+
className={`border rounded-md overflow-hidden ${
|
|
144
|
+
props.error ? "border-red-400" : "border-gray-300"
|
|
145
|
+
}`}
|
|
146
|
+
style={{ height: "400px" }}
|
|
147
|
+
>
|
|
148
|
+
{/* Search bar */}
|
|
149
|
+
<div className="border-b border-gray-200 p-2">
|
|
150
|
+
<input
|
|
151
|
+
type="text"
|
|
152
|
+
className="w-full rounded-md border border-gray-300 px-3 py-1.5 text-sm focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 focus:outline-none"
|
|
153
|
+
placeholder={props.placeholder || "Search permissions..."}
|
|
154
|
+
value={searchQuery}
|
|
155
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
156
|
+
setSearchQuery(e.target.value);
|
|
157
|
+
}}
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<div className="flex" style={{ height: "calc(100% - 45px)" }}>
|
|
162
|
+
{/* Left sidebar - groups */}
|
|
163
|
+
<div
|
|
164
|
+
className="border-r border-gray-200 overflow-y-auto bg-gray-50 flex-shrink-0"
|
|
165
|
+
style={{ width: "200px" }}
|
|
166
|
+
>
|
|
167
|
+
{groups.map((group: PermissionGroup) => {
|
|
168
|
+
const count: number = getGroupCount(group);
|
|
169
|
+
const isActive: boolean = activeGroup === group;
|
|
170
|
+
const isDimmed: boolean = isSearching && count === 0;
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<button
|
|
174
|
+
key={group}
|
|
175
|
+
type="button"
|
|
176
|
+
className={`w-full text-left px-3 py-2 text-sm flex items-center justify-between border-b border-gray-100 transition-colors ${
|
|
177
|
+
isActive
|
|
178
|
+
? "bg-indigo-50 text-indigo-700 font-medium"
|
|
179
|
+
: isDimmed
|
|
180
|
+
? "text-gray-300 cursor-default"
|
|
181
|
+
: "text-gray-700 hover:bg-gray-100"
|
|
182
|
+
}`}
|
|
183
|
+
onClick={() => {
|
|
184
|
+
if (!isDimmed) {
|
|
185
|
+
setActiveGroup(group);
|
|
186
|
+
}
|
|
187
|
+
}}
|
|
188
|
+
>
|
|
189
|
+
<span className="truncate">{group}</span>
|
|
190
|
+
<span
|
|
191
|
+
className={`ml-1 text-xs flex-shrink-0 ${
|
|
192
|
+
isActive ? "text-indigo-500" : "text-gray-400"
|
|
193
|
+
}`}
|
|
194
|
+
>
|
|
195
|
+
{count}
|
|
196
|
+
</span>
|
|
197
|
+
</button>
|
|
198
|
+
);
|
|
199
|
+
})}
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
{/* Right panel - permissions */}
|
|
203
|
+
<div className="flex-1 overflow-y-auto">
|
|
204
|
+
{visiblePermissions.length === 0 && (
|
|
205
|
+
<div className="flex items-center justify-center h-full text-gray-400 text-sm">
|
|
206
|
+
{isSearching
|
|
207
|
+
? "No permissions match your search."
|
|
208
|
+
: "Select a group to view permissions."}
|
|
209
|
+
</div>
|
|
210
|
+
)}
|
|
211
|
+
|
|
212
|
+
{visiblePermissions.map((perm: PermissionProps) => {
|
|
213
|
+
const isSelected: boolean =
|
|
214
|
+
selectedPermission === perm.permission;
|
|
215
|
+
return (
|
|
216
|
+
<button
|
|
217
|
+
key={perm.permission}
|
|
218
|
+
type="button"
|
|
219
|
+
className={`w-full text-left px-4 py-2.5 border-b border-gray-100 transition-colors ${
|
|
220
|
+
isSelected
|
|
221
|
+
? "bg-indigo-50 border-l-2 border-l-indigo-500"
|
|
222
|
+
: "hover:bg-gray-50"
|
|
223
|
+
}`}
|
|
224
|
+
onClick={() => {
|
|
225
|
+
handlePermissionClick(perm);
|
|
226
|
+
}}
|
|
227
|
+
>
|
|
228
|
+
<div className="flex items-start gap-2">
|
|
229
|
+
<div className="flex-1 min-w-0">
|
|
230
|
+
<div
|
|
231
|
+
className={`text-sm font-medium ${
|
|
232
|
+
isSelected ? "text-indigo-700" : "text-gray-900"
|
|
233
|
+
}`}
|
|
234
|
+
>
|
|
235
|
+
{perm.title}
|
|
236
|
+
</div>
|
|
237
|
+
<div className="text-xs text-gray-500 mt-0.5">
|
|
238
|
+
{perm.description}
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
{isSearching && (
|
|
242
|
+
<span className="inline-flex items-center rounded bg-gray-100 px-1.5 py-0.5 text-xs text-gray-600 flex-shrink-0">
|
|
243
|
+
{perm.group}
|
|
244
|
+
</span>
|
|
245
|
+
)}
|
|
246
|
+
</div>
|
|
247
|
+
</button>
|
|
248
|
+
);
|
|
249
|
+
})}
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
{props.error && (
|
|
255
|
+
<p className="mt-1 text-sm text-red-400">{props.error}</p>
|
|
256
|
+
)}
|
|
257
|
+
</div>
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export default PermissionPicker;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
CheckboxCategory,
|
|
5
5
|
} from "../../CategoryCheckbox/CategoryCheckboxTypes";
|
|
6
6
|
import { CardSelectOption } from "../../CardSelect/CardSelect";
|
|
7
|
-
import { DropdownOption } from "../../Dropdown/Dropdown";
|
|
7
|
+
import { DropdownOption, DropdownOptionGroup } from "../../Dropdown/Dropdown";
|
|
8
8
|
import { RadioButton } from "../../RadioButtons/GroupRadioButtons";
|
|
9
9
|
import FormFieldSchemaType from "./FormFieldSchemaType";
|
|
10
10
|
import FormValues from "./FormValues";
|
|
@@ -50,10 +50,12 @@ export default interface Field<TEntity> {
|
|
|
50
50
|
disabled?: boolean;
|
|
51
51
|
stepId?: string | undefined;
|
|
52
52
|
required?: boolean | ((item: FormValues<TEntity>) => boolean) | undefined;
|
|
53
|
-
dropdownOptions?: Array<DropdownOption> | undefined;
|
|
53
|
+
dropdownOptions?: Array<DropdownOption | DropdownOptionGroup> | undefined;
|
|
54
54
|
cardSelectOptions?: Array<CardSelectOption> | undefined;
|
|
55
55
|
fetchDropdownOptions?:
|
|
56
|
-
| ((
|
|
56
|
+
| ((
|
|
57
|
+
item: FormValues<TEntity>,
|
|
58
|
+
) => Promise<Array<DropdownOption | DropdownOptionGroup>>)
|
|
57
59
|
| undefined;
|
|
58
60
|
showHorizontalRuleBelow?: boolean | undefined;
|
|
59
61
|
showHorizontalRuleAbove?: boolean | undefined;
|
|
@@ -12,6 +12,7 @@ import React, { ReactElement, useState } from "react";
|
|
|
12
12
|
export interface ComponentProps<TBaseModel extends BaseModel> {
|
|
13
13
|
modelType: { new (): TBaseModel };
|
|
14
14
|
modelId: ObjectID;
|
|
15
|
+
modelAPI?: typeof ModelAPI | undefined;
|
|
15
16
|
onDeleteSuccess: () => void;
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -29,7 +30,9 @@ const ModelDelete: <TBaseModel extends BaseModel>(
|
|
|
29
30
|
const deleteItem: PromiseVoidFunction = async (): Promise<void> => {
|
|
30
31
|
setIsLoading(true);
|
|
31
32
|
try {
|
|
32
|
-
|
|
33
|
+
const modelAPI: typeof ModelAPI = props.modelAPI || ModelAPI;
|
|
34
|
+
|
|
35
|
+
await modelAPI.deleteItem<TBaseModel>({
|
|
33
36
|
modelType: props.modelType,
|
|
34
37
|
id: props.modelId,
|
|
35
38
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ModelAPI from "../../Utils/ModelAPI/ModelAPI";
|
|
1
2
|
import PermissionUtil from "../../Utils/Permission";
|
|
2
3
|
import User from "../../Utils/User";
|
|
3
4
|
import Navigation from "../../Utils/Navigation";
|
|
@@ -33,6 +34,7 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
|
|
33
34
|
formFields?: undefined | Fields<TBaseModel>;
|
|
34
35
|
className?: string | undefined;
|
|
35
36
|
name: string;
|
|
37
|
+
modelAPI?: typeof ModelAPI | undefined;
|
|
36
38
|
createEditModalWidth?: ModalWidth | undefined;
|
|
37
39
|
refresher?: boolean;
|
|
38
40
|
createOrUpdateApiUrl?: URL | undefined;
|
|
@@ -130,6 +132,7 @@ const CardModelDetail: <TBaseModel extends BaseModel>(
|
|
|
130
132
|
<ModelDetail
|
|
131
133
|
refresher={refresher}
|
|
132
134
|
{...props.modelDetailProps}
|
|
135
|
+
modelAPI={props.modelAPI}
|
|
133
136
|
onItemLoaded={(item: TBaseModel) => {
|
|
134
137
|
setItem(item);
|
|
135
138
|
if (props.modelDetailProps.onItemLoaded) {
|
|
@@ -144,6 +147,7 @@ const CardModelDetail: <TBaseModel extends BaseModel>(
|
|
|
144
147
|
<ModelFormModal<TBaseModel>
|
|
145
148
|
title={`Edit ${model.singularName}`}
|
|
146
149
|
modalWidth={props.createEditModalWidth}
|
|
150
|
+
modelAPI={props.modelAPI}
|
|
147
151
|
onClose={() => {
|
|
148
152
|
setShowModal(false);
|
|
149
153
|
}}
|
|
@@ -28,6 +28,7 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
|
|
28
28
|
fields: Array<Field<TBaseModel>>;
|
|
29
29
|
onLoadingChange?: undefined | ((isLoading: boolean) => void);
|
|
30
30
|
modelId: ObjectID;
|
|
31
|
+
modelAPI?: typeof ModelAPI | undefined;
|
|
31
32
|
onError?: ((error: string) => void) | undefined;
|
|
32
33
|
onItemLoaded?: (item: TBaseModel) => void | undefined;
|
|
33
34
|
refresher?: undefined | boolean;
|
|
@@ -179,7 +180,9 @@ const ModelDetail: <TBaseModel extends BaseModel>(
|
|
|
179
180
|
setOnBeforeFetchData(model);
|
|
180
181
|
}
|
|
181
182
|
|
|
182
|
-
const
|
|
183
|
+
const modelAPI: typeof ModelAPI = props.modelAPI || ModelAPI;
|
|
184
|
+
|
|
185
|
+
const item: TBaseModel | null = await modelAPI.getItem({
|
|
183
186
|
modelType: props.modelType,
|
|
184
187
|
id: props.modelId,
|
|
185
188
|
select: {
|
|
@@ -20,6 +20,7 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
|
|
20
20
|
modelType: { new (): TBaseModel };
|
|
21
21
|
modelId: ObjectID;
|
|
22
22
|
modelNameField: string;
|
|
23
|
+
modelAPI?: typeof ModelAPI | undefined;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const ModelPage: <TBaseModel extends BaseModel>(
|
|
@@ -54,7 +55,9 @@ const ModelPage: <TBaseModel extends BaseModel>(
|
|
|
54
55
|
} as Select<TBaseModel>;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
const
|
|
58
|
+
const modelAPI: typeof ModelAPI = props.modelAPI || ModelAPI;
|
|
59
|
+
|
|
60
|
+
const item: TBaseModel | null = await modelAPI.getItem({
|
|
58
61
|
modelType: props.modelType,
|
|
59
62
|
id: props.modelId,
|
|
60
63
|
select: select as Select<TBaseModel>,
|
package/UI/Utils/Permission.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
DropdownOption,
|
|
3
|
+
DropdownOptionGroup,
|
|
4
|
+
} from "../Components/Dropdown/Dropdown";
|
|
2
5
|
import LocalStorage from "./LocalStorage";
|
|
3
6
|
import { JSONObject } from "../../Types/JSON";
|
|
4
7
|
import Permission, {
|
|
8
|
+
PermissionGroup,
|
|
5
9
|
PermissionHelper,
|
|
6
10
|
PermissionProps,
|
|
7
11
|
UserGlobalAccessPermission,
|
|
@@ -60,16 +64,35 @@ export default class PermissionUtil {
|
|
|
60
64
|
return userTenantAccessPermission;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
public static projectPermissionsAsDropdownOptions(): Array<
|
|
67
|
+
public static projectPermissionsAsDropdownOptions(): Array<DropdownOptionGroup> {
|
|
64
68
|
const permissions: Array<PermissionProps> =
|
|
65
69
|
PermissionHelper.getTenantPermissionProps();
|
|
66
70
|
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
const groupMap: Map<PermissionGroup, Array<DropdownOption>> = new Map();
|
|
72
|
+
|
|
73
|
+
for (const permissionProp of permissions) {
|
|
74
|
+
const group: PermissionGroup = permissionProp.group;
|
|
75
|
+
|
|
76
|
+
if (!groupMap.has(group)) {
|
|
77
|
+
groupMap.set(group, []);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
groupMap.get(group)!.push({
|
|
69
81
|
value: permissionProp.permission,
|
|
70
82
|
label: permissionProp.title,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const groups: Array<DropdownOptionGroup> = [];
|
|
87
|
+
|
|
88
|
+
for (const [group, options] of groupMap) {
|
|
89
|
+
groups.push({
|
|
90
|
+
label: group,
|
|
91
|
+
options,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return groups;
|
|
73
96
|
}
|
|
74
97
|
|
|
75
98
|
public static setGlobalPermissions(
|