From d6861e04d7d53394d57262b926c98f1a5689e5ca Mon Sep 17 00:00:00 2001 From: phamhieu Date: Fri, 25 Feb 2022 11:54:20 +0700 Subject: [PATCH 01/38] feat: use new organizations and projects api to improve loading performance --- .../interfaces/App/RouteValidationWrapper.tsx | 51 +++-- studio/hooks/misc/withAuth.tsx | 13 +- studio/pages/new/[slug].tsx | 215 +++++++++--------- studio/stores/common/PostgresMetaInterface.ts | 2 +- 4 files changed, 144 insertions(+), 137 deletions(-) diff --git a/studio/components/interfaces/App/RouteValidationWrapper.tsx b/studio/components/interfaces/App/RouteValidationWrapper.tsx index ac97fd6a19e..5dbc8420ccc 100644 --- a/studio/components/interfaces/App/RouteValidationWrapper.tsx +++ b/studio/components/interfaces/App/RouteValidationWrapper.tsx @@ -8,41 +8,42 @@ import { Organization, Project } from 'types' // Ideally these could all be within a _middleware when we use Next 12 const RouteValidationWrapper: FC = ({ children }) => { const { ui, app } = useStore() - const userProfile = ui.profile + const orgsInitialized = app.organizations.isInitialized + const projectsInitialized = app.projects.isInitialized const router = useRouter() const projectRef = router.query.ref const orgSlug = router.query.slug useEffect(() => { - if (userProfile) { + if (orgsInitialized && orgSlug) { // Check validity of organization that user is trying to access - if (orgSlug) { - const organizations = app.organizations.list() - const organizationSlugs = organizations.map((org: Organization) => org.slug) - const isValidOrg = organizationSlugs.includes(orgSlug as string) + const organizations = app.organizations.list() + const organizationSlugs = organizations.map((org: Organization) => org.slug) + const isValidOrg = organizationSlugs.includes(orgSlug as string) - if (!isValidOrg) { - ui.setNotification({ category: 'error', message: 'This organization does not exist' }) - router.push('/') - return - } - } - - // Check validity of project that the user is trying to access - if (projectRef) { - const projects = app.projects.list() - const projectRefs = projects.map((project: Project) => project.ref) - const isValidProject = projectRefs.includes(projectRef as string) - - if (!isValidProject) { - ui.setNotification({ category: 'error', message: 'This project does not exist' }) - router.push('/') - return - } + if (!isValidOrg) { + ui.setNotification({ category: 'error', message: 'This organization does not exist' }) + router.push('/') + return } } - }, [userProfile]) + }, [orgsInitialized]) + + useEffect(() => { + if (projectsInitialized && projectRef) { + // Check validity of project that the user is trying to access + const projects = app.projects.list() + const projectRefs = projects.map((project: Project) => project.ref) + const isValidProject = projectRefs.includes(projectRef as string) + + if (!isValidProject) { + ui.setNotification({ category: 'error', message: 'This project does not exist' }) + router.push('/') + return + } + } + }, [projectsInitialized]) return <>{children} } diff --git a/studio/hooks/misc/withAuth.tsx b/studio/hooks/misc/withAuth.tsx index 680c4c34cb1..1ebbe3ef816 100644 --- a/studio/hooks/misc/withAuth.tsx +++ b/studio/hooks/misc/withAuth.tsx @@ -42,11 +42,14 @@ export function withAuth( if (!profile) { ui.setProfile(undefined) } else if (returning !== 'minimal') { - const { organizations, ...userProfile } = profile - const projects: Project[] = flatten(organizations?.map((org: any) => org.projects)) - app.organizations.initialDataArray(organizations) - app.projects.initialDataArray(projects) - ui.setProfile(userProfile) + ui.setProfile(profile) + + if (!app.organizations.isInitialized) { + app.organizations.load() + } + if (!app.projects.isInitialized) { + app.projects.load() + } } } diff --git a/studio/pages/new/[slug].tsx b/studio/pages/new/[slug].tsx index 56a6e08074c..bc0ed031068 100644 --- a/studio/pages/new/[slug].tsx +++ b/studio/pages/new/[slug].tsx @@ -80,7 +80,8 @@ export const Wizard = observer(() => { const totalFreeProjects = subscriptionStats.total_free_projects const freeProjectsLimit = ui.profile?.free_project_limit ?? DEFAULT_FREE_PROJECTS_LIMIT - const isEmptyOrganizations = organizations.length <= 0 + const isOrganizationOwner = currentOrg?.is_owner || !app.organizations.isInitialized + const isEmptyOrganizations = organizations.length <= 0 && app.organizations.isInitialized const isEmptyPaymentMethod = stripeCustomer ? stripeCustomer.paymentMethods?.data?.length <= 0 : undefined @@ -191,6 +192,7 @@ export const Wizard = observer(() => { @@ -218,126 +220,127 @@ export const Wizard = observer(() => { } > - <> - - - Your project will have its own dedicated instance and full postgres database. -
- An API will be set up so you can easily interact with your new database. -
-
-
- + + + Your project will have its own dedicated instance and full postgres database. +
+ An API will be set up so you can easily interact with your new database. +
+
+
+ + {/* Listbox will throw error on empty list box item. We need to add a check */} + {organizations.length > 0 && ( router.push(`/new/${slug}`)} > - {organizations.map((x: any) => ( + {organizations?.map((x: any) => ( } > - {x.name} + {x?.name} + + ))} + + )} + + {!isOrganizationOwner && } + + + {canCreateProject && ( + <> + + + + + + + } + errorMessage={passwordStrengthWarning} + /> + + + + + + + )} + + {currentOrg?.is_owner && ( + + + Select a plan that suits your needs.  + + More details + + + } + > + {Object.entries(PRICING_PLANS).map(([k, v]) => ( + + {`${v}${v === PRICING_PLANS.PRO ? ' - $25/month' : ''}`} ))} - {!currentOrg?.is_owner && } + {isSelectFreeTier && isOverFreeProjectLimit && ( + + )} + {!isSelectFreeTier && isEmptyPaymentMethod && ( + + )} + )} - {canCreateProject && ( - <> - - - - - - - } - errorMessage={passwordStrengthWarning} - /> - - - - - - - )} - - {currentOrg?.is_owner && ( - - - Select a plan that suits your needs.  - - More details - - - } - > - {Object.entries(PRICING_PLANS).map(([k, v]) => ( - - {`${v}${v === PRICING_PLANS.PRO ? ' - $25/month' : ''}`} - - ))} - - - {isSelectFreeTier && isOverFreeProjectLimit && ( - - )} - {!isSelectFreeTier && isEmptyPaymentMethod && ( - - )} - - )} - - {subscriptionStats.isLoading && ( - -
- {/* @ts-ignore */} - -
-
- )} - + {subscriptionStats.isLoading && ( + +
+ {/* @ts-ignore */} + +
+
+ )}
) diff --git a/studio/stores/common/PostgresMetaInterface.ts b/studio/stores/common/PostgresMetaInterface.ts index 3314521b081..343824ad984 100644 --- a/studio/stores/common/PostgresMetaInterface.ts +++ b/studio/stores/common/PostgresMetaInterface.ts @@ -87,7 +87,7 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface< } get isInitialized() { - return this.state !== this.STATES.INITIAL + return this.state === this.STATES.LOADED || this.state === this.STATES.ERROR } async fetchData() { From bd8621cc5a40583b6d2036882d9a2157f509cd0d Mon Sep 17 00:00:00 2001 From: phamhieu Date: Fri, 25 Feb 2022 12:00:11 +0700 Subject: [PATCH 02/38] chore: clean up --- studio/stores/app/AppStore.ts | 2 +- studio/stores/app/ProjectStore.ts | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/studio/stores/app/AppStore.ts b/studio/stores/app/AppStore.ts index 235cdda2682..4146516ddc2 100644 --- a/studio/stores/app/AppStore.ts +++ b/studio/stores/app/AppStore.ts @@ -2,7 +2,7 @@ import { cloneDeep } from 'lodash' import { values } from 'mobx' import { Organization, Project } from 'types' -import { API_URL, STRIPE_PRODUCT_IDS } from 'lib/constants' +import { API_URL } from 'lib/constants' import { IRootStore } from '../RootStore' import DatabaseStore, { IDatabaseStore } from './DatabaseStore' import OrganizationStore from './OrganizationStore' diff --git a/studio/stores/app/ProjectStore.ts b/studio/stores/app/ProjectStore.ts index 80dfd492a80..0168c004e66 100644 --- a/studio/stores/app/ProjectStore.ts +++ b/studio/stores/app/ProjectStore.ts @@ -13,16 +13,4 @@ export default class ProjectStore extends PostgresMetaInterface { ) { super(rootStore, dataUrl, headers, options) } - - initialDataArray(value: Project[]) { - const finalValue = value.map((project: any) => { - const kpsVersion = - project?.services?.length > 0 - ? project?.services[0]?.infrastructure[0]?.app_versions?.version - : undefined - - return { ...project, kpsVersion } - }) - super.initialDataArray(finalValue) - } } From 8f558184b4636345243aedc30298223006a6da02 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Wed, 2 Mar 2022 11:55:30 +0700 Subject: [PATCH 03/38] fix: project layouts to support metaStore with lazy projects data --- .../layouts/AuthLayout/AuthLayout.tsx | 6 +- .../layouts/DatabaseLayout/DatabaseLayout.tsx | 18 +++-- .../layouts/DocsLayout/DocsLayout.tsx | 6 +- .../layouts/ProjectLayout/ProjectLayout.tsx | 79 +++++++++++-------- .../TableEditorLayout/TableEditorLayout.tsx | 8 +- studio/pages/project/[ref]/index.tsx | 68 +++++++++------- studio/stores/RootStore.ts | 32 +++++--- studio/stores/content/ProjectContentStore.ts | 4 +- studio/stores/pgmeta/MetaStore.ts | 3 + 9 files changed, 138 insertions(+), 86 deletions(-) diff --git a/studio/components/layouts/AuthLayout/AuthLayout.tsx b/studio/components/layouts/AuthLayout/AuthLayout.tsx index 3feb7ba12a2..dfde160c31b 100644 --- a/studio/components/layouts/AuthLayout/AuthLayout.tsx +++ b/studio/components/layouts/AuthLayout/AuthLayout.tsx @@ -24,8 +24,10 @@ const AuthLayout: FC = ({ title, children }) => { const [loaded, setLoaded] = useState(isInitialized) useEffect(() => { - meta.tables.load() - }, []) + if (meta.ready) { + meta.tables.load() + } + }, [meta.ready]) useEffect(() => { if (!isLoading && !loaded) { diff --git a/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx b/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx index 50f018fc642..df81b0d5dec 100644 --- a/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx +++ b/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx @@ -24,15 +24,17 @@ const DatabaseLayout: FC = ({ title, children }) => { const [loaded, setLoaded] = useState(isInitialized) useEffect(() => { - // Eventually should only load the required stores based on the pages - meta.schemas.load() - meta.tables.load() + if (meta.ready) { + // Eventually should only load the required stores based on the pages + meta.schemas.load() + meta.tables.load() - meta.roles.load() - meta.triggers.load() - meta.extensions.load() - meta.publications.load() - }, []) + meta.roles.load() + meta.triggers.load() + meta.extensions.load() + meta.publications.load() + } + }, [meta.ready]) useEffect(() => { if (!isLoading && !loaded) { diff --git a/studio/components/layouts/DocsLayout/DocsLayout.tsx b/studio/components/layouts/DocsLayout/DocsLayout.tsx index ab09e77ad7c..780080a6ec9 100644 --- a/studio/components/layouts/DocsLayout/DocsLayout.tsx +++ b/studio/components/layouts/DocsLayout/DocsLayout.tsx @@ -12,8 +12,10 @@ function DocsLayout({ title, children }: { title: string; children: ReactElement const { data, isLoading, error } = meta.openApi useEffect(() => { - meta.openApi.load() - }, []) + if (meta.ready) { + meta.openApi.load() + } + }, [meta.ready]) if (error) { return ( diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index d232be0baf3..673cada9ac5 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -32,34 +32,6 @@ const ProjectLayout: FC = ({ hideHeader = false, hideIconBar = false, }) => { - const { ui } = useStore() - const project: Project | undefined = ui.selectedProject - - const router = useRouter() - const requiresDbConnection: boolean = router.pathname !== '/project/[ref]/settings/general' - - // useEffect(() => { - // if (isUndefined(project)) router.push('/404') - // }, []) - - const renderContent = () => { - if (!isUndefined(project)) { - return requiresDbConnection ? ( - - {isLoading ? ( - - ) : ( -
{children}
- )} -
- ) : ( - <>{children} - ) - } else { - return <>{children} - } - } - return ( <> @@ -72,16 +44,16 @@ const ProjectLayout: FC = ({ {!hideIconBar && } {/* Product menu bar */} - {productMenu && !isLoading && ( + {productMenu} - )} +
{!hideHeader && } - {renderContent()} + {children}
@@ -89,3 +61,48 @@ const ProjectLayout: FC = ({ } export default observer(ProjectLayout) + +interface MenuBarWrapperProps { + isLoading: boolean + productMenu?: ReactNode +} + +const MenuBarWrapper: FC = observer(({ isLoading, productMenu, children }) => { + const { ui, meta } = useStore() + const project = ui.selectedProject + return <>{!isLoading && productMenu && project != undefined && meta.ready ? children : null} +}) + +interface ContentWrapperProps { + isLoading: boolean +} + +const ContentWrapper: FC = observer(({ isLoading, children }) => { + const { ui, meta, content } = useStore() + const router = useRouter() + const project = ui.selectedProject + const requiresDbConnection: boolean = router.pathname !== '/project/[ref]/settings/general' + + console.log( + 'ProjectLayout: ', + project, + ' meta.ready:', + meta.ready, + ' content.ready:', + content.ready + ) + + return ( + <> + {isLoading || project == undefined || !meta.ready ? ( + + ) : requiresDbConnection ? ( + +
{children}
+
+ ) : ( + <>{children} + )} + + ) +}) diff --git a/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx b/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx index 3ea80225b6c..729a0276ce2 100644 --- a/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx +++ b/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx @@ -33,9 +33,11 @@ const TableEditorLayout: FC = ({ const [loaded, setLoaded] = useState(isInitialized) useEffect(() => { - meta.schemas.load() - meta.tables.load() - }, []) + if (meta.ready) { + meta.schemas.load() + meta.tables.load() + } + }, [meta.ready]) useEffect(() => { let cancel = false diff --git a/studio/pages/project/[ref]/index.tsx b/studio/pages/project/[ref]/index.tsx index 076d15751ff..ab16f05bfb9 100644 --- a/studio/pages/project/[ref]/index.tsx +++ b/studio/pages/project/[ref]/index.tsx @@ -15,50 +15,24 @@ import { NewProjectPanel, } from 'components/interfaces/Home' import { CLIENT_LIBRARIES, EXAMPLE_PROJECTS } from 'components/interfaces/Home/Home.constants' +import { FC } from 'react' const Home: NextPage = () => { const { ui } = useStore() const project = ui.selectedProject const projectName = - project?.ref !== 'default' - ? project?.name ?? 'Welcome to your project' + project?.ref !== 'default' && project?.name != undefined + ? project?.name : 'Welcome to your project' - const { data: usage, error: usageError }: any = useSWR( - `${API_URL}/projects/${project?.ref}/usage`, - get - ) - - if (usageError) { - return Error loading data {usageError.message} - } - - const hasProjectData = - usage && (usage?.bucketSize || (usage?.authUsers ?? '0') !== '0' || usage?.dbTables) - ? true - : false - return (

{projectName}

- {IS_PLATFORM && project && ( - <> - {isUndefined(usage) ? ( -
- -

Retrieving project usage statistics

-
- ) : !usage.error && hasProjectData ? ( - - ) : ( - - )} - - )} +
Client libraries @@ -85,3 +59,37 @@ const Home: NextPage = () => { } export default withAuth(observer(Home)) + +const ProjectUsageSection: FC = observer(({}) => { + const { ui } = useStore() + + const project = ui.selectedProject + const { data: usage, error: usageError }: any = useSWR( + `${API_URL}/projects/${project?.ref}/usage`, + get + ) + + if (usageError) { + return Error loading data {usageError.message} + } + + const hasProjectData = + usage && (usage?.bucketSize || (usage?.authUsers ?? '0') !== '0' || usage?.dbTables) + ? true + : false + + return IS_PLATFORM ? ( + <> + {isUndefined(usage) ? ( +
+ +

Retrieving project usage statistics

+
+ ) : !usage.error && hasProjectData ? ( + + ) : ( + + )} + + ) : null +}) diff --git a/studio/stores/RootStore.ts b/studio/stores/RootStore.ts index 4bd4eb7bf5c..f9d2b83e1f3 100644 --- a/studio/stores/RootStore.ts +++ b/studio/stores/RootStore.ts @@ -1,4 +1,4 @@ -import { configure } from 'mobx' +import { configure, reaction } from 'mobx' import AppStore, { IAppStore } from './app/AppStore' import MetaStore, { IMetaStore } from './pgmeta/MetaStore' import UiStore, { IUiStore } from './UiStore' @@ -32,17 +32,31 @@ export class RootStore implements IRootStore { connectionString: '', }) this.app = new AppStore(this) + + reaction( + () => this.ui.selectedProject, + (selectedProject) => { + if (selectedProject) { + this.content = new ProjectContentStore(this, { projectRef: selectedProject.ref }) + this.meta = new MetaStore(this, { + projectRef: selectedProject.ref, + connectionString: selectedProject.connectionString ?? '', + }) + } else { + this.content = new ProjectContentStore(this, { projectRef: '' }) + this.meta = new MetaStore(this, { + projectRef: '', + connectionString: '', + }) + } + console.log('selectedProject changed: ', selectedProject) + } + ) } setProjectRef(value?: string) { - if (this.ui.selectedProject?.ref != value) { - this.ui.setProjectRef(value) - this.content = new ProjectContentStore(this, { projectRef: value || '' }) - this.meta = new MetaStore(this, { - projectRef: value || '', - connectionString: this.ui.selectedProject?.connectionString ?? '', - }) - } + if (this.ui.selectedProject?.ref == value) return + this.ui.setProjectRef(value) } setOrganizationSlug(value?: string) { diff --git a/studio/stores/content/ProjectContentStore.ts b/studio/stores/content/ProjectContentStore.ts index 2952aaa42a0..ec05327bc2b 100644 --- a/studio/stores/content/ProjectContentStore.ts +++ b/studio/stores/content/ProjectContentStore.ts @@ -10,6 +10,7 @@ import { API_URL } from 'lib/constants' // but use the one under the stores folder projectContentStore first while we transition export interface IProjectContentStore { + ready: boolean isLoading: boolean isInitialized: boolean error: any @@ -21,6 +22,7 @@ export interface IProjectContentStore { } export default class ProjectContentStore implements IProjectContentStore { + ready: boolean rootStore: IRootStore STATES = { @@ -38,7 +40,7 @@ export default class ProjectContentStore implements IProjectContentStore { constructor(rootStore: IRootStore, options: { projectRef: string }) { const { projectRef } = options - + this.ready = projectRef !== '' this.rootStore = rootStore this.baseUrl = `${API_URL}/projects/${projectRef}/content` makeAutoObservable(this) diff --git a/studio/stores/pgmeta/MetaStore.ts b/studio/stores/pgmeta/MetaStore.ts index c2954314903..0537bb4538d 100644 --- a/studio/stores/pgmeta/MetaStore.ts +++ b/studio/stores/pgmeta/MetaStore.ts @@ -43,6 +43,7 @@ const BATCH_SIZE = 1000 const CHUNK_SIZE = 1024 * 1024 * 0.25 // 0.25MB export interface IMetaStore { + ready: boolean excludedSchemas: string[] openApi: IOpenApiStore @@ -114,6 +115,7 @@ export default class MetaStore implements IMetaStore { extensions: ExtensionsStore publications: PublicationStore + ready: boolean connectionString?: string baseUrl: string queryBaseUrl: string @@ -131,6 +133,7 @@ export default class MetaStore implements IMetaStore { constructor(rootStore: IRootStore, options: { projectRef: string; connectionString: string }) { const { projectRef, connectionString } = options + this.ready = projectRef !== '' this.rootStore = rootStore this.baseUrl = `/api/pg-meta/${projectRef}` this.queryBaseUrl = `${API_URL}/pg-meta/${projectRef}` From 936f9ec128005bf2c64ab2033a9da9f42256e26b Mon Sep 17 00:00:00 2001 From: phamhieu Date: Wed, 2 Mar 2022 11:56:04 +0700 Subject: [PATCH 04/38] chore: clean up --- .../components/layouts/ProjectLayout/ProjectLayout.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 673cada9ac5..48d1c3f39f5 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -83,15 +83,6 @@ const ContentWrapper: FC = observer(({ isLoading, children const project = ui.selectedProject const requiresDbConnection: boolean = router.pathname !== '/project/[ref]/settings/general' - console.log( - 'ProjectLayout: ', - project, - ' meta.ready:', - meta.ready, - ' content.ready:', - content.ready - ) - return ( <> {isLoading || project == undefined || !meta.ready ? ( From 7d3badab44eb1a15ea482d54e896f0e8ad4738d4 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Wed, 2 Mar 2022 11:59:43 +0700 Subject: [PATCH 05/38] chore: another cleanup --- studio/components/layouts/ProjectLayout/ProjectLayout.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 48d1c3f39f5..7a150f1aed8 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -1,10 +1,7 @@ import Head from 'next/head' import { FC, ReactNode } from 'react' import { observer } from 'mobx-react-lite' -import { isUndefined } from 'lodash' import { useRouter } from 'next/router' - -import { Project } from 'types' import { useStore } from 'hooks' import Connecting from 'components/ui/Loading' From 06e835fd86d9752a6f4dc73f665e7edbb8534623 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Wed, 2 Mar 2022 12:38:20 +0700 Subject: [PATCH 06/38] fix: sql editor --- .../layouts/ProjectLayout/ProjectLayout.tsx | 2 +- .../SQLEditorLayout/SQLEditorLayout.tsx | 25 ++++--------- studio/pages/project/[ref]/sql/index.tsx | 35 ++++++++----------- studio/stores/RootStore.ts | 8 +++++ studio/stores/content/ProjectContentStore.ts | 1 + 5 files changed, 31 insertions(+), 40 deletions(-) diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 7a150f1aed8..e6fb1ee7004 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -75,7 +75,7 @@ interface ContentWrapperProps { } const ContentWrapper: FC = observer(({ isLoading, children }) => { - const { ui, meta, content } = useStore() + const { ui, meta } = useStore() const router = useRouter() const project = ui.selectedProject const requiresDbConnection: boolean = router.pathname !== '/project/[ref]/settings/general' diff --git a/studio/components/layouts/SQLEditorLayout/SQLEditorLayout.tsx b/studio/components/layouts/SQLEditorLayout/SQLEditorLayout.tsx index 9de4c3bdbef..2b0bbb61456 100644 --- a/studio/components/layouts/SQLEditorLayout/SQLEditorLayout.tsx +++ b/studio/components/layouts/SQLEditorLayout/SQLEditorLayout.tsx @@ -14,31 +14,18 @@ interface Props { const SQLEditorLayout: FC = ({ title, children }) => { const { content } = useStore() - const { isInitialized, isLoading, error } = content - - const [loaded, setLoaded] = useState(isInitialized) useEffect(() => { content.load() }, []) - useEffect(() => { - if (!isLoading && !loaded) { - setLoaded(true) - } - }, [isLoading]) - - if (error) { - return ( - - - - ) - } - - return ( + return content.error ? ( + + + + ) : ( } diff --git a/studio/pages/project/[ref]/sql/index.tsx b/studio/pages/project/[ref]/sql/index.tsx index 6b48869468c..b9ebdec4c95 100644 --- a/studio/pages/project/[ref]/sql/index.tsx +++ b/studio/pages/project/[ref]/sql/index.tsx @@ -18,23 +18,17 @@ import { useProjectContentStore } from 'stores/projectContentStore' import { useStore, withAuth } from 'hooks' import { SQLEditorLayout } from 'components/layouts' +import BaseLayout from 'components/layouts' const PageConfig = () => { - const router = useRouter() - const { ref } = router.query - const { meta, ui } = useStore() const { profile: user } = ui - const contentStore: any = useProjectContentStore(ref) - const sqlEditorStore: any = useSqlEditorStore(ref, meta) - useEffect(() => { - /* - * Load persited data - */ - loadPersistantData() - }, [ref]) + if (ui.selectedProject != undefined) { + loadPersistantData() + } + }, [ui.selectedProject]) /* * Load persited data @@ -44,23 +38,24 @@ const PageConfig = () => { await sqlEditorStore.loadRemotePersistentData(contentStore, (user as any)?.id) } + if (ui.selectedProject == undefined || !meta.ready) { + return + } + + const contentStore: any = useProjectContentStore(ui.selectedProject.ref) + const sqlEditorStore: any = useSqlEditorStore(ui.selectedProject.ref, meta) + return ( - + + + ) } export default withAuth(observer(PageConfig)) -const PageLayout = observer(() => { - return ( - - - - ) -}) - const SqlEditor = observer(() => { const { meta, ui } = useStore() const { isDarkTheme } = ui diff --git a/studio/stores/RootStore.ts b/studio/stores/RootStore.ts index f9d2b83e1f3..30a22288245 100644 --- a/studio/stores/RootStore.ts +++ b/studio/stores/RootStore.ts @@ -33,6 +33,14 @@ export class RootStore implements IRootStore { }) this.app = new AppStore(this) + /** + * TODO: meta and content are not observable + * meaning that when meta and content object change mobx doesnt trigger new event + * + * Workaround for now + * we need to use ui.selectedProject along with meta and content + * cos whenever ui.selectedProject changes, the reaction will create new meta and content stores + */ reaction( () => this.ui.selectedProject, (selectedProject) => { diff --git a/studio/stores/content/ProjectContentStore.ts b/studio/stores/content/ProjectContentStore.ts index ec05327bc2b..81a1ae0a3b7 100644 --- a/studio/stores/content/ProjectContentStore.ts +++ b/studio/stores/content/ProjectContentStore.ts @@ -73,6 +73,7 @@ export default class ProjectContentStore implements IProjectContentStore { this.state = LOADING await this.fetchData() this.state = LOADED + console.log('this.state: ', this.state) return this.data } catch (e: any) { console.error('Failed to load project content', e.message) From 1da264d357c0cb0bc5704c6bf112b2a944a1fb68 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Wed, 2 Mar 2022 05:29:50 +0000 Subject: [PATCH 07/38] fix: layout of paused card --- .../Home/ProjectList/ProjectCard.tsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index e3c627806f4..d7424d22556 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -27,21 +27,23 @@ const ProjectCard: FC = ({ - {name} +
+ {name} {paused && ( - -
- - Project paused -
-
+
+ +
+ + Project paused +
+
+
)}
} description={''} footer={ -
+
{desc} {paused && (
From 4e6c5c06e6c70cdfa9cca8485851df24e90f14c7 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Wed, 2 Mar 2022 04:44:17 +0000 Subject: [PATCH 08/38] fix: header and description issue with ConfirmModal --- .../to-be-cleaned/ModalsDeprecated/ConfirmModal.js | 9 ++++++++- studio/pages/new/[slug].tsx | 12 ++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/studio/components/to-be-cleaned/ModalsDeprecated/ConfirmModal.js b/studio/components/to-be-cleaned/ModalsDeprecated/ConfirmModal.js index 37a908a55d5..b7a041346a9 100644 --- a/studio/components/to-be-cleaned/ModalsDeprecated/ConfirmModal.js +++ b/studio/components/to-be-cleaned/ModalsDeprecated/ConfirmModal.js @@ -33,13 +33,20 @@ function ConfirmModal({ return ( + > + {description && ( + +

{description}

+
+ )} +
) } diff --git a/studio/pages/new/[slug].tsx b/studio/pages/new/[slug].tsx index 651ceebbf81..ec52ae957c3 100644 --- a/studio/pages/new/[slug].tsx +++ b/studio/pages/new/[slug].tsx @@ -83,15 +83,15 @@ export const Wizard = observer(() => { const isEmptyPaymentMethod = stripeCustomer ? stripeCustomer.paymentMethods?.data?.length <= 0 : undefined - const isOverFreeProjectLimit = totalFreeProjects >= freeProjectsLimit + const isOverFreeProjectLimit = false //totalFreeProjects >= freeProjectsLimit const isInvalidSlug = isUndefined(currentOrg) const isSelectFreeTier = dbPricingPlan === PRICING_PLANS.FREE - const canCreateProject = - currentOrg?.is_owner && - !subscriptionStats.isError && - !subscriptionStats.isLoading && - (!isSelectFreeTier || (isSelectFreeTier && !isOverFreeProjectLimit)) + const canCreateProject = true + // currentOrg?.is_owner && + // !subscriptionStats.isError && + // !subscriptionStats.isLoading && + // (!isSelectFreeTier || (isSelectFreeTier && !isOverFreeProjectLimit)) const canSubmit = projectName != '' && From 4b8cd688f6f378c99caa4b3f3a10f66de83418b2 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Wed, 2 Mar 2022 04:45:06 +0000 Subject: [PATCH 09/38] revert changes --- studio/pages/new/[slug].tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/studio/pages/new/[slug].tsx b/studio/pages/new/[slug].tsx index ec52ae957c3..651ceebbf81 100644 --- a/studio/pages/new/[slug].tsx +++ b/studio/pages/new/[slug].tsx @@ -83,15 +83,15 @@ export const Wizard = observer(() => { const isEmptyPaymentMethod = stripeCustomer ? stripeCustomer.paymentMethods?.data?.length <= 0 : undefined - const isOverFreeProjectLimit = false //totalFreeProjects >= freeProjectsLimit + const isOverFreeProjectLimit = totalFreeProjects >= freeProjectsLimit const isInvalidSlug = isUndefined(currentOrg) const isSelectFreeTier = dbPricingPlan === PRICING_PLANS.FREE - const canCreateProject = true - // currentOrg?.is_owner && - // !subscriptionStats.isError && - // !subscriptionStats.isLoading && - // (!isSelectFreeTier || (isSelectFreeTier && !isOverFreeProjectLimit)) + const canCreateProject = + currentOrg?.is_owner && + !subscriptionStats.isError && + !subscriptionStats.isLoading && + (!isSelectFreeTier || (isSelectFreeTier && !isOverFreeProjectLimit)) const canSubmit = projectName != '' && From a7160e78cee7468f5925b4e16c627b774045a82d Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Wed, 2 Mar 2022 05:19:04 +0000 Subject: [PATCH 10/38] fix: ConfirmModal now shows title and description --- .../ModalsDeprecated/ConfirmModal.js | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/studio/components/to-be-cleaned/ModalsDeprecated/ConfirmModal.js b/studio/components/to-be-cleaned/ModalsDeprecated/ConfirmModal.js index b7a041346a9..0b3ea97dcf8 100644 --- a/studio/components/to-be-cleaned/ModalsDeprecated/ConfirmModal.js +++ b/studio/components/to-be-cleaned/ModalsDeprecated/ConfirmModal.js @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import { Modal } from '@supabase/ui' +import { Button, Modal } from '@supabase/ui' import { render, unmountComponentAtNode } from 'react-dom' function ConfirmModal({ @@ -38,15 +38,26 @@ function ConfirmModal({ visible={true} size="small" onCancel={onCancelClick} - onConfirm={onConfirmClick} + onConfirm={onCancelClick} loading={loading} - > - {description && ( - -

{description}

-
- )} - + customFooter={ +
+ + +
+ } + children={ + description && ( + +

{description}

+
+ ) + } + /> ) } From e7858421777e3ffc4c585c042f7f9c159708014f Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 1 Mar 2022 16:42:48 +0000 Subject: [PATCH 11/38] chore: add 'Simple pg_stat_statements report' to SQL editor templates --- .../SQLEditor/SQLEditor.constants.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts b/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts index 9146dd0e829..dda3d73160e 100644 --- a/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts +++ b/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts @@ -93,6 +93,73 @@ language sql volatile; -- const { data, error } = await supabase.rpc('increment', { row_id: 2 }) `.trim(), }, + { + id: 8, + type: 'template', + title: 'Simple pg_stat_statements report', + description: 'Select from pg_stat_statements and view recent queries', + sql: `-- Simple pg_stat_statements report + +-- A limit of 100 has been added below + +SELECT + a.rolname, + s.query, + s.calls, + -- -- Postgres 13, 14 + s.total_exec_time + s.total_plan_time as total_time, + s.min_exec_time + s.min_plan_time as min_time, + s.max_exec_time + s.max_plan_time as max_time, + s.mean_exec_time + s.mean_plan_time as mean_time, + -- -- Postgres <= 12 + -- total_time, + -- min_time, + -- max_time, + -- mean_time, + s.rows / s.calls as avg_rows, + s.wal_bytes, + s.wal_records + from pg_stat_statements as s + join pg_authid as a on s.userid = a.oid + where + query not like 'SELECT%FROM net.http_request_queue%' + and query not like 'DELETE FROM net.http_request_queue%' + and query <> 'SELECT version()' + and query <> 'BEGIN ISOLATION LEVEL READ COMMITTED READ ONLY' + and query <> 'COMMIT' + and query <> 'SET client_encoding = ''UTF8''' + and query <> 'SET client_min_messages TO WARNING' + and query not like 'select set_config(%' + and trim(regexp_replace(query, E'\n', ' ', 'g')) <> 'select description from pg_catalog.pg_namespace n left join pg_catalog.pg_description d on d.objoid = n.oid where n.nspname = $1' + and query <> 'LISTEN "ddl_command_end"' + and query <> 'LISTEN "pgrst"' + and query <> 'SELECT * FROM migrations ORDER BY id' + and query <> 'SELECT COUNT(*) = $1 FROM pg_publication WHERE pubname = $2' + and query <> 'SELECT COUNT(*) >= $1 FROM pg_replication_slots WHERE slot_name = $2' + and query <> 'SELECT EXISTS (SELECT schema_migrations.* FROM schema_migrations AS schema_migrations WHERE version = $1)' + and query <> 'SELECT EXISTS (SELECT schema_migrations.* FROM schema_migrations AS schema_migrations WHERE version = $1)' + and query <> 'SELECT current_setting($1)::integer, current_setting($2)' + and query <> 'SELECT pg_advisory_unlock($1)' + and query <> 'SELECT pg_try_advisory_lock($1)' + and query <> 'SELECT slot_name, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) FROM pg_replication_slots' + and query <> 'SELECT typname::text, oid::int4, typarray::int4 FROM pg_type WHERE typname IN ($1,$2) ORDER BY typname' + and query <> 'select * from schema_migrations' + and query <> 'set local schema ''''' + and query not like '%LATERAL (SELECT * FROM pg_namespace WHERE pg_namespace.oid = other.relnamespace) AS ns2%' + and query not like '%LEFT JOIN (pg_collation co JOIN pg_namespace nco ON co.collnamespace = nco.oid)%' + and query not like '%LEFT JOIN pg_catalog.pg_description as d ON d.objoid = p.oid%' + and query not like '%LEFT JOIN pg_catalog.pg_description as d on d.objoid = c.oid%' + and query not like '%WHEN ss.contype = $3 THEN information_schema._pg_index_position(ss.conindid, ss.confkey[(ss.x).n])%' + and query not like '%join pg_namespace sch on sch.oid = tbl.relnamespace%' + and query not like '%select setdatabase, unnest(setconfig) as setting from pg_catalog.pg_db_role_setting%' + and trim(regexp_replace(query, E'\n', ' ', 'g')) <> 'SELECT EXISTS ( SELECT $2 FROM pg_catalog.pg_class c WHERE c.relname = $1 AND c.relkind = $3 )' + -- query used to sort daily backup order + and query <> 'SELECT SUM(pg_database_size(pg_database.datname)) / ($1 * $2) as size_mb FROM pg_database' + ORDER BY + total_time DESC + LIMIT + 100`, + }, { id: 9, type: 'quickstart', From 27e5362d8576c054b7300094c3a3fe7a5bb3b20f Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 1 Mar 2022 16:49:25 +0000 Subject: [PATCH 12/38] Update SQLEditor.constants.ts --- .../SQLEditor/SQLEditor.constants.ts | 38 +------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts b/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts index dda3d73160e..ba4808e166d 100644 --- a/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts +++ b/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts @@ -96,9 +96,9 @@ language sql volatile; { id: 8, type: 'template', - title: 'Simple pg_stat_statements report', + title: 'pg_stat_statements report', description: 'Select from pg_stat_statements and view recent queries', - sql: `-- Simple pg_stat_statements report + sql: `-- pg_stat_statements report -- A limit of 100 has been added below @@ -121,40 +121,6 @@ SELECT s.wal_records from pg_stat_statements as s join pg_authid as a on s.userid = a.oid - where - query not like 'SELECT%FROM net.http_request_queue%' - and query not like 'DELETE FROM net.http_request_queue%' - and query <> 'SELECT version()' - and query <> 'BEGIN ISOLATION LEVEL READ COMMITTED READ ONLY' - and query <> 'COMMIT' - and query <> 'SET client_encoding = ''UTF8''' - and query <> 'SET client_min_messages TO WARNING' - and query not like 'select set_config(%' - and trim(regexp_replace(query, E'\n', ' ', 'g')) <> 'select description from pg_catalog.pg_namespace n left join pg_catalog.pg_description d on d.objoid = n.oid where n.nspname = $1' - and query <> 'LISTEN "ddl_command_end"' - and query <> 'LISTEN "pgrst"' - and query <> 'SELECT * FROM migrations ORDER BY id' - and query <> 'SELECT COUNT(*) = $1 FROM pg_publication WHERE pubname = $2' - and query <> 'SELECT COUNT(*) >= $1 FROM pg_replication_slots WHERE slot_name = $2' - and query <> 'SELECT EXISTS (SELECT schema_migrations.* FROM schema_migrations AS schema_migrations WHERE version = $1)' - and query <> 'SELECT EXISTS (SELECT schema_migrations.* FROM schema_migrations AS schema_migrations WHERE version = $1)' - and query <> 'SELECT current_setting($1)::integer, current_setting($2)' - and query <> 'SELECT pg_advisory_unlock($1)' - and query <> 'SELECT pg_try_advisory_lock($1)' - and query <> 'SELECT slot_name, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) FROM pg_replication_slots' - and query <> 'SELECT typname::text, oid::int4, typarray::int4 FROM pg_type WHERE typname IN ($1,$2) ORDER BY typname' - and query <> 'select * from schema_migrations' - and query <> 'set local schema ''''' - and query not like '%LATERAL (SELECT * FROM pg_namespace WHERE pg_namespace.oid = other.relnamespace) AS ns2%' - and query not like '%LEFT JOIN (pg_collation co JOIN pg_namespace nco ON co.collnamespace = nco.oid)%' - and query not like '%LEFT JOIN pg_catalog.pg_description as d ON d.objoid = p.oid%' - and query not like '%LEFT JOIN pg_catalog.pg_description as d on d.objoid = c.oid%' - and query not like '%WHEN ss.contype = $3 THEN information_schema._pg_index_position(ss.conindid, ss.confkey[(ss.x).n])%' - and query not like '%join pg_namespace sch on sch.oid = tbl.relnamespace%' - and query not like '%select setdatabase, unnest(setconfig) as setting from pg_catalog.pg_db_role_setting%' - and trim(regexp_replace(query, E'\n', ' ', 'g')) <> 'SELECT EXISTS ( SELECT $2 FROM pg_catalog.pg_class c WHERE c.relname = $1 AND c.relkind = $3 )' - -- query used to sort daily backup order - and query <> 'SELECT SUM(pg_database_size(pg_database.datname)) / ($1 * $2) as size_mb FROM pg_database' ORDER BY total_time DESC LIMIT From 0fba3f23205dd69afed7d2ad64bc2bacfce2e39d Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Wed, 2 Mar 2022 14:25:07 +0800 Subject: [PATCH 13/38] feat: add storage settings with upload file size limit --- .../SettingsLayout/SettingsMenu.utils.ts | 6 ++ .../layouts/StorageLayout/StorageMenu.tsx | 9 ++- .../StorageSettings/StorageSettings.tsx | 79 +++++++++++++++++++ .../to-be-cleaned/Storage/StorageSidebar.js | 15 +++- .../components/to-be-cleaned/Storage/index.js | 2 + .../pages/project/[ref]/storage/settings.tsx | 38 +++++++++ 6 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx create mode 100644 studio/pages/project/[ref]/storage/settings.tsx diff --git a/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts b/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts index 1c6f13d71b2..eea18e36585 100644 --- a/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts +++ b/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts @@ -12,6 +12,12 @@ export const generateSettingsMenu = (ref: string): ProductMenuGroup[] => { { name: 'Database', key: 'database', url: `/project/${ref}/settings/database`, items: [] }, { name: 'API', key: 'api', url: `/project/${ref}/settings/api`, items: [] }, { name: 'Auth Settings', key: 'auth', url: `/project/${ref}/auth/settings`, items: [] }, + { + name: 'Storage Settings', + key: 'storage', + url: `/project/${ref}/storage/settings`, + items: [], + }, { name: 'Billing & Usage', key: 'billing', diff --git a/studio/components/layouts/StorageLayout/StorageMenu.tsx b/studio/components/layouts/StorageLayout/StorageMenu.tsx index 83e406a7d83..bf0df8fc04c 100644 --- a/studio/components/layouts/StorageLayout/StorageMenu.tsx +++ b/studio/components/layouts/StorageLayout/StorageMenu.tsx @@ -24,7 +24,7 @@ interface Props {} const StorageMenu: FC = () => { const router = useRouter() const { ref, bucketId } = router.query - const page = router.pathname.split('/')[4] as undefined | 'policies' | 'usage' + const page = router.pathname.split('/')[4] as undefined | 'policies' | 'settings' | 'usage' const { ui } = useStore() const projectRef = ui.selectedProject?.ref @@ -92,8 +92,13 @@ const StorageMenu: FC = () => {
- + + + + Settings + + Policies diff --git a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx new file mode 100644 index 00000000000..748158dbbd2 --- /dev/null +++ b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx @@ -0,0 +1,79 @@ +import useSWR from 'swr' +import { FC, useState } from 'react' +import { AutoField } from 'uniforms-bootstrap4' +import { Typography } from '@supabase/ui' + +import { API_URL } from 'lib/constants' +import { patch, get } from 'lib/common/fetch' +import { useStore } from 'hooks' +import SchemaFormPanel from 'components/to-be-cleaned/forms/SchemaFormPanel' + +const StorageSettings: FC = ({ projectRef }) => { + const { data, error } = useSWR(`${API_URL}/projects/${projectRef}/config?app=storage`, get) + + if (error) { + return ( +
+ Error loading storage settings +
+ ) + } + + if (!data) { + return ( +
+ Loading... +
+ ) + } + + return +} + +const StorageConfig = ({ config, projectRef }: any) => { + const { ui } = useStore() + + const [updates, setUpdates] = useState(config) + + const updateConfig = async (updatedConfig: any) => { + try { + const response = await patch( + `${API_URL}/projects/${projectRef}/config?app=storage`, + updatedConfig + ) + if (response.error) { + throw response.error + } else { + ui.setNotification({ category: 'success', message: 'Settings saved' }) + } + } catch (error: any) { + ui.setNotification({ category: 'error', message: `Update config failed: ${error.message}` }) + } + } + + return ( +
+ updateConfig(model)} + onReset={() => setUpdates(config)} + > + + +
+ ) +} + +export default StorageSettings diff --git a/studio/components/to-be-cleaned/Storage/StorageSidebar.js b/studio/components/to-be-cleaned/Storage/StorageSidebar.js index dc387937def..fc12d8dcb6c 100644 --- a/studio/components/to-be-cleaned/Storage/StorageSidebar.js +++ b/studio/components/to-be-cleaned/Storage/StorageSidebar.js @@ -137,8 +137,21 @@ const StorageSidebar = ({
- +
+
diff --git a/studio/components/to-be-cleaned/Storage/index.js b/studio/components/to-be-cleaned/Storage/index.js index f35c92af17f..e903df9cd68 100644 --- a/studio/components/to-be-cleaned/Storage/index.js +++ b/studio/components/to-be-cleaned/Storage/index.js @@ -1,5 +1,6 @@ import StorageExplorer from './StorageExplorer/StorageExplorer' import StoragePolicies from './StoragePolicies/StoragePolicies' +import StorageSettings from './StorageSettings/StorageSettings' import StorageUsage from './StorageUsage/StorageUsage' import StorageSidebar from './StorageSidebar' @@ -10,6 +11,7 @@ import ToggleBucketPublicModal from './ToggleBucketPublicModal' export { StorageExplorer, StoragePolicies, + StorageSettings, StorageUsage, StorageSidebar, CreateBucketModal, diff --git a/studio/pages/project/[ref]/storage/settings.tsx b/studio/pages/project/[ref]/storage/settings.tsx new file mode 100644 index 00000000000..ecb47812553 --- /dev/null +++ b/studio/pages/project/[ref]/storage/settings.tsx @@ -0,0 +1,38 @@ +import React, { useEffect } from 'react' +import { useRouter } from 'next/router' +import { observer } from 'mobx-react-lite' + +import { API_URL, PROJECT_STATUS } from 'lib/constants' +import { useStore, withAuth } from 'hooks' +import { post } from 'lib/common/fetch' +import { StorageLayout } from 'components/layouts' +import { StorageSettings } from 'components/to-be-cleaned/Storage' + +/** + * PageLayout is used to setup layout - as usual it will requires inject global store + */ +const PageLayout = () => { + const router = useRouter() + const { ref } = router.query + + const { ui } = useStore() + const project = ui.selectedProject + + useEffect(() => { + if (project && project.status === PROJECT_STATUS.INACTIVE) { + post(`${API_URL}/projects/${ref}/restore`, {}) + } + }, [project]) + + if (!project) return
+ + return ( + +
+ +
+
+ ) +} + +export default withAuth(observer(PageLayout)) From 4ccd10e7b3e0fb5e3ace88531548f0a3d1e049e2 Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Tue, 22 Feb 2022 15:49:09 +0800 Subject: [PATCH 14/38] feat: add minimum and maximum for file size limit --- .../Storage/StorageSettings/StorageSettings.tsx | 6 ++++-- studio/lib/constants/infrastructure.ts | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx index 748158dbbd2..6a0e0addb73 100644 --- a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx +++ b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx @@ -3,7 +3,7 @@ import { FC, useState } from 'react' import { AutoField } from 'uniforms-bootstrap4' import { Typography } from '@supabase/ui' -import { API_URL } from 'lib/constants' +import { API_URL, STORAGE_FILE_SIZE_LIMIT_MAX } from 'lib/constants' import { patch, get } from 'lib/common/fetch' import { useStore } from 'hooks' import SchemaFormPanel from 'components/to-be-cleaned/forms/SchemaFormPanel' @@ -58,9 +58,11 @@ const StorageConfig = ({ config, projectRef }: any) => { schema={{ properties: { fileSizeLimit: { + help: 'The maximum size in bytes of a file that can be uploaded.', + maximum: STORAGE_FILE_SIZE_LIMIT_MAX, + minimum: 0, title: 'Upload file size limit', type: 'integer', - help: 'The maximum size in bytes of a file that can be uploaded.', }, }, required: ['fileSizeLimit'], diff --git a/studio/lib/constants/infrastructure.ts b/studio/lib/constants/infrastructure.ts index 36e626507ce..d13a8e06b34 100644 --- a/studio/lib/constants/infrastructure.ts +++ b/studio/lib/constants/infrastructure.ts @@ -132,3 +132,5 @@ export const PASSWORD_STRENGTH_PERCENTAGE = { } export const DEFAULT_FREE_PROJECTS_LIMIT = 2 + +export const STORAGE_FILE_SIZE_LIMIT_MAX = 5_368_709_120 From bd56d496f798223e82ebfb78481448d310ff9fde Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Wed, 2 Mar 2022 14:19:35 +0800 Subject: [PATCH 15/38] refactor: extract `UpgradeToPro` component --- .../Database/Backups/BackupsList.tsx | 35 ++++------------- studio/components/ui/UpgradeToPro.tsx | 38 +++++++++++++++++++ 2 files changed, 45 insertions(+), 28 deletions(-) create mode 100644 studio/components/ui/UpgradeToPro.tsx diff --git a/studio/components/interfaces/Database/Backups/BackupsList.tsx b/studio/components/interfaces/Database/Backups/BackupsList.tsx index 8ba7c4151bc..a66e989f6bf 100644 --- a/studio/components/interfaces/Database/Backups/BackupsList.tsx +++ b/studio/components/interfaces/Database/Backups/BackupsList.tsx @@ -1,12 +1,12 @@ -import Link from 'next/link' import React, { FC, useState, useEffect } from 'react' import { observer } from 'mobx-react-lite' -import { Button, Typography, IconClock, IconInfo } from '@supabase/ui' +import { Typography, IconInfo } from '@supabase/ui' import { useStore } from 'hooks' import { STRIPE_PRODUCT_IDS } from 'lib/constants' import BackupItem from './BackupItem' import Loading from 'components/ui/Loading' +import UpgradeToPro from 'components/ui/UpgradeToPro' import Panel from 'components/to-be-cleaned/Panel' interface Props {} @@ -44,32 +44,11 @@ const BackupsList: FC = ({}) => { if (tierId === STRIPE_PRODUCT_IDS.FREE) { return ( -
-
-
- -
-
-
-

Free Plan does not include project backups.

-
-

- Please upgrade to Pro plan for up to 7 days of backups. -

-
-
- - - -
-
-
+ ) } diff --git a/studio/components/ui/UpgradeToPro.tsx b/studio/components/ui/UpgradeToPro.tsx new file mode 100644 index 00000000000..46bc5c0224b --- /dev/null +++ b/studio/components/ui/UpgradeToPro.tsx @@ -0,0 +1,38 @@ +import { Button, IconClock, Typography } from '@supabase/ui' +import Link from 'next/link' +import { FC } from 'react' + +interface Props { + primaryText: string + projectRef: string + secondaryText: string +} + +const UpgradeToPro: FC = ({ primaryText, projectRef, secondaryText }) => ( +
+
+
+ +
+
+
+

{primaryText}

+
+

{secondaryText}

+
+
+ + + +
+
+
+) + +export default UpgradeToPro From 063fbda2cc8362a102cddd35e2d70ca93531945b Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Tue, 22 Feb 2022 15:51:23 +0800 Subject: [PATCH 16/38] feat: disable updating file size limit if free tier --- .../Storage/StorageSettings/StorageSettings.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx index 6a0e0addb73..965135173e9 100644 --- a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx +++ b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx @@ -33,6 +33,7 @@ const StorageSettings: FC = ({ projectRef }) => { const StorageConfig = ({ config, projectRef }: any) => { const { ui } = useStore() + const { isFreeTier } = config const [updates, setUpdates] = useState(config) const updateConfig = async (updatedConfig: any) => { @@ -72,7 +73,14 @@ const StorageConfig = ({ config, projectRef }: any) => { onSubmit={(model: any) => updateConfig(model)} onReset={() => setUpdates(config)} > - +
) From c468f1e6212ef18e147d0818b0f172d168e9c75d Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Tue, 22 Feb 2022 15:54:36 +0800 Subject: [PATCH 17/38] feat: display upload file size limit messaging for free tier --- .../Storage/StorageSettings/StorageSettings.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx index 965135173e9..9f02cc5a0f1 100644 --- a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx +++ b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx @@ -7,6 +7,7 @@ import { API_URL, STORAGE_FILE_SIZE_LIMIT_MAX } from 'lib/constants' import { patch, get } from 'lib/common/fetch' import { useStore } from 'hooks' import SchemaFormPanel from 'components/to-be-cleaned/forms/SchemaFormPanel' +import UpgradeToPro from 'components/ui/UpgradeToPro' const StorageSettings: FC = ({ projectRef }) => { const { data, error } = useSWR(`${API_URL}/projects/${projectRef}/config?app=storage`, get) @@ -81,6 +82,13 @@ const StorageConfig = ({ config, projectRef }: any) => { STORAGE_FILE_SIZE_LIMIT_MAX )} bytes.`} /> + {isFreeTier && ( + + )}
) From a4bd946cb1dedd167ae97eae785b24cf347d1d2a Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Wed, 2 Mar 2022 14:25:45 +0800 Subject: [PATCH 18/38] Small style updates --- .../components/layouts/SettingsLayout/SettingsMenu.utils.ts | 4 ++-- studio/components/layouts/StorageLayout/StorageMenu.tsx | 2 +- studio/components/to-be-cleaned/Storage/StorageSidebar.js | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts b/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts index eea18e36585..d8330917a1e 100644 --- a/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts +++ b/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts @@ -11,9 +11,9 @@ export const generateSettingsMenu = (ref: string): ProductMenuGroup[] => { { name: 'General', key: 'general', url: `/project/${ref}/settings/general`, items: [] }, { name: 'Database', key: 'database', url: `/project/${ref}/settings/database`, items: [] }, { name: 'API', key: 'api', url: `/project/${ref}/settings/api`, items: [] }, - { name: 'Auth Settings', key: 'auth', url: `/project/${ref}/auth/settings`, items: [] }, + { name: 'Authentication', key: 'auth', url: `/project/${ref}/auth/settings`, items: [] }, { - name: 'Storage Settings', + name: 'Storage', key: 'storage', url: `/project/${ref}/storage/settings`, items: [], diff --git a/studio/components/layouts/StorageLayout/StorageMenu.tsx b/studio/components/layouts/StorageLayout/StorageMenu.tsx index bf0df8fc04c..b50f2d2e25d 100644 --- a/studio/components/layouts/StorageLayout/StorageMenu.tsx +++ b/studio/components/layouts/StorageLayout/StorageMenu.tsx @@ -92,7 +92,7 @@ const StorageMenu: FC = () => {
- + diff --git a/studio/components/to-be-cleaned/Storage/StorageSidebar.js b/studio/components/to-be-cleaned/Storage/StorageSidebar.js index fc12d8dcb6c..fed5e0d6c80 100644 --- a/studio/components/to-be-cleaned/Storage/StorageSidebar.js +++ b/studio/components/to-be-cleaned/Storage/StorageSidebar.js @@ -13,6 +13,8 @@ import Link from 'next/link' import { useRouter } from 'next/router' import { STORAGE_ROW_STATUS } from './Storage.constants' +// [Joshen] I think this file is no longer in use, double check before removing (replaced by StorageMenu.tsx) + const BucketRow = ({ bucket = {}, projectRef = '', @@ -137,7 +139,7 @@ const StorageSidebar = ({
- +
From 6656c18ad3703e9b52a8cb0f91344d739568a593 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 22 Feb 2022 17:21:01 +0800 Subject: [PATCH 19/38] update constants/infra for storage max file size limit --- studio/lib/constants/infrastructure.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/lib/constants/infrastructure.ts b/studio/lib/constants/infrastructure.ts index d13a8e06b34..39001df34b2 100644 --- a/studio/lib/constants/infrastructure.ts +++ b/studio/lib/constants/infrastructure.ts @@ -133,4 +133,4 @@ export const PASSWORD_STRENGTH_PERCENTAGE = { export const DEFAULT_FREE_PROJECTS_LIMIT = 2 -export const STORAGE_FILE_SIZE_LIMIT_MAX = 5_368_709_120 +export const STORAGE_FILE_SIZE_LIMIT_MAX = 5 * 1024 * 1024 * 1024 // 5GB From 0b39e50668f8e59280dd1c736c2a0a0e4008346a Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Wed, 2 Mar 2022 12:17:04 +0800 Subject: [PATCH 20/38] refactor: s/STORAGE_FILE_SIZE_LIMIT_MAX/STORAGE_FILE_SIZE_LIMIT_MAX_BYTES/ --- .../Storage/StorageSettings/StorageSettings.tsx | 6 +++--- studio/lib/constants/infrastructure.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx index 9f02cc5a0f1..5eb64c9382b 100644 --- a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx +++ b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx @@ -3,7 +3,7 @@ import { FC, useState } from 'react' import { AutoField } from 'uniforms-bootstrap4' import { Typography } from '@supabase/ui' -import { API_URL, STORAGE_FILE_SIZE_LIMIT_MAX } from 'lib/constants' +import { API_URL, STORAGE_FILE_SIZE_LIMIT_MAX_BYTES } from 'lib/constants' import { patch, get } from 'lib/common/fetch' import { useStore } from 'hooks' import SchemaFormPanel from 'components/to-be-cleaned/forms/SchemaFormPanel' @@ -61,7 +61,7 @@ const StorageConfig = ({ config, projectRef }: any) => { properties: { fileSizeLimit: { help: 'The maximum size in bytes of a file that can be uploaded.', - maximum: STORAGE_FILE_SIZE_LIMIT_MAX, + maximum: STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, minimum: 0, title: 'Upload file size limit', type: 'integer', @@ -79,7 +79,7 @@ const StorageConfig = ({ config, projectRef }: any) => { name="fileSizeLimit" showInlineError errorMessage={`Must be a number up to ${new Intl.NumberFormat('en-US').format( - STORAGE_FILE_SIZE_LIMIT_MAX + STORAGE_FILE_SIZE_LIMIT_MAX_BYTES )} bytes.`} /> {isFreeTier && ( diff --git a/studio/lib/constants/infrastructure.ts b/studio/lib/constants/infrastructure.ts index 39001df34b2..d15adeb97fe 100644 --- a/studio/lib/constants/infrastructure.ts +++ b/studio/lib/constants/infrastructure.ts @@ -133,4 +133,4 @@ export const PASSWORD_STRENGTH_PERCENTAGE = { export const DEFAULT_FREE_PROJECTS_LIMIT = 2 -export const STORAGE_FILE_SIZE_LIMIT_MAX = 5 * 1024 * 1024 * 1024 // 5GB +export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES = 5 * 1024 * 1024 * 1024 // 5 GB From ca71b7cbb3340f01d69deae49c1552abe2157ec4 Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Wed, 2 Mar 2022 12:18:56 +0800 Subject: [PATCH 21/38] fix: tweak storage settings upgrade to pro text --- .../to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx index 5eb64c9382b..657ca386959 100644 --- a/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx +++ b/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx @@ -84,9 +84,9 @@ const StorageConfig = ({ config, projectRef }: any) => { /> {isFreeTier && ( )} From ea9dde1c66bdcb40fae2f79c768d77a81a9b6e6c Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Wed, 2 Mar 2022 12:27:17 +0800 Subject: [PATCH 22/38] fix: guard storage menu items with `storageSettings` flag --- .../SettingsLayout/SettingsMenu.utils.ts | 13 +++++---- .../layouts/StorageLayout/StorageMenu.tsx | 13 +++++---- .../to-be-cleaned/Storage/StorageSidebar.js | 29 ++++++++++--------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts b/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts index d8330917a1e..dfcb29c0ab2 100644 --- a/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts +++ b/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts @@ -12,12 +12,13 @@ export const generateSettingsMenu = (ref: string): ProductMenuGroup[] => { { name: 'Database', key: 'database', url: `/project/${ref}/settings/database`, items: [] }, { name: 'API', key: 'api', url: `/project/${ref}/settings/api`, items: [] }, { name: 'Authentication', key: 'auth', url: `/project/${ref}/auth/settings`, items: [] }, - { - name: 'Storage', - key: 'storage', - url: `/project/${ref}/storage/settings`, - items: [], - }, + // TODO(thebengeu): Uncomment once all tenants migrated to multitenant storage API. + // { + // name: 'Storage', + // key: 'storage', + // url: `/project/${ref}/storage/settings`, + // items: [], + // }, { name: 'Billing & Usage', key: 'billing', diff --git a/studio/components/layouts/StorageLayout/StorageMenu.tsx b/studio/components/layouts/StorageLayout/StorageMenu.tsx index b50f2d2e25d..2cef4c5c904 100644 --- a/studio/components/layouts/StorageLayout/StorageMenu.tsx +++ b/studio/components/layouts/StorageLayout/StorageMenu.tsx @@ -15,6 +15,7 @@ import { IconEdit, } from '@supabase/ui' +import Flag from 'components/ui/Flag/Flag' import ProductMenuItem from 'components/ui/ProductMenu/ProductMenuItem' import { STORAGE_ROW_STATUS } from 'components/to-be-cleaned/Storage/Storage.constants' import { useStorageStore } from 'localStores/storageExplorer/StorageExplorerStore' @@ -94,11 +95,13 @@ const StorageMenu: FC = () => {
- - - Settings - - + + + + Settings + + + Policies diff --git a/studio/components/to-be-cleaned/Storage/StorageSidebar.js b/studio/components/to-be-cleaned/Storage/StorageSidebar.js index fed5e0d6c80..61378a165af 100644 --- a/studio/components/to-be-cleaned/Storage/StorageSidebar.js +++ b/studio/components/to-be-cleaned/Storage/StorageSidebar.js @@ -12,6 +12,7 @@ import { import Link from 'next/link' import { useRouter } from 'next/router' import { STORAGE_ROW_STATUS } from './Storage.constants' +import Flag from 'components/ui/Flag/Flag' // [Joshen] I think this file is no longer in use, double check before removing (replaced by StorageMenu.tsx) @@ -141,19 +142,21 @@ const StorageSidebar = ({
- + + +
From 21e0cd71369450129104315abe8105bf5cf99ba3 Mon Sep 17 00:00:00 2001 From: TzeYiing Date: Thu, 24 Feb 2022 01:45:08 +0800 Subject: [PATCH 23/38] added metadata overflow, added copy buttons --- .../interfaces/Settings/Logs/LogSelection.tsx | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/studio/components/interfaces/Settings/Logs/LogSelection.tsx b/studio/components/interfaces/Settings/Logs/LogSelection.tsx index 49f119f46dd..dc1921a1c61 100644 --- a/studio/components/interfaces/Settings/Logs/LogSelection.tsx +++ b/studio/components/interfaces/Settings/Logs/LogSelection.tsx @@ -1,6 +1,7 @@ import dayjs from 'dayjs' import { FC } from 'react' -import { Input, Typography, IconX } from '@supabase/ui' +import { Typography, IconX, Button, IconCopy } from '@supabase/ui' +import { copyToClipboard } from 'lib/helpers' import { LogData } from './Logs.types' @@ -43,16 +44,43 @@ const LogSelection: FC = ({ log, onClose }) => { {log.event_message}{' '} +
+ +
Metadata - + + + {JSON.stringify(log.metadata, null, 2)} + +
+ +
From d6f51326acc0090a6853053e2e4147312b296ab0 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 2 Mar 2022 14:54:38 +0800 Subject: [PATCH 24/38] Update logs panel to use INput component with built in copy functionality --- .../interfaces/Settings/Logs/LogSelection.tsx | 61 ++++++------------- studio/styles/main.scss | 7 ++- 2 files changed, 25 insertions(+), 43 deletions(-) diff --git a/studio/components/interfaces/Settings/Logs/LogSelection.tsx b/studio/components/interfaces/Settings/Logs/LogSelection.tsx index dc1921a1c61..f4a37c23abf 100644 --- a/studio/components/interfaces/Settings/Logs/LogSelection.tsx +++ b/studio/components/interfaces/Settings/Logs/LogSelection.tsx @@ -1,7 +1,6 @@ import dayjs from 'dayjs' import { FC } from 'react' -import { Typography, IconX, Button, IconCopy } from '@supabase/ui' -import { copyToClipboard } from 'lib/helpers' +import { Typography, IconX, Input } from '@supabase/ui' import { LogData } from './Logs.types' @@ -40,47 +39,27 @@ const LogSelection: FC = ({ log, onClose }) => {
- Event message - - {log.event_message}{' '} - -
- -
+

Event message

+
- Metadata - - - {JSON.stringify(log.metadata, null, 2)} - -
- -
+

Metadata

+
diff --git a/studio/styles/main.scss b/studio/styles/main.scss index cfe8c1eec35..61c8b616732 100644 --- a/studio/styles/main.scss +++ b/studio/styles/main.scss @@ -147,8 +147,11 @@ a { text-decoration: none; } -.input-mono input { - @apply font-mono; +.input-mono { + input, + textarea { + @apply font-mono; + } } input[type='radio'] { From 1f5b3edc9f862d9f16883b766c8af273b2207a74 Mon Sep 17 00:00:00 2001 From: Beng Eu Date: Wed, 2 Mar 2022 14:54:20 +0800 Subject: [PATCH 25/38] fix: update @supabase/ui to 0.37.0-alpha.43 To include https://github.com/supabase/ui/commit/e62d7e92c191ef06d2e1077e3e165660e78128f7. --- studio/package-lock.json | 6 +++--- studio/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/studio/package-lock.json b/studio/package-lock.json index 6127a589915..3ccdea4d60b 100644 --- a/studio/package-lock.json +++ b/studio/package-lock.json @@ -2704,9 +2704,9 @@ } }, "@supabase/ui": { - "version": "0.37.0-alpha.41", - "resolved": "https://registry.npmjs.org/@supabase/ui/-/ui-0.37.0-alpha.41.tgz", - "integrity": "sha512-ie8FfK6tUtCAlGmITwNwzhv7GStfu6xGhGUbHDq3tk0HjLuiKKkVg7a2OEdAh4TcJIYRBVBN65XCTvbfSvAUhA==", + "version": "0.37.0-alpha.43", + "resolved": "https://registry.npmjs.org/@supabase/ui/-/ui-0.37.0-alpha.43.tgz", + "integrity": "sha512-sFPzzxc71S/E/HOLSho5g8+dkZ3MY2VW4RxFiG37U7YgHmb2ItYyfc4O2j1NNSkoLYHJkqxEtDVG1tjJQtdmkQ==", "requires": { "@headlessui/react": "^1.0.0", "@mertasan/tailwindcss-variables": "^2.0.1", diff --git a/studio/package.json b/studio/package.json index df832fb91ae..da18b86e1e7 100644 --- a/studio/package.json +++ b/studio/package.json @@ -22,7 +22,7 @@ "@supabase/react-data-grid": "^7.1.0-beta.7", "@supabase/shared-types": "^0.1.15", "@supabase/supabase-js": "^1.29.1", - "@supabase/ui": "0.37.0-alpha.41", + "@supabase/ui": "0.37.0-alpha.43", "ajv": "^8.6.3", "analytics-node": "^3.5.0", "clipboard": "^2.0.8", From 5d5666615b1e46f1beea9ddd08210e86196c5815 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Wed, 2 Mar 2022 15:24:17 +0700 Subject: [PATCH 26/38] chore: clean up again --- .../interfaces/Home/ProjectUsageSection.tsx | 41 +++++++++++++ .../layouts/ProjectLayout/ProjectLayout.tsx | 4 +- studio/pages/project/[ref]/index.tsx | 57 +++---------------- studio/pages/project/[ref]/sql/index.tsx | 6 +- studio/stores/RootStore.ts | 3 +- studio/stores/content/ProjectContentStore.ts | 1 - 6 files changed, 52 insertions(+), 60 deletions(-) create mode 100644 studio/components/interfaces/Home/ProjectUsageSection.tsx diff --git a/studio/components/interfaces/Home/ProjectUsageSection.tsx b/studio/components/interfaces/Home/ProjectUsageSection.tsx new file mode 100644 index 00000000000..4561dd7c3be --- /dev/null +++ b/studio/components/interfaces/Home/ProjectUsageSection.tsx @@ -0,0 +1,41 @@ +import useSWR from 'swr' +import { FC } from 'react' +import { observer } from 'mobx-react-lite' +import { Typography, IconLoader } from '@supabase/ui' +import { get } from 'lib/common/fetch' +import { API_URL } from 'lib/constants' +import { useStore } from 'hooks' +import { ProjectUsage, NewProjectPanel } from 'components/interfaces/Home' + +const ProjectUsageSection: FC = observer(({}) => { + const { ui } = useStore() + const project = ui.selectedProject + const { data: usage, error: usageError }: any = useSWR( + `${API_URL}/projects/${project?.ref}/usage`, + get + ) + const hasProjectData = + usage && (usage?.bucketSize || (usage?.authUsers ?? '0') !== '0' || usage?.dbTables) + ? true + : false + + if (usageError) { + return Error loading data {usageError.message} + } + + return ( + <> + {usage === undefined ? ( +
+ +

Retrieving project usage statistics

+
+ ) : !usage.error && hasProjectData ? ( + + ) : ( + + )} + + ) +}) +export default ProjectUsageSection diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index e6fb1ee7004..5f98cde9668 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -67,7 +67,7 @@ interface MenuBarWrapperProps { const MenuBarWrapper: FC = observer(({ isLoading, productMenu, children }) => { const { ui, meta } = useStore() const project = ui.selectedProject - return <>{!isLoading && productMenu && project != undefined && meta.ready ? children : null} + return <>{!isLoading && productMenu && project !== undefined && meta.ready ? children : null} }) interface ContentWrapperProps { @@ -82,7 +82,7 @@ const ContentWrapper: FC = observer(({ isLoading, children return ( <> - {isLoading || project == undefined || !meta.ready ? ( + {isLoading || project === undefined || !meta.ready ? ( ) : requiresDbConnection ? ( diff --git a/studio/pages/project/[ref]/index.tsx b/studio/pages/project/[ref]/index.tsx index ab16f05bfb9..36d861d5660 100644 --- a/studio/pages/project/[ref]/index.tsx +++ b/studio/pages/project/[ref]/index.tsx @@ -1,28 +1,19 @@ -import useSWR from 'swr' import { NextPage } from 'next' import { observer } from 'mobx-react-lite' -import { isUndefined } from 'lodash' -import { Typography, IconLoader } from '@supabase/ui' - -import { get } from 'lib/common/fetch' -import { API_URL, IS_PLATFORM } from 'lib/constants' +import { Typography } from '@supabase/ui' import { useStore, withAuth } from 'hooks' -import BaseLayout from 'components/layouts' -import { - ExampleProject, - ClientLibrary, - ProjectUsage, - NewProjectPanel, -} from 'components/interfaces/Home' +import { ExampleProject, ClientLibrary } from 'components/interfaces/Home' import { CLIENT_LIBRARIES, EXAMPLE_PROJECTS } from 'components/interfaces/Home/Home.constants' -import { FC } from 'react' +import { IS_PLATFORM } from 'lib/constants' +import BaseLayout from 'components/layouts' +import ProjectUsageSection from 'components/interfaces/Home/ProjectUsageSection' const Home: NextPage = () => { const { ui } = useStore() const project = ui.selectedProject const projectName = - project?.ref !== 'default' && project?.name != undefined + project?.ref !== 'default' && project?.name !== undefined ? project?.name : 'Welcome to your project' @@ -32,7 +23,7 @@ const Home: NextPage = () => {

{projectName}

- + {IS_PLATFORM && }
Client libraries @@ -59,37 +50,3 @@ const Home: NextPage = () => { } export default withAuth(observer(Home)) - -const ProjectUsageSection: FC = observer(({}) => { - const { ui } = useStore() - - const project = ui.selectedProject - const { data: usage, error: usageError }: any = useSWR( - `${API_URL}/projects/${project?.ref}/usage`, - get - ) - - if (usageError) { - return Error loading data {usageError.message} - } - - const hasProjectData = - usage && (usage?.bucketSize || (usage?.authUsers ?? '0') !== '0' || usage?.dbTables) - ? true - : false - - return IS_PLATFORM ? ( - <> - {isUndefined(usage) ? ( -
- -

Retrieving project usage statistics

-
- ) : !usage.error && hasProjectData ? ( - - ) : ( - - )} - - ) : null -}) diff --git a/studio/pages/project/[ref]/sql/index.tsx b/studio/pages/project/[ref]/sql/index.tsx index b9ebdec4c95..91bc5a5d937 100644 --- a/studio/pages/project/[ref]/sql/index.tsx +++ b/studio/pages/project/[ref]/sql/index.tsx @@ -1,8 +1,6 @@ import React, { useEffect, useState } from 'react' -import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' import { useMonaco } from '@monaco-editor/react' - import { useSqlEditorStore, SqlEditorContext, @@ -13,9 +11,7 @@ import TabWelcome from 'components/to-be-cleaned/SqlEditor/TabWelcome' import TabSqlQuery from 'components/to-be-cleaned/SqlEditor/TabSqlQuery' import getPgsqlCompletionProvider from 'components/to-be-cleaned/SqlEditor/PgsqlCompletionProvider' import getPgsqlSignatureHelpProvider from 'components/to-be-cleaned/SqlEditor/PgsqlSignatureHelpProvider' - import { useProjectContentStore } from 'stores/projectContentStore' - import { useStore, withAuth } from 'hooks' import { SQLEditorLayout } from 'components/layouts' import BaseLayout from 'components/layouts' @@ -25,7 +21,7 @@ const PageConfig = () => { const { profile: user } = ui useEffect(() => { - if (ui.selectedProject != undefined) { + if (ui.selectedProject !== undefined) { loadPersistantData() } }, [ui.selectedProject]) diff --git a/studio/stores/RootStore.ts b/studio/stores/RootStore.ts index 30a22288245..57cc645b3af 100644 --- a/studio/stores/RootStore.ts +++ b/studio/stores/RootStore.ts @@ -57,13 +57,12 @@ export class RootStore implements IRootStore { connectionString: '', }) } - console.log('selectedProject changed: ', selectedProject) } ) } setProjectRef(value?: string) { - if (this.ui.selectedProject?.ref == value) return + if (this.ui.selectedProject?.ref === value) return this.ui.setProjectRef(value) } diff --git a/studio/stores/content/ProjectContentStore.ts b/studio/stores/content/ProjectContentStore.ts index 81a1ae0a3b7..ec05327bc2b 100644 --- a/studio/stores/content/ProjectContentStore.ts +++ b/studio/stores/content/ProjectContentStore.ts @@ -73,7 +73,6 @@ export default class ProjectContentStore implements IProjectContentStore { this.state = LOADING await this.fetchData() this.state = LOADED - console.log('this.state: ', this.state) return this.data } catch (e: any) { console.error('Failed to load project content', e.message) From e07032c0858d0ee5a3f246e630f4078fc3a5f1e8 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Wed, 2 Mar 2022 15:51:24 +0700 Subject: [PATCH 27/38] fix: bugs --- studio/components/layouts/WizardLayout.tsx | 19 +++++------- studio/pages/new/[slug].tsx | 36 ++++++++++++---------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/studio/components/layouts/WizardLayout.tsx b/studio/components/layouts/WizardLayout.tsx index 2a526e3a8f4..e6ef990668e 100644 --- a/studio/components/layouts/WizardLayout.tsx +++ b/studio/components/layouts/WizardLayout.tsx @@ -1,20 +1,17 @@ import { FC } from 'react' import Link from 'next/link' -import BaseLayout from 'components/layouts' -import { IconChevronRight, Typography } from '@supabase/ui' +import { IconChevronRight } from '@supabase/ui' const WizardLayout: FC = ({ organization, project, children }) => { return ( - -
-
-
-
- {children} -
-
+
+
+
+
+ {children} +
- +
) } export default WizardLayout diff --git a/studio/pages/new/[slug].tsx b/studio/pages/new/[slug].tsx index b601cf0432d..97f2d8fdec0 100644 --- a/studio/pages/new/[slug].tsx +++ b/studio/pages/new/[slug].tsx @@ -226,23 +226,25 @@ export const Wizard = observer(() => { - router.push(`/new/${slug}`)} - > - {organizations.map((x: any) => ( - } - > - {x.name} - - ))} - + {organizations.length > 0 && ( + router.push(`/new/${slug}`)} + > + {organizations.map((x: any) => ( + } + > + {x.name} + + ))} + + )} {!isOrganizationOwner && } From 158ed791349f292fd00c34ecd31bfb1db2bf99c9 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Wed, 2 Mar 2022 16:38:40 +0700 Subject: [PATCH 28/38] fix: remove ready variable cos metaStore and contentStore are not observable --- studio/components/layouts/AuthLayout/AuthLayout.tsx | 4 ++-- .../components/layouts/DatabaseLayout/DatabaseLayout.tsx | 4 ++-- studio/components/layouts/DocsLayout/DocsLayout.tsx | 4 ++-- .../components/layouts/ProjectLayout/ProjectLayout.tsx | 9 ++++----- .../layouts/TableEditorLayout/TableEditorLayout.tsx | 6 +++--- studio/pages/project/[ref]/sql/index.tsx | 2 +- studio/stores/content/ProjectContentStore.ts | 3 --- studio/stores/pgmeta/MetaStore.ts | 3 --- 8 files changed, 14 insertions(+), 21 deletions(-) diff --git a/studio/components/layouts/AuthLayout/AuthLayout.tsx b/studio/components/layouts/AuthLayout/AuthLayout.tsx index dfde160c31b..da038f2bfa5 100644 --- a/studio/components/layouts/AuthLayout/AuthLayout.tsx +++ b/studio/components/layouts/AuthLayout/AuthLayout.tsx @@ -24,10 +24,10 @@ const AuthLayout: FC = ({ title, children }) => { const [loaded, setLoaded] = useState(isInitialized) useEffect(() => { - if (meta.ready) { + if (ui.selectedProject) { meta.tables.load() } - }, [meta.ready]) + }, [ui.selectedProject]) useEffect(() => { if (!isLoading && !loaded) { diff --git a/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx b/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx index df81b0d5dec..5df6936a062 100644 --- a/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx +++ b/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx @@ -24,7 +24,7 @@ const DatabaseLayout: FC = ({ title, children }) => { const [loaded, setLoaded] = useState(isInitialized) useEffect(() => { - if (meta.ready) { + if (ui.selectedProject) { // Eventually should only load the required stores based on the pages meta.schemas.load() meta.tables.load() @@ -34,7 +34,7 @@ const DatabaseLayout: FC = ({ title, children }) => { meta.extensions.load() meta.publications.load() } - }, [meta.ready]) + }, [ui.selectedProject]) useEffect(() => { if (!isLoading && !loaded) { diff --git a/studio/components/layouts/DocsLayout/DocsLayout.tsx b/studio/components/layouts/DocsLayout/DocsLayout.tsx index 780080a6ec9..14a85bd5dc9 100644 --- a/studio/components/layouts/DocsLayout/DocsLayout.tsx +++ b/studio/components/layouts/DocsLayout/DocsLayout.tsx @@ -12,10 +12,10 @@ function DocsLayout({ title, children }: { title: string; children: ReactElement const { data, isLoading, error } = meta.openApi useEffect(() => { - if (meta.ready) { + if (ui.selectedProject) { meta.openApi.load() } - }, [meta.ready]) + }, [ui.selectedProject]) if (error) { return ( diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 5f98cde9668..be75878a99f 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -65,9 +65,8 @@ interface MenuBarWrapperProps { } const MenuBarWrapper: FC = observer(({ isLoading, productMenu, children }) => { - const { ui, meta } = useStore() - const project = ui.selectedProject - return <>{!isLoading && productMenu && project !== undefined && meta.ready ? children : null} + const { ui } = useStore() + return <>{!isLoading && productMenu && ui.selectedProject !== undefined ? children : null} }) interface ContentWrapperProps { @@ -75,14 +74,14 @@ interface ContentWrapperProps { } const ContentWrapper: FC = observer(({ isLoading, children }) => { - const { ui, meta } = useStore() + const { ui } = useStore() const router = useRouter() const project = ui.selectedProject const requiresDbConnection: boolean = router.pathname !== '/project/[ref]/settings/general' return ( <> - {isLoading || project === undefined || !meta.ready ? ( + {isLoading || project === undefined ? ( ) : requiresDbConnection ? ( diff --git a/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx b/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx index 729a0276ce2..787635589bd 100644 --- a/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx +++ b/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx @@ -27,17 +27,17 @@ const TableEditorLayout: FC = ({ onDuplicateTable = () => {}, children, }) => { - const { meta } = useStore() + const { meta, ui } = useStore() const { isInitialized, isLoading, error } = meta.tables const [loaded, setLoaded] = useState(isInitialized) useEffect(() => { - if (meta.ready) { + if (ui.selectedProject) { meta.schemas.load() meta.tables.load() } - }, [meta.ready]) + }, [ui.selectedProject]) useEffect(() => { let cancel = false diff --git a/studio/pages/project/[ref]/sql/index.tsx b/studio/pages/project/[ref]/sql/index.tsx index 91bc5a5d937..21e61263935 100644 --- a/studio/pages/project/[ref]/sql/index.tsx +++ b/studio/pages/project/[ref]/sql/index.tsx @@ -34,7 +34,7 @@ const PageConfig = () => { await sqlEditorStore.loadRemotePersistentData(contentStore, (user as any)?.id) } - if (ui.selectedProject == undefined || !meta.ready) { + if (ui.selectedProject === undefined) { return } diff --git a/studio/stores/content/ProjectContentStore.ts b/studio/stores/content/ProjectContentStore.ts index ec05327bc2b..4dd3e2c00ea 100644 --- a/studio/stores/content/ProjectContentStore.ts +++ b/studio/stores/content/ProjectContentStore.ts @@ -10,7 +10,6 @@ import { API_URL } from 'lib/constants' // but use the one under the stores folder projectContentStore first while we transition export interface IProjectContentStore { - ready: boolean isLoading: boolean isInitialized: boolean error: any @@ -22,7 +21,6 @@ export interface IProjectContentStore { } export default class ProjectContentStore implements IProjectContentStore { - ready: boolean rootStore: IRootStore STATES = { @@ -40,7 +38,6 @@ export default class ProjectContentStore implements IProjectContentStore { constructor(rootStore: IRootStore, options: { projectRef: string }) { const { projectRef } = options - this.ready = projectRef !== '' this.rootStore = rootStore this.baseUrl = `${API_URL}/projects/${projectRef}/content` makeAutoObservable(this) diff --git a/studio/stores/pgmeta/MetaStore.ts b/studio/stores/pgmeta/MetaStore.ts index 0537bb4538d..c2954314903 100644 --- a/studio/stores/pgmeta/MetaStore.ts +++ b/studio/stores/pgmeta/MetaStore.ts @@ -43,7 +43,6 @@ const BATCH_SIZE = 1000 const CHUNK_SIZE = 1024 * 1024 * 0.25 // 0.25MB export interface IMetaStore { - ready: boolean excludedSchemas: string[] openApi: IOpenApiStore @@ -115,7 +114,6 @@ export default class MetaStore implements IMetaStore { extensions: ExtensionsStore publications: PublicationStore - ready: boolean connectionString?: string baseUrl: string queryBaseUrl: string @@ -133,7 +131,6 @@ export default class MetaStore implements IMetaStore { constructor(rootStore: IRootStore, options: { projectRef: string; connectionString: string }) { const { projectRef, connectionString } = options - this.ready = projectRef !== '' this.rootStore = rootStore this.baseUrl = `/api/pg-meta/${projectRef}` this.queryBaseUrl = `${API_URL}/pg-meta/${projectRef}` From dddd347e00dc68c16f68a4df021fb7f719a0883b Mon Sep 17 00:00:00 2001 From: phamhieu Date: Wed, 2 Mar 2022 16:43:37 +0700 Subject: [PATCH 29/38] fix: render OrganizationSettings when selected org is available --- studio/pages/org/[slug]/settings.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/studio/pages/org/[slug]/settings.tsx b/studio/pages/org/[slug]/settings.tsx index eaa953be40a..a3ff6ad7b78 100644 --- a/studio/pages/org/[slug]/settings.tsx +++ b/studio/pages/org/[slug]/settings.tsx @@ -94,6 +94,7 @@ const OrgSettings = () => { export default withAuth(observer(OrgSettings)) const PageLayout = observer(() => { + const { ui } = useStore() const PageState: any = useContext(PageContext) return ( @@ -107,7 +108,7 @@ const PageLayout = observer(() => { }, ]} > - + {ui.selectedOrganization && } ) }) From 91644069baeb7261e64ccbbc9c260cdf194ce9b2 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Wed, 2 Mar 2022 16:21:46 +0000 Subject: [PATCH 30/38] fix: issue with code labels in API settings panel --- .../components/to-be-cleaned/DisplayProjectSettings.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/studio/components/to-be-cleaned/DisplayProjectSettings.tsx b/studio/components/to-be-cleaned/DisplayProjectSettings.tsx index 6ab1f91e4a4..a120dc92352 100644 --- a/studio/components/to-be-cleaned/DisplayProjectSettings.tsx +++ b/studio/components/to-be-cleaned/DisplayProjectSettings.tsx @@ -71,18 +71,16 @@ export const DisplayApiSettings = () => { label={ <> {x.tags?.split(',').map((x: any, i: number) => ( - + {x} ))} {x.tags === 'service_role' && ( <> - {'secret'} + {'secret'} )} - {x.tags === 'anon' && ( - {'public'} - )} + {x.tags === 'anon' && {'public'}} } readOnly From dad3b3e7f1045b2a7eb9b12e9819c5b0945bd98f Mon Sep 17 00:00:00 2001 From: Isaiah Hamilton Date: Wed, 2 Mar 2022 17:48:14 -0500 Subject: [PATCH 31/38] fix: feedback button --- .../LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx index 650c4d938cf..0dbd3a022e0 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx @@ -34,7 +34,8 @@ const FeedbackDropdown = () => { type="default" icon={} > - Feedback on this page? + Feedback + Feedback on this page? ) From 88847226e2c1802f14c0b775c8ad300bd26a84f8 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Thu, 3 Mar 2022 09:58:54 +0700 Subject: [PATCH 32/38] fix: missing observer on OrganizationDropdown --- .../to-be-cleaned/Dropdown/OrganizationDropdown.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/studio/components/to-be-cleaned/Dropdown/OrganizationDropdown.js b/studio/components/to-be-cleaned/Dropdown/OrganizationDropdown.js index 48652fc7519..97f93d6cba9 100644 --- a/studio/components/to-be-cleaned/Dropdown/OrganizationDropdown.js +++ b/studio/components/to-be-cleaned/Dropdown/OrganizationDropdown.js @@ -1,7 +1,8 @@ import React from 'react' import { useRouter } from 'next/router' import { toJS } from 'mobx' -import { Button, Divider, Dropdown, IconPlus } from '@supabase/ui' +import { Button, Dropdown, IconPlus } from '@supabase/ui' +import { observer } from 'mobx-react-lite' const OrganizationDropdown = ({ organizations }) => { const router = useRouter() @@ -39,4 +40,4 @@ const OrganizationDropdown = ({ organizations }) => { ) } -export default OrganizationDropdown +export default observer(OrganizationDropdown) From 1427a53fed1b8e7cdbf17f616b0abd2ab1a713c1 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 3 Mar 2022 11:01:50 +0800 Subject: [PATCH 33/38] Update ProjectList component --- .../Home/ProjectList/ProjectList.tsx | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx index 540e544fa99..db63585245f 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx @@ -1,5 +1,5 @@ import { FC } from 'react' -import { Button, IconPlus, Loading, Typography } from '@supabase/ui' +import { Button, IconPlus, Loading } from '@supabase/ui' import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' @@ -8,7 +8,6 @@ import { Organization, Project } from 'types' import { makeRandomString } from 'lib/helpers' import { PROJECT_STATUS } from 'lib/constants' import ProjectCard from './ProjectCard' -import PausedProjectCard from './PausedProjectCard' interface Props { onSelectRestore: (project: Project) => void @@ -36,35 +35,36 @@ const ProjectList: FC = ({ const isEmpty = sortedProjects?.length == 0 return (
+

{name}

-

{name}

+
    + {!isLoading && isEmpty && ( +
    +
    +

    No projects

    +

    + Get started by creating a new project. +

    +
    +
    + +
    +
    + )} + {sortedProjects?.map((project: Project) => ( + onSelectDelete(project)} + onSelectRestore={() => onSelectRestore(project)} + /> + ))} +
-
    - {!isLoading && isEmpty && ( -
    -
    -

    No projects

    -

    - Get started by creating a new project. -

    -
    -
    - -
    -
    - )} - {sortedProjects?.map((project: Project) => ( - onSelectDelete(project)} - onSelectRestore={() => onSelectRestore(project)} - /> - ))} -
) })} From 7b07f647aa53b2d6e3788feee4403eee5c15b4d5 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 3 Mar 2022 11:11:32 +0800 Subject: [PATCH 34/38] Fix generic routes page --- studio/pages/project/_/[[...routeSlug]].tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/studio/pages/project/_/[[...routeSlug]].tsx b/studio/pages/project/_/[[...routeSlug]].tsx index 73500eee0fc..b18f770153a 100644 --- a/studio/pages/project/_/[[...routeSlug]].tsx +++ b/studio/pages/project/_/[[...routeSlug]].tsx @@ -5,9 +5,7 @@ import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' import { withAuth } from 'hooks' -import BaseLayout from 'components/layouts' import ProjectList from 'components/interfaces/Home/ProjectList' -import { Typography } from '@supabase/ui' interface Props {} @@ -50,7 +48,7 @@ const GenericProjectPage: NextPage = () => { } return ( - +

Select a project to continue

@@ -62,7 +60,7 @@ const GenericProjectPage: NextPage = () => { />
-
+
) } From 2c38185545c2e38c349cc5419acd69db887a320d Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 3 Mar 2022 11:22:53 +0800 Subject: [PATCH 35/38] Improve loading UX for orgs --- .../Home/ProjectList/ProjectList.tsx | 8 ++--- studio/pages/index.tsx | 34 ++++++++++++------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx index db63585245f..ca96483e6e7 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx @@ -1,5 +1,5 @@ import { FC } from 'react' -import { Button, IconPlus, Loading } from '@supabase/ui' +import { Button, IconLoader, IconPlus, Loading } from '@supabase/ui' import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' @@ -23,7 +23,7 @@ const ProjectList: FC = ({ const router = useRouter() const { app } = useStore() const { organizations, projects } = app - const { isLoading } = projects + const { isLoading: isLoadingProjects } = projects return ( <> @@ -36,9 +36,9 @@ const ProjectList: FC = ({ return (

{name}

- +