mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 15:31:19 +00:00
Merge branch 'main' into feat/addGroupsToSshHosts
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useNavigate, useSearch } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ContentLoader } from "@app/components/v2";
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import { useOrganization } from "@app/context";
|
||||
import { useCreateMicrosoftTeamsIntegration } from "@app/hooks/api";
|
||||
|
||||
const stateSchema = z.object({
|
||||
redirectUri: z.string(),
|
||||
tenantId: z.string(),
|
||||
slug: z.string(),
|
||||
description: z.string().optional(),
|
||||
csrfToken: z.string(),
|
||||
clientId: z.string()
|
||||
});
|
||||
|
||||
export const OAuthCallbackPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const { currentOrg } = useOrganization();
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
|
||||
const search = useSearch({
|
||||
from: ROUTE_PATHS.Organization.Settings.OauthCallbackPage.id
|
||||
});
|
||||
|
||||
const createMicrosoftTeamsWorkflowIntegration = useCreateMicrosoftTeamsIntegration();
|
||||
|
||||
const { state: rawState, code } = search;
|
||||
|
||||
const state = stateSchema.parse(rawState);
|
||||
|
||||
const clearState = () => {
|
||||
if (state.csrfToken !== localStorage.getItem("latestCSRFToken")) {
|
||||
throw new Error("Invalid CSRF token");
|
||||
}
|
||||
|
||||
localStorage.removeItem("latestCSRFToken");
|
||||
};
|
||||
|
||||
const handleMicrosoftTeams = useCallback(async () => {
|
||||
clearState();
|
||||
|
||||
if (!code) {
|
||||
throw new Error("No code provided");
|
||||
}
|
||||
|
||||
await createMicrosoftTeamsWorkflowIntegration.mutateAsync({
|
||||
orgId: currentOrg.id,
|
||||
tenantId: state.tenantId,
|
||||
code,
|
||||
slug: state.slug,
|
||||
description: state.description ?? "",
|
||||
redirectUri: state.redirectUri
|
||||
});
|
||||
|
||||
navigate({
|
||||
to: ROUTE_PATHS.Organization.SettingsPage.path
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Ensure that the localstorage is ready for use, to avoid the form data being malformed
|
||||
useEffect(() => {
|
||||
if (!isReady) {
|
||||
setIsReady(!!localStorage.length);
|
||||
}
|
||||
}, [localStorage.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isReady) return;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await handleMicrosoftTeams();
|
||||
|
||||
createNotification({
|
||||
text: "Successfully created Microsoft Teams workflow integration",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to create Microsoft Teams workflow integration",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [isReady]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<ContentLoader text="Please wait! Authentication in process." />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { z } from "zod";
|
||||
|
||||
import { OAuthCallbackPage } from "./OauthCallbackPage";
|
||||
|
||||
const SettingsOAuthCallbackPageQueryParamsSchema = z.object({
|
||||
state: z
|
||||
.object({
|
||||
clientId: z.string(),
|
||||
tenantId: z.string(),
|
||||
slug: z.string(),
|
||||
description: z.string().optional(),
|
||||
redirectUri: z.string(),
|
||||
csrfToken: z.string()
|
||||
})
|
||||
.nullable()
|
||||
.catch(null),
|
||||
code: z.string().catch("")
|
||||
});
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/settings/oauth/callback"
|
||||
)({
|
||||
component: OAuthCallbackPage,
|
||||
validateSearch: zodValidator(SettingsOAuthCallbackPageQueryParamsSchema)
|
||||
});
|
||||
+25
-3
@@ -1,4 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { BsMicrosoftTeams } from "react-icons/bs";
|
||||
import { faSlack } from "@fortawesome/free-brands-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
@@ -6,6 +7,7 @@ import { AnimatePresence, motion } from "framer-motion";
|
||||
import { Modal, ModalContent } from "@app/components/v2";
|
||||
import { WorkflowIntegrationPlatform } from "@app/hooks/api/workflowIntegrations/types";
|
||||
|
||||
import { MicrosoftTeamsIntegrationForm } from "./MicrosoftTeamsIntegrationForm";
|
||||
import { SlackIntegrationForm } from "./SlackIntegrationForm";
|
||||
|
||||
type Props = {
|
||||
@@ -20,9 +22,14 @@ enum WizardSteps {
|
||||
|
||||
const PLATFORM_LIST = [
|
||||
{
|
||||
icon: faSlack,
|
||||
icon: <FontAwesomeIcon icon={faSlack} size="lg" />,
|
||||
platform: WorkflowIntegrationPlatform.SLACK,
|
||||
title: "Slack"
|
||||
},
|
||||
{
|
||||
icon: <BsMicrosoftTeams className="text-lg" />,
|
||||
platform: WorkflowIntegrationPlatform.MICROSOFT_TEAMS,
|
||||
title: "Microsoft Teams"
|
||||
}
|
||||
];
|
||||
|
||||
@@ -38,7 +45,10 @@ export const AddWorkflowIntegrationForm = ({ isOpen, onToggle }: Props) => {
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onOpenChange={(state) => handleFormReset(state)}>
|
||||
<ModalContent title={`Add a ${selectedPlatform ?? "workflow"} integration`} className="my-4">
|
||||
<ModalContent
|
||||
title={`Add a ${selectedPlatform?.replace("-", " ").replace(/\b\w/g, (char) => char.toUpperCase()) ?? "workflow"} integration`}
|
||||
className="my-4"
|
||||
>
|
||||
<AnimatePresence mode="wait">
|
||||
{wizardStep === WizardSteps.SelectPlatform && (
|
||||
<motion.div
|
||||
@@ -67,7 +77,7 @@ export const AddWorkflowIntegrationForm = ({ isOpen, onToggle }: Props) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={icon} size="lg" />
|
||||
<div>{icon}</div>
|
||||
<div className="whitespace-pre-wrap text-center text-sm">{title}</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -86,6 +96,18 @@ export const AddWorkflowIntegrationForm = ({ isOpen, onToggle }: Props) => {
|
||||
<SlackIntegrationForm onClose={() => onToggle(false)} />
|
||||
</motion.div>
|
||||
)}
|
||||
{wizardStep === WizardSteps.PlatformInputs &&
|
||||
selectedPlatform === WorkflowIntegrationPlatform.MICROSOFT_TEAMS && (
|
||||
<motion.div
|
||||
key="microsoft-teams-platform"
|
||||
transition={{ duration: 0.1 }}
|
||||
initial={{ opacity: 0, translateX: 30 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
exit={{ opacity: 0, translateX: -30 }}
|
||||
>
|
||||
<MicrosoftTeamsIntegrationForm onClose={() => onToggle(false)} />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
+4
@@ -1,6 +1,7 @@
|
||||
import { Modal, ModalContent } from "@app/components/v2";
|
||||
import { WorkflowIntegrationPlatform } from "@app/hooks/api/workflowIntegrations/types";
|
||||
|
||||
import { MicrosoftTeamsIntegrationForm } from "./MicrosoftTeamsIntegrationForm";
|
||||
import { SlackIntegrationForm } from "./SlackIntegrationForm";
|
||||
|
||||
type Props = {
|
||||
@@ -20,6 +21,9 @@ export const IntegrationFormDetails = ({ isOpen, id, onOpenChange, workflowPlatf
|
||||
{workflowPlatform === WorkflowIntegrationPlatform.SLACK && (
|
||||
<SlackIntegrationForm id={id} onClose={() => onOpenChange(false)} />
|
||||
)}
|
||||
{workflowPlatform === WorkflowIntegrationPlatform.MICROSOFT_TEAMS && (
|
||||
<MicrosoftTeamsIntegrationForm id={id} onClose={() => onOpenChange(false)} />
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
import crypto from "crypto";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input } from "@app/components/v2";
|
||||
import { useOrganization } from "@app/context";
|
||||
import {
|
||||
useGetMicrosoftTeamsClientId,
|
||||
useGetMicrosoftTeamsIntegrationById,
|
||||
useUpdateMicrosoftTeamsIntegration
|
||||
} from "@app/hooks/api";
|
||||
import { slugSchema } from "@app/lib/schemas";
|
||||
|
||||
type Props = {
|
||||
id?: string;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const microsoftTeamsFormSchema = z.object({
|
||||
slug: slugSchema({ min: 1, field: "Alias" }),
|
||||
tenantId: z
|
||||
.string()
|
||||
.min(1, { message: "Tenant ID is required" })
|
||||
.trim()
|
||||
.uuid("Tenant ID must be a valid UUID"),
|
||||
description: z.string().optional()
|
||||
});
|
||||
|
||||
type TMicrosoftTeamsFormData = z.infer<typeof microsoftTeamsFormSchema>;
|
||||
|
||||
export const MicrosoftTeamsIntegrationForm = ({ id, onClose }: Props) => {
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { isSubmitting, isDirty }
|
||||
} = useForm<TMicrosoftTeamsFormData>({
|
||||
resolver: zodResolver(microsoftTeamsFormSchema)
|
||||
});
|
||||
|
||||
const { currentOrg } = useOrganization();
|
||||
const { data: microsoftTeamsIntegration } = useGetMicrosoftTeamsIntegrationById(id);
|
||||
const { mutateAsync: updateMicrosoftTeamsIntegration } = useUpdateMicrosoftTeamsIntegration();
|
||||
const { data: microsoftTeamsClientId } = useGetMicrosoftTeamsClientId();
|
||||
|
||||
useEffect(() => {
|
||||
if (microsoftTeamsIntegration) {
|
||||
setValue("slug", microsoftTeamsIntegration.slug);
|
||||
setValue("description", microsoftTeamsIntegration.description ?? "");
|
||||
setValue("tenantId", microsoftTeamsIntegration.tenantId);
|
||||
}
|
||||
}, [microsoftTeamsIntegration]);
|
||||
|
||||
const handleMicrosoftTeamsFormSubmit = async ({
|
||||
slug,
|
||||
description,
|
||||
tenantId
|
||||
}: TMicrosoftTeamsFormData) => {
|
||||
if (!microsoftTeamsClientId) {
|
||||
createNotification({
|
||||
text: "Microsoft Teams client ID is not set. Please contact your instance administrator.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (id && microsoftTeamsIntegration) {
|
||||
if (!currentOrg) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tenantId !== microsoftTeamsIntegration.tenantId) {
|
||||
createNotification({
|
||||
text: "Tenant ID cannot be changed",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateMicrosoftTeamsIntegration({
|
||||
id,
|
||||
orgId: currentOrg.id,
|
||||
slug,
|
||||
description
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully updated Microsoft Teams integration",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
onClose();
|
||||
} else {
|
||||
const csrfToken = crypto.randomBytes(32).toString("hex");
|
||||
localStorage.setItem("latestCSRFToken", csrfToken);
|
||||
|
||||
const state = {
|
||||
redirectUri: `${window.location.origin}/organization/settings/oauth/callback`,
|
||||
tenantId,
|
||||
slug,
|
||||
description,
|
||||
csrfToken,
|
||||
clientId: microsoftTeamsClientId.clientId
|
||||
};
|
||||
|
||||
const url = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize?
|
||||
client_id=${microsoftTeamsClientId.clientId}
|
||||
&redirect_uri=${state.redirectUri}
|
||||
&response_type=code
|
||||
&response_mode=query
|
||||
&scope=https://graph.microsoft.com/.default
|
||||
&state=${encodeURIComponent(JSON.stringify(state))}
|
||||
&prompt=consent
|
||||
&admin_consent=true`;
|
||||
|
||||
window.location.href = url;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(handleMicrosoftTeamsFormSubmit)} autoComplete="off">
|
||||
<div className="mb-4 text-xs text-mineshaft-200">
|
||||
For seamless installations, ensure that the Infisical bot is already installed in your
|
||||
Microsoft Teams tenant. For more information, please refer to the{" "}
|
||||
<a
|
||||
className="text-primary-500"
|
||||
href="https://infisical.com/docs/documentation/platform/workflow-integrations/microsoft-teams-integration"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Microsoft Teams Workflow Integration Documentation
|
||||
</a>
|
||||
, which will guide you through the download and installation process.
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="slug"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl label="Alias" isRequired errorText={error?.message} isError={Boolean(error)}>
|
||||
<Input placeholder="" {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{!microsoftTeamsIntegration && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="tenantId"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
isRequired
|
||||
label="Tenant ID"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input placeholder="" {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Controller
|
||||
control={control}
|
||||
name="description"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl label="Description" errorText={error?.message} isError={Boolean(error)}>
|
||||
<Input placeholder="" {...field} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{microsoftTeamsIntegration && (
|
||||
<FormControl label="Connected Microsoft Teams Tenant">
|
||||
<Input
|
||||
value={microsoftTeamsIntegration.tenantId}
|
||||
isReadOnly
|
||||
className="bg-white/[0.07]"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
<div className="mt-6 flex items-center space-x-4">
|
||||
<Button type="submit" isLoading={isSubmitting} isDisabled={!isDirty || isSubmitting}>
|
||||
{id && microsoftTeamsIntegration ? "Save" : "Create Microsoft Teams Integration"}
|
||||
</Button>
|
||||
<Button variant="outline_bg" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
+123
-44
@@ -1,5 +1,5 @@
|
||||
import { faSlack } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faEllipsis, faGear, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { BsMicrosoftTeams, BsSlack } from "react-icons/bs";
|
||||
import { faEllipsis, faGear, faInfoCircle, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import axios from "axios";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
@@ -7,6 +7,7 @@ import { twMerge } from "tailwind-merge";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
DropdownMenu,
|
||||
@@ -20,6 +21,7 @@ import {
|
||||
TBody,
|
||||
Td,
|
||||
THead,
|
||||
Tooltip,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
@@ -27,14 +29,31 @@ import { withPermission } from "@app/hoc";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import {
|
||||
fetchSlackReinstallUrl,
|
||||
useCheckMicrosoftTeamsIntegrationInstallationStatus,
|
||||
useDeleteMicrosoftTeamsIntegration,
|
||||
useDeleteSlackIntegration,
|
||||
useGetWorkflowIntegrations
|
||||
} from "@app/hooks/api";
|
||||
import { WorkflowIntegrationPlatform } from "@app/hooks/api/workflowIntegrations/types";
|
||||
import {
|
||||
WorkflowIntegrationPlatform,
|
||||
WorkflowIntegrationStatus
|
||||
} from "@app/hooks/api/workflowIntegrations/types";
|
||||
|
||||
import { AddWorkflowIntegrationForm } from "./AddWorkflowIntegrationForm";
|
||||
import { IntegrationFormDetails } from "./IntegrationFormDetails";
|
||||
|
||||
const renderStatus = (status: WorkflowIntegrationStatus) => {
|
||||
if (status === WorkflowIntegrationStatus.Installed) {
|
||||
return <Badge variant="success">Installed</Badge>;
|
||||
}
|
||||
|
||||
if (status === WorkflowIntegrationStatus.Pending) {
|
||||
return <Badge variant="primary">Pending</Badge>;
|
||||
}
|
||||
|
||||
return <Badge variant="danger">Failed</Badge>;
|
||||
};
|
||||
|
||||
export const OrgWorkflowIntegrationTab = withPermission(
|
||||
() => {
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle, handlePopUpClose } = usePopUp([
|
||||
@@ -48,6 +67,9 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
||||
useGetWorkflowIntegrations(currentOrg?.id);
|
||||
|
||||
const { mutateAsync: deleteSlackIntegration } = useDeleteSlackIntegration();
|
||||
const { mutateAsync: deleteMicrosoftTeamsIntegration } = useDeleteMicrosoftTeamsIntegration();
|
||||
const { mutateAsync: checkMicrosoftTeamsInstallationStatus } =
|
||||
useCheckMicrosoftTeamsIntegrationInstallationStatus();
|
||||
|
||||
const handleRemoveIntegration = async () => {
|
||||
if (!currentOrg) {
|
||||
@@ -62,6 +84,13 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
||||
});
|
||||
}
|
||||
|
||||
if (platform === WorkflowIntegrationPlatform.MICROSOFT_TEAMS) {
|
||||
await deleteMicrosoftTeamsIntegration({
|
||||
id,
|
||||
orgId: currentOrg?.id
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpClose("removeIntegration");
|
||||
createNotification({
|
||||
text: "Successfully deleted integration",
|
||||
@@ -69,27 +98,37 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
||||
});
|
||||
};
|
||||
|
||||
const triggerReinstall = async (platform: WorkflowIntegrationPlatform, id: string) => {
|
||||
if (platform === WorkflowIntegrationPlatform.SLACK) {
|
||||
try {
|
||||
const slackReinstallUrl = await fetchSlackReinstallUrl({
|
||||
id
|
||||
});
|
||||
const triggerSlackReinstall = async (id: string) => {
|
||||
try {
|
||||
const slackReinstallUrl = await fetchSlackReinstallUrl({
|
||||
id
|
||||
});
|
||||
|
||||
if (slackReinstallUrl) {
|
||||
window.location.href = slackReinstallUrl;
|
||||
}
|
||||
} catch (err) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
createNotification({
|
||||
text: (err.response?.data as { message: string })?.message,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
if (slackReinstallUrl) {
|
||||
window.location.href = slackReinstallUrl;
|
||||
}
|
||||
} catch (err) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
createNotification({
|
||||
text: (err.response?.data as { message: string })?.message,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const triggerMicrosoftTeamsInstallationStatusCheck = async (id: string) => {
|
||||
await checkMicrosoftTeamsInstallationStatus({
|
||||
workflowIntegrationId: id,
|
||||
orgId: currentOrg?.id
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "The Microsoft Teams bot is successfully installed in your Microsoft Teams tenant",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex justify-between">
|
||||
@@ -117,6 +156,7 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
||||
<Tr>
|
||||
<Td>Provider</Td>
|
||||
<Td>Alias</Td>
|
||||
<Td>Status</Td>
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
@@ -134,11 +174,23 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
||||
)}
|
||||
{workflowIntegrations?.map((workflowIntegration) => (
|
||||
<Tr key={workflowIntegration.id}>
|
||||
<Td className="flex max-w-xs items-center overflow-hidden text-ellipsis hover:overflow-auto hover:break-all">
|
||||
<FontAwesomeIcon icon={faSlack} />
|
||||
<div className="ml-2">{workflowIntegration.integration.toUpperCase()}</div>
|
||||
<Td className="flex max-w-xs items-center gap-2 overflow-hidden text-ellipsis hover:overflow-auto hover:break-all">
|
||||
{workflowIntegration.integration === WorkflowIntegrationPlatform.SLACK ? (
|
||||
<BsSlack />
|
||||
) : (
|
||||
<BsMicrosoftTeams />
|
||||
)}
|
||||
<div className="capitalize">
|
||||
{workflowIntegration.integration.replaceAll("-", " ")}
|
||||
</div>
|
||||
{workflowIntegration.description && (
|
||||
<Tooltip content={workflowIntegration.description}>
|
||||
<FontAwesomeIcon icon={faInfoCircle} className="text-gray-400" size="sm" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</Td>
|
||||
<Td>{workflowIntegration.slug}</Td>
|
||||
<Td>{renderStatus(workflowIntegration.status)}</Td>
|
||||
<Td>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||
@@ -159,29 +211,56 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
||||
>
|
||||
More details
|
||||
</DropdownMenuItem>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Create}
|
||||
an={OrgPermissionSubjects.Settings}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
disabled={!isAllowed}
|
||||
className={twMerge(
|
||||
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
triggerReinstall(
|
||||
workflowIntegration.integration,
|
||||
workflowIntegration.id
|
||||
);
|
||||
}}
|
||||
>
|
||||
Reinstall
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
{workflowIntegration.integration === WorkflowIntegrationPlatform.SLACK && (
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Create}
|
||||
an={OrgPermissionSubjects.Settings}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
disabled={!isAllowed}
|
||||
className={twMerge(
|
||||
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
await triggerSlackReinstall(workflowIntegration.id);
|
||||
}}
|
||||
>
|
||||
Reinstall
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
|
||||
{workflowIntegration.integration ===
|
||||
WorkflowIntegrationPlatform.MICROSOFT_TEAMS && (
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Edit}
|
||||
an={OrgPermissionSubjects.Settings}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
disabled={!isAllowed}
|
||||
className={twMerge(
|
||||
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
await triggerMicrosoftTeamsInstallationStatusCheck(
|
||||
workflowIntegration.id
|
||||
);
|
||||
}}
|
||||
>
|
||||
Check Installation Status
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Delete}
|
||||
an={OrgPermissionSubjects.Settings}
|
||||
|
||||
@@ -1,33 +1,37 @@
|
||||
import { faHome } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createFileRoute, linkOptions, stripSearchParams } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { z } from "zod";
|
||||
import { faHome } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import {
|
||||
createFileRoute,
|
||||
linkOptions,
|
||||
stripSearchParams,
|
||||
} from '@tanstack/react-router'
|
||||
import { zodValidator } from '@tanstack/zod-adapter'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { SettingsPage } from "./SettingsPage";
|
||||
import { SettingsPage } from './SettingsPage'
|
||||
|
||||
const SettingsPageQueryParams = z.object({
|
||||
selectedTab: z.string().catch("")
|
||||
});
|
||||
selectedTab: z.string().catch(''),
|
||||
})
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/settings"
|
||||
'/_authenticate/_inject-org-details/_org-layout/organization/settings/',
|
||||
)({
|
||||
component: SettingsPage,
|
||||
validateSearch: zodValidator(SettingsPageQueryParams),
|
||||
search: {
|
||||
middlewares: [stripSearchParams({ selectedTab: "" })]
|
||||
middlewares: [stripSearchParams({ selectedTab: '' })],
|
||||
},
|
||||
context: () => ({
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: "Home",
|
||||
label: 'Home',
|
||||
icon: () => <FontAwesomeIcon icon={faHome} />,
|
||||
link: linkOptions({ to: "/" })
|
||||
link: linkOptions({ to: '/' }),
|
||||
},
|
||||
{
|
||||
label: "Settings"
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
||||
label: 'Settings',
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user