"use client"; import { useState, useCallback } from "react"; import { IconBrandGithub, IconPlus, IconTrashX } from "@tabler/icons-react"; import { Button } from "@kandev/ui/button"; import { Card, CardContent } from "@kandev/ui/card"; import { Separator } from "@kandev/ui/separator"; import { TooltipProvider } from "@kandev/ui/tooltip"; import { useToast } from "@/components/toast-provider"; import { SettingsSection } from "@/components/settings/settings-section"; import { GitHubAutomationSettings, GitHubPersonalSettings } from "./github-status"; import { GitHubCallbackNotice } from "./github-callback-notice"; import { ReviewWatchTable } from "./review-watch-table"; import { ReviewWatchDialog } from "./review-watch-dialog"; import { IssueWatchTable } from "./issue-watch-table"; import { IssueWatchDialog } from "./issue-watch-dialog"; import { ActionPresetsSection } from "./action-presets-section"; import { DefaultQueriesSection } from "./default-queries-section"; import { GitHubRepoScopeSection } from "./github-repo-scope-section"; import { PRStatsPanel } from "./pr-stats"; import { useReviewWatches } from "@/hooks/domains/github/use-review-watches"; import { useIssueWatches } from "@/hooks/domains/github/use-issue-watches"; import { WorkspaceScopedSection } from "@/components/integrations/workspace-scoped-section"; import { useWatcherEnabledDrafts } from "@/components/integrations/use-watcher-enabled-drafts"; import { ResetWatchDialog, useWatchResetController } from "@/components/watches/reset-watch-dialog"; import { cleanupMergedReviewTasks, cleanupClosedIssueTasks } from "@/lib/api/domains/github-api"; import type { ReviewWatch, IssueWatch } from "@/lib/types/github"; import { useTranslation } from "react-i18next"; import { INTEGRATION_SETTINGS_TARGETS } from "@/lib/settings-discovery/catalog/integrations"; // CleanupNowButton runs a manual global sweep over the dedup tables. Useful // for users who upgraded with a pile of legacy merged-PR / closed-issue // review tasks that pre-date the cleanup_policy work — clicking once drains // them according to each watch's policy. function CleanupNowButton({ label, run, }: { label: string; run: () => Promise<{ deleted: number }>; }) { const { t } = useTranslation(); const { toast } = useToast(); const [busy, setBusy] = useState(false); return ( ); } function useWatchActions(workspaceId?: string | null) { const { t } = useTranslation(); const { items: watches, create, update, remove, trigger, previewReset, reset, } = useReviewWatches(workspaceId); const { toast } = useToast(); const handleDelete = useCallback( async (id: string) => { const watch = watches.find((item) => item.id === id); if (!watch) { toast({ description: t("github:reviewWatchNotFound"), variant: "error" }); return; } try { await remove(id, watch.workspace_id); toast({ description: t("github:reviewWatchDeleted"), variant: "success" }); } catch { toast({ description: t("github:failedToDeleteReviewWatch"), variant: "error" }); } }, [remove, t, toast, watches], ); const handleTrigger = useCallback( async (id: string) => { const watch = watches.find((item) => item.id === id); if (!watch) { toast({ description: t("github:reviewWatchNotFound"), variant: "error" }); return; } try { const result = await trigger(id, watch.workspace_id); const count = result?.new_prs_found ?? 0; if (count > 0) { toast({ description: t("github:foundNewPrs", { count }), variant: "success", }); } else { toast({ description: t("github:noNewPrsFound") }); } } catch { toast({ description: t("github:failedToCheckForPrs"), variant: "error" }); } }, [trigger, t, toast, watches], ); const handleReset = useCallback( async (id: string, workspaceId: string) => { try { const { tasksDeleted } = await reset(id, workspaceId); toast({ description: tasksDeleted > 0 ? t("github:resetCompleteDeletedTasks", { count: tasksDeleted }) : t("github:resetCompleteNextPollWillRe"), variant: "success", }); } catch { toast({ description: t("github:failedToResetReviewWatch"), variant: "error" }); throw new Error("reset failed"); } }, [reset, t, toast], ); return { watches, create, update, previewReset, handleDelete, handleTrigger, handleReset, }; } function useIssueWatchActions(workspaceId?: string | null) { const { t } = useTranslation(); const { items: watches, create, update, remove, trigger, previewReset, reset, } = useIssueWatches(workspaceId); const { toast } = useToast(); const handleDelete = useCallback( async (id: string) => { const watch = watches.find((item) => item.id === id); if (!watch) { toast({ description: t("github:issueWatchNotFound"), variant: "error" }); return; } try { await remove(id, watch.workspace_id); toast({ description: t("github:issueWatchDeleted"), variant: "success" }); } catch { toast({ description: t("github:failedToDeleteIssueWatch"), variant: "error" }); } }, [remove, t, toast, watches], ); const handleTrigger = useCallback( async (id: string) => { const watch = watches.find((item) => item.id === id); if (!watch) { toast({ description: t("github:issueWatchNotFound"), variant: "error" }); return; } try { const result = await trigger(id, watch.workspace_id); const count = result?.new_issues_found ?? 0; if (count > 0) { toast({ description: t("github:foundNewIssues", { count }), variant: "success", }); } else { toast({ description: t("github:noNewIssuesFound") }); } } catch { toast({ description: t("github:failedToCheckForIssues"), variant: "error" }); } }, [trigger, t, toast, watches], ); const handleReset = useCallback( async (id: string, workspaceId: string) => { try { const { tasksDeleted } = await reset(id, workspaceId); toast({ description: tasksDeleted > 0 ? t("github:resetCompleteDeletedTasks", { count: tasksDeleted }) : t("github:resetCompleteNextPollWillRe"), variant: "success", }); } catch { toast({ description: t("github:failedToResetIssueWatch"), variant: "error" }); throw new Error("reset failed"); } }, [reset, t, toast], ); return { watches, create, update, previewReset, handleDelete, handleTrigger, handleReset, }; } export function GitHubConnectionSection({ workspaceId }: { workspaceId: string }) { const { t } = useTranslation(); return ( <>
{t("github:chooseTheAutomationAndPersonalIdentities")}