refactor: workspace integrations code (#872)

This commit is contained in:
Aaryan Khandelwal 2023-04-18 10:54:45 +05:30 committed by GitHub
parent 682a1477fb
commit 1627a587ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 115 additions and 107 deletions

View File

@ -1,51 +1,25 @@
import { FC, useRef, useState } from "react";
// hooks
import useIntegrationPopup from "hooks/use-integration-popup";
// ui
import { PrimaryButton } from "components/ui";
// types
import { IWorkspaceIntegration } from "types";
type Props = {
workspaceSlug: string | undefined;
workspaceIntegration: any;
workspaceIntegration: false | IWorkspaceIntegration | undefined;
provider: string | undefined;
};
export const GithubAuth: FC<Props> = ({ workspaceSlug, workspaceIntegration }) => {
const popup = useRef<any>();
const [authLoader, setAuthLoader] = useState(false);
const checkPopup = () => {
const check = setInterval(() => {
if (!popup || popup.current.closed || popup.current.closed === undefined) {
clearInterval(check);
setAuthLoader(false);
}
}, 2000);
};
const openPopup = () => {
const width = 600,
height = 600;
const left = window.innerWidth / 2 - width / 2;
const top = window.innerHeight / 2 - height / 2;
const url = `https://github.com/apps/${
process.env.NEXT_PUBLIC_GITHUB_APP_NAME
}/installations/new?state=${workspaceSlug as string}`;
return window.open(url, "", `width=${width}, height=${height}, top=${top}, left=${left}`);
};
const startAuth = () => {
popup.current = openPopup();
checkPopup();
setAuthLoader(true);
};
export const GithubAuth: React.FC<Props> = ({ workspaceIntegration, provider }) => {
const { startAuth, isConnecting } = useIntegrationPopup(provider);
return (
<div>
{workspaceIntegration && workspaceIntegration?.id ? (
<PrimaryButton disabled>Successfully Connected</PrimaryButton>
) : (
<PrimaryButton onClick={startAuth} loading={authLoader}>
{authLoader ? "Connecting..." : "Connect"}
<PrimaryButton onClick={startAuth} loading={isConnecting}>
{isConnecting ? "Connecting..." : "Connect"}
</PrimaryButton>
)}
</div>

View File

@ -1,30 +1,23 @@
import { FC } from "react";
import { useRouter } from "next/router";
// components
import { GithubAuth, TIntegrationSteps } from "components/integration";
// ui
import { PrimaryButton } from "components/ui";
// types
import { IAppIntegrations, IWorkspaceIntegrations } from "types";
import { IAppIntegration, IWorkspaceIntegration } from "types";
type Props = {
provider: string | undefined;
handleStepChange: (value: TIntegrationSteps) => void;
appIntegrations: IAppIntegrations[] | undefined;
workspaceIntegrations: IWorkspaceIntegrations[] | undefined;
appIntegrations: IAppIntegration[] | undefined;
workspaceIntegrations: IWorkspaceIntegration[] | undefined;
};
export const GithubImportConfigure: FC<Props> = ({
export const GithubImportConfigure: React.FC<Props> = ({
handleStepChange,
provider,
appIntegrations,
workspaceIntegrations,
}) => {
const router = useRouter();
const { workspaceSlug } = router.query;
// current integration from all the integrations available
const integration =
appIntegrations &&
@ -46,10 +39,7 @@ export const GithubImportConfigure: FC<Props> = ({
<div className="text-sm text-gray-600">Set up your GitHub import.</div>
</div>
<div className="flex-shrink-0">
<GithubAuth
workspaceSlug={workspaceSlug as string}
workspaceIntegration={workspaceIntegration}
/>
<GithubAuth workspaceIntegration={workspaceIntegration} provider={provider} />
</div>
</div>

View File

@ -11,11 +11,11 @@ import { CustomSearchSelect, PrimaryButton, SecondaryButton } from "components/u
// helpers
import { truncateText } from "helpers/string.helper";
// types
import { IWorkspaceIntegrations } from "types";
import { IWorkspaceIntegration } from "types";
type Props = {
handleStepChange: (value: TIntegrationSteps) => void;
integration: IWorkspaceIntegrations | false | undefined;
integration: IWorkspaceIntegration | false | undefined;
control: Control<TFormValues, any>;
watch: UseFormWatch<TFormValues>;
};

View File

@ -11,10 +11,10 @@ import { CustomSearchSelect } from "components/ui";
// helpers
import { truncateText } from "helpers/string.helper";
// types
import { IWorkspaceIntegrations } from "types";
import { IWorkspaceIntegration } from "types";
type Props = {
integration: IWorkspaceIntegrations;
integration: IWorkspaceIntegration;
value: any;
label: string;
onChange: (repo: any) => void;

View File

@ -2,9 +2,9 @@
export * from "./delete-import-modal";
export * from "./guide";
export * from "./single-import";
export * from "./single-integration-card";
// github
export * from "./github";
// jira
export * from "./jira";

View File

@ -1,4 +1,4 @@
import React, { useRef, useState } from "react";
import { useState } from "react";
import Image from "next/image";
import { useRouter } from "next/router";
@ -9,49 +9,44 @@ import useSWR, { mutate } from "swr";
import IntegrationService from "services/integration";
// hooks
import useToast from "hooks/use-toast";
import useIntegrationPopup from "hooks/use-integration-popup";
// ui
import { DangerButton, Loader, SecondaryButton } from "components/ui";
// icons
import GithubLogo from "public/logos/github-square.png";
import GithubLogo from "public/services/github.png";
import SlackLogo from "public/services/slack.png";
// types
import { IWorkspaceIntegrations } from "types";
import { IAppIntegration, IWorkspaceIntegration } from "types";
// fetch-keys
import { WORKSPACE_INTEGRATIONS } from "constants/fetch-keys";
const OAuthPopUp = ({ integration }: any) => {
const [deletingIntegration, setDeletingIntegration] = useState(false);
type Props = {
integration: IAppIntegration;
};
const popup = useRef<any>();
const integrationDetails: { [key: string]: any } = {
github: {
logo: GithubLogo,
installed:
"Activate GitHub integrations on individual projects to sync with specific repositories.",
notInstalled: "Connect with GitHub with your Plane workspace to sync project issues.",
},
slack: {
logo: SlackLogo,
installed: "Activate Slack integrations on individual projects to sync with specific cahnnels.",
notInstalled: "Connect with Slack with your Plane workspace to sync project issues.",
},
};
export const SingleIntegrationCard: React.FC<Props> = ({ integration }) => {
const [deletingIntegration, setDeletingIntegration] = useState(false);
const router = useRouter();
const { workspaceSlug } = router.query;
const { setToastAlert } = useToast();
const checkPopup = () => {
const check = setInterval(() => {
if (!popup || popup.current.closed || popup.current.closed === undefined) {
clearInterval(check);
}
}, 1000);
};
const openPopup = () => {
const width = 600,
height = 600;
const left = window.innerWidth / 2 - width / 2;
const top = window.innerHeight / 2 - height / 2;
const url = `https://github.com/apps/${
process.env.NEXT_PUBLIC_GITHUB_APP_NAME
}/installations/new?state=${workspaceSlug as string}`;
return window.open(url, "", `width=${width}, height=${height}, top=${top}, left=${left}`);
};
const startAuth = () => {
popup.current = openPopup();
checkPopup();
};
const { startAuth, isConnecting: isInstalling } = useIntegrationPopup(integration.provider);
const { data: workspaceIntegrations } = useSWR(
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null,
@ -75,7 +70,7 @@ const OAuthPopUp = ({ integration }: any) => {
workspaceIntegrationId ?? ""
)
.then(() => {
mutate<IWorkspaceIntegrations[]>(
mutate<IWorkspaceIntegration[]>(
WORKSPACE_INTEGRATIONS(workspaceSlug as string),
(prevData) => prevData?.filter((i) => i.id !== workspaceIntegrationId),
false
@ -107,7 +102,10 @@ const OAuthPopUp = ({ integration }: any) => {
<div className="flex items-center justify-between gap-2 rounded-[10px] border bg-white p-5">
<div className="flex items-start gap-4">
<div className="h-12 w-12 flex-shrink-0">
<Image src={GithubLogo} alt="GithubLogo" />
<Image
src={integrationDetails[integration.provider].logo}
alt={`${integration.title} Logo`}
/>
</div>
<div>
<h3 className="flex items-center gap-4 text-xl font-semibold">
@ -128,8 +126,8 @@ const OAuthPopUp = ({ integration }: any) => {
<p className="text-sm text-gray-400">
{workspaceIntegrations
? isInstalled
? "Activate GitHub integrations on individual projects to sync with specific repositories."
: "Connect with GitHub with your Plane workspace to sync project issues."
? integrationDetails[integration.provider].installed
: integrationDetails[integration.provider].notInstalled
: "Loading..."}
</p>
</div>
@ -141,8 +139,8 @@ const OAuthPopUp = ({ integration }: any) => {
{deletingIntegration ? "Removing..." : "Remove installation"}
</DangerButton>
) : (
<SecondaryButton onClick={startAuth} outline>
Add installation
<SecondaryButton onClick={startAuth} loading={isInstalling}>
{isInstalling ? "Installing..." : "Add installation"}
</SecondaryButton>
)
) : (
@ -153,5 +151,3 @@ const OAuthPopUp = ({ integration }: any) => {
</div>
);
};
export default OAuthPopUp;

View File

@ -14,12 +14,12 @@ import { SelectRepository } from "components/integration";
// icons
import GithubLogo from "public/logos/github-square.png";
// types
import { IWorkspaceIntegrations } from "types";
import { IWorkspaceIntegration } from "types";
// fetch-keys
import { PROJECT_GITHUB_REPOSITORY } from "constants/fetch-keys";
type Props = {
integration: IWorkspaceIntegrations;
integration: IWorkspaceIntegration;
};
export const SingleIntegration: React.FC<Props> = ({ integration }) => {

View File

@ -0,0 +1,52 @@
import { useRef, useState } from "react";
import { useRouter } from "next/router";
const useIntegrationPopup = (provider: string | undefined) => {
const [authLoader, setAuthLoader] = useState(false);
const router = useRouter();
const { workspaceSlug } = router.query;
const providerUrls: { [key: string]: string } = {
github: `https://github.com/apps/${
process.env.NEXT_PUBLIC_GITHUB_APP_NAME
}/installations/new?state=${workspaceSlug as string}`,
slack: "",
};
const popup = useRef<any>();
const checkPopup = () => {
const check = setInterval(() => {
if (!popup || popup.current.closed || popup.current.closed === undefined) {
clearInterval(check);
setAuthLoader(false);
}
}, 1000);
};
const openPopup = () => {
if (!provider) return;
const width = 600,
height = 600;
const left = window.innerWidth / 2 - width / 2;
const top = window.innerHeight / 2 - height / 2;
const url = providerUrls[provider];
return window.open(url, "", `width=${width}, height=${height}, top=${top}, left=${left}`);
};
const startAuth = () => {
popup.current = openPopup();
checkPopup();
setAuthLoader(true);
};
return {
startAuth,
isConnecting: authLoader,
};
};
export default useIntegrationPopup;

View File

@ -10,7 +10,7 @@ import IntegrationService from "services/integration";
// layouts
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
// components
import OAuthPopUp from "components/popup";
import { SingleIntegrationCard } from "components/integration";
// ui
import { Loader } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
@ -49,11 +49,7 @@ const WorkspaceIntegrations: NextPage = () => {
<div className="space-y-5">
{appIntegrations ? (
appIntegrations.map((integration) => (
<OAuthPopUp
key={integration.id}
workspaceSlug={workspaceSlug}
integration={integration}
/>
<SingleIntegrationCard key={integration.id} integration={integration} />
))
) : (
<Loader className="space-y-5">

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,6 +1,6 @@
import APIService from "services/api.service";
// types
import { IAppIntegrations, IImporterService, IWorkspaceIntegrations } from "types";
import { IAppIntegration, IImporterService, IWorkspaceIntegration } from "types";
const { NEXT_PUBLIC_API_BASE_URL } = process.env;
@ -9,7 +9,7 @@ class IntegrationService extends APIService {
super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000");
}
async getAppIntegrationsList(): Promise<IAppIntegrations[]> {
async getAppIntegrationsList(): Promise<IAppIntegration[]> {
return this.get(`/api/integrations/`)
.then((response) => response?.data)
.catch((error) => {
@ -17,7 +17,7 @@ class IntegrationService extends APIService {
});
}
async getWorkspaceIntegrationsList(workspaceSlug: string): Promise<IWorkspaceIntegrations[]> {
async getWorkspaceIntegrationsList(workspaceSlug: string): Promise<IWorkspaceIntegration[]> {
return this.get(`/api/workspaces/${workspaceSlug}/workspace-integrations/`)
.then((response) => response?.data)
.catch((error) => {

View File

@ -1,5 +1,5 @@
// All the app integrations that are available
export interface IAppIntegrations {
export interface IAppIntegration {
author: string;
author: "";
avatar_url: string | null;
@ -19,7 +19,7 @@ export interface IAppIntegrations {
webhook_url: string;
}
export interface IWorkspaceIntegrations {
export interface IWorkspaceIntegration {
actor: string;
api_token: string;
config: any;