mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
## What kind of change does this PR introduce? Mechanical cleanup on top of the Button default-variant change (#50160). ## What is the current behavior? Many callsites still pass `variant="default"` even though that is now the component default. ## What is the new behavior? Removes redundant static `variant="default"` from legacy `Button` and `ButtonTooltip` callsites. Keeps explicit defaults where they document the API: - `button-default.tsx` and `button-sizes.tsx` demos - `DocsButton`, which pins neutral styling at the wrapper boundary ## To test Studio: - [Auth → Rate Limits](https://studio-staging-2s957kwc4-supabase.vercel.app/dashboard/project/_/auth/rate-limits): dirty the form so Cancel appears; Cancel stays neutral, Save stays green - [Project Settings → API Keys](https://studio-staging-2s957kwc4-supabase.vercel.app/dashboard/project/_/settings/api-keys): `DocsButton` in the header actions stays neutral Design system: - [Design system → Button](https://design-system-git-dnywh-dc924ac1-supabase.vercel.app/design-system/docs/components/button): `button-default` / `button-sizes` still show explicit default styling; Primary (green) is restricted to the Primary section (and `asChild`) WWW: - [www → Brand assets](https://zone-www-dot-com-git-dnywh-dc924ac1-supabase.vercel.app/brand-assets): Download logo kit / Download button kit stay neutral
145 lines
4.7 KiB
TypeScript
145 lines
4.7 KiB
TypeScript
import { CRON_CLEANUP_JOB_NAME, getScheduleDeleteCronJobRunDetailsSql } from '@supabase/pg-meta'
|
|
import { useState } from 'react'
|
|
import { toast } from 'sonner'
|
|
import {
|
|
Button,
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogSection,
|
|
DialogSectionSeparator,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from 'ui'
|
|
import { CodeBlock } from 'ui-patterns/CodeBlock'
|
|
|
|
import { CLEANUP_INTERVALS } from './CronJobsTab.constants'
|
|
import { useCronJobQuery } from '@/data/database-cron-jobs/database-cron-job-query'
|
|
import { useScheduleCronJobRunDetailsCleanupMutation } from '@/data/database-cron-jobs/schedule-clean-up-mutation'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
import { useTrack } from '@/lib/telemetry/track'
|
|
|
|
const DEFAULT_CLEANUP_INTERVAL =
|
|
CLEANUP_INTERVALS.find((option) => option.value === '7 days')?.value ?? CLEANUP_INTERVALS[0].value
|
|
|
|
interface EnableCleanupButtonProps {
|
|
onScheduled: () => void
|
|
}
|
|
|
|
/**
|
|
* One-click action to schedule the daily cleanup job that trims old rows from
|
|
* cron.job_run_details. Hidden once the cleanup job already exists — the job
|
|
* itself then shows up in the jobs grid.
|
|
*/
|
|
export const EnableCleanupButton = ({ onScheduled }: EnableCleanupButtonProps) => {
|
|
const track = useTrack()
|
|
const { data: project } = useSelectedProjectQuery()
|
|
|
|
const [open, setOpen] = useState(false)
|
|
const [cleanupInterval, setCleanupInterval] = useState(DEFAULT_CLEANUP_INTERVAL)
|
|
|
|
const { data: cleanupJob, isSuccess } = useCronJobQuery({
|
|
projectRef: project?.ref,
|
|
connectionString: project?.connectionString,
|
|
name: CRON_CLEANUP_JOB_NAME,
|
|
})
|
|
|
|
const { mutate: scheduleCleanup, isPending: isScheduling } =
|
|
useScheduleCronJobRunDetailsCleanupMutation({
|
|
onSuccess: () => {
|
|
toast.success('Scheduled daily cleanup job.')
|
|
setOpen(false)
|
|
onScheduled()
|
|
},
|
|
})
|
|
|
|
if (!isSuccess || cleanupJob !== null) return null
|
|
|
|
const onConfirm = () => {
|
|
if (!project?.ref) {
|
|
return toast.error('There was an error scheduling the cleanup. Please try again.')
|
|
}
|
|
track('cron_job_cleanup_enable_button_clicked', {
|
|
retentionInterval: cleanupInterval,
|
|
})
|
|
scheduleCleanup({
|
|
projectRef: project.ref,
|
|
connectionString: project.connectionString,
|
|
interval: cleanupInterval,
|
|
})
|
|
}
|
|
|
|
return (
|
|
<Dialog
|
|
open={open}
|
|
onOpenChange={(isOpen) => {
|
|
setOpen(isOpen)
|
|
if (isOpen) track('cron_job_cleanup_dialog_opened')
|
|
}}
|
|
>
|
|
<DialogTrigger asChild>
|
|
<Button>Enable cleanup</Button>
|
|
</DialogTrigger>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>Enable automatic cleanup</DialogTitle>
|
|
<DialogDescription>
|
|
Schedules a daily job that deletes old cron job run records
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<DialogSectionSeparator />
|
|
<DialogSection className="flex flex-col gap-y-4">
|
|
<p className="text-sm">
|
|
Every cron job run is recorded in the{' '}
|
|
<code className="text-code-inline break-keep!">cron.job_run_details</code> table.
|
|
Without periodic cleanup, the table grows indefinitely and bloats the database.
|
|
</p>
|
|
<div className="flex flex-col gap-y-2 text-sm">
|
|
<p className="text-foreground">Delete run history</p>
|
|
<div className="sm:w-64">
|
|
<Select
|
|
disabled={isScheduling}
|
|
value={cleanupInterval}
|
|
onValueChange={setCleanupInterval}
|
|
>
|
|
<SelectTrigger className="w-full">
|
|
<SelectValue placeholder="Select an interval" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{CLEANUP_INTERVALS.map((option) => (
|
|
<SelectItem key={option.value} value={option.value}>
|
|
{option.label}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
<CodeBlock
|
|
hideLineNumbers
|
|
language="sql"
|
|
value={getScheduleDeleteCronJobRunDetailsSql(cleanupInterval)}
|
|
className="py-3 px-4 text-xs"
|
|
wrapperClassName="max-w-full"
|
|
/>
|
|
</DialogSection>
|
|
<DialogFooter>
|
|
<Button disabled={isScheduling} onClick={() => setOpen(false)}>
|
|
Cancel
|
|
</Button>
|
|
<Button variant="primary" loading={isScheduling} onClick={onConfirm}>
|
|
Enable cleanup
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|