@@ -13,6 +13,7 @@ import {
1313import { AnimatePresence , motion } from 'framer-motion' ;
1414import Link from 'next/link' ;
1515import { useState } from 'react' ;
16+ import { toast } from 'sonner' ;
1617import { FaviconImage } from '@/components/analytics/favicon-image' ;
1718import { Badge } from '@/components/ui/badge' ;
1819import { 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-
5540const 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 >
0 commit comments