@kuadrant/kuadrant-backstage-plugin-frontend 0.2.1 → 0.3.0-aaa46b4

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/api.esm.js +40 -2
  2. package/dist/api.esm.js.map +1 -1
  3. package/dist/components/ApiAccessCard/ApiAccessCard.esm.js +20 -85
  4. package/dist/components/ApiAccessCard/ApiAccessCard.esm.js.map +1 -1
  5. package/dist/components/ApiKeyApprovalPage/ApiKeyApprovalPageWithPermissions.esm.js +18 -0
  6. package/dist/components/ApiKeyApprovalPage/ApiKeyApprovalPageWithPermissions.esm.js.map +1 -0
  7. package/dist/components/ApiKeyApprovalPage/index.esm.js +1 -0
  8. package/dist/components/ApiKeyApprovalPage/index.esm.js.map +1 -1
  9. package/dist/components/ApiKeyDetailPage/ApiKeyDetailPage.esm.js +19 -60
  10. package/dist/components/ApiKeyDetailPage/ApiKeyDetailPage.esm.js.map +1 -1
  11. package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js +45 -110
  12. package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js.map +1 -1
  13. package/dist/components/ApiProductInfoCard/ApiProductInfoCard.esm.js +3 -2
  14. package/dist/components/ApiProductInfoCard/ApiProductInfoCard.esm.js.map +1 -1
  15. package/dist/components/ApprovalQueueTable/ApprovalQueueTable.esm.js +16 -15
  16. package/dist/components/ApprovalQueueTable/ApprovalQueueTable.esm.js.map +1 -1
  17. package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js +2 -3
  18. package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js.map +1 -1
  19. package/dist/components/KuadrantPage/ApiProductsPage.esm.js +5 -18
  20. package/dist/components/KuadrantPage/ApiProductsPage.esm.js.map +1 -1
  21. package/dist/components/MyApiKeysTable/MyApiKeysTable.esm.js +28 -138
  22. package/dist/components/MyApiKeysTable/MyApiKeysTable.esm.js.map +1 -1
  23. package/dist/components/PermissionGate/PermissionGate.esm.js +2 -2
  24. package/dist/components/PermissionGate/PermissionGate.esm.js.map +1 -1
  25. package/dist/components/RequestAccessDialog/RequestAccessDialog.esm.js +32 -18
  26. package/dist/components/RequestAccessDialog/RequestAccessDialog.esm.js.map +1 -1
  27. package/dist/components/SimpleRequestAccessDialog/SimpleRequestAccessDialog.esm.js +13 -2
  28. package/dist/components/SimpleRequestAccessDialog/SimpleRequestAccessDialog.esm.js.map +1 -1
  29. package/dist/index.d.ts +47 -6
  30. package/dist/index.esm.js +1 -1
  31. package/dist/plugin.esm.js +2 -10
  32. package/dist/plugin.esm.js.map +1 -1
  33. package/dist/utils/apikeys.esm.js +21 -0
  34. package/dist/utils/apikeys.esm.js.map +1 -0
  35. package/dist/utils/styles.esm.js +5 -2
  36. package/dist/utils/styles.esm.js.map +1 -1
  37. package/dist-scalprum/internal.plugin-kuadrant.7f1de85661b1b848af15.js +2 -0
  38. package/dist-scalprum/internal.plugin-kuadrant.7f1de85661b1b848af15.js.map +1 -0
  39. package/dist-scalprum/plugin-manifest.json +3 -3
  40. package/dist-scalprum/static/1340.7c4f4722.chunk.js +3 -0
  41. package/dist-scalprum/static/1340.7c4f4722.chunk.js.map +1 -0
  42. package/dist-scalprum/static/1719.52ca7daf.chunk.js +2 -0
  43. package/dist-scalprum/static/1719.52ca7daf.chunk.js.map +1 -0
  44. package/dist-scalprum/static/{1881.56cde36b.chunk.js → 1931.f6a5edbb.chunk.js} +2 -2
  45. package/dist-scalprum/static/1931.f6a5edbb.chunk.js.map +1 -0
  46. package/dist-scalprum/static/20.b3a37d1d.chunk.js +3 -0
  47. package/dist-scalprum/static/20.b3a37d1d.chunk.js.map +1 -0
  48. package/dist-scalprum/static/2251.08f3db99.chunk.js +2 -0
  49. package/dist-scalprum/static/2251.08f3db99.chunk.js.map +1 -0
  50. package/dist-scalprum/static/2769.e09b0ab0.chunk.js +2 -0
  51. package/dist-scalprum/static/2769.e09b0ab0.chunk.js.map +1 -0
  52. package/dist-scalprum/static/2821.1ac360ec.chunk.js +2 -0
  53. package/dist-scalprum/static/2821.1ac360ec.chunk.js.map +1 -0
  54. package/dist-scalprum/static/2967.004a950e.chunk.js +2 -0
  55. package/dist-scalprum/static/2967.004a950e.chunk.js.map +1 -0
  56. package/dist-scalprum/static/327.74ecd572.chunk.js +2 -0
  57. package/dist-scalprum/static/327.74ecd572.chunk.js.map +1 -0
  58. package/dist-scalprum/static/3658.104704cc.chunk.js +2 -0
  59. package/dist-scalprum/static/3658.104704cc.chunk.js.map +1 -0
  60. package/dist-scalprum/static/3917.888fba36.chunk.js +2 -0
  61. package/dist-scalprum/static/3917.888fba36.chunk.js.map +1 -0
  62. package/dist-scalprum/static/3947.f5320e89.chunk.js +2 -0
  63. package/dist-scalprum/static/3947.f5320e89.chunk.js.map +1 -0
  64. package/dist-scalprum/static/438.4335b833.chunk.js +2 -0
  65. package/dist-scalprum/static/438.4335b833.chunk.js.map +1 -0
  66. package/dist-scalprum/static/4908.4f21d453.chunk.js +2 -0
  67. package/dist-scalprum/static/4908.4f21d453.chunk.js.map +1 -0
  68. package/dist-scalprum/static/5197.53535ff4.chunk.js +2 -0
  69. package/dist-scalprum/static/5197.53535ff4.chunk.js.map +1 -0
  70. package/dist-scalprum/static/5289.6c803140.chunk.js +2 -0
  71. package/dist-scalprum/static/5289.6c803140.chunk.js.map +1 -0
  72. package/dist-scalprum/static/5943.4e1dedc5.chunk.js +2 -0
  73. package/dist-scalprum/static/5943.4e1dedc5.chunk.js.map +1 -0
  74. package/dist-scalprum/static/6311.da1849d1.chunk.js +2 -0
  75. package/dist-scalprum/static/6311.da1849d1.chunk.js.map +1 -0
  76. package/dist-scalprum/static/6371.02375475.chunk.js +3 -0
  77. package/dist-scalprum/static/6371.02375475.chunk.js.map +1 -0
  78. package/dist-scalprum/static/6837.6252b97a.chunk.js +2 -0
  79. package/dist-scalprum/static/6837.6252b97a.chunk.js.map +1 -0
  80. package/dist-scalprum/static/7270.8ee9d452.chunk.js +2 -0
  81. package/dist-scalprum/static/7270.8ee9d452.chunk.js.map +1 -0
  82. package/dist-scalprum/static/980.744a4d36.chunk.js +2 -0
  83. package/dist-scalprum/static/980.744a4d36.chunk.js.map +1 -0
  84. package/dist-scalprum/static/exposed-KuadrantPage.f123b200.chunk.js +2 -0
  85. package/dist-scalprum/static/exposed-KuadrantPage.f123b200.chunk.js.map +1 -0
  86. package/dist-scalprum/static/exposed-PluginRoot.3a5f9157.chunk.js +2 -0
  87. package/dist-scalprum/static/exposed-PluginRoot.3a5f9157.chunk.js.map +1 -0
  88. package/package.json +1 -1
  89. package/dist/components/EntityApiApprovalTab/EntityApiApprovalTab.esm.js +0 -265
  90. package/dist/components/EntityApiApprovalTab/EntityApiApprovalTab.esm.js.map +0 -1
  91. package/dist/components/EntityApiApprovalTab/index.esm.js +0 -2
  92. package/dist/components/EntityApiApprovalTab/index.esm.js.map +0 -1
  93. package/dist-scalprum/internal.plugin-kuadrant.c4fb9c2347d3ad03a5d0.js +0 -2
  94. package/dist-scalprum/internal.plugin-kuadrant.c4fb9c2347d3ad03a5d0.js.map +0 -1
  95. package/dist-scalprum/static/1584.1e45e570.chunk.js +0 -3
  96. package/dist-scalprum/static/1584.1e45e570.chunk.js.map +0 -1
  97. package/dist-scalprum/static/1719.246c020d.chunk.js +0 -2
  98. package/dist-scalprum/static/1719.246c020d.chunk.js.map +0 -1
  99. package/dist-scalprum/static/1881.56cde36b.chunk.js.map +0 -1
  100. package/dist-scalprum/static/286.ee43f560.chunk.js +0 -2
  101. package/dist-scalprum/static/286.ee43f560.chunk.js.map +0 -1
  102. package/dist-scalprum/static/2967.0c2ef90e.chunk.js +0 -2
  103. package/dist-scalprum/static/2967.0c2ef90e.chunk.js.map +0 -1
  104. package/dist-scalprum/static/3581.ebe5968a.chunk.js +0 -2
  105. package/dist-scalprum/static/3581.ebe5968a.chunk.js.map +0 -1
  106. package/dist-scalprum/static/369.2374941c.chunk.js +0 -2
  107. package/dist-scalprum/static/369.2374941c.chunk.js.map +0 -1
  108. package/dist-scalprum/static/3947.4f4aa8c2.chunk.js +0 -2
  109. package/dist-scalprum/static/3947.4f4aa8c2.chunk.js.map +0 -1
  110. package/dist-scalprum/static/3976.e8a8fda2.chunk.js +0 -2
  111. package/dist-scalprum/static/3976.e8a8fda2.chunk.js.map +0 -1
  112. package/dist-scalprum/static/4434.f92f6ba4.chunk.js +0 -2
  113. package/dist-scalprum/static/4434.f92f6ba4.chunk.js.map +0 -1
  114. package/dist-scalprum/static/4908.196a4425.chunk.js +0 -2
  115. package/dist-scalprum/static/4908.196a4425.chunk.js.map +0 -1
  116. package/dist-scalprum/static/512.d592d164.chunk.js +0 -2
  117. package/dist-scalprum/static/512.d592d164.chunk.js.map +0 -1
  118. package/dist-scalprum/static/5289.55069930.chunk.js +0 -2
  119. package/dist-scalprum/static/5289.55069930.chunk.js.map +0 -1
  120. package/dist-scalprum/static/5605.430f697e.chunk.js +0 -2
  121. package/dist-scalprum/static/5605.430f697e.chunk.js.map +0 -1
  122. package/dist-scalprum/static/5978.bf347a09.chunk.js +0 -2
  123. package/dist-scalprum/static/5978.bf347a09.chunk.js.map +0 -1
  124. package/dist-scalprum/static/6371.7b57ac27.chunk.js +0 -3
  125. package/dist-scalprum/static/6371.7b57ac27.chunk.js.map +0 -1
  126. package/dist-scalprum/static/6426.4c18bab5.chunk.js +0 -2
  127. package/dist-scalprum/static/6426.4c18bab5.chunk.js.map +0 -1
  128. package/dist-scalprum/static/6777.a2b77ec5.chunk.js +0 -2
  129. package/dist-scalprum/static/6777.a2b77ec5.chunk.js.map +0 -1
  130. package/dist-scalprum/static/6800.a45da2f4.chunk.js +0 -2
  131. package/dist-scalprum/static/6800.a45da2f4.chunk.js.map +0 -1
  132. package/dist-scalprum/static/7005.8190ad95.chunk.js +0 -2
  133. package/dist-scalprum/static/7005.8190ad95.chunk.js.map +0 -1
  134. package/dist-scalprum/static/7057.fcd688c7.chunk.js +0 -2
  135. package/dist-scalprum/static/7057.fcd688c7.chunk.js.map +0 -1
  136. package/dist-scalprum/static/7270.d52407cd.chunk.js +0 -2
  137. package/dist-scalprum/static/7270.d52407cd.chunk.js.map +0 -1
  138. package/dist-scalprum/static/7717.6f5b9dd7.chunk.js +0 -3
  139. package/dist-scalprum/static/7717.6f5b9dd7.chunk.js.map +0 -1
  140. package/dist-scalprum/static/9079.2ddaea57.chunk.js +0 -2
  141. package/dist-scalprum/static/9079.2ddaea57.chunk.js.map +0 -1
  142. package/dist-scalprum/static/9239.1acfe112.chunk.js +0 -2
  143. package/dist-scalprum/static/9239.1acfe112.chunk.js.map +0 -1
  144. package/dist-scalprum/static/9625.b0c6610a.chunk.js +0 -2
  145. package/dist-scalprum/static/9625.b0c6610a.chunk.js.map +0 -1
  146. package/dist-scalprum/static/exposed-KuadrantPage.5ae00605.chunk.js +0 -2
  147. package/dist-scalprum/static/exposed-KuadrantPage.5ae00605.chunk.js.map +0 -1
  148. package/dist-scalprum/static/exposed-PluginRoot.949381c2.chunk.js +0 -2
  149. package/dist-scalprum/static/exposed-PluginRoot.949381c2.chunk.js.map +0 -1
  150. /package/dist-scalprum/static/{1584.1e45e570.chunk.js.LICENSE.txt → 1340.7c4f4722.chunk.js.LICENSE.txt} +0 -0
  151. /package/dist-scalprum/static/{7717.6f5b9dd7.chunk.js.LICENSE.txt → 20.b3a37d1d.chunk.js.LICENSE.txt} +0 -0
  152. /package/dist-scalprum/static/{6371.7b57ac27.chunk.js.LICENSE.txt → 6371.02375475.chunk.js.LICENSE.txt} +0 -0
