Skip to content

Commit

Permalink
Added label instead of Alert
Browse files Browse the repository at this point in the history
  • Loading branch information
lokanandaprabhu committed Sep 9, 2022
1 parent 6dcbbd2 commit 38b459c
Show file tree
Hide file tree
Showing 12 changed files with 305 additions and 71 deletions.
1 change: 0 additions & 1 deletion frontend/packages/dev-console/locales/en/devconsole.json
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,6 @@
"{{kindForRefPlural}} in {{apiVersionForRefPlural}}": "{{kindForRefPlural}} in {{apiVersionForRefPlural}}",
"The server doesn't have a resource type {{missingType}}. Try refreshing the page if it was recently added.": "The server doesn't have a resource type {{missingType}}. Try refreshing the page if it was recently added.",
"Select a Project to view the list of {{projectLabelPlural}} or <4>create a Project</4>.": "Select a Project to view the list of {{projectLabelPlural}} or <4>create a Project</4>.",
"Resource quota reached": "Resource quota reached",
"{{count}} resource reached quota": "{{count}} resource reached quota",
"{{count}} resource reached quota_plural": "{{count}} resources reached quota",
"Select a Project to search inside or <2>create a Project</2>.": "Select a Project to search inside or <2>create a Project</2>.",
Expand Down
6 changes: 1 addition & 5 deletions frontend/packages/dev-console/src/components/add/AddPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { match as RMatch } from 'react-router';
import { withStartGuide } from '../../../../../public/components/start-guide';
import NamespacedPage, { NamespacedPageVariants } from '../NamespacedPage';
import CreateProjectListPage from '../projects/CreateProjectListPage';
import { ResourceQuotaAlert } from '../resource-quota/ResourceQuotaAlert';
import AddPageLayout from './AddPageLayout';

