Skip to content

Commit c1467bc

Browse files
Various improvements and optimizations on the web side
1 parent 5fe554e commit c1467bc

39 files changed

Lines changed: 1196 additions & 1554 deletions

packages/web/src/actions.ts

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { prisma } from "@/prisma";
1010
import { render } from "@react-email/components";
1111
import * as Sentry from '@sentry/nextjs';
1212
import { decrypt, encrypt, generateApiKey, getTokenFromConfig, hashSecret } from "@sourcebot/crypto";
13-
import { ApiKey, ConnectionSyncStatus, Org, OrgRole, Prisma, RepoIndexingStatus, StripeSubscriptionStatus } from "@sourcebot/db";
13+
import { ApiKey, ConnectionSyncStatus, Org, OrgRole, Prisma, RepoIndexingStatus, RepoJobStatus, RepoJobType, StripeSubscriptionStatus } from "@sourcebot/db";
1414
import { createLogger } from "@sourcebot/logger";
1515
import { azuredevopsSchema } from "@sourcebot/schemas/v3/azuredevops.schema";
1616
import { bitbucketSchema } from "@sourcebot/schemas/v3/bitbucket.schema";
@@ -638,22 +638,20 @@ export const getConnectionInfo = async (connectionId: number, domain: string) =>
638638
}
639639
})));
640640

