mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: jira plugin page (#4627)
![image](https://github.com/Unleash/unleash/assets/964450/b58d3d27-4939-42b0-bf0d-51a31aa93dde) ![image](https://github.com/Unleash/unleash/assets/964450/acceedc4-9d53-461a-910f-b60dae0d6b6a)
This commit is contained in:
parent
3b754ec7ed
commit
1d414db982
1
frontend/src/assets/icons/jira-comment.svg
Normal file
1
frontend/src/assets/icons/jira-comment.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg height="2500" preserveAspectRatio="xMidYMid" width="2500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 -30.632388516510233 255.324 285.95638851651023"><linearGradient id="a"><stop offset=".18" stop-color="#0052cc"/><stop offset="1" stop-color="#2684ff"/></linearGradient><linearGradient id="b" x1="98.031%" x2="58.888%" xlink:href="#a" y1=".161%" y2="40.766%"/><linearGradient id="c" x1="100.665%" x2="55.402%" xlink:href="#a" y1=".455%" y2="44.727%"/><path d="M244.658 0H121.707a55.502 55.502 0 0 0 55.502 55.502h22.649V77.37c.02 30.625 24.841 55.447 55.466 55.467V10.666C255.324 4.777 250.55 0 244.658 0z" fill="#2684ff"/><path d="M183.822 61.262H60.872c.019 30.625 24.84 55.447 55.466 55.467h22.649v21.938c.039 30.625 24.877 55.43 55.502 55.43V71.93c0-5.891-4.776-10.667-10.667-10.667z" fill="url(#b)"/><path d="M122.951 122.489H0c0 30.653 24.85 55.502 55.502 55.502h22.72v21.867c.02 30.597 24.798 55.408 55.396 55.466V133.156c0-5.891-4.776-10.667-10.667-10.667z" fill="url(#c)"/></svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -1 +1,15 @@
|
|||||||
<svg height="2500" preserveAspectRatio="xMidYMid" width="2500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 -30.632388516510233 255.324 285.95638851651023"><linearGradient id="a"><stop offset=".18" stop-color="#0052cc"/><stop offset="1" stop-color="#2684ff"/></linearGradient><linearGradient id="b" x1="98.031%" x2="58.888%" xlink:href="#a" y1=".161%" y2="40.766%"/><linearGradient id="c" x1="100.665%" x2="55.402%" xlink:href="#a" y1=".455%" y2="44.727%"/><path d="M244.658 0H121.707a55.502 55.502 0 0 0 55.502 55.502h22.649V77.37c.02 30.625 24.841 55.447 55.466 55.467V10.666C255.324 4.777 250.55 0 244.658 0z" fill="#2684ff"/><path d="M183.822 61.262H60.872c.019 30.625 24.84 55.447 55.466 55.467h22.649v21.938c.039 30.625 24.877 55.43 55.502 55.43V71.93c0-5.891-4.776-10.667-10.667-10.667z" fill="url(#b)"/><path d="M122.951 122.489H0c0 30.653 24.85 55.502 55.502 55.502h22.72v21.867c.02 30.597 24.798 55.408 55.396 55.466V133.156c0-5.891-4.776-10.667-10.667-10.667z" fill="url(#c)"/></svg>
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M31.2987 15.1469L17.9562 1.58157L16.6466 0.25L6.61925 10.4448L2.03534 15.1469C1.54421 15.6464 1.54421 16.4369 2.03534 16.8947L11.2032 26.2157L16.6466 31.75L26.6738 21.5552L26.8375 21.3887L31.2987 16.8947C31.7898 16.3952 31.7898 15.6048 31.2987 15.1469ZM16.6466 20.6813L12.0626 16.0208L16.6466 11.3603L21.2305 16.0208L16.6466 20.6813Z" fill="#2684FF"/>
|
||||||
|
<path d="M16.6465 11.3451C13.6587 8.31147 13.6178 3.36621 16.6055 0.291016L6.61914 10.4724L12.0625 15.9995L16.6465 11.3451Z" fill="url(#paint0_linear_17148_2032)"/>
|
||||||
|
<path d="M21.2714 15.959L16.6465 20.6549C19.6752 23.73 19.6752 28.6752 16.6465 31.7505L26.7148 21.5276L21.2714 15.959Z" fill="url(#paint1_linear_17148_2032)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_17148_2032" x1="15.8439" y1="6.62639" x2="9.34422" y2="13.0277" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.176" stop-color="#0052CC"/>
|
||||||
|
<stop offset="1" stop-color="#2684FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_17148_2032" x1="17.5385" y1="25.3007" x2="24.0253" y2="18.9121" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.176" stop-color="#0052CC"/>
|
||||||
|
<stop offset="1" stop-color="#2684FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.3 KiB |
BIN
frontend/src/assets/img/jira/connect.png
Normal file
BIN
frontend/src/assets/img/jira/connect.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 243 KiB |
BIN
frontend/src/assets/img/jira/cr.png
Normal file
BIN
frontend/src/assets/img/jira/cr.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 243 KiB |
BIN
frontend/src/assets/img/jira/manage.png
Normal file
BIN
frontend/src/assets/img/jira/manage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 243 KiB |
@ -26,7 +26,7 @@ interface ICreateProps {
|
|||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
modal?: boolean;
|
modal?: boolean;
|
||||||
disablePadding?: boolean;
|
disablePadding?: boolean;
|
||||||
formatApiCode: () => string;
|
formatApiCode?: () => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledContainer = styled('section', {
|
const StyledContainer = styled('section', {
|
||||||
@ -165,6 +165,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
|||||||
const { setToastData } = useToast();
|
const { setToastData } = useToast();
|
||||||
const smallScreen = useMediaQuery(`(max-width:${1099}px)`);
|
const smallScreen = useMediaQuery(`(max-width:${1099}px)`);
|
||||||
const copyCommand = () => {
|
const copyCommand = () => {
|
||||||
|
if (formatApiCode !== undefined) {
|
||||||
if (copy(formatApiCode())) {
|
if (copy(formatApiCode())) {
|
||||||
setToastData({
|
setToastData({
|
||||||
title: 'Successfully copied the command',
|
title: 'Successfully copied the command',
|
||||||
@ -182,6 +183,26 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
|||||||
show: true,
|
show: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderApiInfo = (apiDisabled: boolean) => {
|
||||||
|
if (!apiDisabled) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StyledSidebarDivider />
|
||||||
|
<StyledSubtitle>
|
||||||
|
API Command{' '}
|
||||||
|
<Tooltip title="Copy command" arrow>
|
||||||
|
<IconButton onClick={copyCommand} size="large">
|
||||||
|
<StyledIcon />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</StyledSubtitle>
|
||||||
|
<Codebox text={formatApiCode!()} />{' '}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -221,16 +242,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
|||||||
documentationLink={documentationLink}
|
documentationLink={documentationLink}
|
||||||
documentationLinkLabel={documentationLinkLabel}
|
documentationLinkLabel={documentationLinkLabel}
|
||||||
>
|
>
|
||||||
<StyledSidebarDivider />
|
{renderApiInfo(formatApiCode === undefined)}
|
||||||
<StyledSubtitle>
|
|
||||||
API Command{' '}
|
|
||||||
<Tooltip title="Copy command" arrow>
|
|
||||||
<IconButton onClick={copyCommand} size="large">
|
|
||||||
<StyledIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</StyledSubtitle>
|
|
||||||
<Codebox text={formatApiCode()} />
|
|
||||||
</Guidance>
|
</Guidance>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -5,15 +5,19 @@ import { PageContent } from 'component/common/PageContent/PageContent';
|
|||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||||
import { IntegrationCard } from '../IntegrationCard/IntegrationCard';
|
import { IntegrationCard } from '../IntegrationCard/IntegrationCard';
|
||||||
import { StyledCardsGrid } from '../IntegrationList.styles';
|
import { StyledCardsGrid } from '../IntegrationList.styles';
|
||||||
|
import { JIRA_INFO } from '../../JiraIntegration/JiraIntegration';
|
||||||
|
|
||||||
interface IAvailableIntegrationsProps {
|
interface IAvailableIntegrationsProps {
|
||||||
providers: AddonTypeSchema[];
|
providers: AddonTypeSchema[];
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AvailableIntegrations: VFC<IAvailableIntegrationsProps> = ({
|
export const AvailableIntegrations: VFC<IAvailableIntegrationsProps> = ({
|
||||||
providers,
|
providers,
|
||||||
loading,
|
loading,
|
||||||
}) => {
|
}) => {
|
||||||
|
const customProviders = [JIRA_INFO];
|
||||||
|
|
||||||
const ref = useLoading(loading || false);
|
const ref = useLoading(loading || false);
|
||||||
return (
|
return (
|
||||||
<PageContent
|
<PageContent
|
||||||
@ -30,6 +34,16 @@ export const AvailableIntegrations: VFC<IAvailableIntegrationsProps> = ({
|
|||||||
link={`/integrations/create/${name}`}
|
link={`/integrations/create/${name}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
{customProviders?.map(({ name, displayName, description }) => (
|
||||||
|
<IntegrationCard
|
||||||
|
key={name}
|
||||||
|
icon={name}
|
||||||
|
title={displayName || name}
|
||||||
|
description={description}
|
||||||
|
link={`/integrations/view/${name}`}
|
||||||
|
configureActionText={'View integration'}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</StyledCardsGrid>
|
</StyledCardsGrid>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,7 @@ import { DeviceHub } from '@mui/icons-material';
|
|||||||
import { formatAssetPath } from 'utils/formatPath';
|
import { formatAssetPath } from 'utils/formatPath';
|
||||||
|
|
||||||
import slackIcon from 'assets/icons/slack.svg';
|
import slackIcon from 'assets/icons/slack.svg';
|
||||||
|
import jiraCommentIcon from 'assets/icons/jira-comment.svg';
|
||||||
import jiraIcon from 'assets/icons/jira.svg';
|
import jiraIcon from 'assets/icons/jira.svg';
|
||||||
import webhooksIcon from 'assets/icons/webhooks.svg';
|
import webhooksIcon from 'assets/icons/webhooks.svg';
|
||||||
import teamsIcon from 'assets/icons/teams.svg';
|
import teamsIcon from 'assets/icons/teams.svg';
|
||||||
@ -34,7 +35,7 @@ export const IntegrationIcon = ({ name }: IIntegrationIconProps) => {
|
|||||||
<img
|
<img
|
||||||
style={style}
|
style={style}
|
||||||
alt="JIRA logo"
|
alt="JIRA logo"
|
||||||
src={formatAssetPath(jiraIcon)}
|
src={formatAssetPath(jiraCommentIcon)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'webhook':
|
case 'webhook':
|
||||||
@ -61,6 +62,14 @@ export const IntegrationIcon = ({ name }: IIntegrationIconProps) => {
|
|||||||
src={formatAssetPath(dataDogIcon)}
|
src={formatAssetPath(dataDogIcon)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case 'jira':
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
style={style}
|
||||||
|
alt="JIRA logo"
|
||||||
|
src={formatAssetPath(jiraIcon)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Avatar>
|
<Avatar>
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
import { styled, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
import { formatAssetPath } from '../../../utils/formatPath';
|
||||||
|
import { FC } from 'react';
|
||||||
|
|
||||||
|
export const StyledFigure = styled('figure')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
flexDirection: 'column',
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const StyledFigCaption = styled('figcaption')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
flexDirection: 'column',
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const StyledImg = styled('img')({
|
||||||
|
maxWidth: '100%',
|
||||||
|
maxHeight: '100%',
|
||||||
|
width: 'auto',
|
||||||
|
height: 'auto',
|
||||||
|
});
|
||||||
|
|
||||||
|
interface JiraIntegrationProps {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
src: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const JiraImageContainer: FC<JiraIntegrationProps> = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
src,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<StyledFigure>
|
||||||
|
<StyledFigCaption>
|
||||||
|
<Typography variant={'h3'}>{title}</Typography>
|
||||||
|
<Typography>{description}</Typography>
|
||||||
|
</StyledFigCaption>
|
||||||
|
<StyledImg src={formatAssetPath(src)} alt={title} />
|
||||||
|
</StyledFigure>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,114 @@
|
|||||||
|
import FormTemplate from '../../common/FormTemplate/FormTemplate';
|
||||||
|
import { Divider, styled } from '@mui/material';
|
||||||
|
|
||||||
|
import { IntegrationIcon } from '../IntegrationList/IntegrationIcon/IntegrationIcon';
|
||||||
|
import cr from 'assets/img/jira/cr.png';
|
||||||
|
import connect from 'assets/img/jira/connect.png';
|
||||||
|
import manage from 'assets/img/jira/manage.png';
|
||||||
|
import React from 'react';
|
||||||
|
import { JiraImageContainer } from './JiraImageContainer';
|
||||||
|
|
||||||
|
export const StyledContainer = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const StyledGrayContainer = styled('div')(({ theme }) => ({
|
||||||
|
backgroundColor: theme.palette.grey[100],
|
||||||
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
|
padding: theme.spacing(3),
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const StyledIconLine = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
marginLeft: theme.spacing(1),
|
||||||
|
alignItems: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const StyledLink = styled('a')({
|
||||||
|
textDecoration: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const JIRA_INFO = {
|
||||||
|
name: 'jira',
|
||||||
|
displayName: 'Jira',
|
||||||
|
description:
|
||||||
|
'Create, connect, manage, and approve Unleash feature flags directly from Jira',
|
||||||
|
documentationUrl:
|
||||||
|
'https://docs.getunleash.io/reference/integrations/jira-cloud-plugin-installation',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const JiraIntegration = () => {
|
||||||
|
const { name, displayName, description, documentationUrl } = JIRA_INFO;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormTemplate
|
||||||
|
title={`${displayName}`}
|
||||||
|
description={description || ''}
|
||||||
|
documentationLink={documentationUrl}
|
||||||
|
documentationLinkLabel="Jira documentation"
|
||||||
|
>
|
||||||
|
<StyledContainer>
|
||||||
|
<StyledGrayContainer>
|
||||||
|
<StyledIconLine>
|
||||||
|
<IntegrationIcon name={name} /> How does it work?
|
||||||
|
</StyledIconLine>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Create a new feature flag directly within Jira, or
|
||||||
|
connect existing flags to any Jira issue.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Keep track of your flag status for each environment.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Activate/deactivate feature flags directly within
|
||||||
|
Jira.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Initiate change requests when guarded flags are
|
||||||
|
activated/deactivated within Jira.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</StyledGrayContainer>
|
||||||
|
<StyledGrayContainer>
|
||||||
|
<StyledLink
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href="https://marketplace.atlassian.com/apps/1231447/unleash-enterprise-for-jira"
|
||||||
|
>
|
||||||
|
View plugin on Atlassian marketplace
|
||||||
|
</StyledLink>
|
||||||
|
</StyledGrayContainer>
|
||||||
|
<Divider />
|
||||||
|
<JiraImageContainer
|
||||||
|
title={'Manage your feature flags for each environment'}
|
||||||
|
description={
|
||||||
|
'View your feature flag status for each of your environments. Quickly turn features on and off directly within Jira.'
|
||||||
|
}
|
||||||
|
src={manage}
|
||||||
|
/>
|
||||||
|
<Divider />
|
||||||
|
<JiraImageContainer
|
||||||
|
title={'Connect your feature flags to any Jira issue'}
|
||||||
|
description={
|
||||||
|
'Link as many feature flags as you want to any issue. Create new feature flags directly within Jira.'
|
||||||
|
}
|
||||||
|
src={connect}
|
||||||
|
/>
|
||||||
|
<Divider />
|
||||||
|
<JiraImageContainer
|
||||||
|
title={'Automatically initiate change requests'}
|
||||||
|
description={
|
||||||
|
'Automatically initiate change requests when you activate a guarded flag. You’ll receive a link inside Jira to review, approve, and apply the change.'
|
||||||
|
}
|
||||||
|
src={cr}
|
||||||
|
/>
|
||||||
|
</StyledContainer>
|
||||||
|
</FormTemplate>
|
||||||
|
);
|
||||||
|
};
|
@ -324,6 +324,14 @@ exports[`returns all baseRoutes 1`] = `
|
|||||||
"title": "Create",
|
"title": "Create",
|
||||||
"type": "protected",
|
"type": "protected",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"component": [Function],
|
||||||
|
"menu": {},
|
||||||
|
"parent": "/integrations",
|
||||||
|
"path": "/integrations/view/:providerId",
|
||||||
|
"title": "View",
|
||||||
|
"type": "protected",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"component": [Function],
|
"component": [Function],
|
||||||
"menu": {},
|
"menu": {},
|
||||||
|
@ -45,6 +45,7 @@ import { LoginHistory } from 'component/loginHistory/LoginHistory';
|
|||||||
import { FeatureTypesList } from 'component/featureTypes/FeatureTypesList';
|
import { FeatureTypesList } from 'component/featureTypes/FeatureTypesList';
|
||||||
import { AddonsList } from 'component/integrations/IntegrationList/AddonsList';
|
import { AddonsList } from 'component/integrations/IntegrationList/AddonsList';
|
||||||
import { TemporaryApplicationListWrapper } from 'component/application/ApplicationList/TemporaryApplicationListWrapper';
|
import { TemporaryApplicationListWrapper } from 'component/application/ApplicationList/TemporaryApplicationListWrapper';
|
||||||
|
import { JiraIntegration } from '../integrations/JiraIntegration/JiraIntegration';
|
||||||
|
|
||||||
export const routes: IRoute[] = [
|
export const routes: IRoute[] = [
|
||||||
// Splash
|
// Splash
|
||||||
@ -337,6 +338,14 @@ export const routes: IRoute[] = [
|
|||||||
type: 'protected',
|
type: 'protected',
|
||||||
menu: {},
|
menu: {},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/integrations/view/:providerId',
|
||||||
|
parent: '/integrations',
|
||||||
|
title: 'View',
|
||||||
|
component: JiraIntegration,
|
||||||
|
type: 'protected',
|
||||||
|
menu: {},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/integrations/edit/:addonId',
|
path: '/integrations/edit/:addonId',
|
||||||
parent: '/integrations',
|
parent: '/integrations',
|
||||||
|
Loading…
Reference in New Issue
Block a user