feat(integration-page): implemented new optimized integrations page

This commit is contained in:
akhilmhdh
2023-06-24 23:31:37 +05:30
parent c04ea7e731
commit 0ef4ac1cdc
10 changed files with 695 additions and 444 deletions
@@ -0,0 +1,223 @@
import { useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useRouter } from 'next/router';
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
import NavHeader from '@app/components/navigation/NavHeader';
import { Button,Modal, ModalContent } from '@app/components/v2';
import { useWorkspace } from '@app/context';
import { usePopUp } from '@app/hooks';
import {
useDeleteIntegration,
useDeleteIntegrationAuth,
useGetCloudIntegrations,
useGetUserWsKey,
useGetWorkspaceAuthorizations,
useGetWorkspaceBot,
useGetWorkspaceIntegrations,
useUpdateBotActiveStatus} from '@app/hooks/api';
import { IntegrationAuth } from '@app/hooks/api/types';
import { CloudIntegrationSection } from './components/CloudIntegrationSection';
import { FrameworkIntegrationSection } from './components/FrameworkIntegrationSection';
import { IntegrationsSection } from './components/IntegrationsSection';
import {
generateBotKey,
redirectForProviderAuth,
redirectToIntegrationAppConfigScreen
} from './IntegrationPage.utils';
type Props = {
frameworkIntegrations: Array<{ name: string; slug: string; image: string; docsLink: string }>;
};
export const IntegrationsPage = ({ frameworkIntegrations }: Props) => {
const { t } = useTranslation();
const { createNotification } = useNotificationContext();
const router = useRouter();
const { currentWorkspace } = useWorkspace();
const workspaceId = currentWorkspace?._id || '';
const environments = currentWorkspace?.environments || [];
const { data: latestWsKey } = useGetUserWsKey(workspaceId);
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
'activeBot',
'revokeProviderPermissionConf',
'removeIntegrationConf'
] as const);
const { data: cloudIntegrations, isLoading: isCloudIntegrationsLoading } =
useGetCloudIntegrations();
const { data: integrationAuths, isLoading: isIntegrationAuthLoading } =
useGetWorkspaceAuthorizations(
workspaceId,
useCallback((data: IntegrationAuth[]) => {
const groupBy: Record<string, IntegrationAuth> = {};
data.forEach((el) => {
groupBy[el.integration] = el;
});
return groupBy;
}, [])
);
// mutation
const {
data: integrations,
isLoading: isIntegrationLoading,
isFetching: isIntegrationFetching
} = useGetWorkspaceIntegrations(workspaceId);
const { data: bot } = useGetWorkspaceBot(workspaceId);
// mutation
const { mutateAsync: updateBotActiveStatus, mutate: updateBotActiveStatusSync } =
useUpdateBotActiveStatus();
const { mutateAsync: deleteIntegration } = useDeleteIntegration();
const {
mutateAsync: deleteIntegrationAuth,
isLoading: isDeleteIntegrationAuthSuccess,
reset: resetDeleteIntegrationAuth
} = useDeleteIntegrationAuth();
// summary: this use effect is trigger when all integration auths are removed thus deactivate bot
// details: so onsuccessfully deleting an integration auth, immediately integration list is refeteched
// After the refetch is completed check if its empty. Then set bot active and reset the submit hook
useEffect(() => {
if (isDeleteIntegrationAuthSuccess && !isIntegrationFetching && !integrations?.length) {
if (bot?._id)
updateBotActiveStatusSync({
isActive: false,
botId: bot._id,
workspaceId
});
resetDeleteIntegrationAuth();
}
}, [isIntegrationFetching, isDeleteIntegrationAuthSuccess, integrations?.length]);
const handleProviderIntegration = async (provider: string) => {
const selectedCloudIntegration = cloudIntegrations?.find(({ slug }) => provider === slug);
if (!selectedCloudIntegration) return;
try {
if (bot && !bot.isActive) {
const botKey = generateBotKey(bot.publicKey, latestWsKey!);
await updateBotActiveStatus({
workspaceId,
botKey,
isActive: true,
botId: bot._id
});
}
const integrationAuthForProvider = integrationAuths?.[provider];
if (!integrationAuthForProvider) {
redirectForProviderAuth(selectedCloudIntegration);
return;
}
const url = redirectToIntegrationAppConfigScreen(provider, integrationAuthForProvider._id);
router.push(url);
} catch (error) {
console.error(error);
}
};
// function to strat integration for a provider
// confirmation to user passing the bot key for provider to get secret access
const handleProviderIntegrationStart = (provider: string) => {
if (!bot?.isActive) {
handlePopUpOpen('activeBot', { provider });
return;
}
handleProviderIntegration(provider);
};
const handleUserAcceptBotCondition = () => {
const { provider } = popUp.activeBot?.data as { provider: string };
handleProviderIntegration(provider);
handlePopUpClose('activeBot');
};
const handleIntegrationDelete = async (integrationId: string, cb: () => void) => {
try {
await deleteIntegration({ id: integrationId, workspaceId });
if (cb) cb();
createNotification({
type: 'success',
text: 'Deleted integration'
});
} catch (err) {
console.log(err);
createNotification({
type: 'error',
text: 'Failed to delete integration'
});
}
};
const handleIntegrationAuthRevoke = async (provider: string, cb?: () => void) => {
const integrationAuthForProvider = integrationAuths?.[provider];
if (!integrationAuthForProvider) return;
try {
await deleteIntegrationAuth({
id: integrationAuthForProvider._id,
workspaceId
});
if (cb) cb();
createNotification({
type: 'success',
text: 'Revoked provider authentication'
});
} catch (err) {
console.error(err);
createNotification({
type: 'error',
text: 'Failed to revoke provider authentication'
});
}
};
return (
<div className="container mx-auto px-8 pb-12 text-white">
<NavHeader pageName={t('integrations.title')} isProjectRelated />
<IntegrationsSection
isLoading={isIntegrationLoading}
integrations={integrations}
environments={environments}
onIntegrationDelete={({ _id: id }, cb) => handleIntegrationDelete(id, cb)}
/>
<CloudIntegrationSection
isLoading={isCloudIntegrationsLoading || isIntegrationAuthLoading}
cloudIntegrations={cloudIntegrations}
integrationAuths={integrationAuths}
onIntegrationStart={handleProviderIntegrationStart}
onIntegrationRevoke={handleIntegrationAuthRevoke}
/>
<Modal
isOpen={popUp.activeBot?.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle('activeBot', isOpen)}
>
<ModalContent
title={t('integrations.grant-access-to-secrets') as string}
footerContent={
<div className="flex items-center space-x-2">
<Button onClick={() => handleUserAcceptBotCondition()}>
{t('integrations.grant-access-button') as string}
</Button>
<Button
onClick={() => handlePopUpClose('activeBot')}
variant="outline_bg"
colorSchema="secondary"
>
Cancel
</Button>
</div>
}
>
{t('integrations.why-infisical-needs-access')}
</ModalContent>
</Modal>
<FrameworkIntegrationSection frameworks={frameworkIntegrations} />
</div>
);
};