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

View File

@@ -8,7 +8,7 @@ module.exports = {
env: {
browser: true,
es2021: true,
"es6": true
es6: true
},
extends: [
"airbnb",

View File

@@ -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
}
};

View File

@@ -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 };

View File

@@ -0,0 +1 @@
export { Alert, AlertDescription } from "./Alert";

View File

@@ -1,4 +1,5 @@
export * from "./Accordion";
export * from "./Alert";
export * from "./Button";
export * from "./Card";
export * from "./Checkbox";

View File

@@ -202,6 +202,8 @@ export const IntegrationsPage = withProjectPermission(
integrations={integrations}
environments={environments}
onIntegrationDelete={({ _id: id }, cb) => handleIntegrationDelete(id, cb)}
isBotActive={bot?.isActive}
workspaceId={workspaceId}
/>
<CloudIntegrationSection
isLoading={isCloudIntegrationsLoading || isIntegrationAuthLoading}

View File

@@ -1,9 +1,12 @@
import Link from "next/link";
import { faArrowRight, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { integrationSlugNameMapping } from "public/data/frequentConstants";
import { ProjectPermissionCan } from "@app/components/permissions";
import {
Alert,
AlertDescription,
DeleteActionModal,
EmptyState,
FormControl,
@@ -23,17 +26,22 @@ type Props = {
integrations?: TIntegration[];
isLoading?: boolean;
onIntegrationDelete: (integration: TIntegration, cb: () => void) => void;
isBotActive: boolean | undefined;
workspaceId: string;
};
export const IntegrationsSection = ({
integrations = [],
environments = [],
isLoading,
onIntegrationDelete
onIntegrationDelete,
isBotActive,
workspaceId
}: Props) => {
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"deleteConfirmation"
] as const);
return (
<div className="mb-8">
<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" />
</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">
<EmptyState
className="rounded-md border border-mineshaft-700 pt-8 pb-4"
@@ -53,7 +76,7 @@ export const IntegrationsSection = ({
/>
</div>
)}
{!isLoading && (
{!isLoading && isBotActive && (
<div className="flex flex-col space-y-4 p-6 pt-0">
{integrations?.map((integration) => (
<div

View File

@@ -3,7 +3,7 @@ import {
decryptAssymmetric,
encryptAssymmetric
} 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 { useGetUserWsKey, useGetWorkspaceBot, useUpdateBotActiveStatus } from "@app/hooks/api";
@@ -76,30 +76,39 @@ export const E2EESection = () => {
};
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="text-gray-400 mb-8">
<p className="mb-8 text-gray-400">
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
read/decrypt your secret values.
</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.
</p>
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
{(isAllowed) => (
<div className="w-max">
<Checkbox
className="data-[state=checked]:bg-primary"
id="autoCapitalization"
isChecked={!bot.isActive}
isDisabled={!isAllowed}
onCheckedChange={async () => {
await toggleBotActivate();
}}
>
End-to-end encryption enabled
</Checkbox>
<div className="flex w-full flex-col gap-y-3">
<div className="w-max">
<Checkbox
className="data-[state=checked]:bg-primary"
id="autoCapitalization"
isChecked={!bot.isActive}
isDisabled={!isAllowed}
onCheckedChange={async () => {
await toggleBotActivate();
}}
>
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>
)}
</ProjectPermissionCan>