@replicated/portal-components 0.0.2 → 0.0.3

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 (216) hide show
  1. package/components/metadata/registry.json +83 -2
  2. package/components/metadata/registry.md +27 -2
  3. package/dist/actions/index.d.mts +566 -3
  4. package/dist/actions/index.d.ts +566 -3
  5. package/dist/actions/index.js +1853 -12
  6. package/dist/actions/index.js.map +1 -1
  7. package/dist/airgap-instances.d.mts +26 -0
  8. package/dist/airgap-instances.d.ts +26 -0
  9. package/dist/airgap-instances.js +354 -0
  10. package/dist/airgap-instances.js.map +1 -0
  11. package/dist/error-page.d.mts +14 -0
  12. package/dist/error-page.d.ts +14 -0
  13. package/dist/error-page.js +153 -0
  14. package/dist/error-page.js.map +1 -0
  15. package/dist/error.d.mts +15 -0
  16. package/dist/error.d.ts +15 -0
  17. package/dist/error.js +144 -0
  18. package/dist/error.js.map +1 -0
  19. package/dist/esm/actions/index.js +1816 -13
  20. package/dist/esm/actions/index.js.map +1 -1
  21. package/dist/esm/airgap-instances.js +352 -0
  22. package/dist/esm/airgap-instances.js.map +1 -0
  23. package/dist/esm/error-page.js +151 -0
  24. package/dist/esm/error-page.js.map +1 -0
  25. package/dist/esm/error.js +142 -0
  26. package/dist/esm/error.js.map +1 -0
  27. package/dist/esm/helm-install-wizard.js +1007 -0
  28. package/dist/esm/helm-install-wizard.js.map +1 -0
  29. package/dist/esm/index.js +2232 -155
  30. package/dist/esm/index.js.map +1 -1
  31. package/dist/esm/install-actions.js +746 -0
  32. package/dist/esm/install-actions.js.map +1 -0
  33. package/dist/esm/install-card.js +115 -0
  34. package/dist/esm/install-card.js.map +1 -0
  35. package/dist/esm/install-targets.js +48 -0
  36. package/dist/esm/install-targets.js.map +1 -0
  37. package/dist/esm/instance-card.js +197 -0
  38. package/dist/esm/instance-card.js.map +1 -0
  39. package/dist/esm/join-team.js +218 -0
  40. package/dist/esm/join-team.js.map +1 -0
  41. package/dist/esm/license-card.js +131 -0
  42. package/dist/esm/license-card.js.map +1 -0
  43. package/dist/esm/license-details.js +667 -0
  44. package/dist/esm/license-details.js.map +1 -0
  45. package/dist/esm/linux-install-wizard.js +1083 -0
  46. package/dist/esm/linux-install-wizard.js.map +1 -0
  47. package/dist/esm/login.js +261 -0
  48. package/dist/esm/login.js.map +1 -0
  49. package/dist/esm/online-instance-list.js +287 -0
  50. package/dist/esm/online-instance-list.js.map +1 -0
  51. package/dist/esm/pending-installations.js +235 -0
  52. package/dist/esm/pending-installations.js.map +1 -0
  53. package/dist/esm/release-history-panel.js +100 -0
  54. package/dist/esm/release-history-panel.js.map +1 -0
  55. package/dist/esm/release-notes-card.js +23 -0
  56. package/dist/esm/release-notes-card.js.map +1 -0
  57. package/dist/esm/security-card.js +700 -0
  58. package/dist/esm/security-card.js.map +1 -0
  59. package/dist/esm/support-bundle-collection-card.js +170 -0
  60. package/dist/esm/support-bundle-collection-card.js.map +1 -0
  61. package/dist/esm/support-bundles-card.js +306 -0
  62. package/dist/esm/support-bundles-card.js.map +1 -0
  63. package/dist/esm/support-card.js +305 -0
  64. package/dist/esm/support-card.js.map +1 -0
  65. package/dist/esm/team-selection.js +117 -0
  66. package/dist/esm/team-selection.js.map +1 -0
  67. package/dist/esm/team-settings-card.js +78 -0
  68. package/dist/esm/team-settings-card.js.map +1 -0
  69. package/dist/esm/team-settings.js +136 -0
  70. package/dist/esm/team-settings.js.map +1 -0
  71. package/dist/esm/top-nav-user-menu.js +173 -0
  72. package/dist/esm/top-nav-user-menu.js.map +1 -0
  73. package/dist/esm/top-nav.js +398 -0
  74. package/dist/esm/top-nav.js.map +1 -0
  75. package/dist/esm/update-layout.js +405 -0
  76. package/dist/esm/update-layout.js.map +1 -0
  77. package/dist/esm/updates-card.js +85 -0
  78. package/dist/esm/updates-card.js.map +1 -0
  79. package/dist/esm/upload-support-bundle-modal.js +143 -0
  80. package/dist/esm/upload-support-bundle-modal.js.map +1 -0
  81. package/dist/esm/user-settings-card.js +21 -0
  82. package/dist/esm/user-settings-card.js.map +1 -0
  83. package/dist/esm/user-settings.js +368 -0
  84. package/dist/esm/user-settings.js.map +1 -0
  85. package/dist/esm/utils/index.js +170 -0
  86. package/dist/esm/utils/index.js.map +1 -0
  87. package/dist/helm-install-wizard.d.mts +38 -0
  88. package/dist/helm-install-wizard.d.ts +38 -0
  89. package/dist/helm-install-wizard.js +1011 -0
  90. package/dist/helm-install-wizard.js.map +1 -0
  91. package/dist/index.d.mts +11 -27
  92. package/dist/index.d.ts +11 -27
  93. package/dist/index.js +2258 -154
  94. package/dist/index.js.map +1 -1
  95. package/dist/install-B19AaKF_.d.mts +233 -0
  96. package/dist/install-Bi1qJ8Bu.d.ts +233 -0
  97. package/dist/install-actions.d.mts +141 -0
  98. package/dist/install-actions.d.ts +141 -0
  99. package/dist/install-actions.js +765 -0
  100. package/dist/install-actions.js.map +1 -0
  101. package/dist/install-card.d.mts +15 -0
  102. package/dist/install-card.d.ts +15 -0
  103. package/dist/install-card.js +117 -0
  104. package/dist/install-card.js.map +1 -0
  105. package/dist/install-targets.d.mts +19 -0
  106. package/dist/install-targets.d.ts +19 -0
  107. package/dist/install-targets.js +50 -0
  108. package/dist/install-targets.js.map +1 -0
  109. package/dist/instance-card.d.mts +22 -0
  110. package/dist/instance-card.d.ts +22 -0
  111. package/dist/instance-card.js +199 -0
  112. package/dist/instance-card.js.map +1 -0
  113. package/dist/join-team.d.mts +30 -0
  114. package/dist/join-team.d.ts +30 -0
  115. package/dist/join-team.js +220 -0
  116. package/dist/join-team.js.map +1 -0
  117. package/dist/license-card.d.mts +15 -0
  118. package/dist/license-card.d.ts +15 -0
  119. package/dist/license-card.js +133 -0
  120. package/dist/license-card.js.map +1 -0
  121. package/dist/license-details.d.mts +10 -0
  122. package/dist/license-details.d.ts +10 -0
  123. package/dist/license-details.js +669 -0
  124. package/dist/license-details.js.map +1 -0
  125. package/dist/linux-install-wizard.d.mts +66 -0
  126. package/dist/linux-install-wizard.d.ts +66 -0
  127. package/dist/linux-install-wizard.js +1093 -0
  128. package/dist/linux-install-wizard.js.map +1 -0
  129. package/dist/login.d.mts +37 -0
  130. package/dist/login.d.ts +37 -0
  131. package/dist/login.js +263 -0
  132. package/dist/login.js.map +1 -0
  133. package/dist/online-instance-list.d.mts +22 -0
  134. package/dist/online-instance-list.d.ts +22 -0
  135. package/dist/online-instance-list.js +289 -0
  136. package/dist/online-instance-list.js.map +1 -0
  137. package/dist/pending-installations.d.mts +15 -0
  138. package/dist/pending-installations.d.ts +15 -0
  139. package/dist/pending-installations.js +237 -0
  140. package/dist/pending-installations.js.map +1 -0
  141. package/dist/release-history-panel.d.mts +22 -0
  142. package/dist/release-history-panel.d.ts +22 -0
  143. package/dist/release-history-panel.js +102 -0
  144. package/dist/release-history-panel.js.map +1 -0
  145. package/dist/release-notes-card.d.mts +13 -0
  146. package/dist/release-notes-card.d.ts +13 -0
  147. package/dist/release-notes-card.js +25 -0
  148. package/dist/release-notes-card.js.map +1 -0
  149. package/dist/security-card.d.mts +73 -0
  150. package/dist/security-card.d.ts +73 -0
  151. package/dist/security-card.js +702 -0
  152. package/dist/security-card.js.map +1 -0
  153. package/dist/styles.css +1877 -194
  154. package/dist/support-bundle-collection-card.d.mts +20 -0
  155. package/dist/support-bundle-collection-card.d.ts +20 -0
  156. package/dist/support-bundle-collection-card.js +172 -0
  157. package/dist/support-bundle-collection-card.js.map +1 -0
  158. package/dist/support-bundles-card.d.mts +19 -0
  159. package/dist/support-bundles-card.d.ts +19 -0
  160. package/dist/support-bundles-card.js +308 -0
  161. package/dist/support-bundles-card.js.map +1 -0
  162. package/dist/support-card.d.mts +8 -0
  163. package/dist/support-card.d.ts +8 -0
  164. package/dist/support-card.js +307 -0
  165. package/dist/support-card.js.map +1 -0
  166. package/dist/team-selection.d.mts +23 -0
  167. package/dist/team-selection.d.ts +23 -0
  168. package/dist/team-selection.js +119 -0
  169. package/dist/team-selection.js.map +1 -0
  170. package/dist/team-settings-card-Dq1d9b5c.d.mts +14 -0
  171. package/dist/team-settings-card-Dq1d9b5c.d.ts +14 -0
  172. package/dist/team-settings-card.d.mts +2 -0
  173. package/dist/team-settings-card.d.ts +2 -0
  174. package/dist/team-settings-card.js +80 -0
  175. package/dist/team-settings-card.js.map +1 -0
  176. package/dist/team-settings.d.mts +25 -0
  177. package/dist/team-settings.d.ts +25 -0
  178. package/dist/team-settings.js +138 -0
  179. package/dist/team-settings.js.map +1 -0
  180. package/dist/top-nav-0mb1K_H0.d.mts +32 -0
  181. package/dist/top-nav-0mb1K_H0.d.ts +32 -0
  182. package/dist/top-nav-user-menu.d.mts +18 -0
  183. package/dist/top-nav-user-menu.d.ts +18 -0
  184. package/dist/top-nav-user-menu.js +175 -0
  185. package/dist/top-nav-user-menu.js.map +1 -0
  186. package/dist/top-nav.d.mts +3 -0
  187. package/dist/top-nav.d.ts +3 -0
  188. package/dist/top-nav.js +400 -0
  189. package/dist/top-nav.js.map +1 -0
  190. package/dist/update-layout.d.mts +12 -0
  191. package/dist/update-layout.d.ts +12 -0
  192. package/dist/update-layout.js +407 -0
  193. package/dist/update-layout.js.map +1 -0
  194. package/dist/updates-card-BbubBrVR.d.mts +18 -0
  195. package/dist/updates-card-BbubBrVR.d.ts +18 -0
  196. package/dist/updates-card.d.mts +2 -0
  197. package/dist/updates-card.d.ts +2 -0
  198. package/dist/updates-card.js +87 -0
  199. package/dist/updates-card.js.map +1 -0
  200. package/dist/upload-support-bundle-modal.d.mts +19 -0
  201. package/dist/upload-support-bundle-modal.d.ts +19 -0
  202. package/dist/upload-support-bundle-modal.js +145 -0
  203. package/dist/upload-support-bundle-modal.js.map +1 -0
  204. package/dist/user-settings-card.d.mts +8 -0
  205. package/dist/user-settings-card.d.ts +8 -0
  206. package/dist/user-settings-card.js +23 -0
  207. package/dist/user-settings-card.js.map +1 -0
  208. package/dist/user-settings.d.mts +47 -0
  209. package/dist/user-settings.d.ts +47 -0
  210. package/dist/user-settings.js +370 -0
  211. package/dist/user-settings.js.map +1 -0
  212. package/dist/utils/index.d.mts +70 -0
  213. package/dist/utils/index.d.ts +70 -0
  214. package/dist/utils/index.js +177 -0
  215. package/dist/utils/index.js.map +1 -0
  216. package/package.json +163 -3
