@kuadrant/kuadrant-backstage-plugin-frontend 0.0.2-dev-39963ad → 0.0.2-dev-30af554

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 (54) hide show
  1. package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js +153 -39
  2. package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js.map +1 -1
  3. package/dist/components/ApiProductInfoCard/ApiProductInfoCard.esm.js +35 -4
  4. package/dist/components/ApiProductInfoCard/ApiProductInfoCard.esm.js.map +1 -1
  5. package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js +114 -32
  6. package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js.map +1 -1
  7. package/dist/components/EditAPIKeyRequestDialog/EditAPIKeyRequestDialog.esm.js +106 -0
  8. package/dist/components/EditAPIKeyRequestDialog/EditAPIKeyRequestDialog.esm.js.map +1 -0
  9. package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js +8 -2
  10. package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js.map +1 -1
  11. package/dist/components/KuadrantPage/KuadrantPage.esm.js +40 -9
  12. package/dist/components/KuadrantPage/KuadrantPage.esm.js.map +1 -1
  13. package/dist/components/MyApiKeysCard/MyApiKeysCard.esm.js +169 -11
  14. package/dist/components/MyApiKeysCard/MyApiKeysCard.esm.js.map +1 -1
  15. package/dist/index.d.ts +57 -7
  16. package/dist/index.esm.js +1 -1
  17. package/dist/permissions.esm.js +43 -13
  18. package/dist/permissions.esm.js.map +1 -1
  19. package/dist-scalprum/{internal.plugin-kuadrant.908832be3d1bbb2c9c5b.js → internal.plugin-kuadrant.86c50e4da6b961c623e9.js} +2 -2
  20. package/dist-scalprum/internal.plugin-kuadrant.86c50e4da6b961c623e9.js.map +1 -0
  21. package/dist-scalprum/plugin-manifest.json +2 -2
  22. package/dist-scalprum/static/2120.44884438.chunk.js +3 -0
  23. package/dist-scalprum/static/2120.44884438.chunk.js.LICENSE.txt +9 -0
  24. package/dist-scalprum/static/2120.44884438.chunk.js.map +1 -0
  25. package/dist-scalprum/static/4306.04dfa125.chunk.js +2 -0
  26. package/dist-scalprum/static/4306.04dfa125.chunk.js.map +1 -0
  27. package/dist-scalprum/static/4556.c6bedfc4.chunk.js +3 -0
  28. package/dist-scalprum/static/{4161.aa5fd28d.chunk.js.LICENSE.txt → 4556.c6bedfc4.chunk.js.LICENSE.txt} +0 -10
  29. package/dist-scalprum/static/4556.c6bedfc4.chunk.js.map +1 -0
  30. package/dist-scalprum/static/5453.ee6fb9af.chunk.js +2 -0
  31. package/dist-scalprum/static/5453.ee6fb9af.chunk.js.map +1 -0
  32. package/dist-scalprum/static/6281.14f57b13.chunk.js +2 -0
  33. package/dist-scalprum/static/6281.14f57b13.chunk.js.map +1 -0
  34. package/dist-scalprum/static/7632.646001ce.chunk.js +2 -0
  35. package/dist-scalprum/static/7632.646001ce.chunk.js.map +1 -0
  36. package/dist-scalprum/static/9555.59e77806.chunk.js +2 -0
  37. package/dist-scalprum/static/9555.59e77806.chunk.js.map +1 -0
  38. package/dist-scalprum/static/exposed-PluginRoot.9522bcfa.chunk.js +2 -0
  39. package/dist-scalprum/static/{exposed-PluginRoot.50e173c2.chunk.js.map → exposed-PluginRoot.9522bcfa.chunk.js.map} +1 -1
  40. package/package.json +1 -1
  41. package/dist-scalprum/internal.plugin-kuadrant.908832be3d1bbb2c9c5b.js.map +0 -1
  42. package/dist-scalprum/static/4161.aa5fd28d.chunk.js +0 -3
  43. package/dist-scalprum/static/4161.aa5fd28d.chunk.js.map +0 -1
  44. package/dist-scalprum/static/4306.133aa342.chunk.js +0 -2
  45. package/dist-scalprum/static/4306.133aa342.chunk.js.map +0 -1
  46. package/dist-scalprum/static/5453.d8d305e9.chunk.js +0 -2
  47. package/dist-scalprum/static/5453.d8d305e9.chunk.js.map +0 -1
  48. package/dist-scalprum/static/6281.29f4549b.chunk.js +0 -2
  49. package/dist-scalprum/static/6281.29f4549b.chunk.js.map +0 -1
  50. package/dist-scalprum/static/7632.26afde62.chunk.js +0 -2
  51. package/dist-scalprum/static/7632.26afde62.chunk.js.map +0 -1
  52. package/dist-scalprum/static/8617.d1dc70a8.chunk.js +0 -2
  53. package/dist-scalprum/static/8617.d1dc70a8.chunk.js.map +0 -1
  54. package/dist-scalprum/static/exposed-PluginRoot.50e173c2.chunk.js +0 -2
@@ -1,17 +1,18 @@
1
1
  import React, { useState, useMemo } from 'react';
2
2
  import { useAsync } from 'react-use';
3
3
  import { CodeSnippet, Progress, ResponseErrorPanel, Table } from '@backstage/core-components';
4
- import { Box, Typography, Tabs, Tab, Grid, Button, Dialog, DialogTitle, DialogContent, FormControl, InputLabel, Select, MenuItem, TextField, DialogActions, Chip, IconButton } from '@material-ui/core';
4
+ import { Box, Typography, Tabs, Tab, Grid, Button, Dialog, DialogTitle, DialogContent, FormControl, InputLabel, Select, MenuItem, TextField, DialogActions, Menu, Chip, IconButton, Tooltip } from '@material-ui/core';
5
5
  import { useApi, configApiRef, identityApiRef, fetchApiRef } from '@backstage/core-plugin-api';
6
6
  import { useEntity } from '@backstage/plugin-catalog-react';
7
7
  import VisibilityIcon from '@material-ui/icons/Visibility';
8
8
  import VisibilityOffIcon from '@material-ui/icons/VisibilityOff';
9
- import DeleteIcon from '@material-ui/icons/Delete';
10
9
  import HourglassEmptyIcon from '@material-ui/icons/HourglassEmpty';
11
10
  import CancelIcon from '@material-ui/icons/Cancel';
12
11
  import AddIcon from '@material-ui/icons/Add';
13
- import { kuadrantApiKeyRequestCreatePermission, kuadrantApiKeyDeleteOwnPermission, kuadrantApiKeyDeleteAllPermission } from '../../permissions.esm.js';
12
+ import MoreVertIcon from '@material-ui/icons/MoreVert';
13
+ import { kuadrantApiKeyRequestCreatePermission, kuadrantApiKeyDeleteOwnPermission, kuadrantApiKeyDeleteAllPermission, kuadrantApiKeyRequestUpdateOwnPermission } from '../../permissions.esm.js';
14
14
  import { useKuadrantPermission, canDeleteResource } from '../../utils/permissions.esm.js';
15
+ import { EditAPIKeyRequestDialog } from '../EditAPIKeyRequestDialog/EditAPIKeyRequestDialog.esm.js';
15
16
 
