Skip to content

Commit 6490f6e

Browse files
SaxonFjoshenlim
andauthored
Refine the integrations page (supabase#38113)
* integrations refine * refactoring * integration page updates * div * clean up * prettier * Couple of minor refactors and fixes * Fix missing base path for integration card image * Fix editing a cron job in the previous job run tab, not updating the cron job details in the page header * Cron job bread crumb to use job name instead of id if available, and add loading state for cron job subtitle * Fixy fix * Small fi * ANother one * Do not reroute after purging queue * nit * Final final final v3 --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
1 parent 48a7aa9 commit 6490f6e

File tree

31 files changed

+1166
-846
lines changed

31 files changed

+1166
-846
lines changed

apps/studio/components/interfaces/BranchManagement/BranchPanels.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { PropsWithChildren, ReactNode } from 'react'
66
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
77
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
88
import type { Branch } from 'data/branches/branches-query'
9+
import { BASE_PATH } from 'lib/constants'
910
import { Tooltip, TooltipContent, TooltipTrigger } from 'ui'
1011
import { WorkflowLogs } from './WorkflowLogs'
1112

@@ -87,7 +88,7 @@ export const BranchRow = ({
8788

8889
const handleRowClick = () => {
8990
if (external) {
90-
window.open(navigateUrl, '_blank', 'noopener noreferrer')
91+
window.open(`${BASE_PATH}/${navigateUrl}`, '_blank', 'noopener noreferrer')
9192
} else {
9293
router.push(navigateUrl)
9394
}

apps/studio/components/interfaces/Home/ProjectList/ProjectTableRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export const ProjectTableRow = ({
4242
className="cursor-pointer hover:bg-surface-200"
4343
onClick={(event) => {
4444
if (event.metaKey) {
45-
window.open(url, '_blank')
45+
window.open(`${BASE_PATH}/${url}`, '_blank')
4646
} else {
4747
router.push(url)
4848
}

apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { toast } from 'sonner'
88
import z from 'zod'
99

1010
import { useWatch } from '@ui/components/shadcn/ui/form'
11+
import { useParams } from 'common'
1112
import { urlRegex } from 'components/interfaces/Auth/Auth.constants'
1213
import EnableExtensionModal from 'components/interfaces/Database/Extensions/EnableExtensionModal'
1314
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
@@ -201,11 +202,13 @@ export const CreateCronJobSheet = ({
201202
setIsClosing,
202203
onClose,
203204
}: CreateCronJobSheetProps) => {
205+
const { childId } = useParams()
204206
const { data: project } = useSelectedProjectQuery()
205207
const { data: org } = useSelectedOrganizationQuery()
206208
const [searchQuery] = useQueryState('search', parseAsString.withDefault(''))
207209
const [isLoadingGetCronJob, setIsLoadingGetCronJob] = useState(false)
208210

211+
const jobId = Number(childId)
209212
const isEditing = !!selectedCronJob?.jobname
210213
const [showEnableExtensionModal, setShowEnableExtensionModal] = useState(false)
211214

@@ -340,6 +343,8 @@ export const CreateCronJobSheet = ({
340343
connectionString: project?.connectionString,
341344
query,
342345
searchTerm: searchQuery,
346+
// [Joshen] Only need to invalidate a specific cron job if in the job's previous run tab
347+
identifier: !!jobId ? jobId : undefined,
343348
},
344349
{
345350
onSuccess: () => {
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import { toString as CronToString } from 'cronstrue'
2+
import { Edit3, List } from 'lucide-react'
3+
import Link from 'next/link'
4+
import { useRouter } from 'next/router'
5+
import { useState } from 'react'
6+
7+
import { useParams } from 'common'
8+
import { NavigationItem, PageLayout } from 'components/layouts/PageLayout/PageLayout'
9+
import { useCronJobQuery } from 'data/database-cron-jobs/database-cron-job-query'
10+
import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query'
11+
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
12+
import {
13+
Button,
14+
cn,
15+
CodeBlock,
16+
Sheet,
17+
SheetContent,
18+
Tooltip,
19+
TooltipContent,
20+
TooltipTrigger,
21+
} from 'ui'
22+
import ShimmeringLoader from 'ui-patterns/ShimmeringLoader'
23+
import { CreateCronJobSheet } from './CreateCronJobSheet'
24+
import { isSecondsFormat, parseCronJobCommand } from './CronJobs.utils'
25+
import { PreviousRunsTab } from './PreviousRunsTab'
26+
27+
export const CronJobPage = () => {
28+
const router = useRouter()
29+
const { ref, id, pageId, childId } = useParams()
30+
const childLabel = router?.query?.['child-label'] as string
31+
const { data: project } = useSelectedProjectQuery()
32+
33+
const [isEditSheetOpen, setIsEditSheetOpen] = useState(false)
34+
const [isClosing, setIsClosing] = useState(false)
35+
36+
const jobId = Number(childId)
37+
38+
const { data: job, isLoading } = useCronJobQuery({
39+
projectRef: project?.ref,
40+
connectionString: project?.connectionString,
41+
id: jobId,
42+
})
43+
44+
const { data: edgeFunctions = [] } = useEdgeFunctionsQuery({ projectRef: project?.ref })
45+
46+
// Parse the cron job command to check if it's an edge function
47+
const cronJobValues = parseCronJobCommand(job?.command || '', project?.ref!)
48+
const edgeFunction =
49+
cronJobValues.type === 'edge_function' ? cronJobValues.edgeFunctionName : undefined
50+
const edgeFunctionSlug = edgeFunction?.split('/functions/v1/').pop()
51+
const isValidEdgeFunction = edgeFunctions.some((x) => x.slug === edgeFunctionSlug)
52+
53+
const breadcrumbItems = [
54+
{
55+
label: 'Integrations',
56+
href: `/project/${ref}/integrations`,
57+
},
58+
{
59+
label: 'Cron',
60+
href: pageId
61+
? `/project/${ref}/integrations/${id}/${pageId}`
62+
: `/project/${ref}/integrations/${id}`,
63+
},
64+
{
65+
label: childLabel ?? job?.jobname ?? '',
66+
},
67+
]
68+
69+
const navigationItems: NavigationItem[] = []
70+
71+
const pageTitle = childLabel || childId || 'Cron Job'
72+
73+
const pageSubtitle = job ? (
74+
<div className="text-sm text-foreground-light">
75+
Running{' '}
76+
<Tooltip>
77+
<TooltipTrigger asChild>
78+
<span className="cursor-pointer underline decoration-dotted lowercase">
79+
{isSecondsFormat(job.schedule)
80+
? job.schedule.toLowerCase()
81+
: CronToString(job.schedule.toLowerCase())}
82+
</span>
83+
</TooltipTrigger>
84+
<TooltipContent side="bottom" align="center">
85+
<div className="text-xs">
86+
<p className="font-mono mb-1">{job.schedule.toLowerCase()}</p>
87+
{!isSecondsFormat(job.schedule) && (
88+
<p className="text-foreground-light">{CronToString(job.schedule.toLowerCase())}</p>
89+
)}
90+
</div>
91+
</TooltipContent>
92+
</Tooltip>{' '}
93+
with command{' '}
94+
<Tooltip>
95+
<TooltipTrigger asChild>
96+
<code className="text-xs font-mono bg-surface-200 px-1 py-0.5 rounded max-w-[200px] inline-block truncate align-bottom cursor-pointer">
97+
{job.command}
98+
</code>
99+
</TooltipTrigger>
100+
<TooltipContent
101+
side="bottom"
102+
align="center"
103+
className="min-w-[200px] max-w-[400px] text-wrap p-0"
104+
>
105+
<p className="text-xs font-mono px-2 py-1 border-b bg-surface-100">Command</p>
106+
<CodeBlock
107+
hideLineNumbers
108+
language="sql"
109+
value={job.command.trim()}
110+
className={cn(
111+
'py-0 px-3.5 max-w-full prose dark:prose-dark border-0 rounded-t-none',
112+
'[&>code]:m-0 [&>code>span]:flex [&>code>span]:flex-wrap min-h-11',
113+
'[&>code]:text-xs'
114+
)}
115+
/>
116+
</TooltipContent>
117+
</Tooltip>
118+
</div>
119+
) : null
120+
121+
// Secondary actions
122+
const secondaryActions = [
123+
<Button
124+
key="edit"
125+
type="outline"
126+
icon={<Edit3 strokeWidth={1.5} size="14" />}
127+
onClick={() => setIsEditSheetOpen(true)}
128+
>
129+
Edit
130+
</Button>,
131+
<Button key="view-logs" asChild type="outline" icon={<List strokeWidth={1.5} size="14" />}>
132+
<Link
133+
target="_blank"
134+
rel="noopener noreferrer"
135+
href={`/project/${project?.ref}/logs/pgcron-logs/`}
136+
>
137+
View Cron logs
138+
</Link>
139+
</Button>,
140+
...(isValidEdgeFunction
141+
? [
142+
<Button key="view-edge-logs" asChild type="outline">
143+
<Link
144+
target="_blank"
145+
rel="noopener noreferrer"
146+
href={`/project/${project?.ref}/functions/${edgeFunctionSlug}/logs`}
147+
>
148+
View Edge Function logs
149+
</Link>
150+
</Button>,
151+
]
152+
: []),
153+
]
154+
155+
return (
156+
<>
157+
<PageLayout
158+
title={pageTitle}
159+
size="full"
160+
breadcrumbs={breadcrumbItems}
161+
navigationItems={navigationItems}
162+
secondaryActions={secondaryActions}
163+
subtitle={isLoading ? <ShimmeringLoader className="py-0 h-[20px] w-96" /> : pageSubtitle}
164+
className="border-b-0"
165+
>
166+
<PreviousRunsTab />
167+
</PageLayout>
168+
169+
<Sheet open={isEditSheetOpen} onOpenChange={setIsEditSheetOpen}>
170+
<SheetContent size="lg">
171+
{job && (
172+
<CreateCronJobSheet
173+
selectedCronJob={{
174+
jobname: job.jobname,
175+
schedule: job.schedule,
176+
active: job.active,
177+
command: job.command,
178+
}}
179+
supportsSeconds={true}
180+
isClosing={isClosing}
181+
setIsClosing={setIsClosing}
182+
onClose={() => {
183+
setIsEditSheetOpen(false)
184+
setIsClosing(false)
185+
}}
186+
/>
187+
)}
188+
</SheetContent>
189+
</Sheet>
190+
</>
191+
)
192+
}

apps/studio/components/interfaces/Integrations/CronJobs/CronJobsTab.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-ex
1717
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
1818
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
1919
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
20+
import { BASE_PATH } from 'lib/constants'
2021
import { isAtBottom } from 'lib/helpers'
2122
import { Button, cn, LoadingLine, Sheet, SheetContent } from 'ui'
2223
import { Input } from 'ui-patterns/DataInputs/Input'
@@ -211,7 +212,7 @@ export const CronjobsTab = () => {
211212
})
212213

213214
if (e.metaKey) {
214-
window.open(url, '_blank')
215+
window.open(`${BASE_PATH}/${url}`, '_blank')
215216
} else {
216217
router.push(url)
217218
}

0 commit comments

Comments
 (0)