Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert part of Infra Provider dashboard to React, add Quadicon. #6248

Merged
merged 15 commits into from Oct 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/assets/stylesheets/aggregate_status_card.scss
@@ -0,0 +1,4 @@
.card-pf-body .miq-quadicon {
margin-left: auto;
margin-right: auto;
}
1 change: 1 addition & 0 deletions app/assets/stylesheets/application.css
Expand Up @@ -14,4 +14,5 @@
*= require ./service_dialogs
*= require ./remote_console
*= require ./piecharts
*= require ./aggregate_status_card
*/
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Expand Up @@ -38,6 +38,7 @@ class ApplicationController < ActionController::Base
include Mixins::CheckedIdMixin
include ParamsHelper
include ApplicationHelper::Toolbar::Mixins::CustomButtonToolbarMixin
include QuadiconHelper

helper ToolbarHelper
helper JsHelper
Expand Down
4 changes: 4 additions & 0 deletions app/helpers/application_helper.rb
Expand Up @@ -1329,4 +1329,8 @@ def safe_right_cell_text
return '' if @right_cell_text.nil?
ActiveSupport::SafeBuffer === @right_cell_text ? raw(@right_cell_text) : @right_cell_text
end

def camelize_quadicon(quad)
quad.keys.each_with_object({}) { |k, h| h[k.to_s.camelcase(:lower)] = quad[k] }
end
end
82 changes: 82 additions & 0 deletions app/javascript/components/aggregate_status_card.jsx
@@ -0,0 +1,82 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';

import { Quadicon } from '@manageiq/react-ui-components/dist/quadicon';

import PfAggregateStatusCard from './pf_aggregate_status_card';
import { http } from '../http_api/';

const AggregateStatusCard = ({ providerId, providerType }) => {
const [data, setCardData] = useState({ loading: true });

useEffect(() => {
const url = `/${providerType}_dashboard/aggregate_status_data/${providerId || ''}`;
http.get(url)
.then((response) => {
const aggStatusData = response.data.aggStatus;
setCardData({
loading: false,
status: aggStatusData.status,
quadicon: aggStatusData.quadicon,
aggStatus: aggStatusData.attrData,
showTopBorder: aggStatusData.showTopBorder,
aggregateLayout: aggStatusData.aggregateLayout,
aggregateClass: aggStatusData.aggregateClass,
});
});
}, []);

if (data.loading) {
return <div>loading...</div>;
}

const renderPlain = () => (
<PfAggregateStatusCard
layout={data.aggregateLayout}
className={data.aggregateClass}
data={data.status}
showTopBorder={data.showTopBorder}
/>
);

const renderQuad = () => (
<div className="card-pf card-pf-aggregate-status">
<h2 className="card-pf-body">
<Quadicon data={data.quadicon} />
</h2>
</div>
);

return (
<div className="aggregate_status">
<div className="col-xs-12 col-sm-12 col-md-3 col-lg-2 here">
{ data.quadicon ? renderQuad() : renderPlain() }
</div>
<div className="col-xs-12 col-sm-12 col-md-9 col-lg-10">
<div className="row">
{ data.aggStatus.map(st => (
<div key={st.id} className="col-xs-12 col-sm-6 col-md-4 col-lg-3">
<PfAggregateStatusCard
layout="mini"
id={st.id}
data={st}
showTopBorder
/>
</div>
))}
</div>
</div>
</div>
);
};

AggregateStatusCard.propTypes = {
providerId: PropTypes.string,
providerType: PropTypes.string.isRequired,
};

AggregateStatusCard.defaultProps = {
providerId: null,
};

export default AggregateStatusCard;
123 changes: 123 additions & 0 deletions app/javascript/components/pf_aggregate_status_card.jsx
@@ -0,0 +1,123 @@
import React from 'react';
import PropTypes from 'prop-types';

