11import React from "react" ;
2- import { Link } from "react-router-dom" ;
2+ import { Link , useNavigate } from "react-router-dom" ;
3+
4+ import type { AxiosError } from "axios" ;
35
46import {
57 Breadcrumb ,
68 BreadcrumbItem ,
7- Button ,
9+ ButtonVariant ,
810 Content ,
11+ Divider ,
12+ Dropdown ,
13+ DropdownItem ,
14+ DropdownList ,
915 Flex ,
1016 FlexItem ,
1117 Label ,
18+ MenuToggle ,
19+ type MenuToggleElement ,
1220 PageSection ,
1321 Split ,
1422 SplitItem ,
1523 Tab ,
1624 TabContent ,
17- TabTitleText ,
1825 Tabs ,
26+ TabTitleText ,
1927} from "@patternfly/react-core" ;
20- import DownloadIcon from "@patternfly/react-icons/dist/esm/icons/download-icon" ;
2128
29+ import {
30+ advisoryDeletedErrorMessage ,
31+ advisoryDeleteDialogProps ,
32+ advisoryDeletedSuccessMessage ,
33+ } from "@app/Constants" ;
2234import { PathParam , Paths , useRouteParams } from "@app/Routes" ;
23-
35+ import type { AdvisorySummary } from "@app/client" ;
36+ import { ConfirmDialog } from "@app/components/ConfirmDialog" ;
2437import { LoadingWrapper } from "@app/components/LoadingWrapper" ;
38+ import { NotificationsContext } from "@app/components/NotificationsContext" ;
2539import { useDownload } from "@app/hooks/domain-controls/useDownload" ;
26- import { useFetchAdvisoryById } from "@app/queries/advisories" ;
40+ import {
41+ useDeleteAdvisoryMutation ,
42+ useFetchAdvisoryById ,
43+ } from "@app/queries/advisories" ;
2744
2845import { Overview } from "./overview" ;
2946import { VulnerabilitiesByAdvisory } from "./vulnerabilities-by-advisory" ;
3047
3148export const AdvisoryDetails : React . FC = ( ) => {
49+ const navigate = useNavigate ( ) ;
50+ const { pushNotification } = React . useContext ( NotificationsContext ) ;
51+
52+ const advisoryId = useRouteParams ( PathParam . ADVISORY_ID ) ;
53+ const { advisory, isFetching, fetchError } = useFetchAdvisoryById ( advisoryId ) ;
54+
55+ // Actions Dropdown
56+ const [ isActionsDropdownOpen , setIsActionsDropdownOpen ] =
57+ React . useState ( false ) ;
58+
59+ const handleActionsDropdownToggle = ( ) => {
60+ setIsActionsDropdownOpen ( ! isActionsDropdownOpen ) ;
61+ } ;
62+
63+ // Download action
64+ const { downloadAdvisory } = useDownload ( ) ;
65+
66+ // Delete action
67+ const [ isDeleteDialogOpen , setIsDeleteDialogOpen ] = React . useState ( false ) ;
68+
69+ const onDeleteAdvisorySuccess = ( advisory : AdvisorySummary ) => {
70+ setIsDeleteDialogOpen ( false ) ;
71+ pushNotification ( {
72+ title : advisoryDeletedSuccessMessage ( advisory ) ,
73+ variant : "success" ,
74+ } ) ;
75+ navigate ( "/advisories" ) ;
76+ } ;
77+
78+ const onDeleteAdvisoryError = ( error : AxiosError ) => {
79+ pushNotification ( {
80+ title : advisoryDeletedErrorMessage ( error ) ,
81+ variant : "danger" ,
82+ } ) ;
83+ } ;
84+
85+ const { mutate : deleteAdvisory , isPending : isDeleting } =
86+ useDeleteAdvisoryMutation ( onDeleteAdvisorySuccess , onDeleteAdvisoryError ) ;
87+
88+ // Tabs
3289 const [ activeTabKey , setActiveTabKey ] = React . useState < string | number > ( 0 ) ;
3390
3491 const handleTabClick = (
@@ -41,13 +98,6 @@ export const AdvisoryDetails: React.FC = () => {
4198 const infoTabRef = React . createRef < HTMLElement > ( ) ;
4299 const vulnerabilitiesTabRef = React . createRef < HTMLElement > ( ) ;
43100
44- //
45-
46- const advisoryId = useRouteParams ( PathParam . ADVISORY_ID ) ;
47- const { advisory, isFetching, fetchError } = useFetchAdvisoryById ( advisoryId ) ;
48-
49- const { downloadAdvisory } = useDownload ( ) ;
50-
51101 return (
52102 < >
53103 < PageSection type = "breadcrumb" >
@@ -78,23 +128,49 @@ export const AdvisoryDetails: React.FC = () => {
78128 </ Flex >
79129 </ SplitItem >
80130 < SplitItem >
81- { ! isFetching && (
82- < Button
83- variant = "secondary"
84- icon = { < DownloadIcon /> }
85- onClick = { ( ) => {
86- if ( advisoryId ) {
87- downloadAdvisory (
88- advisoryId ,
89- advisory ?. identifier
90- ? `${ advisory ?. identifier } .json`
91- : `${ advisoryId } .json` ,
92- ) ;
93- }
94- } }
131+ { advisory && (
132+ < Dropdown
133+ isOpen = { isActionsDropdownOpen }
134+ onSelect = { ( ) => setIsActionsDropdownOpen ( false ) }
135+ onOpenChange = { ( isOpen ) => setIsActionsDropdownOpen ( isOpen ) }
136+ popperProps = { { position : "right" } }
137+ toggle = { ( toggleRef : React . Ref < MenuToggleElement > ) => (
138+ < MenuToggle
139+ ref = { toggleRef }
140+ onClick = { handleActionsDropdownToggle }
141+ isExpanded = { isActionsDropdownOpen }
142+ >
143+ Actions
144+ </ MenuToggle >
145+ ) }
146+ ouiaId = "BasicDropdown"
147+ shouldFocusToggleOnSelect
95148 >
96- Download
97- </ Button >
149+ < DropdownList >
150+ < DropdownItem
151+ key = "advisory"
152+ onClick = { ( ) => {
153+ if ( advisoryId ) {
154+ downloadAdvisory (
155+ advisoryId ,
156+ advisory ?. identifier
157+ ? `${ advisory ?. identifier } .json`
158+ : `${ advisoryId } .json` ,
159+ ) ;
160+ }
161+ } }
162+ >
163+ Download Advisory
164+ </ DropdownItem >
165+ < Divider component = "li" key = "separator" />
166+ < DropdownItem
167+ key = "delete"
168+ onClick = { ( ) => setIsDeleteDialogOpen ( true ) }
169+ >
170+ Delete
171+ </ DropdownItem >
172+ </ DropdownList >
173+ </ Dropdown >
98174 ) }
99175 </ SplitItem >
100176 </ Split >
@@ -104,7 +180,7 @@ export const AdvisoryDetails: React.FC = () => {
104180 mountOnEnter
105181 activeKey = { activeTabKey }
106182 onSelect = { handleTabClick }
107- aria-label = "Tabs that contain the SBOM information"
183+ aria-label = "Tabs that contain the Advisory information"
108184 role = "region"
109185 >
110186 < Tab
@@ -136,7 +212,7 @@ export const AdvisoryDetails: React.FC = () => {
136212 eventKey = { 1 }
137213 id = "refVulnerabilitiesSection"
138214 ref = { vulnerabilitiesTabRef }
139- aria-label = "Vulnerabilities within the SBOM "
215+ aria-label = "Vulnerabilities within the Advisory "
140216 hidden
141217 >
142218 < VulnerabilitiesByAdvisory
@@ -146,6 +222,23 @@ export const AdvisoryDetails: React.FC = () => {
146222 />
147223 </ TabContent >
148224 </ PageSection >
225+
226+ < ConfirmDialog
227+ { ...advisoryDeleteDialogProps ( advisory ) }
228+ inProgress = { isDeleting }
229+ titleIconVariant = "warning"
230+ isOpen = { isDeleteDialogOpen }
231+ confirmBtnVariant = { ButtonVariant . danger }
232+ confirmBtnLabel = "Delete"
233+ cancelBtnLabel = "Cancel"
234+ onCancel = { ( ) => setIsDeleteDialogOpen ( false ) }
235+ onClose = { ( ) => setIsDeleteDialogOpen ( false ) }
236+ onConfirm = { ( ) => {
237+ if ( advisory ) {
238+ deleteAdvisory ( advisory . uuid ) ;
239+ }
240+ } }
241+ />
149242 </ >
150243 ) ;
151244} ;
0 commit comments