16
17
  const ApiKeyManagementTab = ({ namespace: propNamespace }) => {
17
18
  const { entity } = useEntity();
@@ -28,9 +29,10 @@ const ApiKeyManagementTab = ({ namespace: propNamespace }) => {
28
29
  const [useCase, setUseCase] = useState("");
29
30
  const [creating, setCreating] = useState(false);
30
31
  const [createError, setCreateError] = useState(null);
31
- const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
32
- const [requestToDelete, setRequestToDelete] = useState(null);
33
- const [deleting, setDeleting] = useState(false);
32
+ const [editDialogOpen, setEditDialogOpen] = useState(false);
33
+ const [requestToEdit, setRequestToEdit] = useState(null);
34
+ const [menuAnchor, setMenuAnchor] = useState(null);
35
+ const [menuRequest, setMenuRequest] = useState(null);
34
36
  const apiProductName = entity.metadata.annotations?.["kuadrant.io/apiproduct"] || entity.metadata.name;
35
37
  const namespace = entity.metadata.annotations?.["kuadrant.io/namespace"] || propNamespace || "default";
36
38
  useAsync(async () => {
@@ -78,33 +80,51 @@ const ApiKeyManagementTab = ({ namespace: propNamespace }) => {
78
80
  loading: deleteAllPermissionLoading,
79
81
  error: deleteAllPermissionError
80
82
  } = useKuadrantPermission(kuadrantApiKeyDeleteAllPermission);
81
- const handleDeleteClick = (name, displayName) => {
82
- setRequestToDelete({ name, displayName });
83
- setDeleteDialogOpen(true);
84
- };
85
- const handleDeleteConfirm = async () => {
86
- if (!requestToDelete) return;
87
- setDeleting(true);
83
+ const {
84
+ allowed: canUpdateRequest,
85
+ loading: updateRequestPermissionLoading,
86
+ error: updateRequestPermissionError
87
+ } = useKuadrantPermission(kuadrantApiKeyRequestUpdateOwnPermission);
88
+ const handleDeleteRequest = async (name) => {
88
89
  try {
89
90
  const response = await fetchApi.fetch(
90
- `${backendUrl}/api/kuadrant/requests/${namespace}/${requestToDelete.name}`,
91
+ `${backendUrl}/api/kuadrant/requests/${namespace}/${name}`,
91
92
  { method: "DELETE" }
92
93
  );
93
94
  if (!response.ok) {
94
95
  throw new Error("failed to delete request");
95
96
  }
96
97
  setRefresh((r) => r + 1);
97
- setDeleteDialogOpen(false);
98
- setRequestToDelete(null);
99
98
  } catch (err) {
100
99
  console.error("error deleting request:", err);
101
- } finally {
102
- setDeleting(false);
103
100
  }
104
101
  };
105
- const handleDeleteCancel = () => {
106
- setDeleteDialogOpen(false);
107
- setRequestToDelete(null);
102
+ const handleEditRequest = (request) => {
103
+ setRequestToEdit(request);
104
+ setEditDialogOpen(true);
105
+ };
106
+ const handleEditSuccess = () => {
107
+ setRefresh((r) => r + 1);
108
+ setEditDialogOpen(false);
109
+ setRequestToEdit(null);
110
+ };
111
+ const handleMenuClose = () => {
112
+ setMenuAnchor(null);
113
+ setMenuRequest(null);
114
+ };
115
+ const handleMenuEdit = () => {
116
+ if (!menuRequest) return;
117
+ handleEditRequest(menuRequest);
118
+ handleMenuClose();
119
+ };
120
+ const handleMenuDelete = async () => {
121
+ if (!menuRequest) return;
122
+ const requestName = menuRequest.metadata.name;
123
+ handleMenuClose();
124
+ if (!window.confirm("Are you sure you want to delete this request?")) {
125
+ return;
126
+ }
127
+ await handleDeleteRequest(requestName);
108
128
  };
109
129
  const toggleVisibility = (keyName) => {
110
130
  setVisibleKeys((prev) => {
@@ -166,7 +186,18 @@ const ApiKeyManagementTab = ({ namespace: propNamespace }) => {
166
186
  const DetailPanelContent = ({ request, apiName: api }) => {
167
187
  const [selectedLanguage, setSelectedLanguage] = useState(0);
168
188
  const hostname = request.status?.apiHostname || `${api}.apps.example.com`;
169
- return /* @__PURE__ */ React.createElement(Box, { p: 3, bgcolor: "background.default", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6", gutterBottom: true }, "Usage Examples"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", paragraph: true }, "Use these code examples to test the API with your ", request.spec.planTier, " tier key."), /* @__PURE__ */ React.createElement(Box, { onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement(
189
+ return /* @__PURE__ */ React.createElement(Box, { p: 3, bgcolor: "background.default", onClick: (e) => e.stopPropagation() }, request.spec.useCase && /* @__PURE__ */ React.createElement(Box, { mb: 3 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6", gutterBottom: true }, "Use Case"), /* @__PURE__ */ React.createElement(Box, { p: 2, bgcolor: "background.paper", borderRadius: 1, border: "1px solid rgba(0, 0, 0, 0.12)" }, /* @__PURE__ */ React.createElement(
190
+ Typography,
191
+ {
192
+ variant: "body2",
193
+ style: {
194
+ whiteSpace: "pre-wrap",
195
+ wordBreak: "break-word",
196
+ overflowWrap: "break-word"
197
+ }
198
+ },
199
+ request.spec.useCase
200
+ ))), /* @__PURE__ */ React.createElement(Typography, { variant: "h6", gutterBottom: true }, "Usage Examples"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", paragraph: true }, "Use these code examples to test the API with your ", request.spec.planTier, " tier key."), /* @__PURE__ */ React.createElement(Box, { onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement(
170
201
  Tabs,
171
202
  {
172
203
  value: selectedLanguage,
@@ -259,9 +290,9 @@ func main() {
259
290
  }
260
291
  )));
261
292
  };
262
- const loading = requestsLoading || plansLoading || createRequestPermissionLoading || deleteOwnPermissionLoading || deleteAllPermissionLoading;
293
+ const loading = requestsLoading || plansLoading || createRequestPermissionLoading || deleteOwnPermissionLoading || deleteAllPermissionLoading || updateRequestPermissionLoading;
263
294
  const error = requestsError || plansError;
264
- const permissionError = createRequestPermissionError || deleteOwnPermissionError || deleteAllPermissionError;
295
+ const permissionError = createRequestPermissionError || deleteOwnPermissionError || deleteAllPermissionError || updateRequestPermissionError;
265
296
  if (loading) {
266
297
  return /* @__PURE__ */ React.createElement(Progress, null);
267
298
  }
@@ -269,7 +300,7 @@ func main() {
269
300
  return /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error });
270
301
  }
271
302
  if (permissionError) {
272
- const failedPermission = createRequestPermissionError ? "kuadrant.apikeyrequest.create" : deleteOwnPermissionError ? "kuadrant.apikey.delete.own" : deleteAllPermissionError ? "kuadrant.apikey.delete.all" : "unknown";
303
+ const failedPermission = createRequestPermissionError ? "kuadrant.apikeyrequest.create" : deleteOwnPermissionError ? "kuadrant.apikey.delete.own" : deleteAllPermissionError ? "kuadrant.apikey.delete.all" : updateRequestPermissionError ? "kuadrant.apikeyrequest.update.own" : "unknown";
273
304
  return /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Typography, { color: "error" }, "Unable to check permissions: ", permissionError.message), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Permission: ", failedPermission), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Please try again or contact your administrator"));
274
305
  }
275
306
  const myRequests = requests || [];
@@ -316,7 +347,7 @@ func main() {
316
347
  }
317
348
  },
318
349
  {
319
- title: "Actions",
350
+ title: "",
320
351
  field: "actions",
321
352
  searchable: false,
322
353
  render: (row) => {
@@ -327,11 +358,17 @@ func main() {
327
358
  IconButton,
328
359
  {
329
360
  size: "small",
330
- onClick: () => handleDeleteClick(row.metadata.name, apiProductName),
331
- color: "secondary",
332
- title: "Revoke access and delete key"
361
+ onClick: (e) => {
362
+ e.stopPropagation();
363
+ const rect = e.currentTarget.getBoundingClientRect();
364
+ setMenuAnchor({ top: rect.bottom, left: rect.left });
365
+ setMenuRequest(row);
366
+ },
367
+ title: "Actions",
368
+ "aria-controls": menuAnchor ? "actions-menu" : undefined,
369
+ "aria-haspopup": "true"
333
370
  },
334
- /* @__PURE__ */ React.createElement(DeleteIcon, null)
371
+ /* @__PURE__ */ React.createElement(MoreVertIcon, null)
335
372
  );
336
373
  }
337
374
  }
@@ -362,7 +399,24 @@ func main() {
362
399
  {
363
400
  title: "Use Case",
364
401
  field: "spec.useCase",
365
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.spec.useCase || "-")
402
+ render: (row) => {
403
+ if (!row.spec.useCase) {
404
+ return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "-");
405
+ }
406
+ return /* @__PURE__ */ React.createElement(Tooltip, { title: row.spec.useCase, placement: "top" }, /* @__PURE__ */ React.createElement(
407
+ Typography,
408
+ {
409
+ variant: "body2",
410
+ style: {
411
+ maxWidth: "200px",
412
+ overflow: "hidden",
413
+ textOverflow: "ellipsis",
414
+ whiteSpace: "nowrap"
415
+ }
416
+ },
417
+ row.spec.useCase
418
+ ));
419
+ }
366
420
  },
367
421
  {
368
422
  title: "Requested",
@@ -380,29 +434,57 @@ func main() {
380
434
  {
381
435
  title: "Reason",
382
436
  field: "status.reason",
383
- render: (row) => /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, row.status?.reason || "-")
437
+ render: (row) => {
438
+ if (!row.status?.reason) {
439
+ return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "-");
440
+ }
441
+ return /* @__PURE__ */ React.createElement(Tooltip, { title: row.status.reason, placement: "top" }, /* @__PURE__ */ React.createElement(
442
+ Typography,
443
+ {
444
+ variant: "body2",
445
+ style: {
446
+ maxWidth: "200px",
447
+ overflow: "hidden",
448
+ textOverflow: "ellipsis",
449
+ whiteSpace: "nowrap"
450
+ }
451
+ },
452
+ row.status.reason
453
+ ));
454
+ }
384
455
  },
385
456
  {
386
- title: "Actions",
457
+ title: "",
387
458
  field: "actions",
388
459
  searchable: false,
389
460
  render: (row) => {
390
461
  const isPending = !row.status?.phase || row.status.phase === "Pending";
391
462
  const ownerId = row.spec.requestedBy.userId;
392
463
  const canDelete = canDeleteResource(ownerId, userId, canDeleteOwnKey, canDeleteAllKeys);
393
- if (!isPending || !canDelete) return null;
464
+ const canEdit = canUpdateRequest && ownerId === userId;
465
+ if (!isPending || !canEdit && !canDelete) return null;
394
466
  return /* @__PURE__ */ React.createElement(
395
467
  IconButton,
396
468
  {
397
469
  size: "small",
398
- onClick: () => handleDeleteClick(row.metadata.name, apiProductName),
399
- color: "secondary"
470
+ onClick: (e) => {
471
+ e.stopPropagation();
472
+ const rect = e.currentTarget.getBoundingClientRect();
473
+ setMenuAnchor({ top: rect.bottom, left: rect.left });
474
+ setMenuRequest(row);
475
+ },
476
+ title: "Actions",
477
+ "aria-controls": menuAnchor ? "actions-menu" : undefined,
478
+ "aria-haspopup": "true"
400
479
  },
401
- /* @__PURE__ */ React.createElement(DeleteIcon, null)
480
+ /* @__PURE__ */ React.createElement(MoreVertIcon, null)
402
481
  );
403
482
  }
404
483
  }
405
484
  ];
485
+ const pendingRequestColumns = requestColumns.filter(
486
+ (col) => col.title !== "Reviewed" && col.title !== "Reason"
487
+ );
406
488
  return /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3, direction: "column" }, canCreateRequest && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Box, { display: "flex", flexDirection: "column", alignItems: "flex-end", mb: 2 }, /* @__PURE__ */ React.createElement(
407
489
  Button,
408
490
  {
@@ -421,7 +503,7 @@ func main() {
421
503
  paging: false,
422
504
  search: false
423
505
  },
424
- columns: requestColumns,
506
+ columns: pendingRequestColumns,
425
507
  data: pendingRequests
426
508
  }
427
509
  )), rejectedRequests.length > 0 && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(
@@ -479,7 +561,39 @@ func main() {
479
561
  disabled: !selectedPlan || creating
480
562
  },
481
563
  creating ? "Submitting..." : "Submit Request"
482
- ))), /* @__PURE__ */ React.createElement(Dialog, { open: deleteDialogOpen, onClose: handleDeleteCancel }, /* @__PURE__ */ React.createElement(DialogTitle, null, "Confirm Delete"), /* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Typography, null, "Are you sure you want to revoke access and delete your API key for ", /* @__PURE__ */ React.createElement("strong", null, requestToDelete?.displayName), "?"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary", style: { marginTop: 8 } }, "This action cannot be undone. You will need to request access again if you want to use this API in the future.")), /* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: handleDeleteCancel, color: "primary" }, "Cancel"), /* @__PURE__ */ React.createElement(Button, { onClick: handleDeleteConfirm, color: "secondary", disabled: deleting }, deleting ? "Deleting..." : "Delete"))));
564
+ ))), /* @__PURE__ */ React.createElement(
565
+ Menu,
566
+ {
567
+ id: "actions-menu",
568
+ open: Boolean(menuAnchor),
569
+ onClose: handleMenuClose,
570
+ anchorReference: "anchorPosition",
571
+ anchorPosition: menuAnchor || { top: 0, left: 0 }
572
+ },
573
+ menuRequest && (() => {
574
+ const isPending = !menuRequest.status?.phase || menuRequest.status.phase === "Pending";
575
+ const ownerId = menuRequest.spec.requestedBy.userId;
576
+ const canEdit = canUpdateRequest && ownerId === userId && isPending;
577
+ const items = [];
578
+ if (canEdit) {
579
+ items.push(/* @__PURE__ */ React.createElement(MenuItem, { key: "edit", onClick: handleMenuEdit }, "Edit"));
580
+ }
581
+ items.push(/* @__PURE__ */ React.createElement(MenuItem, { key: "delete", onClick: handleMenuDelete }, "Delete"));
582
+ return items;
583
+ })()
584
+ ), requestToEdit && /* @__PURE__ */ React.createElement(
585
+ EditAPIKeyRequestDialog,
586
+ {
587
+ open: editDialogOpen,
588
+ onClose: () => {
589
+ setEditDialogOpen(false);
590
+ setRequestToEdit(null);
591
+ },
592
+ onSuccess: handleEditSuccess,
593
+ request: requestToEdit,
594
+ availablePlans: plans
595
+ }
596
+ ));
483
597
  };
