@kuadrant/kuadrant-backstage-plugin-frontend 0.0.2-dev-844db4d → 0.0.2-dev-d4f65ae
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/README.md +4 -4
- package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js +69 -22
- package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js.map +1 -1
- package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js +93 -53
- package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js.map +1 -1
- package/dist/components/ConfirmDeleteDialog/ConfirmDeleteDialog.esm.js +65 -0
- package/dist/components/ConfirmDeleteDialog/ConfirmDeleteDialog.esm.js.map +1 -0
- package/dist/components/CreateAPIProductDialog/CreateAPIProductDialog.esm.js +28 -15
- package/dist/components/CreateAPIProductDialog/CreateAPIProductDialog.esm.js.map +1 -1
- package/dist/components/EditAPIKeyRequestDialog/EditAPIKeyRequestDialog.esm.js +6 -4
- package/dist/components/EditAPIKeyRequestDialog/EditAPIKeyRequestDialog.esm.js.map +1 -1
- package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js +26 -14
- package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js.map +1 -1
- package/dist/components/KuadrantPage/KuadrantPage.esm.js +51 -22
- package/dist/components/KuadrantPage/KuadrantPage.esm.js.map +1 -1
- package/dist/components/MyApiKeysCard/MyApiKeysCard.esm.js +50 -12
- package/dist/components/MyApiKeysCard/MyApiKeysCard.esm.js.map +1 -1
- package/dist-scalprum/{internal.plugin-kuadrant.58f9452dd6d088d4816c.js → internal.plugin-kuadrant.810174392d0016a5a388.js} +2 -2
- package/dist-scalprum/{internal.plugin-kuadrant.58f9452dd6d088d4816c.js.map → internal.plugin-kuadrant.810174392d0016a5a388.js.map} +1 -1
- package/dist-scalprum/plugin-manifest.json +2 -2
- package/dist-scalprum/static/4306.4587e025.chunk.js +2 -0
- package/dist-scalprum/static/4306.4587e025.chunk.js.map +1 -0
- package/dist-scalprum/static/6281.b000c79f.chunk.js +2 -0
- package/dist-scalprum/static/6281.b000c79f.chunk.js.map +1 -0
- package/dist-scalprum/static/7632.daef27e4.chunk.js +2 -0
- package/dist-scalprum/static/7632.daef27e4.chunk.js.map +1 -0
- package/dist-scalprum/static/8441.62394cfd.chunk.js +2 -0
- package/dist-scalprum/static/8441.62394cfd.chunk.js.map +1 -0
- package/dist-scalprum/static/{exposed-PluginRoot.9522bcfa.chunk.js → exposed-PluginRoot.0ac7fe4b.chunk.js} +2 -2
- package/dist-scalprum/static/{exposed-PluginRoot.9522bcfa.chunk.js.map → exposed-PluginRoot.0ac7fe4b.chunk.js.map} +1 -1
- package/package.json +1 -1
- package/dist-scalprum/static/4306.bc289e67.chunk.js +0 -2
- package/dist-scalprum/static/4306.bc289e67.chunk.js.map +0 -1
- package/dist-scalprum/static/6281.e7e36f6a.chunk.js +0 -2
- package/dist-scalprum/static/6281.e7e36f6a.chunk.js.map +0 -1
- package/dist-scalprum/static/7632.4bb9bd69.chunk.js +0 -2
- package/dist-scalprum/static/7632.4bb9bd69.chunk.js.map +0 -1
- package/dist-scalprum/static/9555.59e77806.chunk.js +0 -2
- package/dist-scalprum/static/9555.59e77806.chunk.js.map +0 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { useApi, configApiRef, fetchApiRef, identityApiRef } from '@backstage/core-plugin-api';
|
|
2
|
+
import { useApi, configApiRef, fetchApiRef, identityApiRef, alertApiRef } from '@backstage/core-plugin-api';
|
|
3
3
|
import { useAsync } from 'react-use';
|
|
4
4
|
import { Progress, ResponseErrorPanel, InfoCard, Table } from '@backstage/core-components';
|
|
5
5
|
import { kuadrantApiKeyRequestUpdateAllPermission, kuadrantApiKeyRequestUpdateOwnPermission } from '../../permissions.esm.js';
|
|
6
6
|
import { useKuadrantPermission } from '../../utils/permissions.esm.js';
|
|
7
|
-
import { Box, Typography, Tabs, Tab, Button, Accordion, AccordionSummary, Chip, AccordionDetails, Dialog, DialogTitle, DialogContent, TextField, DialogActions, Tooltip } from '@material-ui/core';
|
|
7
|
+
import { Box, Typography, Tabs, Tab, Button, Accordion, AccordionSummary, Chip, AccordionDetails, Dialog, DialogTitle, DialogContent, TextField, DialogActions, CircularProgress, Tooltip } from '@material-ui/core';
|
|
8
8
|
import CheckCircleIcon from '@material-ui/icons/CheckCircle';
|
|
9
9
|
import CancelIcon from '@material-ui/icons/Cancel';
|
|
10
10
|
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
|
|
11
11
|
|
|
12
|
-
const ApprovalDialog = ({ open, request, action, onClose, onConfirm }) => {
|
|
12
|
+
const ApprovalDialog = ({ open, request, action, processing, onClose, onConfirm }) => {
|
|
13
13
|
const [comment, setComment] = useState("");
|
|
14
14
|
useEffect(() => {
|
|
15
15
|
if (!open) {
|
|
@@ -18,9 +18,10 @@ const ApprovalDialog = ({ open, request, action, onClose, onConfirm }) => {
|
|
|
18
18
|
}, [open]);
|
|
19
19
|
const handleConfirm = () => {
|
|
20
20
|
onConfirm(comment);
|
|
21
|
-
onClose();
|
|
22
21
|
};
|
|
23
|
-
|
|
22
|
+
const actionLabel = action === "approve" ? "Approve" : "Reject";
|
|
23
|
+
const processingLabel = action === "approve" ? "Approving..." : "Rejecting...";
|
|
24
|
+
return /* @__PURE__ */ React.createElement(Dialog, { open, onClose: processing ? undefined : onClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React.createElement(DialogTitle, null, actionLabel, " API Key Request"), /* @__PURE__ */ React.createElement(DialogContent, null, request && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", null, "User:"), " ", request.spec.requestedBy.userId), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", null, "API:"), " ", request.spec.apiName), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", null, "Tier:"), " ", request.spec.planTier), /* @__PURE__ */ React.createElement(Box, { mb: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", component: "span", style: { fontWeight: "bold" } }, "Use Case:"), " ", /* @__PURE__ */ React.createElement(Typography, { variant: "body2", component: "span", style: { whiteSpace: "pre-wrap" } }, request.spec.useCase || "-")), /* @__PURE__ */ React.createElement(
|
|
24
25
|
TextField,
|
|
25
26
|
{
|
|
26
27
|
label: "Comment (optional)",
|
|
@@ -29,19 +30,22 @@ const ApprovalDialog = ({ open, request, action, onClose, onConfirm }) => {
|
|
|
29
30
|
fullWidth: true,
|
|
30
31
|
margin: "normal",
|
|
31
32
|
value: comment,
|
|
32
|
-
onChange: (e) => setComment(e.target.value)
|
|
33
|
+
onChange: (e) => setComment(e.target.value),
|
|
34
|
+
disabled: processing
|
|
33
35
|
}
|
|
34
|
-
))), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: onClose }, "Cancel"), /* @__PURE__ */ React.createElement(
|
|
36
|
+
))), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: onClose, disabled: processing }, "Cancel"), /* @__PURE__ */ React.createElement(
|
|
35
37
|
Button,
|
|
36
38
|
{
|
|
37
39
|
onClick: handleConfirm,
|
|
38
40
|
color: action === "approve" ? "primary" : "secondary",
|
|
39
|
-
variant: "contained"
|
|
41
|
+
variant: "contained",
|
|
42
|
+
disabled: processing,
|
|
43
|
+
startIcon: processing ? /* @__PURE__ */ React.createElement(CircularProgress, { size: 16, color: "inherit" }) : undefined
|
|
40
44
|
},
|
|
41
|
-
|
|
45
|
+
processing ? processingLabel : actionLabel
|
|
42
46
|
)));
|
|
43
47
|
};
|
|
44
|
-
const BulkActionDialog = ({ open, requests, action, onClose, onConfirm }) => {
|
|
48
|
+
const BulkActionDialog = ({ open, requests, action, processing, onClose, onConfirm }) => {
|
|
45
49
|
const [comment, setComment] = useState("");
|
|
46
50
|
useEffect(() => {
|
|
47
51
|
if (!open) {
|
|
@@ -50,10 +54,11 @@ const BulkActionDialog = ({ open, requests, action, onClose, onConfirm }) => {
|
|
|
50
54
|
}, [open]);
|
|
51
55
|
const handleConfirm = () => {
|
|
52
56
|
onConfirm(comment);
|
|
53
|
-
onClose();
|
|
54
57
|
};
|
|
55
58
|
const isApprove = action === "approve";
|
|
56
|
-
|
|
59
|
+
const actionLabel = isApprove ? "Approve All" : "Reject All";
|
|
60
|
+
const processingLabel = isApprove ? "Approving..." : "Rejecting...";
|
|
61
|
+
return /* @__PURE__ */ React.createElement(Dialog, { open, onClose: processing ? undefined : onClose, maxWidth: "md", fullWidth: true }, /* @__PURE__ */ React.createElement(DialogTitle, null, isApprove ? "Approve" : "Reject", " ", requests.length, " API Key Requests"), /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", paragraph: true }, "You are about to ", isApprove ? "approve" : "reject", " the following requests:"), /* @__PURE__ */ React.createElement(Box, { mb: 2, maxHeight: 200, overflow: "auto" }, requests.map((request) => /* @__PURE__ */ React.createElement(Box, { key: `${request.metadata.namespace}/${request.metadata.name}`, mb: 1, p: 1, bgcolor: "background.default" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React.createElement("strong", null, request.spec.requestedBy.userId), " - ", request.spec.apiName, " (", request.spec.planTier, ")")))), /* @__PURE__ */ React.createElement(
|
|
57
62
|
TextField,
|
|
58
63
|
{
|
|
59
64
|
label: "Comment (optional)",
|
|
@@ -62,22 +67,26 @@ const BulkActionDialog = ({ open, requests, action, onClose, onConfirm }) => {
|
|
|
62
67
|
margin: "normal",
|
|
63
68
|
value: comment,
|
|
64
69
|
onChange: (e) => setComment(e.target.value),
|
|
65
|
-
helperText: `This comment will be applied to all ${isApprove ? "approved" : "rejected"} requests
|
|
70
|
+
helperText: `This comment will be applied to all ${isApprove ? "approved" : "rejected"} requests`,
|
|
71
|
+
disabled: processing
|
|
66
72
|
}
|
|
67
|
-
)), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: onClose }, "Cancel"), /* @__PURE__ */ React.createElement(
|
|
73
|
+
)), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: onClose, disabled: processing }, "Cancel"), /* @__PURE__ */ React.createElement(
|
|
68
74
|
Button,
|
|
69
75
|
{
|
|
70
76
|
onClick: handleConfirm,
|
|
71
77
|
color: isApprove ? "primary" : "secondary",
|
|
72
|
-
variant: "contained"
|
|
78
|
+
variant: "contained",
|
|
79
|
+
disabled: processing,
|
|
80
|
+
startIcon: processing ? /* @__PURE__ */ React.createElement(CircularProgress, { size: 16, color: "inherit" }) : undefined
|
|
73
81
|
},
|
|
74
|
-
|
|
82
|
+
processing ? processingLabel : actionLabel
|
|
75
83
|
)));
|
|
76
84
|
};
|
|
77
85
|
const ApprovalQueueCard = () => {
|
|
78
86
|
const config = useApi(configApiRef);
|
|
79
87
|
const fetchApi = useApi(fetchApiRef);
|
|
80
88
|
const identityApi = useApi(identityApiRef);
|
|
89
|
+
const alertApi = useApi(alertApiRef);
|
|
81
90
|
const backendUrl = config.getString("backend.baseUrl");
|
|
82
91
|
const [refresh, setRefresh] = useState(0);
|
|
83
92
|
const [selectedTab, setSelectedTab] = useState(0);
|
|
@@ -85,12 +94,14 @@ const ApprovalQueueCard = () => {
|
|
|
85
94
|
const [dialogState, setDialogState] = useState({
|
|
86
95
|
open: false,
|
|
87
96
|
request: null,
|
|
88
|
-
action: "approve"
|
|
97
|
+
action: "approve",
|
|
98
|
+
processing: false
|
|
89
99
|
});
|
|
90
100
|
const [bulkDialogState, setBulkDialogState] = useState({
|
|
91
101
|
open: false,
|
|
92
102
|
requests: [],
|
|
93
|
-
action: "approve"
|
|
103
|
+
action: "approve",
|
|
104
|
+
processing: false
|
|
94
105
|
});
|
|
95
106
|
const {
|
|
96
107
|
allowed: canUpdateAllRequests,
|
|
@@ -102,29 +113,39 @@ const ApprovalQueueCard = () => {
|
|
|
102
113
|
loading: updateOwnPermissionLoading,
|
|
103
114
|
error: updateOwnPermissionError
|
|
104
115
|
} = useKuadrantPermission(kuadrantApiKeyRequestUpdateOwnPermission);
|
|
105
|
-
const canUpdateRequests = canUpdateAllRequests || canUpdateOwnRequests;
|
|
106
116
|
const updatePermissionLoading = updateAllPermissionLoading || updateOwnPermissionLoading;
|
|
107
117
|
const updatePermissionError = updateAllPermissionError || updateOwnPermissionError;
|
|
108
118
|
const { value, loading, error } = useAsync(async () => {
|
|
109
119
|
const identity = await identityApi.getBackstageIdentity();
|
|
110
120
|
const reviewedBy = identity.userEntityRef;
|
|
111
121
|
console.log("ApprovalQueueCard: fetching all requests from", `${backendUrl}/api/kuadrant/requests`);
|
|
112
|
-
const
|
|
113
|
-
`${backendUrl}/api/kuadrant/requests`
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
122
|
+
const [requestsResponse, apiProductsResponse] = await Promise.all([
|
|
123
|
+
fetchApi.fetch(`${backendUrl}/api/kuadrant/requests`),
|
|
124
|
+
fetchApi.fetch(`${backendUrl}/api/kuadrant/apiproducts`)
|
|
125
|
+
]);
|
|
126
|
+
if (!requestsResponse.ok) {
|
|
127
|
+
console.log("ApprovalQueueCard: failed to fetch requests, status:", requestsResponse.status);
|
|
128
|
+
return { pending: [], approved: [], rejected: [], reviewedBy, ownedApiProducts: /* @__PURE__ */ new Set() };
|
|
118
129
|
}
|
|
119
|
-
const contentType =
|
|
130
|
+
const contentType = requestsResponse.headers.get("content-type");
|
|
120
131
|
if (!contentType || !contentType.includes("application/json")) {
|
|
121
132
|
console.log("ApprovalQueueCard: received non-json response");
|
|
122
|
-
return { pending: [], approved: [], rejected: [], reviewedBy };
|
|
133
|
+
return { pending: [], approved: [], rejected: [], reviewedBy, ownedApiProducts: /* @__PURE__ */ new Set() };
|
|
123
134
|
}
|
|
124
|
-
const data = await
|
|
135
|
+
const data = await requestsResponse.json();
|
|
125
136
|
const allRequests = data.items || [];
|
|
137
|
+
const ownedApiProducts = /* @__PURE__ */ new Set();
|
|
138
|
+
if (apiProductsResponse.ok) {
|
|
139
|
+
const apiProductsData = await apiProductsResponse.json();
|
|
140
|
+
for (const product of apiProductsData.items || []) {
|
|
141
|
+
const owner = product.metadata?.annotations?.["backstage.io/owner"];
|
|
142
|
+
if (owner === reviewedBy) {
|
|
143
|
+
ownedApiProducts.add(`${product.metadata.namespace}/${product.metadata.name}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
126
147
|
console.log("ApprovalQueueCard: received", allRequests.length, "total requests");
|
|
127
|
-
console.log("ApprovalQueueCard:
|
|
148
|
+
console.log("ApprovalQueueCard: user owns", ownedApiProducts.size, "api products");
|
|
128
149
|
const pending2 = allRequests.filter((r) => {
|
|
129
150
|
const phase = r.status?.phase || "Pending";
|
|
130
151
|
return phase === "Pending";
|
|
@@ -142,16 +163,17 @@ const ApprovalQueueCard = () => {
|
|
|
142
163
|
approved: approved2.length,
|
|
143
164
|
rejected: rejected2.length
|
|
144
165
|
});
|
|
145
|
-
return { pending: pending2, approved: approved2, rejected: rejected2, reviewedBy };
|
|
166
|
+
return { pending: pending2, approved: approved2, rejected: rejected2, reviewedBy, ownedApiProducts };
|
|
146
167
|
}, [backendUrl, fetchApi, identityApi, refresh]);
|
|
147
168
|
const handleApprove = (request) => {
|
|
148
|
-
setDialogState({ open: true, request, action: "approve" });
|
|
169
|
+
setDialogState({ open: true, request, action: "approve", processing: false });
|
|
149
170
|
};
|
|
150
171
|
const handleReject = (request) => {
|
|
151
|
-
setDialogState({ open: true, request, action: "reject" });
|
|
172
|
+
setDialogState({ open: true, request, action: "reject", processing: false });
|
|
152
173
|
};
|
|
153
174
|
const handleConfirm = async (comment) => {
|
|
154
175
|
if (!dialogState.request || !value) return;
|
|
176
|
+
setDialogState((prev) => ({ ...prev, processing: true }));
|
|
155
177
|
const endpoint = dialogState.action === "approve" ? `${backendUrl}/api/kuadrant/requests/${dialogState.request.metadata.namespace}/${dialogState.request.metadata.name}/approve` : `${backendUrl}/api/kuadrant/requests/${dialogState.request.metadata.namespace}/${dialogState.request.metadata.name}/reject`;
|
|
156
178
|
try {
|
|
157
179
|
const response = await fetchApi.fetch(endpoint, {
|
|
@@ -165,22 +187,27 @@ const ApprovalQueueCard = () => {
|
|
|
165
187
|
if (!response.ok) {
|
|
166
188
|
throw new Error(`failed to ${dialogState.action} request`);
|
|
167
189
|
}
|
|
168
|
-
setDialogState({ open: false, request: null, action: "approve" });
|
|
190
|
+
setDialogState({ open: false, request: null, action: "approve", processing: false });
|
|
169
191
|
setRefresh((r) => r + 1);
|
|
192
|
+
const action = dialogState.action === "approve" ? "approved" : "rejected";
|
|
193
|
+
alertApi.post({ message: `Request ${action}`, severity: "success", display: "transient" });
|
|
170
194
|
} catch (err) {
|
|
171
195
|
console.error(`error ${dialogState.action}ing request:`, err);
|
|
196
|
+
setDialogState((prev) => ({ ...prev, processing: false }));
|
|
197
|
+
alertApi.post({ message: `Failed to ${dialogState.action} request`, severity: "error", display: "transient" });
|
|
172
198
|
}
|
|
173
199
|
};
|
|
174
200
|
const handleBulkApprove = () => {
|
|
175
201
|
if (selectedRequests.length === 0) return;
|
|
176
|
-
setBulkDialogState({ open: true, requests: selectedRequests, action: "approve" });
|
|
202
|
+
setBulkDialogState({ open: true, requests: selectedRequests, action: "approve", processing: false });
|
|
177
203
|
};
|
|
178
204
|
const handleBulkReject = () => {
|
|
179
205
|
if (selectedRequests.length === 0) return;
|
|
180
|
-
setBulkDialogState({ open: true, requests: selectedRequests, action: "reject" });
|
|
206
|
+
setBulkDialogState({ open: true, requests: selectedRequests, action: "reject", processing: false });
|
|
181
207
|
};
|
|
182
208
|
const handleBulkConfirm = async (comment) => {
|
|
183
209
|
if (!value || bulkDialogState.requests.length === 0) return;
|
|
210
|
+
setBulkDialogState((prev) => ({ ...prev, processing: true }));
|
|
184
211
|
const isApprove = bulkDialogState.action === "approve";
|
|
185
212
|
const endpoint = isApprove ? `${backendUrl}/api/kuadrant/requests/bulk-approve` : `${backendUrl}/api/kuadrant/requests/bulk-reject`;
|
|
186
213
|
try {
|
|
@@ -199,11 +226,16 @@ const ApprovalQueueCard = () => {
|
|
|
199
226
|
if (!response.ok) {
|
|
200
227
|
throw new Error(`failed to bulk ${bulkDialogState.action} requests`);
|
|
201
228
|
}
|
|
202
|
-
|
|
229
|
+
const count = bulkDialogState.requests.length;
|
|
230
|
+
const action = isApprove ? "approved" : "rejected";
|
|
231
|
+
setBulkDialogState({ open: false, requests: [], action: "approve", processing: false });
|
|
203
232
|
setSelectedRequests([]);
|
|
204
233
|
setRefresh((r) => r + 1);
|
|
234
|
+
alertApi.post({ message: `${count} requests ${action}`, severity: "success", display: "transient" });
|
|
205
235
|
} catch (err) {
|
|
206
236
|
console.error(`error bulk ${bulkDialogState.action}ing requests:`, err);
|
|
237
|
+
setBulkDialogState((prev) => ({ ...prev, processing: false }));
|
|
238
|
+
alertApi.post({ message: `Failed to bulk ${bulkDialogState.action} requests`, severity: "error", display: "transient" });
|
|
207
239
|
}
|
|
208
240
|
};
|
|
209
241
|
if (loading || updatePermissionLoading) {
|
|
@@ -250,7 +282,7 @@ const ApprovalQueueCard = () => {
|
|
|
250
282
|
render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.spec.apiNamespace)
|
|
251
283
|
},
|
|
252
284
|
{
|
|
253
|
-
title: "
|
|
285
|
+
title: "Tier",
|
|
254
286
|
field: "spec.planTier",
|
|
255
287
|
render: (row) => /* @__PURE__ */ React.createElement(
|
|
256
288
|
Chip,
|
|
@@ -291,7 +323,10 @@ const ApprovalQueueCard = () => {
|
|
|
291
323
|
title: "Actions",
|
|
292
324
|
filtering: false,
|
|
293
325
|
render: (row) => {
|
|
294
|
-
|
|
326
|
+
const apiProductKey = `${row.spec.apiNamespace}/${row.spec.apiName}`;
|
|
327
|
+
const ownsApiProduct = value?.ownedApiProducts?.has(apiProductKey) ?? false;
|
|
328
|
+
const canUpdate = canUpdateAllRequests || canUpdateOwnRequests && ownsApiProduct;
|
|
329
|
+
if (!canUpdate) return null;
|
|
295
330
|
return /* @__PURE__ */ React.createElement(Box, { display: "flex", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(
|
|
296
331
|
Button,
|
|
297
332
|
{
|
|
@@ -338,7 +373,7 @@ const ApprovalQueueCard = () => {
|
|
|
338
373
|
render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.spec.apiNamespace)
|
|
339
374
|
},
|
|
340
375
|
{
|
|
341
|
-
title: "
|
|
376
|
+
title: "Tier",
|
|
342
377
|
field: "spec.planTier",
|
|
343
378
|
render: (row) => /* @__PURE__ */ React.createElement(
|
|
344
379
|
Chip,
|
|
@@ -401,7 +436,7 @@ const ApprovalQueueCard = () => {
|
|
|
401
436
|
render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.spec.apiNamespace)
|
|
402
437
|
},
|
|
403
438
|
{
|
|
404
|
-
title: "
|
|
439
|
+
title: "Tier",
|
|
405
440
|
field: "spec.planTier",
|
|
406
441
|
render: (row) => /* @__PURE__ */ React.createElement(
|
|
407
442
|
Chip,
|
|
@@ -475,16 +510,16 @@ const ApprovalQueueCard = () => {
|
|
|
475
510
|
const groupByApiProduct = (requests) => {
|
|
476
511
|
const grouped = /* @__PURE__ */ new Map();
|
|
477
512
|
requests.forEach((request) => {
|
|
478
|
-
const
|
|
479
|
-
if (!grouped.has(
|
|
480
|
-
grouped.set(
|
|
513
|
+
const key = `${request.spec.apiNamespace}/${request.spec.apiName}`;
|
|
514
|
+
if (!grouped.has(key)) {
|
|
515
|
+
grouped.set(key, []);
|
|
481
516
|
}
|
|
482
|
-
grouped.get(
|
|
517
|
+
grouped.get(key).push(request);
|
|
483
518
|
});
|
|
484
519
|
return grouped;
|
|
485
520
|
};
|
|
486
521
|
const groupedData = groupByApiProduct(tabData.data);
|
|
487
|
-
const
|
|
522
|
+
const apiProductKeys = Array.from(groupedData.keys()).sort();
|
|
488
523
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
489
524
|
InfoCard,
|
|
490
525
|
{
|
|
@@ -506,7 +541,7 @@ const ApprovalQueueCard = () => {
|
|
|
506
541
|
/* @__PURE__ */ React.createElement(Tab, { label: `Pending (${pending.length})` }),
|
|
507
542
|
/* @__PURE__ */ React.createElement(Tab, { label: `Rejected (${rejected.length})` })
|
|
508
543
|
)),
|
|
509
|
-
|
|
544
|
+
selectedTab === 1 && selectedRequests.length > 0 && /* @__PURE__ */ React.createElement(Box, { mb: 2, display: "flex", alignItems: "center", justifyContent: "space-between", p: 2, bgcolor: "background.default" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, selectedRequests.length, " request", selectedRequests.length !== 1 ? "s" : "", " selected"), /* @__PURE__ */ React.createElement(Box, { display: "flex", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(
|
|
510
545
|
Button,
|
|
511
546
|
{
|
|
512
547
|
size: "small",
|
|
@@ -527,9 +562,12 @@ const ApprovalQueueCard = () => {
|
|
|
527
562
|
},
|
|
528
563
|
"Reject Selected"
|
|
529
564
|
))),
|
|
530
|
-
tabData.data.length === 0 ? /* @__PURE__ */ React.createElement(Box, { p: 3, textAlign: "center" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", color: "textSecondary" }, selectedTab === 0 && "No approved requests.", selectedTab === 1 && "No pending requests.", selectedTab === 2 && "No rejected requests.")) : /* @__PURE__ */ React.createElement(Box, null,
|
|
531
|
-
const requests = groupedData.get(
|
|
532
|
-
|
|
565
|
+
tabData.data.length === 0 ? /* @__PURE__ */ React.createElement(Box, { p: 3, textAlign: "center" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", color: "textSecondary" }, selectedTab === 0 && "No approved requests.", selectedTab === 1 && "No pending requests.", selectedTab === 2 && "No rejected requests.")) : /* @__PURE__ */ React.createElement(Box, null, apiProductKeys.map((apiProductKey) => {
|
|
566
|
+
const requests = groupedData.get(apiProductKey) || [];
|
|
567
|
+
const displayName = requests[0]?.spec.apiName || apiProductKey;
|
|
568
|
+
const ownsThisApiProduct = value?.ownedApiProducts?.has(apiProductKey) ?? false;
|
|
569
|
+
const canSelectRows = canUpdateAllRequests || canUpdateOwnRequests && ownsThisApiProduct;
|
|
570
|
+
return /* @__PURE__ */ React.createElement(Accordion, { key: apiProductKey, defaultExpanded: apiProductKeys.length === 1 }, /* @__PURE__ */ React.createElement(AccordionSummary, { expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null) }, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, displayName), /* @__PURE__ */ React.createElement(
|
|
533
571
|
Chip,
|
|
534
572
|
{
|
|
535
573
|
label: `${requests.length} request${requests.length !== 1 ? "s" : ""}`,
|
|
@@ -541,7 +579,7 @@ const ApprovalQueueCard = () => {
|
|
|
541
579
|
Table,
|
|
542
580
|
{
|
|
543
581
|
options: {
|
|
544
|
-
selection:
|
|
582
|
+
selection: canSelectRows && tabData.showSelection,
|
|
545
583
|
paging: requests.length > 5,
|
|
546
584
|
pageSize: 20,
|
|
547
585
|
search: true,
|
|
@@ -555,7 +593,7 @@ const ApprovalQueueCard = () => {
|
|
|
555
593
|
columns: tabData.columns,
|
|
556
594
|
onSelectionChange: (rows) => {
|
|
557
595
|
const otherSelections = selectedRequests.filter(
|
|
558
|
-
(r) => r.spec.apiName !==
|
|
596
|
+
(r) => `${r.spec.apiNamespace}/${r.spec.apiName}` !== apiProductKey
|
|
559
597
|
);
|
|
560
598
|
setSelectedRequests([...otherSelections, ...rows]);
|
|
561
599
|
}
|
|
@@ -568,7 +606,8 @@ const ApprovalQueueCard = () => {
|
|
|
568
606
|
open: dialogState.open,
|
|
569
607
|
request: dialogState.request,
|
|
570
608
|
action: dialogState.action,
|
|
571
|
-
|
|
609
|
+
processing: dialogState.processing,
|
|
610
|
+
onClose: () => setDialogState({ open: false, request: null, action: "approve", processing: false }),
|
|
572
611
|
onConfirm: handleConfirm
|
|
573
612
|
}
|
|
574
613
|
), /* @__PURE__ */ React.createElement(
|
|
@@ -577,7 +616,8 @@ const ApprovalQueueCard = () => {
|
|
|
577
616
|
open: bulkDialogState.open,
|
|
578
617
|
requests: bulkDialogState.requests,
|
|
579
618
|
action: bulkDialogState.action,
|
|
580
|
-
|
|
619
|
+
processing: bulkDialogState.processing,
|
|
620
|
+
onClose: () => setBulkDialogState({ open: false, requests: [], action: "approve", processing: false }),
|
|
581
621
|
onConfirm: handleBulkConfirm
|
|
582
622
|
}
|
|
583
623
|
));
|