@@ -58,10 +58,12 @@ interface ResourceRow {
5858 resource_type : string ;
5959 created_by : CreatedBy ;
6060 share_with ?: ShareWith ; // may be empty/undefined
61+ can_share ?: boolean ; // whether the current user can share this resource
6162}
6263interface TypeEntry {
6364 type : string ; // fully qualified class name
6465 index : string ; // name of the index to be supplied to the API as resource_type
66+ action_groups : string [ ] ; // known action-groups for this type
6567}
6668// Tiny API that the panel consumes
6769interface Api {
@@ -91,17 +93,6 @@ interface Props {
9193 toasts : CoreStart [ 'notifications' ] [ 'toasts' ] ;
9294}
9395
94- const ACTION_GROUP_SUGGESTIONS = [
95- 'read' ,
96- 'write' ,
97- 'manage' ,
98- 'admin' ,
99- 'monitor' ,
100- 'index' ,
101- 'delete' ,
102- 'all' ,
103- ] ;
104-
10596const hasSharingInfo = ( sw ?: ShareWith ) =>
10697 ! ! sw &&
10798 Object . values ( sw ) . some (
@@ -226,6 +217,7 @@ interface ModalProps {
226217 resource : ResourceRow ;
227218 resourceType : string ;
228219 resourceTypeIndex : string ;
220+ actionGroups : string [ ] ;
229221}
230222
231223const hasNonEmptyRecipients = ( r ?: ShareRecipients ) =>
@@ -247,6 +239,7 @@ const ShareAccessModal: React.FC<ModalProps> = ({
247239 resource,
248240 resourceType,
249241 resourceTypeIndex,
242+ actionGroups,
250243} ) => {
251244 const original = useMemo ( ( ) => cloneShareWith ( resource ?. share_with ) , [ resource ?. share_with ] ) ;
252245 const [ working , setWorking ] = useState < ShareWith > ( ( ) =>
@@ -262,9 +255,21 @@ const ShareAccessModal: React.FC<ModalProps> = ({
262255 } , [ mode , resource ] ) ;
263256
264257 const groups = Object . keys ( working ) ;
258+ // suggestions list for this modal (unique + keep order)
259+ const SUGGESTIONS = useMemo ( ( ) => Array . from ( new Set ( actionGroups ?? [ ] ) ) , [ actionGroups ] ) ;
260+
265261 const addGroup = ( ) => {
266- const base =
267- ACTION_GROUP_SUGGESTIONS . find ( ( g ) => ! groups . includes ( g ) ) || `GROUP_${ groups . length + 1 } ` ;
262+ // If no suggestions were provided for this type, do nothing.
263+ if ( ! SUGGESTIONS . length ) {
264+ // Optional: show a subtle warning/toast instead of silent no-op
265+ // toasts?.addWarning?.('No predefined action-groups for this type');
266+ return ;
267+ }
268+
269+ // Pick the first unused suggestion
270+ const base = SUGGESTIONS . find ( ( g ) => ! groups . includes ( g ) ) ;
271+ if ( ! base ) return ; // all suggestions already used
272+
268273 setWorking ( { ...working , [ base ] : { } } ) ;
269274 } ;
270275
@@ -391,18 +396,18 @@ const ShareAccessModal: React.FC<ModalProps> = ({
391396 ) }
392397
393398 { Object . entries ( working ) . map ( ( [ groupName , recipients ] ) => {
394- // TODO: update these to fetch static action-groups from backend
395- const groupSuggestions = [
396- ... new Set ( [ groupName , ... ACTION_GROUP_SUGGESTIONS ] ) ,
397- ] . map ( ( l ) => ( { label : l } ) ) ;
399+ const groupOptions = [ ... new Set ( [ groupName , ... SUGGESTIONS ] ) ] . map ( ( l ) => ( {
400+ label : l ,
401+ } ) ) ;
402+
398403 return (
399404 < EuiPanel key = { groupName } paddingSize = "m" hasShadow = { false } hasBorder >
400405 < EuiFlexGroup gutterSize = "m" alignItems = "center" >
401406 < EuiFlexItem grow = { 3 } >
402407 < EuiFormRow label = "Action-group" >
403408 < EuiComboBox
404409 singleSelection = { { asPlainText : true } }
405- options = { groupSuggestions }
410+ options = { groupOptions }
406411 selectedOptions = { [ { label : groupName } ] }
407412 onChange = { ( opts ) => {
408413 const newLabel = opts [ 0 ] ?. label || groupName ;
@@ -539,7 +544,7 @@ const humanizeClassName = (fqn: string) => {
539544/** ---------- Main table ---------- */
540545export const ResourceSharingPanel : React . FC < Props > = ( { api, toasts } ) => {
541546 const [ typeOptions , setTypeOptions ] = useState <
542- Array < { value : string ; text : string ; fqn : string } >
547+ Array < { value : string ; text : string ; fqn : string ; actionGroups : string [ ] } >
543548 > ( [ ] ) ;
544549 const [ selectedType , setSelectedType ] = useState < string > ( '' ) ; // no default selection
545550 const [ rows , setRows ] = useState < ResourceRow [ ] > ( [ ] ) ;
@@ -561,7 +566,12 @@ export const ResourceSharingPanel: React.FC<Props> = ({ api, toasts }) => {
561566 const raw : TypeEntry [ ] = Array . isArray ( res ) ? res : res ?. types || [ ] ;
562567 // value = index (what we send as resourceType); text = type (what we display)
563568 const options = raw
564- . map ( ( t ) => ( { value : t . index , text : humanizeClassName ( t . type ) , fqn : t . type } ) )
569+ . map ( ( t ) => ( {
570+ value : t . index ,
571+ text : humanizeClassName ( t . type ) ,
572+ fqn : t . type ,
573+ actionGroups : t . action_groups ,
574+ } ) )
565575 // sort alphabetically by text (and by value if text is equal)
566576 . sort ( ( a , b ) => {
567577 const byText = a . text . localeCompare ( b . text , undefined , { sensitivity : 'base' } ) ;
@@ -601,6 +611,11 @@ export const ResourceSharingPanel: React.FC<Props> = ({ api, toasts }) => {
601611 const selectedTypeLabel = selectedTypeMeta ?. text ?? ( selectedType || '—' ) ;
602612 const selectedTypeTooltip = selectedTypeMeta ?. fqn ; // actual class name
603613
614+ const currentActionGroups = useMemo (
615+ ( ) => Array . from ( new Set ( selectedTypeMeta ?. actionGroups ?? [ ] ) ) . sort ( ) ,
616+ [ selectedTypeMeta ]
617+ ) ;
618+
604619 const columns = [
605620 // id column
606621 {
@@ -674,20 +689,36 @@ export const ResourceSharingPanel: React.FC<Props> = ({ api, toasts }) => {
674689 name : 'Actions' ,
675690 render : ( item : ResourceRow ) => {
676691 const label = hasSharingInfo ( item . share_with ) ? 'Update Access' : 'Share' ;
677- return (
692+ const canShare = item . can_share === true ;
693+
694+ const handleClick = ( ) => {
695+ if ( ! canShare ) return ;
696+ setModalState ( {
697+ open : true ,
698+ mode : hasSharingInfo ( item . share_with ) ? 'edit' : 'create' ,
699+ resource : item ,
700+ } ) ;
701+ } ;
702+
703+ const btn = (
678704 < EuiButton
679705 size = "s"
680- onClick = { ( ) => {
681- setModalState ( {
682- open : true ,
683- mode : hasSharingInfo ( item . share_with ) ? 'edit' : 'create' ,
684- resource : item ,
685- } ) ;
686- } }
706+ isDisabled = { ! canShare }
707+ data-test-subj = { `share-button-${ item . resource_id } ` }
708+ onClick = { handleClick }
687709 >
688710 { label }
689711 </ EuiButton >
690712 ) ;
713+
714+ // Show tooltip only when disabled
715+ return canShare ? (
716+ btn
717+ ) : (
718+ < EuiToolTip content = "You do not have access to update sharing information of this resource" >
719+ < span > { btn } </ span >
720+ </ EuiToolTip >
721+ ) ;
691722 } ,
692723 } ,
693724 ] ;
@@ -818,6 +849,7 @@ export const ResourceSharingPanel: React.FC<Props> = ({ api, toasts }) => {
818849 resource = { modalState . resource as ResourceRow }
819850 resourceType = { selectedTypeLabel }
820851 resourceTypeIndex = { selectedType }
852+ actionGroups = { currentActionGroups }
821853 />
822854 </ EuiPanel >
823855 ) ;
0 commit comments