feat(integrations): one-step install for workspace-scoped providers

This commit is contained in:
Philipinho
2026-05-23 12:22:26 +01:00
parent 2c80459674
commit 2f313187ab
3 changed files with 62 additions and 24 deletions
@@ -19,6 +19,7 @@ export default function ConnectionRow({
isDisconnecting,
}: ConnectionRowProps) {
const { t } = useTranslation();
const isWorkspaceScoped = definition.oauth?.connectionScope === 'workspace';
return (
<Box
@@ -42,30 +43,47 @@ export default function ConnectionRow({
</Group>
<Group gap="sm" wrap="nowrap" style={{ flexShrink: 0 }}>
{connection ? (
{isWorkspaceScoped ? (
<>
<Text size="xs" c="green">
{t("Connected")}
{connection.providerUserId && ` (${connection.providerUserId})`}
</Text>
<Button
size="xs"
variant="subtle"
color="red"
onClick={() => onDisconnect(connection.integrationId)}
loading={isDisconnecting}
>
{t("Disconnect")}
</Button>
{connection ? (
<Text size="xs" c="green">
{t("Linked")}
{connection.providerUserId && ` as @${connection.providerUserId}`}
</Text>
) : (
<Text size="xs" c="dimmed">
{t("Use")} <code>/docmost help</code> {t("in")} {definition.name} {t("to link your account.")}
</Text>
)}
</>
) : (
<Button
size="xs"
variant="light"
onClick={() => onConnect(definition.type)}
>
{t("Connect")}
</Button>
<>
{connection ? (
<>
<Text size="xs" c="green">
{t("Connected")}
{connection.providerUserId && ` (${connection.providerUserId})`}
</Text>
<Button
size="xs"
variant="subtle"
color="red"
onClick={() => onDisconnect(connection.integrationId)}
loading={isDisconnecting}
>
{t("Disconnect")}
</Button>
</>
) : (
<Button
size="xs"
variant="light"
onClick={() => onConnect(definition.type)}
>
{t("Connect")}
</Button>
)}
</>
)}
</Group>
</Group>
@@ -14,6 +14,7 @@ import {
useUpdateIntegrationSettings,
} from "../queries/integration-query";
import { Integration } from "../types/integration.types";
import { getOAuthAuthorizeUrl } from "../services/integration-service";
export default function Integrations() {
const { t } = useTranslation();
@@ -28,10 +29,21 @@ export default function Integrations() {
const [configuring, setConfiguring] = useState<Integration | null>(null);
const handleInstall = useCallback(
(type: string) => {
installMutation.mutate({ type });
async (type: string) => {
const definition = available?.find((d) => d.type === type);
try {
const integration = await installMutation.mutateAsync({ type });
if (definition?.oauth?.connectionScope === 'workspace') {
const { authorizationUrl } = await getOAuthAuthorizeUrl({
integrationId: integration.id,
});
window.location.href = authorizationUrl;
}
} catch (err) {
// installMutation's onError already shows a notification
}
},
[installMutation],
[installMutation, available],
);
const handleUninstall = useCallback(
@@ -1,11 +1,19 @@
export type IntegrationCapability = "oauth" | "unfurl" | "actions" | "webhooks";
export type OAuthConfig = {
authUrl: string;
tokenUrl: string;
scopes: string[];
connectionScope?: 'workspace' | 'user';
};
export type IntegrationDefinition = {
type: string;
name: string;
description: string;
icon: string;
capabilities: IntegrationCapability[];
oauth?: OAuthConfig;
};
export type Integration = {