484
598
 
485
599
  export { ApiKeyManagementTab };
@@ -1 +1 @@
1
- {"version":3,"file":"ApiKeyManagementTab.esm.js","sources":["../../../src/components/ApiKeyManagementTab/ApiKeyManagementTab.tsx"],"sourcesContent":["import React, { useState, useMemo } from 'react';\nimport { useAsync } from 'react-use';\nimport {\n Table,\n TableColumn,\n Progress,\n ResponseErrorPanel,\n CodeSnippet,\n} from '@backstage/core-components';\nimport {\n IconButton,\n Typography,\n Box,\n Chip,\n Grid,\n Button,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n Select,\n MenuItem,\n FormControl,\n InputLabel,\n Tabs,\n Tab,\n} from '@material-ui/core';\nimport { useApi, configApiRef, identityApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport VisibilityOffIcon from '@material-ui/icons/VisibilityOff';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport HourglassEmptyIcon from '@material-ui/icons/HourglassEmpty';\nimport CancelIcon from '@material-ui/icons/Cancel';\nimport AddIcon from '@material-ui/icons/Add';\nimport { APIKeyRequest } from '../../types/api-management';\nimport {\n kuadrantApiKeyRequestCreatePermission,\n kuadrantApiKeyDeleteOwnPermission,\n kuadrantApiKeyDeleteAllPermission,\n} from '../../permissions';\nimport { useKuadrantPermission, canDeleteResource } from '../../utils/permissions';\n\ninterface APIProduct {\n metadata: {\n name: string;\n namespace: string;\n };\n spec: {\n plans?: Array<{\n tier: string;\n description?: string;\n limits?: any;\n }>;\n };\n}\n\ninterface Plan {\n tier: string;\n limits: any;\n}\n\nexport interface ApiKeyManagementTabProps {\n namespace?: string;\n}\n\nexport const ApiKeyManagementTab = ({ namespace: propNamespace }: ApiKeyManagementTabProps) => {\n const { entity } = useEntity();\n const config = useApi(configApiRef);\n const identityApi = useApi(identityApiRef);\n const fetchApi = useApi(fetchApiRef);\n const backendUrl = config.getString('backend.baseUrl');\n const [visibleKeys, setVisibleKeys] = useState<Set<string>>(new Set());\n const [refresh, setRefresh] = useState(0);\n const [userId, setUserId] = useState<string>('guest');\n const [userEmail, setUserEmail] = useState<string>('');\n const [open, setOpen] = useState(false);\n const [selectedPlan, setSelectedPlan] = useState('');\n const [useCase, setUseCase] = useState('');\n const [creating, setCreating] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [requestToDelete, setRequestToDelete] = useState<{ name: string; displayName: string } | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n // get apiproduct name from entity annotation (set by entity provider)\n const apiProductName = entity.metadata.annotations?.['kuadrant.io/apiproduct'] || entity.metadata.name;\n const namespace = entity.metadata.annotations?.['kuadrant.io/namespace'] || propNamespace || 'default';\n\n useAsync(async () => {\n const identity = await identityApi.getBackstageIdentity();\n const profile = await identityApi.getProfileInfo();\n setUserId(identity.userEntityRef.split('/')[1] || 'guest');\n setUserEmail(profile.email || '');\n }, [identityApi]);\n\n const { value: requests, loading: requestsLoading, error: requestsError } = useAsync(async () => {\n const response = await fetchApi.fetch(\n `${backendUrl}/api/kuadrant/requests/my?namespace=${namespace}`\n );\n if (!response.ok) {\n throw new Error('failed to fetch requests');\n }\n const data = await response.json();\n // filter by apiproduct name, not httproute name\n return (data.items || []).filter(\n (r: APIKeyRequest) => r.spec.apiName === apiProductName && r.spec.apiNamespace === namespace\n );\n }, [apiProductName, namespace, refresh, fetchApi, backendUrl]);\n\n const { value: apiProduct, loading: plansLoading, error: plansError } = useAsync(async () => {\n const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/apiproducts`);\n if (!response.ok) {\n throw new Error('failed to fetch api products');\n }\n const data = await response.json();\n\n const product = data.items?.find((p: APIProduct) =>\n p.metadata.namespace === namespace &&\n p.metadata.name === apiProductName\n );\n\n return product;\n }, [namespace, apiProductName, fetchApi]);\n\n // check permissions with resource reference once we have the apiproduct\n const resourceRef = apiProduct ? `apiproduct:${apiProduct.metadata.namespace}/${apiProduct.metadata.name}` : undefined;\n\n const {\n allowed: canCreateRequest,\n loading: createRequestPermissionLoading,\n error: createRequestPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyRequestCreatePermission, resourceRef);\n\n const {\n allowed: canDeleteOwnKey,\n loading: deleteOwnPermissionLoading,\n error: deleteOwnPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyDeleteOwnPermission);\n\n const {\n allowed: canDeleteAllKeys,\n loading: deleteAllPermissionLoading,\n error: deleteAllPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyDeleteAllPermission);\n\n const handleDeleteClick = (name: string, displayName: string) => {\n setRequestToDelete({ name, displayName });\n setDeleteDialogOpen(true);\n };\n\n const handleDeleteConfirm = async () => {\n if (!requestToDelete) return;\n\n setDeleting(true);\n try {\n const response = await fetchApi.fetch(\n `${backendUrl}/api/kuadrant/requests/${namespace}/${requestToDelete.name}`,\n { method: 'DELETE' }\n );\n if (!response.ok) {\n throw new Error('failed to delete request');\n }\n setRefresh(r => r + 1);\n setDeleteDialogOpen(false);\n setRequestToDelete(null);\n } catch (err) {\n console.error('error deleting request:', err);\n } finally {\n setDeleting(false);\n }\n };\n\n const handleDeleteCancel = () => {\n setDeleteDialogOpen(false);\n setRequestToDelete(null);\n };\n\n const toggleVisibility = (keyName: string) => {\n setVisibleKeys(prev => {\n const newSet = new Set(prev);\n if (newSet.has(keyName)) {\n newSet.delete(keyName);\n } else {\n newSet.add(keyName);\n }\n return newSet;\n });\n };\n\n const handleRequestAccess = async () => {\n if (!selectedPlan) return;\n\n setCreating(true);\n setCreateError(null);\n try {\n const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/requests`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n apiName: apiProductName,\n apiNamespace: namespace,\n userId,\n userEmail,\n planTier: selectedPlan,\n useCase: useCase.trim() || '',\n namespace,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(errorData.error || `failed to create request: ${response.status}`);\n }\n\n setOpen(false);\n setSelectedPlan('');\n setUseCase('');\n setRefresh(r => r + 1);\n } catch (err) {\n console.error('error creating api key request:', err);\n setCreateError(err instanceof Error ? err.message : 'unknown error occurred');\n } finally {\n setCreating(false);\n }\n };\n\n const detailPanelConfig = useMemo(() => [\n {\n render: (data: any) => {\n // backstage Table wraps the data in { rowData: actualData }\n const request = data.rowData as APIKeyRequest;\n if (!request?.metadata?.name) {\n return <Box />;\n }\n\n return <DetailPanelContent request={request} apiName={apiProductName} />;\n },\n },\n ], [apiProductName]);\n\n // separate component to isolate state\n const DetailPanelContent = ({ request, apiName: api }: { request: APIKeyRequest; apiName: string }) => {\n const [selectedLanguage, setSelectedLanguage] = useState(0);\n const hostname = request.status?.apiHostname || `${api}.apps.example.com`;\n\n return (\n <Box p={3} bgcolor=\"background.default\" onClick={(e) => e.stopPropagation()}>\n <Typography variant=\"h6\" gutterBottom>\n Usage Examples\n </Typography>\n <Typography variant=\"body2\" paragraph>\n Use these code examples to test the API with your {request.spec.planTier} tier key.\n </Typography>\n <Box onClick={(e) => e.stopPropagation()}>\n <Tabs\n value={selectedLanguage}\n onChange={(e, newValue) => {\n e.stopPropagation();\n setSelectedLanguage(newValue);\n }}\n indicatorColor=\"primary\"\n >\n <Tab label=\"cURL\" onClick={(e) => e.stopPropagation()} />\n <Tab label=\"Node.js\" onClick={(e) => e.stopPropagation()} />\n <Tab label=\"Python\" onClick={(e) => e.stopPropagation()} />\n <Tab label=\"Go\" onClick={(e) => e.stopPropagation()} />\n </Tabs>\n </Box>\n <Box mt={2}>\n {selectedLanguage === 0 && (\n <CodeSnippet\n text={`curl -X GET https://${hostname}/api/v1/endpoint \\\\\n -H \"Authorization: Bearer ${request.status?.apiKey}\"`}\n language=\"bash\"\n showCopyCodeButton\n />\n )}\n {selectedLanguage === 1 && (\n <CodeSnippet\n text={`const fetch = require('node-fetch');\n\nconst apiKey = '${request.status?.apiKey}';\nconst endpoint = 'https://${hostname}/api/v1/endpoint';\n\nfetch(endpoint, {\n method: 'GET',\n headers: {\n 'Authorization': \\`Bearer \\${apiKey}\\`\n }\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`}\n language=\"javascript\"\n showCopyCodeButton\n />\n )}\n {selectedLanguage === 2 && (\n <CodeSnippet\n text={`import requests\n\napi_key = '${request.status?.apiKey}'\nendpoint = 'https://${hostname}/api/v1/endpoint'\n\nheaders = {\n 'Authorization': f'Bearer {api_key}'\n}\n\nresponse = requests.get(endpoint, headers=headers)\nprint(response.json())`}\n language=\"python\"\n showCopyCodeButton\n />\n )}\n {selectedLanguage === 3 && (\n <CodeSnippet\n text={`package main\n\nimport (\n \"fmt\"\n \"net/http\"\n \"io\"\n)\n\nfunc main() {\n apiKey := \"${request.status?.apiKey}\"\n endpoint := \"https://${hostname}/api/v1/endpoint\"\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", endpoint, nil)\n req.Header.Add(\"Authorization\", \"Bearer \" + apiKey)\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println(\"Error:\", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`}\n language=\"go\"\n showCopyCodeButton\n />\n )}\n </Box>\n </Box>\n );\n };\n\n const loading = requestsLoading || plansLoading || createRequestPermissionLoading || deleteOwnPermissionLoading || deleteAllPermissionLoading;\n const error = requestsError || plansError;\n const permissionError = createRequestPermissionError || deleteOwnPermissionError || deleteAllPermissionError;\n\n if (loading) {\n return <Progress />;\n }\n\n if (error) {\n return <ResponseErrorPanel error={error} />;\n }\n\n if (permissionError) {\n const failedPermission = createRequestPermissionError ? 'kuadrant.apikeyrequest.create' :\n deleteOwnPermissionError ? 'kuadrant.apikey.delete.own' :\n deleteAllPermissionError ? 'kuadrant.apikey.delete.all' : 'unknown';\n return (\n <Box p={2}>\n <Typography color=\"error\">\n Unable to check permissions: {permissionError.message}\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Permission: {failedPermission}\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Please try again or contact your administrator\n </Typography>\n </Box>\n );\n }\n\n const myRequests = (requests || []) as APIKeyRequest[];\n const plans = (apiProduct?.spec?.plans || []) as Plan[];\n\n const pendingRequests = myRequests.filter(r => !r.status?.phase || r.status.phase === 'Pending');\n const approvedRequests = myRequests.filter(r => r.status?.phase === 'Approved');\n const rejectedRequests = myRequests.filter(r => r.status?.phase === 'Rejected');\n\n const approvedColumns: TableColumn<APIKeyRequest>[] = [\n {\n title: 'Plan Tier',\n field: 'spec.planTier',\n render: (row: APIKeyRequest) => (\n <Chip label={row.spec.planTier} color=\"primary\" size=\"small\" />\n ),\n },\n {\n title: 'Approved',\n field: 'status.reviewedAt',\n render: (row: APIKeyRequest) => (\n <Typography variant=\"body2\">\n {row.status?.reviewedAt ? new Date(row.status.reviewedAt).toLocaleDateString() : '-'}\n </Typography>\n ),\n },\n {\n title: 'API Key',\n field: 'status.apiKey',\n searchable: false,\n render: (row: APIKeyRequest) => {\n const isVisible = visibleKeys.has(row.metadata.name);\n const apiKey = row.status?.apiKey || 'N/A';\n\n return (\n <Box display=\"flex\" alignItems=\"center\">\n <Typography\n variant=\"body2\"\n style={{\n fontFamily: 'monospace',\n marginRight: 8,\n }}\n >\n {isVisible ? apiKey : '••••••••••••••••'}\n </Typography>\n <IconButton\n size=\"small\"\n onClick={() => toggleVisibility(row.metadata.name)}\n >\n {isVisible ? <VisibilityOffIcon /> : <VisibilityIcon />}\n </IconButton>\n </Box>\n );\n },\n },\n {\n title: 'Actions',\n field: 'actions',\n searchable: false,\n render: (row: APIKeyRequest) => {\n const ownerId = row.spec.requestedBy.userId;\n const canDelete = canDeleteResource(ownerId, userId, canDeleteOwnKey, canDeleteAllKeys);\n if (!canDelete) return null;\n return (\n <IconButton\n size=\"small\"\n onClick={() => handleDeleteClick(row.metadata.name, apiProductName)}\n color=\"secondary\"\n title=\"Revoke access and delete key\"\n >\n <DeleteIcon />\n </IconButton>\n );\n },\n },\n ];\n\n const requestColumns: TableColumn<APIKeyRequest>[] = [\n {\n title: 'Status',\n field: 'status.phase',\n render: (row: APIKeyRequest) => {\n const phase = row.status?.phase || 'Pending';\n const isPending = phase === 'Pending';\n return (\n <Chip\n label={phase}\n size=\"small\"\n icon={isPending ? <HourglassEmptyIcon /> : <CancelIcon />}\n color={isPending ? 'default' : 'secondary'}\n />\n );\n },\n },\n {\n title: 'Plan Tier',\n field: 'spec.planTier',\n render: (row: APIKeyRequest) => (\n <Chip label={row.spec.planTier} color=\"primary\" size=\"small\" />\n ),\n },\n {\n title: 'Use Case',\n field: 'spec.useCase',\n render: (row: APIKeyRequest) => (\n <Typography variant=\"body2\">{row.spec.useCase || '-'}</Typography>\n ),\n },\n {\n title: 'Requested',\n field: 'spec.requestedAt',\n render: (row: APIKeyRequest) => (\n <Typography variant=\"body2\">\n {row.spec.requestedAt ? new Date(row.spec.requestedAt).toLocaleDateString() : '-'}\n </Typography>\n ),\n },\n {\n title: 'Reviewed',\n field: 'status.reviewedAt',\n render: (row: APIKeyRequest) => {\n if (!row.status?.reviewedAt) return <Typography variant=\"body2\">-</Typography>;\n return (\n <Typography variant=\"body2\">\n {new Date(row.status.reviewedAt).toLocaleDateString()}\n </Typography>\n );\n },\n },\n {\n title: 'Reason',\n field: 'status.reason',\n render: (row: APIKeyRequest) => (\n <Typography variant=\"body2\">{row.status?.reason || '-'}</Typography>\n ),\n },\n {\n title: 'Actions',\n field: 'actions',\n searchable: false,\n render: (row: APIKeyRequest) => {\n const isPending = !row.status?.phase || row.status.phase === 'Pending';\n const ownerId = row.spec.requestedBy.userId;\n const canDelete = canDeleteResource(ownerId, userId, canDeleteOwnKey, canDeleteAllKeys);\n if (!isPending || !canDelete) return null;\n return (\n <IconButton\n size=\"small\"\n onClick={() => handleDeleteClick(row.metadata.name, apiProductName)}\n color=\"secondary\"\n >\n <DeleteIcon />\n </IconButton>\n );\n },\n },\n ];\n\n return (\n <Box p={2}>\n <Grid container spacing={3} direction=\"column\">\n {canCreateRequest && (\n <Grid item>\n <Box display=\"flex\" flexDirection=\"column\" alignItems=\"flex-end\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n startIcon={<AddIcon />}\n onClick={() => setOpen(true)}\n disabled={plans.length === 0}\n >\n Request API Access\n </Button>\n {plans.length === 0 && (\n <Typography variant=\"caption\" color=\"textSecondary\" style={{ marginTop: 4 }}>\n {!apiProduct ? 'API product not found' : 'No plans available'}\n </Typography>\n )}\n </Box>\n </Grid>\n )}\n {pendingRequests.length === 0 && rejectedRequests.length === 0 && approvedRequests.length === 0 && (\n <Grid item>\n <Box p={3} textAlign=\"center\">\n <Typography variant=\"body1\" color=\"textSecondary\">\n No API keys yet. Request access to get started.\n </Typography>\n </Box>\n </Grid>\n )}\n {pendingRequests.length > 0 && (\n <Grid item>\n <Table\n title=\"Pending Requests\"\n options={{\n paging: false,\n search: false,\n }}\n columns={requestColumns}\n data={pendingRequests}\n />\n </Grid>\n )}\n {rejectedRequests.length > 0 && (\n <Grid item>\n <Table\n title=\"Rejected Requests\"\n options={{\n paging: false,\n search: false,\n }}\n columns={requestColumns}\n data={rejectedRequests}\n />\n </Grid>\n )}\n {approvedRequests.length > 0 && (\n <Grid item>\n <Table\n key=\"api-keys-table\"\n title=\"API Keys\"\n options={{\n paging: false,\n search: false,\n }}\n columns={approvedColumns}\n data={approvedRequests}\n detailPanel={detailPanelConfig}\n />\n </Grid>\n )}\n </Grid>\n\n <Dialog open={open} onClose={() => setOpen(false)} maxWidth=\"sm\" fullWidth>\n <DialogTitle>Request API Access</DialogTitle>\n <DialogContent>\n {createError && (\n <Box mb={2} p={2} bgcolor=\"error.main\" color=\"error.contrastText\" borderRadius={1}>\n <Typography variant=\"body2\">{createError}</Typography>\n </Box>\n )}\n <FormControl fullWidth margin=\"normal\">\n <InputLabel>Select Plan Tier</InputLabel>\n <Select\n value={selectedPlan}\n onChange={(e) => setSelectedPlan(e.target.value as string)}\n >\n {plans.map((plan: Plan) => {\n const limitDesc = Object.entries(plan.limits || {})\n .map(([key, val]) => `${val} per ${key}`)\n .join(', ');\n return (\n <MenuItem key={plan.tier} value={plan.tier}>\n {plan.tier} {limitDesc ? `(${limitDesc})` : ''}\n </MenuItem>\n );\n })}\n </Select>\n </FormControl>\n <TextField\n label=\"Use Case (optional)\"\n placeholder=\"Describe how you plan to use this API\"\n multiline\n rows={3}\n fullWidth\n margin=\"normal\"\n value={useCase}\n onChange={(e) => setUseCase(e.target.value)}\n helperText=\"Explain your intended use of this API for admin review\"\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setOpen(false)}>Cancel</Button>\n <Button\n onClick={handleRequestAccess}\n color=\"primary\"\n disabled={!selectedPlan || creating}\n >\n {creating ? 'Submitting...' : 'Submit Request'}\n </Button>\n </DialogActions>\n </Dialog>\n\n <Dialog open={deleteDialogOpen} onClose={handleDeleteCancel}>\n <DialogTitle>Confirm Delete</DialogTitle>\n <DialogContent>\n <Typography>\n Are you sure you want to revoke access and delete your API key for <strong>{requestToDelete?.displayName}</strong>?\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" style={{ marginTop: 8 }}>\n This action cannot be undone. You will need to request access again if you want to use this API in the future.\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleDeleteCancel} color=\"primary\">\n Cancel\n </Button>\n <Button onClick={handleDeleteConfirm} color=\"secondary\" disabled={deleting}>\n {deleting ? 'Deleting...' : 'Delete'}\n </Button>\n </DialogActions>\n </Dialog>\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ApiKeyManagementTab.esm.js","sources":["../../../src/components/ApiKeyManagementTab/ApiKeyManagementTab.tsx"],"sourcesContent":["import React, { useState, useMemo } from 'react';\nimport { useAsync } from 'react-use';\nimport {\n Table,\n TableColumn,\n Progress,\n ResponseErrorPanel,\n CodeSnippet,\n} from '@backstage/core-components';\nimport {\n IconButton,\n Typography,\n Box,\n Chip,\n Grid,\n Button,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n Select,\n MenuItem,\n FormControl,\n InputLabel,\n Tabs,\n Tab,\n Menu,\n Tooltip,\n} from '@material-ui/core';\nimport { useApi, configApiRef, identityApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport VisibilityOffIcon from '@material-ui/icons/VisibilityOff';\nimport HourglassEmptyIcon from '@material-ui/icons/HourglassEmpty';\nimport CancelIcon from '@material-ui/icons/Cancel';\nimport AddIcon from '@material-ui/icons/Add';\nimport MoreVertIcon from '@material-ui/icons/MoreVert';\nimport { APIKeyRequest } from '../../types/api-management';\nimport {\n kuadrantApiKeyRequestCreatePermission,\n kuadrantApiKeyDeleteOwnPermission,\n kuadrantApiKeyDeleteAllPermission,\n kuadrantApiKeyRequestUpdateOwnPermission,\n} from '../../permissions';\nimport { useKuadrantPermission, canDeleteResource } from '../../utils/permissions';\nimport { EditAPIKeyRequestDialog } from '../EditAPIKeyRequestDialog';\n\ninterface APIProduct {\n metadata: {\n name: string;\n namespace: string;\n };\n spec: {\n plans?: Array<{\n tier: string;\n description?: string;\n limits?: any;\n }>;\n };\n}\n\ninterface Plan {\n tier: string;\n limits: any;\n}\n\nexport interface ApiKeyManagementTabProps {\n namespace?: string;\n}\n\nexport const ApiKeyManagementTab = ({ namespace: propNamespace }: ApiKeyManagementTabProps) => {\n const { entity } = useEntity();\n const config = useApi(configApiRef);\n const identityApi = useApi(identityApiRef);\n const fetchApi = useApi(fetchApiRef);\n const backendUrl = config.getString('backend.baseUrl');\n const [visibleKeys, setVisibleKeys] = useState<Set<string>>(new Set());\n const [refresh, setRefresh] = useState(0);\n const [userId, setUserId] = useState<string>('guest');\n const [userEmail, setUserEmail] = useState<string>('');\n const [open, setOpen] = useState(false);\n const [selectedPlan, setSelectedPlan] = useState('');\n const [useCase, setUseCase] = useState('');\n const [creating, setCreating] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [editDialogOpen, setEditDialogOpen] = useState(false);\n const [requestToEdit, setRequestToEdit] = useState<APIKeyRequest | null>(null);\n const [menuAnchor, setMenuAnchor] = useState<{ top: number; left: number } | null>(null);\n const [menuRequest, setMenuRequest] = useState<APIKeyRequest | null>(null);\n\n // get apiproduct name from entity annotation (set by entity provider)\n const apiProductName = entity.metadata.annotations?.['kuadrant.io/apiproduct'] || entity.metadata.name;\n const namespace = entity.metadata.annotations?.['kuadrant.io/namespace'] || propNamespace || 'default';\n\n useAsync(async () => {\n const identity = await identityApi.getBackstageIdentity();\n const profile = await identityApi.getProfileInfo();\n setUserId(identity.userEntityRef.split('/')[1] || 'guest');\n setUserEmail(profile.email || '');\n }, [identityApi]);\n\n const { value: requests, loading: requestsLoading, error: requestsError } = useAsync(async () => {\n const response = await fetchApi.fetch(\n `${backendUrl}/api/kuadrant/requests/my?namespace=${namespace}`\n );\n if (!response.ok) {\n throw new Error('failed to fetch requests');\n }\n const data = await response.json();\n // filter by apiproduct name, not httproute name\n return (data.items || []).filter(\n (r: APIKeyRequest) => r.spec.apiName === apiProductName && r.spec.apiNamespace === namespace\n );\n }, [apiProductName, namespace, refresh, fetchApi, backendUrl]);\n\n const { value: apiProduct, loading: plansLoading, error: plansError } = useAsync(async () => {\n const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/apiproducts`);\n if (!response.ok) {\n throw new Error('failed to fetch api products');\n }\n const data = await response.json();\n\n const product = data.items?.find((p: APIProduct) =>\n p.metadata.namespace === namespace &&\n p.metadata.name === apiProductName\n );\n\n return product;\n }, [namespace, apiProductName, fetchApi]);\n\n // check permissions with resource reference once we have the apiproduct\n const resourceRef = apiProduct ? `apiproduct:${apiProduct.metadata.namespace}/${apiProduct.metadata.name}` : undefined;\n\n const {\n allowed: canCreateRequest,\n loading: createRequestPermissionLoading,\n error: createRequestPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyRequestCreatePermission, resourceRef);\n\n const {\n allowed: canDeleteOwnKey,\n loading: deleteOwnPermissionLoading,\n error: deleteOwnPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyDeleteOwnPermission);\n\n const {\n allowed: canDeleteAllKeys,\n loading: deleteAllPermissionLoading,\n error: deleteAllPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyDeleteAllPermission);\n\n const {\n allowed: canUpdateRequest,\n loading: updateRequestPermissionLoading,\n error: updateRequestPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyRequestUpdateOwnPermission);\n\n const handleDeleteRequest = async (name: string) => {\n try {\n const response = await fetchApi.fetch(\n `${backendUrl}/api/kuadrant/requests/${namespace}/${name}`,\n { method: 'DELETE' }\n );\n if (!response.ok) {\n throw new Error('failed to delete request');\n }\n setRefresh(r => r + 1);\n } catch (err) {\n console.error('error deleting request:', err);\n }\n };\n\n const handleEditRequest = (request: APIKeyRequest) => {\n setRequestToEdit(request);\n setEditDialogOpen(true);\n };\n\n const handleEditSuccess = () => {\n setRefresh(r => r + 1);\n setEditDialogOpen(false);\n setRequestToEdit(null);\n };\n\n const handleMenuClose = () => {\n setMenuAnchor(null);\n setMenuRequest(null);\n };\n\n const handleMenuEdit = () => {\n if (!menuRequest) return;\n handleEditRequest(menuRequest);\n handleMenuClose();\n };\n\n const handleMenuDelete = async () => {\n if (!menuRequest) return;\n const requestName = menuRequest.metadata.name;\n handleMenuClose();\n\n if (!window.confirm('Are you sure you want to delete this request?')) {\n return;\n }\n\n await handleDeleteRequest(requestName);\n };\n\n const toggleVisibility = (keyName: string) => {\n setVisibleKeys(prev => {\n const newSet = new Set(prev);\n if (newSet.has(keyName)) {\n newSet.delete(keyName);\n } else {\n newSet.add(keyName);\n }\n return newSet;\n });\n };\n\n const handleRequestAccess = async () => {\n if (!selectedPlan) return;\n\n setCreating(true);\n setCreateError(null);\n try {\n const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/requests`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n apiName: apiProductName,\n apiNamespace: namespace,\n userId,\n userEmail,\n planTier: selectedPlan,\n useCase: useCase.trim() || '',\n namespace,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(errorData.error || `failed to create request: ${response.status}`);\n }\n\n setOpen(false);\n setSelectedPlan('');\n setUseCase('');\n setRefresh(r => r + 1);\n } catch (err) {\n console.error('error creating api key request:', err);\n setCreateError(err instanceof Error ? err.message : 'unknown error occurred');\n } finally {\n setCreating(false);\n }\n };\n\n const detailPanelConfig = useMemo(() => [\n {\n render: (data: any) => {\n // backstage Table wraps the data in { rowData: actualData }\n const request = data.rowData as APIKeyRequest;\n if (!request?.metadata?.name) {\n return <Box />;\n }\n\n return <DetailPanelContent request={request} apiName={apiProductName} />;\n },\n },\n ], [apiProductName]);\n\n // separate component to isolate state\n const DetailPanelContent = ({ request, apiName: api }: { request: APIKeyRequest; apiName: string }) => {\n const [selectedLanguage, setSelectedLanguage] = useState(0);\n const hostname = request.status?.apiHostname || `${api}.apps.example.com`;\n\n return (\n <Box p={3} bgcolor=\"background.default\" onClick={(e) => e.stopPropagation()}>\n {request.spec.useCase && (\n <Box mb={3}>\n <Typography variant=\"h6\" gutterBottom>\n Use Case\n </Typography>\n <Box p={2} bgcolor=\"background.paper\" borderRadius={1} border=\"1px solid rgba(0, 0, 0, 0.12)\">\n <Typography\n variant=\"body2\"\n style={{\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n overflowWrap: 'break-word',\n }}\n >\n {request.spec.useCase}\n </Typography>\n </Box>\n </Box>\n )}\n <Typography variant=\"h6\" gutterBottom>\n Usage Examples\n </Typography>\n <Typography variant=\"body2\" paragraph>\n Use these code examples to test the API with your {request.spec.planTier} tier key.\n </Typography>\n <Box onClick={(e) => e.stopPropagation()}>\n <Tabs\n value={selectedLanguage}\n onChange={(e, newValue) => {\n e.stopPropagation();\n setSelectedLanguage(newValue);\n }}\n indicatorColor=\"primary\"\n >\n <Tab label=\"cURL\" onClick={(e) => e.stopPropagation()} />\n <Tab label=\"Node.js\" onClick={(e) => e.stopPropagation()} />\n <Tab label=\"Python\" onClick={(e) => e.stopPropagation()} />\n <Tab label=\"Go\" onClick={(e) => e.stopPropagation()} />\n </Tabs>\n </Box>\n <Box mt={2}>\n {selectedLanguage === 0 && (\n <CodeSnippet\n text={`curl -X GET https://${hostname}/api/v1/endpoint \\\\\n -H \"Authorization: Bearer ${request.status?.apiKey}\"`}\n language=\"bash\"\n showCopyCodeButton\n />\n )}\n {selectedLanguage === 1 && (\n <CodeSnippet\n text={`const fetch = require('node-fetch');\n\nconst apiKey = '${request.status?.apiKey}';\nconst endpoint = 'https://${hostname}/api/v1/endpoint';\n\nfetch(endpoint, {\n method: 'GET',\n headers: {\n 'Authorization': \\`Bearer \\${apiKey}\\`\n }\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`}\n language=\"javascript\"\n showCopyCodeButton\n />\n )}\n {selectedLanguage === 2 && (\n <CodeSnippet\n text={`import requests\n\napi_key = '${request.status?.apiKey}'\nendpoint = 'https://${hostname}/api/v1/endpoint'\n\nheaders = {\n 'Authorization': f'Bearer {api_key}'\n}\n\nresponse = requests.get(endpoint, headers=headers)\nprint(response.json())`}\n language=\"python\"\n showCopyCodeButton\n />\n )}\n {selectedLanguage === 3 && (\n <CodeSnippet\n text={`package main\n\nimport (\n \"fmt\"\n \"net/http\"\n \"io\"\n)\n\nfunc main() {\n apiKey := \"${request.status?.apiKey}\"\n endpoint := \"https://${hostname}/api/v1/endpoint\"\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", endpoint, nil)\n req.Header.Add(\"Authorization\", \"Bearer \" + apiKey)\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println(\"Error:\", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`}\n language=\"go\"\n showCopyCodeButton\n />\n )}\n </Box>\n </Box>\n );\n };\n\n const loading = requestsLoading || plansLoading || createRequestPermissionLoading || deleteOwnPermissionLoading || deleteAllPermissionLoading || updateRequestPermissionLoading;\n const error = requestsError || plansError;\n const permissionError = createRequestPermissionError || deleteOwnPermissionError || deleteAllPermissionError || updateRequestPermissionError;\n\n if (loading) {\n return <Progress />;\n }\n\n if (error) {\n return <ResponseErrorPanel error={error} />;\n }\n\n if (permissionError) {\n const failedPermission = createRequestPermissionError ? 'kuadrant.apikeyrequest.create' :\n deleteOwnPermissionError ? 'kuadrant.apikey.delete.own' :\n deleteAllPermissionError ? 'kuadrant.apikey.delete.all' :\n updateRequestPermissionError ? 'kuadrant.apikeyrequest.update.own' : 'unknown';\n return (\n <Box p={2}>\n <Typography color=\"error\">\n Unable to check permissions: {permissionError.message}\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Permission: {failedPermission}\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Please try again or contact your administrator\n </Typography>\n </Box>\n );\n }\n\n const myRequests = (requests || []) as APIKeyRequest[];\n const plans = (apiProduct?.spec?.plans || []) as Plan[];\n\n const pendingRequests = myRequests.filter(r => !r.status?.phase || r.status.phase === 'Pending');\n const approvedRequests = myRequests.filter(r => r.status?.phase === 'Approved');\n const rejectedRequests = myRequests.filter(r => r.status?.phase === 'Rejected');\n\n const approvedColumns: TableColumn<APIKeyRequest>[] = [\n {\n title: 'Plan Tier',\n field: 'spec.planTier',\n render: (row: APIKeyRequest) => (\n <Chip label={row.spec.planTier} color=\"primary\" size=\"small\" />\n ),\n },\n {\n title: 'Approved',\n field: 'status.reviewedAt',\n render: (row: APIKeyRequest) => (\n <Typography variant=\"body2\">\n {row.status?.reviewedAt ? new Date(row.status.reviewedAt).toLocaleDateString() : '-'}\n </Typography>\n ),\n },\n {\n title: 'API Key',\n field: 'status.apiKey',\n searchable: false,\n render: (row: APIKeyRequest) => {\n const isVisible = visibleKeys.has(row.metadata.name);\n const apiKey = row.status?.apiKey || 'N/A';\n\n return (\n <Box display=\"flex\" alignItems=\"center\">\n <Typography\n variant=\"body2\"\n style={{\n fontFamily: 'monospace',\n marginRight: 8,\n }}\n >\n {isVisible ? apiKey : '••••••••••••••••'}\n </Typography>\n <IconButton\n size=\"small\"\n onClick={() => toggleVisibility(row.metadata.name)}\n >\n {isVisible ? <VisibilityOffIcon /> : <VisibilityIcon />}\n </IconButton>\n </Box>\n );\n },\n },\n {\n title: '',\n field: 'actions',\n searchable: false,\n render: (row: APIKeyRequest) => {\n const ownerId = row.spec.requestedBy.userId;\n const canDelete = canDeleteResource(ownerId, userId, canDeleteOwnKey, canDeleteAllKeys);\n if (!canDelete) return null;\n return (\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n const rect = e.currentTarget.getBoundingClientRect();\n setMenuAnchor({ top: rect.bottom, left: rect.left });\n setMenuRequest(row);\n }}\n title=\"Actions\"\n aria-controls={menuAnchor ? 'actions-menu' : undefined}\n aria-haspopup=\"true\"\n >\n <MoreVertIcon />\n </IconButton>\n );\n },\n },\n ];\n\n const requestColumns: TableColumn<APIKeyRequest>[] = [\n {\n title: 'Status',\n field: 'status.phase',\n render: (row: APIKeyRequest) => {\n const phase = row.status?.phase || 'Pending';\n const isPending = phase === 'Pending';\n return (\n <Chip\n label={phase}\n size=\"small\"\n icon={isPending ? <HourglassEmptyIcon /> : <CancelIcon />}\n color={isPending ? 'default' : 'secondary'}\n />\n );\n },\n },\n {\n title: 'Plan Tier',\n field: 'spec.planTier',\n render: (row: APIKeyRequest) => (\n <Chip label={row.spec.planTier} color=\"primary\" size=\"small\" />\n ),\n },\n {\n title: 'Use Case',\n field: 'spec.useCase',\n render: (row: APIKeyRequest) => {\n if (!row.spec.useCase) {\n return <Typography variant=\"body2\">-</Typography>;\n }\n return (\n <Tooltip title={row.spec.useCase} placement=\"top\">\n <Typography\n variant=\"body2\"\n style={{\n maxWidth: '200px',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {row.spec.useCase}\n </Typography>\n </Tooltip>\n );\n },\n },\n {\n title: 'Requested',\n field: 'spec.requestedAt',\n render: (row: APIKeyRequest) => (\n <Typography variant=\"body2\">\n {row.spec.requestedAt ? new Date(row.spec.requestedAt).toLocaleDateString() : '-'}\n </Typography>\n ),\n },\n {\n title: 'Reviewed',\n field: 'status.reviewedAt',\n render: (row: APIKeyRequest) => {\n if (!row.status?.reviewedAt) return <Typography variant=\"body2\">-</Typography>;\n return (\n <Typography variant=\"body2\">\n {new Date(row.status.reviewedAt).toLocaleDateString()}\n </Typography>\n );\n },\n },\n {\n title: 'Reason',\n field: 'status.reason',\n render: (row: APIKeyRequest) => {\n if (!row.status?.reason) {\n return <Typography variant=\"body2\">-</Typography>;\n }\n return (\n <Tooltip title={row.status.reason} placement=\"top\">\n <Typography\n variant=\"body2\"\n style={{\n maxWidth: '200px',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {row.status.reason}\n </Typography>\n </Tooltip>\n );\n },\n },\n {\n title: '',\n field: 'actions',\n searchable: false,\n render: (row: APIKeyRequest) => {\n const isPending = !row.status?.phase || row.status.phase === 'Pending';\n const ownerId = row.spec.requestedBy.userId;\n const canDelete = canDeleteResource(ownerId, userId, canDeleteOwnKey, canDeleteAllKeys);\n const canEdit = canUpdateRequest && ownerId === userId;\n if (!isPending || (!canEdit && !canDelete)) return null;\n return (\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n const rect = e.currentTarget.getBoundingClientRect();\n setMenuAnchor({ top: rect.bottom, left: rect.left });\n setMenuRequest(row);\n }}\n title=\"Actions\"\n aria-controls={menuAnchor ? 'actions-menu' : undefined}\n aria-haspopup=\"true\"\n >\n <MoreVertIcon />\n </IconButton>\n );\n },\n },\n ];\n\n // Filter columns for pending requests (no Reviewed or Reason)\n const pendingRequestColumns = requestColumns.filter(\n col => col.title !== 'Reviewed' && col.title !== 'Reason'\n );\n\n return (\n <Box p={2}>\n <Grid container spacing={3} direction=\"column\">\n {canCreateRequest && (\n <Grid item>\n <Box display=\"flex\" flexDirection=\"column\" alignItems=\"flex-end\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n startIcon={<AddIcon />}\n onClick={() => setOpen(true)}\n disabled={plans.length === 0}\n >\n Request API Access\n </Button>\n {plans.length === 0 && (\n <Typography variant=\"caption\" color=\"textSecondary\" style={{ marginTop: 4 }}>\n {!apiProduct ? 'API product not found' : 'No plans available'}\n </Typography>\n )}\n </Box>\n </Grid>\n )}\n {pendingRequests.length === 0 && rejectedRequests.length === 0 && approvedRequests.length === 0 && (\n <Grid item>\n <Box p={3} textAlign=\"center\">\n <Typography variant=\"body1\" color=\"textSecondary\">\n No API keys yet. Request access to get started.\n </Typography>\n </Box>\n </Grid>\n )}\n {pendingRequests.length > 0 && (\n <Grid item>\n <Table\n title=\"Pending Requests\"\n options={{\n paging: false,\n search: false,\n }}\n columns={pendingRequestColumns}\n data={pendingRequests}\n />\n </Grid>\n )}\n {rejectedRequests.length > 0 && (\n <Grid item>\n <Table\n title=\"Rejected Requests\"\n options={{\n paging: false,\n search: false,\n }}\n columns={requestColumns}\n data={rejectedRequests}\n />\n </Grid>\n )}\n {approvedRequests.length > 0 && (\n <Grid item>\n <Table\n key=\"api-keys-table\"\n title=\"API Keys\"\n options={{\n paging: false,\n search: false,\n }}\n columns={approvedColumns}\n data={approvedRequests}\n detailPanel={detailPanelConfig}\n />\n </Grid>\n )}\n </Grid>\n\n <Dialog open={open} onClose={() => setOpen(false)} maxWidth=\"sm\" fullWidth>\n <DialogTitle>Request API Access</DialogTitle>\n <DialogContent>\n {createError && (\n <Box mb={2} p={2} bgcolor=\"error.main\" color=\"error.contrastText\" borderRadius={1}>\n <Typography variant=\"body2\">{createError}</Typography>\n </Box>\n )}\n <FormControl fullWidth margin=\"normal\">\n <InputLabel>Select Plan Tier</InputLabel>\n <Select\n value={selectedPlan}\n onChange={(e) => setSelectedPlan(e.target.value as string)}\n >\n {plans.map((plan: Plan) => {\n const limitDesc = Object.entries(plan.limits || {})\n .map(([key, val]) => `${val} per ${key}`)\n .join(', ');\n return (\n <MenuItem key={plan.tier} value={plan.tier}>\n {plan.tier} {limitDesc ? `(${limitDesc})` : ''}\n </MenuItem>\n );\n })}\n </Select>\n </FormControl>\n <TextField\n label=\"Use Case (optional)\"\n placeholder=\"Describe how you plan to use this API\"\n multiline\n rows={3}\n fullWidth\n margin=\"normal\"\n value={useCase}\n onChange={(e) => setUseCase(e.target.value)}\n helperText=\"Explain your intended use of this API for admin review\"\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setOpen(false)}>Cancel</Button>\n <Button\n onClick={handleRequestAccess}\n color=\"primary\"\n disabled={!selectedPlan || creating}\n >\n {creating ? 'Submitting...' : 'Submit Request'}\n </Button>\n </DialogActions>\n </Dialog>\n\n <Menu\n id=\"actions-menu\"\n open={Boolean(menuAnchor)}\n onClose={handleMenuClose}\n anchorReference=\"anchorPosition\"\n anchorPosition={menuAnchor || { top: 0, left: 0 }}\n >\n {menuRequest && (() => {\n const isPending = !menuRequest.status?.phase || menuRequest.status.phase === 'Pending';\n const ownerId = menuRequest.spec.requestedBy.userId;\n const canEdit = canUpdateRequest && ownerId === userId && isPending;\n\n const items = [];\n if (canEdit) {\n items.push(<MenuItem key=\"edit\" onClick={handleMenuEdit}>Edit</MenuItem>);\n }\n items.push(<MenuItem key=\"delete\" onClick={handleMenuDelete}>Delete</MenuItem>);\n return items;\n })()}\n </Menu>\n\n {requestToEdit && (\n <EditAPIKeyRequestDialog\n open={editDialogOpen}\n onClose={() => {\n setEditDialogOpen(false);\n setRequestToEdit(null);\n }}\n onSuccess={handleEditSuccess}\n request={requestToEdit}\n availablePlans={plans}\n />\n )}\n </Box>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -1,17 +1,27 @@
1
1
  import React from 'react';
2
2
  import { useEntity } from '@backstage/plugin-catalog-react';
3
- import { useApi, configApiRef, fetchApiRef } from '@backstage/core-plugin-api';
3
+ import { useApi, configApiRef, fetchApiRef, identityApiRef } from '@backstage/core-plugin-api';
4
4
  import { InfoCard, Progress, ResponseErrorPanel, Link } from '@backstage/core-components';
5
- import { Typography, Grid, Box, Chip, Table, TableHead, TableRow, TableCell, TableBody } from '@material-ui/core';
5
+ import { Typography, Box, Grid, Chip, Table, TableHead, TableRow, TableCell, TableBody } from '@material-ui/core';
6
6
  import useAsync from 'react-use/lib/useAsync';
7
+ import { useKuadrantPermission } from '../../utils/permissions.esm.js';
8
+ import { kuadrantApiProductReadAllPermission } from '../../permissions.esm.js';
7
9
 
8
10
  const ApiProductInfoCard = () => {
9
11
  const { entity } = useEntity();
10
12
  const config = useApi(configApiRef);
11
13
  const fetchApi = useApi(fetchApiRef);
14
+ const identityApi = useApi(identityApiRef);
12
15
  const backendUrl = config.getString("backend.baseUrl");
16
+ const { allowed: canReadAll, loading: permLoading } = useKuadrantPermission(
17
+ kuadrantApiProductReadAllPermission
18
+ );
13
19
  const namespace = entity.metadata.annotations?.["kuadrant.io/namespace"];
14
20
  const apiProductName = entity.metadata.annotations?.["kuadrant.io/apiproduct"];
21
+ const { value: currentUserId } = useAsync(async () => {
22
+ const identity = await identityApi.getBackstageIdentity();
23
+ return identity.userEntityRef.split("/")[1] || "guest";
24
+ }, [identityApi]);
15
25
  const { value: apiProduct, loading, error } = useAsync(async () => {
16
26
  if (!namespace || !apiProductName) {
17
27
  return null;
@@ -19,14 +29,27 @@ const ApiProductInfoCard = () => {
19
29
  const response = await fetchApi.fetch(
20
30
  `${backendUrl}/api/kuadrant/apiproducts/${namespace}/${apiProductName}`
21
31
  );
32
+ if (!response.ok) {
33
+ const errorData = await response.json();
34
+ throw new Error(errorData.error || `Failed to fetch API product: ${response.status}`);
35
+ }
22
36
  return await response.json();
23
37
  }, [backendUrl, fetchApi, namespace, apiProductName]);
38
+ const owner = apiProduct?.metadata?.annotations?.["backstage.io/owner"];
39
+ const ownerUserId = owner?.split("/")[1];
40
+ const canView = canReadAll || currentUserId && ownerUserId === currentUserId;
24
41
  if (!namespace || !apiProductName) {
25
42
  return /* @__PURE__ */ React.createElement(InfoCard, { title: "API Product Information" }, /* @__PURE__ */ React.createElement(Typography, null, "No APIProduct linked to this API entity"));
26
43
  }
27
- if (loading) {
44
+ if (loading || permLoading) {
28
45
  return /* @__PURE__ */ React.createElement(InfoCard, { title: "API Product Information" }, /* @__PURE__ */ React.createElement(Progress, null));
29
46
  }
47
+ if (apiProduct && !canView) {
48
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: "API Product Information" }, /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "You don't have permission to view this API product's details. Only the API owner or users with admin permissions can view this information.")));
49
+ }
50
+ if (error && error.message.includes("you can only read your own")) {
51
+ return /* @__PURE__ */ React.createElement(InfoCard, { title: "API Product Information" }, /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "You don't have permission to view this API product's details. Only the API owner or users with admin permissions can view this information.")));
52
+ }
30
53
  if (error) {
31
54
  return /* @__PURE__ */ React.createElement(InfoCard, { title: "API Product Information" }, /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error }));
