diff --git a/apps/app/components/integration/github/auth.tsx b/apps/app/components/integration/github/auth.tsx index 6055cd903..b530b4a24 100644 --- a/apps/app/components/integration/github/auth.tsx +++ b/apps/app/components/integration/github/auth.tsx @@ -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 = ({ workspaceSlug, workspaceIntegration }) => { - const popup = useRef(); - 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 = ({ workspaceIntegration, provider }) => { + const { startAuth, isConnecting } = useIntegrationPopup(provider); return (
{workspaceIntegration && workspaceIntegration?.id ? ( Successfully Connected ) : ( - - {authLoader ? "Connecting..." : "Connect"} + + {isConnecting ? "Connecting..." : "Connect"} )}
diff --git a/apps/app/components/integration/github/import-configure.tsx b/apps/app/components/integration/github/import-configure.tsx index ecae21eae..55abe058c 100644 --- a/apps/app/components/integration/github/import-configure.tsx +++ b/apps/app/components/integration/github/import-configure.tsx @@ -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 = ({ +export const GithubImportConfigure: React.FC = ({ 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 = ({
Set up your GitHub import.
- +
diff --git a/apps/app/components/integration/github/import-data.tsx b/apps/app/components/integration/github/import-data.tsx index c3fa871e3..2b93017c1 100644 --- a/apps/app/components/integration/github/import-data.tsx +++ b/apps/app/components/integration/github/import-data.tsx @@ -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; watch: UseFormWatch; }; diff --git a/apps/app/components/integration/github/select-repository.tsx b/apps/app/components/integration/github/select-repository.tsx index a306ff0fc..306b1e90e 100644 --- a/apps/app/components/integration/github/select-repository.tsx +++ b/apps/app/components/integration/github/select-repository.tsx @@ -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; diff --git a/apps/app/components/integration/index.ts b/apps/app/components/integration/index.ts index e970bbd84..39ae51e33 100644 --- a/apps/app/components/integration/index.ts +++ b/apps/app/components/integration/index.ts @@ -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"; diff --git a/apps/app/components/popup/index.tsx b/apps/app/components/integration/single-integration-card.tsx similarity index 71% rename from apps/app/components/popup/index.tsx rename to apps/app/components/integration/single-integration-card.tsx index 5e3971a39..824ea0b9b 100644 --- a/apps/app/components/popup/index.tsx +++ b/apps/app/components/integration/single-integration-card.tsx @@ -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(); +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 = ({ 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( + mutate( WORKSPACE_INTEGRATIONS(workspaceSlug as string), (prevData) => prevData?.filter((i) => i.id !== workspaceIntegrationId), false @@ -107,7 +102,10 @@ const OAuthPopUp = ({ integration }: any) => {
- GithubLogo + {`${integration.title}

@@ -128,8 +126,8 @@ const OAuthPopUp = ({ integration }: any) => {

{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..."}

@@ -141,8 +139,8 @@ const OAuthPopUp = ({ integration }: any) => { {deletingIntegration ? "Removing..." : "Remove installation"} ) : ( - - Add installation + + {isInstalling ? "Installing..." : "Add installation"} ) ) : ( @@ -153,5 +151,3 @@ const OAuthPopUp = ({ integration }: any) => {
); }; - -export default OAuthPopUp; diff --git a/apps/app/components/project/single-integration-card.tsx b/apps/app/components/project/single-integration-card.tsx index c8de0fe3e..bbf21f438 100644 --- a/apps/app/components/project/single-integration-card.tsx +++ b/apps/app/components/project/single-integration-card.tsx @@ -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 = ({ integration }) => { diff --git a/apps/app/hooks/use-integration-popup.tsx b/apps/app/hooks/use-integration-popup.tsx new file mode 100644 index 000000000..03137a195 --- /dev/null +++ b/apps/app/hooks/use-integration-popup.tsx @@ -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(); + + 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; diff --git a/apps/app/pages/[workspaceSlug]/settings/integrations.tsx b/apps/app/pages/[workspaceSlug]/settings/integrations.tsx index 71908bc5d..44717f67d 100644 --- a/apps/app/pages/[workspaceSlug]/settings/integrations.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/integrations.tsx @@ -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 = () => {
{appIntegrations ? ( appIntegrations.map((integration) => ( - + )) ) : ( diff --git a/apps/app/public/services/slack.png b/apps/app/public/services/slack.png new file mode 100644 index 000000000..fc918001f Binary files /dev/null and b/apps/app/public/services/slack.png differ diff --git a/apps/app/services/integration/index.ts b/apps/app/services/integration/index.ts index 45ee0adb8..425087085 100644 --- a/apps/app/services/integration/index.ts +++ b/apps/app/services/integration/index.ts @@ -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 { + async getAppIntegrationsList(): Promise { return this.get(`/api/integrations/`) .then((response) => response?.data) .catch((error) => { @@ -17,7 +17,7 @@ class IntegrationService extends APIService { }); } - async getWorkspaceIntegrationsList(workspaceSlug: string): Promise { + async getWorkspaceIntegrationsList(workspaceSlug: string): Promise { return this.get(`/api/workspaces/${workspaceSlug}/workspace-integrations/`) .then((response) => response?.data) .catch((error) => { diff --git a/apps/app/types/integration.d.ts b/apps/app/types/integration.d.ts index 77ea9d35d..b499d743f 100644 --- a/apps/app/types/integration.d.ts +++ b/apps/app/types/integration.d.ts @@ -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;