@kuadrant/kuadrant-backstage-plugin-frontend 0.0.2-dev-7d09bfa → 0.0.2-dev-724df72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/index.d.ts +1 -4
  2. package/dist/index.esm.js +1 -2
  3. package/dist/index.esm.js.map +1 -1
  4. package/dist/plugin.esm.js +1 -9
  5. package/dist/plugin.esm.js.map +1 -1
  6. package/dist-scalprum/internal.plugin-kuadrant.1ec7b1dc71663b4b9a17.js +2 -0
  7. package/dist-scalprum/internal.plugin-kuadrant.1ec7b1dc71663b4b9a17.js.map +1 -0
  8. package/dist-scalprum/plugin-manifest.json +2 -2
  9. package/dist-scalprum/static/1085.d17e689a.chunk.js +3 -0
  10. package/dist-scalprum/static/1085.d17e689a.chunk.js.map +1 -0
  11. package/dist-scalprum/static/1645.db086648.chunk.js +3 -0
  12. package/dist-scalprum/static/{5568.5dbce633.chunk.js.LICENSE.txt → 1645.db086648.chunk.js.LICENSE.txt} +0 -9
  13. package/dist-scalprum/static/1645.db086648.chunk.js.map +1 -0
  14. package/dist-scalprum/static/2233.bf0c10d7.chunk.js +3 -0
  15. package/dist-scalprum/static/2233.bf0c10d7.chunk.js.LICENSE.txt +9 -0
  16. package/dist-scalprum/static/2233.bf0c10d7.chunk.js.map +1 -0
  17. package/dist-scalprum/static/2628.20156dc1.chunk.js +3 -0
  18. package/dist-scalprum/static/2628.20156dc1.chunk.js.map +1 -0
  19. package/dist-scalprum/static/2946.a35243f1.chunk.js +3 -0
  20. package/dist-scalprum/static/{9510.e4112e19.chunk.js.LICENSE.txt → 2946.a35243f1.chunk.js.LICENSE.txt} +10 -0
  21. package/dist-scalprum/static/2946.a35243f1.chunk.js.map +1 -0
  22. package/dist-scalprum/static/2952.b3c7e41c.chunk.js +2 -0
  23. package/dist-scalprum/static/2952.b3c7e41c.chunk.js.map +1 -0
  24. package/dist-scalprum/static/{2967.8d9c1b1f.chunk.js → 2967.6a1db3ad.chunk.js} +2 -2
  25. package/dist-scalprum/static/2967.6a1db3ad.chunk.js.map +1 -0
  26. package/dist-scalprum/static/3091.9a74ea5f.chunk.js +2 -0
  27. package/dist-scalprum/static/{65.0e01be7c.chunk.js.map → 3091.9a74ea5f.chunk.js.map} +1 -1
  28. package/dist-scalprum/static/{327.cd6b3fee.chunk.js → 327.a9a99299.chunk.js} +2 -2
  29. package/dist-scalprum/static/327.a9a99299.chunk.js.map +1 -0
  30. package/dist-scalprum/static/3504.1c2f2e54.chunk.js +3 -0
  31. package/dist-scalprum/static/3504.1c2f2e54.chunk.js.LICENSE.txt +9 -0
  32. package/dist-scalprum/static/3504.1c2f2e54.chunk.js.map +1 -0
  33. package/dist-scalprum/static/3657.882640ca.chunk.js +3 -0
  34. package/dist-scalprum/static/3657.882640ca.chunk.js.LICENSE.txt +8 -0
  35. package/dist-scalprum/static/3657.882640ca.chunk.js.map +1 -0
  36. package/dist-scalprum/static/3954.12dd8006.chunk.js +3 -0
  37. package/dist-scalprum/static/3954.12dd8006.chunk.js.LICENSE.txt +9 -0
  38. package/dist-scalprum/static/3954.12dd8006.chunk.js.map +1 -0
  39. package/dist-scalprum/static/3984.647ef00c.chunk.js +2 -0
  40. package/dist-scalprum/static/3984.647ef00c.chunk.js.map +1 -0
  41. package/dist-scalprum/static/4026.d652a7a1.chunk.js +2 -0
  42. package/dist-scalprum/static/4026.d652a7a1.chunk.js.map +1 -0
  43. package/dist-scalprum/static/{6386.6386.ce38cef3.css → 4093.4093.3597955d.css} +1 -1
  44. package/dist-scalprum/static/4093.4093.3597955d.css.map +1 -0
  45. package/dist-scalprum/static/4093.ee6f0452.chunk.js +3 -0
  46. package/dist-scalprum/static/4093.ee6f0452.chunk.js.map +1 -0
  47. package/dist-scalprum/static/4382.a7363415.chunk.js +3 -0
  48. package/dist-scalprum/static/4382.a7363415.chunk.js.map +1 -0
  49. package/dist-scalprum/static/4614.23f2a85b.chunk.js +2 -0
  50. package/dist-scalprum/static/4614.23f2a85b.chunk.js.map +1 -0
  51. package/dist-scalprum/static/{4682.3c7098a8.chunk.js → 4682.bd2dfccd.chunk.js} +2 -2
  52. package/dist-scalprum/static/4682.bd2dfccd.chunk.js.map +1 -0
  53. package/dist-scalprum/static/4935.e14465ff.chunk.js +3 -0
  54. package/dist-scalprum/static/4935.e14465ff.chunk.js.LICENSE.txt +9 -0
  55. package/dist-scalprum/static/4935.e14465ff.chunk.js.map +1 -0
  56. package/dist-scalprum/static/5010.0cd6c959.chunk.js +3 -0
  57. package/dist-scalprum/static/5010.0cd6c959.chunk.js.map +1 -0
  58. package/dist-scalprum/static/{5203.43732b3f.chunk.js → 5203.539ed3ec.chunk.js} +2 -2
  59. package/dist-scalprum/static/{5203.43732b3f.chunk.js.map → 5203.539ed3ec.chunk.js.map} +1 -1
  60. package/dist-scalprum/static/{5453.b3ee2392.chunk.js → 5453.ecdee66d.chunk.js} +2 -2
  61. package/dist-scalprum/static/{5453.b3ee2392.chunk.js.map → 5453.ecdee66d.chunk.js.map} +1 -1
  62. package/dist-scalprum/static/6038.63d1f832.chunk.js +3 -0
  63. package/dist-scalprum/static/6038.63d1f832.chunk.js.LICENSE.txt +8 -0
  64. package/dist-scalprum/static/6038.63d1f832.chunk.js.map +1 -0
  65. package/dist-scalprum/static/635.e030b18a.chunk.js +3 -0
  66. package/dist-scalprum/static/635.e030b18a.chunk.js.LICENSE.txt +9 -0
  67. package/dist-scalprum/static/635.e030b18a.chunk.js.map +1 -0
  68. package/dist-scalprum/static/6371.d45f37cc.chunk.js +2 -0
  69. package/dist-scalprum/static/6371.d45f37cc.chunk.js.map +1 -0
  70. package/dist-scalprum/static/{6800.8ec3a2eb.chunk.js → 6800.6e2a08fe.chunk.js} +2 -2
  71. package/dist-scalprum/static/{6800.8ec3a2eb.chunk.js.map → 6800.6e2a08fe.chunk.js.map} +1 -1
  72. package/dist-scalprum/static/{6840.6cc88a16.chunk.js → 6840.3bd67cf4.chunk.js} +2 -2
  73. package/dist-scalprum/static/{6840.6cc88a16.chunk.js.map → 6840.3bd67cf4.chunk.js.map} +1 -1
  74. package/dist-scalprum/static/7164.5dc4bc72.chunk.js +2 -0
  75. package/dist-scalprum/static/7164.5dc4bc72.chunk.js.map +1 -0
  76. package/dist-scalprum/static/{7791.55db7365.chunk.js → 7791.8135d440.chunk.js} +2 -2
  77. package/dist-scalprum/static/{7791.55db7365.chunk.js.map → 7791.8135d440.chunk.js.map} +1 -1
  78. package/dist-scalprum/static/8083.4763f88b.chunk.js +206 -0
  79. package/dist-scalprum/static/8083.4763f88b.chunk.js.LICENSE.txt +9 -0
  80. package/dist-scalprum/static/8083.4763f88b.chunk.js.map +1 -0
  81. package/dist-scalprum/static/{8172.e89bbae7.chunk.js → 8172.cbe1f2c4.chunk.js} +2 -2
  82. package/dist-scalprum/static/8172.cbe1f2c4.chunk.js.map +1 -0
  83. package/dist-scalprum/static/{8799.4ea4639c.chunk.js → 8799.83d049f3.chunk.js} +2 -2
  84. package/dist-scalprum/static/{8799.4ea4639c.chunk.js.map → 8799.83d049f3.chunk.js.map} +1 -1
  85. package/dist-scalprum/static/{3587.490690d6.chunk.js → 9316.d9f2fe18.chunk.js} +2 -2
  86. package/dist-scalprum/static/{3587.490690d6.chunk.js.map → 9316.d9f2fe18.chunk.js.map} +1 -1
  87. package/dist-scalprum/static/{5568.5dbce633.chunk.js → 9655.db0c82b2.chunk.js} +3 -3
  88. package/dist-scalprum/static/9655.db0c82b2.chunk.js.LICENSE.txt +8 -0
  89. package/dist-scalprum/static/9655.db0c82b2.chunk.js.map +1 -0
  90. package/dist-scalprum/static/9710.7ec34777.chunk.js +2 -0
  91. package/dist-scalprum/static/9710.7ec34777.chunk.js.map +1 -0
  92. package/dist-scalprum/static/{4302.9a59485e.chunk.js → 9738.b037daf3.chunk.js} +2 -2
  93. package/dist-scalprum/static/9738.b037daf3.chunk.js.map +1 -0
  94. package/dist-scalprum/static/exposed-PluginRoot.c3fd0cc1.chunk.js +2 -0
  95. package/dist-scalprum/static/exposed-PluginRoot.c3fd0cc1.chunk.js.map +1 -0
  96. package/dist-scalprum/static/react-syntax-highlighter_languages_highlight_http.a55e168b.chunk.js +2 -0
  97. package/dist-scalprum/static/{react-syntax-highlighter_languages_highlight_http.5859273c.chunk.js.map → react-syntax-highlighter_languages_highlight_http.a55e168b.chunk.js.map} +1 -1
  98. package/package.json +10 -11
  99. package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js +0 -761
  100. package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js.map +0 -1
  101. package/dist/components/ApprovalQueueCard/index.esm.js +0 -2
  102. package/dist/components/ApprovalQueueCard/index.esm.js.map +0 -1
  103. package/dist-scalprum/internal.plugin-kuadrant.5d9e1e73c7f21bbed39a.js +0 -2
  104. package/dist-scalprum/internal.plugin-kuadrant.5d9e1e73c7f21bbed39a.js.map +0 -1
  105. package/dist-scalprum/static/1994.19e6a1c5.chunk.js +0 -3
  106. package/dist-scalprum/static/1994.19e6a1c5.chunk.js.map +0 -1
  107. package/dist-scalprum/static/2628.0605e07f.chunk.js +0 -3
  108. package/dist-scalprum/static/2628.0605e07f.chunk.js.map +0 -1
  109. package/dist-scalprum/static/2752.df63f31c.chunk.js +0 -2
  110. package/dist-scalprum/static/2752.df63f31c.chunk.js.map +0 -1
  111. package/dist-scalprum/static/2967.8d9c1b1f.chunk.js.map +0 -1
  112. package/dist-scalprum/static/3097.582b68d3.chunk.js +0 -2
  113. package/dist-scalprum/static/3097.582b68d3.chunk.js.map +0 -1
  114. package/dist-scalprum/static/327.cd6b3fee.chunk.js.map +0 -1
  115. package/dist-scalprum/static/3584.c820a5c7.chunk.js +0 -2
  116. package/dist-scalprum/static/3584.c820a5c7.chunk.js.map +0 -1
  117. package/dist-scalprum/static/3647.67079e5f.chunk.js +0 -2
  118. package/dist-scalprum/static/3647.67079e5f.chunk.js.map +0 -1
  119. package/dist-scalprum/static/3984.7bc07774.chunk.js +0 -2
  120. package/dist-scalprum/static/3984.7bc07774.chunk.js.map +0 -1
  121. package/dist-scalprum/static/4302.9a59485e.chunk.js.map +0 -1
  122. package/dist-scalprum/static/4682.3c7098a8.chunk.js.map +0 -1
  123. package/dist-scalprum/static/5010.2228c754.chunk.js +0 -3
  124. package/dist-scalprum/static/5010.2228c754.chunk.js.map +0 -1
  125. package/dist-scalprum/static/5568.5dbce633.chunk.js.map +0 -1
  126. package/dist-scalprum/static/6272.ef31cb1c.chunk.js +0 -2
  127. package/dist-scalprum/static/6272.ef31cb1c.chunk.js.map +0 -1
  128. package/dist-scalprum/static/6371.c4899d73.chunk.js +0 -3
  129. package/dist-scalprum/static/6371.c4899d73.chunk.js.map +0 -1
  130. package/dist-scalprum/static/6386.6386.ce38cef3.css.map +0 -1
  131. package/dist-scalprum/static/6386.903891f3.chunk.js +0 -3
  132. package/dist-scalprum/static/6386.903891f3.chunk.js.map +0 -1
  133. package/dist-scalprum/static/65.0e01be7c.chunk.js +0 -2
  134. package/dist-scalprum/static/6753.76832e72.chunk.js +0 -2
  135. package/dist-scalprum/static/6753.76832e72.chunk.js.map +0 -1
  136. package/dist-scalprum/static/8172.e89bbae7.chunk.js.map +0 -1
  137. package/dist-scalprum/static/8627.111cbac9.chunk.js +0 -2
  138. package/dist-scalprum/static/8627.111cbac9.chunk.js.map +0 -1
  139. package/dist-scalprum/static/9510.e4112e19.chunk.js +0 -3
  140. package/dist-scalprum/static/9510.e4112e19.chunk.js.map +0 -1
  141. package/dist-scalprum/static/9644.7d342123.chunk.js +0 -2
  142. package/dist-scalprum/static/9644.7d342123.chunk.js.map +0 -1
  143. package/dist-scalprum/static/993.c164940e.chunk.js +0 -2
  144. package/dist-scalprum/static/993.c164940e.chunk.js.map +0 -1
  145. package/dist-scalprum/static/exposed-PluginRoot.5b6638e2.chunk.js +0 -2
  146. package/dist-scalprum/static/exposed-PluginRoot.5b6638e2.chunk.js.map +0 -1
  147. package/dist-scalprum/static/react-syntax-highlighter_languages_highlight_http.5859273c.chunk.js +0 -2
  148. /package/dist-scalprum/static/{6371.c4899d73.chunk.js.LICENSE.txt → 1085.d17e689a.chunk.js.LICENSE.txt} +0 -0
  149. /package/dist-scalprum/static/{2628.0605e07f.chunk.js.LICENSE.txt → 2628.20156dc1.chunk.js.LICENSE.txt} +0 -0
  150. /package/dist-scalprum/static/{6386.903891f3.chunk.js.LICENSE.txt → 4093.ee6f0452.chunk.js.LICENSE.txt} +0 -0
  151. /package/dist-scalprum/static/{1994.19e6a1c5.chunk.js.LICENSE.txt → 4382.a7363415.chunk.js.LICENSE.txt} +0 -0
  152. /package/dist-scalprum/static/{5010.2228c754.chunk.js.LICENSE.txt → 5010.0cd6c959.chunk.js.LICENSE.txt} +0 -0
