11import { AuthorizationAccessCheck } from "@flanksource-ui/components/Permissions/AuthorizationAccessCheck" ;
2+ import CollapsiblePanel from "@flanksource-ui/ui/CollapsiblePanel/CollapsiblePanel" ;
23import { Icon } from "@flanksource-ui/ui/Icons/Icon" ;
3- import {
4- Menu ,
5- MenuButton ,
6- MenuItem ,
7- MenuItems ,
8- Transition
9- } from "@headlessui/react" ;
4+ import { Popover , PopoverButton , PopoverPanel } from "@headlessui/react" ;
105import { ChevronDownIcon } from "@heroicons/react/solid" ;
11- import { Fragment , useState } from "react" ;
6+ import { useMemo , useState } from "react" ;
127import { useGetPlaybooksToRun } from "../../../../api/query-hooks/playbooks" ;
138import { RunnablePlaybook } from "../../../../api/types/playbooks" ;
149import PlaybookSpecIcon from "../../Settings/PlaybookSpecIcon" ;
@@ -43,47 +38,79 @@ export default function PlaybooksDropdownMenu({
4338 config_id
4439 } ) ;
4540
41+ const playbooksGroupedByCategory = useMemo (
42+ ( ) =>
43+ playbooks ?. reduce (
44+ ( acc , playbook ) => {
45+ const category = playbook . spec ?. category || "Uncategorized" ;
46+ if ( ! acc [ category ] ) {
47+ acc [ category ] = [ ] ;
48+ }
49+ acc [ category ] . push ( playbook ) ;
50+ return acc ;
51+ } ,
52+ { } as Record <
53+ string ,
54+ ( RunnablePlaybook & {
55+ spec : any ;
56+ } ) [ ]
57+ >
58+ ) ,
59+ [ playbooks ]
60+ ) ;
61+
4662 if ( error || playbooks ?. length === 0 || isLoading ) {
4763 return null ;
4864 }
4965
5066 return (
5167 < AuthorizationAccessCheck resource = { "playbook_runs" } action = { "write" } >
52- < div className = "my-2 text-right" >
53- < Menu as = "div" className = "relative inline-block text-left " >
54- < MenuButton className = "btn-white px-2 py-1" >
68+ < >
69+ < Popover className = "group " >
70+ < PopoverButton className = "btn-white px-2 py-1" >
5571 < Icon name = "playbook" className = "mr-2 mt-0.5 h-5 w-5" />
5672 Playbooks
57- < ChevronDownIcon
58- className = "-mr-1 ml-2 h-5 w-5 text-gray-400"
59- aria-hidden = "true"
60- />
61- </ MenuButton >
62-
63- { /* @ts -ignore */ }
64- < Transition
65- as = { Fragment as any }
66- enter = "transition ease-out duration-100"
67- enterFrom = "transform opacity-0 scale-95"
68- enterTo = "transform opacity-100 scale-100"
69- leave = "transition ease-in duration-75"
70- leaveFrom = "transform opacity-100 scale-100"
71- leaveTo = "transform opacity-0 scale-95"
73+ < ChevronDownIcon className = "size-5 group-data-[open]:rotate-180" />
74+ </ PopoverButton >
75+ < PopoverPanel
76+ portal
77+ className = "menu-items absolute right-0 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
78+ anchor = "bottom start"
7279 >
73- < MenuItems portal className = "menu-items" anchor = "bottom start" >
74- { playbooks ?. map ( ( playbook ) => (
75- < MenuItem
76- as = "button"
77- className = "menu-item w-full"
78- onClick = { ( ) => setSelectedPlaybookSpec ( playbook ) }
79- key = { playbook . id }
80- >
81- < PlaybookSpecIcon playbook = { playbook } showLabel showTag />
82- </ MenuItem >
83- ) ) }
84- </ MenuItems >
85- </ Transition >
86- </ Menu >
80+ { playbooksGroupedByCategory && (
81+ < >
82+ { Object . entries ( playbooksGroupedByCategory ) . map (
83+ ( [ category , playbooks ] ) => (
84+ < CollapsiblePanel
85+ key = { category }
86+ Header = {
87+ < div className = "flex flex-row items-center gap-2 text-sm text-gray-600" >
88+ { category }
89+ </ div >
90+ }
91+ iconClassName = "h-4 w-4"
92+ isCollapsed
93+ >
94+ < div className = { `flex flex-col` } >
95+ { playbooks . map ( ( playbook ) => (
96+ < div
97+ key = { playbook . id }
98+ onClick = { ( ) => {
99+ setSelectedPlaybookSpec ( playbook ) ;
100+ } }
101+ className = { `flex cursor-pointer flex-col justify-between gap-1 px-4 py-2 text-sm` }
102+ >
103+ < PlaybookSpecIcon playbook = { playbook } showLabel />
104+ </ div >
105+ ) ) }
106+ </ div >
107+ </ CollapsiblePanel >
108+ )
109+ ) }
110+ </ >
111+ ) }
112+ </ PopoverPanel >
113+ </ Popover >
87114 { selectedPlaybookSpec && (
88115 < SubmitPlaybookRunForm
89116 componentId = { component_id }
@@ -96,7 +123,7 @@ export default function PlaybooksDropdownMenu({
96123 playbook = { selectedPlaybookSpec }
97124 />
98125 ) }
99- </ div >
126+ </ >
100127 </ AuthorizationAccessCheck >
101128 ) ;
102129}
0 commit comments