@@ -2,14 +2,14 @@ import React, { useState } from 'react';
2
2
  import { useParams } from 'react-router-dom';
3
3
  import { useApi, alertApiRef } from '@backstage/core-plugin-api';
4
4
  import { kuadrantApiRef } from '../../api.esm.js';
5
+ import { getAPIKeyPhase } from '../../utils/apikeys.esm.js';
5
6
  import { useAsync } from 'react-use';
6
7
  import { Page, Header, Content, ResponseErrorPanel, Link, Breadcrumbs, InfoCard } from '@backstage/core-components';
7
- import { makeStyles, Box, Button, Typography, Grid, Chip, Tooltip, IconButton, Tabs, Tab, Dialog, DialogTitle, DialogContent, DialogActions } from '@material-ui/core';
8
+ import { makeStyles, Box, Button, Typography, Grid, Chip, Tooltip, IconButton, Tabs, Tab } from '@material-ui/core';
8
9
  import { Skeleton } from '@material-ui/lab';
9
10
  import VisibilityIcon from '@material-ui/icons/Visibility';
10
11
  import VisibilityOffIcon from '@material-ui/icons/VisibilityOff';
11
12
  import FileCopyIcon from '@material-ui/icons/FileCopy';
12
- import WarningIcon from '@material-ui/icons/Warning';
13
13
  import ArrowBackIcon from '@material-ui/icons/ArrowBack';
14
14
  import OpenInNewIcon from '@material-ui/icons/OpenInNew';
15
15
  import EmailIcon from '@material-ui/icons/Email';
