From 812d8d16bf95c21fe731329e4beb6b28b120b8f2 Mon Sep 17 00:00:00 2001 From: CyberT17 Date: Tue, 30 Dec 2025 11:52:57 -0600 Subject: [PATCH 1/5] feat(web): Added Trigger Sync to /repos dropdown menu --- .../[domain]/repos/components/reposTable.tsx | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/web/src/app/[domain]/repos/components/reposTable.tsx b/packages/web/src/app/[domain]/repos/components/reposTable.tsx index f75af96a..b9266e62 100644 --- a/packages/web/src/app/[domain]/repos/components/reposTable.tsx +++ b/packages/web/src/app/[domain]/repos/components/reposTable.tsx @@ -14,7 +14,7 @@ import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/in import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants" -import { cn, getCodeHostCommitUrl, getCodeHostIcon, getCodeHostInfoForRepo, getRepoImageSrc } from "@/lib/utils" +import { cn, getCodeHostCommitUrl, getCodeHostIcon, getCodeHostInfoForRepo, getRepoImageSrc, isServiceError } from "@/lib/utils" import { type ColumnDef, type VisibilityState, @@ -35,6 +35,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip import { NotificationDot } from "../../components/notificationDot" import { CodeHostType } from "@sourcebot/db" import { useHotkeys } from "react-hotkeys-hook" +import { indexRepo } from "@/features/workerApi/actions" // @see: https://v0.app/chat/repo-indexing-status-uhjdDim8OUS @@ -84,6 +85,8 @@ interface ColumnsContext { onSortChange: (sortBy: string) => void; currentSortBy?: string; currentSortOrder: string; + onTriggerSync: (repoId: number) => void; + syncingRepoId: number | null; } export const getColumns = (context: ColumnsContext): ColumnDef[] => [ @@ -259,6 +262,7 @@ export const getColumns = (context: ColumnsContext): ColumnDef[] => [ displayName: repo.displayName ?? undefined, webUrl: repo.webUrl ?? undefined, }); + const isSyncing = context.syncingRepoId === repo.id; return ( @@ -276,6 +280,12 @@ export const getColumns = (context: ColumnsContext): ColumnDef[] => [ {repo.webUrl && ( <> + context.onTriggerSync(repo.id)} + disabled={isSyncing} + > + Trigger Sync + Open in {codeHostInfo.codeHostName} @@ -324,6 +334,7 @@ export const ReposTable = ({ const [rowSelection, setRowSelection] = useState({}) const [searchValue, setSearchValue] = useState(initialSearch) const [isPendingSearch, setIsPendingSearch] = useState(false) + const [syncingRepoId, setSyncingRepoId] = useState(null) const searchInputRef = useRef(null) const router = useRouter(); const searchParams = useSearchParams(); @@ -386,12 +397,33 @@ export const ReposTable = ({ router.replace(`${pathname}?${params.toString()}`); }; + const handleTriggerSync = async (repoId: number) => { + setSyncingRepoId(repoId); + const response = await indexRepo(repoId); + + if (!isServiceError(response)) { + const { jobId } = response; + toast({ + description: `✅ Repository sync triggered successfully. Job ID: ${jobId}`, + }); + router.refresh(); + } else { + toast({ + description: `❌ Failed to sync repository. ${response.message}`, + }); + } + + setSyncingRepoId(null); + }; + const totalPages = Math.ceil(totalCount / pageSize); const columns = getColumns({ onSortChange: handleSortChange, currentSortBy: initialSortBy, currentSortOrder: initialSortOrder, + onTriggerSync: handleTriggerSync, + syncingRepoId: syncingRepoId, }); const table = useReactTable({ From 51f0b3e0f252ddd9eaf1341adcfad55bb6cb7312 Mon Sep 17 00:00:00 2001 From: CyberT17 Date: Tue, 30 Dec 2025 11:55:42 -0600 Subject: [PATCH 2/5] feat(web): Updated CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f00117c..0533fd3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Changed the default `/repos` pagination size to 20. [#706](https://github.com/sourcebot-dev/sourcebot/pull/706) +- Added Trigger Sync to /repos dropdown menu [#710](https://github.com/sourcebot-dev/sourcebot/pull/710) ## [4.10.7] - 2025-12-29 From 41bc0762d57c51e8cc3f0ea42075a2ae98c7d884 Mon Sep 17 00:00:00 2001 From: CyberT17 Date: Tue, 30 Dec 2025 17:03:08 -0600 Subject: [PATCH 3/5] feat(web): moved trigger sync dropdown menu item outside of repo.webUrl block - Moved the whole repo actions dropdown to its own component (repoActionsDropdown.tsx) --- .../repos/components/repoActionsDropdown.tsx | 84 +++++++++++++++++++ .../[domain]/repos/components/reposTable.tsx | 55 +----------- 2 files changed, 88 insertions(+), 51 deletions(-) create mode 100644 packages/web/src/app/[domain]/repos/components/repoActionsDropdown.tsx diff --git a/packages/web/src/app/[domain]/repos/components/repoActionsDropdown.tsx b/packages/web/src/app/[domain]/repos/components/repoActionsDropdown.tsx new file mode 100644 index 00000000..2f8dd572 --- /dev/null +++ b/packages/web/src/app/[domain]/repos/components/repoActionsDropdown.tsx @@ -0,0 +1,84 @@ +"use client" + +import { Button } from "@/components/ui/button" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants" +import { getCodeHostInfoForRepo, isServiceError } from "@/lib/utils" +import { ExternalLink, MoreHorizontal } from "lucide-react" +import Link from "next/link" +import { useState } from "react" +import { indexRepo } from "@/features/workerApi/actions" +import { useRouter } from "next/navigation" +import { useToast } from "@/components/hooks/use-toast" +import type { Repo } from "./reposTable" + +interface RepoActionsDropdownProps { + repo: Repo +} + +export const RepoActionsDropdown = ({ repo }: RepoActionsDropdownProps) => { + const [isSyncing, setIsSyncing] = useState(false) + const router = useRouter() + const { toast } = useToast() + + const codeHostInfo = getCodeHostInfoForRepo({ + codeHostType: repo.codeHostType, + name: repo.name, + displayName: repo.displayName ?? undefined, + webUrl: repo.webUrl ?? undefined, + }) + + const handleTriggerSync = async () => { + setIsSyncing(true) + const response = await indexRepo(repo.id) + + if (!isServiceError(response)) { + const { jobId } = response + toast({ + description: `✅ Repository sync triggered successfully. Job ID: ${jobId}`, + }) + router.refresh() + } else { + toast({ + description: `❌ Failed to sync repository. ${response.message}`, + }) + } + + setIsSyncing(false) + } + + return ( + + + + + + Actions + + View details + + + Trigger sync + + {repo.webUrl && ( + <> + + + + Open in {codeHostInfo.codeHostName} + + + + + )} + + + ) +} diff --git a/packages/web/src/app/[domain]/repos/components/reposTable.tsx b/packages/web/src/app/[domain]/repos/components/reposTable.tsx index b9266e62..8511a4af 100644 --- a/packages/web/src/app/[domain]/repos/components/reposTable.tsx +++ b/packages/web/src/app/[domain]/repos/components/reposTable.tsx @@ -2,19 +2,11 @@ import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants" -import { cn, getCodeHostCommitUrl, getCodeHostIcon, getCodeHostInfoForRepo, getRepoImageSrc, isServiceError } from "@/lib/utils" +import { cn, getCodeHostCommitUrl, getCodeHostIcon, getRepoImageSrc, isServiceError } from "@/lib/utils" import { type ColumnDef, type VisibilityState, @@ -23,7 +15,7 @@ import { useReactTable, } from "@tanstack/react-table" import { cva } from "class-variance-authority" -import { ArrowDown, ArrowUp, ArrowUpDown, ExternalLink, Loader2, MoreHorizontal, RefreshCwIcon } from "lucide-react" +import { ArrowDown, ArrowUp, ArrowUpDown, Loader2, RefreshCwIcon } from "lucide-react" import Image from "next/image" import Link from "next/link" import { useEffect, useRef, useState } from "react" @@ -36,6 +28,7 @@ import { NotificationDot } from "../../components/notificationDot" import { CodeHostType } from "@sourcebot/db" import { useHotkeys } from "react-hotkeys-hook" import { indexRepo } from "@/features/workerApi/actions" +import { RepoActionsDropdown } from "./repoActionsDropdown" // @see: https://v0.app/chat/repo-indexing-status-uhjdDim8OUS @@ -256,47 +249,7 @@ export const getColumns = (context: ColumnsContext): ColumnDef[] => [ enableHiding: false, cell: ({ row }) => { const repo = row.original - const codeHostInfo = getCodeHostInfoForRepo({ - codeHostType: repo.codeHostType, - name: repo.name, - displayName: repo.displayName ?? undefined, - webUrl: repo.webUrl ?? undefined, - }); - const isSyncing = context.syncingRepoId === repo.id; - - return ( - - - - - - Actions - - View details - - {repo.webUrl && ( - <> - - context.onTriggerSync(repo.id)} - disabled={isSyncing} - > - Trigger Sync - - - - Open in {codeHostInfo.codeHostName} - - - - - )} - - - ) + return }, }, ] From 5e7743e0e7bbd11d0504c33d74833dbebd897a8c Mon Sep 17 00:00:00 2001 From: CyberT17 Date: Tue, 30 Dec 2025 17:04:33 -0600 Subject: [PATCH 4/5] feat(web): Fixed import --- .../src/app/[domain]/repos/components/repoActionsDropdown.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/web/src/app/[domain]/repos/components/repoActionsDropdown.tsx b/packages/web/src/app/[domain]/repos/components/repoActionsDropdown.tsx index 2f8dd572..11653a76 100644 --- a/packages/web/src/app/[domain]/repos/components/repoActionsDropdown.tsx +++ b/packages/web/src/app/[domain]/repos/components/repoActionsDropdown.tsx @@ -1,8 +1,7 @@ "use client" import { Button } from "@/components/ui/button" -import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants" import { getCodeHostInfoForRepo, isServiceError } from "@/lib/utils" import { ExternalLink, MoreHorizontal } from "lucide-react" From e453f1c2650ad0a389b6620e889f586f8d5afd30 Mon Sep 17 00:00:00 2001 From: CyberT17 Date: Wed, 31 Dec 2025 09:19:46 -0600 Subject: [PATCH 5/5] feat(web): Removed unused var --- .../web/src/app/[domain]/repos/components/reposTable.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/web/src/app/[domain]/repos/components/reposTable.tsx b/packages/web/src/app/[domain]/repos/components/reposTable.tsx index 8511a4af..0578b95e 100644 --- a/packages/web/src/app/[domain]/repos/components/reposTable.tsx +++ b/packages/web/src/app/[domain]/repos/components/reposTable.tsx @@ -79,7 +79,6 @@ interface ColumnsContext { currentSortBy?: string; currentSortOrder: string; onTriggerSync: (repoId: number) => void; - syncingRepoId: number | null; } export const getColumns = (context: ColumnsContext): ColumnDef[] => [ @@ -287,7 +286,6 @@ export const ReposTable = ({ const [rowSelection, setRowSelection] = useState({}) const [searchValue, setSearchValue] = useState(initialSearch) const [isPendingSearch, setIsPendingSearch] = useState(false) - const [syncingRepoId, setSyncingRepoId] = useState(null) const searchInputRef = useRef(null) const router = useRouter(); const searchParams = useSearchParams(); @@ -351,7 +349,6 @@ export const ReposTable = ({ }; const handleTriggerSync = async (repoId: number) => { - setSyncingRepoId(repoId); const response = await indexRepo(repoId); if (!isServiceError(response)) { @@ -365,8 +362,6 @@ export const ReposTable = ({ description: `❌ Failed to sync repository. ${response.message}`, }); } - - setSyncingRepoId(null); }; const totalPages = Math.ceil(totalCount / pageSize); @@ -375,8 +370,7 @@ export const ReposTable = ({ onSortChange: handleSortChange, currentSortBy: initialSortBy, currentSortOrder: initialSortOrder, - onTriggerSync: handleTriggerSync, - syncingRepoId: syncingRepoId, + onTriggerSync: handleTriggerSync }); const table = useReactTable({