Merge pull request #1014 from G3root/e2e-warning

feat: display warning message in integrations page when e2e is enabled
This commit is contained in:
Maidul Islam
2023-10-22 14:42:15 +01:00
committed by GitHub
8 changed files with 202 additions and 20 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ module.exports = {
env: { env: {
browser: true, browser: true,
es2021: true, es2021: true,
"es6": true es6: true
}, },
extends: [ extends: [
"airbnb", "airbnb",
@@ -0,0 +1,61 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import type { Meta, StoryObj } from "@storybook/react";
import { Alert, AlertDescription } from "./Alert";
const meta: Meta<typeof Alert> = {
title: "Components/Alert",
component: Alert,
tags: ["v2"]
};
export default meta;
type Story = StoryObj<typeof Alert>;
const ExampleComponent = () => <AlertDescription>this is a description</AlertDescription>;
export const Default: Story = {
args: {
children: <ExampleComponent />
}
};
export const Warning: Story = {
args: {
children: <ExampleComponent />,
variant: "warning"
}
};
export const Danger: Story = {
args: {
children: <ExampleComponent />,
variant: "danger"
}
};
export const WithCustomIcon: Story = {
args: {
children: <ExampleComponent />,
variant: "warning",
icon: <FontAwesomeIcon icon={faPlus} />
}
};
export const WithOutIcon: Story = {
args: {
children: <ExampleComponent />,
variant: "warning",
icon: null
}
};
export const WithOutTitle: Story = {
args: {
children: <ExampleComponent />,
variant: "warning",
hideTitle: true
}
};
@@ -0,0 +1,85 @@
import { forwardRef } from "react";
import {
faExclamationCircle,
faExclamationTriangle,
faInfoCircle
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { type VariantProps, cva } from "cva";
import { twMerge } from "tailwind-merge";
const alertVariants = cva(
"w-full bg-mineshaft-800 rounded-lg border px-4 py-3 text-sm flex items-center gap-x-4",
{
variants: {
variant: {
default: "",
danger: "text-red border-red",
warning: "text-yellow border-yellow"
}
},
defaultVariants: {
variant: "default"
}
}
);
type AlertProps = {
title?: string;
hideTitle?: boolean;
icon?: React.ReactNode;
};
const variantTitleMap = {
default: "Info",
danger: "Danger",
warning: "Warning"
};
const variantIconMap = {
default: faInfoCircle,
danger: faExclamationCircle,
warning: faExclamationTriangle
};
const Alert = forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants> & AlertProps
>(({ className, variant, title, icon, hideTitle = false, children, ...props }, ref) => {
const defaultTitle = title ?? variantTitleMap[variant ?? "default"];
return (
<div
ref={ref}
role="alert"
className={twMerge(alertVariants({ variant }), className)}
{...props}
>
<div>
{typeof icon !== "undefined" ? (
<>{icon} </>
) : (
<FontAwesomeIcon className="text-lg" icon={variantIconMap[variant ?? "default"]} />
)}
</div>
<div className="flex flex-col gap-y-1">
{hideTitle ? null : (
<h5 className="font-medium leading-none tracking-tight" {...props}>
{defaultTitle}
</h5>
)}
{children}
</div>
</div>
);
});
Alert.displayName = "Alert";
const AlertDescription = forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={twMerge("text-sm [&_p]:leading-relaxed", className)} {...props} />
));
AlertDescription.displayName = "AlertDescription";
export { Alert, AlertDescription };
@@ -0,0 +1 @@
export { Alert, AlertDescription } from "./Alert";
+1
View File
@@ -1,4 +1,5 @@
export * from "./Accordion"; export * from "./Accordion";
export * from "./Alert";
export * from "./Button"; export * from "./Button";
export * from "./Card"; export * from "./Card";
export * from "./Checkbox"; export * from "./Checkbox";
@@ -202,6 +202,8 @@ export const IntegrationsPage = withProjectPermission(
integrations={integrations} integrations={integrations}
environments={environments} environments={environments}
onIntegrationDelete={({ _id: id }, cb) => handleIntegrationDelete(id, cb)} onIntegrationDelete={({ _id: id }, cb) => handleIntegrationDelete(id, cb)}
isBotActive={bot?.isActive}
workspaceId={workspaceId}
/> />
<CloudIntegrationSection <CloudIntegrationSection
isLoading={isCloudIntegrationsLoading || isIntegrationAuthLoading} isLoading={isCloudIntegrationsLoading || isIntegrationAuthLoading}
@@ -1,9 +1,12 @@
import Link from "next/link";
import { faArrowRight, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faArrowRight, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { integrationSlugNameMapping } from "public/data/frequentConstants"; import { integrationSlugNameMapping } from "public/data/frequentConstants";
import { ProjectPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
Alert,
AlertDescription,
DeleteActionModal, DeleteActionModal,
EmptyState, EmptyState,
FormControl, FormControl,
@@ -23,17 +26,22 @@ type Props = {
integrations?: TIntegration[]; integrations?: TIntegration[];
isLoading?: boolean; isLoading?: boolean;
onIntegrationDelete: (integration: TIntegration, cb: () => void) => void; onIntegrationDelete: (integration: TIntegration, cb: () => void) => void;
isBotActive: boolean | undefined;
workspaceId: string;
}; };
export const IntegrationsSection = ({ export const IntegrationsSection = ({
integrations = [], integrations = [],
environments = [], environments = [],
isLoading, isLoading,
onIntegrationDelete onIntegrationDelete,
isBotActive,
workspaceId
}: Props) => { }: Props) => {
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"deleteConfirmation" "deleteConfirmation"
] as const); ] as const);
return ( return (
<div className="mb-8"> <div className="mb-8">
<div className="mx-4 mb-4 mt-6 flex flex-col items-start justify-between px-2 text-xl"> <div className="mx-4 mb-4 mt-6 flex flex-col items-start justify-between px-2 text-xl">
@@ -45,7 +53,22 @@ export const IntegrationsSection = ({
<Skeleton className="h-28" /> <Skeleton className="h-28" />
</div> </div>
)} )}
{!isLoading && !integrations.length && (
{!isBotActive && (
<div className="px-6 py-4">
<Alert hideTitle variant="warning">
<AlertDescription>
All the active integrations will be disabled. Disable End-to-End Encryption in{" "}
<Link href={`/project/${workspaceId}/settings`} passHref>
<a className="underline underline-offset-2">project settings </a>
</Link>
to re-enable it .
</AlertDescription>
</Alert>
</div>
)}
{!isLoading && !integrations.length && isBotActive && (
<div className="mx-6"> <div className="mx-6">
<EmptyState <EmptyState
className="rounded-md border border-mineshaft-700 pt-8 pb-4" className="rounded-md border border-mineshaft-700 pt-8 pb-4"
@@ -53,7 +76,7 @@ export const IntegrationsSection = ({
/> />
</div> </div>
)} )}
{!isLoading && ( {!isLoading && isBotActive && (
<div className="flex flex-col space-y-4 p-6 pt-0"> <div className="flex flex-col space-y-4 p-6 pt-0">
{integrations?.map((integration) => ( {integrations?.map((integration) => (
<div <div
@@ -3,7 +3,7 @@ import {
decryptAssymmetric, decryptAssymmetric,
encryptAssymmetric encryptAssymmetric
} from "@app/components/utilities/cryptography/crypto"; } from "@app/components/utilities/cryptography/crypto";
import { Checkbox } from "@app/components/v2"; import { Alert, AlertDescription, Checkbox } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { useGetUserWsKey, useGetWorkspaceBot, useUpdateBotActiveStatus } from "@app/hooks/api"; import { useGetUserWsKey, useGetWorkspaceBot, useUpdateBotActiveStatus } from "@app/hooks/api";
@@ -76,30 +76,39 @@ export const E2EESection = () => {
}; };
return bot ? ( return bot ? (
<div className="mb-6 p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<p className="mb-3 text-xl font-semibold">End-to-End Encryption</p> <p className="mb-3 text-xl font-semibold">End-to-End Encryption</p>
<p className="text-gray-400 mb-8"> <p className="mb-8 text-gray-400">
Disabling, end-to-end encryption (E2EE) unlocks capabilities like native integrations to Disabling, end-to-end encryption (E2EE) unlocks capabilities like native integrations to
cloud providers as well as HTTP calls to get secrets back raw but enables the server to cloud providers as well as HTTP calls to get secrets back raw but enables the server to
read/decrypt your secret values. read/decrypt your secret values.
</p> </p>
<p className="text-gray-400 mb-8"> <p className="mb-8 text-gray-400">
Note that, even with E2EE disabled, your secrets are always encrypted at rest. Note that, even with E2EE disabled, your secrets are always encrypted at rest.
</p> </p>
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}> <ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
{(isAllowed) => ( {(isAllowed) => (
<div className="w-max"> <div className="flex w-full flex-col gap-y-3">
<Checkbox <div className="w-max">
className="data-[state=checked]:bg-primary" <Checkbox
id="autoCapitalization" className="data-[state=checked]:bg-primary"
isChecked={!bot.isActive} id="autoCapitalization"
isDisabled={!isAllowed} isChecked={!bot.isActive}
onCheckedChange={async () => { isDisabled={!isAllowed}
await toggleBotActivate(); onCheckedChange={async () => {
}} await toggleBotActivate();
> }}
End-to-end encryption enabled >
</Checkbox> End-to-end encryption enabled
</Checkbox>
</div>
<div>
<Alert variant="warning">
<AlertDescription>
Enabling End-to-end encryption disables all the integrations
</AlertDescription>
</Alert>
</div>
</div> </div>
)} )}
</ProjectPermissionCan> </ProjectPermissionCan>