Skip to content

Commit 9b8401b

Browse files
committed
fix: vercel integration
1 parent 181af14 commit 9b8401b

File tree

17 files changed

+3043
-1371
lines changed

17 files changed

+3043
-1371
lines changed

apps/dashboard/app/(main)/settings/integrations/vercel/_components/project-row.tsx

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { AnimatePresence, motion } from 'framer-motion';
1414
import Link from 'next/link';
1515
import { useState } from 'react';
16+
import { toast } from 'sonner';
1617
import { FaviconImage } from '@/components/analytics/favicon-image';
1718
import { Badge } from '@/components/ui/badge';
1819
import { Button } from '@/components/ui/button';
@@ -36,22 +37,6 @@ interface ProjectRowProps {
3637
integrationStatus?: any;
3738
}
3839

39-
const formatTimeAgo = (timestamp: number): string => {
40-
const now = Date.now();
41-
const diff = now - timestamp;
42-
const minutes = Math.floor(diff / (1000 * 60));
43-
const hours = Math.floor(diff / (1000 * 60 * 60));
44-
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
45-
46-
if (minutes < 60) {
47-
return `${minutes}m ago`;
48-
}
49-
if (hours < 24) {
50-
return `${hours}h ago`;
51-
}
52-
return `${days}d ago`;
53-
};
54-
5540
const GitHubIcon = () => (
5641
<svg
5742
className="h-4 w-4 flex-shrink-0"
@@ -67,7 +52,7 @@ const StatusBadge = ({ status }: { status: string }) => {
6752
const badges = {
6853
integrated: (
6954
<Badge
70-
className="flex items-center gap-1 border-green-200 bg-green-50 text-green-700 text-xs"
55+
className="flex items-center gap-1 border-border bg-accent text-accent-foreground text-xs"
7156
variant="outline"
7257
>
7358
<CheckCircleIcon className="h-3 w-3" />
@@ -76,7 +61,7 @@ const StatusBadge = ({ status }: { status: string }) => {
7661
),
7762
orphaned: (
7863
<Badge
79-
className="flex items-center gap-1 border-yellow-200 bg-yellow-50 text-xs text-yellow-700"
64+
className="flex items-center gap-1 border-border bg-muted text-muted-foreground text-xs"
8065
variant="outline"
8166
>
8267
<WarningIcon className="h-3 w-3" />
@@ -85,7 +70,7 @@ const StatusBadge = ({ status }: { status: string }) => {
8570
),
8671
invalid: (
8772
<Badge
88-
className="flex items-center gap-1 border-red-200 bg-red-50 text-red-700 text-xs"
73+
className="flex items-center gap-1 border-destructive/20 bg-destructive/10 text-destructive text-xs"
8974
variant="outline"
9075
>
9176
<XCircleIcon className="h-3 w-3" />
@@ -260,7 +245,7 @@ const DomainRow = ({
260245
<span className="font-medium text-sm">{domain.name}</span>
261246
{!domain.verified && (
262247
<Badge
263-
className="border-yellow-200 bg-yellow-50 text-xs text-yellow-600"
248+
className="border-border bg-muted text-muted-foreground text-xs"
264249
variant="outline"
265250
>
266251
Pending
@@ -272,19 +257,24 @@ const DomainRow = ({
272257
{isIntegrated &&
273258
domainStatus?.websiteName &&
274259
domainStatus?.websiteId && (
275-
<span className="text-green-700">
276-
{' '}
260+
<div className="flex items-center gap-1">
261+
<span className="text-muted-foreground"></span>
277262
<Link
278-
className="hover:underline"
263+
className="inline-flex items-center gap-1 rounded border border-border bg-accent px-2 py-1 font-medium text-accent-foreground text-xs transition-all hover:border-ring hover:bg-muted hover:shadow-sm"
279264
href={`/websites/${domainStatus.websiteId}`}
280265
>
281266
{domainStatus.websiteName}
267+
<ArrowRightIcon className="h-3 w-3" weight="bold" />
282268
</Link>
283-
</span>
269+
</div>
284270
)}
285271
{hasIssues && (
286272
<div className="flex items-center gap-2">
287-
<span className="text-red-600">{domainStatus.issues[0]}</span>
273+
<span className="text-destructive">
274+
{domainStatus.length > 1
275+
? domainStatus.issues.join(', ')
276+
: domainStatus.issues[0]}
277+
</span>
288278
<TriageMenu
289279
domain={domain}
290280
domainStatus={domainStatus}
@@ -315,9 +305,6 @@ const DomainRow = ({
315305
{domain.customEnvironmentId}
316306
</Badge>
317307
)}
318-
<span>
319-
Created {new Date(domain.createdAt).toLocaleDateString()}
320-
</span>
321308
</div>
322309
</div>
323310
</div>
@@ -415,9 +402,10 @@ export function ProjectRow({
415402
try {
416403
if (action === 'unintegrate') {
417404
if (!envVarId) {
418-
throw new Error(
405+
toast.error(
419406
'Environment variable ID is required for unintegrate action'
420407
);
408+
return;
421409
}
422410
await unintegrateMutation.mutateAsync({
423411
projectId: project.id,
@@ -427,6 +415,7 @@ export function ProjectRow({
427415
deleteWebsite: false, // Default to keeping the website
428416
organizationId: activeOrganization?.id,
429417
});
418+
toast.success(`Successfully unintegrated ${domainName}`);
430419
} else {
431420
await triageIssueMutation.mutateAsync({
432421
projectId: project.id,
@@ -436,12 +425,26 @@ export function ProjectRow({
436425
websiteId,
437426
organizationId: activeOrganization?.id,
438427
});
428+
toast.success(`Successfully resolved issues for ${domainName}`);
439429
}
440430

441431
// Refresh the projects data to show updated status
442432
await utils.vercel.getProjects.invalidate();
443-
} catch (error) {
444-
// Error handling is done by the mutation
433+
} catch (error: any) {
434+
// Handle specific error cases
435+
if (error?.data?.code === 'UNAUTHORIZED') {
436+
toast.error(
437+
'Missing organization permissions. Please check your Vercel integration settings.'
438+
);
439+
} else if (error?.data?.code === 'FORBIDDEN') {
440+
toast.error('Insufficient permissions to perform this action.');
441+
} else if (error?.data?.code === 'NOT_FOUND') {
442+
toast.error('Project or domain not found. It may have been deleted.');
443+
} else if (error?.message) {
444+
toast.error(error.message);
445+
} else {
446+
toast.error('An unexpected error occurred. Please try again.');
447+
}
445448
}
446449
};
447450

@@ -500,7 +503,7 @@ export function ProjectRow({
500503
)}
501504
{project.live && (
502505
<Badge
503-
className="ml-2 bg-green-100 text-green-800 text-xs dark:bg-green-900 dark:text-green-100"
506+
className="ml-2 bg-accent text-accent-foreground text-xs"
504507
variant="default"
505508
>
506509
Live
@@ -510,7 +513,7 @@ export function ProjectRow({
510513
</div>
511514
</div>
512515

513-
<div className="grid min-w-0 grid-cols-[1fr_auto] items-center gap-2 text-muted-foreground text-sm sm:grid-cols-[150px_1fr_100px_70px] sm:gap-4 lg:grid-cols-[200px_1fr_120px_80px]">
516+
<div className="grid min-w-0 grid-cols-[1fr_auto] items-center gap-2 text-muted-foreground text-sm sm:grid-cols-[150px_1fr] sm:gap-4 lg:grid-cols-[200px_1fr]">
514517
<div className="flex justify-start">
515518
{project.link?.repo ? (
516519
<Badge className="max-w-full" variant="outline">
@@ -538,16 +541,6 @@ export function ProjectRow({
538541
<span className="text-muted-foreground"></span>
539542
)}
540543
</div>
541-
542-
<div className="flex justify-end">
543-
{project.updatedAt ? (
544-
<span className="whitespace-nowrap">
545-
{formatTimeAgo(project.updatedAt)}
546-
</span>
547-
) : (
548-
<span className="text-muted-foreground"></span>
549-
)}
550-
</div>
551544
</div>
552545
</div>
553546

@@ -604,15 +597,15 @@ export function ProjectRow({
604597
{integrationStatus?.summary && (
605598
<>
606599
{' • '}
607-
<span className="text-green-700">
600+
<span className="text-accent-foreground">
608601
{integrationStatus.summary.integratedCount}{' '}
609602
integrated
610603
</span>
611604
{integrationStatus.summary.orphanedCount >
612605
0 && (
613606
<>
614607
{' • '}
615-
<span className="text-yellow-700">
608+
<span className="text-muted-foreground">
616609
{
617610
integrationStatus.summary
618611
.orphanedCount
@@ -625,7 +618,7 @@ export function ProjectRow({
625618
0 && (
626619
<>
627620
{' • '}
628-
<span className="text-red-700">
621+
<span className="text-destructive">
629622
{integrationStatus.summary.invalidCount}{' '}
630623
invalid
631624
</span>

apps/dashboard/app/(main)/settings/integrations/vercel/page.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { authClient } from '@databuddy/auth/client';
44
import { RocketLaunchIcon } from '@phosphor-icons/react';
55
import { useState } from 'react';
6+
import { toast } from 'sonner';
67
import { trpc } from '@/lib/trpc';
78
import {
89
CreateWebsiteDialog,
@@ -80,13 +81,29 @@ export default function VercelConfigPage() {
8081

8182
if (result.success) {
8283
await utils.vercel.getProjects.invalidate();
84+
toast.success(
85+
`Successfully integrated ${configs.length} website${configs.length > 1 ? 's' : ''}`
86+
);
8387
}
8488

8589
setIsDialogOpen(false);
8690
setSelectedDomains([]);
8791
setSelectedProject(null);
88-
} catch (error) {
89-
// Handle error silently or show user-friendly message
92+
} catch (error: any) {
93+
// Handle specific error cases
94+
if (error?.data?.code === 'UNAUTHORIZED') {
95+
toast.error(
96+
'Missing organization permissions. Please check your Vercel integration settings.'
97+
);
98+
} else if (error?.data?.code === 'FORBIDDEN') {
99+
toast.error('Insufficient permissions to integrate websites.');
100+
} else if (error?.data?.code === 'NOT_FOUND') {
101+
toast.error('Project not found. It may have been deleted.');
102+
} else if (error?.message) {
103+
toast.error(error.message);
104+
} else {
105+
toast.error('Failed to integrate websites. Please try again.');
106+
}
90107
}
91108
};
92109

0 commit comments

Comments
 (0)