Skip to content

Commit 4608b9a

Browse files
SaxonFjoshenlim
andauthored
Simplify paused state (supabase#39922)
* simplify paused state * copy * dialog * Update UI * More nudges * nit * Nudge --------- Co-authored-by: Joshen Lim <[email protected]>
1 parent 2c7492e commit 4608b9a

File tree

6 files changed

+334
-347
lines changed

6 files changed

+334
-347
lines changed

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

Lines changed: 76 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ export const Home = () => {
9898
// [Joshen] JFYI minus 1 as the replicas endpoint returns the primary DB minimally
9999
const replicasCount = Math.max(0, (replicasData?.length ?? 1) - 1)
100100

101+
if (isPaused) {
102+
return (
103+
<div className="w-full h-full flex items-center justify-center">
104+
<ProjectPausedState />
105+
</div>
106+
)
107+
}
108+
101109
return (
102110
<div className="w-full px-4">
103111
<div className={cn('py-16 ', !isPaused && 'border-b border-muted ')}>
@@ -200,84 +208,81 @@ export const Home = () => {
200208
</div>
201209
</div>
202210
<ProjectUpgradeFailedBanner />
203-
{isPaused && <ProjectPausedState />}
204211
</div>
205212
</div>
206213

207-
{!isPaused && (
208-
<>
209-
<div className="py-16 border-b border-muted">
210-
<div className="mx-auto max-w-7xl space-y-16">
211-
{IS_PLATFORM && project?.status !== PROJECT_STATUS.INACTIVE && (
212-
<>{isNewProject ? <NewProjectPanel /> : <ProjectUsageSection />}</>
213-
)}
214-
{!isNewProject && project?.status !== PROJECT_STATUS.INACTIVE && <AdvisorWidget />}
215-
</div>
214+
<>
215+
<div className="py-16 border-b border-muted">
216+
<div className="mx-auto max-w-7xl space-y-16">
217+
{IS_PLATFORM && project?.status !== PROJECT_STATUS.INACTIVE && (
218+
<>{isNewProject ? <NewProjectPanel /> : <ProjectUsageSection />}</>
219+
)}
220+
{!isNewProject && project?.status !== PROJECT_STATUS.INACTIVE && <AdvisorWidget />}
216221
</div>
222+
</div>
217223

218-
<div className="bg-surface-100/5 py-16">
219-
<div className="mx-auto max-w-7xl space-y-16">
220-
{project?.status !== PROJECT_STATUS.INACTIVE && (
221-
<>
222-
<div className="space-y-8">
223-
<h2 className="text-lg">Client libraries</h2>
224-
<div className="grid grid-cols-2 gap-x-8 gap-y-8 md:gap-12 mb-12 md:grid-cols-3">
225-
{clientLibraries!.map((library) => (
226-
// [Alaister]: Looks like the useCustomContent has wonky types. I'll look at a fix later.
227-
<ClientLibrary key={(library as any).language} {...(library as any)} />
228-
))}
229-
</div>
224+
<div className="bg-surface-100/5 py-16">
225+
<div className="mx-auto max-w-7xl space-y-16">
226+
{project?.status !== PROJECT_STATUS.INACTIVE && (
227+
<>
228+
<div className="space-y-8">
229+
<h2 className="text-lg">Client libraries</h2>
230+
<div className="grid grid-cols-2 gap-x-8 gap-y-8 md:gap-12 mb-12 md:grid-cols-3">
231+
{clientLibraries!.map((library) => (
232+
// [Alaister]: Looks like the useCustomContent has wonky types. I'll look at a fix later.
233+
<ClientLibrary key={(library as any).language} {...(library as any)} />
234+
))}
230235
</div>
231-
{showExamples && (
232-
<div className="flex flex-col gap-y-8">
233-
<h4 className="text-lg">Example projects</h4>
234-
{!!projectHomepageExampleProjects ? (
235-
<div className="grid gap-2 md:gap-8 md:grid-cols-2 lg:grid-cols-3">
236-
{/* [Alaister]: Looks like the useCustomContent has wonky types. I'll look at a fix later. */}
237-
{(projectHomepageExampleProjects as any)
238-
.sort((a: any, b: any) => a.title.localeCompare(b.title))
239-
.map((project: any) => (
240-
<ExampleProject key={project.url} {...project} />
241-
))}
242-
</div>
243-
) : (
244-
<div className="flex justify-center">
245-
<Tabs_Shadcn_ defaultValue="app" className="w-full">
246-
<TabsList_Shadcn_ className="flex gap-4 mb-8">
247-
<TabsTrigger_Shadcn_ value="app">App Frameworks</TabsTrigger_Shadcn_>
248-
<TabsTrigger_Shadcn_ value="mobile">
249-
Mobile Frameworks
250-
</TabsTrigger_Shadcn_>
251-
</TabsList_Shadcn_>
252-
<TabsContent_Shadcn_ value="app">
253-
<div className="grid gap-2 md:gap-8 md:grid-cols-2 lg:grid-cols-3">
254-
{EXAMPLE_PROJECTS.filter((project) => project.type === 'app')
255-
.sort((a, b) => a.title.localeCompare(b.title))
256-
.map((project) => (
257-
<ExampleProject key={project.url} {...project} />
258-
))}
259-
</div>
260-
</TabsContent_Shadcn_>
261-
<TabsContent_Shadcn_ value="mobile">
262-
<div className="grid gap-2 md:gap-8 md:grid-cols-2 lg:grid-cols-3">
263-
{EXAMPLE_PROJECTS.filter((project) => project.type === 'mobile')
264-
.sort((a, b) => a.title.localeCompare(b.title))
265-
.map((project) => (
266-
<ExampleProject key={project.url} {...project} />
267-
))}
268-
</div>
269-
</TabsContent_Shadcn_>
270-
</Tabs_Shadcn_>
271-
</div>
272-
)}
273-
</div>
274-
)}
275-
</>
276-
)}
277-
</div>
236+
</div>
237+
{showExamples && (
238+
<div className="flex flex-col gap-y-8">
239+
<h4 className="text-lg">Example projects</h4>
240+
{!!projectHomepageExampleProjects ? (
241+
<div className="grid gap-2 md:gap-8 md:grid-cols-2 lg:grid-cols-3">
242+
{/* [Alaister]: Looks like the useCustomContent has wonky types. I'll look at a fix later. */}
243+
{(projectHomepageExampleProjects as any)
244+
.sort((a: any, b: any) => a.title.localeCompare(b.title))
245+
.map((project: any) => (
246+
<ExampleProject key={project.url} {...project} />
247+
))}
248+
</div>
249+
) : (
250+
<div className="flex justify-center">
251+
<Tabs_Shadcn_ defaultValue="app" className="w-full">
252+
<TabsList_Shadcn_ className="flex gap-4 mb-8">
253+
<TabsTrigger_Shadcn_ value="app">App Frameworks</TabsTrigger_Shadcn_>
254+
<TabsTrigger_Shadcn_ value="mobile">
255+
Mobile Frameworks
256+
</TabsTrigger_Shadcn_>
257+
</TabsList_Shadcn_>
258+
<TabsContent_Shadcn_ value="app">
259+
<div className="grid gap-2 md:gap-8 md:grid-cols-2 lg:grid-cols-3">
260+
{EXAMPLE_PROJECTS.filter((project) => project.type === 'app')
261+
.sort((a, b) => a.title.localeCompare(b.title))
262+
.map((project) => (
263+
<ExampleProject key={project.url} {...project} />
264+
))}
265+
</div>
266+
</TabsContent_Shadcn_>
267+
<TabsContent_Shadcn_ value="mobile">
268+
<div className="grid gap-2 md:gap-8 md:grid-cols-2 lg:grid-cols-3">
269+
{EXAMPLE_PROJECTS.filter((project) => project.type === 'mobile')
270+
.sort((a, b) => a.title.localeCompare(b.title))
271+
.map((project) => (
272+
<ExampleProject key={project.url} {...project} />
273+
))}
274+
</div>
275+
</TabsContent_Shadcn_>
276+
</Tabs_Shadcn_>
277+
</div>
278+
)}
279+
</div>
280+
)}
281+
</>
282+
)}
278283
</div>
279-
</>
280-
)}
284+
</div>
285+
</>
281286
</div>
282287
)
283288
}

apps/studio/components/interfaces/HomeNew/TopSection.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,7 @@ export const TopSection = () => {
3737
: 'Welcome to your project'
3838

3939
if (isPaused) {
40-
return (
41-
<div className="w-full">
42-
<div className="mb-8">
43-
{!isMainBranch && (
44-
<Link href={`/project/${parentProject?.ref}`} className="text-sm text-foreground-light">
45-
{parentProject?.name}
46-
</Link>
47-
)}
48-
<h1 className="text-3xl">{projectName}</h1>
49-
</div>
50-
<ProjectPausedState />
51-
</div>
52-
)
40+
return <ProjectPausedState />
5341
}
5442

5543
return (

apps/studio/components/interfaces/ProjectCreation/PostgresVersionSelector.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
1919
import { smartRegionToExactRegion } from './ProjectCreation.utils'
2020

2121
interface PostgresVersionDetails {
22-
postgresEngine: PostgresEngine | undefined
23-
releaseChannel: ReleaseChannel | undefined
22+
postgresEngine?: Exclude<PostgresEngine, '13' | '14'>
23+
releaseChannel?: ReleaseChannel
2424
}
2525

2626
interface PostgresVersionSelectorProps {
2727
cloudProvider: CloudProvider
2828
dbRegion: string
29-
organizationSlug: string | undefined
29+
organizationSlug?: string
3030
field: ControllerRenderProps<any, 'postgresVersionSelection'>
3131
form: UseFormReturn<any>
3232
type?: 'create' | 'unpause'

apps/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert.tsx

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import { z } from 'zod'
1010
import { useFlag, useParams } from 'common'
1111
import { PLAN_DETAILS } from 'components/interfaces/DiskManagement/ui/DiskManagement.constants'
1212
import { Markdown } from 'components/interfaces/Markdown'
13+
import { extractPostgresVersionDetails } from 'components/interfaces/ProjectCreation/PostgresVersionSelector'
1314
import { useDiskAttributesQuery } from 'data/config/disk-attributes-query'
1415
import {
1516
ProjectUpgradeTargetVersion,
1617
useProjectUpgradeEligibilityQuery,
1718
} from 'data/config/project-upgrade-eligibility-query'
18-
import { ReleaseChannel } from 'data/projects/new-project.constants'
1919
import { useSetProjectStatus } from 'data/projects/project-detail-query'
2020
import { useProjectUpgradeMutation } from 'data/projects/project-upgrade-mutation'
2121
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
@@ -43,24 +43,10 @@ import {
4343
import { Admonition } from 'ui-patterns/admonition'
4444
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
4545

46-
interface PostgresVersionDetails {
47-
postgresEngine: string
48-
releaseChannel: ReleaseChannel
49-
}
50-
5146
const formatValue = ({ postgres_version, release_channel }: ProjectUpgradeTargetVersion) => {
5247
return `${postgres_version}|${release_channel}`
5348
}
5449

55-
export const extractPostgresVersionDetails = (value: string): PostgresVersionDetails | null => {
56-
if (!value) {
57-
return null
58-
}
59-
60-
const [postgresEngine, releaseChannel] = value.split('|')
61-
return { postgresEngine, releaseChannel } as PostgresVersionDetails
62-
}
63-
6450
export const ProjectUpgradeAlert = () => {
6551
const router = useRouter()
6652
const { ref } = useParams()
@@ -99,6 +85,7 @@ export const ProjectUpgradeAlert = () => {
9985

10086
const versionDetails = extractPostgresVersionDetails(postgresVersionSelection)
10187
if (!versionDetails) return toast.error('Invalid Postgres version')
88+
if (!versionDetails.postgresEngine) return toast.error('Missing target version')
10289

10390
upgradeProject({
10491
ref,

apps/studio/components/layouts/ProjectLayout/PausedState/PauseDisabledState.tsx

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { toast } from 'sonner'
44

55
import { useParams } from 'common'
66
import { DropdownMenuItemTooltip } from 'components/ui/DropdownMenuItemTooltip'
7+
import { InlineLink } from 'components/ui/InlineLink'
78
import { useBackupDownloadMutation } from 'data/database/backup-download-mutation'
89
import { useProjectPauseStatusQuery } from 'data/projects/project-pause-status-query'
910
import { useStorageArchiveCreateMutation } from 'data/storage/storage-archive-create-mutation'
@@ -12,16 +13,13 @@ import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
1213
import { Database, Storage } from 'icons'
1314
import { DOCS_URL, PROJECT_STATUS } from 'lib/constants'
1415
import {
15-
Alert_Shadcn_,
16-
AlertDescription_Shadcn_,
17-
AlertTitle_Shadcn_,
1816
Button,
1917
DropdownMenu,
2018
DropdownMenuContent,
2119
DropdownMenuItem,
2220
DropdownMenuTrigger,
23-
WarningIcon,
2421
} from 'ui'
22+
import { Admonition } from 'ui-patterns'
2523

2624
export const PauseDisabledState = () => {
2725
const { ref } = useParams()
@@ -116,25 +114,56 @@ export const PauseDisabledState = () => {
116114
}
117115

118116
return (
119-
<Alert_Shadcn_ variant="warning">
120-
<WarningIcon />
121-
<AlertTitle_Shadcn_>Project cannot be restored through the dashboard</AlertTitle_Shadcn_>
122-
<AlertDescription_Shadcn_>
123-
This project has been paused for over{' '}
124-
<span className="text-foreground">
125-
{pauseStatus?.max_days_till_restore_disabled ?? 90} days
126-
</span>{' '}
127-
and cannot be restored through the dashboard. However, your data remains intact and can be
128-
downloaded as a backup.
129-
</AlertDescription_Shadcn_>
130-
<AlertDescription_Shadcn_ className="gap-x-2 mt-3">
117+
<>
118+
<Admonition
119+
showIcon={false}
120+
type="warning"
121+
className="mb-0 rounded-none border-0 px-6"
122+
title="Project can no longer be restored through the dashboard"
123+
>
124+
<p className="!leading-normal !mb-3">
125+
This project has been paused for over{' '}
126+
<span className="text-foreground">
127+
{pauseStatus?.max_days_till_restore_disabled ?? 90} days
128+
</span>{' '}
129+
and cannot be restored through the dashboard. However, your data remains intact and can be
130+
downloaded as a backup.
131+
</p>
132+
133+
<div>
134+
<p className="!leading-normal !mb-1">Recovery options:</p>
135+
<ul className="flex flex-col gap-y-0.5">
136+
<li className="flex items-center gap-x-2">
137+
<ExternalLink size={14} />
138+
<InlineLink
139+
href={`${DOCS_URL}/guides/platform/migrating-within-supabase/dashboard-restore`}
140+
>
141+
Restore the backup to a new Supabase project
142+
</InlineLink>
143+
</li>
144+
<li className="flex items-center gap-x-2">
145+
<ExternalLink size={14} />
146+
<InlineLink href={`${DOCS_URL}/guides/local-development/restoring-downloaded-backup`}>
147+
Restore the backup on your local machine
148+
</InlineLink>
149+
</li>
150+
</ul>
151+
</div>
152+
</Admonition>
153+
<div className="border-t flex justify-between items-center px-6 py-4 bg-alternative">
154+
<div>
155+
<p className="text-sm">Export your data</p>
156+
<p className="text-sm text-foreground-lighter">
157+
Download backups for your database and storage objects
158+
</p>
159+
</div>
131160
<DropdownMenu>
132161
<DropdownMenuTrigger asChild>
133162
<Button type="default" icon={<Download />} iconRight={<ChevronDown />}>
134163
Download backups
135164
</Button>
136165
</DropdownMenuTrigger>
137-
<DropdownMenuContent className="w-56" align="start">
166+
<DropdownMenuContent className="w-60" align="end">
138167
<DropdownMenuItemTooltip
139168
className="gap-x-2"
140169
disabled={!latestBackup}
@@ -160,25 +189,7 @@ export const PauseDisabledState = () => {
160189
</DropdownMenuItem> */}
161190
</DropdownMenuContent>
162191
</DropdownMenu>
163-
<Button asChild type="default" icon={<ExternalLink />} className="my-3">
164-
<a
165-
target="_blank"
166-
rel="noreferrer"
167-
href={`${DOCS_URL}/guides/platform/migrating-within-supabase/dashboard-restore`}
168-
>
169-
Restore backup to a new Supabase project guide
170-
</a>
171-
</Button>
172-
<Button asChild type="default" icon={<ExternalLink />} className="mb-3">
173-
<a
174-
target="_blank"
175-
rel="noreferrer"
176-
href={`${DOCS_URL}/guides/local-development/restoring-downloaded-backup`}
177-
>
178-
Restore backup on your local machine guide
179-
</a>
180-
</Button>
181-
</AlertDescription_Shadcn_>
182-
</Alert_Shadcn_>
192+
</div>
193+
</>
183194
)
184195
}

0 commit comments

Comments
 (0)