mirror of
https://github.com/supabase/supabase.git
synced 2026-10-14 21:59:16 +03:00
Improve empty state
This commit is contained in:
1 parent
d99b672b8d
commit
424d6ec2ac
4 files changed
+188
-48
No files matched your search
@@ -9,6 +9,7 @@ import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { DatabaseMigration, useMigrationsQuery } from 'data/database/migrations-query'
|
||||
import { Editor } from '@monaco-editor/react'
|
||||
import CodeEditor from 'components/ui/CodeEditor'
|
||||
import MigrationsEmptyState from './MigrationsEmptyState'
|
||||
|
||||
const Wrappers = () => {
|
||||
const [search, setSearch] = useState('')
|
||||
@@ -24,6 +25,8 @@ const Wrappers = () => {
|
||||
? data?.result ?? []
|
||||
: data?.result.filter((migration) => migration.version.includes(search)) ?? []
|
||||
|
||||
console.log({ data })
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
@@ -80,51 +83,52 @@ const Wrappers = () => {
|
||||
)}
|
||||
{isSuccess && (
|
||||
<div>
|
||||
<div className="w-80 mb-4">
|
||||
<Input
|
||||
size="small"
|
||||
placeholder="Search for a migration"
|
||||
value={search}
|
||||
onChange={(e: any) => setSearch(e.target.value)}
|
||||
icon={<IconSearch size="tiny" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{data.result.length <= 0 && <div>No migrations state...</div>}
|
||||
{data.result.length <= 0 && <MigrationsEmptyState />}
|
||||
|
||||
{data.result.length > 0 && (
|
||||
<Table
|
||||
head={[
|
||||
<Table.th key="version">Version</Table.th>,
|
||||
<Table.th key="buttons"></Table.th>,
|
||||
]}
|
||||
body={
|
||||
migrations.length > 0 ? (
|
||||
migrations.map((migration) => (
|
||||
<Table.tr key={migration.version}>
|
||||
<Table.td>{migration.version}</Table.td>
|
||||
<Table.td align="right">
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => setSelectedMigration(migration)}
|
||||
>
|
||||
View migration SQL
|
||||
</Button>
|
||||
<>
|
||||
<div className="w-80 mb-4">
|
||||
<Input
|
||||
size="small"
|
||||
placeholder="Search for a migration"
|
||||
value={search}
|
||||
onChange={(e: any) => setSearch(e.target.value)}
|
||||
icon={<IconSearch size="tiny" />}
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
head={[
|
||||
<Table.th key="version">Version</Table.th>,
|
||||
<Table.th key="buttons"></Table.th>,
|
||||
]}
|
||||
body={
|
||||
migrations.length > 0 ? (
|
||||
migrations.map((migration) => (
|
||||
<Table.tr key={migration.version}>
|
||||
<Table.td>{migration.version}</Table.td>
|
||||
<Table.td align="right">
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => setSelectedMigration(migration)}
|
||||
>
|
||||
View migration SQL
|
||||
</Button>
|
||||
</Table.td>
|
||||
</Table.tr>
|
||||
))
|
||||
) : (
|
||||
<Table.tr>
|
||||
<Table.td>
|
||||
<p className="text-sm text-scale-1200">No results found</p>
|
||||
<p className="text-sm text-scale-1100">
|
||||
Your search for "{search}" did not return any results
|
||||
</p>
|
||||
</Table.td>
|
||||
</Table.tr>
|
||||
))
|
||||
) : (
|
||||
<Table.tr>
|
||||
<Table.td>
|
||||
<p className="text-sm text-scale-1200">No results found</p>
|
||||
<p className="text-sm text-scale-1100">
|
||||
Your search for "{search}" did not return any results
|
||||
</p>
|
||||
</Table.td>
|
||||
</Table.tr>
|
||||
)
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import clsx from 'clsx'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
Collapsible,
|
||||
IconExternalLink,
|
||||
IconMaximize2,
|
||||
IconMinimize2,
|
||||
IconTerminal,
|
||||
} from 'ui'
|
||||
|
||||
import CommandRender from 'components/interfaces/Functions/CommandRender'
|
||||
import { useParams } from 'common'
|
||||
|
||||
const MigrationsEmptyState = () => {
|
||||
const { ref } = useParams()
|
||||
const [showInstructions, setShowInstructions] = useState(false)
|
||||
|
||||
const commands = [
|
||||
{
|
||||
comment: 'Link your project',
|
||||
command: `supabase link --project-ref ${ref}`,
|
||||
jsx: () => {
|
||||
return (
|
||||
<>
|
||||
<span className="text-brand-1100">supabase</span> link --project-ref {ref}
|
||||
</>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: 'Create a new migration called "new-migration"',
|
||||
command: `supabase migration new new-migration`,
|
||||
jsx: () => {
|
||||
return (
|
||||
<>
|
||||
<span className="text-brand-1100">supabase</span> migration new new-migration
|
||||
</>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
comment: 'Run all migrations against this project',
|
||||
command: `supabase db push`,
|
||||
jsx: () => {
|
||||
return (
|
||||
<>
|
||||
<span className="text-brand-1100">supabase</span> db push
|
||||
</>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="w-full px-12 py-12 bg-white bg-no-repeat border rounded dark:bg-scale-200 border-scale-500">
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-2 w-4/5">
|
||||
<h4 className="text-lg">Database migrations</h4>
|
||||
<p className="text-sm text-scale-1100">
|
||||
Database changes are managed through "migrations" and they are a common way of tracking
|
||||
changes to your database over time. You can run migrations against your project's
|
||||
database via the Supabase CLI.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Collapsible open={showInstructions} onOpenChange={setShowInstructions}>
|
||||
<Collapsible.Trigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className={clsx(
|
||||
'flex w-full items-center justify-between rounded py-3 px-6 text-scale-1200 border bg-scale-100 dark:bg-scale-300',
|
||||
showInstructions && 'rounded-b-none'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3 w-full">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center justify-center w-8 h-8 p-2 border rounded bg-scale-100">
|
||||
<IconTerminal strokeWidth={2} />
|
||||
</div>
|
||||
<h4>Terminal instructions</h4>
|
||||
</div>
|
||||
{showInstructions ? (
|
||||
<IconMinimize2
|
||||
className="text-scale-800 transition data-open-parent:rotate-0 data-closed-parent:rotate-180"
|
||||
strokeWidth={2}
|
||||
width={14}
|
||||
/>
|
||||
) : (
|
||||
<IconMaximize2
|
||||
className="text-scale-800 transition data-open-parent:rotate-0 data-closed-parent:rotate-180"
|
||||
strokeWidth={2}
|
||||
width={14}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
<div className="border border-t-0 bg-scale-100 dark:bg-scale-300 rounded-b py-3 px-6">
|
||||
<CommandRender commands={commands} />
|
||||
</div>
|
||||
</Collapsible.Content>
|
||||
</Collapsible>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MigrationsEmptyState
|
||||
@@ -36,6 +36,16 @@ export const useMigrationsQuery = <TData extends MigrationsData = MigrationsData
|
||||
connectionString,
|
||||
sql: getMigrationsQuery(),
|
||||
queryKey: ['migrations'],
|
||||
handleError: (error: { code: number; message: string; requestId: string }) => {
|
||||
if (
|
||||
error.code === 400 &&
|
||||
error.message.includes('relation "supabase_migrations.schema_migrations" does not exist')
|
||||
) {
|
||||
return { result: [] }
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
},
|
||||
},
|
||||
options
|
||||
)
|
||||
|
||||
@@ -10,12 +10,16 @@ import { post } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { useCallback } from 'react'
|
||||
import { sqlKeys } from './keys'
|
||||
import { noop } from 'lodash'
|
||||
|
||||
export type Error = { code: number; message: string; requestId: string }
|
||||
|
||||
export type ExecuteSqlVariables = {
|
||||
projectRef?: string
|
||||
connectionString?: string
|
||||
sql: string
|
||||
queryKey?: QueryKey
|
||||
handleError?: (error: { code: number; message: string; requestId: string }) => any
|
||||
}
|
||||
|
||||
export async function executeSql(
|
||||
@@ -24,7 +28,11 @@ export async function executeSql(
|
||||
connectionString,
|
||||
sql,
|
||||
queryKey,
|
||||
}: Pick<ExecuteSqlVariables, 'projectRef' | 'connectionString' | 'sql' | 'queryKey'>,
|
||||
handleError,
|
||||
}: Pick<
|
||||
ExecuteSqlVariables,
|
||||
'projectRef' | 'connectionString' | 'sql' | 'queryKey' | 'handleError'
|
||||
>,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
if (!projectRef) {
|
||||
@@ -44,8 +52,13 @@ export async function executeSql(
|
||||
{ query: sql },
|
||||
{ headers: Object.fromEntries(headers), signal }
|
||||
)
|
||||
|
||||
if (response.error) {
|
||||
throw response.error
|
||||
if (handleError !== undefined) {
|
||||
return handleError(response.error)
|
||||
} else {
|
||||
throw response.error
|
||||
}
|
||||
}
|
||||
|
||||
return { result: response }
|
||||
@@ -55,21 +68,22 @@ export type ExecuteSqlData = Awaited<ReturnType<typeof executeSql>>
|
||||
export type ExecuteSqlError = unknown
|
||||
|
||||
export const useExecuteSqlQuery = <TData = ExecuteSqlData>(
|
||||
{ projectRef, connectionString, sql, queryKey }: ExecuteSqlVariables,
|
||||
{ projectRef, connectionString, sql, queryKey, handleError }: ExecuteSqlVariables,
|
||||
{ enabled = true, ...options }: UseQueryOptions<ExecuteSqlData, ExecuteSqlError, TData> = {}
|
||||
) =>
|
||||
useQuery<ExecuteSqlData, ExecuteSqlError, TData>(
|
||||
sqlKeys.query(projectRef, queryKey ?? [md5(sql)]),
|
||||
({ signal }) => executeSql({ projectRef, connectionString, sql, queryKey }, signal),
|
||||
({ signal }) =>
|
||||
executeSql({ projectRef, connectionString, sql, queryKey, handleError }, signal),
|
||||
{ enabled: enabled && typeof projectRef !== 'undefined', ...options }
|
||||
)
|
||||
|
||||
export const prefetchExecuteSql = (
|
||||
client: QueryClient,
|
||||
{ projectRef, connectionString, sql, queryKey }: ExecuteSqlVariables
|
||||
{ projectRef, connectionString, sql, queryKey, handleError }: ExecuteSqlVariables
|
||||
) => {
|
||||
return client.prefetchQuery(sqlKeys.query(projectRef, queryKey ?? [md5(sql)]), ({ signal }) =>
|
||||
executeSql({ projectRef, connectionString, sql, queryKey }, signal)
|
||||
executeSql({ projectRef, connectionString, sql, queryKey, handleError }, signal)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -89,13 +103,14 @@ export const useExecuteSqlPrefetch = () => {
|
||||
const client = useQueryClient()
|
||||
|
||||
return useCallback(
|
||||
({ projectRef, connectionString, sql, queryKey }: ExecuteSqlVariables) => {
|
||||
({ projectRef, connectionString, sql, queryKey, handleError }: ExecuteSqlVariables) => {
|
||||
if (projectRef) {
|
||||
return prefetchExecuteSql(client, {
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql,
|
||||
queryKey,
|
||||
handleError,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user