export interface AddPageProps {
Expand All @@ -21,10 +20,7 @@ export const PageContents: React.FC<AddPageProps> = ({ match }) => {
const namespace = match.params.ns;

return namespace ? (
<>
<ResourceQuotaAlert namespace={namespace} />
<AddPageLayout title={t('devconsole~Add')} />
</>
<AddPageLayout title={t('devconsole~Add')} />
) : (
<CreateProjectListPage title={t('devconsole~Add')}>
{(openProjectModal) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,8 @@
&__additional-hint-block {
padding-bottom: var(--pf-global--spacer--md);
}

&__resource-quota-message-block {
margin-right: var(--pf-global--spacer--xl);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import TopologyQuickSearch from '@console/topology/src/components/quick-search/T
import TopologyQuickSearchButton from '@console/topology/src/components/quick-search/TopologyQuickSearchButton';
import { filterNamespaceScopedUrl } from '../../utils/add-page-utils';
import { useAddActionExtensions } from '../../utils/useAddActionExtensions';
import { ResourceQuotaAlert } from '../resource-quota/ResourceQuotaAlert';
import AddCardSection from './AddCardSection';
import { GETTING_STARTED_USER_SETTINGS_KEY } from './constants';
import { GettingStartedSection } from './GettingStartedSection';
Expand Down Expand Up @@ -65,6 +66,9 @@ const AddPageLayout: React.FC<AddPageLayoutProps> = ({ title, hintBlock: additio
<TopologyQuickSearchButton onClick={() => setIsQuickSearchOpen(true)} />
</div>
<div className="odc-add-page-layout__hint-block__actions">
<div className="odc-add-page-layout__resource-quota-message-block">
<ResourceQuotaAlert namespace={activeNamespace} />
</div>
<RestoreGettingStartedButton userSettingsKey={GETTING_STARTED_USER_SETTINGS_KEY} />
<div
className={cx('odc-add-page-layout__hint-block__details-switch', {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import * as React from 'react';
import { Alert } from '@patternfly/react-core';
import { Label } from '@patternfly/react-core';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { useK8sWatchResource } from '@console/dynamic-plugin-sdk/src/api/core-api';
import {
useK8sWatchResources,
YellowExclamationTriangleIcon,
} from '@console/dynamic-plugin-sdk/src/api/core-api';
import { resourcePathFromModel } from '@console/internal/components/utils/resource-link';
import { AppliedClusterResourceQuotaModel, ResourceQuotaModel } from '@console/internal/models';
import { AppliedClusterResourceQuotaKind, ResourceQuotaKind } from '@console/internal/module/k8s';
import { checkQuotaLimit } from '@console/topology/src/components/utils/checkResourceQuota';
Expand All @@ -14,30 +18,52 @@ export interface ResourceQuotaAlertProps {
export const ResourceQuotaAlert: React.FC<ResourceQuotaAlertProps> = ({ namespace }) => {
const { t } = useTranslation();
const [warningMessageFlag, setWarningMessageFlag] = React.useState<boolean>();
const [resourceQuotaName, setResourceQuotaName] = React.useState<string>('');
const [resourceQuotaKind, setResourceQuotaKind] = React.useState<string>('');
const [resourceQuotaName, setResourceQuotaName] = React.useState(null);
const [resourceQuotaKind, setResourceQuotaKind] = React.useState(null);

const [quotas, rqLoaded] = useK8sWatchResource<ResourceQuotaKind[]>({
groupVersionKind: {
kind: ResourceQuotaModel.kind,
version: ResourceQuotaModel.apiVersion,
},
namespace,
isList: true,
});
const watchedResources = React.useMemo(
() => ({
resourcequotas: {
groupVersionKind: {
kind: ResourceQuotaModel.kind,
version: ResourceQuotaModel.apiVersion,
},
namespace,
isList: true,
},
appliedclusterresourcequotas: {
groupVersionKind: {
kind: AppliedClusterResourceQuotaModel.kind,
version: AppliedClusterResourceQuotaModel.apiVersion,
group: AppliedClusterResourceQuotaModel.apiGroup,
},
namespace,
isList: true,
},
}),
[namespace],
);

const { resourcequotas, appliedclusterresourcequotas } = useK8sWatchResources<{
resourcequotas: ResourceQuotaKind[];
appliedclusterresourcequotas: AppliedClusterResourceQuotaKind[];
}>(watchedResources);

const [clusterQuotas, acrqLoaded] = useK8sWatchResource<AppliedClusterResourceQuotaKind[]>({
groupVersionKind: {
kind: AppliedClusterResourceQuotaModel.kind,
version: AppliedClusterResourceQuotaModel.apiVersion,
group: AppliedClusterResourceQuotaModel.apiGroup,
},
namespace,
isList: true,
});
const [totalRQatQuota = [], quotaName, quotaKind] = React.useMemo(
() =>
resourcequotas.loaded && !resourcequotas.loadError
? checkQuotaLimit(resourcequotas.data)
: [],
[resourcequotas],
);

const [totalRQatQuota, quotaName, quotaKind] = checkQuotaLimit(quotas);
const [totalACRQatQuota, clusterRQName, clusterRQKind] = checkQuotaLimit(clusterQuotas);
const [totalACRQatQuota = [], clusterRQName, clusterRQKind] = React.useMemo(
() =>
appliedclusterresourcequotas.loaded && !appliedclusterresourcequotas.loadError
? checkQuotaLimit(appliedclusterresourcequotas.data)
: [],
[appliedclusterresourcequotas],
);

let totalResourcesAtQuota = [...totalRQatQuota, ...totalACRQatQuota];
totalResourcesAtQuota = totalResourcesAtQuota.filter((resourceAtQuota) => resourceAtQuota !== 0);
Expand All @@ -47,35 +73,38 @@ export const ResourceQuotaAlert: React.FC<ResourceQuotaAlertProps> = ({ namespac
setResourceQuotaName(quotaName || clusterRQName);
setResourceQuotaKind(quotaKind || clusterRQKind);
} else {
setResourceQuotaName('');
setResourceQuotaKind('');
setResourceQuotaName(null);
setResourceQuotaKind(null);
}
}, [clusterRQKind, clusterRQName, totalResourcesAtQuota, quotaKind, quotaName]);

React.useEffect(() => {
if (totalResourcesAtQuota.length > 0) {
setWarningMessageFlag(true);
} else {
setWarningMessageFlag(false);
}
}, [clusterRQKind, clusterRQName, totalResourcesAtQuota, quotaKind, quotaName]);
}, [totalResourcesAtQuota]);

const getRedirectLink = () => {
if (resourceQuotaName && resourceQuotaKind === AppliedClusterResourceQuotaModel.kind) {
return `/k8s/ns/${namespace}/${AppliedClusterResourceQuotaModel.apiGroup}~${AppliedClusterResourceQuotaModel.apiVersion}~${AppliedClusterResourceQuotaModel.kind}/${resourceQuotaName}`;
return resourcePathFromModel(AppliedClusterResourceQuotaModel, resourceQuotaName, namespace);
}
if (resourceQuotaName) {
return `/k8s/ns/${namespace}/${ResourceQuotaModel.plural}/${resourceQuotaName}`;
return resourcePathFromModel(ResourceQuotaModel, resourceQuotaName, namespace);
}
return `/k8s/ns/${namespace}/${ResourceQuotaModel.plural}`;
return resourcePathFromModel(ResourceQuotaModel, null, namespace);
};
return (
<>
{warningMessageFlag && rqLoaded && acrqLoaded ? (
<Alert variant="warning" title={t('devconsole~Resource quota reached')} isInline>
<Link to={getRedirectLink()}>
{warningMessageFlag && resourcequotas.loaded && appliedclusterresourcequotas.loaded ? (
<Label color="orange" icon={<YellowExclamationTriangleIcon />}>
<Link to={getRedirectLink()} data-test="resource-quota-warning">
{t('devconsole~{{count}} resource reached quota', {
count: totalResourcesAtQuota.reduce((a, b) => a + b, 0),
})}
</Link>
</Alert>
</Label>
) : null}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,7 @@ When('user enters the {string} file data to YAML Editor', (yamlFile: string) =>
});

When('user clicks on link to view resource quota details', () => {
cy.get('h4.pf-c-alert__title').should('contain.text', 'Resource quota reached');
cy.get('a')
.contains('reached quota')
.click();
cy.byTestID('resource-quota-warning').click();
});

Then('user is redirected to resource quota details page', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import { observer } from '@patternfly/react-topology';
import { useTranslation } from 'react-i18next';
import ResourceQuotaAlert from '@console/dev-console/src/components/resource-quota/ResourceQuotaAlert';
import { StatusBox } from '@console/internal/components/utils';
import { ModelContext, ExtensibleModel } from '../../data-transforms/ModelContext';
import { TopologyViewType } from '../../topology-types';
Expand All @@ -17,27 +16,24 @@ const TopologyDataRenderer: React.FC<TopologyDataRendererProps> = observer(
const { namespace, model, loaded, loadError } = React.useContext<ExtensibleModel>(ModelContext);

return (
<>
<ResourceQuotaAlert namespace={namespace} />
<StatusBox
skeleton={
viewType === TopologyViewType.list && (
<div className="co-m-pane__body skeleton-overview">
<div className="skeleton-overview--head" />
<div className="skeleton-overview--tile" />
<div className="skeleton-overview--tile" />
<div className="skeleton-overview--tile" />
</div>
)
}
data={model}
label={t('topology~Topology')}
loaded={loaded}
loadError={loadError}
>
<DroppableTopologyComponent viewType={viewType} model={model} namespace={namespace} />
</StatusBox>
</>
<StatusBox
skeleton={
viewType === TopologyViewType.list && (
<div className="co-m-pane__body skeleton-overview">
<div className="skeleton-overview--head" />
<div className="skeleton-overview--tile" />
<div className="skeleton-overview--tile" />
<div className="skeleton-overview--tile" />
</div>
)
}
data={model}
label={t('topology~Topology')}
loaded={loaded}
loadError={loadError}
>
<DroppableTopologyComponent viewType={viewType} model={model} namespace={namespace} />
</StatusBox>
);
},
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { checkQuotaLimit } from '../checkResourceQuota';
import {
singleResourceQuota,
multipleResourceQuota,
noResourceAtQuota,
twoResourceAtQuota,
} from './mockData';

describe('get resources at quota', () => {
it('should return one resource at quota', () => {
const [totalRQatQuota = [], quotaName, quotaKind] = checkQuotaLimit(singleResourceQuota);
expect(totalRQatQuota.length).toEqual(1);
expect(quotaName).toEqual('example');
expect(quotaKind).toEqual('ResourceQuota');
});

it('should return one resource at quota out of two resource quotas', () => {
const [totalRQsatQuota = [], quotaName, quotaKind] = checkQuotaLimit(multipleResourceQuota);
const totalRQatQuota = totalRQsatQuota.filter((resourceAtQuota) => resourceAtQuota !== 0);
expect(totalRQatQuota.length).toEqual(1);
expect(quotaName).toEqual('example');
expect(quotaKind).toEqual('ResourceQuota');
});

it('should return no resource at quota', () => {
const [totalRQsatQuota = [], quotaName, quotaKind] = checkQuotaLimit(noResourceAtQuota);
const totalRQatQuota = totalRQsatQuota.filter((resourceAtQuota) => resourceAtQuota !== 0);
expect(totalRQatQuota.length).toEqual(0);
expect(quotaName).toEqual('');
expect(quotaKind).toEqual('');
});

it('should return two resource at quota', () => {
let [totalRQatQuota = []] = checkQuotaLimit(twoResourceAtQuota);
totalRQatQuota = totalRQatQuota.filter((resourceAtQuota) => resourceAtQuota !== 0);
expect(totalRQatQuota.length).toEqual(2);
});
});
Loading

0 comments on commit 38b459c

Please sign in to comment.