diff --git a/apps/code/src/renderer/features/onboarding/components/GitIntegrationStep.tsx b/apps/code/src/renderer/features/onboarding/components/GitIntegrationStep.tsx
index 95bbd925a3..ec01fbeb0f 100644
--- a/apps/code/src/renderer/features/onboarding/components/GitIntegrationStep.tsx
+++ b/apps/code/src/renderer/features/onboarding/components/GitIntegrationStep.tsx
@@ -22,6 +22,7 @@ import {
FolderOpen,
GearSix,
GitBranch,
+ Plus,
} from "@phosphor-icons/react";
import {
AlertDialog,
@@ -110,12 +111,11 @@ export function GitIntegrationStep({
isLoading: githubUserIntegrationsLoading,
} = useUserGithubIntegrations();
const hasGitIntegration = githubUserIntegrations.length > 0;
- const { repositories, isLoadingRepos, failedInstallationIds } =
+ const { repositories, failedInstallationIds, reposByInstallationId } =
useUserRepositoryIntegration();
- const githubIntegration = githubUserIntegrations[0] ?? null;
- const integrationIsStale =
- !!githubIntegration &&
- failedInstallationIds.includes(githubIntegration.installation_id);
+ const anyIntegrationStale = githubUserIntegrations.some((i) =>
+ failedInstallationIds.includes(i.installation_id),
+ );
const alternativeConnectedProjects = useMemo(() => {
if (hasGitIntegration) return [];
@@ -136,13 +136,6 @@ export function GitIntegrationStep({
);
}, [alternativeConnectedProjects, selectedAlternativeId]);
- const repoSummary = useMemo(() => {
- if (repositories.length === 0) return null;
- const names = repositories.map((r) => r.split("/").pop() ?? r);
- if (names.length <= 2) return names.join(" and ");
- return `${names[0]}, ${names[1]} and ${names.length - 2} more`;
- }, [repositories]);
-
const repoMatchesGitHub = useMemo(() => {
if (!detectedRepo || repositories.length === 0) return false;
return repositories.some(
@@ -151,18 +144,23 @@ export function GitIntegrationStep({
}, [detectedRepo, repositories]);
const apiClient = useOptionalAuthenticatedClient();
- const [disconnectConfirmOpen, setDisconnectConfirmOpen] = useState(false);
+ const [disconnectTarget, setDisconnectTarget] = useState<{
+ installationId: string;
+ accountName: string;
+ } | null>(null);
+ const [reconnectingInstallationId, setReconnectingInstallationId] = useState<
+ string | null
+ >(null);
const disconnectMutation = useMutation({
- mutationFn: async (opts: { silent?: boolean } = {}) => {
- const installationId = githubIntegration?.installation_id;
- if (!apiClient || !installationId) {
- throw new Error("No GitHub integration to disconnect");
+ mutationFn: async (opts: { installationId: string; silent?: boolean }) => {
+ if (!apiClient) {
+ throw new Error("Not authenticated");
}
- await apiClient.disconnectGithubUserIntegration(installationId);
+ await apiClient.disconnectGithubUserIntegration(opts.installationId);
return { silent: opts.silent ?? false };
},
onSuccess: ({ silent }) => {
- setDisconnectConfirmOpen(false);
+ setDisconnectTarget(null);
invalidateGithubQueries(queryClient, selectedProjectId);
if (!silent) toast.success("GitHub disconnected.");
},
@@ -344,7 +342,7 @@ export function GitIntegrationStep({
{isLoading || githubUserIntegrationsLoading ? (
) : hasGitIntegration ? (
- integrationIsStale ? (
+ anyIntegrationStale ? (
Reconnect needed
@@ -356,7 +354,9 @@ export function GitIntegrationStep({
className="text-(--green-9)"
/>
- Connected
+ {githubUserIntegrations.length > 1
+ ? `Connected (${githubUserIntegrations.length})`
+ : "Connected"}
)
@@ -364,64 +364,126 @@ export function GitIntegrationStep({
{hasGitIntegration ? (
-
- {integrationIsStale
- ? "We can't reach GitHub with your saved authorization. The app may have been uninstalled or the token revoked. Reconnect to restore access."
- : isLoadingRepos
- ? "Loading repositories..."
- : repoSummary
- ? `Access to ${repoSummary}`
- : "No repositories found. Check your GitHub app settings."}
-
-
- {integrationIsStale && (
-
- )}
-
+
+
+
+ {accountName}
+
+
+ {integration.account?.type ===
+ "Organization"
+ ? "org"
+ : "personal"}
+
+
+ {isStale ? (
+
+ Reconnect needed
+
+ ) : (
+
+ {isLoadingInstallRepos
+ ? "Loading…"
+ : installRepos.length === 1
+ ? "1 repo"
+ : `${installRepos.length} repos`}
+
+ )}
+
+
+ {isStale && (
+
+ )}
+
+
+
+
+ );
+ })}
+
@@ -609,15 +672,18 @@ export function GitIntegrationStep({
{
if (!next && !disconnectMutation.isPending) {
- setDisconnectConfirmOpen(false);
+ setDisconnectTarget(null);
}
}}
>
- Disconnect GitHub
+
+ Disconnect{" "}
+ {disconnectTarget ? disconnectTarget.accountName : "GitHub"}
+
This removes your personal GitHub authorization from PostHog. You
can reconnect at any time. The GitHub App itself stays installed
@@ -637,7 +703,12 @@ export function GitIntegrationStep({