@@ -71,8 +71,6 @@ const ApiKeyDetailPage = () => {
71
71
  const [showApiKey, setShowApiKey] = useState(false);
72
72
  const [apiKeyValue, setApiKeyValue] = useState(null);
73
73
  const [apiKeyLoading, setApiKeyLoading] = useState(false);
74
- const [alreadyRead, setAlreadyRead] = useState(false);
75
- const [showWarning, setShowWarning] = useState(false);
76
74
  const {
77
75
  value: data,
78
76
  loading,
@@ -82,9 +80,6 @@ const ApiKeyDetailPage = () => {
82
80
  kuadrantApi.getApiKey(namespace, name),
83
81
  kuadrantApi.getApiProducts()
84
82
  ]);
85
- if (apiKeyData.status?.canReadSecret === false) {
86
- setAlreadyRead(true);
87
- }
88
83
  const apiProduct2 = (productsData.items || []).find(
89
84
  (p) => p.metadata.name === apiKeyData.spec.apiProductRef?.name && p.metadata.namespace === apiKeyData.metadata.namespace
90
85
  );
@@ -97,25 +92,15 @@ const ApiKeyDetailPage = () => {
97
92
  try {
98
93
  const extractedSecret = await kuadrantApi.getApiKeySecret(namespace, name);
99
94
  setApiKeyValue(extractedSecret.apiKey);
100
- setAlreadyRead(true);
101
95
  setShowApiKey(true);
102
96
  } catch (err) {
103
97
  console.error("Failed to fetch API key:", err);
104
98
  const errorMessage = err instanceof Error ? err.message : "unknown error occurred";
105
- if (errorMessage.includes("403") || errorMessage.includes("already been viewed")) {
106
- setAlreadyRead(true);
107
- alertApi.post({
108
- message: "This API key has already been viewed and cannot be retrieved again.",
109
- severity: "warning",
110
- display: "transient"
111
- });
112
- } else {
113
- alertApi.post({
114
- message: `Failed to fetch APIKey. ${errorMessage}`,
115
- severity: "error",
116
- display: "transient"
117
- });
118
- }
99
+ alertApi.post({
100
+ message: `Failed to fetch APIKey. ${errorMessage}`,
101
+ severity: "error",
102
+ display: "transient"
103
+ });
119
104
  } finally {
120
105
  setApiKeyLoading(false);
121
106
  }
@@ -124,14 +109,10 @@ const ApiKeyDetailPage = () => {
124
109
  if (showApiKey) {
125
110
  setShowApiKey(false);
126
111
  setApiKeyValue(null);
127
- } else if (!alreadyRead) {
128
- setShowWarning(true);
112
+ } else {
113
+ fetchApiKeySecret();
129
114
  }
130
115
  };
131
- const handleConfirmReveal = () => {
132
- setShowWarning(false);
133
- fetchApiKeySecret();
134
- };
135
116
  const handleCopy = async (text) => {
136
117
  await navigator.clipboard.writeText(text);
137
118
  alertApi.post({
@@ -146,7 +127,7 @@ const ApiKeyDetailPage = () => {
146
127
  if (error || !apiKey) {
147
128
  return /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error: error || new Error("API key not found") });
148
129
  }
149
- const phase = apiKey.status?.phase || "Pending";
130
+ const phase = getAPIKeyPhase(apiKey.status?.conditions || []);
150
131
  const statusLabel = phase === "Approved" ? "Active" : phase;
151
132
  const hostname = apiKey.status?.apiHostname || "api.example.com";
152
133
  const displayApiKey = apiKeyValue || "<your-api-key>";
@@ -232,16 +213,12 @@ func main() {
232
213
  }
233
214
  )), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Requester"), /* @__PURE__ */ React.createElement(Typography, { variant: "body1", className: classes.value }, apiKey.spec.requestedBy?.userId), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Requested"), /* @__PURE__ */ React.createElement(Typography, { variant: "body1", className: classes.value }, apiKey.metadata.creationTimestamp ? new Date(
234
215
  apiKey.metadata.creationTimestamp
235
- ).toLocaleDateString() : "-"), apiKey.status?.reviewedBy && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Reviewed By"), /* @__PURE__ */ React.createElement(Typography, { variant: "body1", className: classes.value }, apiKey.status.reviewedBy.replace(/^user:default\//, ""), apiKey.status.reviewedAt && /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, " ", "on", " ", new Date(
236
- apiKey.status.reviewedAt
237
- ).toLocaleDateString())))))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Use Case" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, apiKey.spec.useCase || "No use case provided")), phase === "Approved" && /* @__PURE__ */ React.createElement(Box, { mt: 2 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "API Key" }, alreadyRead && !apiKeyValue ? /* @__PURE__ */ React.createElement(Tooltip, { title: "This API key has already been viewed and cannot be retrieved again" }, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Already viewed - cannot be retrieved again"), /* @__PURE__ */ React.createElement(
238
- VisibilityOffIcon,
239
- {
240
- fontSize: "small",
241
- color: "disabled",
242
- style: { marginLeft: 8 }
243
- }
244
- ))) : /* @__PURE__ */ React.createElement(Box, { className: classes.apiKeyContainer }, /* @__PURE__ */ React.createElement(
216
+ ).toLocaleDateString() : "-"), (() => {
217
+ const approvalDate = apiKey.status?.conditions?.find(
218
+ (c) => c.type === "Approved" && c.status === "True"
219
+ )?.lastTransitionTime;
220
+ return approvalDate ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Approved On"), /* @__PURE__ */ React.createElement(Typography, { variant: "body1", className: classes.value }, new Date(approvalDate).toLocaleDateString())) : null;
221
+ })()))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Use Case" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, apiKey.spec.useCase || "No use case provided")), phase === "Approved" && /* @__PURE__ */ React.createElement(Box, { mt: 2 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "API Key" }, /* @__PURE__ */ React.createElement(Box, { className: classes.apiKeyContainer }, /* @__PURE__ */ React.createElement(
245
222
  Typography,
246
223
  {
247
224
  variant: "body2",
@@ -258,14 +235,14 @@ func main() {
258
235
  )), /* @__PURE__ */ React.createElement(
259
236
  Tooltip,
260
237
  {
261
- title: showApiKey ? "Hide API key" : "Reveal API key (one-time only)"
238
+ title: showApiKey ? "Hide API key" : "Reveal API key"
262
239
  },
263
240
  /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(
264
241
  IconButton,
265
242
  {
266
243
  size: "small",
267
244
  onClick: handleRevealClick,
268
- disabled: apiKeyLoading || alreadyRead && !apiKeyValue
245
+ disabled: apiKeyLoading
269
246
  },
270
247
  showApiKey ? /* @__PURE__ */ React.createElement(VisibilityOffIcon, { fontSize: "small" }) : /* @__PURE__ */ React.createElement(VisibilityIcon, { fontSize: "small" })
271
248
  ))
@@ -284,25 +261,7 @@ func main() {
284
261
  code: codeExamples[selectedTab].code,
285
262
  onCopy: () => handleCopy(codeExamples[selectedTab].code)
286
263
  }
287
- ))))), apiKey.status?.limits && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Rate Limits" }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, apiKey.status.limits.daily && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Daily"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.daily.toLocaleString())), apiKey.status.limits.weekly && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Weekly"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.weekly.toLocaleString())), apiKey.status.limits.monthly && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Monthly"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.monthly.toLocaleString()))))))), /* @__PURE__ */ React.createElement(
288
- Dialog,
289
- {
290
- open: showWarning,
291
- onClose: () => setShowWarning(false),
292
- maxWidth: "sm"
293
- },
294
- /* @__PURE__ */ React.createElement(DialogTitle, null, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(WarningIcon, { color: "primary", style: { marginRight: 8 } }), "View API Key")),
295
- /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", paragraph: true }, "This API key can only be viewed ", /* @__PURE__ */ React.createElement("strong", null, "once"), ". After you reveal it, you will not be able to retrieve it again."), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Make sure to copy and store it securely before closing this view.")),
296
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: () => setShowWarning(false) }, "Cancel"), /* @__PURE__ */ React.createElement(
297
- Button,
298
- {
299
- variant: "contained",
300
- color: "primary",
301
- onClick: handleConfirmReveal
302
- },
303
- "Reveal API Key"
304
- ))
305
- ));
264
+ ))))), apiKey.status?.limits && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Rate Limits" }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, apiKey.status.limits.daily && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Daily"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.daily.toLocaleString())), apiKey.status.limits.weekly && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Weekly"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.weekly.toLocaleString())), apiKey.status.limits.monthly && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Monthly"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.monthly.toLocaleString()))))))));
306
265
  };
307
266
 
308
267
  export { ApiKeyDetailPage };
