Skip to content

Commit 03858c5

Browse files
Add unwrapServiceError helper
1 parent a8a17fa commit 03858c5

File tree

10 files changed

+134
-164
lines changed

10 files changed

+134
-164
lines changed

packages/web/src/app/[domain]/components/connectionCreationForms/secretCombobox.tsx

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
CommandList,
1111
} from "@/components/ui/command"
1212
import { Button } from "@/components/ui/button";
13-
import { cn, isServiceError } from "@/lib/utils";
13+
import { cn, isServiceError, unwrapServiceError } from "@/lib/utils";
1414
import { ChevronsUpDown, Check, PlusCircleIcon, Loader2, Eye, EyeOff, TriangleAlert } from "lucide-react";
1515
import { useCallback, useMemo, useState } from "react";
1616
import { Separator } from "@/components/ui/separator";
@@ -49,26 +49,16 @@ export const SecretCombobox = ({
4949
const [isCreateSecretDialogOpen, setIsCreateSecretDialogOpen] = useState(false);
5050
const captureEvent = useCaptureEvent();
5151

52-
const { data: secrets, isLoading, refetch } = useQuery({
52+
const { data: secrets, isPending, isError, refetch } = useQuery({
5353
queryKey: ["secrets"],
54-
queryFn: () => getSecrets(domain),
54+
queryFn: () => unwrapServiceError(getSecrets(domain)),
5555
});
5656

5757
const onSecretCreated = useCallback((key: string) => {
5858
onSecretChange(key);
5959
refetch();
6060
}, [onSecretChange, refetch]);
6161

62-
const isSecretNotFoundWarningVisible = useMemo(() => {
63-
if (!isDefined(secretKey)) {
64-
return false;
65-
}
66-
if (isServiceError(secrets)) {
67-
return false;
68-
}
69-
return !secrets?.some(({ key }) => key === secretKey);
70-
}, [secretKey, secrets]);
71-
7262
return (
7363
<>
7464
<Popover>
@@ -83,7 +73,7 @@ export const SecretCombobox = ({
8373
)}
8474
disabled={isDisabled}
8575
>
86-
{isSecretNotFoundWarningVisible && (
76+
{!(isPending || isError) && isDefined(secretKey) && !secrets.some(({ key }) => key === secretKey) && (
8777
<TooltipProvider>
8878

8979
<Tooltip
@@ -105,12 +95,13 @@ export const SecretCombobox = ({
10595
</Button>
10696
</PopoverTrigger>
10797
<PopoverContent className="p-0.5">
108-
{isLoading && (
98+
{isPending ? (
10999
<div className="flex items-center justify-center p-8">
110100
<Loader2 className="h-4 w-4 animate-spin" />
111101
</div>
112-
)}
113-
{secrets && !isServiceError(secrets) && secrets.length > 0 && (
102+
) : isError ? (
103+
<p className="p-2 text-sm text-destructive">Failed to load secrets</p>
104+
) : secrets.length > 0 && (
114105
<>
115106
<Command className="mb-2">
116107
<CommandInput

packages/web/src/app/[domain]/components/errorNavIndicator.tsx

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Link from "next/link";
44
import { HoverCard, HoverCardTrigger, HoverCardContent } from "@/components/ui/hover-card";
55
import { CircleXIcon } from "lucide-react";
66
import { useDomain } from "@/hooks/useDomain";
7-
import { isServiceError } from "@/lib/utils";
7+
import { unwrapServiceError } from "@/lib/utils";
88
import useCaptureEvent from "@/hooks/useCaptureEvent";
99
import { NEXT_PUBLIC_POLLING_INTERVAL_MS } from "@/lib/environment.client";
1010
import { useQuery } from "@tanstack/react-query";
@@ -16,49 +16,43 @@ export const ErrorNavIndicator = () => {
1616
const domain = useDomain();
1717
const captureEvent = useCaptureEvent();
1818

19-
const { data: failedRepos } = useQuery({
19+
const { data: repos, isPending: isPendingRepos, isError: isErrorRepos } = useQuery({
2020
queryKey: ['repos', domain],
21-
queryFn: () => getRepos(domain),
22-
select: (data) => {
23-
if (isServiceError(data)) {
24-
return data;
25-
}
26-
return data.filter(repo => repo.repoIndexingStatus === RepoIndexingStatus.FAILED);
27-
},
21+
queryFn: () => unwrapServiceError(getRepos(domain)),
22+
select: (data) => data.filter(repo => repo.repoIndexingStatus === RepoIndexingStatus.FAILED),
2823
refetchInterval: NEXT_PUBLIC_POLLING_INTERVAL_MS,
29-
initialData: [],
3024
});
3125

32-
const { data: failedConnections } = useQuery({
26+
const { data: connections, isPending: isPendingConnections, isError: isErrorConnections } = useQuery({
3327
queryKey: ['connections', domain],
34-
queryFn: () => getConnections(domain),
35-
select: (data) => {
36-
if (isServiceError(data)) {
37-
return data;
38-
}
39-
return data.filter(connection => connection.syncStatus === ConnectionSyncStatus.FAILED)
40-
},
28+
queryFn: () => unwrapServiceError(getConnections(domain)),
29+
select: (data) => data.filter(connection => connection.syncStatus === ConnectionSyncStatus.FAILED),
4130
refetchInterval: NEXT_PUBLIC_POLLING_INTERVAL_MS,
42-
initialData: [],
4331
});
4432

45-
if (isServiceError(failedRepos) || isServiceError(failedConnections) || (failedRepos.length === 0 && failedConnections.length === 0)) return null;
33+
if (isPendingRepos || isErrorRepos || isPendingConnections || isErrorConnections) {
34+
return null;
35+
}
36+
37+
if (repos.length === 0 && connections.length === 0) {
38+
return null;
39+
}
4640

4741
return (
4842
<HoverCard openDelay={50}>
4943
<HoverCardTrigger asChild onMouseEnter={() => captureEvent('wa_error_nav_hover', {})}>
5044
<Link href={`/${domain}/connections`} onClick={() => captureEvent('wa_error_nav_pressed', {})}>
5145
<div className="flex items-center gap-2 px-3 py-1.5 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-700 rounded-full text-red-700 dark:text-red-400 text-xs font-medium hover:bg-red-100 dark:hover:bg-red-900/30 transition-colors cursor-pointer">
5246
<CircleXIcon className="h-4 w-4" />
53-
{failedRepos.length + failedConnections.length > 0 && (
54-
<span>{failedRepos.length + failedConnections.length}</span>
47+
{repos.length + connections.length > 0 && (
48+
<span>{repos.length + connections.length}</span>
5549
)}
5650
</div>
5751
</Link>
5852
</HoverCardTrigger>
5953
<HoverCardContent className="w-80 border border-red-200 dark:border-red-800 rounded-lg">
6054
<div className="flex flex-col gap-6 p-5">
61-
{failedConnections.length > 0 && (
55+
{connections.length > 0 && (
6256
<div className="flex flex-col gap-4 pb-6">
6357
<div className="flex items-center gap-2">
6458
<div className="h-2 w-2 rounded-full bg-red-500"></div>
@@ -68,7 +62,7 @@ export const ErrorNavIndicator = () => {
6862
The following connections have failed to sync:
6963
</p>
7064
<div className="flex flex-col gap-2">
71-
{failedConnections
65+
{connections
7266
.slice(0, 10)
7367
.map(connection => (
7468
<Link key={connection.name} href={`/${domain}/connections/${connection.id}`} onClick={() => captureEvent('wa_error_nav_job_pressed', {})}>
@@ -80,16 +74,16 @@ export const ErrorNavIndicator = () => {
8074
</div>
8175
</Link>
8276
))}
83-
{failedConnections.length > 10 && (
77+
{connections.length > 10 && (
8478
<div className="text-sm text-red-600/90 dark:text-red-300/90 pl-3 pt-1">
85-
And {failedConnections.length - 10} more...
79+
And {connections.length - 10} more...
8680
</div>
8781
)}
8882
</div>
8983
</div>
9084
)}
9185

92-
{failedRepos.length > 0 && (
86+
{repos.length > 0 && (
9387
<div className="flex flex-col gap-4">
9488
<div className="flex items-center gap-2">
9589
<div className="h-2 w-2 rounded-full bg-red-500"></div>
@@ -99,7 +93,7 @@ export const ErrorNavIndicator = () => {
9993
The following repositories failed to index:
10094
</p>
10195
<div className="flex flex-col gap-2">
102-
{failedRepos
96+
{repos
10397
.slice(0, 10)
10498
.map(repo => (
10599
// Link to the first connection for the repo
@@ -115,9 +109,9 @@ export const ErrorNavIndicator = () => {
115109
</div>
116110
</Link>
117111
))}
118-
{failedRepos.length > 10 && (
112+
{repos.length > 10 && (
119113
<div className="text-sm text-red-600/90 dark:text-red-300/90 pl-3 pt-1">
120-
And {failedRepos.length - 10} more...
114+
And {repos.length - 10} more...
121115
</div>
122116
)}
123117
</div>

packages/web/src/app/[domain]/components/progressNavIndicator.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/h
55
import useCaptureEvent from "@/hooks/useCaptureEvent";
66
import { useDomain } from "@/hooks/useDomain";
77
import { NEXT_PUBLIC_POLLING_INTERVAL_MS } from "@/lib/environment.client";
8-
import { isServiceError } from "@/lib/utils";
8+
import { unwrapServiceError } from "@/lib/utils";
99
import { RepoIndexingStatus } from "@prisma/client";
1010
import { useQuery } from "@tanstack/react-query";
1111
import { Loader2Icon } from "lucide-react";
@@ -15,20 +15,14 @@ export const ProgressNavIndicator = () => {
1515
const domain = useDomain();
1616
const captureEvent = useCaptureEvent();
1717

18-
const { data: inProgressRepos } = useQuery({
18+
const { data: inProgressRepos, isPending, isError } = useQuery({
1919
queryKey: ['repos', domain],
20-
queryFn: () => getRepos(domain),
21-
select: (data) => {
22-
if (isServiceError(data)) {
23-
return data;
24-
}
25-
return data.filter(repo => repo.repoIndexingStatus === RepoIndexingStatus.IN_INDEX_QUEUE || repo.repoIndexingStatus === RepoIndexingStatus.INDEXING);
26-
},
20+
queryFn: () => unwrapServiceError(getRepos(domain)),
21+
select: (data) => data.filter(repo => repo.repoIndexingStatus === RepoIndexingStatus.IN_INDEX_QUEUE || repo.repoIndexingStatus === RepoIndexingStatus.INDEXING),
2722
refetchInterval: NEXT_PUBLIC_POLLING_INTERVAL_MS,
28-
initialData: [],
2923
});
3024

31-
if (isServiceError(inProgressRepos) || inProgressRepos.length === 0) {
25+
if (isPending || isError || inProgressRepos.length === 0) {
3226
return null;
3327
}
3428

packages/web/src/app/[domain]/components/warningNavIndicator.tsx

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { HoverCard, HoverCardTrigger, HoverCardContent } from "@/components/ui/h
55
import { AlertTriangleIcon } from "lucide-react";
66
import { useDomain } from "@/hooks/useDomain";
77
import { getConnections } from "@/actions";
8-
import { isServiceError } from "@/lib/utils";
8+
import { unwrapServiceError } from "@/lib/utils";
99
import useCaptureEvent from "@/hooks/useCaptureEvent";
1010
import { NEXT_PUBLIC_POLLING_INTERVAL_MS } from "@/lib/environment.client";
1111
import { useQuery } from "@tanstack/react-query";
@@ -15,20 +15,14 @@ export const WarningNavIndicator = () => {
1515
const domain = useDomain();
1616
const captureEvent = useCaptureEvent();
1717

18-
const { data: connections } = useQuery({
18+
const { data: connections, isPending, isError } = useQuery({
1919
queryKey: ['connections', domain],
20-
queryFn: () => getConnections(domain),
21-
select: (data) => {
22-
if (isServiceError(data)) {
23-
return data;
24-
}
25-
return data.filter(connection => connection.syncStatus === ConnectionSyncStatus.SYNCED_WITH_WARNINGS);
26-
},
20+
queryFn: () => unwrapServiceError(getConnections(domain)),
21+
select: (data) => data.filter(connection => connection.syncStatus === ConnectionSyncStatus.SYNCED_WITH_WARNINGS),
2722
refetchInterval: NEXT_PUBLIC_POLLING_INTERVAL_MS,
28-
initialData: [],
2923
});
3024

31-
if (isServiceError(connections) || connections.length === 0) {
25+
if (isPending || isError || connections.length === 0) {
3226
return null;
3327
}
3428

packages/web/src/app/[domain]/connections/[id]/components/notFoundWarning.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@
22

33
import { AlertTriangle } from "lucide-react"
44
import { Prisma, ConnectionSyncStatus } from "@sourcebot/db"
5-
import { RetrySyncButton } from "./retrySyncButton"
65
import { SyncStatusMetadataSchema } from "@/lib/syncStatusMetadataSchema"
76
import useCaptureEvent from "@/hooks/useCaptureEvent";
7+
import { ReloadIcon } from "@radix-ui/react-icons";
8+
import { Button } from "@/components/ui/button";
89

910
interface NotFoundWarningProps {
1011
syncStatus: ConnectionSyncStatus
1112
syncStatusMetadata: Prisma.JsonValue
1213
onSecretsClick: () => void
13-
connectionId: number
14-
domain: string
1514
connectionType: string
15+
onRetrySync: () => void
1616
}
1717

18-
export const NotFoundWarning = ({ syncStatus, syncStatusMetadata, onSecretsClick, connectionId, domain, connectionType }: NotFoundWarningProps) => {
18+
export const NotFoundWarning = ({ syncStatus, syncStatusMetadata, onSecretsClick, connectionType, onRetrySync }: NotFoundWarningProps) => {
1919
const captureEvent = useCaptureEvent();
2020

2121
const parseResult = SyncStatusMetadataSchema.safeParse(syncStatusMetadata);
@@ -65,7 +65,15 @@ export const NotFoundWarning = ({ syncStatus, syncStatusMetadata, onSecretsClick
6565
)}
6666
</ul>
6767
<div className="w-full flex justify-center">
68-
<RetrySyncButton connectionId={connectionId} domain={domain} />
68+
<Button
69+
variant="outline"
70+
size="sm"
71+
className="ml-2"
72+
onClick={onRetrySync}
73+
>
74+
<ReloadIcon className="h-4 w-4 mr-2" />
75+
Retry Sync
76+
</Button>
6977
</div>
7078
</div>
7179
)

0 commit comments

Comments
 (0)