@@ -0,0 +1,702 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ /**
8
+ * Enterprise Portal Components
9
+ * This file is generated by tsup. Do not edit manually.
10
+ */
11
+
12
+ var DownloadIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs(
13
+ "svg",
14
+ {
15
+ xmlns: "http://www.w3.org/2000/svg",
16
+ viewBox: "0 0 24 24",
17
+ fill: "none",
18
+ stroke: "currentColor",
19
+ strokeWidth: "1.5",
20
+ className,
21
+ "aria-hidden": "true",
22
+ children: [
23
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 3v14" }),
24
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 13l4 4 4-4" }),
25
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 21h14" })
26
+ ]
27
+ }
28
+ );
29
+ var InfoIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs(
30
+ "svg",
31
+ {
32
+ xmlns: "http://www.w3.org/2000/svg",
33
+ viewBox: "0 0 24 24",
34
+ fill: "none",
35
+ stroke: "currentColor",
36
+ strokeWidth: "2",
37
+ className,
38
+ "aria-hidden": "true",
39
+ children: [
40
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
41
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 16v-4" }),
42
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 8h.01" })
43
+ ]
44
+ }
45
+ );
46
+ var ChevronDownIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx(
47
+ "svg",
48
+ {
49
+ xmlns: "http://www.w3.org/2000/svg",
50
+ viewBox: "0 0 24 24",
51
+ fill: "none",
52
+ stroke: "currentColor",
53
+ strokeWidth: "2",
54
+ className,
55
+ "aria-hidden": "true",
56
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 9l6 6 6-6" })
57
+ }
58
+ );
59
+ var XIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs(
60
+ "svg",
61
+ {
62
+ xmlns: "http://www.w3.org/2000/svg",
63
+ viewBox: "0 0 24 24",
64
+ fill: "none",
65
+ stroke: "currentColor",
66
+ strokeWidth: "2",
67
+ className,
68
+ "aria-hidden": "true",
69
+ children: [
70
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18" }),
71
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 6l12 12" })
72
+ ]
73
+ }
74
+ );
75
+ var SpinnerIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs(
76
+ "svg",
77
+ {
78
+ className: `animate-spin ${className ?? ""}`,
79
+ xmlns: "http://www.w3.org/2000/svg",
80
+ fill: "none",
81
+ viewBox: "0 0 24 24",
82
+ "aria-hidden": "true",
83
+ children: [
84
+ /* @__PURE__ */ jsxRuntime.jsx(
85
+ "circle",
86
+ {
87
+ className: "opacity-25",
88
+ cx: "12",
89
+ cy: "12",
90
+ r: "10",
91
+ stroke: "currentColor",
92
+ strokeWidth: "4"
93
+ }
94
+ ),
95
+ /* @__PURE__ */ jsxRuntime.jsx(
96
+ "path",
97
+ {
98
+ className: "opacity-75",
99
+ fill: "currentColor",
100
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
101
+ }
102
+ )
103
+ ]
104
+ }
105
+ );
106
+ var CopyIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs(
107
+ "svg",
108
+ {
109
+ xmlns: "http://www.w3.org/2000/svg",
110
+ viewBox: "0 0 24 24",
111
+ fill: "none",
112
+ stroke: "currentColor",
113
+ strokeWidth: "2",
114
+ className,
115
+ "aria-hidden": "true",
116
+ children: [
117
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
118
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
119
+ ]
120
+ }
121
+ );
122
+ var getSeverityColor = (severity) => {
123
+ switch (severity) {
124
+ case "critical":
125
+ return "text-pink-600";
126
+ case "high":
127
+ return "text-yellow-600";
128
+ case "medium":
129
+ return "text-blue-400";
130
+ case "low":
131
+ return "text-green-500";
132
+ default:
133
+ return "text-gray-500";
134
+ }
135
+ };
136
+ var formatLastScanned = (dateString) => {
137
+ if (!dateString) return "---";
138
+ try {
139
+ const date = new Date(dateString);
140
+ return date.toLocaleString("en-US", {
141
+ month: "numeric",
142
+ day: "numeric",
143
+ year: "numeric",
144
+ hour: "numeric",
145
+ minute: "2-digit",
146
+ hour12: true
147
+ });
148
+ } catch {
149
+ return "---";
150
+ }
151
+ };
152
+ var getShortImageName = (imageString) => {
153
+ const parts = imageString.split("/");
154
+ return parts[parts.length - 1] || imageString;
155
+ };
156
+ var Dropdown = ({ value, options, onChange, disabled, testId }) => {
157
+ const [isOpen, setIsOpen] = react.useState(false);
158
+ const selected = options.find((o) => o.value === value);
159
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
160
+ /* @__PURE__ */ jsxRuntime.jsxs(
161
+ "button",
162
+ {
163
+ type: "button",
164
+ onClick: () => !disabled && setIsOpen(!isOpen),
165
+ disabled,
166
+ "data-testid": testId,
167
+ className: "flex w-full items-center justify-between rounded-md border border-gray-200 bg-white px-4 py-2 text-left text-sm font-semibold text-gray-900 hover:bg-gray-50 disabled:opacity-50",
168
+ children: [
169
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
170
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: selected?.label || "Select..." }),
171
+ selected?.sublabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-normal text-gray-500", children: selected.sublabel })
172
+ ] }),
173
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDownIcon, { className: "h-4 w-4 text-gray-500" })
174
+ ]
175
+ }
176
+ ),
177
+ isOpen && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
178
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-10", onClick: () => setIsOpen(false) }),
179
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute z-20 mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-200 bg-white py-1 shadow-lg", children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs(
180
+ "button",
181
+ {
182
+ type: "button",
183
+ onClick: () => {
184
+ onChange(option.value);
185
+ setIsOpen(false);
186
+ },
187
+ "data-testid": `${testId}-option-${option.value}`,
188
+ className: "flex w-full items-center justify-between px-4 py-2 text-left text-sm hover:bg-gray-100",
189
+ children: [
190
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-gray-900", children: option.label }),
191
+ option.sublabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500", children: option.sublabel })
192
+ ]
193
+ },
194
+ option.value
195
+ )) })
196
+ ] })
197
+ ] });
198
+ };
199
+ var VulnerabilityBadges = ({ summary }) => {
200
+ if (!summary) {
201
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-400", children: "No scan data" });
202
+ }
203
+ const counts = [
204
+ { severity: "critical", count: Object.keys(summary.critical || {}).length },
205
+ { severity: "high", count: Object.keys(summary.high || {}).length },
206
+ { severity: "medium", count: Object.keys(summary.medium || {}).length },
207
+ { severity: "low", count: Object.keys(summary.low || {}).length }
208
+ ].filter((c) => c.count > 0);
209
+ if (counts.length === 0) {
210
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-green-600", children: "No vulnerabilities" });
211
+ }
212
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5", children: counts.map(({ severity, count }) => /* @__PURE__ */ jsxRuntime.jsxs(
213
+ "span",
214
+ {
215
+ className: `inline-flex items-center rounded px-1.5 py-0.5 text-xs font-medium ${getSeverityColor(severity)} bg-gray-100`,
216
+ children: [
217
+ count,
218
+ " ",
219
+ severity.charAt(0).toUpperCase()
220
+ ]
221
+ },
222
+ severity
223
+ )) });
224
+ };
225
+ var FixedCount = ({ count, hasDiffData }) => {
226
+ if (!hasDiffData) {
227
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-400", children: "None" });
228
+ }
229
+ if (count === void 0 || count === 0) {
230
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-400", children: "None" });
231
+ }
232
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-green-600", children: count });
233
+ };
234
+ var ImageName = ({ imageString, compact }) => {
235
+ const parts = imageString.split("/");
236
+ const namePart = parts[parts.length - 1] || imageString;
237
+ const [name, tag] = namePart.split(":");
238
+ if (compact && imageString.length > 50) {
239
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "truncate text-sm text-gray-700", title: imageString, children: [
240
+ name,
241
+ tag && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-400", children: [
242
+ ":",
243
+ tag
244
+ ] })
245
+ ] });
246
+ }
247
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-gray-700", title: imageString, children: [
248
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-400", children: [
249
+ parts.slice(0, -1).join("/"),
250
+ "/"
251
+ ] }),
252
+ name,
253
+ tag && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-400", children: [
254
+ ":",
255
+ tag
256
+ ] })
257
+ ] });
258
+ };
259
+ var Modal = ({ isOpen, onClose, title, children }) => {
260
+ if (!isOpen) return null;
261
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
262
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 bg-black/50", onClick: onClose }),
263
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative z-10 max-h-[90vh] w-full max-w-6xl overflow-auto rounded-lg bg-white shadow-xl", children: [
264
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 px-6 py-4", children: [
265
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-900", children: title }),
266
+ /* @__PURE__ */ jsxRuntime.jsx(
267
+ "button",
268
+ {
269
+ type: "button",
270
+ onClick: onClose,
271
+ className: "rounded-full p-1 hover:bg-gray-100",
272
+ children: /* @__PURE__ */ jsxRuntime.jsx(XIcon, { className: "h-5 w-5 text-gray-500" })
273
+ }
274
+ )
275
+ ] }),
276
+ children
277
+ ] })
278
+ ] });
279
+ };
280
+ var Tooltip = ({ content, children }) => {
281
+ const [show, setShow] = react.useState(false);
282
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative inline-flex items-center", children: [
283
+ /* @__PURE__ */ jsxRuntime.jsx(
284
+ "div",
285
+ {
286
+ onMouseEnter: () => setShow(true),
287
+ onMouseLeave: () => setShow(false),
288
+ children
289
+ }
290
+ ),
291
+ show && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-1/2 z-50 mb-2 -translate-x-1/2 whitespace-nowrap rounded bg-gray-900 px-2 py-1 text-xs text-white", children: [
292
+ content,
293
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-1/2 top-full -translate-x-1/2 border-4 border-transparent border-t-gray-900" })
294
+ ] })
295
+ ] });
296
+ };
297
+ function SecurityCard({
298
+ channelReleases,
299
+ showLinux,
300
+ showHelm,
301
+ primaryColor = "#326CE5",
302
+ initialInstallType,
303
+ initialChannelSequence,
304
+ initialData,
305
+ onFetchSecurityInfo,
306
+ onFetchSecurityDiff,
307
+ onFetchSBOM
308
+ }) {
309
+ const availableInstallTypes = react.useMemo(() => {
310
+ const types = [];
311
+ if (showLinux) types.push("linux");
312
+ if (showHelm) types.push("helm");
313
+ return types;
314
+ }, [showLinux, showHelm]);
315
+ const [selectedInstallType, setSelectedInstallType] = react.useState(
316
+ initialData?.installType ?? initialInstallType ?? availableInstallTypes[0] ?? "linux"
317
+ );
318
+ const [selectedSequence, setSelectedSequence] = react.useState(
319
+ initialData?.channelSequence ?? initialChannelSequence ?? null
320
+ );
321
+ const [securityData, setSecurityData] = react.useState(
322
+ initialData?.securityInfo ?? null
323
+ );
324
+ const [diffData, setDiffData] = react.useState(
325
+ initialData?.securityDiff ?? null
326
+ );
327
+ const [sbomData, setSbomData] = react.useState(
328
+ initialData?.sbom ?? null
329
+ );
330
+ const [isLoadingScans, setIsLoadingScans] = react.useState(false);
331
+ const [isLoadingSbom, setIsLoadingSbom] = react.useState(false);
332
+ const hasUsedInitialDataRef = react.useRef(!!initialData);
333
+ const [securityError, setSecurityError] = react.useState(null);
334
+ const [sbomError, setSbomError] = react.useState(null);
335
+ const [showAllContainersModal, setShowAllContainersModal] = react.useState(false);
336
+ const filteredReleases = react.useMemo(() => {
337
+ return channelReleases.filter((release) => {
338
+ if (selectedInstallType === "linux") {
339
+ return !!release.installationTypes?.embeddedCluster;
340
+ }
341
+ if (selectedInstallType === "helm") {
342
+ return !!release.installationTypes?.helm;
343
+ }
344
+ return true;
345
+ });
346
+ }, [channelReleases, selectedInstallType]);
347
+ const selectedRelease = react.useMemo(() => {
348
+ if (!selectedSequence) return filteredReleases[0] || null;
349
+ return filteredReleases.find((r) => r.channelSequence === selectedSequence) || null;
350
+ }, [filteredReleases, selectedSequence]);
351
+ const previousRelease = react.useMemo(() => {
352
+ if (!selectedRelease) return null;
353
+ const idx = filteredReleases.findIndex(
354
+ (r) => r.channelSequence === selectedRelease.channelSequence
355
+ );
356
+ if (idx === -1 || idx >= filteredReleases.length - 1) return null;
357
+ return filteredReleases[idx + 1];
358
+ }, [filteredReleases, selectedRelease]);
359
+ react.useEffect(() => {
360
+ const firstRelease = filteredReleases[0];
361
+ if (filteredReleases.length > 0 && !selectedSequence && firstRelease) {
362
+ setSelectedSequence(firstRelease.channelSequence);
363
+ } else if (filteredReleases.length > 0 && selectedSequence && firstRelease) {
364
+ const isValid = filteredReleases.some((r) => r.channelSequence === selectedSequence);
365
+ if (!isValid) {
366
+ setSelectedSequence(firstRelease.channelSequence);
367
+ }
368
+ }
369
+ }, [filteredReleases, selectedSequence]);
370
+ react.useEffect(() => {
371
+ if (!selectedRelease) return;
372
+ if (hasUsedInitialDataRef.current && initialData) {
373
+ const isMatchingSelection = initialData.installType === selectedInstallType && initialData.channelSequence === selectedRelease.channelSequence;
374
+ if (isMatchingSelection) {
375
+ hasUsedInitialDataRef.current = false;
376
+ return;
377
+ }
378
+ hasUsedInitialDataRef.current = false;
379
+ }
380
+ const fetchData = async () => {
381
+ setIsLoadingScans(true);
382
+ setIsLoadingSbom(true);
383
+ setSecurityError(null);
384
+ setSbomError(null);
385
+ try {
386
+ const result = await onFetchSecurityInfo({
387
+ installType: selectedInstallType,
388
+ channelSequence: selectedRelease.channelSequence
389
+ });
390
+ setSecurityData(result);
391
+ } catch (err) {
392
+ setSecurityError(err instanceof Error ? err.message : "Failed to fetch security data");
393
+ setSecurityData(null);
394
+ }
395
+ if (previousRelease && onFetchSecurityDiff) {
396
+ try {
397
+ const diffResult = await onFetchSecurityDiff({
398
+ installType: selectedInstallType,
399
+ fromChannelSequence: previousRelease.channelSequence,
400
+ toChannelSequence: selectedRelease.channelSequence
401
+ });
402
+ setDiffData(diffResult);
403
+ } catch (err) {
404
+ console.error("Failed to fetch security diff:", err);
405
+ setDiffData(null);
406
+ }
407
+ } else {
408
+ setDiffData(null);
409
+ }
410
+ setIsLoadingScans(false);
411
+ if (onFetchSBOM) {
412
+ try {
413
+ const sbomResult = await onFetchSBOM({
414
+ installType: selectedInstallType,
415
+ channelSequence: selectedRelease.channelSequence
416
+ });
417
+ setSbomData(sbomResult);
418
+ } catch (err) {
419
+ setSbomError(err instanceof Error ? err.message : "Failed to fetch SBOM");
420
+ setSbomData(null);
421
+ }
422
+ }
423
+ setIsLoadingSbom(false);
424
+ };
425
+ fetchData();
426
+ }, [selectedRelease, selectedInstallType, previousRelease, onFetchSecurityInfo, onFetchSecurityDiff, onFetchSBOM, initialData]);
427
+ const installTypeOptions = react.useMemo(() => {
428
+ const options = [];
429
+ if (showLinux) options.push({ value: "linux", label: "Linux" });
430
+ if (showHelm) options.push({ value: "helm", label: "Helm" });
431
+ return options;
432
+ }, [showLinux, showHelm]);
433
+ const releaseOptions = react.useMemo(() => {
434
+ return filteredReleases.map((r) => ({
435
+ value: r.channelSequence.toString(),
436
+ label: `Version ${r.versionLabel}`,
437
+ sublabel: `Sequence ${r.channelSequence}${r.isRequired ? " (required)" : ""}`
438
+ }));
439
+ }, [filteredReleases]);
440
+ const cveTotals = react.useMemo(() => {
441
+ if (!securityData?.images) {
442
+ return { critical: 0, high: 0, medium: 0, low: 0 };
443
+ }
444
+ return securityData.images.reduce(
445
+ (acc, image) => {
446
+ if (!image.security?.result) return acc;
447
+ const r = image.security.result;
448
+ return {
449
+ critical: acc.critical + Object.keys(r.critical || {}).length,
450
+ high: acc.high + Object.keys(r.high || {}).length,
451
+ medium: acc.medium + Object.keys(r.medium || {}).length,
452
+ low: acc.low + Object.keys(r.low || {}).length
453
+ };
454
+ },
455
+ { critical: 0, high: 0, medium: 0, low: 0 }
456
+ );
457
+ }, [securityData]);
458
+ const fixedTotals = react.useMemo(() => {
459
+ if (!diffData?.images) {
460
+ return { critical: 0, high: 0, medium: 0, low: 0 };
461
+ }
462
+ return Object.values(diffData.images).reduce(
463
+ (acc, diff) => {
464
+ if (!diff.removed) return acc;
465
+ return {
466
+ critical: acc.critical + Object.keys(diff.removed.critical || {}).length,
467
+ high: acc.high + Object.keys(diff.removed.high || {}).length,
468
+ medium: acc.medium + Object.keys(diff.removed.medium || {}).length,
469
+ low: acc.low + Object.keys(diff.removed.low || {}).length
470
+ };
471
+ },
472
+ { critical: 0, high: 0, medium: 0, low: 0 }
473
+ );
474
+ }, [diffData]);
475
+ const parsedSbom = react.useMemo(() => {
476
+ if (!sbomData?.sboms?.unified?.sbom) return null;
477
+ try {
478
+ return JSON.parse(sbomData.sboms.unified.sbom);
479
+ } catch {
480
+ return null;
481
+ }
482
+ }, [sbomData]);
483
+ const handleDownloadSBOM = react.useCallback(() => {
484
+ if (!sbomData?.sboms?.unified?.sbom) return;
485
+ const blob = new Blob([sbomData.sboms.unified.sbom], { type: "application/json" });
486
+ const url = URL.createObjectURL(blob);
487
+ const a = document.createElement("a");
488
+ a.href = url;
489
+ a.download = `sbom-report-${selectedRelease?.versionLabel || "unknown"}.json`;
490
+ document.body.appendChild(a);
491
+ a.click();
492
+ document.body.removeChild(a);
493
+ URL.revokeObjectURL(url);
494
+ }, [sbomData, selectedRelease]);
495
+ const hasSecurityData = securityData?.images?.some((img) => img.security);
496
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
497
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
498
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold text-gray-900", children: "Security Center" }),
499
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600", children: "Security information and compliance reports for each release" })
500
+ ] }),
501
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-2", children: [
502
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-72", children: [
503
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Install Type" }),
504
+ availableInstallTypes.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx(
505
+ Dropdown,
506
+ {
507
+ value: selectedInstallType,
508
+ options: installTypeOptions,
509
+ onChange: (v) => setSelectedInstallType(v),
510
+ testId: "install-type-dropdown"
511
+ }
512
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-gray-200 bg-gray-50 px-4 py-2 text-sm font-semibold text-gray-900", children: installTypeOptions[0]?.label || "Linux" })
513
+ ] }),
514
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-80", children: [
515
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-2 block text-sm font-medium text-gray-700", children: "Release Version" }),
516
+ /* @__PURE__ */ jsxRuntime.jsx(
517
+ Dropdown,
518
+ {
519
+ value: selectedSequence?.toString() || "",
520
+ options: releaseOptions,
521
+ onChange: (v) => setSelectedSequence(parseInt(v, 10)),
522
+ disabled: releaseOptions.length === 0,
523
+ testId: "release-dropdown"
524
+ }
525
+ )
526
+ ] })
527
+ ] }),
528
+ (hasSecurityData || isLoadingScans || isLoadingSbom || securityError || sbomError) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-2", children: [
529
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-gray-200 bg-white", children: [
530
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between border-b border-gray-200 bg-gray-50 px-6 py-5", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-medium text-gray-900", children: "CVE Report" }) }),
531
+ isLoadingScans ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[200px] items-center justify-center p-6", children: /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, { className: "h-8 w-8 text-gray-400" }) }) : securityError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "py-8 text-pink-500", children: [
532
+ "Error loading security data: ",
533
+ securityError
534
+ ] }) }) : !hasSecurityData ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-8 text-gray-500", children: "No security report available for this release" }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6", children: [
535
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-6 text-gray-600", children: "Comprehensive vulnerability scanning using Trivy to identify and assess security risks in dependencies and container images." }),
536
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 overflow-auto rounded-md bg-gray-50 p-2", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse text-sm", children: [
537
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
538
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase text-gray-500", children: "Severity" }),
539
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase text-gray-500", children: "Found" }),
540
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase text-gray-500", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
541
+ "Fixed",
542
+ /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: "CVEs fixed compared to the previous release version", children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, { className: "h-4 w-4 cursor-pointer text-blue-400" }) })
543
+ ] }) })
544
+ ] }) }),
545
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: ["critical", "high", "medium", "low"].map((severity) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
546
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 capitalize text-gray-700", children: severity }),
547
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-medium ${getSeverityColor(severity)}`, children: cveTotals[severity] }) }),
548
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(FixedCount, { count: fixedTotals[severity], hasDiffData: !!diffData }) })
549
+ ] }, severity)) })
550
+ ] }) }),
551
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
552
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium text-gray-900", children: "Container Vulnerabilities" }) }),
553
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto rounded-lg border border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full", children: [
554
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
555
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500", children: "Name" }),
556
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500", children: "Found" }),
557
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
558
+ "Fixed",
559
+ /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: "CVEs fixed compared to the previous release version", children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, { className: "h-4 w-4 cursor-pointer text-blue-400" }) })
560
+ ] }) })
561
+ ] }) }),
562
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-200 bg-white", children: securityData?.images?.slice(0, 4).map((image, idx) => {
563
+ const imageWithoutTag = image.image.split(":")[0] || image.image;
564
+ const imageDiff = diffData?.images?.[imageWithoutTag];
565
+ const fixedVulns = imageDiff?.removed;
566
+ const totalFixed = fixedVulns ? Object.keys(fixedVulns.critical || {}).length + Object.keys(fixedVulns.high || {}).length + Object.keys(fixedVulns.medium || {}).length + Object.keys(fixedVulns.low || {}).length : void 0;
567
+ return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
568
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-[300px]", children: /* @__PURE__ */ jsxRuntime.jsx(ImageName, { imageString: image.image, compact: true }) }) }),
569
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(VulnerabilityBadges, { summary: image.security?.result }) }),
570
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(FixedCount, { count: totalFixed, hasDiffData: !!imageDiff }) })
571
+ ] }, idx);
572
+ }) })
573
+ ] }) }),
574
+ (securityData?.images?.length ?? 0) > 4 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-right", children: /* @__PURE__ */ jsxRuntime.jsx(
575
+ "button",
576
+ {
577
+ type: "button",
578
+ onClick: () => setShowAllContainersModal(true),
579
+ className: "text-sm font-medium text-blue-600 hover:underline",
580
+ children: "Show all containers"
581
+ }
582
+ ) })
583
+ ] })
584
+ ] })
585
+ ] }),
586
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-gray-200 bg-white", children: [
587
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 bg-gray-50 px-6 py-5", children: [
588
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-medium text-gray-900", children: "Software Bill of Materials" }),
589
+ parsedSbom && /* @__PURE__ */ jsxRuntime.jsxs(
590
+ "button",
591
+ {
592
+ type: "button",
593
+ onClick: handleDownloadSBOM,
594
+ style: { backgroundColor: primaryColor, borderColor: primaryColor },
595
+ className: "inline-flex items-center gap-2 rounded px-3 py-1.5 text-sm font-medium text-white hover:opacity-90",
596
+ children: [
597
+ /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, { className: "h-4 w-4" }),
598
+ "Download report"
599
+ ]
600
+ }
601
+ )
602
+ ] }),
603
+ isLoadingSbom ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[200px] items-center justify-center p-6", children: /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, { className: "h-8 w-8 text-gray-400" }) }) : sbomError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "py-8 text-pink-500", children: [
604
+ "Error loading SBOM data: ",
605
+ sbomError
606
+ ] }) }) : !parsedSbom ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-8 text-gray-500", children: "No SBOM available for this release" }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6", children: [
607
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-6 text-gray-600", children: "Comprehensive SBOM in SPDX format providing detailed software component inventory and licensing information." }),
608
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4 rounded-md bg-gray-50 p-4 text-sm text-gray-600", children: [
609
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
610
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Name:" }),
611
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: parsedSbom.name || "Unknown" })
612
+ ] }),
613
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
614
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "SPDX Version:" }),
615
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: parsedSbom.spdxVersion || "Unknown" })
616
+ ] }),
617
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
618
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Packages:" }),
619
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: parsedSbom.packages?.length ?? 0 })
620
+ ] }),
621
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
622
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Files:" }),
623
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: parsedSbom.files?.length ?? 0 })
624
+ ] }),
625
+ parsedSbom.creationInfo?.created && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
626
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Created:" }),
627
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: new Date(parsedSbom.creationInfo.created).toLocaleDateString() })
628
+ ] }),
629
+ parsedSbom.creationInfo?.creators?.[0] && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
630
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Tool:" }),
631
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: parsedSbom.creationInfo.creators[0].replace("Tool: ", "") })
632
+ ] })
633
+ ] })
634
+ ] })
635
+ ] })
636
+ ] }),
637
+ !isLoadingScans && !isLoadingSbom && !hasSecurityData && !securityError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-center pt-6 text-gray-600", children: "No security report available for this release" }),
638
+ /* @__PURE__ */ jsxRuntime.jsx(
639
+ Modal,
640
+ {
641
+ isOpen: showAllContainersModal,
642
+ onClose: () => setShowAllContainersModal(false),
643
+ title: `Container Images (${securityData?.images?.length ?? 0})`,
644
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-[640px] space-y-4 overflow-auto px-6 pb-6", children: securityData?.images?.map((image, idx) => {
645
+ const imageWithoutTag = image.image.split(":")[0] || image.image;
646
+ const imageDiff = diffData?.images?.[imageWithoutTag];
647
+ const fixedVulns = imageDiff?.removed;
648
+ const totalFixed = fixedVulns ? Object.keys(fixedVulns.critical || {}).length + Object.keys(fixedVulns.high || {}).length + Object.keys(fixedVulns.medium || {}).length + Object.keys(fixedVulns.low || {}).length : void 0;
649
+ const digest = image.security?.digest || image.sha;
650
+ const lastScanned = image.security?.last_scanned_at;
651
+ const hasDigest = digest && digest !== "---";
652
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-gray-200 pb-4 last:border-b-0 last:pb-0", children: [
653
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-1.5 text-sm font-semibold text-gray-900", children: getShortImageName(image.image) }),
654
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2 rounded bg-gray-100 px-2.5 py-1.5", children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-xs text-gray-700", children: image.image }) }),
655
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between text-xs", children: [
656
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
657
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: "Digest:" }),
658
+ hasDigest ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
659
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-gray-700", children: digest }),
660
+ /* @__PURE__ */ jsxRuntime.jsxs(
661
+ "button",
662
+ {
663
+ type: "button",
664
+ onClick: () => {
665
+ navigator.clipboard.writeText(digest);
666
+ },
667
+ className: "inline-flex items-center gap-1 text-blue-600 hover:text-blue-800",
668
+ children: [
669
+ /* @__PURE__ */ jsxRuntime.jsx(CopyIcon, { className: "h-3.5 w-3.5" }),
670
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Copy" })
671
+ ]
672
+ }
673
+ )
674
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "---" })
675
+ ] }),
676
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-gray-500", children: [
677
+ "Last Scanned: ",
678
+ formatLastScanned(lastScanned)
679
+ ] })
680
+ ] }),
681
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 text-xs", children: [
682
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
683
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: "Found:" }),
684
+ image.security?.result ? /* @__PURE__ */ jsxRuntime.jsx(VulnerabilityBadges, { summary: image.security.result }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "Unavailable" })
685
+ ] }),
686
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
687
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: "Fixed:" }),
688
+ /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: "CVEs fixed compared to the previous release version", children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, { className: "h-3.5 w-3.5 cursor-pointer text-blue-400" }) }),
689
+ imageDiff ? totalFixed && totalFixed > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-green-600", children: totalFixed }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "None" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "Unavailable" })
690
+ ] })
691
+ ] })
692
+ ] }, idx);
693
+ }) })
694
+ }
695
+ )
696
+ ] });
697
+ }
698
+ SecurityCard.displayName = "SecurityCard";
699
+
700
+ exports.SecurityCard = SecurityCard;
701
+ //# sourceMappingURL=security-card.js.map
702
+ //# sourceMappingURL=security-card.js.map