@@ -1,761 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { useApi, configApiRef, fetchApiRef, identityApiRef, alertApiRef } from '@backstage/core-plugin-api';
3
- import { useAsync } from 'react-use';
4
- import { Progress, ResponseErrorPanel, InfoCard, Table } from '@backstage/core-components';
5
- import { kuadrantApiKeyUpdateAllPermission, kuadrantApiKeyUpdateOwnPermission } from '../../permissions.esm.js';
6
- import { useKuadrantPermission } from '../../utils/permissions.esm.js';
7
- import { Box, Typography, Tabs, Tab, Button, Accordion, AccordionSummary, Chip, AccordionDetails, Dialog, DialogTitle, DialogContent, DialogActions, CircularProgress, Tooltip } from '@material-ui/core';
8
- import CheckCircleIcon from '@material-ui/icons/CheckCircle';
9
- import CancelIcon from '@material-ui/icons/Cancel';
10
- import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
11
-
12
- const ApprovalDialog = ({
13
- open,
14
- request,
15
- action,
16
- processing,
17
- onClose,
18
- onConfirm
19
- }) => {
20
- const actionLabel = action === "approve" ? "Approve" : "Reject";
21
- const processingLabel = action === "approve" ? "Approving..." : "Rejecting...";
22
- return /* @__PURE__ */ React.createElement(
23
- Dialog,
24
- {
25
- open,
26
- onClose: processing ? undefined : onClose,
27
- maxWidth: "sm",
28
- fullWidth: true
29
- },
30
- /* @__PURE__ */ React.createElement(DialogTitle, null, actionLabel, " API Key"),
31
- /* @__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.apiProductRef?.name || "unknown"), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("strong", null, "Tier:"), " ", request.spec.planTier), /* @__PURE__ */ React.createElement(Box, { mb: 2 }, /* @__PURE__ */ React.createElement(
32
- Typography,
33
- {
34
- variant: "body2",
35
- component: "span",
36
- style: { fontWeight: "bold" }
37
- },
38
- "Use Case:"
39
- ), " ", /* @__PURE__ */ React.createElement(
40
- Typography,
41
- {
42
- variant: "body2",
43
- component: "span",
44
- style: { whiteSpace: "pre-wrap" }
45
- },
46
- request.spec.useCase || "-"
47
- )))),
48
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: onClose, disabled: processing }, "Cancel"), /* @__PURE__ */ React.createElement(
49
- Button,
50
- {
51
- onClick: onConfirm,
52
- color: action === "approve" ? "primary" : "secondary",
53
- variant: "contained",
54
- disabled: processing,
55
- startIcon: processing ? /* @__PURE__ */ React.createElement(CircularProgress, { size: 16, color: "inherit" }) : undefined
56
- },
57
- processing ? processingLabel : actionLabel
58
- ))
59
- );
60
- };
61
- const BulkActionDialog = ({
62
- open,
63
- requests,
64
- action,
65
- processing,
66
- onClose,
67
- onConfirm
68
- }) => {
69
- const isApprove = action === "approve";
70
- const actionLabel = isApprove ? "Approve All" : "Reject All";
71
- const processingLabel = isApprove ? "Approving..." : "Rejecting...";
72
- return /* @__PURE__ */ React.createElement(
73
- Dialog,
74
- {
75
- open,
76
- onClose: processing ? undefined : onClose,
77
- maxWidth: "md",
78
- fullWidth: true
79
- },
80
- /* @__PURE__ */ React.createElement(DialogTitle, null, isApprove ? "Approve" : "Reject", " ", requests.length, " API Keys"),
81
- /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", paragraph: true }, "You are about to ", isApprove ? "approve" : "reject", " the following API keys:"), /* @__PURE__ */ React.createElement(Box, { mb: 2, maxHeight: 200, overflow: "auto" }, requests.map((request) => /* @__PURE__ */ React.createElement(
82
- Box,
83
- {
84
- key: `${request.metadata.namespace}/${request.metadata.name}`,
85
- mb: 1,
86
- p: 1,
87
- bgcolor: "background.default"
88
- },
89
- /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React.createElement("strong", null, request.spec.requestedBy.userId), " -", " ", request.spec.apiProductRef?.name || "unknown", " (", request.spec.planTier, ")")
90
- )))),
91
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: onClose, disabled: processing }, "Cancel"), /* @__PURE__ */ React.createElement(
92
- Button,
93
- {
94
- onClick: onConfirm,
95
- color: isApprove ? "primary" : "secondary",
96
- variant: "contained",
97
- disabled: processing,
98
- startIcon: processing ? /* @__PURE__ */ React.createElement(CircularProgress, { size: 16, color: "inherit" }) : undefined
99
- },
100
- processing ? processingLabel : actionLabel
101
- ))
102
- );
103
- };
104
- const ApprovalQueueCard = () => {
105
- const config = useApi(configApiRef);
106
- const fetchApi = useApi(fetchApiRef);
107
- const identityApi = useApi(identityApiRef);
108
- const alertApi = useApi(alertApiRef);
109
- const backendUrl = config.getString("backend.baseUrl");
110
- const [refresh, setRefresh] = useState(0);
111
- const [selectedTab, setSelectedTab] = useState(0);
112
- const [selectedRequests, setSelectedRequests] = useState([]);
113
- const [dialogState, setDialogState] = useState({
114
- open: false,
115
- request: null,
116
- action: "approve",
117
- processing: false
118
- });
119
- const [bulkDialogState, setBulkDialogState] = useState({
120
- open: false,
121
- requests: [],
122
- action: "approve",
123
- processing: false
124
- });
125
- const {
126
- allowed: canUpdateAllRequests,
127
- loading: updateAllPermissionLoading,
128
- error: updateAllPermissionError
129
- } = useKuadrantPermission(kuadrantApiKeyUpdateAllPermission);
130
- const {
131
- allowed: canUpdateOwnRequests,
132
- loading: updateOwnPermissionLoading,
133
- error: updateOwnPermissionError
134
- } = useKuadrantPermission(kuadrantApiKeyUpdateOwnPermission);
135
- const updatePermissionLoading = updateAllPermissionLoading || updateOwnPermissionLoading;
136
- const updatePermissionError = updateAllPermissionError || updateOwnPermissionError;
137
- const { value, loading, error } = useAsync(async () => {
138
- const identity = await identityApi.getBackstageIdentity();
139
- const reviewedBy = identity.userEntityRef;
140
- console.log(
141
- "ApprovalQueueCard: fetching all requests from",
142
- `${backendUrl}/api/kuadrant/requests`
143
- );
144
- const [requestsResponse, apiProductsResponse] = await Promise.all([
145
- fetchApi.fetch(`${backendUrl}/api/kuadrant/requests`),
146
- fetchApi.fetch(`${backendUrl}/api/kuadrant/apiproducts`)
147
- ]);
148
- if (!requestsResponse.ok) {
149
- console.log(
150
- "ApprovalQueueCard: failed to fetch requests, status:",
151
- requestsResponse.status
152
- );
153
- return {
154
- pending: [],
155
- approved: [],
156
- rejected: [],
157
- reviewedBy,
158
- ownedApiProducts: /* @__PURE__ */ new Set()
159
- };
160
- }
161
- const contentType = requestsResponse.headers.get("content-type");
162
- if (!contentType?.includes("application/json")) {
163
- console.error("ApprovalQueueCard: received non-json response");
164
- alertApi.post({
165
- message: "Unexpected content-type from the server response. Please contact support.",
166
- display: "transient",
167
- severity: "warning"
168
- });
169
- return {
170
- pending: [],
171
- approved: [],
172
- rejected: [],
173
- reviewedBy,
174
- ownedApiProducts: /* @__PURE__ */ new Set()
175
- };
176
- }
177
- const data = await requestsResponse.json();
178
- const allRequests = data.items || [];
179
- const ownedApiProducts = /* @__PURE__ */ new Set();
180
- if (apiProductsResponse.ok) {
181
- const apiProductsData = await apiProductsResponse.json();
182
- for (const product of apiProductsData.items || []) {
183
- const owner = product.metadata?.annotations?.["backstage.io/owner"];
184
- if (owner === reviewedBy) {
185
- ownedApiProducts.add(
186
- `${product.metadata.namespace}/${product.metadata.name}`
187
- );
188
- }
189
- }
190
- }
191
- console.log(
192
- "ApprovalQueueCard: received",
193
- allRequests.length,
194
- "total requests"
195
- );
196
- console.log(
197
- "ApprovalQueueCard: user owns",
198
- ownedApiProducts.size,
199
- "api products"
200
- );
201
- const pending2 = allRequests.filter((r) => {
202
- const phase = r.status?.phase || "Pending";
203
- return phase === "Pending";
204
- });
205
- const approved2 = allRequests.filter((r) => {
206
- const phase = r.status?.phase;
207
- return phase === "Approved";
208
- });
209
- const rejected2 = allRequests.filter((r) => {
210
- const phase = r.status?.phase;
211
- return phase === "Rejected";
212
- });
213
- console.log("ApprovalQueueCard: grouped -", {
214
- pending: pending2.length,
215
- approved: approved2.length,
216
- rejected: rejected2.length
217
- });
218
- return { pending: pending2, approved: approved2, rejected: rejected2, reviewedBy, ownedApiProducts };
219
- }, [backendUrl, fetchApi, identityApi, refresh]);
220
- const handleApprove = (request) => {
221
- setDialogState({
222
- open: true,
223
- request,
224
- action: "approve",
225
- processing: false
226
- });
227
- };
228
- const handleReject = (request) => {
229
- setDialogState({
230
- open: true,
231
- request,
232
- action: "reject",
233
- processing: false
234
- });
235
- };
236
- const handleConfirm = async () => {
237
- if (!dialogState.request || !value) return;
238
- setDialogState((prev) => ({ ...prev, processing: true }));
239
- 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`;
240
- try {
241
- const response = await fetchApi.fetch(endpoint, {
242
- method: "POST",
243
- headers: { "Content-Type": "application/json" },
244
- body: JSON.stringify({
245
- reviewedBy: value.reviewedBy
246
- })
247
- });
248
- if (!response.ok) {
249
- throw new Error(`failed to ${dialogState.action} request`);
250
- }
251
- setDialogState({
252
- open: false,
253
- request: null,
254
- action: "approve",
255
- processing: false
256
- });
257
- setRefresh((r) => r + 1);
258
- const action = dialogState.action === "approve" ? "approved" : "rejected";
259
- alertApi.post({
260
- message: `API key ${action}`,
261
- severity: "success",
262
- display: "transient"
263
- });
264
- } catch (err) {
265
- console.error(`error ${dialogState.action}ing request:`, err);
266
- setDialogState((prev) => ({ ...prev, processing: false }));
267
- alertApi.post({
268
- message: `Failed to ${dialogState.action} API key`,
269
- severity: "error",
270
- display: "transient"
271
- });
272
- }
273
- };
274
- const handleBulkApprove = () => {
275
- if (selectedRequests.length === 0) return;
276
- setBulkDialogState({
277
- open: true,
278
- requests: selectedRequests,
279
- action: "approve",
280
- processing: false
281
- });
282
- };
283
- const handleBulkReject = () => {
284
- if (selectedRequests.length === 0) return;
285
- setBulkDialogState({
286
- open: true,
287
- requests: selectedRequests,
288
- action: "reject",
289
- processing: false
290
- });
291
- };
292
- const handleBulkConfirm = async () => {
293
- if (!value || bulkDialogState.requests.length === 0) return;
294
- setBulkDialogState((prev) => ({ ...prev, processing: true }));
295
- const isApprove = bulkDialogState.action === "approve";
296
- const endpoint = isApprove ? `${backendUrl}/api/kuadrant/requests/bulk-approve` : `${backendUrl}/api/kuadrant/requests/bulk-reject`;
297
- try {
298
- const response = await fetchApi.fetch(endpoint, {
299
- method: "POST",
300
- headers: { "Content-Type": "application/json" },
301
- body: JSON.stringify({
302
- requests: bulkDialogState.requests.map((r) => ({
303
- namespace: r.metadata.namespace,
304
- name: r.metadata.name
305
- })),
306
- reviewedBy: value.reviewedBy
307
- })
308
- });
309
- if (!response.ok) {
310
- throw new Error(`failed to bulk ${bulkDialogState.action} requests`);
311
- }
312
- const count = bulkDialogState.requests.length;
313
- const action = isApprove ? "approved" : "rejected";
314
- setBulkDialogState({
315
- open: false,
316
- requests: [],
317
- action: "approve",
318
- processing: false
319
- });
320
- setSelectedRequests([]);
321
- setRefresh((r) => r + 1);
322
- alertApi.post({
323
- message: `${count} API keys ${action}`,
324
- severity: "success",
325
- display: "transient"
326
- });
327
- } catch (err) {
328
- console.error(`error bulk ${bulkDialogState.action}ing requests:`, err);
329
- setBulkDialogState((prev) => ({ ...prev, processing: false }));
330
- alertApi.post({
331
- message: `Failed to bulk ${bulkDialogState.action} API keys`,
332
- severity: "error",
333
- display: "transient"
334
- });
335
- }
336
- };
337
- if (loading || updatePermissionLoading) {
338
- return /* @__PURE__ */ React.createElement(Progress, null);
339
- }
340
- if (error) {
341
- return /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error });
342
- }
343
- if (updatePermissionError) {
344
- return /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Typography, { color: "error" }, "Unable to check permissions: ", updatePermissionError.message), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Permission: kuadrant.apikey.update.all"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Please try again or contact your administrator"));
345
- }
346
- const pending = value?.pending || [];
347
- const approved = value?.approved || [];
348
- const rejected = value?.rejected || [];
349
- const formatDate = (dateString) => {
350
- const date = new Date(dateString);
351
- return date.toLocaleDateString("en-GB", {
352
- year: "numeric",
353
- month: "short",
354
- day: "numeric",
355
- hour: "2-digit",
356
- minute: "2-digit"
357
- });
358
- };
359
- const pendingColumns = [
360
- {
361
- title: "Name",
362
- field: "metadata.name",
363
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.metadata.name)
364
- },
365
- {
366
- title: "User",
367
- field: "spec.requestedBy.userId",
368
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.spec.requestedBy.userId)
369
- },
370
- {
371
- title: "API",
372
- field: "spec.apiProductRef.name",
373
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React.createElement("strong", null, row.spec.apiProductRef?.name || "unknown"))
374
- },
375
- {
376
- title: "Namespace",
377
- field: "metadata.namespace",
378
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.metadata.namespace)
379
- },
380
- {
381
- title: "Tier",
382
- field: "spec.planTier",
383
- render: (row) => /* @__PURE__ */ React.createElement(Chip, { label: row.spec.planTier, size: "small" })
384
- },
385
- {
386
- title: "Use Case",
387
- field: "spec.useCase",
388
- render: (row) => {
389
- if (!row.spec.useCase) {
390
- return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "-");
391
- }
392
- return /* @__PURE__ */ React.createElement(Tooltip, { title: row.spec.useCase, placement: "top" }, /* @__PURE__ */ React.createElement(
393
- Typography,
394
- {
395
- variant: "body2",
396
- style: {
397
- maxWidth: "200px",
398
- overflow: "hidden",
399
- textOverflow: "ellipsis",
400
- whiteSpace: "nowrap"
401
- }
402
- },
403
- row.spec.useCase
404
- ));
405
- }
406
- },
407
- {
408
- title: "Requested",
409
- field: "metadata.creationTimestamp",
410
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.metadata.creationTimestamp ? formatDate(row.metadata.creationTimestamp) : "-")
411
- },
412
- {
413
- title: "Actions",
414
- filtering: false,
415
- render: (row) => {
416
- const apiProductKey = `${row.metadata.namespace}/${row.spec.apiProductRef?.name || "unknown"}`;
417
- const ownsApiProduct = value?.ownedApiProducts?.has(apiProductKey) ?? false;
418
- const canUpdate = canUpdateAllRequests || canUpdateOwnRequests && ownsApiProduct;
419
- if (!canUpdate) return null;
420
- return /* @__PURE__ */ React.createElement(Box, { display: "flex", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(
421
- Button,
422
- {
423
- size: "small",
424
- startIcon: /* @__PURE__ */ React.createElement(CheckCircleIcon, null),
425
- onClick: () => handleApprove(row),
426
- color: "primary",
427
- variant: "outlined"
428
- },
429
- "Approve"
430
- ), /* @__PURE__ */ React.createElement(
431
- Button,
432
- {
433
- size: "small",
434
- startIcon: /* @__PURE__ */ React.createElement(CancelIcon, null),
435
- onClick: () => handleReject(row),
436
- color: "secondary",
437
- variant: "outlined"
438
- },
439
- "Reject"
440
- ));
441
- }
442
- }
443
- ];
444
- const approvedColumns = [
445
- {
446
- title: "Name",
447
- field: "metadata.name",
448
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.metadata.name)
449
- },
450
- {
451
- title: "User",
452
- field: "spec.requestedBy.userId",
453
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.spec.requestedBy.userId)
454
- },
455
- {
456
- title: "API",
457
- field: "spec.apiProductRef.name",
458
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React.createElement("strong", null, row.spec.apiProductRef?.name || "unknown"))
459
- },
460
- {
461
- title: "Namespace",
462
- field: "metadata.namespace",
463
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.metadata.namespace)
464
- },
465
- {
466
- title: "Tier",
467
- field: "spec.planTier",
468
- render: (row) => /* @__PURE__ */ React.createElement(Chip, { label: row.spec.planTier, size: "small" })
469
- },
470
- {
471
- title: "Requested",
472
- field: "metadata.creationTimestamp",
473
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.metadata.creationTimestamp ? formatDate(row.metadata.creationTimestamp) : "-")
474
- },
475
- {
476
- title: "Approved",
477
- field: "status.reviewedAt",
478
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.status?.reviewedAt ? formatDate(row.status.reviewedAt) : "-")
479
- },
480
- {
481
- title: "Reviewed By",
482
- field: "status.reviewedBy",
483
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.status?.reviewedBy || "-")
484
- },
485
- {
486
- title: "Approval Type",
487
- field: "status.reviewedBy",
488
- render: (row) => {
489
- const isAutomatic = row.status?.reviewedBy === "system";
490
- return /* @__PURE__ */ React.createElement(
491
- Chip,
492
- {
493
- label: isAutomatic ? "Automatic" : "Manual",
494
- size: "small",
495
- color: isAutomatic ? "default" : "primary"
496
- }
497
- );
498
- }
499
- }
500
- ];
501
- const rejectedColumns = [
502
- {
503
- title: "Name",
504
- field: "metadata.name",
505
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.metadata.name)
506
- },
507
- {
508
- title: "User",
509
- field: "spec.requestedBy.userId",
510
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.spec.requestedBy.userId)
511
- },
512
- {
513
- title: "API",
514
- field: "spec.apiProductRef.name",
515
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React.createElement("strong", null, row.spec.apiProductRef?.name || "unknown"))
516
- },
517
- {
518
- title: "Namespace",
519
- field: "metadata.namespace",
520
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.metadata.namespace)
521
- },
522
- {
523
- title: "Tier",
524
- field: "spec.planTier",
525
- render: (row) => /* @__PURE__ */ React.createElement(Chip, { label: row.spec.planTier, size: "small" })
526
- },
527
- {
528
- title: "Requested",
529
- field: "metadata.creationTimestamp",
530
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.metadata.creationTimestamp ? formatDate(row.metadata.creationTimestamp) : "-")
531
- },
532
- {
533
- title: "Rejected",
534
- field: "status.reviewedAt",
535
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.status?.reviewedAt ? formatDate(row.status.reviewedAt) : "-")
536
- },
537
- {
538
- title: "Reviewed By",
539
- field: "status.reviewedBy",
540
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.status?.reviewedBy || "-")
541
- }
542
- ];
543
- const getTabData = () => {
544
- const addIds = (data) => data.map((item) => ({ ...item, id: item.metadata.name }));
545
- switch (selectedTab) {
546
- case 0:
547
- const pendingWithSelection = pending.map((row) => {
548
- const isSelected = selectedRequests.some(
549
- (selected) => selected.metadata.name === row.metadata.name && selected.metadata.namespace === row.metadata.namespace
550
- );
551
- return {
552
- ...row,
553
- tableData: { checked: isSelected }
554
- };
555
- });
556
- return {
557
- data: pendingWithSelection,
558
- columns: pendingColumns,
559
- showSelection: true
560
- };
561
- case 1:
562
- return {
563
- data: addIds(approved),
564
- columns: approvedColumns,
565
- showSelection: false
566
- };
567
- case 2:
568
- return {
569
- data: addIds(rejected),
570
- columns: rejectedColumns,
571
- showSelection: false
572
- };
573
- default:
574
- return {
575
- data: addIds(pending),
576
- columns: pendingColumns,
577
- showSelection: true
578
- };
579
- }
580
- };
581
- const tabData = getTabData();
582
- const groupByApiProduct = (requests) => {
583
- const grouped = /* @__PURE__ */ new Map();
584
- requests.forEach((request) => {
585
- const key = `${request.metadata.namespace}/${request.spec.apiProductRef?.name || "unknown"}`;
586
- if (!grouped.has(key)) {
587
- grouped.set(key, []);
588
- }
589
- grouped.get(key).push(request);
590
- });
591
- return grouped;
592
- };
593
- const groupedData = groupByApiProduct(tabData.data);
594
- const apiProductKeys = Array.from(groupedData.keys()).sort();
595
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
596
- InfoCard,
597
- {
598
- title: "API Access Requests",
599
- subheader: `${pending.length} pending, ${approved.length} approved, ${rejected.length} rejected`
600
- },
601
- /* @__PURE__ */ React.createElement(Box, { mb: 2, "data-testid": "approval-queue-card" }, /* @__PURE__ */ React.createElement(
602
- Tabs,
603
- {
604
- value: selectedTab,
605
- onChange: (_, newValue) => {
606
- setSelectedTab(newValue);
607
- setSelectedRequests([]);
608
- },
609
- indicatorColor: "primary",
610
- textColor: "primary",
611
- "data-testid": "approval-queue-tabs"
612
- },
613
- /* @__PURE__ */ React.createElement(
614
- Tab,
615
- {
616
- label: `Pending (${pending.length})`,
617
- "data-testid": "approval-queue-pending-tab"
618
- }
619
- ),
620
- /* @__PURE__ */ React.createElement(
621
- Tab,
622
- {
623
- label: `Approved (${approved.length})`,
624
- "data-testid": "approval-queue-approved-tab"
625
- }
626
- ),
627
- /* @__PURE__ */ React.createElement(
628
- Tab,
629
- {
630
- label: `Rejected (${rejected.length})`,
631
- "data-testid": "approval-queue-rejected-tab"
632
- }
633
- )
634
- )),
635
- selectedTab === 0 && selectedRequests.length > 0 && /* @__PURE__ */ React.createElement(
636
- Box,
637
- {
638
- mb: 2,
639
- display: "flex",
640
- alignItems: "center",
641
- justifyContent: "space-between",
642
- p: 2,
643
- bgcolor: "background.default"
644
- },
645
- /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, selectedRequests.length, " request", selectedRequests.length !== 1 ? "s" : "", " selected"),
646
- /* @__PURE__ */ React.createElement(Box, { display: "flex", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(
647
- Button,
648
- {
649
- size: "small",
650
- variant: "contained",
651
- color: "primary",
652
- startIcon: /* @__PURE__ */ React.createElement(CheckCircleIcon, null),
653
- onClick: handleBulkApprove
654
- },
655
- "Approve Selected"
656
- ), /* @__PURE__ */ React.createElement(
657
- Button,
658
- {
659
- size: "small",
660
- variant: "contained",
661
- color: "secondary",
662
- startIcon: /* @__PURE__ */ React.createElement(CancelIcon, null),
663
- onClick: handleBulkReject
664
- },
665
- "Reject Selected"
666
- ))
667
- ),
668
- tabData.data.length === 0 ? /* @__PURE__ */ React.createElement(Box, { p: 3, textAlign: "center" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", color: "textSecondary" }, selectedTab === 0 && "No pending requests.", selectedTab === 1 && "No approved requests.", selectedTab === 2 && "No rejected requests.")) : /* @__PURE__ */ React.createElement(Box, null, apiProductKeys.map((apiProductKey) => {
669
- const requests = groupedData.get(apiProductKey) || [];
670
- const displayName = requests[0]?.spec.apiProductRef?.name || apiProductKey;
671
- const ownsThisApiProduct = value?.ownedApiProducts?.has(apiProductKey) ?? false;
672
- const canSelectRows = canUpdateAllRequests || canUpdateOwnRequests && ownsThisApiProduct;
673
- return /* @__PURE__ */ React.createElement(
674
- Accordion,
675
- {
676
- key: apiProductKey,
677
- defaultExpanded: apiProductKeys.length === 1
678
- },
679
- /* @__PURE__ */ React.createElement(AccordionSummary, { expandIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null) }, /* @__PURE__ */ React.createElement(
680
- Box,
681
- {
682
- display: "flex",
683
- alignItems: "center",
684
- justifyContent: "space-between",
685
- width: "100%"
686
- },
687
- /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, displayName),
688
- /* @__PURE__ */ React.createElement(
689
- Chip,
690
- {
691
- label: `${requests.length} request${requests.length !== 1 ? "s" : ""}`,
692
- size: "small",
693
- color: "primary",
694
- style: { marginRight: 16 }
695
- }
696
- )
697
- )),
698
- /* @__PURE__ */ React.createElement(AccordionDetails, null, /* @__PURE__ */ React.createElement(Box, { width: "100%" }, /* @__PURE__ */ React.createElement(
699
- Table,
700
- {
701
- options: {
702
- selection: canSelectRows && tabData.showSelection,
703
- paging: requests.length > 5,
704
- pageSize: 20,
705
- search: true,
706
- filtering: true,
707
- debounceInterval: 300,
708
- showTextRowsSelected: false,
709
- toolbar: true,
710
- emptyRowsWhenPaging: false
711
- },
712
- data: requests,
713
- columns: tabData.columns,
714
- onSelectionChange: (rows) => {
715
- const otherSelections = selectedRequests.filter(
716
- (r) => `${r.metadata.namespace}/${r.spec.apiProductRef?.name || "unknown"}` !== apiProductKey
717
- );
718
- setSelectedRequests([
719
- ...otherSelections,
720
- ...rows
721
- ]);
722
- }
723
- }
724
- )))
725
- );
726
- }))
727
- ), /* @__PURE__ */ React.createElement(
728
- ApprovalDialog,
729
- {
730
- open: dialogState.open,
731
- request: dialogState.request,
732
- action: dialogState.action,
733
- processing: dialogState.processing,
734
- onClose: () => setDialogState({
735
- open: false,
736
- request: null,
737
- action: "approve",
738
- processing: false
739
- }),
740
- onConfirm: handleConfirm
741
- }
742
- ), /* @__PURE__ */ React.createElement(
743
- BulkActionDialog,
744
- {
745
- open: bulkDialogState.open,
746
- requests: bulkDialogState.requests,
747
- action: bulkDialogState.action,
748
- processing: bulkDialogState.processing,
749
- onClose: () => setBulkDialogState({
750
- open: false,
751
- requests: [],
752
- action: "approve",
753
- processing: false
754
- }),
755
- onConfirm: handleBulkConfirm
756
- }
757
- ));
758
- };
759
-
760
- export { ApprovalQueueCard };
761
- //# sourceMappingURL=ApprovalQueueCard.esm.js.map