Skip to content

Commit

Permalink
Action menu: sort items, show in resource details page (#11008)
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Eftimie <alex.eftimie@getyourguide.com>
Co-authored-by: Remington Breeze <remington@breeze.software>
  • Loading branch information
alexef and rbreeze committed Nov 8, 2022
1 parent d5a961c commit 149926d
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {DataLoader, Tab, Tabs} from 'argo-ui';
import {DataLoader, DropDown, Tab, Tabs} from 'argo-ui';
import * as React from 'react';
import {useState} from 'react';
import {EventsList, YamlEditor} from '../../../shared/components';
Expand Down Expand Up @@ -309,9 +309,21 @@ export const ResourceDetails = (props: ResourceDetailsProps) => {
className='argo-button argo-button--base'>
<i className='fa fa-sync-alt' /> SYNC
</button>
<button onClick={() => AppUtils.deletePopup(appContext, selectedNode, application)} className='argo-button argo-button--base'>
<button
onClick={() => AppUtils.deletePopup(appContext, selectedNode, application)}
style={{marginRight: '5px'}}
className='argo-button argo-button--base'>
<i className='fa fa-trash' /> DELETE
</button>
<DropDown
isMenu={true}
anchor={() => (
<button className='argo-button argo-button--light argo-button--lg argo-button--short'>
<i className='fa fa-ellipsis-v' />
</button>
)}>
{() => AppUtils.renderResourceActionMenu(selectedNode, application, tree, {apis: appContext})}
</DropDown>
</div>
<Tabs
navTransparent={true}
Expand Down
90 changes: 61 additions & 29 deletions ui/src/app/applications/components/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {DataLoader, FormField, MenuItem, NotificationType, Tooltip} from 'argo-ui';
import {models, DataLoader, FormField, MenuItem, NotificationType, Tooltip} from 'argo-ui';
import {ActionButton} from 'argo-ui/v2';
import * as classNames from 'classnames';
import * as React from 'react';
Expand Down Expand Up @@ -397,6 +397,37 @@ export const deletePopup = async (ctx: ContextApis, resource: ResourceTreeNode,
);
};

function getResourceActionsMenuItems(resource: ResourceTreeNode, metadata: models.Metadata, appContext: AppContext): Observable<ActionMenuItem[]> {
return services.applications
.getResourceActions(metadata.name, metadata.namespace, resource)
.then(actions => {
return actions.map(
action =>
({
title: action.name,
disabled: !!action.disabled,
action: async () => {
try {
const confirmed = await appContext.apis.popup.confirm(
`Execute '${action.name}' action?`,
`Are you sure you want to execute '${action.name}' action?`
);
if (confirmed) {
await services.applications.runResourceAction(metadata.name, metadata.namespace, resource, action.name);
}
} catch (e) {
appContext.apis.notifications.show({
content: <ErrorNotification title='Unable to execute resource action' e={e} />,
type: NotificationType.Error
});
}
}
} as MenuItem)
);
})
.catch(() => [] as MenuItem[]);
}

function getActionItems(
resource: ResourceTreeNode,
application: appModels.Application,
Expand Down Expand Up @@ -460,34 +491,8 @@ function getActionItems(
})
.catch(() => [] as MenuItem[]);

const resourceActions = services.applications
.getResourceActions(application.metadata.name, application.metadata.namespace, resource)
.then(actions => {
return actions.map(
action =>
({
title: action.name,
disabled: !!action.disabled,
action: async () => {
try {
const confirmed = await appContext.apis.popup.confirm(
`Execute '${action.name}' action?`,
`Are you sure you want to execute '${action.name}' action?`
);
if (confirmed) {
await services.applications.runResourceAction(application.metadata.name, application.metadata.namespace, resource, action.name);
}
} catch (e) {
appContext.apis.notifications.show({
content: <ErrorNotification title='Unable to execute resource action' e={e} />,
type: NotificationType.Error
});
}
}
} as MenuItem)
);
})
.catch(() => [] as MenuItem[]);
const resourceActions = getResourceActionsMenuItems(resource, application, appContext);

return combineLatest(
from([items]), // this resolves immediately
concat([[] as MenuItem[]], resourceActions), // this resolves at first to [] and then whatever the API returns
Expand Down Expand Up @@ -534,6 +539,33 @@ export function renderResourceMenu(
);
}

export function renderResourceActionMenu(resource: ResourceTreeNode, application: appModels.Application, tree: appModels.ApplicationTree, appContext: AppContext): React.ReactNode {
const menuItems = getResourceActionsMenuItems(resource, application.metadata, appContext);

return (
<DataLoader load={() => menuItems}>
{items => (
<ul>
{items.map((item, i) => (
<li
className={classNames('application-details__action-menu', {disabled: item.disabled})}
key={i}
onClick={e => {
e.stopPropagation();
if (!item.disabled) {
item.action();
document.body.click();
}
}}>
{item.iconClassName && <i className={item.iconClassName} />} {item.title}
</li>
))}
</ul>
)}
</DataLoader>
);
}

export function renderResourceButtons(
resource: ResourceTreeNode,
application: appModels.Application,
Expand Down
6 changes: 5 additions & 1 deletion ui/src/app/shared/services/applications-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,11 @@ export class ApplicationsService {
kind: resource.kind,
group: resource.group
})
.then(res => (res.body.actions as models.ResourceAction[]) || []);
.then(res => {
const actions = (res.body.actions as models.ResourceAction[]) || [];
actions.sort((actionA, actionB) => actionA.name.localeCompare(actionB.name));
return actions;
});
}

public runResourceAction(name: string, appNamspace: string, resource: models.ResourceNode, action: string): Promise<models.ResourceAction[]> {
Expand Down

0 comments on commit 149926d

Please sign in to comment.