32
55
  }
@@ -34,7 +57,15 @@ const ApiProductInfoCard = () => {
34
57
  return /* @__PURE__ */ React.createElement(InfoCard, { title: "API Product Information" }, /* @__PURE__ */ React.createElement(Typography, null, "APIProduct not found"));
35
58
  }
36
59
  const { spec } = apiProduct;
37
- return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "API Product Details" }, /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6", gutterBottom: true }, spec.displayName || apiProductName), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary", paragraph: true }, spec.description), /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", flexWrap: "wrap", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React.createElement("strong", null, "Version:"), " ", spec.version || "v1"), spec.tags && spec.tags.length > 0 && /* @__PURE__ */ React.createElement(Box, { display: "flex", ml: 2, style: { gap: 4 } }, spec.tags.map((tag) => /* @__PURE__ */ React.createElement(Chip, { key: tag, label: tag, size: "small" }))))))), spec.plans && spec.plans.length > 0 && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Available Plans" }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Tier"), /* @__PURE__ */ React.createElement(TableCell, null, "Description"), /* @__PURE__ */ React.createElement(TableCell, null, "Rate Limits"))), /* @__PURE__ */ React.createElement(TableBody, null, spec.plans.map((plan) => /* @__PURE__ */ React.createElement(TableRow, { key: plan.tier }, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(
60
+ return /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "API Product Details" }, /* @__PURE__ */ React.createElement(Box, { p: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "h6", gutterBottom: true }, spec.displayName || apiProductName), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary", paragraph: true }, spec.description), /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center", flexWrap: "wrap", style: { gap: 8 } }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, /* @__PURE__ */ React.createElement("strong", null, "Version:"), " ", spec.version || "v1"), spec.tags && spec.tags.length > 0 && /* @__PURE__ */ React.createElement(Box, { display: "flex", ml: 2, style: { gap: 4 } }, spec.tags.map((tag) => /* @__PURE__ */ React.createElement(Chip, { key: tag, label: tag, size: "small" })))), /* @__PURE__ */ React.createElement(Box, { mt: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2", component: "div" }, /* @__PURE__ */ React.createElement("strong", null, "Approval Mode:"), " ", /* @__PURE__ */ React.createElement(
61
+ Chip,
62
+ {
63
+ label: (spec.approvalMode || "manual") === "automatic" ? "Automatic" : "Manual",
64
+ size: "small",
65
+ color: (spec.approvalMode || "manual") === "automatic" ? "primary" : "default",
66
+ style: { marginLeft: 8 }
67
+ }
68
+ )), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary", style: { marginTop: 4, display: "block" } }, (spec.approvalMode || "manual") === "automatic" ? "API keys are created immediately when requested" : "API keys require manual approval before creation"))))), spec.plans && spec.plans.length > 0 && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Available Plans" }, /* @__PURE__ */ React.createElement(Table, { size: "small" }, /* @__PURE__ */ React.createElement(TableHead, null, /* @__PURE__ */ React.createElement(TableRow, null, /* @__PURE__ */ React.createElement(TableCell, null, "Tier"), /* @__PURE__ */ React.createElement(TableCell, null, "Description"), /* @__PURE__ */ React.createElement(TableCell, null, "Rate Limits"))), /* @__PURE__ */ React.createElement(TableBody, null, spec.plans.map((plan) => /* @__PURE__ */ React.createElement(TableRow, { key: plan.tier }, /* @__PURE__ */ React.createElement(TableCell, null, /* @__PURE__ */ React.createElement(
38
69
  Chip,
39
70
  {
40
71
  label: plan.tier,