641-
export const getRepos = async (filter: { status?: RepoIndexingStatus[], connectionId?: number } = {}) => sew(() =>
641+
export const getRepos = async ({
642+
where,
643+
take,
644+
}: {
645+
where?: Prisma.RepoWhereInput,
646+
take?: number
647+
} = {}) => sew(() =>
642648
withOptionalAuthV2(async ({ org, prisma }) => {
643649
const repos = await prisma.repo.findMany({
644650
where: {
645651
orgId: org.id,
646-
...(filter.status ? {
647-
repoIndexingStatus: { in: filter.status }
648-
} : {}),
649-
...(filter.connectionId ? {
650-
connections: {
651-
some: {
652-
connectionId: filter.connectionId
653-
}
654-
}
655-
} : {}),
656-
}
652+
...where,
653+
},
654+
take,
657655
});
658656

659657
return repos.map((repo) => repositoryQuerySchema.parse({
@@ -669,6 +667,60 @@ export const getRepos = async (filter: { status?: RepoIndexingStatus[], connecti
669667
}))
670668
}));
671669

670+
/**
671+
* Returns a set of aggregated stats about the repos in the org
672+
*/
673+
export const getReposStats = async () => sew(() =>
674+
withOptionalAuthV2(async ({ org, prisma }) => {
675+
const [
676+
// Total number of repos.
677+
numberOfRepos,
678+
// Number of repos with their first time indexing jobs either
679+
// pending or in progress.
680+
numberOfReposWithFirstTimeIndexingJobsInProgress,
681+
// Number of repos that have been indexed at least once.
682+
numberOfReposWithIndex,
683+
] = await Promise.all([
684+
prisma.repo.count({
685+
where: {
686+
orgId: org.id,
687+
}
688+
}),
689+
prisma.repo.count({
690+
where: {
691+
orgId: org.id,
692+
jobs: {
693+
some: {
694+
type: RepoJobType.INDEX,
695+
status: {
696+
in: [
697+
RepoJobStatus.PENDING,
698+
RepoJobStatus.IN_PROGRESS,
699+
]
700+
}
701+
},
702+
},
703+
indexedAt: null,
704+
}
705+
}),
706+
prisma.repo.count({
707+
where: {
708+
orgId: org.id,
709+
NOT: {
710+
indexedAt: null,
711+
}
712+
}
713+
})
714+
]);
715+
716+
return {
717+
numberOfRepos,
718+
numberOfReposWithFirstTimeIndexingJobsInProgress,
719+
numberOfReposWithIndex,
720+
};
721+
})
722+
)
723+
672724
export const getRepoInfoByName = async (repoName: string) => sew(() =>
673725
withOptionalAuthV2(async ({ org, prisma }) => {
674726
// @note: repo names are represented by their remote url

packages/web/src/app/[domain]/browse/[...path]/components/pureTreePreviewPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useRef } from "react";
44
import { FileTreeItem } from "@/features/fileTree/actions";
55
import { FileTreeItemComponent } from "@/features/fileTree/components/fileTreeItemComponent";
6-
import { getBrowsePath } from "../../hooks/useBrowseNavigation";
6+
import { getBrowsePath } from "../../hooks/utils";
77
import { ScrollArea } from "@/components/ui/scroll-area";
88
import { useBrowseParams } from "../../hooks/useBrowseParams";
99
import { useDomain } from "@/hooks/useDomain";

packages/web/src/app/[domain]/browse/hooks/useBrowseNavigation.ts

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import { useRouter } from "next/navigation";
44
import { useDomain } from "@/hooks/useDomain";
55
import { useCallback } from "react";
6-
import { BrowseState, SET_BROWSE_STATE_QUERY_PARAM } from "../browseStateProvider";
6+
import { BrowseState } from "../browseStateProvider";
7+
import { getBrowsePath } from "./utils";
78

89
export type BrowseHighlightRange = {
910
start: { lineNumber: number; column: number; };
@@ -25,37 +26,6 @@ export interface GetBrowsePathProps {
2526
domain: string;
2627
}
2728

28-
export const getBrowsePath = ({
29-
repoName,
30-
revisionName = 'HEAD',
31-
path,
32-
pathType,
33-
highlightRange,
34-
setBrowseState,
35-
domain,
36-
}: GetBrowsePathProps) => {
37-
const params = new URLSearchParams();
38-
39-
if (highlightRange) {
40-
const { start, end } = highlightRange;
41-
42-
if ('column' in start && 'column' in end) {
43-
params.set(HIGHLIGHT_RANGE_QUERY_PARAM, `${start.lineNumber}:${start.column},${end.lineNumber}:${end.column}`);
44-
} else {
45-
params.set(HIGHLIGHT_RANGE_QUERY_PARAM, `${start.lineNumber},${end.lineNumber}`);
46-
}
47-
}
48-
49-
if (setBrowseState) {
50-
params.set(SET_BROWSE_STATE_QUERY_PARAM, JSON.stringify(setBrowseState));
51-
}
52-
53-
const encodedPath = encodeURIComponent(path);
54-
const browsePath = `/${domain}/browse/${repoName}@${revisionName}/-/${pathType}/${encodedPath}${params.size > 0 ? `?${params.toString()}` : ''}`;
55-
return browsePath;
56-
}
57-
58-
5929
export const useBrowseNavigation = () => {
6030
const router = useRouter();
6131
const domain = useDomain();

packages/web/src/app/[domain]/browse/hooks/useBrowsePath.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use client';
22

33
import { useMemo } from "react";
4-
import { getBrowsePath, GetBrowsePathProps } from "./useBrowseNavigation";
4+
import { GetBrowsePathProps } from "./useBrowseNavigation";
5+
import { getBrowsePath } from "./utils";
56
import { useDomain } from "@/hooks/useDomain";
67

78
export const useBrowsePath = ({

packages/web/src/app/[domain]/browse/hooks/utils.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { SET_BROWSE_STATE_QUERY_PARAM } from "../browseStateProvider";
2+
import { GetBrowsePathProps, HIGHLIGHT_RANGE_QUERY_PARAM } from "./useBrowseNavigation";
13

24
export const getBrowseParamsFromPathParam = (pathParam: string) => {
35
const sentinelIndex = pathParam.search(/\/-\/(tree|blob)/);
@@ -7,7 +9,7 @@ export const getBrowseParamsFromPathParam = (pathParam: string) => {
79

810
const repoAndRevisionPart = decodeURIComponent(pathParam.substring(0, sentinelIndex));
911
const lastAtIndex = repoAndRevisionPart.lastIndexOf('@');
10-
12+
1113
const repoName = lastAtIndex === -1 ? repoAndRevisionPart : repoAndRevisionPart.substring(0, lastAtIndex);
1214
const revisionName = lastAtIndex === -1 ? undefined : repoAndRevisionPart.substring(lastAtIndex + 1);
1315

@@ -40,4 +42,28 @@ export const getBrowseParamsFromPathParam = (pathParam: string) => {
4042
path,
4143
pathType,
4244
}
43-
}
45+
};
46+
47+
export const getBrowsePath = ({
48+
repoName, revisionName = 'HEAD', path, pathType, highlightRange, setBrowseState, domain,
49+
}: GetBrowsePathProps) => {
50+
const params = new URLSearchParams();
51+
52+
if (highlightRange) {
53+
const { start, end } = highlightRange;
54+
55+
if ('column' in start && 'column' in end) {
56+
params.set(HIGHLIGHT_RANGE_QUERY_PARAM, `${start.lineNumber}:${start.column},${end.lineNumber}:${end.column}`);
57+
} else {
58+
params.set(HIGHLIGHT_RANGE_QUERY_PARAM, `${start.lineNumber},${end.lineNumber}`);
59+
}
60+
}
61+
62+
if (setBrowseState) {
63+
params.set(SET_BROWSE_STATE_QUERY_PARAM, JSON.stringify(setBrowseState));
64+
}
65+
66+
const encodedPath = encodeURIComponent(path);
67+
const browsePath = `/${domain}/browse/${repoName}@${revisionName}/-/${pathType}/${encodedPath}${params.size > 0 ? `?${params.toString()}` : ''}`;
68+
return browsePath;
69+
};

packages/web/src/app/[domain]/components/homepage/askSourcebotDemoCards.tsx renamed to packages/web/src/app/[domain]/chat/components/demoCards.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ import { cn, getCodeHostIcon } from "@/lib/utils";
1111
import useCaptureEvent from "@/hooks/useCaptureEvent";
1212
import { SearchScopeInfoCard } from "@/features/chat/components/chatBox/searchScopeInfoCard";
1313

14-
interface AskSourcebotDemoCardsProps {
14+
interface DemoCards {
1515
demoExamples: DemoExamples;
1616
}
1717

18-
export const AskSourcebotDemoCards = ({
18+
export const DemoCards = ({
1919
demoExamples,
20-
}: AskSourcebotDemoCardsProps) => {
20+
}: DemoCards) => {
2121
const captureEvent = useCaptureEvent();
2222
const [selectedFilterSearchScope, setSelectedFilterSearchScope] = useState<number | null>(null);
2323

packages/web/src/app/[domain]/components/homepage/agenticSearch.tsx renamed to packages/web/src/app/[domain]/chat/components/landingPageChatBox.tsx

Lines changed: 6 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,24 @@ import { ChatBoxToolbar } from "@/features/chat/components/chatBox/chatBoxToolba
66
import { LanguageModelInfo, SearchScope } from "@/features/chat/types";
77
import { useCreateNewChatThread } from "@/features/chat/useCreateNewChatThread";
88
import { RepositoryQuery, SearchContextQuery } from "@/lib/types";
9-
import { useCallback, useState } from "react";
10-
import { SearchModeSelector, SearchModeSelectorProps } from "./toolbar";
9+
import { useState } from "react";
1110
import { useLocalStorage } from "usehooks-ts";
12-
import { DemoExamples } from "@/types";
13-
import { AskSourcebotDemoCards } from "./askSourcebotDemoCards";
14-
import { AgenticSearchTutorialDialog } from "./agenticSearchTutorialDialog";
15-
import { setAgenticSearchTutorialDismissedCookie } from "@/actions";
16-
import { RepositorySnapshot } from "./repositorySnapshot";
11+
import { SearchModeSelector } from "../../components/searchModeSelector";
1712

18-
interface AgenticSearchProps {
19-
searchModeSelectorProps: SearchModeSelectorProps;
13+
interface LandingPageChatBox {
2014
languageModels: LanguageModelInfo[];
2115
repos: RepositoryQuery[];
2216
searchContexts: SearchContextQuery[];
23-
chatHistory: {
24-
id: string;
25-
createdAt: Date;
26-
name: string | null;
27-
}[];
28-
demoExamples: DemoExamples | undefined;
29-
isTutorialDismissed: boolean;
3017
}
3118

32-
export const AgenticSearch = ({
33-
searchModeSelectorProps,
19+
export const LandingPageChatBox = ({
3420
languageModels,
3521
repos,
3622
searchContexts,
37-
demoExamples,
38-
isTutorialDismissed,
39-
}: AgenticSearchProps) => {
23+
}: LandingPageChatBox) => {
4024
const { createNewChatThread, isLoading } = useCreateNewChatThread();
4125
const [selectedSearchScopes, setSelectedSearchScopes] = useLocalStorage<SearchScope[]>("selectedSearchScopes", [], { initializeWithValue: false });
4226
const [isContextSelectorOpen, setIsContextSelectorOpen] = useState(false);
43-
44-
const [isTutorialOpen, setIsTutorialOpen] = useState(!isTutorialDismissed);
45-
const onTutorialDismissed = useCallback(() => {
46-
setIsTutorialOpen(false);
47-
setAgenticSearchTutorialDismissedCookie(true);
48-
}, []);
49-
5027
return (
5128
<div className="flex flex-col items-center w-full">
5229
<div className="mt-4 w-full border rounded-md shadow-sm max-w-[800px]">
@@ -74,34 +51,12 @@ export const AgenticSearch = ({
7451
onContextSelectorOpenChanged={setIsContextSelectorOpen}
7552
/>
7653
<SearchModeSelector
77-
{...searchModeSelectorProps}
54+
searchMode="agentic"
7855
className="ml-auto"
7956
/>
8057
</div>
8158
</div>
8259
</div>
83-
84-
<div className="mt-8">
85-
<RepositorySnapshot
86-
repos={repos}
87-
/>
88-
</div>
89-
90-
<div className="flex flex-col items-center w-fit gap-6">
91-
<Separator className="mt-5 w-[700px]" />
92-
</div>
93-
94-
{demoExamples && (
95-
<AskSourcebotDemoCards
96-
demoExamples={demoExamples}
97-
/>
98-
)}
99-
100-
{isTutorialOpen && (
101-
<AgenticSearchTutorialDialog
102-
onClose={onTutorialDismissed}
103-
/>
104-
)}
10560
</div >
10661
)
10762
}

packages/web/src/app/[domain]/chat/components/newChatPanel.tsx

Lines changed: 0 additions & 72 deletions
This file was deleted.

0 commit comments

Comments
 (0)