diff --git a/frontend/src/assets/icons/jira-comment.svg b/frontend/src/assets/icons/jira-comment.svg
new file mode 100644
index 0000000000..4ace5cc84a
--- /dev/null
+++ b/frontend/src/assets/icons/jira-comment.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/src/assets/icons/jira.svg b/frontend/src/assets/icons/jira.svg
index 4ace5cc84a..b29b8db9af 100644
--- a/frontend/src/assets/icons/jira.svg
+++ b/frontend/src/assets/icons/jira.svg
@@ -1 +1,15 @@
-
\ No newline at end of file
+
diff --git a/frontend/src/assets/img/jira/connect.png b/frontend/src/assets/img/jira/connect.png
new file mode 100644
index 0000000000..894b332fc2
Binary files /dev/null and b/frontend/src/assets/img/jira/connect.png differ
diff --git a/frontend/src/assets/img/jira/cr.png b/frontend/src/assets/img/jira/cr.png
new file mode 100644
index 0000000000..894b332fc2
Binary files /dev/null and b/frontend/src/assets/img/jira/cr.png differ
diff --git a/frontend/src/assets/img/jira/manage.png b/frontend/src/assets/img/jira/manage.png
new file mode 100644
index 0000000000..894b332fc2
Binary files /dev/null and b/frontend/src/assets/img/jira/manage.png differ
diff --git a/frontend/src/component/common/FormTemplate/FormTemplate.tsx b/frontend/src/component/common/FormTemplate/FormTemplate.tsx
index 4f97820f95..fa7a84b130 100644
--- a/frontend/src/component/common/FormTemplate/FormTemplate.tsx
+++ b/frontend/src/component/common/FormTemplate/FormTemplate.tsx
@@ -26,7 +26,7 @@ interface ICreateProps {
loading?: boolean;
modal?: boolean;
disablePadding?: boolean;
- formatApiCode: () => string;
+ formatApiCode?: () => string;
}
const StyledContainer = styled('section', {
@@ -165,22 +165,43 @@ const FormTemplate: React.FC = ({
const { setToastData } = useToast();
const smallScreen = useMediaQuery(`(max-width:${1099}px)`);
const copyCommand = () => {
- if (copy(formatApiCode())) {
- setToastData({
- title: 'Successfully copied the command',
- text: 'The command should now be automatically copied to your clipboard',
- autoHideDuration: 6000,
- type: 'success',
- show: true,
- });
- } else {
- setToastData({
- title: 'Could not copy the command',
- text: 'Sorry, but we could not copy the command.',
- autoHideDuration: 6000,
- type: 'error',
- show: true,
- });
+ if (formatApiCode !== undefined) {
+ if (copy(formatApiCode())) {
+ setToastData({
+ title: 'Successfully copied the command',
+ text: 'The command should now be automatically copied to your clipboard',
+ autoHideDuration: 6000,
+ type: 'success',
+ show: true,
+ });
+ } else {
+ setToastData({
+ title: 'Could not copy the command',
+ text: 'Sorry, but we could not copy the command.',
+ autoHideDuration: 6000,
+ type: 'error',
+ show: true,
+ });
+ }
+ }
+ };
+
+ const renderApiInfo = (apiDisabled: boolean) => {
+ if (!apiDisabled) {
+ return (
+ <>
+
+
+ API Command{' '}
+
+
+
+
+
+
+ {' '}
+ >
+ );
}
};
@@ -221,16 +242,7 @@ const FormTemplate: React.FC = ({
documentationLink={documentationLink}
documentationLinkLabel={documentationLinkLabel}
>
-
-
- API Command{' '}
-
-
-
-
-
-
-
+ {renderApiInfo(formatApiCode === undefined)}
}
/>
diff --git a/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx
index fa75d7f0f0..2343d020b6 100644
--- a/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx
+++ b/frontend/src/component/integrations/IntegrationList/AvailableIntegrations/AvailableIntegrations.tsx
@@ -5,15 +5,19 @@ import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { IntegrationCard } from '../IntegrationCard/IntegrationCard';
import { StyledCardsGrid } from '../IntegrationList.styles';
+import { JIRA_INFO } from '../../JiraIntegration/JiraIntegration';
interface IAvailableIntegrationsProps {
providers: AddonTypeSchema[];
loading?: boolean;
}
+
export const AvailableIntegrations: VFC = ({
providers,
loading,
}) => {
+ const customProviders = [JIRA_INFO];
+
const ref = useLoading(loading || false);
return (
= ({
link={`/integrations/create/${name}`}
/>
))}
+ {customProviders?.map(({ name, displayName, description }) => (
+
+ ))}
);
diff --git a/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx b/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx
index ee2e6f1e2c..b7c7fd822a 100644
--- a/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx
+++ b/frontend/src/component/integrations/IntegrationList/IntegrationIcon/IntegrationIcon.tsx
@@ -3,6 +3,7 @@ import { DeviceHub } from '@mui/icons-material';
import { formatAssetPath } from 'utils/formatPath';
import slackIcon from 'assets/icons/slack.svg';
+import jiraCommentIcon from 'assets/icons/jira-comment.svg';
import jiraIcon from 'assets/icons/jira.svg';
import webhooksIcon from 'assets/icons/webhooks.svg';
import teamsIcon from 'assets/icons/teams.svg';
@@ -34,7 +35,7 @@ export const IntegrationIcon = ({ name }: IIntegrationIconProps) => {
);
case 'webhook':
@@ -61,6 +62,14 @@ export const IntegrationIcon = ({ name }: IIntegrationIconProps) => {
src={formatAssetPath(dataDogIcon)}
/>
);
+ case 'jira':
+ return (
+
+ );
default:
return (
diff --git a/frontend/src/component/integrations/JiraIntegration/JiraImageContainer.tsx b/frontend/src/component/integrations/JiraIntegration/JiraImageContainer.tsx
new file mode 100644
index 0000000000..cba28045c2
--- /dev/null
+++ b/frontend/src/component/integrations/JiraIntegration/JiraImageContainer.tsx
@@ -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 = ({
+ title,
+ description,
+ src,
+}) => {
+ return (
+
+
+ {title}
+ {description}
+
+
+
+ );
+};
diff --git a/frontend/src/component/integrations/JiraIntegration/JiraIntegration.tsx b/frontend/src/component/integrations/JiraIntegration/JiraIntegration.tsx
new file mode 100644
index 0000000000..84c9c5d7d3
--- /dev/null
+++ b/frontend/src/component/integrations/JiraIntegration/JiraIntegration.tsx
@@ -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 (
+
+
+
+
+ How does it work?
+
+
+ -
+ Create a new feature flag directly within Jira, or
+ connect existing flags to any Jira issue.
+
+ -
+ Keep track of your flag status for each environment.
+
+ -
+ Activate/deactivate feature flags directly within
+ Jira.
+
+ -
+ Initiate change requests when guarded flags are
+ activated/deactivated within Jira.
+
+
+
+
+
+ View plugin on Atlassian marketplace
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
index bfe0a24394..2c96ef3a01 100644
--- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
+++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap
@@ -324,6 +324,14 @@ exports[`returns all baseRoutes 1`] = `
"title": "Create",
"type": "protected",
},
+ {
+ "component": [Function],
+ "menu": {},
+ "parent": "/integrations",
+ "path": "/integrations/view/:providerId",
+ "title": "View",
+ "type": "protected",
+ },
{
"component": [Function],
"menu": {},
diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts
index 6ee0a2daca..385b7180a4 100644
--- a/frontend/src/component/menu/routes.ts
+++ b/frontend/src/component/menu/routes.ts
@@ -45,6 +45,7 @@ import { LoginHistory } from 'component/loginHistory/LoginHistory';
import { FeatureTypesList } from 'component/featureTypes/FeatureTypesList';
import { AddonsList } from 'component/integrations/IntegrationList/AddonsList';
import { TemporaryApplicationListWrapper } from 'component/application/ApplicationList/TemporaryApplicationListWrapper';
+import { JiraIntegration } from '../integrations/JiraIntegration/JiraIntegration';
export const routes: IRoute[] = [
// Splash
@@ -337,6 +338,14 @@ export const routes: IRoute[] = [
type: 'protected',
menu: {},
},
+ {
+ path: '/integrations/view/:providerId',
+ parent: '/integrations',
+ title: 'View',
+ component: JiraIntegration,
+ type: 'protected',
+ menu: {},
+ },
{
path: '/integrations/edit/:addonId',
parent: '/integrations',