import { useId, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import type { SlackSearchStatus, SlackToolCapabilities, } from "@paperclipai/shared"; import { slackToolsApi } from "@/api/slackTools"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; export function SlackCapabilitiesView({ capabilities, error, }: { capabilities?: SlackToolCapabilities; error?: string; }) { return (

Read, search, and act in Slack. Some actions require approval.

{error && (

{error}

)} {!capabilities && !error && (

Checking Slack permissions…

)} {capabilities && ( <> {capabilities.missingScopes.length > 0 && (

Add permissions to unlock more tools

Your existing connection still works. In{" "} Slack app settings , choose your app, open OAuth & Permissions, add these Bot Token Scopes, then reinstall the app to your workspace.

{capabilities.missingScopes.join(", ")}

)} )}
); } export function SlackSearchView({ status, onConnect, onDisconnect, onConfigure, }: { status: SlackSearchStatus; onConnect: () => Promise; onDisconnect: () => Promise; onConfigure: (input: { clientId: string; clientSecret: string; }) => Promise; }) { const id = useId(); const [clientId, setClientId] = useState(status.clientId ?? ""); const [clientSecret, setClientSecret] = useState(""); const [pending, setPending] = useState(false); const [error, setError] = useState(null); const perform = async (action: () => Promise) => { setPending(true); setError(null); try { await action(); } catch (e) { setError( e instanceof Error ? e.message : "Unable to update Slack search", ); } finally { setPending(false); } }; return (

Your Slack search access

Optional personal authorization enables private search on supported runtimes. It cannot read channels the bot hasn’t joined or let the bot write as you. Basic channel reading works without it.

{!status.nativeSearchAvailable && (

{status.limitation}

)} {status.connected ? (
Slack search connected
) : ( )} {!status.configured && (

A connection manager needs to configure your Slack app’s OAuth credentials first.

)} {status.canConfigure && (
OAuth app configuration for connection managers
{ event.preventDefault(); void perform(async () => { await onConfigure({ clientId, clientSecret }); setClientSecret(""); }); }} >

In Slack app settings, add this redirect URL under OAuth & Permissions. Add user scopes search:read.public,{" "} search:read.private and{" "} search:read.files. Find Client ID and Client Secret under Basic Information.

{status.redirectUri ?? "Configure a public HTTPS URL first."}

setClientId(e.target.value)} />
setClientSecret(e.target.value)} />
)} {error && (

{error}

)}
); } export function SlackToolsSettings({ companyId, endpointId, }: { companyId: string; endpointId: string; }) { const query = useQuery({ queryKey: ["slack-capabilities", companyId, endpointId], queryFn: () => slackToolsApi.capabilities(companyId, endpointId), staleTime: 60_000, }); return (
); } export function SlackSearchAccess({ companyId, endpointId, }: { companyId: string; endpointId: string; }) { const query = useQuery({ queryKey: ["slack-search", companyId, endpointId], queryFn: () => slackToolsApi.search(companyId, endpointId), }); if (query.error) return (

{query.error.message}

); if (!query.data) return (

Loading search access…

); return ( { const result = await slackToolsApi.connect(companyId, endpointId); window.location.assign(result.url); }} onDisconnect={async () => { await slackToolsApi.disconnect(companyId, endpointId); await query.refetch(); }} onConfigure={async (input) => { await slackToolsApi.configure(companyId, endpointId, input); await query.refetch(); }} /> ); }