@@ -1 +1 @@
1
- {"version":3,"file":"ApiKeyDetailPage.esm.js","sources":["../../../src/components/ApiKeyDetailPage/ApiKeyDetailPage.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n useApi,\n alertApiRef,\n} from \"@backstage/core-plugin-api\";\nimport { kuadrantApiRef } from '../../api';\nimport { useAsync } from \"react-use\";\nimport {\n Header,\n Page,\n Content,\n ResponseErrorPanel,\n InfoCard,\n Link,\n Breadcrumbs,\n} from \"@backstage/core-components\";\nimport {\n Box,\n Grid,\n Typography,\n Chip,\n IconButton,\n Tooltip,\n Tabs,\n Tab,\n Button,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n makeStyles,\n} from \"@material-ui/core\";\nimport { Skeleton } from \"@material-ui/lab\";\nimport VisibilityIcon from \"@material-ui/icons/Visibility\";\nimport VisibilityOffIcon from \"@material-ui/icons/VisibilityOff\";\nimport FileCopyIcon from \"@material-ui/icons/FileCopy\";\nimport WarningIcon from \"@material-ui/icons/Warning\";\nimport ArrowBackIcon from \"@material-ui/icons/ArrowBack\";\nimport OpenInNewIcon from \"@material-ui/icons/OpenInNew\";\nimport EmailIcon from \"@material-ui/icons/Email\";\nimport { APIKey, APIProduct } from \"../../types/api-management\";\nimport { getApprovalQueueStatusChipStyle } from \"../../utils/styles\";\n\nconst useStyles = makeStyles((theme) => ({\n label: {\n fontWeight: 600,\n color: theme.palette.text.secondary,\n marginBottom: theme.spacing(0.5),\n },\n value: {\n marginBottom: theme.spacing(2),\n },\n codeBlock: {\n backgroundColor: theme.palette.background.default,\n padding: theme.spacing(2),\n borderRadius: theme.shape.borderRadius,\n fontFamily: \"monospace\",\n fontSize: \"0.875rem\",\n overflow: \"auto\",\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-all\",\n },\n apiKeyContainer: {\n display: \"flex\",\n alignItems: \"center\",\n gap: theme.spacing(1),\n padding: theme.spacing(1.5),\n backgroundColor: theme.palette.background.default,\n borderRadius: theme.shape.borderRadius,\n fontFamily: \"monospace\",\n },\n tabPanel: {\n marginTop: theme.spacing(2),\n },\n}));\n\nconst CodeExample = ({\n code,\n onCopy,\n}: {\n code: string;\n onCopy: () => void;\n}) => {\n const classes = useStyles();\n\n return (\n <Box position=\"relative\">\n <Box className={classes.codeBlock}>{code}</Box>\n <Tooltip title=\"Copy code\">\n <IconButton\n size=\"small\"\n style={{ position: \"absolute\", top: 8, right: 8 }}\n onClick={onCopy}\n >\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </Box>\n );\n};\n\nexport const ApiKeyDetailPage = () => {\n const classes = useStyles();\n const { namespace, name } = useParams<{ namespace: string; name: string }>();\n const kuadrantApi = useApi(kuadrantApiRef);\n const alertApi = useApi(alertApiRef);\n\n const [selectedTab, setSelectedTab] = useState(0);\n const [showApiKey, setShowApiKey] = useState(false);\n const [apiKeyValue, setApiKeyValue] = useState<string | null>(null);\n const [apiKeyLoading, setApiKeyLoading] = useState(false);\n const [alreadyRead, setAlreadyRead] = useState(false);\n const [showWarning, setShowWarning] = useState(false);\n\n const {\n value: data,\n loading,\n error,\n } = useAsync(async () => {\n const [apiKeyData, productsData] = await Promise.all([\n kuadrantApi.getApiKey(namespace!, name!),\n kuadrantApi.getApiProducts(),\n ]);\n\n // check if key has already been read\n if (apiKeyData.status?.canReadSecret === false) {\n setAlreadyRead(true);\n }\n\n // find matching api product to get contact info\n const apiProduct = (productsData.items || []).find(\n (p: APIProduct) =>\n p.metadata.name === apiKeyData.spec.apiProductRef?.name &&\n p.metadata.namespace === apiKeyData.metadata.namespace,\n );\n\n return { apiKey: apiKeyData as APIKey, apiProduct };\n }, [namespace, name, kuadrantApi]);\n\n const apiKey = data?.apiKey;\n const apiProduct = data?.apiProduct;\n\n const fetchApiKeySecret = async () => {\n setApiKeyLoading(true);\n try {\n const extractedSecret = await kuadrantApi.getApiKeySecret(namespace!, name!);\n setApiKeyValue(extractedSecret.apiKey);\n setAlreadyRead(true);\n setShowApiKey(true);\n } catch (err) {\n console.error(\"Failed to fetch API key:\", err);\n const errorMessage = err instanceof Error ? err.message : \"unknown error occurred\";\n if (errorMessage.includes(\"403\") || errorMessage.includes(\"already been viewed\")) {\n setAlreadyRead(true);\n alertApi.post({\n message:\n \"This API key has already been viewed and cannot be retrieved again.\",\n severity: \"warning\",\n display: \"transient\",\n });\n } else {\n alertApi.post({\n message: `Failed to fetch APIKey. ${errorMessage}`,\n severity: 'error',\n display: 'transient',\n });\n }\n } finally {\n setApiKeyLoading(false);\n }\n };\n\n const handleRevealClick = () => {\n if (showApiKey) {\n setShowApiKey(false);\n setApiKeyValue(null);\n } else if (!alreadyRead) {\n setShowWarning(true);\n }\n };\n\n const handleConfirmReveal = () => {\n setShowWarning(false);\n fetchApiKeySecret();\n };\n\n const handleCopy = async (text: string) => {\n await navigator.clipboard.writeText(text);\n alertApi.post({\n message: \"Copied to clipboard\",\n severity: \"success\",\n display: \"transient\",\n });\n };\n\n if (loading) {\n return (\n <Page themeId=\"tool\">\n <Header title=\"Loading...\" />\n <Content>\n <Box p={2}>\n {[...Array(5)].map((_, i) => (\n <Box key={i} p={2}>\n <Skeleton variant=\"text\" width=\"100%\" />\n </Box>\n ))}\n </Box>\n </Content>\n </Page>\n );\n }\n\n if (error || !apiKey) {\n return (\n <ResponseErrorPanel error={error || new Error(\"API key not found\")} />\n );\n }\n\n const phase = apiKey.status?.phase || \"Pending\";\n const statusLabel = phase === \"Approved\" ? \"Active\" : phase;\n const hostname = apiKey.status?.apiHostname || \"api.example.com\";\n const displayApiKey = apiKeyValue || \"<your-api-key>\";\n\n // code examples\n const curlExample = `curl -H \"Authorization: Bearer ${displayApiKey}\" \\\\\n https://${hostname}/`;\n\n const nodeExample = `const response = await fetch('https://${hostname}/', {\n headers: {\n 'Authorization': 'Bearer ${displayApiKey}'\n }\n});\nconst data = await response.json();`;\n\n const pythonExample = `import requests\n\nresponse = requests.get(\n 'https://${hostname}/',\n headers={'Authorization': 'Bearer ${displayApiKey}'}\n)\ndata = response.json()`;\n\n const goExample = `package main\n\nimport (\n \"net/http\"\n)\n\nfunc main() {\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", \"https://${hostname}/\", nil)\n req.Header.Set(\"Authorization\", \"Bearer ${displayApiKey}\")\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n}`;\n\n const codeExamples = [\n { label: \"cURL\", code: curlExample },\n { label: \"Node.js\", code: nodeExample },\n { label: \"Python\", code: pythonExample },\n { label: \"Go\", code: goExample },\n ];\n\n return (\n <Page themeId=\"tool\">\n <Header\n title={apiKey.metadata.name}\n subtitle={`API Key for ${apiKey.spec.apiProductRef?.name || \"unknown\"}`}\n >\n <Link to=\"/kuadrant/my-api-keys\">\n <Button startIcon={<ArrowBackIcon />}>Back to API Keys</Button>\n </Link>\n </Header>\n <Content>\n <Box mb={2}>\n <Breadcrumbs aria-label=\"breadcrumb\">\n <Link to=\"/kuadrant/my-api-keys\">API keys</Link>\n <Typography>{apiKey.metadata.name}</Typography>\n </Breadcrumbs>\n </Box>\n\n <Box mb={3} display=\"flex\" style={{ gap: 8 }}>\n <Link to={`/catalog/default/api/${apiKey.spec.apiProductRef?.name}`}>\n <Button\n variant=\"outlined\"\n startIcon={<OpenInNewIcon />}\n data-testid=\"view-api-button\"\n >\n View API\n </Button>\n </Link>\n {apiProduct?.spec?.contact &&\n (apiProduct.spec.contact.email ||\n apiProduct.spec.contact.url ||\n apiProduct.spec.contact.slack) && (\n <Button\n variant=\"outlined\"\n startIcon={<EmailIcon />}\n href={\n apiProduct.spec.contact.email\n ? `mailto:${apiProduct.spec.contact.email}`\n : apiProduct.spec.contact.slack\n ? apiProduct.spec.contact.slack\n : apiProduct.spec.contact.url || \"#\"\n }\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Contact Owner\n </Button>\n )}\n </Box>\n\n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n <InfoCard title=\"API Key Details\">\n <Box>\n <Typography variant=\"caption\" className={classes.label}>\n Status\n </Typography>\n <Box className={classes.value}>\n <Chip\n label={statusLabel}\n size=\"small\"\n style={getApprovalQueueStatusChipStyle(phase)}\n data-testid=\"api-key-status-chip\"\n />\n </Box>\n\n <Typography variant=\"caption\" className={classes.label}>\n API Product\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n <Link\n to={`/catalog/default/api/${apiKey.spec.apiProductRef?.name}/api-keys`}\n >\n {apiKey.spec.apiProductRef?.name || \"unknown\"}\n </Link>\n </Typography>\n\n <Typography variant=\"caption\" className={classes.label}>\n Tier\n </Typography>\n <Box className={classes.value}>\n <Chip\n label={apiKey.spec.planTier}\n size=\"small\"\n variant=\"outlined\"\n />\n </Box>\n\n <Typography variant=\"caption\" className={classes.label}>\n Requester\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.spec.requestedBy?.userId}\n </Typography>\n\n <Typography variant=\"caption\" className={classes.label}>\n Requested\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.metadata.creationTimestamp\n ? new Date(\n apiKey.metadata.creationTimestamp,\n ).toLocaleDateString()\n : \"-\"}\n </Typography>\n\n {apiKey.status?.reviewedBy && (\n <>\n <Typography variant=\"caption\" className={classes.label}>\n Reviewed By\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.status.reviewedBy.replace(/^user:default\\//, \"\")}\n {apiKey.status.reviewedAt && (\n <Typography variant=\"caption\" color=\"textSecondary\">\n {\" \"}\n on{\" \"}\n {new Date(\n apiKey.status.reviewedAt,\n ).toLocaleDateString()}\n </Typography>\n )}\n </Typography>\n </>\n )}\n </Box>\n </InfoCard>\n </Grid>\n\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Use Case\">\n <Typography variant=\"body1\">\n {apiKey.spec.useCase || \"No use case provided\"}\n </Typography>\n </InfoCard>\n\n {phase === \"Approved\" && (\n <Box mt={2}>\n <InfoCard title=\"API Key\">\n {alreadyRead && !apiKeyValue ? (\n <Tooltip title=\"This API key has already been viewed and cannot be retrieved again\">\n <Box display=\"flex\" alignItems=\"center\">\n <Typography variant=\"body2\" color=\"textSecondary\">\n Already viewed - cannot be retrieved again\n </Typography>\n <VisibilityOffIcon\n fontSize=\"small\"\n color=\"disabled\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n </Tooltip>\n ) : (\n <Box className={classes.apiKeyContainer}>\n <Typography\n variant=\"body2\"\n style={{ fontFamily: \"monospace\", flex: 1 }}\n >\n {apiKeyLoading\n ? \"Loading...\"\n : showApiKey && apiKeyValue\n ? apiKeyValue\n : \"•\".repeat(32)}\n </Typography>\n {showApiKey && apiKeyValue && (\n <Tooltip title=\"Copy to clipboard\">\n <IconButton\n size=\"small\"\n onClick={() => handleCopy(apiKeyValue)}\n >\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n <Tooltip\n title={\n showApiKey\n ? \"Hide API key\"\n : \"Reveal API key (one-time only)\"\n }\n >\n <span>\n <IconButton\n size=\"small\"\n onClick={handleRevealClick}\n disabled={\n apiKeyLoading || (alreadyRead && !apiKeyValue)\n }\n >\n {showApiKey ? (\n <VisibilityOffIcon fontSize=\"small\" />\n ) : (\n <VisibilityIcon fontSize=\"small\" />\n )}\n </IconButton>\n </span>\n </Tooltip>\n </Box>\n )}\n </InfoCard>\n </Box>\n )}\n </Grid>\n\n {phase === \"Approved\" && (\n <Grid item xs={12}>\n <InfoCard title=\"Code Examples\">\n <Box>\n <Tabs\n value={selectedTab}\n onChange={(_, newValue) => setSelectedTab(newValue)}\n indicatorColor=\"primary\"\n textColor=\"primary\"\n >\n {codeExamples.map((ex) => (\n <Tab key={ex.label} label={ex.label} />\n ))}\n </Tabs>\n <Box className={classes.tabPanel}>\n <CodeExample\n code={codeExamples[selectedTab].code}\n onCopy={() => handleCopy(codeExamples[selectedTab].code)}\n />\n </Box>\n </Box>\n </InfoCard>\n </Grid>\n )}\n\n {apiKey.status?.limits && (\n <Grid item xs={12}>\n <InfoCard title=\"Rate Limits\">\n <Grid container spacing={2}>\n {apiKey.status.limits.daily && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Daily\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.daily.toLocaleString()}\n </Typography>\n </Grid>\n )}\n {apiKey.status.limits.weekly && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Weekly\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.weekly.toLocaleString()}\n </Typography>\n </Grid>\n )}\n {apiKey.status.limits.monthly && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Monthly\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.monthly.toLocaleString()}\n </Typography>\n </Grid>\n )}\n </Grid>\n </InfoCard>\n </Grid>\n )}\n </Grid>\n </Content>\n\n <Dialog\n open={showWarning}\n onClose={() => setShowWarning(false)}\n maxWidth=\"sm\"\n >\n <DialogTitle>\n <Box display=\"flex\" alignItems=\"center\">\n <WarningIcon color=\"primary\" style={{ marginRight: 8 }} />\n View API Key\n </Box>\n </DialogTitle>\n <DialogContent>\n <Typography variant=\"body1\" paragraph>\n This API key can only be viewed <strong>once</strong>. After you\n reveal it, you will not be able to retrieve it again.\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Make sure to copy and store it securely before closing this view.\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setShowWarning(false)}>Cancel</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleConfirmReveal}\n >\n Reveal API Key\n </Button>\n </DialogActions>\n </Dialog>\n </Page>\n );\n};\n"],"names":["apiProduct"],"mappings":"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"}
1
+ {"version":3,"file":"ApiKeyDetailPage.esm.js","sources":["../../../src/components/ApiKeyDetailPage/ApiKeyDetailPage.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n useApi,\n alertApiRef,\n} from \"@backstage/core-plugin-api\";\nimport { kuadrantApiRef } from '../../api';\nimport { getAPIKeyPhase } from '../../utils/apikeys';\nimport { useAsync } from \"react-use\";\nimport {\n Header,\n Page,\n Content,\n ResponseErrorPanel,\n InfoCard,\n Link,\n Breadcrumbs,\n} from \"@backstage/core-components\";\nimport {\n Box,\n Grid,\n Typography,\n Chip,\n IconButton,\n Tooltip,\n Tabs,\n Tab,\n Button,\n makeStyles,\n} from \"@material-ui/core\";\nimport { Skeleton } from \"@material-ui/lab\";\nimport VisibilityIcon from \"@material-ui/icons/Visibility\";\nimport VisibilityOffIcon from \"@material-ui/icons/VisibilityOff\";\nimport FileCopyIcon from \"@material-ui/icons/FileCopy\";\nimport ArrowBackIcon from \"@material-ui/icons/ArrowBack\";\nimport OpenInNewIcon from \"@material-ui/icons/OpenInNew\";\nimport EmailIcon from \"@material-ui/icons/Email\";\nimport { APIKey, APIProduct } from \"../../types/api-management\";\nimport { getApprovalQueueStatusChipStyle } from \"../../utils/styles\";\n\nconst useStyles = makeStyles((theme) => ({\n label: {\n fontWeight: 600,\n color: theme.palette.text.secondary,\n marginBottom: theme.spacing(0.5),\n },\n value: {\n marginBottom: theme.spacing(2),\n },\n codeBlock: {\n backgroundColor: theme.palette.background.default,\n padding: theme.spacing(2),\n borderRadius: theme.shape.borderRadius,\n fontFamily: \"monospace\",\n fontSize: \"0.875rem\",\n overflow: \"auto\",\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-all\",\n },\n apiKeyContainer: {\n display: \"flex\",\n alignItems: \"center\",\n gap: theme.spacing(1),\n padding: theme.spacing(1.5),\n backgroundColor: theme.palette.background.default,\n borderRadius: theme.shape.borderRadius,\n fontFamily: \"monospace\",\n },\n tabPanel: {\n marginTop: theme.spacing(2),\n },\n}));\n\nconst CodeExample = ({\n code,\n onCopy,\n}: {\n code: string;\n onCopy: () => void;\n}) => {\n const classes = useStyles();\n\n return (\n <Box position=\"relative\">\n <Box className={classes.codeBlock}>{code}</Box>\n <Tooltip title=\"Copy code\">\n <IconButton\n size=\"small\"\n style={{ position: \"absolute\", top: 8, right: 8 }}\n onClick={onCopy}\n >\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </Box>\n );\n};\n\nexport const ApiKeyDetailPage = () => {\n const classes = useStyles();\n const { namespace, name } = useParams<{ namespace: string; name: string }>();\n const kuadrantApi = useApi(kuadrantApiRef);\n const alertApi = useApi(alertApiRef);\n\n const [selectedTab, setSelectedTab] = useState(0);\n const [showApiKey, setShowApiKey] = useState(false);\n const [apiKeyValue, setApiKeyValue] = useState<string | null>(null);\n const [apiKeyLoading, setApiKeyLoading] = useState(false);\n\n const {\n value: data,\n loading,\n error,\n } = useAsync(async () => {\n const [apiKeyData, productsData] = await Promise.all([\n kuadrantApi.getApiKey(namespace!, name!),\n kuadrantApi.getApiProducts(),\n ]);\n\n // find matching api product to get contact info\n const apiProduct = (productsData.items || []).find(\n (p: APIProduct) =>\n p.metadata.name === apiKeyData.spec.apiProductRef?.name &&\n p.metadata.namespace === apiKeyData.metadata.namespace,\n );\n\n return { apiKey: apiKeyData as APIKey, apiProduct };\n }, [namespace, name, kuadrantApi]);\n\n const apiKey = data?.apiKey;\n const apiProduct = data?.apiProduct;\n\n const fetchApiKeySecret = async () => {\n setApiKeyLoading(true);\n try {\n const extractedSecret = await kuadrantApi.getApiKeySecret(namespace!, name!);\n setApiKeyValue(extractedSecret.apiKey);\n setShowApiKey(true);\n } catch (err) {\n console.error(\"Failed to fetch API key:\", err);\n const errorMessage = err instanceof Error ? err.message : \"unknown error occurred\";\n alertApi.post({\n message: `Failed to fetch APIKey. ${errorMessage}`,\n severity: 'error',\n display: 'transient',\n });\n } finally {\n setApiKeyLoading(false);\n }\n };\n\n const handleRevealClick = () => {\n if (showApiKey) {\n setShowApiKey(false);\n setApiKeyValue(null);\n } else {\n fetchApiKeySecret();\n }\n };\n\n const handleCopy = async (text: string) => {\n await navigator.clipboard.writeText(text);\n alertApi.post({\n message: \"Copied to clipboard\",\n severity: \"success\",\n display: \"transient\",\n });\n };\n\n if (loading) {\n return (\n <Page themeId=\"tool\">\n <Header title=\"Loading...\" />\n <Content>\n <Box p={2}>\n {[...Array(5)].map((_, i) => (\n <Box key={i} p={2}>\n <Skeleton variant=\"text\" width=\"100%\" />\n </Box>\n ))}\n </Box>\n </Content>\n </Page>\n );\n }\n\n if (error || !apiKey) {\n return (\n <ResponseErrorPanel error={error || new Error(\"API key not found\")} />\n );\n }\n\n const phase = getAPIKeyPhase(apiKey.status?.conditions || []);\n const statusLabel = phase === \"Approved\" ? \"Active\" : phase;\n const hostname = apiKey.status?.apiHostname || \"api.example.com\";\n const displayApiKey = apiKeyValue || \"<your-api-key>\";\n\n // code examples\n const curlExample = `curl -H \"Authorization: Bearer ${displayApiKey}\" \\\\\n https://${hostname}/`;\n\n const nodeExample = `const response = await fetch('https://${hostname}/', {\n headers: {\n 'Authorization': 'Bearer ${displayApiKey}'\n }\n});\nconst data = await response.json();`;\n\n const pythonExample = `import requests\n\nresponse = requests.get(\n 'https://${hostname}/',\n headers={'Authorization': 'Bearer ${displayApiKey}'}\n)\ndata = response.json()`;\n\n const goExample = `package main\n\nimport (\n \"net/http\"\n)\n\nfunc main() {\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", \"https://${hostname}/\", nil)\n req.Header.Set(\"Authorization\", \"Bearer ${displayApiKey}\")\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n}`;\n\n const codeExamples = [\n { label: \"cURL\", code: curlExample },\n { label: \"Node.js\", code: nodeExample },\n { label: \"Python\", code: pythonExample },\n { label: \"Go\", code: goExample },\n ];\n\n return (\n <Page themeId=\"tool\">\n <Header\n title={apiKey.metadata.name}\n subtitle={`API Key for ${apiKey.spec.apiProductRef?.name || \"unknown\"}`}\n >\n <Link to=\"/kuadrant/my-api-keys\">\n <Button startIcon={<ArrowBackIcon />}>Back to API Keys</Button>\n </Link>\n </Header>\n <Content>\n <Box mb={2}>\n <Breadcrumbs aria-label=\"breadcrumb\">\n <Link to=\"/kuadrant/my-api-keys\">API keys</Link>\n <Typography>{apiKey.metadata.name}</Typography>\n </Breadcrumbs>\n </Box>\n\n <Box mb={3} display=\"flex\" style={{ gap: 8 }}>\n <Link to={`/catalog/default/api/${apiKey.spec.apiProductRef?.name}`}>\n <Button\n variant=\"outlined\"\n startIcon={<OpenInNewIcon />}\n data-testid=\"view-api-button\"\n >\n View API\n </Button>\n </Link>\n {apiProduct?.spec?.contact &&\n (apiProduct.spec.contact.email ||\n apiProduct.spec.contact.url ||\n apiProduct.spec.contact.slack) && (\n <Button\n variant=\"outlined\"\n startIcon={<EmailIcon />}\n href={\n apiProduct.spec.contact.email\n ? `mailto:${apiProduct.spec.contact.email}`\n : apiProduct.spec.contact.slack\n ? apiProduct.spec.contact.slack\n : apiProduct.spec.contact.url || \"#\"\n }\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Contact Owner\n </Button>\n )}\n </Box>\n\n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n <InfoCard title=\"API Key Details\">\n <Box>\n <Typography variant=\"caption\" className={classes.label}>\n Status\n </Typography>\n <Box className={classes.value}>\n <Chip\n label={statusLabel}\n size=\"small\"\n style={getApprovalQueueStatusChipStyle(phase)}\n data-testid=\"api-key-status-chip\"\n />\n </Box>\n\n <Typography variant=\"caption\" className={classes.label}>\n API Product\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n <Link\n to={`/catalog/default/api/${apiKey.spec.apiProductRef?.name}/api-keys`}\n >\n {apiKey.spec.apiProductRef?.name || \"unknown\"}\n </Link>\n </Typography>\n\n <Typography variant=\"caption\" className={classes.label}>\n Tier\n </Typography>\n <Box className={classes.value}>\n <Chip\n label={apiKey.spec.planTier}\n size=\"small\"\n variant=\"outlined\"\n />\n </Box>\n\n <Typography variant=\"caption\" className={classes.label}>\n Requester\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.spec.requestedBy?.userId}\n </Typography>\n\n <Typography variant=\"caption\" className={classes.label}>\n Requested\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.metadata.creationTimestamp\n ? new Date(\n apiKey.metadata.creationTimestamp,\n ).toLocaleDateString()\n : \"-\"}\n </Typography>\n\n {(() => {\n const approvalDate = apiKey.status?.conditions?.find(\n c => c.type === 'Approved' && c.status === 'True'\n )?.lastTransitionTime;\n return approvalDate ? (\n <>\n <Typography variant=\"caption\" className={classes.label}>\n Approved On\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {new Date(approvalDate).toLocaleDateString()}\n </Typography>\n </>\n ) : null;\n })()}\n </Box>\n </InfoCard>\n </Grid>\n\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Use Case\">\n <Typography variant=\"body1\">\n {apiKey.spec.useCase || \"No use case provided\"}\n </Typography>\n </InfoCard>\n\n {phase === \"Approved\" && (\n <Box mt={2}>\n <InfoCard title=\"API Key\">\n <Box className={classes.apiKeyContainer}>\n <Typography\n variant=\"body2\"\n style={{ fontFamily: \"monospace\", flex: 1 }}\n >\n {apiKeyLoading\n ? \"Loading...\"\n : showApiKey && apiKeyValue\n ? apiKeyValue\n : \"•\".repeat(32)}\n </Typography>\n {showApiKey && apiKeyValue && (\n <Tooltip title=\"Copy to clipboard\">\n <IconButton\n size=\"small\"\n onClick={() => handleCopy(apiKeyValue)}\n >\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n <Tooltip\n title={\n showApiKey\n ? \"Hide API key\"\n : \"Reveal API key\"\n }\n >\n <span>\n <IconButton\n size=\"small\"\n onClick={handleRevealClick}\n disabled={apiKeyLoading}\n >\n {showApiKey ? (\n <VisibilityOffIcon fontSize=\"small\" />\n ) : (\n <VisibilityIcon fontSize=\"small\" />\n )}\n </IconButton>\n </span>\n </Tooltip>\n </Box>\n </InfoCard>\n </Box>\n )}\n </Grid>\n\n {phase === \"Approved\" && (\n <Grid item xs={12}>\n <InfoCard title=\"Code Examples\">\n <Box>\n <Tabs\n value={selectedTab}\n onChange={(_, newValue) => setSelectedTab(newValue)}\n indicatorColor=\"primary\"\n textColor=\"primary\"\n >\n {codeExamples.map((ex) => (\n <Tab key={ex.label} label={ex.label} />\n ))}\n </Tabs>\n <Box className={classes.tabPanel}>\n <CodeExample\n code={codeExamples[selectedTab].code}\n onCopy={() => handleCopy(codeExamples[selectedTab].code)}\n />\n </Box>\n </Box>\n </InfoCard>\n </Grid>\n )}\n\n {apiKey.status?.limits && (\n <Grid item xs={12}>\n <InfoCard title=\"Rate Limits\">\n <Grid container spacing={2}>\n {apiKey.status.limits.daily && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Daily\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.daily.toLocaleString()}\n </Typography>\n </Grid>\n )}\n {apiKey.status.limits.weekly && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Weekly\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.weekly.toLocaleString()}\n </Typography>\n </Grid>\n )}\n {apiKey.status.limits.monthly && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Monthly\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.monthly.toLocaleString()}\n </Typography>\n </Grid>\n )}\n </Grid>\n </InfoCard>\n </Grid>\n )}\n </Grid>\n </Content>\n </Page>\n );\n};\n"],"names":["apiProduct"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useMemo } from 'react';
2
2
  import { useAsync } from 'react-use';
3
3
  import { CodeSnippet, ResponseErrorPanel, Table } from '@backstage/core-components';
4
- import { Box, Typography, Tabs, Tab, Grid, Button, Menu, MenuItem, Dialog, DialogTitle, DialogContent, DialogActions, Chip, Tooltip, IconButton, CircularProgress } from '@material-ui/core';
4
+ import { Box, Typography, Tabs, Tab, Grid, Button, Menu, MenuItem, Chip, Tooltip, IconButton, CircularProgress } from '@material-ui/core';
5
5
  import { Skeleton } from '@material-ui/lab';
6
6
  import { useApi, identityApiRef, alertApiRef } from '@backstage/core-plugin-api';
7
7
  import { kuadrantApiRef } from '../../api.esm.js';
@@ -13,9 +13,9 @@ import CancelIcon from '@material-ui/icons/Cancel';
13
13
  import AddIcon from '@material-ui/icons/Add';
14
14
  import MoreVertIcon from '@material-ui/icons/MoreVert';
15
15
  import FileCopyIcon from '@material-ui/icons/FileCopy';
16
- import WarningIcon from '@material-ui/icons/Warning';
17
16
  import { kuadrantApiKeyCreatePermission, kuadrantApiKeyDeleteOwnPermission, kuadrantApiKeyDeleteAllPermission, kuadrantApiKeyUpdateOwnPermission } from '../../permissions.esm.js';
18
17
  import { useKuadrantPermission, canDeleteResource } from '../../utils/permissions.esm.js';
18
+ import { getAPIKeyPhase } from '../../utils/apikeys.esm.js';
19
19
  import { EditAPIKeyDialog } from '../EditAPIKeyDialog/EditAPIKeyDialog.esm.js';
20
20
  import { ConfirmDeleteDialog } from '../ConfirmDeleteDialog/ConfirmDeleteDialog.esm.js';
21
21
  import { generateAuthCodeSnippets } from '../../utils/codeSnippets.esm.js';
@@ -45,11 +45,6 @@ const ApiKeyManagementTab = ({
45
45
  /* @__PURE__ */ new Map()
46
46
  );
47
47
  const [apiKeyLoading, setApiKeyLoading] = useState(/* @__PURE__ */ new Set());
48
- const [alreadyReadKeys, setAlreadyReadKeys] = useState(
49
- /* @__PURE__ */ new Set()
50
- );
51
- const [showOnceWarningOpen, setShowOnceWarningOpen] = useState(false);
52
- const [pendingKeyReveal, setPendingKeyReveal] = useState(null);
53
48
  const apiProductName = entity.metadata.annotations?.["kuadrant.io/apiproduct"] || entity.metadata.name;
54
49
  const namespace = entity.metadata.annotations?.["kuadrant.io/namespace"] || propNamespace || "default";
55
50
  useAsync(async () => {
@@ -70,11 +65,8 @@ const ApiKeyManagementTab = ({
70
65
  loading: requestsLoading,
71
66
  error: requestsError
72
67
  } = useAsync(async () => {
73
- const data = await kuadrantApi.getRequestsByNamespace(namespace);
74
- return (data.items || []).filter(
75
- (r) => r.spec.apiProductRef.name === apiProductName && r.metadata.namespace === namespace
76
- // APIProducts and APIKeys (and its Secret) will be in the same NS
77
- );
68
+ const data = await kuadrantApi.getRequestsByApiProduct(apiProductName, namespace);
69
+ return data.items || [];
78
70
  }, [apiProductName, namespace, refresh, kuadrantApi]);
79
71
  const {
80
72
  value: apiProduct,
@@ -108,11 +100,11 @@ const ApiKeyManagementTab = ({
108
100
  loading: updateRequestPermissionLoading,
109
101
  error: updateRequestPermissionError
110
102
  } = useKuadrantPermission(kuadrantApiKeyUpdateOwnPermission);
111
- const handleDeleteRequest = async (name) => {
112
- setOptimisticallyDeleted((prev) => new Set(prev).add(name));
113
- setDeleting(name);
103
+ const handleDeleteRequest = async (deleteNamespace, deleteName) => {
104
+ setOptimisticallyDeleted((prev) => new Set(prev).add(deleteName));
105
+ setDeleting(deleteName);
114
106
  try {
115
- await kuadrantApi.deleteRequest(namespace, name);
107
+ await kuadrantApi.deleteRequest(deleteNamespace, deleteName);
116
108
  alertApi.post({
117
109
  message: "API key deleted successfully",
118
110
  severity: "success",
@@ -123,7 +115,7 @@ const ApiKeyManagementTab = ({
123
115
  const errorMessage = err instanceof Error ? err.message : "unknown error occurred";
124
116
  setOptimisticallyDeleted((prev) => {
125
117
  const next = new Set(prev);
126
- next.delete(name);
118
+ next.delete(deleteName);
127
119
  return next;
128
120
  });
129
121
  alertApi.post({
@@ -144,23 +136,14 @@ const ApiKeyManagementTab = ({
144
136
  try {
145
137
  const data = await kuadrantApi.getApiKeySecret(requestNamespace, requestName);
146
138
  setApiKeyValues((prev) => new Map(prev).set(key, data.apiKey));
147
- setAlreadyReadKeys((prev) => new Set(prev).add(key));
139
+ toggleVisibility(requestName);
148
140
  } catch (err) {
149
141
  const errorMessage = err instanceof Error ? err.message : "unknown error occurred";
150
- if (errorMessage.includes("403") || errorMessage.includes("already been viewed")) {
151
- setAlreadyReadKeys((prev) => new Set(prev).add(key));
152
- alertApi.post({
153
- message: "This API key has already been viewed and cannot be retrieved again.",
154
- severity: "warning",
155
- display: "transient"
156
- });
157
- } else {
158
- alertApi.post({
159
- message: `Failed to fetch api key: ${errorMessage}`,
160
- severity: "error",
161
- display: "transient"
162
- });
163
- }
142
+ alertApi.post({
143
+ message: `Failed to fetch api key: ${errorMessage}`,
144
+ severity: "error",
145
+ display: "transient"
146
+ });
164
147
  } finally {
165
148
  setApiKeyLoading((prev) => {
166
149
  const next = new Set(prev);
@@ -208,7 +191,10 @@ const ApiKeyManagementTab = ({
208
191
  };
209
192
  const handleDeleteConfirm = async () => {
210
193
  if (!deleteDialogState.request) return;
211
- await handleDeleteRequest(deleteDialogState.request.metadata.name);
194
+ await handleDeleteRequest(
195
+ deleteDialogState.request.metadata.namespace,
196
+ deleteDialogState.request.metadata.name
197
+ );
212
198
  setDeleteDialogState({ open: false, request: null });
213
199
  };
214
200
  const handleDeleteCancel = () => {
@@ -352,13 +338,13 @@ const ApiKeyManagementTab = ({
352
338
  );
353
339
  const plans = apiProduct?.status?.discoveredPlans || [];
354
340
  const pendingRequests = myRequests.filter(
355
- (r) => !r.status?.phase || r.status.phase === "Pending"
341
+ (r) => getAPIKeyPhase(r.status?.conditions) === "Pending"
356
342
  );
357
343
  const approvedRequests = myRequests.filter(
358
- (r) => r.status?.phase === "Approved"
344
+ (r) => getAPIKeyPhase(r.status?.conditions) === "Approved"
359
345
  );
360
346
  const rejectedRequests = myRequests.filter(
361
- (r) => r.status?.phase === "Rejected"
347
+ (r) => getAPIKeyPhase(r.status?.conditions) === "Denied"
362
348
  );
363
349
  const approvedColumns = [
364
350
  {
@@ -368,8 +354,13 @@ const ApiKeyManagementTab = ({
368
354
  },
369
355
  {
370
356
  title: "Approved",
371
- field: "status.reviewedAt",
372
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.status?.reviewedAt ? new Date(row.status.reviewedAt).toLocaleDateString() : "-")
357
+ field: "status.conditions",
358
+ render: (row) => {
359
+ const approvalDate = row.status?.conditions?.find(
360
+ (c) => c.type === "Approved" && c.status === "True"
361
+ )?.lastTransitionTime;
362
+ return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, approvalDate ? new Date(approvalDate).toLocaleDateString() : "-");
363
+ }
373
364
  },
374
365
  {
375
366
  title: "API Key",
@@ -381,33 +372,16 @@ const ApiKeyManagementTab = ({
381
372
  const isVisible = visibleKeys.has(row.metadata.name);
382
373
  const isLoading = apiKeyLoading.has(key);
383
374
  const apiKeyValue = apiKeyValues.get(key);
384
- const hasSecretRef = row.status?.secretRef?.name;
385
- const canReadSecret = row.status?.canReadSecret !== false;
386
- const isAlreadyRead = alreadyReadKeys.has(key) || !canReadSecret;
375
+ const hasSecretRef = Boolean(row.spec?.secretRef?.name);
387
376
  if (!hasSecretRef) {
388
377
  return /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Awaiting secret...");
389
378
  }
390
- if (isAlreadyRead && !apiKeyValue) {
391
- return /* @__PURE__ */ React.createElement(Tooltip, { title: "This API key has already been viewed and cannot be retrieved again" }, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(
392
- Typography,
393
- {
394
- variant: "body2",
395
- color: "textSecondary",
396
- style: { fontFamily: "monospace", marginRight: 8 }
397
- },
398
- "Already viewed"
399
- ), /* @__PURE__ */ React.createElement(VisibilityOffIcon, { fontSize: "small", color: "disabled" })));
400
- }
401
379
  const handleRevealClick = () => {
402
380
  if (isVisible) {
403
381
  clearApiKeyValue(row.metadata.namespace, row.metadata.name);
404
382
  toggleVisibility(row.metadata.name);
405
- } else if (!isAlreadyRead) {
406
- setPendingKeyReveal({
407
- namespace: row.metadata.namespace,
408
- name: row.metadata.name
409
- });
410
- setShowOnceWarningOpen(true);
383
+ } else {
384
+ fetchApiKeyFromSecret(row.metadata.namespace, row.metadata.name);
411
385
  }
412
386
  };
413
387
  const handleCopy = async () => {
@@ -433,14 +407,14 @@ const ApiKeyManagementTab = ({
433
407
  ), isVisible && apiKeyValue && /* @__PURE__ */ React.createElement(Tooltip, { title: "Copy to clipboard" }, /* @__PURE__ */ React.createElement(IconButton, { size: "small", onClick: handleCopy }, /* @__PURE__ */ React.createElement(FileCopyIcon, { fontSize: "small" }))), /* @__PURE__ */ React.createElement(
434
408
  Tooltip,
435
409
  {
436
- title: isVisible ? "Hide API key" : "Reveal API key (one-time only)"
410
+ title: isVisible ? "Hide API key" : "Reveal API key"
437
411
  },
438
412
  /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(
439
413
  IconButton,
440
414
  {
441
415
  size: "small",
442
416
  onClick: handleRevealClick,
443
- disabled: isLoading || isAlreadyRead && !apiKeyValue
417
+ disabled: isLoading
444
418
  },
445
419
  isVisible ? /* @__PURE__ */ React.createElement(VisibilityOffIcon, null) : /* @__PURE__ */ React.createElement(VisibilityIcon, null)
446
420
  ))
@@ -487,9 +461,9 @@ const ApiKeyManagementTab = ({
487
461
  const requestColumns = [
488
462
  {
489
463
  title: "Status",
490
- field: "status.phase",
464
+ field: "status.conditions",
491
465
  render: (row) => {
492
- const phase = row.status?.phase || "Pending";
466
+ const phase = getAPIKeyPhase(row.status?.conditions || []);
493
467
  const isPending = phase === "Pending";
494
468
  return /* @__PURE__ */ React.createElement(
495
469
  Chip,
@@ -536,11 +510,12 @@ const ApiKeyManagementTab = ({
536
510
  },
537
511
  {
538
512
  title: "Reviewed",
539
- field: "status.reviewedAt",
513
+ field: "status.conditions",
540
514
  render: (row) => {
541
- if (!row.status?.reviewedAt)
542
- return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "-");
543
- return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, new Date(row.status.reviewedAt).toLocaleDateString());
515
+ const approvedDate = row.status?.conditions?.find(
516
+ (c) => (c.type === "Approved" || c.type === "Denied") && c.status === "True"
517
+ )?.lastTransitionTime;
518
+ return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, approvedDate ? new Date(approvedDate).toLocaleDateString() : "-");
544
519
  }
545
520
  },
546
521
  {
@@ -553,7 +528,7 @@ const ApiKeyManagementTab = ({
553
528
  if (isDeleting) {
554
529
  return /* @__PURE__ */ React.createElement(CircularProgress, { size: 20 });
555
530
  }
556
- const isPending = !row.status?.phase || row.status.phase === "Pending";
531
+ const isPending = getAPIKeyPhase(row.status?.conditions || []) === "Pending";
557
532
  const ownerId = row.spec.requestedBy.userId;
558
533
  const canDelete = canDeleteResource(
559
534
  ownerId,
@@ -659,7 +634,7 @@ const ApiKeyManagementTab = ({
659
634
  )), rejectedRequests.length > 0 && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(
660
635
  Table,
661
636
  {
662
- title: "Rejected Requests",
637
+ title: "Denied Requests",
663
638
  options: {
664
639
  paging: rejectedRequests.length > 5,
665
640
  pageSize: 20,
@@ -714,7 +689,7 @@ const ApiKeyManagementTab = ({
714
689
  anchorPosition: menuAnchor || { top: 0, left: 0 }
715
690
  },
716
691
  menuRequest && (() => {
717
- const isPending = !menuRequest.status?.phase || menuRequest.status.phase === "Pending";
692
+ const isPending = getAPIKeyPhase(menuRequest.status?.conditions || []) === "Pending";
718
693
  const ownerId = menuRequest.spec.requestedBy.userId;
719
694
  const canEdit = canUpdateRequest && ownerId === userId && isPending;
720
695
  const items = [];
@@ -745,51 +720,11 @@ const ApiKeyManagementTab = ({
745
720
  {
746
721
  open: deleteDialogState.open,
747
722
  title: "Delete Request",
748
- description: `Are you sure you want to delete this ${deleteDialogState.request?.status?.phase === "Approved" ? "API key" : "request"}?`,
723
+ description: `Are you sure you want to delete this ${getAPIKeyPhase(deleteDialogState.request?.status?.conditions || []) === "Approved" ? "API key" : "request"}?`,
749
724
  deleting: deleting !== null,
750
725
  onConfirm: handleDeleteConfirm,
751
726
  onCancel: handleDeleteCancel
752
727
  }
753
- ), /* @__PURE__ */ React.createElement(
754
- Dialog,
755
- {
756
- open: showOnceWarningOpen,
757
- onClose: () => {
758
- setShowOnceWarningOpen(false);
759
- setPendingKeyReveal(null);
760
- },
761
- maxWidth: "sm"
762
- },
763
- /* @__PURE__ */ React.createElement(DialogTitle, null, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(WarningIcon, { color: "primary", style: { marginRight: 8 } }), "View API Key")),
764
- /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", paragraph: true }, "This API key can only be viewed ", /* @__PURE__ */ React.createElement("strong", null, "once"), ". After you reveal it, you will not be able to retrieve it again."), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Make sure to copy and store it securely before closing this view.")),
765
- /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
766
- Button,
767
- {
768
- onClick: () => {
769
- setShowOnceWarningOpen(false);
770
- setPendingKeyReveal(null);
771
- }
772
- },
773
- "Cancel"
774
- ), /* @__PURE__ */ React.createElement(
775
- Button,
776
- {
777
- variant: "contained",
778
- color: "primary",
779
- onClick: () => {
780
- if (pendingKeyReveal) {
781
- fetchApiKeyFromSecret(
782
- pendingKeyReveal.namespace,
783
- pendingKeyReveal.name
784
- );
785
- toggleVisibility(pendingKeyReveal.name);
786
- }
787
- setShowOnceWarningOpen(false);
788
- setPendingKeyReveal(null);
789
- }
790
- },
791
- "Reveal API Key"
792
- ))
793
728
  ));
794
729
  };
795
730