Skip to content

Commit 1631ed3

Browse files
fix
1 parent 92d8abb commit 1631ed3

File tree

4 files changed

+423
-109
lines changed

4 files changed

+423
-109
lines changed

packages/db/tools/scripts/inject-repo-data.ts

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Script } from "../scriptRunner";
22
import { PrismaClient } from "../../dist";
33

4-
const NUM_REPOS = 100000;
4+
const NUM_REPOS = 1000;
5+
const NUM_INDEXING_JOBS_PER_REPO = 10000;
6+
const NUM_PERMISSION_JOBS_PER_REPO = 10000;
57

68
export const injectRepoData: Script = {
79
run: async (prisma: PrismaClient) => {
@@ -32,30 +34,60 @@ export const injectRepoData: Script = {
3234
});
3335

3436

35-
console.log(`Creating ${NUM_REPOS} repos...`);
37+
console.log(`Creating ${NUM_REPOS} repos...`);
3638

37-
for (let i = 0; i < NUM_REPOS; i++) {
38-
await prisma.repo.create({
39-
data: {
40-
name: `test-repo-${i}`,
41-
isFork: false,
42-
isArchived: false,
43-
metadata: {},
44-
cloneUrl: `https://github.com/test-org/test-repo-${i}`,
45-
webUrl: `https://github.com/test-org/test-repo-${i}`,
46-
orgId,
47-
external_id: `test-repo-${i}`,
48-
external_codeHostType: 'github',
49-
external_codeHostUrl: 'https://github.com',
50-
connections: {
51-
create: {
52-
connectionId: connection.id,
53-
}
39+
const statuses = ['PENDING', 'IN_PROGRESS', 'COMPLETED', 'FAILED'] as const;
40+
const indexingJobTypes = ['INDEX', 'CLEANUP'] as const;
41+
42+
for (let i = 0; i < NUM_REPOS; i++) {
43+
const repo = await prisma.repo.create({
44+
data: {
45+
name: `test-repo-${i}`,
46+
isFork: false,
47+
isArchived: false,
48+
metadata: {},
49+
cloneUrl: `https://github.com/test-org/test-repo-${i}`,
50+
webUrl: `https://github.com/test-org/test-repo-${i}`,
51+
orgId,
52+
external_id: `test-repo-${i}`,
53+
external_codeHostType: 'github',
54+
external_codeHostUrl: 'https://github.com',
55+
connections: {
56+
create: {
57+
connectionId: connection.id,
5458
}
5559
}
60+
}
61+
});
62+
63+
for (let j = 0; j < NUM_PERMISSION_JOBS_PER_REPO; j++) {
64+
const status = statuses[Math.floor(Math.random() * statuses.length)];
65+
await prisma.repoPermissionSyncJob.create({
66+
data: {
67+
repoId: repo.id,
68+
status,
69+
completedAt: status === 'COMPLETED' || status === 'FAILED' ? new Date() : null,
70+
errorMessage: status === 'FAILED' ? 'Mock error message' : null
71+
}
5672
});
5773
}
5874

59-
console.log(`Created ${NUM_REPOS} repos.`);
75+
for (let j = 0; j < NUM_INDEXING_JOBS_PER_REPO; j++) {
76+
const status = statuses[Math.floor(Math.random() * statuses.length)];
77+
const type = indexingJobTypes[Math.floor(Math.random() * indexingJobTypes.length)];
78+
await prisma.repoIndexingJob.create({
79+
data: {
80+
repoId: repo.id,
81+
type,
82+
status,
83+
completedAt: status === 'COMPLETED' || status === 'FAILED' ? new Date() : null,
84+
errorMessage: status === 'FAILED' ? 'Mock indexing error' : null,
85+
metadata: {}
86+
}
87+
});
88+
}
89+
}
90+
91+
console.log(`Created ${NUM_REPOS} repos with associated jobs.`);
6092
}
6193
};

packages/web/src/app/[domain]/repos/components/reposTable.tsx

Lines changed: 105 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,33 @@ import {
1010
DropdownMenuSeparator,
1111
DropdownMenuTrigger,
1212
} from "@/components/ui/dropdown-menu"
13-
import { Input } from "@/components/ui/input"
13+
import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group"
1414
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
1515
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
1616
import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants"
1717
import { cn, getCodeHostCommitUrl, getCodeHostIcon, getCodeHostInfoForRepo, getRepoImageSrc } from "@/lib/utils"
1818
import {
1919
type ColumnDef,
20-
type ColumnFiltersState,
2120
type SortingState,
2221
type VisibilityState,
2322
flexRender,
2423
getCoreRowModel,
25-
getFilteredRowModel,
26-
getPaginationRowModel,
2724
getSortedRowModel,
2825
useReactTable,
2926
} from "@tanstack/react-table"
3027
import { cva } from "class-variance-authority"
31-
import { ArrowUpDown, ExternalLink, MoreHorizontal, RefreshCwIcon } from "lucide-react"
28+
import { ArrowUpDown, ExternalLink, Loader2, MoreHorizontal, RefreshCwIcon } from "lucide-react"
3229
import Image from "next/image"
3330
import Link from "next/link"
34-
import { useMemo, useState } from "react"
31+
import { useEffect, useRef, useState } from "react"
3532
import { getBrowsePath } from "../../browse/hooks/utils"
36-
import { useRouter } from "next/navigation"
33+
import { useRouter, useSearchParams, usePathname } from "next/navigation"
3734
import { useToast } from "@/components/hooks/use-toast";
3835
import { DisplayDate } from "../../components/DisplayDate"
3936
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
4037
import { NotificationDot } from "../../components/notificationDot"
4138
import { CodeHostType } from "@sourcebot/db"
39+
import { useHotkeys } from "react-hotkeys-hook"
4240

4341
// @see: https://v0.app/chat/repo-indexing-status-uhjdDim8OUS
4442

@@ -271,46 +269,87 @@ export const columns: ColumnDef<Repo>[] = [
271269
},
272270
]
273271

274-
export const ReposTable = ({ data }: { data: Repo[] }) => {
272+
interface ReposTableProps {
273+
data: Repo[];
274+
currentPage: number;
275+
pageSize: number;
276+
totalCount: number;
277+
initialSearch: string;
278+
initialStatus: string;
279+
}
280+
281+
export const ReposTable = ({
282+
data,
283+
currentPage,
284+
pageSize,
285+
totalCount,
286+
initialSearch,
287+
initialStatus,
288+
}: ReposTableProps) => {
275289
const [sorting, setSorting] = useState<SortingState>([])
276-
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
277290
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
278291
const [rowSelection, setRowSelection] = useState({})
292+
const [searchValue, setSearchValue] = useState(initialSearch)
293+
const [isPendingSearch, setIsPendingSearch] = useState(false)
294+
const searchInputRef = useRef<HTMLInputElement>(null)
279295
const router = useRouter();
296+
const searchParams = useSearchParams();
297+
const pathname = usePathname();
280298
const { toast } = useToast();
281299

282-
const {
283-
numCompleted,
284-
numInProgress,
285-
numPending,
286-
numFailed,
287-
numNoJobs,
288-
} = useMemo(() => {
289-
return {
290-
numCompleted: data.filter((repo) => repo.latestJobStatus === "COMPLETED").length,
291-
numInProgress: data.filter((repo) => repo.latestJobStatus === "IN_PROGRESS").length,
292-
numPending: data.filter((repo) => repo.latestJobStatus === "PENDING").length,
293-
numFailed: data.filter((repo) => repo.latestJobStatus === "FAILED").length,
294-
numNoJobs: data.filter((repo) => repo.latestJobStatus === null).length,
300+
// Focus search box when '/' is pressed
301+
useHotkeys('/', (event) => {
302+
event.preventDefault();
303+
searchInputRef.current?.focus();
304+
});
305+
306+
// Debounced search effect - only runs when searchValue changes
307+
useEffect(() => {
308+
setIsPendingSearch(true);
309+
const timer = setTimeout(() => {
310+
const params = new URLSearchParams(searchParams.toString());
311+
if (searchValue) {
312+
params.set('search', searchValue);
313+
} else {
314+
params.delete('search');
315+
}
316+
params.set('page', '1'); // Reset to page 1 on search
317+
router.replace(`${pathname}?${params.toString()}`);
318+
setIsPendingSearch(false);
319+
}, 300);
320+
321+
return () => {
322+
clearTimeout(timer);
323+
setIsPendingSearch(false);
324+
};
325+
// eslint-disable-next-line react-hooks/exhaustive-deps
326+
}, [searchValue]);
327+
328+
const updateStatusFilter = (value: string) => {
329+
const params = new URLSearchParams(searchParams.toString());
330+
if (value === 'all') {
331+
params.delete('status');
332+
} else {
333+
params.set('status', value);
295334
}
296-
}, [data]);
335+
params.set('page', '1'); // Reset to page 1 on filter change
336+
router.replace(`${pathname}?${params.toString()}`);
337+
};
338+
339+
const totalPages = Math.ceil(totalCount / pageSize);
297340

298341
const table = useReactTable({
299342
data,
300343
columns,
301344
onSortingChange: setSorting,
302-
onColumnFiltersChange: setColumnFilters,
303345
getCoreRowModel: getCoreRowModel(),
304-
getPaginationRowModel: getPaginationRowModel(),
305346
getSortedRowModel: getSortedRowModel(),
306-
getFilteredRowModel: getFilteredRowModel(),
307347
onColumnVisibilityChange: setColumnVisibility,
308348
onRowSelectionChange: setRowSelection,
309349
columnResizeMode: 'onChange',
310350
enableColumnResizing: false,
311351
state: {
312352
sorting,
313-
columnFilters,
314353
columnVisibility,
315354
rowSelection,
316355
},
@@ -319,28 +358,34 @@ export const ReposTable = ({ data }: { data: Repo[] }) => {
319358
return (
320359
<div className="w-full">
321360
<div className="flex items-center gap-4 py-4">
322-
<Input
323-
placeholder="Filter repositories..."
324-
value={(table.getColumn("displayName")?.getFilterValue() as string) ?? ""}
325-
onChange={(event) => table.getColumn("displayName")?.setFilterValue(event.target.value)}
326-
className="max-w-sm"
327-
/>
361+
<InputGroup className="max-w-sm">
362+
<InputGroupInput
363+
ref={searchInputRef}
364+
placeholder="Filter repositories..."
365+
value={searchValue}
366+
onChange={(event) => setSearchValue(event.target.value)}
367+
className="ring-0"
368+
/>
369+
{isPendingSearch && (
370+
<InputGroupAddon align="inline-end">
371+
<Loader2 className="h-4 w-4 animate-spin" />
372+
</InputGroupAddon>
373+
)}
374+
</InputGroup>
328375
<Select
329-
value={(table.getColumn("latestJobStatus")?.getFilterValue() as string) ?? "all"}
330-
onValueChange={(value) => {
331-
table.getColumn("latestJobStatus")?.setFilterValue(value === "all" ? "" : value)
332-
}}
376+
value={initialStatus}
377+
onValueChange={updateStatusFilter}
333378
>
334379
<SelectTrigger className="w-[180px]">
335380
<SelectValue placeholder="Filter by status" />
336381
</SelectTrigger>
337382
<SelectContent>
338383
<SelectItem value="all">Filter by status</SelectItem>
339-
<SelectItem value="COMPLETED">Completed ({numCompleted})</SelectItem>
340-
<SelectItem value="IN_PROGRESS">In progress ({numInProgress})</SelectItem>
341-
<SelectItem value="PENDING">Pending ({numPending})</SelectItem>
342-
<SelectItem value="FAILED">Failed ({numFailed})</SelectItem>
343-
<SelectItem value="null">No status ({numNoJobs})</SelectItem>
384+
<SelectItem value="COMPLETED">Completed</SelectItem>
385+
<SelectItem value="IN_PROGRESS">In progress</SelectItem>
386+
<SelectItem value="PENDING">Pending</SelectItem>
387+
<SelectItem value="FAILED">Failed</SelectItem>
388+
<SelectItem value="null">No status</SelectItem>
344389
</SelectContent>
345390
</Select>
346391
<Button
@@ -401,18 +446,32 @@ export const ReposTable = ({ data }: { data: Repo[] }) => {
401446
</div>
402447
<div className="flex items-center justify-end space-x-2 py-4">
403448
<div className="flex-1 text-sm text-muted-foreground">
404-
{table.getFilteredRowModel().rows.length} {data.length > 1 ? 'repositories' : 'repository'} total
449+
{totalCount} {totalCount !== 1 ? 'repositories' : 'repository'} total
450+
{totalPages > 1 && ` • Page ${currentPage} of ${totalPages}`}
405451
</div>
406452
<div className="space-x-2">
407453
<Button
408454
variant="outline"
409455
size="sm"
410-
onClick={() => table.previousPage()}
411-
disabled={!table.getCanPreviousPage()}
456+
onClick={() => {
457+
const params = new URLSearchParams(searchParams.toString());
458+
params.set('page', String(currentPage - 1));
459+
router.push(`${pathname}?${params.toString()}`);
460+
}}
461+
disabled={currentPage <= 1}
412462
>
413463
Previous
414464
</Button>
415-
<Button variant="outline" size="sm" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>
465+
<Button
466+
variant="outline"
467+
size="sm"
468+
onClick={() => {
469+
const params = new URLSearchParams(searchParams.toString());
470+
params.set('page', String(currentPage + 1));
471+
router.push(`${pathname}?${params.toString()}`);
472+
}}
473+
disabled={currentPage >= totalPages}
474+
>
416475
Next
417476
</Button>
418477
</div>

0 commit comments

Comments
 (0)