/* eslint react/jsx-one-expression-per-line: "off" */
/* need to keep the spaces before { count } */
const PfAggregateStatusCard = ({
showTopBorder, altLayout, layout, data,
}) => {
const shouldShowTopBorder = () => (showTopBorder === true);
const isAltLayout = () => (altLayout === true || layout === 'tall');
const isMiniLayout = () => (layout === 'mini');

const normalLayoutClass = `card-pf card-pf-aggregate-status
${isAltLayout() ? ' card-pf-aggregate-status-alt' : ''}
${shouldShowTopBorder() ? ' card-pf-accented' : ''}`;

const notifications = data.notifications || [];

const renderNormalLayout = () => (
<div className={normalLayoutClass}>
<h2 className="card-pf-title">
<a href={data.href}>
{ data.iconImage && (
data.largeIcon
? <img src={data.iconImage} alt="" width={72} height={72} />
: <img src={data.iconImage} alt="" className="card-pf-icon-image" />
)}
<span className={data.iconClass} />
<span className="card-pf-aggregate-status-count">{data.count}</span>
<span className="card-pf-aggregate-status-title">{data.title}</span>
</a>
</h2>
<div className="card-pf-body">
<p className="card-pf-aggregate-status-notifications">
{ notifications.map(notification => (
<span key={notification} className="card-pf-aggregate-status-notification">
<a href={notification.href}>
{ notification.iconImage && (
<React.Fragment>
<img src={notification.iconImage} alt="" className="card-pf-icon-image" />
<span className={notification.iconClass} />
<span> { notification.count }</span>
</React.Fragment>
)}
</a>
</span>
))}
</p>
</div>
</div>
);

const displayNotification = () =>
data.notification && (data.notification.iconImage || data.notification.iconClass || data.notification.count);

const miniLayoutClass = `card-pf card-pf-aggregate-status card-pf-aggregate-status-mini${shouldShowTopBorder() ? ' card-pf-accented' : ''}`;

const renderMiniLayout = () => (
<div className={miniLayoutClass}>
<h2 className="card-pf-title">
<a href={data.href}>
{ data.iconImage && <img src={data.iconImage} alt="" className="card-pf-icon-image" /> }
{ data.iconClass && <span className={data.iconClass} /> }
<span className="card-pf-aggregate-status-count"> {data.count}</span>
{data.title}
</a>
</h2>
<div className="card-pf-body">
{ displayNotification() && (
<p className="card-pf-aggregate-status-notifications">
<span className="card-pf-aggregate-status-notification" title={data.notification.tooltip}>
{ data.notification.href
? (
<a href={data.notification.href}>
{ data.notification.iconImage && <img src={data.notification.iconImage} alt="" className="card-pf-icon-image" /> }
{ data.notification.iconClass && <span className={data.notification.iconClass} /> }
{ data.notification.count && <span> {data.notification.count}</span> }
</a>)
: (
<span>
{ data.notification.iconImage && <img src={data.notification.iconImage} alt="" className="card-pf-icon-image" /> }
{ data.notification.iconClass && <span className={data.notification.iconClass} /> }
{ data.notification.count && <span> {data.notification.count}</span> }
</span>
)
}
</span>
</p>
)}
</div>
</div>
);

return isMiniLayout() ? renderMiniLayout() : renderNormalLayout();
};

PfAggregateStatusCard.propTypes = {
layout: PropTypes.string,
className: PropTypes.string,
data: PropTypes.shape({
iconImage: PropTypes.string,
iconClass: PropTypes.string,
largeIcon: PropTypes.bool,
notifications: PropTypes.arrayOf(PropTypes.any),
notification: PropTypes.shape({
iconImage: PropTypes.string,
iconClass: PropTypes.string,
count: PropTypes.number,
}),
}).isRequired,
showTopBorder: PropTypes.bool.isRequired,
altLayout: PropTypes.bool,
};

PfAggregateStatusCard.defaultProps = {
layout: null,
className: null,
data: {},
altLayout: false,
showTopBorder: false,
};

export default PfAggregateStatusCard;
5 changes: 5 additions & 0 deletions app/javascript/packs/component-definitions-common.js
@@ -1,9 +1,11 @@
import React from 'react';

import '@manageiq/react-ui-components/dist/tagging.css';
import '@manageiq/react-ui-components/dist/quadicon.css';
import { TagGroup, TableListView, GenericGroup } from '@manageiq/react-ui-components/dist/textual_summary';
import { TagView } from '@manageiq/react-ui-components/dist/tagging';

import AggregateStatusCard from '../components/aggregate_status_card';
import Breadcrumbs from '../components/breadcrumbs';
import CatalogForm from '../components/catalog-form/catalog-form';
import CloudNetworkForm from '../components/cloud-network-form/cloud-network-form';
Expand All @@ -22,6 +24,7 @@ import OptimizationList from '../optimization/optimization_list';
import OpsTenantForm from '../components/ops-tenant-form/ops-tenant-form';
import OrcherstrationTemplateForm from '../components/orchestration-template/orcherstration-template-form';
import PxeServersForm from '../components/pxe-servers-form/pxe-server-form';
import { Quadicon } from '@manageiq/react-ui-components/dist/quadicon';
import RemoveCatalogItemModal from '../components/remove-catalog-item-modal';
import ReportDataTable from '../components/report-data-table';
import ServiceDialogFromForm from '../components/service-dialog-from-form/service-dialog-from';
Expand All @@ -40,6 +43,7 @@ import WorkersForm from '../components/workers-form/workers-form';
* ManageIQ.component.addReact('ComponentName', props => <ComponentName {...props} />);
*/

ManageIQ.component.addReact('AggregateStatusCard', AggregateStatusCard);
ManageIQ.component.addReact('Breadcrumbs', Breadcrumbs);
ManageIQ.component.addReact('CatalogForm', CatalogForm);
ManageIQ.component.addReact('CloudNetworkForm', CloudNetworkForm);
Expand All @@ -59,6 +63,7 @@ ManageIQ.component.addReact('OptimizationList', OptimizationList);
ManageIQ.component.addReact('OpsTenantForm', OpsTenantForm);
ManageIQ.component.addReact('OrcherstrationTemplateForm', OrcherstrationTemplateForm);
ManageIQ.component.addReact('PxeServersForm', PxeServersForm);
ManageIQ.component.addReact('Quadicon', Quadicon);
ManageIQ.component.addReact('RemoveCatalogItemModal', RemoveCatalogItemModal);
ManageIQ.component.addReact('ReportDataTable', ReportDataTable);
ManageIQ.component.addReact('ServiceDialogFromForm', ServiceDialogFromForm);
Expand Down
1 change: 1 addition & 0 deletions app/services/ems_cloud_dashboard_service.rb
Expand Up @@ -15,6 +15,7 @@ def aggregate_status_data

