mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 13:56:43 +00:00
feat: finalized integration selection in project settings
This commit is contained in:
@@ -6,9 +6,9 @@ import { useWorkspace } from "@app/context";
|
||||
import { ProjectVersion } from "@app/hooks/api/workspace/types";
|
||||
|
||||
import { EncryptionTab } from "./components/EncryptionTab";
|
||||
import { NotificationTab } from "./components/NotificationSection";
|
||||
import { ProjectGeneralTab } from "./components/ProjectGeneralTab";
|
||||
import { WebhooksTab } from "./components/WebhooksTab";
|
||||
import { WorkflowIntegrationTab } from "./components/WorkflowIntegrationSection";
|
||||
|
||||
export const ProjectSettingsPage = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -20,7 +20,7 @@ export const ProjectSettingsPage = () => {
|
||||
key: "tab-project-encryption",
|
||||
isHidden: currentWorkspace?.version !== ProjectVersion.V3
|
||||
},
|
||||
{ name: "Notification", key: "tab-project-notification" },
|
||||
{ name: "Workflow Integrations", key: "tab-workflow-integrations" },
|
||||
{ name: "Webhooks", key: "tab-project-webhooks" }
|
||||
];
|
||||
|
||||
@@ -59,7 +59,7 @@ export const ProjectSettingsPage = () => {
|
||||
</Tab.Panel>
|
||||
)}
|
||||
<Tab.Panel>
|
||||
<NotificationTab />
|
||||
<WorkflowIntegrationTab />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<WebhooksTab />
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
export * from "./NotificationTab";
|
||||
+90
-119
@@ -1,45 +1,45 @@
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
ContentLoader,
|
||||
DeleteActionModal,
|
||||
EmptyState,
|
||||
FormControl,
|
||||
Input,
|
||||
Select,
|
||||
SelectItem,
|
||||
Switch
|
||||
} from "@app/components/v2";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { usePopUp, useToggle } from "@app/hooks";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import {
|
||||
fetchSlackInstallUrl,
|
||||
useDeleteSlackIntegration,
|
||||
useGetSlackIntegrationByProject,
|
||||
useUpdateSlackIntegration
|
||||
useGetSlackIntegrations,
|
||||
useGetWorkspaceSlackConfig,
|
||||
useUpdateProjectSlackConfig
|
||||
} from "@app/hooks/api";
|
||||
|
||||
const formSchema = z.object({
|
||||
slackIntegrationId: z.string(),
|
||||
isSecretRequestNotificationEnabled: z.boolean(),
|
||||
secretRequestChannels: z.string(),
|
||||
secretRequestChannels: z.string().default(""),
|
||||
isAccessRequestNotificationEnabled: z.boolean(),
|
||||
accessRequestChannels: z.string()
|
||||
accessRequestChannels: z.string().default("")
|
||||
});
|
||||
|
||||
type TSlackIntegrationForm = z.infer<typeof formSchema>;
|
||||
type TSlackConfigForm = z.infer<typeof formSchema>;
|
||||
|
||||
export const NotificationTab = () => {
|
||||
export const WorkflowIntegrationTab = () => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { data: slackIntegration, isLoading: isSlackIntegrationLoading } =
|
||||
useGetSlackIntegrationByProject(currentWorkspace?.id);
|
||||
const { mutateAsync: updateSlackIntegration } = useUpdateSlackIntegration();
|
||||
const { mutateAsync: deleteSlackIntegration } = useDeleteSlackIntegration();
|
||||
const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp([
|
||||
"deleteSlackIntegration"
|
||||
] as const);
|
||||
const { data: slackConfig, isLoading: isSlackConfigLoading } = useGetWorkspaceSlackConfig({
|
||||
workspaceId: currentWorkspace?.id ?? ""
|
||||
});
|
||||
const { data: slackIntegrations } = useGetSlackIntegrations(currentWorkspace?.orgId);
|
||||
const { mutateAsync: updateProjectSlackConfig } = useUpdateProjectSlackConfig();
|
||||
|
||||
const {
|
||||
control,
|
||||
@@ -47,29 +47,27 @@ export const NotificationTab = () => {
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { isDirty, isSubmitting }
|
||||
} = useForm<TSlackIntegrationForm>({
|
||||
} = useForm<TSlackConfigForm>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
isSecretRequestNotificationEnabled: slackIntegration?.isSecretRequestNotificationEnabled,
|
||||
secretRequestChannels: slackIntegration?.secretRequestChannels || "",
|
||||
isAccessRequestNotificationEnabled: slackIntegration?.isAccessRequestNotificationEnabled,
|
||||
accessRequestChannels: slackIntegration?.accessRequestChannels || ""
|
||||
isAccessRequestNotificationEnabled: false,
|
||||
accessRequestChannels: "",
|
||||
isSecretRequestNotificationEnabled: false,
|
||||
secretRequestChannels: ""
|
||||
}
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const [isConnectToSlackLoading, setIsConnectToSlackLoading] = useToggle(false);
|
||||
const [isReinstallLoading, setIsReinstallLoading] = useToggle(false);
|
||||
const secretRequestNotifState = watch("isSecretRequestNotificationEnabled");
|
||||
const selectedSlackIntegrationId = watch("slackIntegrationId");
|
||||
const accessRequestNotifState = watch("isAccessRequestNotificationEnabled");
|
||||
|
||||
const handleIntegrationSave = async (data: TSlackIntegrationForm) => {
|
||||
if (!currentWorkspace || !slackIntegration) {
|
||||
const handleIntegrationSave = async (data: TSlackConfigForm) => {
|
||||
if (!currentWorkspace) {
|
||||
return;
|
||||
}
|
||||
await updateSlackIntegration({
|
||||
workspaceId: currentWorkspace?.id,
|
||||
id: slackIntegration?.id,
|
||||
|
||||
await updateProjectSlackConfig({
|
||||
workspaceId: currentWorkspace.id,
|
||||
...data
|
||||
});
|
||||
|
||||
@@ -79,96 +77,76 @@ export const NotificationTab = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleIntegrationDelete = async () => {
|
||||
if (!currentWorkspace || !slackIntegration) {
|
||||
return;
|
||||
}
|
||||
await deleteSlackIntegration({
|
||||
workspaceId: currentWorkspace.id,
|
||||
id: slackIntegration.id
|
||||
});
|
||||
|
||||
handlePopUpToggle("deleteSlackIntegration", false);
|
||||
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully deleted slack integration"
|
||||
});
|
||||
};
|
||||
|
||||
const triggerSlackInstall = async () => {
|
||||
const slackInstallUrl = await fetchSlackInstallUrl(currentWorkspace?.id);
|
||||
if (slackInstallUrl) {
|
||||
router.push(slackInstallUrl);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (slackIntegration) {
|
||||
if (slackConfig) {
|
||||
setValue("slackIntegrationId", slackConfig.slackIntegrationId);
|
||||
setValue(
|
||||
"isSecretRequestNotificationEnabled",
|
||||
slackIntegration.isSecretRequestNotificationEnabled
|
||||
slackConfig.isSecretRequestNotificationEnabled
|
||||
);
|
||||
setValue("secretRequestChannels", slackIntegration.secretRequestChannels);
|
||||
setValue("secretRequestChannels", slackConfig.secretRequestChannels);
|
||||
setValue(
|
||||
"isAccessRequestNotificationEnabled",
|
||||
slackIntegration.isAccessRequestNotificationEnabled
|
||||
slackConfig.isAccessRequestNotificationEnabled
|
||||
);
|
||||
setValue("accessRequestChannels", slackIntegration.accessRequestChannels);
|
||||
setValue("accessRequestChannels", slackConfig.accessRequestChannels);
|
||||
}
|
||||
}, [slackIntegration]);
|
||||
}, [slackConfig]);
|
||||
|
||||
if (isSlackIntegrationLoading) {
|
||||
if (isSlackConfigLoading) {
|
||||
return <ContentLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex justify-between">
|
||||
<h2 className="mb-2 flex-1 text-xl font-semibold text-mineshaft-100">
|
||||
Slack Integration
|
||||
</h2>
|
||||
return !slackIntegrations?.length ? (
|
||||
<EmptyState title="You do not have any integrations configured.">
|
||||
<Link href={`/org/${currentWorkspace?.orgId}/settings?tab=workflowIntegrations`}>
|
||||
<div className="mt-2 underline decoration-primary-800 underline-offset-4 duration-200 hover:cursor-pointer hover:text-mineshaft-100 hover:decoration-primary-600">
|
||||
Create one now
|
||||
</div>
|
||||
<p className="mb-4 text-gray-400">
|
||||
This integration allows you to send notifications to your Slack workspace in response to
|
||||
events in your project.
|
||||
</p>
|
||||
{!slackIntegration && (
|
||||
<Button
|
||||
isLoading={isConnectToSlackLoading}
|
||||
onClick={async () => {
|
||||
setIsConnectToSlackLoading.on();
|
||||
await triggerSlackInstall();
|
||||
}}
|
||||
>
|
||||
Connect to Slack
|
||||
</Button>
|
||||
)}
|
||||
{slackIntegration && (
|
||||
<form onSubmit={handleSubmit(handleIntegrationSave)}>
|
||||
<div>Connected Slack workspace: {slackIntegration.teamName}</div>
|
||||
<div className="mt-2 mb-6">
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
size="xs"
|
||||
isLoading={isReinstallLoading}
|
||||
onClick={async () => {
|
||||
setIsReinstallLoading.on();
|
||||
await triggerSlackInstall();
|
||||
}}
|
||||
>
|
||||
Reinstall
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
size="xs"
|
||||
className="ml-2"
|
||||
onClick={() => handlePopUpOpen("deleteSlackIntegration")}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</Link>
|
||||
</EmptyState>
|
||||
) : (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex justify-between">
|
||||
<h2 className="mb-2 flex-1 text-xl font-semibold text-mineshaft-100">Slack Integration</h2>
|
||||
</div>
|
||||
<p className="mb-4 text-gray-400">
|
||||
This integration allows you to send notifications to your Slack workspace in response to
|
||||
events in your project.
|
||||
</p>
|
||||
<form onSubmit={handleSubmit(handleIntegrationSave)}>
|
||||
<div className="max-w-md">
|
||||
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
|
||||
{(isAllowed) => (
|
||||
<Controller
|
||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||
<FormControl errorText={error?.message} isError={Boolean(error)}>
|
||||
<Select
|
||||
{...field}
|
||||
isDisabled={!isAllowed}
|
||||
onValueChange={onChange}
|
||||
defaultValue={slackConfig?.slackIntegrationId}
|
||||
className="w-3/4 bg-mineshaft-600"
|
||||
>
|
||||
{slackIntegrations?.map((slackIntegration) => (
|
||||
<SelectItem
|
||||
value={slackIntegration.id}
|
||||
key={`slack-integration-${slackIntegration.id}`}
|
||||
>
|
||||
{slackIntegration.slug}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
control={control}
|
||||
name="slackIntegrationId"
|
||||
/>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
{selectedSlackIntegrationId && (
|
||||
<>
|
||||
<h2 className="mb-2 flex-1 text-lg font-semibold text-mineshaft-100">Events</h2>
|
||||
<Controller
|
||||
control={control}
|
||||
@@ -260,16 +238,9 @@ export const NotificationTab = () => {
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteSlackIntegration.isOpen}
|
||||
title="Are you sure want to delete your Slack integration?"
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteSlackIntegration", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={handleIntegrationDelete}
|
||||
/>
|
||||
</>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from "./WorkflowIntegrationTab";
|
||||
Reference in New Issue
Block a user