forked from gocd/gocd
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UI fixes for elastic profiles page (gocd#6172)
* UI fixes for elastic profiles page * Change in key-value pair padding for better alignment. * Add a custom collapsible element to show Cluster Profile details as the styles are very specific. * Show plugin name instead of plugin id on cluster profile header panel. * Truncate cluster profile ID. * Header for elastic agent profiles. * Change `New Elastic Agent Profile` button to `Elastic Agent Profile`. * Display generic error message while saving a cluster profile. * Show warning message on `Edit Cluster Profile Modal` for plugins not supporting Cluster Profiles. * Extracted `ClusterProfileWidget` component from `ClusterProfilesWidget`: * To handle `Cluster Info` panel expansion state. * Removed plugin ID as an editable field from elastic agent profile form.
- Loading branch information
1 parent
ccaba00
commit 9b7a1b4
Showing
10 changed files
with
280 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
178 changes: 178 additions & 0 deletions
178
server/webapp/WEB-INF/rails/webpack/views/pages/elastic_profiles/cluster_profile_widget.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
/* | ||
* Copyright 2019 ThoughtWorks, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import {bind} from "classnames/bind"; | ||
import * as Routes from "gen/ts-routes"; | ||
import {MithrilComponent} from "jsx/mithril-component"; | ||
import * as _ from "lodash"; | ||
import * as m from "mithril"; | ||
import {Stream} from "mithril/stream"; | ||
import * as stream from "mithril/stream"; | ||
import {ClusterProfile, ElasticAgentProfile, ElasticAgentProfiles} from "models/elastic_profiles/types"; | ||
import {Configurations} from "models/shared/configuration"; | ||
import {ExtensionType} from "models/shared/plugin_infos_new/extension_type"; | ||
import {Extension} from "models/shared/plugin_infos_new/extensions"; | ||
import {PluginInfo} from "models/shared/plugin_infos_new/plugin_info"; | ||
import {ButtonIcon} from "views/components/buttons"; | ||
import * as Buttons from "views/components/buttons"; | ||
import {CollapsiblePanel} from "views/components/collapsible_panel"; | ||
import {HeaderIcon} from "views/components/header_icon"; | ||
import * as Icons from "views/components/icons"; | ||
import {IconGroup} from "views/components/icons"; | ||
import {KeyValuePair, KeyValueTitle} from "views/components/key_value_pair"; | ||
import {Attrs as ElasticProfilesWidgetAttrs, ElasticProfilesWidget} from "views/pages/elastic_profiles/elastic_profiles_widget"; | ||
import {AddOperation, CloneOperation, DeleteOperation, EditOperation} from "views/pages/page_operations"; | ||
import * as styles from ".//index.scss"; | ||
|
||
const classnames = bind(styles); | ||
export type ClusterProfileOperations = EditOperation<ClusterProfile> & DeleteOperation<string> & AddOperation<void> & CloneOperation<ClusterProfile>; | ||
|
||
export interface Attrs extends ElasticProfilesWidgetAttrs { | ||
pluginInfos: Stream<Array<PluginInfo<Extension>>>; | ||
elasticProfiles: ElasticAgentProfiles; | ||
isUserAnAdmin: boolean; | ||
clusterProfileOperations: ClusterProfileOperations; | ||
} | ||
|
||
interface ClusterProfileAttrs { | ||
clusterProfile: ClusterProfile; | ||
} | ||
|
||
interface State { | ||
clusterProfileDetailsExpanded: Stream<boolean>; | ||
} | ||
|
||
type ClusterProfileWidgetAttrs = Attrs & ClusterProfileAttrs; | ||
|
||
interface HeaderAttrs { | ||
clusterProfileId: string; | ||
pluginName: string; | ||
image: m.Children; | ||
} | ||
|
||
class ClusterProfileHeaderWidget extends MithrilComponent<HeaderAttrs> { | ||
view(vnode: m.Vnode<HeaderAttrs, State>) { | ||
const title = <div data-test-id="cluster-profile-name" className={styles.clusterProfileName}><span>{vnode.attrs.clusterProfileId}</span></div>; | ||
|
||
return [ | ||
<KeyValueTitle title={title} image={vnode.attrs.image}/>, | ||
<KeyValuePair inline={true} data={new Map([["Plugin", vnode.attrs.pluginName]])}/> | ||
]; | ||
} | ||
} | ||
|
||
export class ClusterProfileWidget extends MithrilComponent<ClusterProfileWidgetAttrs, State> { | ||
oninit(vnode: m.Vnode<ClusterProfileWidgetAttrs, State>) { | ||
vnode.state.clusterProfileDetailsExpanded = stream(false); | ||
} | ||
|
||
view(vnode: m.Vnode<ClusterProfileWidgetAttrs, State>) { | ||
const filteredElasticAgentProfiles = vnode.attrs.elasticProfiles.filterByClusterProfile(vnode.attrs.clusterProfile.id()); | ||
const pluginInfo = this.pluginInfo(vnode); | ||
const pluginImageTag = ClusterProfileWidget.createImageTag(pluginInfo); | ||
const pluginName = pluginInfo ? pluginInfo.about.name : ""; | ||
return <CollapsiblePanel key={vnode.attrs.clusterProfile.id()} | ||
header={<ClusterProfileHeaderWidget clusterProfileId={vnode.attrs.clusterProfile.id()} pluginName={pluginName} image={pluginImageTag}/>} | ||
actions={this.getActionButtons(vnode)} | ||
dataTestId={"cluster-profile-panel"}> | ||
{this.getClusterProfileDetails(vnode)} | ||
<h4>Elastic Agent Profiles</h4> | ||
<ElasticProfilesWidget elasticProfiles={new ElasticAgentProfiles(filteredElasticAgentProfiles)} | ||
pluginInfos={vnode.attrs.pluginInfos} | ||
elasticAgentOperations={vnode.attrs.elasticAgentOperations} | ||
onShowUsages={vnode.attrs.onShowUsages.bind(vnode.attrs)} | ||
isUserAnAdmin={vnode.attrs.isUserAnAdmin}/> | ||
</CollapsiblePanel>; | ||
} | ||
|
||
private static supportsClusterStatusReport(pluginInfo: PluginInfo<any> | undefined) { | ||
if (pluginInfo && pluginInfo.extensionOfType(ExtensionType.ELASTIC_AGENTS)) { | ||
const extension = pluginInfo.extensionOfType(ExtensionType.ELASTIC_AGENTS); | ||
return extension && extension.capabilities && extension.capabilities.supportsClusterStatusReport; | ||
} | ||
return false; | ||
} | ||
|
||
private static createImageTag(pluginInfo: PluginInfo<any> | undefined) { | ||
if (pluginInfo && pluginInfo.imageUrl) { | ||
return <HeaderIcon name="Plugin Icon" imageUrl={pluginInfo.imageUrl}/>; | ||
} | ||
return <HeaderIcon/>; | ||
} | ||
|
||
private goToStatusReportPage(statusReportHref: string, event: Event): void { | ||
event.stopPropagation(); | ||
window.location.href = statusReportHref; | ||
} | ||
|
||
private getActionButtons(vnode: m.Vnode<ClusterProfileWidgetAttrs>) { | ||
const actionButtons = []; | ||
const pluginInfo = this.pluginInfo(vnode); | ||
if (pluginInfo != null && ClusterProfileWidget.supportsClusterStatusReport(pluginInfo)) { | ||
const statusReportPath: string = Routes.adminClusterStatusReportPath(vnode.attrs.clusterProfile.pluginId(), vnode.attrs.clusterProfile.id()); | ||
|
||
actionButtons.push( | ||
<Buttons.Secondary onclick={this.goToStatusReportPage.bind(this, statusReportPath)} | ||
data-test-id="status-report-link" | ||
icon={ButtonIcon.DOC} | ||
disabled={!vnode.attrs.isUserAnAdmin || !pluginInfo}> | ||
Status Report | ||
</Buttons.Secondary>); | ||
} | ||
|
||
actionButtons.push( | ||
<Buttons.Secondary onclick={(e) => { | ||
vnode.attrs.elasticAgentOperations.onAdd(new ElasticAgentProfile("", vnode.attrs.clusterProfile.pluginId(), vnode.attrs.clusterProfile.id(), new Configurations([])), e); | ||
}} data-test-id={"new-elastic-agent-profile-button"} disabled={!pluginInfo} icon={ButtonIcon.ADD}> | ||
Elastic Agent Profile | ||
</Buttons.Secondary>); | ||
|
||
actionButtons.push(<div className={styles.clusterProfileCrudActions}> | ||
<IconGroup> | ||
<Icons.Edit data-test-id="edit-cluster-profile" onclick={vnode.attrs.clusterProfileOperations.onEdit.bind(this, vnode.attrs.clusterProfile)} disabled={!pluginInfo}/> | ||
<Icons.Clone data-test-id="clone-cluster-profile" onclick={vnode.attrs.clusterProfileOperations.onClone.bind(this, vnode.attrs.clusterProfile)} disabled={!pluginInfo}/> | ||
<Icons.Delete data-test-id="delete-cluster-profile" onclick={vnode.attrs.clusterProfileOperations.onDelete.bind(this, vnode.attrs.clusterProfile.id())}/> | ||
</IconGroup> | ||
</div>); | ||
|
||
return actionButtons; | ||
} | ||
|
||
private toggle(vnode: m.Vnode<ClusterProfileWidgetAttrs, State>) { | ||
vnode.state.clusterProfileDetailsExpanded(!vnode.state.clusterProfileDetailsExpanded()); | ||
} | ||
|
||
private getClusterProfileDetails(vnode: m.Vnode<ClusterProfileWidgetAttrs, State>) { | ||
const clusterProfileProperties = vnode.attrs.clusterProfile.properties() ? vnode.attrs.clusterProfile.properties().asMap() : []; | ||
const clusterProfileDetails = new Map([ | ||
["Id", vnode.attrs.clusterProfile.id()], | ||
["PluginId", vnode.attrs.clusterProfile.pluginId()], | ||
...Array.from(clusterProfileProperties) | ||
]); | ||
return ( | ||
<div className={styles.clusterProfileDetailsContainer}> | ||
<h5 className={classnames(styles.clusterProfileDetailsHeader, {[styles.expanded]: vnode.state.clusterProfileDetailsExpanded()})} onclick={this.toggle.bind(this, vnode)} data-test-id="cluster-profile-details-header">Cluster configuration</h5> | ||
<div className={classnames(styles.clusterProfileDetails, {[styles.expanded]: vnode.state.clusterProfileDetailsExpanded()})} data-test-id="cluster-profile-details"> | ||
<KeyValuePair data={clusterProfileDetails}/> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
private pluginInfo(vnode: m.Vnode<ClusterProfileWidgetAttrs>) { | ||
return _.find(vnode.attrs.pluginInfos(), ["id", vnode.attrs.clusterProfile.pluginId()]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.