Skip to content

Commit 6e1e5db

Browse files
jordienrjoshenlim
andauthored
Project Status improvements (supabase#29669)
* add logs links, set status as coming up when project is less than 10 minutes old * make whole dorpdown item clickable * Small tweak to UI * Switch to 5 mins * Small tweak * Add useeffect to automatically refresh service status * Add useeffect to automatically refresh service status * Address feedback --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
1 parent 0d5715c commit 6e1e5db

File tree

2 files changed

+113
-35
lines changed

2 files changed

+113
-35
lines changed

apps/studio/components/interfaces/Home/SecurityStatus.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import { CheckCircle2, ChevronRight, Loader2 } from 'lucide-react'
2+
import Link from 'next/link'
23
import { useState } from 'react'
4+
5+
import { useParams } from 'common'
6+
import { useProjectLintsQuery } from 'data/lint/lint-query'
37
import {
48
Button,
59
PopoverContent_Shadcn_,
10+
PopoverSeparator_Shadcn_,
611
PopoverTrigger_Shadcn_,
712
Popover_Shadcn_,
8-
PopoverSeparator_Shadcn_,
913
cn,
1014
} from 'ui'
11-
import { useProjectLintsQuery } from 'data/lint/lint-query'
12-
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
13-
import Link from 'next/link'
14-
1515
import { LINTER_LEVELS, LINT_TABS } from '../Linter/Linter.constants'
16-
import { useParams } from 'common'
1716

1817
export const SecurityStatus = () => {
1918
const { ref } = useParams()

apps/studio/components/interfaces/Home/ServiceStatus.tsx

Lines changed: 108 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { AlertTriangle, CheckCircle2, Loader2 } from 'lucide-react'
2-
import { useState } from 'react'
1+
import dayjs from 'dayjs'
2+
import { AlertTriangle, CheckCircle2, ChevronRight, Loader2 } from 'lucide-react'
3+
import Link from 'next/link'
4+
import { useEffect, useState } from 'react'
35

46
import { useParams } from 'common'
57
import { useEdgeFunctionServiceStatusQuery } from 'data/service-status/edge-functions-status-query'
@@ -9,6 +11,37 @@ import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
911
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
1012
import { Button, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_ } from 'ui'
1113

14+
const SERVICE_STATUS_THRESHOLD = 5 // minutes
15+
16+
const StatusMessage = ({
17+
isLoading,
18+
isSuccess,
19+
isProjectNew,
20+
}: {
21+
isLoading: boolean
22+
isSuccess: boolean
23+
isProjectNew: boolean
24+
}) => {
25+
if (isLoading) return 'Checking status'
26+
if (isProjectNew) return 'Coming up...'
27+
if (isSuccess) return 'No issues'
28+
return 'Unable to connect'
29+
}
30+
31+
const StatusIcon = ({
32+
isLoading,
33+
isSuccess,
34+
isProjectNew,
35+
}: {
36+
isLoading: boolean
37+
isSuccess: boolean
38+
isProjectNew: boolean
39+
}) => {
40+
if (isLoading || isProjectNew) return <Loader2 size={14} className="animate-spin" />
41+
if (isSuccess) return <CheckCircle2 className="text-brand" size={18} strokeWidth={1.5} />
42+
return <AlertTriangle className="text-warning" size={18} strokeWidth={1.5} />
43+
}
44+
1245
const ServiceStatus = () => {
1346
const { ref } = useParams()
1447
const project = useSelectedProject()
@@ -29,13 +62,21 @@ const ServiceStatus = () => {
2962
const isBranch = project?.parentRef !== project?.ref
3063

3164
// [Joshen] Need pooler service check eventually
32-
const { data: status, isLoading } = useProjectServiceStatusQuery({ projectRef: ref })
33-
const { data: edgeFunctionsStatus } = useEdgeFunctionServiceStatusQuery({ projectRef: ref })
34-
const { isLoading: isLoadingPostgres, isSuccess: isSuccessPostgres } =
35-
usePostgresServiceStatusQuery({
36-
projectRef: ref,
37-
connectionString: project?.connectionString,
38-
})
65+
const {
66+
data: status,
67+
isLoading,
68+
refetch: refetchServiceStatus,
69+
} = useProjectServiceStatusQuery({ projectRef: ref })
70+
const { data: edgeFunctionsStatus, refetch: refetchEdgeFunctionServiceStatus } =
71+
useEdgeFunctionServiceStatusQuery({ projectRef: ref })
72+
const {
73+
isLoading: isLoadingPostgres,
74+
isSuccess: isSuccessPostgres,
75+
refetch: refetchPostgresServiceStatus,
76+
} = usePostgresServiceStatusQuery({
77+
projectRef: ref,
78+
connectionString: project?.connectionString,
79+
})
3980

4081
const authStatus = status?.find((service) => service.name === 'auth')
4182
const restStatus = status?.find((service) => service.name === 'rest')
@@ -49,20 +90,23 @@ const ServiceStatus = () => {
4990
docsUrl?: string
5091
isLoading: boolean
5192
isSuccess?: boolean
93+
logsUrl: string
5294
}[] = [
5395
{
5496
name: 'Database',
5597
error: undefined,
5698
docsUrl: undefined,
5799
isLoading: isLoadingPostgres,
58100
isSuccess: isSuccessPostgres,
101+
logsUrl: '/logs/postgres-logs',
59102
},
60103
{
61104
name: 'PostgREST',
62105
error: restStatus?.error,
63106
docsUrl: undefined,
64107
isLoading,
65108
isSuccess: restStatus?.healthy,
109+
logsUrl: '/logs/postgrest-logs',
66110
},
67111
...(authEnabled
68112
? [
@@ -72,6 +116,7 @@ const ServiceStatus = () => {
72116
docsUrl: undefined,
73117
isLoading,
74118
isSuccess: authStatus?.healthy,
119+
logsUrl: '/logs/auth-logs',
75120
},
76121
]
77122
: []),
@@ -83,6 +128,7 @@ const ServiceStatus = () => {
83128
docsUrl: undefined,
84129
isLoading,
85130
isSuccess: realtimeStatus?.healthy,
131+
logsUrl: '/logs/realtime-logs',
86132
},
87133
]
88134
: []),
@@ -94,6 +140,7 @@ const ServiceStatus = () => {
94140
docsUrl: undefined,
95141
isLoading,
96142
isSuccess: storageStatus?.healthy,
143+
logsUrl: '/logs/storage-logs',
97144
},
98145
]
99146
: []),
@@ -105,6 +152,7 @@ const ServiceStatus = () => {
105152
docsUrl: 'https://supabase.com/docs/guides/functions/troubleshooting',
106153
isLoading,
107154
isSuccess: edgeFunctionsStatus?.healthy,
155+
logsUrl: '/logs/edge-functions-logs',
108156
},
109157
]
110158
: []),
@@ -113,13 +161,39 @@ const ServiceStatus = () => {
113161
const isLoadingChecks = services.some((service) => service.isLoading)
114162
const allServicesOperational = services.every((service) => service.isSuccess)
115163

164+
// If the project is less than 5 minutes old, and status is not operational, then it's likely the service is still starting up
165+
const isProjectNew =
166+
dayjs.utc().diff(dayjs.utc(project?.inserted_at), 'minute') < SERVICE_STATUS_THRESHOLD
167+
168+
useEffect(() => {
169+
let timer: any
170+
171+
if (isProjectNew) {
172+
const secondsSinceProjectCreated = dayjs
173+
.utc()
174+
.diff(dayjs.utc(project?.inserted_at), 'seconds')
175+
const remainingTimeTillNextCheck = SERVICE_STATUS_THRESHOLD * 60 - secondsSinceProjectCreated
176+
177+
timer = setTimeout(() => {
178+
refetchServiceStatus()
179+
refetchPostgresServiceStatus()
180+
refetchEdgeFunctionServiceStatus()
181+
}, remainingTimeTillNextCheck * 1000)
182+
}
183+
184+
return () => {
185+
clearTimeout(timer)
186+
}
187+
// eslint-disable-next-line react-hooks/exhaustive-deps
188+
}, [isProjectNew])
189+
116190
return (
117191
<Popover_Shadcn_ modal={false} open={open} onOpenChange={setOpen}>
118192
<PopoverTrigger_Shadcn_ asChild>
119193
<Button
120194
type="default"
121195
icon={
122-
isLoadingChecks ? (
196+
isLoadingChecks || isProjectNew ? (
123197
<Loader2 className="animate-spin" />
124198
) : (
125199
<div
@@ -135,28 +209,33 @@ const ServiceStatus = () => {
135209
</PopoverTrigger_Shadcn_>
136210
<PopoverContent_Shadcn_ className="p-0 w-56" side="bottom" align="center">
137211
{services.map((service) => (
138-
<div
212+
<Link
213+
href={`/project/${ref}${service.logsUrl}`}
139214
key={service.name}
140-
className="px-4 py-2 text-xs flex items-center justify-between border-b last:border-none"
215+
className="transition px-3 py-2 text-xs flex items-center justify-between border-b last:border-none group relative hover:bg-surface-300"
141216
>
142-
<div>
143-
<p>{service.name}</p>
144-
<p className="text-foreground-light">
145-
{service.isLoading
146-
? 'Checking status'
147-
: service.isSuccess
148-
? 'No issues'
149-
: 'Unable to connect'}
150-
</p>
217+
<div className="flex gap-x-2">
218+
<StatusIcon
219+
isLoading={service.isLoading}
220+
isSuccess={!!service.isSuccess}
221+
isProjectNew={isProjectNew}
222+
/>
223+
<div className="flex-1">
224+
<p>{service.name}</p>
225+
<p className="text-foreground-light flex items-center gap-1">
226+
<StatusMessage
227+
isLoading={service.isLoading}
228+
isSuccess={!!service.isSuccess}
229+
isProjectNew={isProjectNew}
230+
/>
231+
</p>
232+
</div>
151233
</div>
152-
{service.isLoading ? (
153-
<Loader2 className="animate-spin text-foreground-light" size={18} />
154-
) : service.isSuccess ? (
155-
<CheckCircle2 className="text-brand" size={18} strokeWidth={1.5} />
156-
) : (
157-
<AlertTriangle className="text-warning" size={18} strokeWidth={1.5} />
158-
)}
159-
</div>
234+
<div className="flex items-center gap-x-1 transition opacity-0 group-hover:opacity-100">
235+
<span className="text-xs text-foreground">View logs</span>
236+
<ChevronRight size={14} className="text-foreground" />
237+
</div>
238+
</Link>
160239
))}
161240
</PopoverContent_Shadcn_>
162241
</Popover_Shadcn_>

0 commit comments

Comments
 (0)