Skip to content

Commit

Permalink
SLAs: Add Enterprise Premium support information (#689)
Browse files Browse the repository at this point in the history
* SLAs: Add Enterprise Premium support information
  • Loading branch information
aleixhub authored Nov 15, 2022
1 parent bca399b commit 7df9f65
Show file tree
Hide file tree
Showing 20 changed files with 473 additions and 70 deletions.
2 changes: 2 additions & 0 deletions catalog/helm/templates/oauth-proxy/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ spec:
- --logout-url=/
- --provider=openshift
- --skip-auth-regex=^/(api/)?workshop($|/.*)
- --skip-auth-regex=^/support($|/)
- --skip-auth-regex=^/fonts/.*
- --skip-auth-regex=^/images/.*
- --skip-auth-regex=^/public/.*
- --skip-auth-regex=(.js|.html|.css|.map|.txt)($|\?.*)
- --skip-provider-button=true
- --tls-cert=/etc/tls/private/tls.crt
Expand Down
95 changes: 58 additions & 37 deletions catalog/ui/src/app/Catalog/CatalogItemCard.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import React from 'react';
import { Link, useLocation, useParams } from 'react-router-dom';
import { Badge, CardBody, CardHeader, Split, SplitItem, Title } from '@patternfly/react-core';
import { Badge, CardBody, CardHeader, Split, SplitItem, Title, Tooltip } from '@patternfly/react-core';
import { CatalogItem } from '@app/types';
import CatalogItemIcon from './CatalogItemIcon';
import { formatString, getDescription, getIsDisabled, getProvider, getStage, getStatus } from './catalog-utils';
import StatusPageIcons from '@app/components/StatusPageIcons';
import { displayName, renderContent, stripHtml } from '@app/util';
import EnterprisePremiumIcon from '@app/Support/EnterprisePremiumIcon';
import {
formatString,
getDescription,
getIsDisabled,
getProvider,
getStage,
getStatus,
getSupportType,
} from './catalog-utils';
import CatalogItemIcon from './CatalogItemIcon';

import './catalog-item-card.css';

Expand All @@ -18,6 +27,7 @@ const CatalogItemCard: React.FC<{ catalogItem: CatalogItem }> = ({ catalogItem }
const stage = getStage(catalogItem);
const isDisabled = getIsDisabled(catalogItem);
const { code: status } = getStatus(catalogItem);
const supportType = getSupportType(catalogItem);

if (!urlSearchParams.has('item')) {
if (namespace) {
Expand All @@ -28,41 +38,52 @@ const CatalogItemCard: React.FC<{ catalogItem: CatalogItem }> = ({ catalogItem }
}

return (
<Link
className={`catalog-item-card ${isDisabled ? 'catalog-item-card--disabled' : ''}`}
to={`${location.pathname}?${urlSearchParams.toString()}`}
>
<CardHeader className="catalog-item-card__header">
<Split>
<SplitItem>
<CatalogItemIcon catalogItem={catalogItem} />
{status && status !== 'operational' ? (
<StatusPageIcons status={status} className="catalog-item-card__statusPageIcon" />
) : null}
</SplitItem>
<SplitItem className="catalog-item-card__badges" isFilled>
{stage === 'dev' ? (
<Badge className="catalog-item-card__badges--dev">development</Badge>
) : stage === 'test' ? (
<Badge className="catalog-item-card__badges--test">test</Badge>
<div className="catalog-item-card__wrapper">
{supportType && stage === 'prod' ? (
<Tooltip content={<p>{supportType.replace(/_+/g, ' ')}</p>}>
<a href="/support" className="catalog-item-card__support-type">
{supportType === 'Enterprise_Premium' ? (
<EnterprisePremiumIcon className="catalog-iten-card__support-type-icon catalog-iten-card__support-type-icon--enterprise-premium" />
) : null}
</SplitItem>
</Split>
</CardHeader>
<CardBody className="catalog-item-card__body">
<Title className="catalog-item-card__title" headingLevel="h3">
{displayName(catalogItem)}
</Title>
<Title className="catalog-item-card__subtitle" headingLevel="h6">
provided by {formatString(provider)}
</Title>
<div className="catalog-item-card__description">
{description
? stripHtml(renderContent(description, { format: descriptionFormat })).slice(0, 150)
: 'No description available.'}
</div>
</CardBody>
</Link>
</a>
</Tooltip>
) : null}
<Link
className={`catalog-item-card ${isDisabled ? 'catalog-item-card--disabled' : ''}`}
to={`${location.pathname}?${urlSearchParams.toString()}`}
>
<CardHeader className="catalog-item-card__header">
<Split>
<SplitItem>
<CatalogItemIcon catalogItem={catalogItem} />
{status && status !== 'operational' ? (
<StatusPageIcons status={status} className="catalog-item-card__statusPageIcon" />
) : null}
</SplitItem>
<SplitItem className="catalog-item-card__badges" isFilled>
{stage === 'dev' ? (
<Badge className="catalog-item-card__badges--dev">development</Badge>
) : stage === 'test' ? (
<Badge className="catalog-item-card__badges--test">test</Badge>
) : null}
</SplitItem>
</Split>
</CardHeader>
<CardBody className="catalog-item-card__body">
<Title className="catalog-item-card__title" headingLevel="h3">
{displayName(catalogItem)}
</Title>
<Title className="catalog-item-card__subtitle" headingLevel="h6">
provided by {formatString(provider)}
</Title>
<div className="catalog-item-card__description">
{description
? stripHtml(renderContent(description, { format: descriptionFormat })).slice(0, 150)
: 'No description available.'}
</div>
</CardBody>
</Link>
</div>
);
};

Expand Down
2 changes: 1 addition & 1 deletion catalog/ui/src/app/Catalog/CatalogItemForm.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('CatalogItemForm Component', () => {
expect(catalogItemDisplayName).toBeInTheDocument();
expect(sfidLabel).toBeInTheDocument();
expect(purposeLabel.closest('.pf-c-form__group').textContent).toContain(purposePlaceholder);
expect(termsOfServiceLabel.closest('.catalog-terms-of-service').textContent).toContain(termsOfServiceAck);
expect(termsOfServiceLabel.closest('.terms-of-service').textContent).toContain(termsOfServiceAck);
});

test('When Cancel button is clicked the history goBack function is called', async () => {
Expand Down
42 changes: 42 additions & 0 deletions catalog/ui/src/app/Catalog/catalog-item-card.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.catalog-item-card__wrapper {
position: relative;
}

.catalog-item-card {
color: var(--pf-global--Color--100);
display: flex;
Expand All @@ -9,6 +13,44 @@
padding: var(--pf-global--spacer--lg);
overflow: hidden;
}
.catalog-item-card__support-type:before {
transform: rotate(-45deg);
left: -3px;
content: '';
width: 0px;
height: 0px;
position: absolute;
border: 4px solid transparent;
border-top-color: var(--pf-global--danger-color--200);
top: 2px;
}
.catalog-item-card__support-type {
top: -6px;
right: var(--pf-global--spacer--lg);
padding: 8px;
width: 32px;
margin: auto;
box-sizing: border-box;
background-color: var(--pf-global--danger-color--100);
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
color: #ffffff;
position: absolute;
z-index: 1;
box-shadow: 0px 5px 10px rgb(0 0 0 / 25%);
height: 42px;
}

.catalog-iten-card__support-type-icon {
width: 32px;
position: absolute;
padding: 10px;
left: 0;
top: 0;
}

.catalog-item-card--disabled {
opacity: 0.45;
}
Expand Down
8 changes: 4 additions & 4 deletions catalog/ui/src/app/Catalog/catalog-item-form.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@
align-items: center;
}

.catalog-item-form__form .catalog-terms-of-service {
.catalog-item-form__form .terms-of-service {
background-color: var(--pf-global--BackgroundColor--light-300);
padding: var(--pf-global--spacer--md);
}
.catalog-item-form__form .catalog-terms-of-service .pf-c-form__label {
.catalog-item-form__form .terms-of-service .pf-c-form__label {
cursor: default;
}

.catalog-item-form__group-control--single,
.catalog-item-form__group-control--multi,
.catalog-item-form__form .catalog-terms-of-service .pf-c-form__group-control {
.catalog-item-form__form .terms-of-service .pf-c-form__group-control {
display: flex;
flex-direction: row;
gap: var(--pf-global--spacer--md);
Expand All @@ -31,7 +31,7 @@
.catalog-item-form__group-control--multi {
padding-bottom: var(--pf-global--spacer--sm);
}
.catalog-item-form__form .catalog-terms-of-service .pf-c-form__group-control {
.catalog-item-form__form .terms-of-service .pf-c-form__group-control {
flex-direction: column;
align-items: flex-start;
}
Expand Down
8 changes: 4 additions & 4 deletions catalog/ui/src/app/Catalog/catalog-item-request.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
align-items: center;
}

.catalog-item-request .catalog-terms-of-service {
.catalog-item-request .terms-of-service {
background-color: var(--pf-global--BackgroundColor--light-300);
padding: var(--pf-global--spacer--md);
}
.catalog-terms-of-service .pf-c-form__label {
.terms-of-service .pf-c-form__label {
cursor: default;
}

.catalog-item-request__group-control--single,
.catalog-item-request__group-control--multi,
.catalog-terms-of-service .pf-c-form__group-control {
.terms-of-service .pf-c-form__group-control {
display: flex;
flex-direction: row;
gap: var(--pf-global--spacer--md);
Expand All @@ -27,7 +27,7 @@
.catalog-item-request__group-control--multi {
padding-bottom: var(--pf-global--spacer--sm);
}
.catalog-terms-of-service .pf-c-form__group-control {
.terms-of-service .pf-c-form__group-control {
flex-direction: column;
align-items: flex-start;
}
Expand Down
11 changes: 10 additions & 1 deletion catalog/ui/src/app/Catalog/catalog-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,19 @@ export function getDescription(catalogItem: CatalogItem): {
(catalogItem.metadata.annotations?.[`${BABYLON_DOMAIN}/descriptionFormat`] as 'html' | 'asciidoc') || 'asciidoc',
};
}

export function getStage(catalogItem: CatalogItem): string | null {
return catalogItem.metadata.labels?.[`${BABYLON_DOMAIN}/stage`];
}

const supportedSupportTypes = ['Enterprise_Premium', 'Enterprise_Standard', 'Community'] as const;
type SupportTypes = typeof supportedSupportTypes[number];
export function getSupportType(catalogItem: CatalogItem): SupportTypes {
const supportType = catalogItem.metadata.labels?.[`${BABYLON_DOMAIN}/Support_Type`] as SupportTypes;
if (!supportedSupportTypes.includes(supportType)) return null;
return supportType;
}

export function getIsDisabled(catalogItem: CatalogItem): boolean {
if (catalogItem.metadata.labels?.[`${BABYLON_DOMAIN}/disabled`]) {
return catalogItem.metadata.labels?.[`${BABYLON_DOMAIN}/disabled`] === 'true';
Expand Down Expand Up @@ -97,5 +106,5 @@ export function setLastFilter(filter: string): void {
export function formatString(string: string): string {
return (string.charAt(0).toUpperCase() + string.slice(1)).replace(/_/g, ' ');
}
export const HIDDEN_LABELS = ['disabled', 'userCatalogItem', 'stage'];
export const HIDDEN_LABELS = ['disabled', 'userCatalogItem', 'stage', 'Support_Type'];
export const HIDDEN_ANNOTATIONS = ['ops', 'displayNameComponent0', 'displayNameComponent1'];
59 changes: 59 additions & 0 deletions catalog/ui/src/app/Header/PublicHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
ApplicationLauncher,
ApplicationLauncherItem,
DropdownPosition,
PageHeader,
PageHeaderTools,
} from '@patternfly/react-core';
import { QuestionCircleIcon } from '@patternfly/react-icons';
import UserInterfaceLogo from '@app/components/UserInterfaceLogo';

import './header.css';

const PublicHeader: React.FC = () => {
const [isUserHelpDropdownOpen, setUserHelpDropdownOpen] = useState(false);
const navigate = useNavigate();

function LogoImg() {
return <UserInterfaceLogo onClick={() => navigate('/')} style={{ width: '278px' }} />;
}
const openSupportCase = (e: { preventDefault: () => void }) => {
e.preventDefault();
window.open('https://red.ht/open-support', '_blank');
};

const UserHelpDropdownItems = [
<ApplicationLauncherItem key="open-support" component="button" onClick={openSupportCase} isExternal>
Open Support Case
</ApplicationLauncherItem>,
<ApplicationLauncherItem
key="status-page-link"
href="https://rhpds-demos.statuspage.io/"
target="_blank"
rel="noreferrer nofollow"
isExternal
>
Status Page
</ApplicationLauncherItem>,
];

const HeaderTools = (
<PageHeaderTools>
<ApplicationLauncher
aria-label="Help menu"
onSelect={() => setUserHelpDropdownOpen((prevIsOpen) => !prevIsOpen)}
onToggle={(isOpen: boolean) => setUserHelpDropdownOpen(isOpen)}
isOpen={isUserHelpDropdownOpen}
items={UserHelpDropdownItems}
position={DropdownPosition.right}
toggleIcon={<QuestionCircleIcon />}
/>
</PageHeaderTools>
);

return <PageHeader logo={<LogoImg />} className="public-header-component" headerTools={HeaderTools} />;
};

export default PublicHeader;
12 changes: 12 additions & 0 deletions catalog/ui/src/app/Support/EnterprisePremiumIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';

const EnterprisePremiumIcon: React.FC<React.HTMLAttributes<HTMLOrSVGElement> & { fill?: string }> = (props) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="24.382 16.489 7.258 13.14" {...props}>
<path
d="M31.62 20.11c0-2-1.63-3.62-3.62-3.62-3.13-.07-4.82 3.93-2.62 6.11V29c0 .25.15.48.39.58.23.1.5.04.68-.13l1.56-1.56 1.56 1.56c.18.18.45.23.68.13.23-.1.39-.32.39-.58v-6.4c.62-.65 1-1.52 1-2.49ZM28 17.73c3.13.08 3.13 4.67 0 4.75-3.13-.08-3.13-4.67 0-4.75Zm.44 8.82a.628.628 0 0 0-.88 0l-.93.93v-4.03c.84.36 1.91.36 2.75 0v4.03l-.93-.93Z"
fill={props.fill || '#fff'}
/>
</svg>
);

export default EnterprisePremiumIcon;
Loading

0 comments on commit 7df9f65

Please sign in to comment.