def aggregate_status
{
:quadicon => @controller.instance_exec(@ems, &EmsInfraDashboardService.quadicon_calc),
:status => status_data,
:attrData => attributes_data,
}
Expand Down
4 changes: 4 additions & 0 deletions app/services/ems_dashboard_service.rb
Expand Up @@ -83,4 +83,8 @@ def status_data
def get_url(resource, ems_id, attr_url)
"/#{resource}/#{ems_id}?display=#{attr_url}"
end

def self.quadicon_calc
@quadicon_calc ||= proc { |ems| camelize_quadicon(quadicon_hash(ems)) }
end
end
1 change: 1 addition & 0 deletions app/services/ems_infra_dashboard_service.rb
Expand Up @@ -31,6 +31,7 @@ def aggregate_status_data

def aggregate_status
{
:quadicon => @controller.instance_exec(@ems, &EmsInfraDashboardService.quadicon_calc),
:status => status_data,
:attrData => attributes_data,
}
Expand Down
4 changes: 2 additions & 2 deletions app/services/ems_physical_infra_dashboard_service.rb
Expand Up @@ -24,15 +24,15 @@ def aggregate_status
{
:status => single_provider,
:attrData => attributes_data,
:showTopBorder => 'false',
:showTopBorder => false,
:aggregateClass => 'aggregate-object-card',
:aggregateLayout => '',
}
else
{
:status => multiple_providers,
:attrData => attributes_data,
:showTopBorder => 'true',
:showTopBorder => true,
:aggregateClass => '',
:aggregateLayout => 'tall',
}
Expand Down
2 changes: 1 addition & 1 deletion app/views/cloud_tenant/_show_dashboard.html.haml
@@ -1,7 +1,7 @@
= render :partial => "layouts/flash_msg"
.container-fluid.container-tiles-pf.cloud-tenant-dashboard
.row.row-tile-pf
%aggregate-status-card{:provider_id => @record.id, :provider_type => "cloud_tenant"}
= react 'AggregateStatusCard', {:providerId => @record.id.to_s, :providerType => 'cloud_tenant'}
.row.row-tile-pf
.col-xs-12.col-sm-12.col-md-6
= render :partial => "recent-instances-line-chart"
Expand Down
2 changes: 1 addition & 1 deletion app/views/ems_cloud/_show_dashboard.html.haml
@@ -1,7 +1,7 @@
= render :partial => "layouts/flash_msg"
.container-fluid.container-tiles-pf.ems-cloud-dashboard
.row.row-tile-pf
%aggregate-status-card{:provider_id => @record.id, :provider_type => "ems_cloud"}
= react 'AggregateStatusCard', {:providerId => @record.id.to_s, :providerType => 'ems_cloud'}
.row.row-tile-pf
.col-xs-12.col-sm-12.col-md-6
= render :partial => "recent-instances-line-chart"
Expand Down
2 changes: 1 addition & 1 deletion app/views/ems_infra/_show_dashboard.html.haml
@@ -1,7 +1,7 @@
= render :partial => "layouts/flash_msg"
.container-fluid.container-tiles-pf.ems-infra-dashboard
.row.row-tile-pf
%aggregate-status-card{:provider_id => @record.id, :provider_type => 'ems_infra'}
= react 'AggregateStatusCard', {:providerId => @record.id.to_s, :providerType => 'ems_infra'}
.row.row-tile-pf
.col-xs-12.col-sm-12.col-md-6
= render :partial => "utilization-trend-chart"
Expand Down
2 changes: 1 addition & 1 deletion app/views/ems_physical_infra/_show_dashboard.html.haml
@@ -1,7 +1,7 @@
= render :partial => "layouts/flash_msg"
.container-fluid.container-tiles-pf.ems-physical-infra-dashboard
.row.row-tile-pf
%aggregate-status-card{:provider_id => @record.id, :provider_type => 'ems_physical_infra'}
= react 'AggregateStatusCard', {:providerId => @record.id.to_s, :providerType => 'ems_physical_infra'}
.row.row-tile-pf
.col-xs-12.col-sm-12.col-md-6
%server-health-pie-chart{:provider_id => @record.id}
Expand Down
2 changes: 1 addition & 1 deletion app/views/physical_infra_overview/show.html.haml
@@ -1,7 +1,7 @@
= render :partial => "layouts/flash_msg"
.container-fluid.container-tiles-pf.ems-physical-infra-dashboard
.row.row-tile-pf
%aggregate-status-card{:provider_type => 'ems_physical_infra'}
= react 'AggregateStatusCard', {:providerType => 'ems_physical_infra'}
.row.row-tile-pf
.col-xs-12.col-sm-12.col-md-6
%server-health-pie-chart
Expand Down