mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 18:05:11 +03:00
Merge pull request #6499 from supabase/hi/fix-project-detail-loading
fix: project creation flow
This commit is contained in:
18 files changed
+284
-257
No files matched your search
@@ -1,11 +1,6 @@
|
||||
import { DisplayApiSettings, DisplayConfigSettings } from 'components/ui/ProjectSettings'
|
||||
import { FC } from 'react'
|
||||
import { Typography } from '@supabase/ui'
|
||||
|
||||
import GetStartedPanel from './GetStartedPanel'
|
||||
import {
|
||||
DisplayApiSettings,
|
||||
DisplayConfigSettings,
|
||||
} from 'components/to-be-cleaned/DisplayProjectSettings'
|
||||
|
||||
interface Props {}
|
||||
|
||||
|
||||
+1
-4
@@ -2,10 +2,6 @@ import Link from 'next/link'
|
||||
import { FC, useEffect, useRef } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Badge, IconArrowRight, IconLoader, Button } from '@supabase/ui'
|
||||
import {
|
||||
DisplayApiSettings,
|
||||
DisplayConfigSettings,
|
||||
} from 'components/to-be-cleaned/DisplayProjectSettings'
|
||||
import ExampleProject from 'components/interfaces/Home/ExampleProject'
|
||||
import ClientLibrary from 'components/interfaces/Home/ClientLibrary'
|
||||
import { CLIENT_LIBRARIES, EXAMPLE_PROJECTS } from 'components/interfaces/Home/Home.constants'
|
||||
@@ -14,6 +10,7 @@ import { API_URL, PROJECT_STATUS } from 'lib/constants'
|
||||
import { useStore } from 'hooks'
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { Project } from 'types'
|
||||
import { DisplayApiSettings, DisplayConfigSettings } from 'components/ui/ProjectSettings'
|
||||
|
||||
type ProjectBuildingState = { project: Project }
|
||||
const ProjectBuildingState: FC<ProjectBuildingState> = ({ project }) => {
|
||||
@@ -76,15 +76,14 @@ interface ContentWrapperProps {
|
||||
const ContentWrapper: FC<ContentWrapperProps> = observer(({ isLoading, children }) => {
|
||||
const { ui } = useStore()
|
||||
const router = useRouter()
|
||||
const project = ui.selectedProject
|
||||
const requiresDbConnection: boolean = router.pathname !== '/project/[ref]/settings/general'
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading || project === undefined ? (
|
||||
{isLoading || ui.selectedProject === undefined ? (
|
||||
<Connecting />
|
||||
) : requiresDbConnection ? (
|
||||
<TestConnection project={project!}>
|
||||
<TestConnection project={ui.selectedProject!}>
|
||||
<div className="flex flex-col flex-1 overflow-y-auto">{children}</div>
|
||||
</TestConnection>
|
||||
) : (
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useRouter } from 'next/router'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FC, ReactNode } from 'react'
|
||||
import { Project } from 'types'
|
||||
import BuildingState from 'components/to-be-cleaned/BuildingState'
|
||||
import { PROJECT_STATUS } from 'lib/constants'
|
||||
import BuildingState from './BuildingState'
|
||||
|
||||
interface Props {
|
||||
project: Project
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
import useSWR from 'swr'
|
||||
import { FC } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { Badge, IconAlertCircle, Input, Loading, Typography } from '@supabase/ui'
|
||||
import { JwtSecretUpdateStatus, ProjectEvents } from '@supabase/shared-types/out/events'
|
||||
|
||||
import { useJwtSecretUpdateStatus } from 'hooks'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import Panel from './Panel'
|
||||
|
||||
const { Text, Title } = Typography
|
||||
const API_SERVICE_ID = 1
|
||||
|
||||
export const DisplayApiSettings = () => {
|
||||
const router = useRouter()
|
||||
const { ref } = router.query
|
||||
|
||||
const { data, error }: any = useSWR(`${API_URL}/props/project/${ref}/settings`, get)
|
||||
const {
|
||||
isError: isJwtSecretUpdateStatusError,
|
||||
isLoading: isJwtSecretUpdateStatusLoading,
|
||||
jwtSecretUpdateStatus,
|
||||
}: any = useJwtSecretUpdateStatus(ref)
|
||||
|
||||
const isNotUpdatingJwtSecret =
|
||||
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
|
||||
|
||||
if (!data || isJwtSecretUpdateStatusLoading)
|
||||
return (
|
||||
<ApiContentWrapper>
|
||||
<Panel.Content className="py-8">
|
||||
{error || isJwtSecretUpdateStatusError ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Typography.Text type="secondary">
|
||||
<IconAlertCircle strokeWidth={2} />
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary">Failed to fetch API keys</Typography.Text>
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-4">
|
||||
{/* @ts-ignore */}
|
||||
<Loading active={true} />
|
||||
</div>
|
||||
)}
|
||||
</Panel.Content>
|
||||
</ApiContentWrapper>
|
||||
)
|
||||
|
||||
// Get the API service
|
||||
const apiService = (data?.services ?? []).find((x: any) => x.app.id == API_SERVICE_ID)
|
||||
const apiKeys = apiService?.service_api_keys ?? []
|
||||
|
||||
return (
|
||||
<ApiContentWrapper>
|
||||
{!data || isJwtSecretUpdateStatusLoading ? (
|
||||
// @ts-ignore
|
||||
<Loading active={true} />
|
||||
) : (
|
||||
apiKeys.map((x: any, i: number) => (
|
||||
<Panel.Content
|
||||
key={x.api_key}
|
||||
className={
|
||||
i >= 1 &&
|
||||
'border-t border-panel-border-interior-light dark:border-panel-border-interior-dark'
|
||||
}
|
||||
>
|
||||
<Input
|
||||
layout="horizontal"
|
||||
// @ts-ignore
|
||||
label={
|
||||
<>
|
||||
{x.tags?.split(',').map((x: any, i: number) => (
|
||||
<code key={`${x}${i}`} className="text-xs text-code">
|
||||
{x}
|
||||
</code>
|
||||
))}
|
||||
{x.tags === 'service_role' && (
|
||||
<>
|
||||
<code className="text-xs bg-red-900 text-white">{'secret'}</code>
|
||||
</>
|
||||
)}
|
||||
{x.tags === 'anon' && <code className="text-xs text-code">{'public'}</code>}
|
||||
</>
|
||||
}
|
||||
readOnly
|
||||
copy={isNotUpdatingJwtSecret}
|
||||
className="input-mono"
|
||||
disabled
|
||||
reveal={x.tags !== 'anon' && isNotUpdatingJwtSecret}
|
||||
value={
|
||||
jwtSecretUpdateStatus === JwtSecretUpdateStatus.Failed
|
||||
? 'JWT secret update failed, new API key may have issues'
|
||||
: jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating
|
||||
? 'Updating JWT secret...'
|
||||
: x.api_key
|
||||
}
|
||||
onChange={() => {}}
|
||||
descriptionText={
|
||||
x.tags === 'service_role'
|
||||
? 'This key has the ability to bypass Row Level Security. Never share it publicly.'
|
||||
: 'This key is safe to use in a browser if you have enabled Row Level Security for your tables and configured policies.'
|
||||
}
|
||||
/>
|
||||
</Panel.Content>
|
||||
))
|
||||
)}
|
||||
</ApiContentWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export const DisplayConfigSettings = () => {
|
||||
const router = useRouter()
|
||||
const { ref } = router.query
|
||||
|
||||
const { data, error }: any = useSWR(`${API_URL}/props/project/${ref}/settings`, get)
|
||||
const {
|
||||
isError: isJwtSecretUpdateStatusError,
|
||||
isLoading: isJwtSecretUpdateStatusLoading,
|
||||
jwtSecretUpdateStatus,
|
||||
}: any = useJwtSecretUpdateStatus(ref)
|
||||
|
||||
const isNotUpdatingJwtSecret =
|
||||
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
|
||||
|
||||
if (!data || isJwtSecretUpdateStatusLoading)
|
||||
return (
|
||||
<ConfigContentWrapper>
|
||||
<Panel.Content className="py-8">
|
||||
{error || isJwtSecretUpdateStatusError ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Typography.Text type="secondary">
|
||||
<IconAlertCircle strokeWidth={2} />
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary">
|
||||
Failed to fetch project configuration
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-4">
|
||||
{/* @ts-ignore */}
|
||||
<Loading active={true} />
|
||||
</div>
|
||||
)}
|
||||
</Panel.Content>
|
||||
</ConfigContentWrapper>
|
||||
)
|
||||
|
||||
// Get the API service
|
||||
const jwtSecret = data?.project.jwt_secret ?? ''
|
||||
const apiService = (data?.services ?? []).find((x: any) => x.app.id == API_SERVICE_ID)
|
||||
const apiConfig = apiService.app_config
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Panel
|
||||
title={
|
||||
<Typography.Title level={5} className="mb-0">
|
||||
Project Configuration
|
||||
</Typography.Title>
|
||||
}
|
||||
>
|
||||
<Panel.Content>
|
||||
<Input
|
||||
label="URL"
|
||||
readOnly
|
||||
copy
|
||||
disabled
|
||||
className="input-mono"
|
||||
value={`https://${apiConfig.endpoint}`}
|
||||
descriptionText="A RESTful endpoint for querying and managing your database."
|
||||
layout="horizontal"
|
||||
/>
|
||||
</Panel.Content>
|
||||
<Panel.Content className="border-t border-panel-border-interior-light dark:border-panel-border-interior-dark">
|
||||
<Input
|
||||
label="JWT Secret"
|
||||
readOnly
|
||||
copy={isNotUpdatingJwtSecret}
|
||||
reveal={isNotUpdatingJwtSecret}
|
||||
disabled
|
||||
value={
|
||||
jwtSecretUpdateStatus === JwtSecretUpdateStatus.Failed
|
||||
? 'JWT secret update failed'
|
||||
: jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating
|
||||
? 'Updating JWT secret...'
|
||||
: jwtSecret
|
||||
}
|
||||
className="input-mono"
|
||||
descriptionText="Used to decode your JWTs. You can also use this to mint your own JWTs."
|
||||
layout="horizontal"
|
||||
/>
|
||||
</Panel.Content>
|
||||
</Panel>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
const ApiContentWrapper: FC<any> = ({ children }) => {
|
||||
return (
|
||||
<Panel
|
||||
title={
|
||||
<div className="space-y-3">
|
||||
<h5 className="text-base">Project API keys</h5>
|
||||
<p className="text-sm text-scale-1000">
|
||||
Your API is secured behind an API gateway which requires an API Key for every request.
|
||||
<br />
|
||||
You can use the keys below to use Supabase client libraries.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
|
||||
const ConfigContentWrapper: FC<any> = ({ children }) => {
|
||||
return (
|
||||
<Panel
|
||||
title={
|
||||
<div className="space-y-3">
|
||||
<h5 className="text-base">Project Configuration</h5>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
@@ -22,7 +22,7 @@ function Panel(props: any) {
|
||||
className={`
|
||||
border border-panel-border-light dark:border-panel-border-dark
|
||||
shadow-sm overflow-hidden
|
||||
rounded rounded-md ${props.noMargin ? '' : 'mb-8'} ${props.className}`}
|
||||
rounded-md ${props.noMargin ? '' : 'mb-8'} ${props.className}`}
|
||||
>
|
||||
{props.title && (
|
||||
<div className={headerClasses.join(' ')}>
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import { FC } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Input } from '@supabase/ui'
|
||||
import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
|
||||
import { useJwtSecretUpdateStatus, useProjectSettings } from 'hooks'
|
||||
import { SettingsLoadingState } from './SettingsLoadingState'
|
||||
import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants'
|
||||
import Panel from 'components/to-be-cleaned/Panel'
|
||||
|
||||
const DisplayApiSettings = () => {
|
||||
const router = useRouter()
|
||||
const { ref } = router.query
|
||||
const {
|
||||
services,
|
||||
isLoading: isProjectSettingsLoading,
|
||||
isError: isProjectSettingsError,
|
||||
} = useProjectSettings(ref as string | undefined)
|
||||
const {
|
||||
isError: isJwtSecretUpdateStatusError,
|
||||
isLoading: isJwtSecretUpdateStatusLoading,
|
||||
jwtSecretUpdateStatus,
|
||||
}: any = useJwtSecretUpdateStatus(ref)
|
||||
|
||||
const isNotUpdatingJwtSecret =
|
||||
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
|
||||
// Get the API service
|
||||
const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID)
|
||||
const apiKeys = apiService?.service_api_keys ?? []
|
||||
// api keys should not be empty. However it can be populated with a delay on project creation
|
||||
const isApikeysEmpty = apiKeys.length === 0
|
||||
|
||||
return (
|
||||
<ApiContentWrapper>
|
||||
{isProjectSettingsLoading || isJwtSecretUpdateStatusLoading || isApikeysEmpty ? (
|
||||
<SettingsLoadingState
|
||||
isError={isProjectSettingsError || isJwtSecretUpdateStatusError}
|
||||
errorMessage="Failed to fetch API keys"
|
||||
/>
|
||||
) : (
|
||||
apiKeys.map((x: any, i: number) => (
|
||||
<Panel.Content
|
||||
key={x.api_key}
|
||||
className={
|
||||
i >= 1 &&
|
||||
'border-t border-panel-border-interior-light dark:border-panel-border-interior-dark'
|
||||
}
|
||||
>
|
||||
<Input
|
||||
layout="horizontal"
|
||||
// @ts-ignore
|
||||
label={
|
||||
<>
|
||||
{x.tags?.split(',').map((x: any, i: number) => (
|
||||
<code key={`${x}${i}`} className="text-xs text-code">
|
||||
{x}
|
||||
</code>
|
||||
))}
|
||||
{x.tags === 'service_role' && (
|
||||
<>
|
||||
<code className="text-xs bg-red-900 text-white">{'secret'}</code>
|
||||
</>
|
||||
)}
|
||||
{x.tags === 'anon' && <code className="text-xs text-code">{'public'}</code>}
|
||||
</>
|
||||
}
|
||||
readOnly
|
||||
copy={isNotUpdatingJwtSecret}
|
||||
className="input-mono"
|
||||
disabled
|
||||
reveal={x.tags !== 'anon' && isNotUpdatingJwtSecret}
|
||||
value={
|
||||
jwtSecretUpdateStatus === JwtSecretUpdateStatus.Failed
|
||||
? 'JWT secret update failed, new API key may have issues'
|
||||
: jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating
|
||||
? 'Updating JWT secret...'
|
||||
: x.api_key
|
||||
}
|
||||
onChange={() => {}}
|
||||
descriptionText={
|
||||
x.tags === 'service_role'
|
||||
? 'This key has the ability to bypass Row Level Security. Never share it publicly.'
|
||||
: 'This key is safe to use in a browser if you have enabled Row Level Security for your tables and configured policies.'
|
||||
}
|
||||
/>
|
||||
</Panel.Content>
|
||||
))
|
||||
)}
|
||||
</ApiContentWrapper>
|
||||
)
|
||||
}
|
||||
export default DisplayApiSettings
|
||||
|
||||
const ApiContentWrapper: FC = ({ children }) => {
|
||||
return (
|
||||
<Panel
|
||||
title={
|
||||
<div className="space-y-3">
|
||||
<h5 className="text-base">Project API keys</h5>
|
||||
<p className="text-sm text-scale-1000">
|
||||
Your API is secured behind an API gateway which requires an API Key for every request.
|
||||
<br />
|
||||
You can use the keys below to use Supabase client libraries.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import { FC } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Input } from '@supabase/ui'
|
||||
import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
|
||||
import { useJwtSecretUpdateStatus, useProjectSettings } from 'hooks'
|
||||
import { SettingsLoadingState } from './SettingsLoadingState'
|
||||
import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants'
|
||||
import Panel from 'components/to-be-cleaned/Panel'
|
||||
|
||||
const DisplayConfigSettings = () => {
|
||||
const router = useRouter()
|
||||
const { ref } = router.query
|
||||
const {
|
||||
project,
|
||||
services,
|
||||
isLoading: isProjectSettingsLoading,
|
||||
isError: isProjectSettingsError,
|
||||
} = useProjectSettings(ref as string | undefined)
|
||||
const {
|
||||
isError: isJwtSecretUpdateStatusError,
|
||||
isLoading: isJwtSecretUpdateStatusLoading,
|
||||
jwtSecretUpdateStatus,
|
||||
}: any = useJwtSecretUpdateStatus(ref)
|
||||
|
||||
const isNotUpdatingJwtSecret =
|
||||
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
|
||||
// Get the API service
|
||||
const jwtSecret = project?.jwt_secret ?? ''
|
||||
const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID)
|
||||
const apiConfig = apiService?.app_config ?? {}
|
||||
|
||||
return (
|
||||
<ConfigContentWrapper>
|
||||
{isProjectSettingsLoading || isJwtSecretUpdateStatusLoading ? (
|
||||
<SettingsLoadingState
|
||||
isError={isProjectSettingsError || isJwtSecretUpdateStatusError}
|
||||
errorMessage="Failed to fetch project configuration"
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Panel.Content>
|
||||
<Input
|
||||
label="URL"
|
||||
readOnly
|
||||
copy
|
||||
disabled
|
||||
className="input-mono"
|
||||
value={`https://${apiConfig.endpoint}`}
|
||||
descriptionText="A RESTful endpoint for querying and managing your database."
|
||||
layout="horizontal"
|
||||
/>
|
||||
</Panel.Content>
|
||||
<Panel.Content className="border-t border-panel-border-interior-light dark:border-panel-border-interior-dark">
|
||||
<Input
|
||||
label="JWT Secret"
|
||||
readOnly
|
||||
copy={isNotUpdatingJwtSecret}
|
||||
reveal={isNotUpdatingJwtSecret}
|
||||
disabled
|
||||
value={
|
||||
jwtSecretUpdateStatus === JwtSecretUpdateStatus.Failed
|
||||
? 'JWT secret update failed'
|
||||
: jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating
|
||||
? 'Updating JWT secret...'
|
||||
: jwtSecret
|
||||
}
|
||||
className="input-mono"
|
||||
descriptionText="Used to decode your JWTs. You can also use this to mint your own JWTs."
|
||||
layout="horizontal"
|
||||
/>
|
||||
</Panel.Content>
|
||||
</>
|
||||
)}
|
||||
</ConfigContentWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default DisplayConfigSettings
|
||||
|
||||
const ConfigContentWrapper: FC = ({ children }) => {
|
||||
return (
|
||||
<Panel
|
||||
title={
|
||||
<div className="space-y-3">
|
||||
<h5 className="text-base">Project Configuration</h5>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { FC } from 'react'
|
||||
import { IconAlertCircle, Loading, Typography } from '@supabase/ui'
|
||||
import { SettingsLoadingStateProps } from './SettingsLoadingState.types'
|
||||
import Panel from 'components/to-be-cleaned/Panel'
|
||||
|
||||
export const SettingsLoadingState: FC<SettingsLoadingStateProps> = ({ isError, errorMessage }) => {
|
||||
return (
|
||||
<Panel.Content className="py-8">
|
||||
{isError ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Typography.Text type="secondary">
|
||||
<IconAlertCircle strokeWidth={2} />
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary">{errorMessage}</Typography.Text>
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-4">
|
||||
{/* @ts-ignore */}
|
||||
<Loading active={true} />
|
||||
</div>
|
||||
)}
|
||||
</Panel.Content>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface SettingsLoadingStateProps {
|
||||
isError: boolean
|
||||
errorMessage: string
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import DisplayApiSettings from './DisplayApiSettings'
|
||||
import DisplayConfigSettings from './DisplayConfigSettings'
|
||||
|
||||
export { DisplayApiSettings, DisplayConfigSettings }
|
||||
@@ -1,10 +1,8 @@
|
||||
import { useProfile, useStore } from 'hooks'
|
||||
import { ComponentType, useEffect, useState } from 'react'
|
||||
import { flatten, isUndefined } from 'lodash'
|
||||
import { NextRouter, useRouter } from 'next/router'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import Connecting from 'components/ui/Loading'
|
||||
import { Project } from 'types'
|
||||
|
||||
const PLATFORM_ONLY_PAGES = ['storage', 'reports', 'settings']
|
||||
|
||||
@@ -79,7 +77,7 @@ export function withAuth(
|
||||
}
|
||||
|
||||
function defaultRedirectTo(ref: string | string[] | undefined) {
|
||||
return IS_PLATFORM ? '/' : !isUndefined(ref) ? `/project/${ref}` : '/'
|
||||
return IS_PLATFORM ? '/' : ref !== undefined ? `/project/${ref}` : '/'
|
||||
}
|
||||
|
||||
function checkRedirectTo(
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { API_URL, DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants'
|
||||
import useSWR from 'swr'
|
||||
|
||||
export function useProjectSettings(ref?: string) {
|
||||
const url = `${API_URL}/props/project/${ref}/settings`
|
||||
const { data, error } = useSWR<any>(ref ? url : null, get)
|
||||
const { data, error } = useSWR<any>(ref ? url : null, get, {
|
||||
/**
|
||||
* on project creation, the service_api_keys will be populated with a delay
|
||||
* check for data.services.service_api_keys, return a valid refresh interval if it's empty
|
||||
*/
|
||||
refreshInterval: function (data: any) {
|
||||
const apiService = (data?.services ?? []).find(
|
||||
(x: any) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID
|
||||
)
|
||||
const apiKeys = apiService?.service_api_keys ?? []
|
||||
const interval = apiKeys.length === 0 ? 2000 : 0
|
||||
return interval
|
||||
},
|
||||
})
|
||||
const anyError = data?.error || error
|
||||
|
||||
return {
|
||||
|
||||
@@ -140,3 +140,5 @@ export const PASSWORD_STRENGTH_PERCENTAGE = {
|
||||
export const DEFAULT_FREE_PROJECTS_LIMIT = 2
|
||||
|
||||
export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES = 5 * 1024 * 1024 * 1024 // 5 GB
|
||||
|
||||
export const DEFAULT_PROJECT_API_SERVICE_ID = 1
|
||||
@@ -188,9 +188,8 @@ export const Wizard = observer(() => {
|
||||
message: `Failed to create new project: ${response.error.message}`,
|
||||
})
|
||||
} else {
|
||||
// Use redirect to reload store data properly
|
||||
// after creating a new project
|
||||
window.location.replace(`/project/${response.ref}/building`)
|
||||
app.onProjectCreated(response)
|
||||
router.push(`/project/${response.ref}/building`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useStore, withAuth } from 'hooks'
|
||||
import { ExampleProject, ClientLibrary } from 'components/interfaces/Home'
|
||||
import { CLIENT_LIBRARIES, EXAMPLE_PROJECTS } from 'components/interfaces/Home/Home.constants'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import BaseLayout from 'components/layouts'
|
||||
import ProjectLayout from 'components/layouts'
|
||||
import ProjectUsageSection from 'components/interfaces/Home/ProjectUsageSection'
|
||||
|
||||
const Home: NextPage = () => {
|
||||
@@ -18,7 +18,7 @@ const Home: NextPage = () => {
|
||||
: 'Welcome to your project'
|
||||
|
||||
return (
|
||||
<BaseLayout>
|
||||
<ProjectLayout>
|
||||
<div className="max-w-7xl mx-auto w-full my-16 space-y-16">
|
||||
<div className="mx-6 flex space-x-6 items-center">
|
||||
<h1 className="text-3xl">{projectName}</h1>
|
||||
@@ -45,7 +45,7 @@ const Home: NextPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
</ProjectLayout>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog'
|
||||
import Panel from 'components/to-be-cleaned/Panel'
|
||||
import MultiSelectUI from 'components/to-be-cleaned/MultiSelect'
|
||||
import SchemaFormPanel from 'components/to-be-cleaned/forms/SchemaFormPanel'
|
||||
import { DisplayApiSettings } from 'components/to-be-cleaned/DisplayProjectSettings'
|
||||
import { DisplayApiSettings } from 'components/ui/ProjectSettings'
|
||||
|
||||
const JWT_SECRET_UPDATE_ERROR_MESSAGES = {
|
||||
[JwtSecretUpdateError.APIServicesConfigurationUpdateFailed]:
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface IAppStore {
|
||||
projects: IProjectStore
|
||||
organizations: OrganizationStore
|
||||
database: IDatabaseStore
|
||||
onProjectCreated: (project: any) => void
|
||||
onProjectUpdated: (project: any) => void
|
||||
onProjectDeleted: (project: any) => void
|
||||
onProjectConnectionStringUpdated: (projectId: number, value: string) => void
|
||||
@@ -39,6 +40,25 @@ export default class AppStore implements IAppStore {
|
||||
this.database = new DatabaseStore(rootStore, `${this.baseUrl}/database`, headers)
|
||||
}
|
||||
|
||||
onProjectCreated(project: any) {
|
||||
if (project && project.id) {
|
||||
const temp: Project = {
|
||||
id: project.id,
|
||||
ref: project.ref,
|
||||
name: project.name,
|
||||
status: project.status,
|
||||
organization_id: project.organization_id,
|
||||
cloud_provider: project.cloud_provider,
|
||||
region: project.region,
|
||||
inserted_at: project.inserted_at,
|
||||
subscription_id: project.subscription_id,
|
||||
kpsVersion: undefined,
|
||||
connectionString: undefined,
|
||||
}
|
||||
this.projects.data[project.id] = temp
|
||||
}
|
||||
}
|
||||
|
||||
onProjectUpdated(project: any) {
|
||||
if (project && project.id) {
|
||||
const clone = cloneDeep(this.projects.data[project.id])
|
||||
|
||||
Reference in new issue
Block a user