more cleanup

This commit is contained in:
bkellam 2025-10-23 12:49:57 -07:00
parent 49578b1074
commit e5bb570650
6 changed files with 102 additions and 31 deletions

View file

@ -2,7 +2,7 @@
import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint"; import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint";
import { useDomain } from "@/hooks/useDomain"; import { useDomain } from "@/hooks/useDomain";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItemNoItemText, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -87,7 +87,7 @@ export const SearchModeSelector = ({
onMouseEnter={() => setFocusedSearchMode("precise")} onMouseEnter={() => setFocusedSearchMode("precise")}
onFocus={() => setFocusedSearchMode("precise")} onFocus={() => setFocusedSearchMode("precise")}
> >
<SelectItem <SelectItemNoItemText
value="precise" value="precise"
className="cursor-pointer" className="cursor-pointer"
> >
@ -99,7 +99,7 @@ export const SearchModeSelector = ({
</div> </div>
</div> </div>
</SelectItem> </SelectItemNoItemText>
<TooltipContent <TooltipContent
side="right" side="right"
className="w-64 z-50" className="w-64 z-50"
@ -126,7 +126,7 @@ export const SearchModeSelector = ({
onMouseEnter={() => setFocusedSearchMode("agentic")} onMouseEnter={() => setFocusedSearchMode("agentic")}
onFocus={() => setFocusedSearchMode("agentic")} onFocus={() => setFocusedSearchMode("agentic")}
> >
<SelectItem <SelectItemNoItemText
value="agentic" value="agentic"
className="cursor-pointer" className="cursor-pointer"
> >
@ -138,7 +138,7 @@ export const SearchModeSelector = ({
<KeyboardShortcutHint shortcut="⌘ I" /> <KeyboardShortcutHint shortcut="⌘ I" />
</div> </div>
</div> </div>
</SelectItem> </SelectItemNoItemText>
</div> </div>
</TooltipTrigger> </TooltipTrigger>
@ -167,5 +167,3 @@ export const SearchModeSelector = ({
</div> </div>
) )
} }

View file

@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Skeleton } from "@/components/ui/skeleton" import { Skeleton } from "@/components/ui/skeleton"
import { RepoJobsTable } from "../components/repo-jobs-table" import { RepoJobsTable } from "../components/repoJobsTable"
import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants" import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants"
import { sew } from "@/actions" import { sew } from "@/actions"
import { withOptionalAuthV2 } from "@/withAuthV2" import { withOptionalAuthV2 } from "@/withAuthV2"

View file

@ -21,6 +21,9 @@ import { cva } from "class-variance-authority"
import { AlertCircle, ArrowUpDown } from "lucide-react" import { AlertCircle, ArrowUpDown } from "lucide-react"
import * as React from "react" import * as React from "react"
import { CopyIconButton } from "../../components/copyIconButton" import { CopyIconButton } from "../../components/copyIconButton"
import { useMemo } from "react"
// @see: https://v0.app/chat/repo-indexing-status-uhjdDim8OUS
export type RepoIndexingJob = { export type RepoIndexingJob = {
id: string id: string
@ -189,6 +192,20 @@ export const RepoJobsTable = ({ data }: { data: RepoIndexingJob[] }) => {
}, },
}) })
const {
numCompleted,
numInProgress,
numPending,
numFailed,
} = useMemo(() => {
return {
numCompleted: data.filter((job) => job.status === "COMPLETED").length,
numInProgress: data.filter((job) => job.status === "IN_PROGRESS").length,
numPending: data.filter((job) => job.status === "PENDING").length,
numFailed: data.filter((job) => job.status === "FAILED").length,
};
}, [data]);
return ( return (
<div className="w-full"> <div className="w-full">
<div className="flex items-center gap-4 py-4"> <div className="flex items-center gap-4 py-4">
@ -200,11 +217,11 @@ export const RepoJobsTable = ({ data }: { data: RepoIndexingJob[] }) => {
<SelectValue placeholder="Filter by status" /> <SelectValue placeholder="Filter by status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="all">All statuses</SelectItem> <SelectItem value="all">Filter by status</SelectItem>
<SelectItem value="PENDING">Pending</SelectItem> <SelectItem value="COMPLETED">Completed ({numCompleted})</SelectItem>
<SelectItem value="IN_PROGRESS">In Progress</SelectItem> <SelectItem value="IN_PROGRESS">In progress ({numInProgress})</SelectItem>
<SelectItem value="COMPLETED">Completed</SelectItem> <SelectItem value="PENDING">Pending ({numPending})</SelectItem>
<SelectItem value="FAILED">Failed</SelectItem> <SelectItem value="FAILED">Failed ({numFailed})</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>

View file

@ -14,7 +14,7 @@ import { Input } from "@/components/ui/input"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants" import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants"
import { getRepoImageSrc } from "@/lib/utils" import { getCodeHostInfoForRepo, getRepoImageSrc } from "@/lib/utils"
import { import {
type ColumnDef, type ColumnDef,
type ColumnFiltersState, type ColumnFiltersState,
@ -31,7 +31,10 @@ import { cva } from "class-variance-authority"
import { ArrowUpDown, ExternalLink, MoreHorizontal } from "lucide-react" import { ArrowUpDown, ExternalLink, MoreHorizontal } from "lucide-react"
import Image from "next/image" import Image from "next/image"
import Link from "next/link" import Link from "next/link"
import * as React from "react" import { useMemo, useState } from "react"
import { getBrowsePath } from "../../browse/hooks/utils"
// @see: https://v0.app/chat/repo-indexing-status-uhjdDim8OUS
export type Repo = { export type Repo = {
id: number id: number
@ -42,6 +45,7 @@ export type Repo = {
indexedAt: Date | null indexedAt: Date | null
createdAt: Date createdAt: Date
webUrl: string | null webUrl: string | null
codeHostType: string
imageUrl: string | null imageUrl: string | null
latestJobStatus: "PENDING" | "IN_PROGRESS" | "COMPLETED" | "FAILED" | null latestJobStatus: "PENDING" | "IN_PROGRESS" | "COMPLETED" | "FAILED" | null
} }
@ -112,7 +116,12 @@ export const columns: ColumnDef<Repo>[] = [
{repo.displayName?.charAt(0) ?? repo.name.charAt(0)} {repo.displayName?.charAt(0) ?? repo.name.charAt(0)}
</div> </div>
)} )}
<Link href={`/repos/${repo.id}`} className="font-medium hover:underline"> <Link href={getBrowsePath({
repoName: repo.name,
path: '/',
pathType: 'tree',
domain: SINGLE_TENANT_ORG_DOMAIN,
})} className="font-medium hover:underline">
{repo.displayName || repo.name} {repo.displayName || repo.name}
</Link> </Link>
</div> </div>
@ -141,6 +150,12 @@ export const columns: ColumnDef<Repo>[] = [
enableHiding: false, enableHiding: false,
cell: ({ row }) => { cell: ({ row }) => {
const repo = row.original const repo = row.original
const codeHostInfo = getCodeHostInfoForRepo({
codeHostType: repo.codeHostType,
name: repo.name,
displayName: repo.displayName ?? undefined,
webUrl: repo.webUrl ?? undefined,
});
return ( return (
<DropdownMenu> <DropdownMenu>
@ -155,12 +170,12 @@ export const columns: ColumnDef<Repo>[] = [
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link href={`/${SINGLE_TENANT_ORG_DOMAIN}/repos/${repo.id}`}>View details</Link> <Link href={`/${SINGLE_TENANT_ORG_DOMAIN}/repos/${repo.id}`}>View details</Link>
</DropdownMenuItem> </DropdownMenuItem>
{repo.webUrl && ( {(repo.webUrl && codeHostInfo) && (
<> <>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<a href={repo.webUrl} target="_blank" rel="noopener noreferrer" className="flex items-center"> <a href={repo.webUrl} target="_blank" rel="noopener noreferrer" className="flex items-center">
Open in GitHub Open in {codeHostInfo.codeHostName}
<ExternalLink className="ml-2 h-3 w-3" /> <ExternalLink className="ml-2 h-3 w-3" />
</a> </a>
</DropdownMenuItem> </DropdownMenuItem>
@ -174,10 +189,26 @@ export const columns: ColumnDef<Repo>[] = [
] ]
export const ReposTable = ({ data }: { data: Repo[] }) => { export const ReposTable = ({ data }: { data: Repo[] }) => {
const [sorting, setSorting] = React.useState<SortingState>([]) const [sorting, setSorting] = useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]) const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({}) const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
const [rowSelection, setRowSelection] = React.useState({}) const [rowSelection, setRowSelection] = useState({})
const {
numCompleted,
numInProgress,
numPending,
numFailed,
numNoJobs,
} = useMemo(() => {
return {
numCompleted: data.filter((repo) => repo.latestJobStatus === "COMPLETED").length,
numInProgress: data.filter((repo) => repo.latestJobStatus === "IN_PROGRESS").length,
numPending: data.filter((repo) => repo.latestJobStatus === "PENDING").length,
numFailed: data.filter((repo) => repo.latestJobStatus === "FAILED").length,
numNoJobs: data.filter((repo) => repo.latestJobStatus === null).length,
}
}, [data]);
const table = useReactTable({ const table = useReactTable({
data, data,
@ -217,12 +248,12 @@ export const ReposTable = ({ data }: { data: Repo[] }) => {
<SelectValue placeholder="Filter by status" /> <SelectValue placeholder="Filter by status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="all">All Statuses</SelectItem> <SelectItem value="all">Filter by status</SelectItem>
<SelectItem value="COMPLETED">Completed</SelectItem> <SelectItem value="COMPLETED">Completed ({numCompleted})</SelectItem>
<SelectItem value="IN_PROGRESS">In Progress</SelectItem> <SelectItem value="IN_PROGRESS">In progress ({numInProgress})</SelectItem>
<SelectItem value="PENDING">Pending</SelectItem> <SelectItem value="PENDING">Pending ({numPending})</SelectItem>
<SelectItem value="FAILED">Failed</SelectItem> <SelectItem value="FAILED">Failed ({numFailed})</SelectItem>
<SelectItem value="null">No Jobs</SelectItem> <SelectItem value="null">No status ({numNoJobs})</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View file

@ -2,7 +2,7 @@ import { sew } from "@/actions";
import { ServiceErrorException } from "@/lib/serviceError"; import { ServiceErrorException } from "@/lib/serviceError";
import { isServiceError } from "@/lib/utils"; import { isServiceError } from "@/lib/utils";
import { withOptionalAuthV2 } from "@/withAuthV2"; import { withOptionalAuthV2 } from "@/withAuthV2";
import { ReposTable } from "./components/repos-table"; import { ReposTable } from "./components/reposTable";
export default async function ReposPage() { export default async function ReposPage() {
@ -27,7 +27,8 @@ export default async function ReposPage() {
createdAt: repo.createdAt, createdAt: repo.createdAt,
webUrl: repo.webUrl, webUrl: repo.webUrl,
imageUrl: repo.imageUrl, imageUrl: repo.imageUrl,
latestJobStatus: repo.jobs.length > 0 ? repo.jobs[0].status : null latestJobStatus: repo.jobs.length > 0 ? repo.jobs[0].status : null,
codeHostType: repo.external_codeHostType,
}))} /> }))} />
</div> </div>
) )

View file

@ -129,11 +129,34 @@ const SelectItem = React.forwardRef<
</SelectPrimitive.ItemIndicator> </SelectPrimitive.ItemIndicator>
</span> </span>
{children} <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item> </SelectPrimitive.Item>
)) ))
SelectItem.displayName = SelectPrimitive.Item.displayName SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectItemNoItemText = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
{children}
</SelectPrimitive.Item>
))
SelectItemNoItemText.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef< const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>, React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
@ -154,6 +177,7 @@ export {
SelectContent, SelectContent,
SelectLabel, SelectLabel,
SelectItem, SelectItem,
SelectItemNoItemText,
SelectSeparator, SelectSeparator,
SelectScrollUpButton, SelectScrollUpButton,
SelectScrollDownButton, SelectScrollDownButton,