From b104c6c8ab4daa7a4d1823a82986f29389b7fa2a Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 14 Jul 2023 09:20:15 +0800 Subject: [PATCH 01/53] Scaffold nav layout change --- .../Home/ProjectList/ProjectCard.tsx | 2 +- .../layouts/AppLayout/AppLayout.tsx | 16 ++++ .../layouts/AppLayout/HeaderBar.tsx | 47 +++++++++++ .../AppLayout/OrganizationDropdown.tsx | 78 +++++++++++++++++++ studio/pages/org/[slug]/index.tsx | 63 +++++++++++++++ 5 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 studio/components/layouts/AppLayout/AppLayout.tsx create mode 100644 studio/components/layouts/AppLayout/HeaderBar.tsx create mode 100644 studio/components/layouts/AppLayout/OrganizationDropdown.tsx create mode 100644 studio/pages/org/[slug]/index.tsx diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index 20bf8488a3a..ad3689be4f9 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -24,7 +24,7 @@ const ProjectCard = ({ project, rewriteHref }: ProjectCardProps) => { const isRestoring = project.status === PROJECT_STATUS.RESTORING return ( -
  • +
  • ) => { + return ( +
    + + {children} +
    + ) +} + +export default AppLayout diff --git a/studio/components/layouts/AppLayout/HeaderBar.tsx b/studio/components/layouts/AppLayout/HeaderBar.tsx new file mode 100644 index 00000000000..543159b4785 --- /dev/null +++ b/studio/components/layouts/AppLayout/HeaderBar.tsx @@ -0,0 +1,47 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' +import FeedbackDropdown from '../ProjectLayout/LayoutHeader/FeedbackDropdown' +import { Button, Dropdown, IconHelpCircle, IconInbox } from 'ui' +import { useProfile } from 'lib/profile' +import OrganizationDropdown from './OrganizationDropdown' + +const HeaderBar = () => { + const router = useRouter() + const { profile } = useProfile() + + return ( +
    + {/* Organization + Project */} +
    + + + Supabase + + + +
    + + {/* Account, Settings, etc */} +
    + +
    +
    + ) +} + +export default HeaderBar diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx new file mode 100644 index 00000000000..b5e5a9426df --- /dev/null +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -0,0 +1,78 @@ +import { useRouter } from 'next/router' +import { Button, Dropdown, IconChevronDown, IconChevronsDown, IconPlus } from 'ui' + +import { useOrganizationsQuery } from 'data/organizations/organizations-query' +import { useFlag, useSelectedOrganization, useStore } from 'hooks' +import { IS_PLATFORM } from 'lib/constants' + +const OrganizationDropdown = () => { + const router = useRouter() + const { ui } = useStore() + const { data: organizations } = useOrganizationsQuery() + const selectedOrganization = useSelectedOrganization() + const orgCreationV2 = useFlag('orgcreationv2') + + return IS_PLATFORM ? ( + + {organizations + ?.sort((a, b) => a.name.localeCompare(b.name)) + .map((x) => { + const slug = x.slug + + return ( + { + if (!slug) { + // The user should not see this error as the page should + // be rerendered with the value of slug before they can click. + // It is just here in case they are the flash. + return ui.setNotification({ + category: 'error', + message: + 'Could not navigate to organization settings, please try again or contact support', + }) + } + + router.push({ + pathname: `/org/[slug]/general`, + query: { slug }, + hash: router.asPath.split('#')[1]?.toLowerCase(), + }) + }} + > + {x.name} + + ) + })} + + } onClick={() => router.push(`/new`)}> + New organization + + {orgCreationV2 && ( + } + onClick={() => router.push(`/new-with-subscription`)} + > + New organization V2 + + )} + + } + > + + + ) : ( + + ) +} + +export default OrganizationDropdown diff --git a/studio/pages/org/[slug]/index.tsx b/studio/pages/org/[slug]/index.tsx new file mode 100644 index 00000000000..0daf06e04b4 --- /dev/null +++ b/studio/pages/org/[slug]/index.tsx @@ -0,0 +1,63 @@ +import ProjectCard from 'components/interfaces/Home/ProjectList/ProjectCard' +import ShimmeringCard from 'components/interfaces/Home/ProjectList/ShimmeringCard' +import AppLayout from 'components/layouts/AppLayout/AppLayout' +import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' +import AlertError from 'components/ui/AlertError' +import { useProjectsQuery } from 'data/projects/projects-query' +import { useSelectedOrganization } from 'hooks' +import Link from 'next/link' +import { NextPageWithLayout } from 'types' +import { Button, IconPlus } from 'ui' + +const ProjectsPage: NextPageWithLayout = () => { + const { + data: allProjects, + error: projectsError, + isLoading: isLoadingProjects, + isError: isErrorProjects, + isSuccess: isSuccessProjects, + } = useProjectsQuery() + const organization = useSelectedOrganization() + const projects = allProjects?.filter((project) => project.organization_id === organization?.id) + + if (!organization) { + // Return a 404 page + } + + return ( + + +
    +
    +

    Projects

    + + + + + +
    + {isLoadingProjects && ( +
      + + +
    + )} + {isErrorProjects && ( + + )} + {isSuccessProjects && ( +
      + {projects?.map((project) => ( + + ))} +
    + )} +
    +
    +
    + ) +} + +ProjectsPage.getLayout = (page) => {page} + +export default ProjectsPage From 5d1b88d4dc6f92362c58ae9132307382f8ab1f48 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 14 Jul 2023 14:44:02 +0800 Subject: [PATCH 02/53] Add help, notifications, feedback widgets to new nav bar, scaffold user settings dropdown --- .../layouts/AccountLayout/AccountLayout.tsx | 1 - .../layouts/AppLayout/HeaderBar.tsx | 24 +++----- .../AppLayout/OrganizationDropdown.tsx | 58 +++++++----------- .../AppLayout/UserSettingsDropdown.tsx | 50 +++++++++++++++ .../LayoutHeader/HelpPopover.tsx | 61 +++++++++++++------ .../NotificationsPopover/index.tsx | 61 +++++++++++++++---- 6 files changed, 171 insertions(+), 84 deletions(-) create mode 100644 studio/components/layouts/AppLayout/UserSettingsDropdown.tsx diff --git a/studio/components/layouts/AccountLayout/AccountLayout.tsx b/studio/components/layouts/AccountLayout/AccountLayout.tsx index f4b7599e8a6..3abeb892928 100644 --- a/studio/components/layouts/AccountLayout/AccountLayout.tsx +++ b/studio/components/layouts/AccountLayout/AccountLayout.tsx @@ -28,7 +28,6 @@ const AccountLayout = ({ children, title, breadcrumbs }: PropsWithChildren { await signOut() - await router.push('/sign-in') } diff --git a/studio/components/layouts/AppLayout/HeaderBar.tsx b/studio/components/layouts/AppLayout/HeaderBar.tsx index 543159b4785..2e2dfa5dad6 100644 --- a/studio/components/layouts/AppLayout/HeaderBar.tsx +++ b/studio/components/layouts/AppLayout/HeaderBar.tsx @@ -1,19 +1,18 @@ import Link from 'next/link' import { useRouter } from 'next/router' import FeedbackDropdown from '../ProjectLayout/LayoutHeader/FeedbackDropdown' -import { Button, Dropdown, IconHelpCircle, IconInbox } from 'ui' -import { useProfile } from 'lib/profile' +import HelpPopover from '../ProjectLayout/LayoutHeader/HelpPopover' +import NotificationsPopover from '../ProjectLayout/LayoutHeader/NotificationsPopover' import OrganizationDropdown from './OrganizationDropdown' +import UserSettingsDropdown from './UserSettingsDropdown' const HeaderBar = () => { const router = useRouter() - const { profile } = useProfile() return ( ) diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index b5e5a9426df..3149b62b941 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -1,9 +1,10 @@ import { useRouter } from 'next/router' -import { Button, Dropdown, IconChevronDown, IconChevronsDown, IconPlus } from 'ui' +import { Button, Dropdown, IconChevronDown, IconChevronsDown, IconCode, IconPlus } from 'ui' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useFlag, useSelectedOrganization, useStore } from 'hooks' import { IS_PLATFORM } from 'lib/constants' +import Link from 'next/link' const OrganizationDropdown = () => { const router = useRouter() @@ -21,50 +22,35 @@ const OrganizationDropdown = () => { {organizations ?.sort((a, b) => a.name.localeCompare(b.name)) .map((x) => { - const slug = x.slug - + // [Joshen] Improvement: maintain URL if navigating in between org pages return ( - { - if (!slug) { - // The user should not see this error as the page should - // be rerendered with the value of slug before they can click. - // It is just here in case they are the flash. - return ui.setNotification({ - category: 'error', - message: - 'Could not navigate to organization settings, please try again or contact support', - }) - } - - router.push({ - pathname: `/org/[slug]/general`, - query: { slug }, - hash: router.asPath.split('#')[1]?.toLowerCase(), - }) - }} - > - {x.name} - + + + {x.name} + + ) })} - } onClick={() => router.push(`/new`)}> - New organization - + + + }>New organization + + {orgCreationV2 && ( - } - onClick={() => router.push(`/new-with-subscription`)} - > - New organization V2 - + + + }>New organization V2 + + )} } > - diff --git a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx new file mode 100644 index 00000000000..6f62bd4f28f --- /dev/null +++ b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx @@ -0,0 +1,50 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' +import { Dropdown, IconLogOut } from 'ui' + +import { useSignOut } from 'lib/auth' +import { useProfile } from 'lib/profile' + +const UserSettingsDropdown = () => { + const signOut = useSignOut() + const router = useRouter() + const { profile } = useProfile() + + const onClickLogout = async () => { + await signOut() + await router.push('/sign-in') + } + + return ( + + + Preferences + + , + + + Access tokens + + , + , + } + onClick={() => onClickLogout()} + > + Logout + , + ]} + > +
    + {profile?.first_name[0]} +
    +
    + ) +} + +export default UserSettingsDropdown diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx index 6b6add879bd..770b8d65722 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx @@ -1,22 +1,23 @@ +import Image from 'next/image' +import Link from 'next/link' +import { useRouter } from 'next/router' +import SVG from 'react-inlinesvg' import { Button, + IconActivity, + IconBookOpen, IconHelpCircle, IconMail, IconMessageCircle, Popover, - IconBookOpen, - IconActivity, } from 'ui' -import { useRouter } from 'next/router' -import { FC } from 'react' -import Link from 'next/link' -import Image from 'next/image' +import * as Tooltip from '@radix-ui/react-tooltip' -import SVG from 'react-inlinesvg' +interface HelpPopoverProps { + alt?: boolean +} -interface Props {} - -const HelpPopover: FC = () => { +const HelpPopover = ({ alt = false }: HelpPopoverProps) => { const router = useRouter() const projectRef = router.query.ref const supportUrl = `/support/new${projectRef ? `?ref=${projectRef}` : ''}` @@ -127,13 +128,39 @@ const HelpPopover: FC = () => { } > - + + + + + {alt && ( + + + +
    +

    Help

    +
    +
    +
    + )} +
    ) } diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx index 1ff8530f549..f4e3db7e003 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx @@ -12,15 +12,19 @@ import { Fragment, useState } from 'react' import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog' import { useNotificationsQuery } from 'data/notifications/notifications-query' import { getProjectDetail } from 'data/projects/project-detail-query' -import { invalidateProjectsQuery, setProjectPostgrestStatus } from 'data/projects/projects-query' +import { setProjectPostgrestStatus } from 'data/projects/projects-query' import { useStore } from 'hooks' import { delete_, patch, post } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { Project } from 'types' -import { Alert, Button, IconArrowRight, IconBell, Popover } from 'ui' +import { Alert, Button, IconArrowRight, IconBell, IconInbox, Popover } from 'ui' import NotificationRow from './NotificationRow' -const NotificationsPopover = () => { +interface NotificationsPopoverProps { + alt?: boolean +} + +const NotificationsPopover = ({ alt = false }: NotificationsPopoverProps) => { const queryClient = useQueryClient() const router = useRouter() const { meta, ui } = useStore() @@ -35,9 +39,10 @@ const NotificationsPopover = () => { if (!notifications) return <> - const hasNewNotifications = notifications?.some( + const newNotifications = notifications?.filter( (notification) => notification.notification_status === NotificationStatus.New ) + const hasNewNotifications = newNotifications.length > 0 const onOpenChange = async (open: boolean) => { // TODO(alaister): move this to a mutation @@ -232,21 +237,51 @@ const NotificationsPopover = () => { > -
    +
    + {/* hasNewNotifications */} + {true && ( + <> + {alt ? null : ( + //
    + //

    5

    + //
    +
    +
    +
    +
    + )} + + )} - {hasNewNotifications && ( -
    -
    -
    -
    - )}
    From 0672fe046444ce9bfa2ebd9c028fed851ad19760 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 14 Jul 2023 14:46:30 +0800 Subject: [PATCH 03/53] Add alt prop to feedback widget --- studio/components/layouts/AppLayout/HeaderBar.tsx | 2 +- .../LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/studio/components/layouts/AppLayout/HeaderBar.tsx b/studio/components/layouts/AppLayout/HeaderBar.tsx index 2e2dfa5dad6..061df2adebe 100644 --- a/studio/components/layouts/AppLayout/HeaderBar.tsx +++ b/studio/components/layouts/AppLayout/HeaderBar.tsx @@ -25,7 +25,7 @@ const HeaderBar = () => {
    - + diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx index 34efce3944f..00da09d71eb 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx @@ -2,7 +2,11 @@ import { useState } from 'react' import { Button, IconMessageCircle, Popover } from 'ui' import FeedbackWidget from './FeedbackWidget' -const FeedbackDropdown = () => { +interface FeedbackDropdownProps { + alt?: boolean +} + +const FeedbackDropdown = ({ alt = false }: FeedbackDropdownProps) => { const [isOpen, setIsOpen] = useState(false) const [feedback, setFeedback] = useState('') const [screenshot, setScreenshot] = useState() @@ -41,7 +45,9 @@ const FeedbackDropdown = () => { asChild onClick={onOpen} type="default" - icon={} + icon={ + alt ? null : + } > Feedback From 14e8c19c965e62e1c351164d9d3261ecb49eaeca Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 17 Jul 2023 22:31:00 +0800 Subject: [PATCH 04/53] Add AppLayout and simplify height logic in layout component --- .../{HeaderBar.tsx => AppHeader.tsx} | 14 +- .../layouts/AppLayout/AppLayout.tsx | 9 +- .../AppLayout/OrganizationDropdown.tsx | 17 +- .../LayoutHeader/ProjectDropdown.tsx | 21 ++- .../NavigationBar/NavigationBar.tsx | 30 ++-- .../layouts/ProjectLayout/ProductMenuBar.tsx | 16 +- .../layouts/ProjectLayout/ProjectLayout.tsx | 165 ++++++++++-------- studio/pages/org/[slug]/index.tsx | 53 +++--- 8 files changed, 181 insertions(+), 144 deletions(-) rename studio/components/layouts/AppLayout/{HeaderBar.tsx => AppHeader.tsx} (69%) diff --git a/studio/components/layouts/AppLayout/HeaderBar.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx similarity index 69% rename from studio/components/layouts/AppLayout/HeaderBar.tsx rename to studio/components/layouts/AppLayout/AppHeader.tsx index 061df2adebe..f27e9d5a24c 100644 --- a/studio/components/layouts/AppLayout/HeaderBar.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -1,18 +1,23 @@ import Link from 'next/link' import { useRouter } from 'next/router' + import FeedbackDropdown from '../ProjectLayout/LayoutHeader/FeedbackDropdown' import HelpPopover from '../ProjectLayout/LayoutHeader/HelpPopover' import NotificationsPopover from '../ProjectLayout/LayoutHeader/NotificationsPopover' import OrganizationDropdown from './OrganizationDropdown' import UserSettingsDropdown from './UserSettingsDropdown' +import { useSelectedOrganization, useSelectedProject } from 'hooks' +import ProjectDropdown from '../ProjectLayout/LayoutHeader/ProjectDropdown' -const HeaderBar = () => { +const AppHeader = () => { const router = useRouter() + const project = useSelectedProject() + const organization = useSelectedOrganization() return ( -
    +
    - + { +
    @@ -34,4 +40,4 @@ const HeaderBar = () => { ) } -export default HeaderBar +export default AppHeader diff --git a/studio/components/layouts/AppLayout/AppLayout.tsx b/studio/components/layouts/AppLayout/AppLayout.tsx index a14f74d752c..275113dbde8 100644 --- a/studio/components/layouts/AppLayout/AppLayout.tsx +++ b/studio/components/layouts/AppLayout/AppLayout.tsx @@ -1,13 +1,14 @@ -import Image from 'next/image' -import Link from 'next/link' import { PropsWithChildren } from 'react' -import HeaderBar from './HeaderBar' +import AppHeader from './AppHeader' +import { useFlag } from 'hooks' const AppLayout = ({ children }: PropsWithChildren<{}>) => { + const navLayoutV2 = useFlag('navigationLayoutV2') + return (
    - + {navLayoutV2 && } {children}
    ) diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index 3149b62b941..52fe6f28602 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -1,18 +1,18 @@ -import { useRouter } from 'next/router' -import { Button, Dropdown, IconChevronDown, IconChevronsDown, IconCode, IconPlus } from 'ui' +import Link from 'next/link' +import { Badge, Button, Dropdown, IconCode, IconPlus } from 'ui' import { useOrganizationsQuery } from 'data/organizations/organizations-query' -import { useFlag, useSelectedOrganization, useStore } from 'hooks' +import { useFlag, useSelectedOrganization } from 'hooks' import { IS_PLATFORM } from 'lib/constants' -import Link from 'next/link' +import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' const OrganizationDropdown = () => { - const router = useRouter() - const { ui } = useStore() const { data: organizations } = useOrganizationsQuery() const selectedOrganization = useSelectedOrganization() const orgCreationV2 = useFlag('orgcreationv2') + const { data, isSuccess } = useOrgSubscriptionQuery({ orgSlug: selectedOrganization?.slug }) + return IS_PLATFORM ? ( { iconRight={} > {selectedOrganization?.name} + {isSuccess && ( + + {data?.plan.name} + + )} ) : ( diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx index b56940ded52..784bf0589a7 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' import { ParsedUrlQuery } from 'querystring' -import { Button, Dropdown, IconPlus, Popover } from 'ui' +import { Button, Dropdown, IconCode, IconPlus, Popover } from 'ui' import { useProjectsQuery } from 'data/projects/projects-query' import { useSelectedOrganization, useSelectedProject } from 'hooks' @@ -30,7 +30,11 @@ export const sanitizeRoute = (route: string, routerQueries: ParsedUrlQuery) => { } } -const ProjectDropdown = () => { +interface ProjectDropdownProps { + alt?: boolean +} + +const ProjectDropdown = ({ alt }: ProjectDropdownProps) => { const selectedOrganization = useSelectedOrganization() const selectedProject = useSelectedProject() const { data: allProjects } = useProjectsQuery() @@ -75,13 +79,18 @@ const ProjectDropdown = () => { } > - ) : ( - ) } diff --git a/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx b/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx index 50bdff1621b..2ef126699c0 100644 --- a/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx +++ b/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx @@ -2,7 +2,6 @@ import * as Tooltip from '@radix-ui/react-tooltip' import { isUndefined } from 'lodash' import Link from 'next/link' import { useRouter } from 'next/router' -import { FC } from 'react' import { useTheme } from 'common' import { useParams } from 'common/hooks' @@ -27,15 +26,13 @@ import { } from './NavigationBar.utils' import NavigationIconButton from './NavigationIconButton' -interface Props {} - -const NavigationBar: FC = ({}) => { +const NavigationBar = ({}) => { const router = useRouter() const { isDarkMode, toggleTheme } = useTheme() const { ref: projectRef } = useParams() const { project } = useProjectContext() - const ongoingIncident = useFlag('ongoingIncident') + const navLayoutV2 = useFlag('navigationLayoutV2') const activeRoute = router.pathname.split('/')[3] const toolRoutes = generateToolRoutes(projectRef, project) @@ -46,22 +43,23 @@ const NavigationBar: FC = ({}) => { const { setIsOpen } = useCommandMenu() return (
      - - - Supabase - - + {!navLayoutV2 && ( + + + Supabase + + + )} = ({}) => { link: `/project/${projectRef}`, }} /> -
      +
      {toolRoutes.map((route) => ( = ({ title, children }) => { - const ongoingIncident = useFlag('ongoingIncident') - const maxHeight = ongoingIncident ? 'calc(100vh - 44px)' : '100vh' - +const ProductMenuBar = ({ title, children }: PropsWithChildren) => { return (
      = ({ title, children }) => { >

      {title}

      -
      {children}
      +
      + {children} +
      ) } diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index e2dae22e23c..7e72ae8ca00 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -16,6 +16,7 @@ import { ProjectContextProvider } from './ProjectContext' import ProjectPausedState from './ProjectPausedState' import RestoringState from './RestoringState' import UpgradingState from './UpgradingState' +import AppLayout from '../AppLayout/AppLayout' export interface ProjectLayoutProps { title?: string @@ -39,61 +40,70 @@ const ProjectLayout = ({ }: PropsWithChildren) => { const router = useRouter() const { ref: projectRef } = useParams() - const ongoingIncident = useFlag('ongoingIncident') const selectedOrganization = useSelectedOrganization() const selectedProject = useSelectedProject() const projectName = selectedProject?.name const organizationName = selectedOrganization?.name + const ongoingIncident = useFlag('ongoingIncident') + const navLayoutV2 = useFlag('navigationLayoutV2') + const appHeaderHeight = navLayoutV2 ? 48 : 0 + const isPaused = selectedProject?.status === PROJECT_STATUS.INACTIVE const ignorePausedState = router.pathname === '/project/[ref]' || router.pathname.includes('/project/[ref]/settings') const showPausedState = isPaused && !ignorePausedState return ( - - - - {title - ? `${title} | Supabase` - : selectedTable - ? `${selectedTable} | ${projectName} | ${organizationName} | Supabase` - : projectName - ? `${projectName} | ${organizationName} | Supabase` - : organizationName - ? `${organizationName} | Supabase` - : 'Supabase'} - - - -
      - {/* Left-most navigation side bar to access products */} - {!hideIconBar && } - - {/* Product menu bar */} - {!showPausedState && ( - - {productMenu} - - )} - -
      + + + + {title + ? `${title} | Supabase` + : selectedTable + ? `${selectedTable} | ${projectName} | ${organizationName} | Supabase` + : projectName + ? `${projectName} | ${organizationName} | Supabase` + : organizationName + ? `${organizationName} | Supabase` + : 'Supabase'} + + + +
      - {!hideHeader && } - {showPausedState ? ( -
      -
      - -
      -
      - ) : ( - {children} + {/* Left-most navigation side bar to access products */} + {!hideIconBar && } + + {/* Product menu bar */} + {!showPausedState && ( + + {productMenu} + )} -
      -
      -
      + +
      + {!navLayoutV2 && !hideHeader && } + {showPausedState ? ( +
      +
      + +
      +
      + ) : ( + {children} + )} +
      +
    + + ) } @@ -190,44 +200,53 @@ export const ProjectLayoutNonBlocking = ({ const selectedProject = useSelectedProject() const router = useRouter() const { ref: projectRef } = useParams() - const ongoingIncident = useFlag('ongoingIncident') const isPaused = selectedProject?.status === PROJECT_STATUS.INACTIVE const ignorePausedState = router.pathname === '/project/[ref]' || router.pathname.includes('/project/[ref]/settings') const showPausedState = isPaused && !ignorePausedState + const ongoingIncident = useFlag('ongoingIncident') + const navLayoutV2 = useFlag('navigationLayoutV2') + const appHeaderHeight = navLayoutV2 ? 48 : 0 + return ( - - - {title ? `${title} | Supabase` : 'Supabase'} - - - -
    - {/* Left-most navigation side bar to access products */} - {!hideIconBar && } - - {/* Product menu bar */} - {productMenu && !showPausedState && ( - {productMenu} - )} - -
    + + + {title ? `${title} | Supabase` : 'Supabase'} + + + +
    - {!hideHeader && } - {showPausedState ? ( -
    -
    - -
    -
    - ) : ( - children + {/* Left-most navigation side bar to access products */} + {!hideIconBar && } + + {/* Product menu bar */} + {productMenu && !showPausedState && ( + {productMenu} )} -
    -
    -
    + +
    + {!hideHeader && } + {showPausedState ? ( +
    +
    + +
    +
    + ) : ( + children + )} +
    +
    + + ) } diff --git a/studio/pages/org/[slug]/index.tsx b/studio/pages/org/[slug]/index.tsx index 0daf06e04b4..263a42d7732 100644 --- a/studio/pages/org/[slug]/index.tsx +++ b/studio/pages/org/[slug]/index.tsx @@ -1,3 +1,6 @@ +import Link from 'next/link' +import { Button, IconPlus } from 'ui' + import ProjectCard from 'components/interfaces/Home/ProjectList/ProjectCard' import ShimmeringCard from 'components/interfaces/Home/ProjectList/ShimmeringCard' import AppLayout from 'components/layouts/AppLayout/AppLayout' @@ -5,9 +8,7 @@ import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' import AlertError from 'components/ui/AlertError' import { useProjectsQuery } from 'data/projects/projects-query' import { useSelectedOrganization } from 'hooks' -import Link from 'next/link' import { NextPageWithLayout } from 'types' -import { Button, IconPlus } from 'ui' const ProjectsPage: NextPageWithLayout = () => { const { @@ -27,31 +28,33 @@ const ProjectsPage: NextPageWithLayout = () => { return ( -
    -
    +
    + + + + + +

    Projects

    - - - - - + {isLoadingProjects && ( +
      + + +
    + )} + {isErrorProjects && ( + + )} + {isSuccessProjects && ( +
      + {projects?.map((project) => ( + + ))} +
    + )}
    - {isLoadingProjects && ( -
      - - -
    - )} - {isErrorProjects && ( - - )} - {isSuccessProjects && ( -
      - {projects?.map((project) => ( - - ))} -
    - )}
    From 93453141a4b798b469fed7b6eca32d6dd6ec65ae Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 17 Jul 2023 23:14:50 +0800 Subject: [PATCH 05/53] Add loaders for org and project dropdowns and add more options in user settings dropdown --- .../layouts/AppLayout/AppHeader.tsx | 7 +- .../AppLayout/OrganizationDropdown.tsx | 7 +- .../AppLayout/UserSettingsDropdown.tsx | 63 +++++++- .../LayoutHeader/ProjectDropdown.tsx | 7 +- .../NavigationBar/NavigationBar.tsx | 145 +++++++++--------- studio/components/to-be-cleaned/KeyMap.tsx | 4 +- 6 files changed, 147 insertions(+), 86 deletions(-) diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index f27e9d5a24c..b89f7cbcc22 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -1,21 +1,20 @@ import Link from 'next/link' import { useRouter } from 'next/router' +import { useSelectedOrganization } from 'hooks' import FeedbackDropdown from '../ProjectLayout/LayoutHeader/FeedbackDropdown' import HelpPopover from '../ProjectLayout/LayoutHeader/HelpPopover' import NotificationsPopover from '../ProjectLayout/LayoutHeader/NotificationsPopover' +import ProjectDropdown from '../ProjectLayout/LayoutHeader/ProjectDropdown' import OrganizationDropdown from './OrganizationDropdown' import UserSettingsDropdown from './UserSettingsDropdown' -import { useSelectedOrganization, useSelectedProject } from 'hooks' -import ProjectDropdown from '../ProjectLayout/LayoutHeader/ProjectDropdown' const AppHeader = () => { const router = useRouter() - const project = useSelectedProject() const organization = useSelectedOrganization() return ( -
    +
    diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index 52fe6f28602..c6ec2767203 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -5,14 +5,19 @@ import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useFlag, useSelectedOrganization } from 'hooks' import { IS_PLATFORM } from 'lib/constants' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' +import ShimmeringLoader from 'components/ui/ShimmeringLoader' const OrganizationDropdown = () => { - const { data: organizations } = useOrganizationsQuery() + const { data: organizations, isLoading: isLoadingOrganizations } = useOrganizationsQuery() const selectedOrganization = useSelectedOrganization() const orgCreationV2 = useFlag('orgcreationv2') const { data, isSuccess } = useOrgSubscriptionQuery({ orgSlug: selectedOrganization?.slug }) + if (isLoadingOrganizations) { + return + } + return IS_PLATFORM ? ( { const signOut = useSignOut() const router = useRouter() const { profile } = useProfile() + const { setIsOpen } = useCommandMenu() + const { isDarkMode, toggleTheme } = useTheme() const onClickLogout = async () => { await signOut() @@ -20,6 +24,13 @@ const UserSettingsDropdown = () => { align="end" side="bottom" overlay={[ +
    +

    + {profile?.first_name} {profile?.last_name} +

    +

    {profile?.primary_email}

    +
    , + ,
    Preferences @@ -30,13 +41,51 @@ const UserSettingsDropdown = () => { Access tokens , - , - } - onClick={() => onClickLogout()} + , + setIsOpen(true)}> +
    +

    Quick search

    +
    + +
    +
    +
    , + // + //
    + //

    Theme

    + // toggleTheme(e === 'dark')} + // // className="w-[200px]" + // > + // + // Dark + // + // + // Light + // + // + //
    + //
    , + , + Theme, + toggleTheme(e === 'dark')} > - Logout + {/* [Joshen] Removing system default for now, needs to be supported in useTheme from common packages */} + {/* System default */} + Dark + Light + , + , + onClickLogout()}> +
    +

    Logout

    + +
    , ]} > diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx index 784bf0589a7..3d873b91d80 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx @@ -6,6 +6,7 @@ import { Button, Dropdown, IconCode, IconPlus, Popover } from 'ui' import { useProjectsQuery } from 'data/projects/projects-query' import { useSelectedOrganization, useSelectedProject } from 'hooks' import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' +import ShimmeringLoader from 'components/ui/ShimmeringLoader' // [Fran] the idea is to let users change projects without losing the current page, // but at the same time we need to redirect correctly between urls that might be @@ -37,11 +38,15 @@ interface ProjectDropdownProps { const ProjectDropdown = ({ alt }: ProjectDropdownProps) => { const selectedOrganization = useSelectedOrganization() const selectedProject = useSelectedProject() - const { data: allProjects } = useProjectsQuery() + const { data: allProjects, isLoading: isLoadingProjects } = useProjectsQuery() const selectedOrganizationSlug = selectedOrganization?.slug const router = useRouter() const sanitizedRoute = sanitizeRoute(router.route, router.query) + if (isLoadingProjects && alt) { + return + } + return IS_PLATFORM ? ( { /> ))} -
      - {IS_PLATFORM && showCmdkHelper && ( - - - - - - - -
      + {IS_PLATFORM && showCmdkHelper && ( + + +
      -
      -
      -
      - )} - - {IS_PLATFORM && ( - <> - - }> - Account Preferences - - - - - )} - Theme - toggleTheme(e === 'dark')} - > - {/* [Joshen] Removing system default for now, needs to be supported in useTheme from common packages */} - {/* System default */} - Dark - Light - - - } - > - - -
    +
    + +
    + + + + + +
    + {os === 'macos' ? ( + + ) : ( +

    CTRL

    + )} +

    K

    +
    +
    +
    + + )} + + {IS_PLATFORM && ( + <> + + } + > + Account Preferences + + + + + )} + Theme + toggleTheme(e === 'dark')} + > + {/* [Joshen] Removing system default for now, needs to be supported in useTheme from common packages */} + {/* System default */} + Dark + Light + + + } + > + + + + )}
    ) } diff --git a/studio/components/to-be-cleaned/KeyMap.tsx b/studio/components/to-be-cleaned/KeyMap.tsx index d70714b6741..a68221cabb2 100644 --- a/studio/components/to-be-cleaned/KeyMap.tsx +++ b/studio/components/to-be-cleaned/KeyMap.tsx @@ -1,5 +1,3 @@ -import { FC } from 'react' - /** * Render keyMap * @@ -13,7 +11,7 @@ const customKeyChars: any = { enter: '↵', } -const KeyMap: FC = ({ keyMap }) => { +const KeyMap = ({ keyMap }: any) => { if (!keyMap) return null const keys = keyMap.split('+') From 4977c95ef7dd988c6416b4cfc05cf928e10f3dfa Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 17 Jul 2023 23:15:00 +0800 Subject: [PATCH 06/53] Small style update to Dropdown.Item --- packages/ui/src/components/Dropdown/Dropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/src/components/Dropdown/Dropdown.tsx b/packages/ui/src/components/Dropdown/Dropdown.tsx index 9e9d688f687..a1c1a3b25fe 100644 --- a/packages/ui/src/components/Dropdown/Dropdown.tsx +++ b/packages/ui/src/components/Dropdown/Dropdown.tsx @@ -103,7 +103,7 @@ export function Item({ icon, className, ...props }: ItemProps) { {...props} > {icon && icon} - {props.children} + {props.children} ) } From 494ada304fc22e2d4bb20f40d79951c10fb4facf Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 17 Jul 2023 23:16:33 +0800 Subject: [PATCH 07/53] Only show project dropdown if within a project --- studio/components/layouts/AppLayout/AppHeader.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index b89f7cbcc22..d064174b550 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' -import { useSelectedOrganization } from 'hooks' +import { useSelectedOrganization, useSelectedProject } from 'hooks' import FeedbackDropdown from '../ProjectLayout/LayoutHeader/FeedbackDropdown' import HelpPopover from '../ProjectLayout/LayoutHeader/HelpPopover' import NotificationsPopover from '../ProjectLayout/LayoutHeader/NotificationsPopover' @@ -11,6 +11,7 @@ import UserSettingsDropdown from './UserSettingsDropdown' const AppHeader = () => { const router = useRouter() + const project = useSelectedProject() const organization = useSelectedOrganization() return ( @@ -26,7 +27,7 @@ const AppHeader = () => { - + {project !== undefined && }
    From 41f1a28ab1cc3bc4e50a7743a2c98299498dff64 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 18 Jul 2023 11:22:27 +0800 Subject: [PATCH 08/53] Update URLs --- studio/components/layouts/AppLayout/UserSettingsDropdown.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx index d5bcb4fe7d7..64b4a42bc8c 100644 --- a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx +++ b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx @@ -31,12 +31,12 @@ const UserSettingsDropdown = () => {

    {profile?.primary_email}

    , , - + Preferences , - + Access tokens From c92c4af57e50e1d2866e0ba2bfa13c1343164c31 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 18 Jul 2023 15:50:43 +0800 Subject: [PATCH 09/53] Scaffold new settings layout --- .../layouts/AppLayout/AppHeader.tsx | 10 +++ .../layouts/AppLayout/AppLayout.tsx | 12 +++- .../layouts/ProjectLayout/ProjectLayout.tsx | 24 +------ .../ProjectSettingsLayout/SettingsLayout.tsx | 55 ++++++++++++++++ .../SettingsMenu.utils.ts | 0 .../SettingsLayout/AccountSettingsMenu.tsx | 5 ++ .../OrganizationSettingsMenu.tsx | 48 ++++++++++++++ .../OrganizationSettingsMenuItem.tsx | 31 +++++++++ .../ProjectSettingsMenuItem.tsx | 26 ++++++++ .../layouts/SettingsLayout/SettingsLayout.tsx | 65 +++++-------------- studio/components/layouts/index.ts | 2 +- studio/next.config.js | 10 +-- studio/pages/org/[slug]/settings/index.tsx | 21 ++++++ 13 files changed, 232 insertions(+), 77 deletions(-) create mode 100644 studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx rename studio/components/layouts/{SettingsLayout => ProjectSettingsLayout}/SettingsMenu.utils.ts (100%) create mode 100644 studio/components/layouts/SettingsLayout/AccountSettingsMenu.tsx create mode 100644 studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx create mode 100644 studio/components/layouts/SettingsLayout/OrganizationSettingsMenuItem.tsx create mode 100644 studio/components/layouts/SettingsLayout/ProjectSettingsMenuItem.tsx create mode 100644 studio/pages/org/[slug]/settings/index.tsx diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index d064174b550..f2524ad6712 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -8,6 +8,7 @@ import NotificationsPopover from '../ProjectLayout/LayoutHeader/NotificationsPop import ProjectDropdown from '../ProjectLayout/LayoutHeader/ProjectDropdown' import OrganizationDropdown from './OrganizationDropdown' import UserSettingsDropdown from './UserSettingsDropdown' +import { Button, IconSettings } from 'ui' const AppHeader = () => { const router = useRouter() @@ -34,6 +35,15 @@ const AppHeader = () => { + + +
    diff --git a/studio/components/layouts/AppLayout/AppLayout.tsx b/studio/components/layouts/AppLayout/AppLayout.tsx index 275113dbde8..6090c66fd64 100644 --- a/studio/components/layouts/AppLayout/AppLayout.tsx +++ b/studio/components/layouts/AppLayout/AppLayout.tsx @@ -5,11 +5,21 @@ import { useFlag } from 'hooks' const AppLayout = ({ children }: PropsWithChildren<{}>) => { const navLayoutV2 = useFlag('navigationLayoutV2') + const ongoingIncident = useFlag('ongoingIncident') + const appHeaderHeight = navLayoutV2 ? 49 : 0 return (
    {navLayoutV2 && } - {children} +
    + {children} +
    ) } diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 7e72ae8ca00..b67b44f4f50 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -45,9 +45,7 @@ const ProjectLayout = ({ const projectName = selectedProject?.name const organizationName = selectedOrganization?.name - const ongoingIncident = useFlag('ongoingIncident') const navLayoutV2 = useFlag('navigationLayoutV2') - const appHeaderHeight = navLayoutV2 ? 48 : 0 const isPaused = selectedProject?.status === PROJECT_STATUS.INACTIVE const ignorePausedState = @@ -71,14 +69,7 @@ const ProjectLayout = ({ -
    +
    {/* Left-most navigation side bar to access products */} {!hideIconBar && } @@ -205,9 +196,7 @@ export const ProjectLayoutNonBlocking = ({ router.pathname === '/project/[ref]' || router.pathname.includes('/project/[ref]/settings') const showPausedState = isPaused && !ignorePausedState - const ongoingIncident = useFlag('ongoingIncident') const navLayoutV2 = useFlag('navigationLayoutV2') - const appHeaderHeight = navLayoutV2 ? 48 : 0 return ( @@ -217,14 +206,7 @@ export const ProjectLayoutNonBlocking = ({ -
    +
    {/* Left-most navigation side bar to access products */} {!hideIconBar && } @@ -234,7 +216,7 @@ export const ProjectLayoutNonBlocking = ({ )}
    - {!hideHeader && } + {!navLayoutV2 && !hideHeader && } {showPausedState ? (
    diff --git a/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx b/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx new file mode 100644 index 00000000000..f49f876963d --- /dev/null +++ b/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx @@ -0,0 +1,55 @@ +import { FC, ReactNode, useEffect } from 'react' +import { observer } from 'mobx-react-lite' +import { useRouter } from 'next/router' +import { useFlag, useSelectedOrganization, useSelectedProject, useStore, withAuth } from 'hooks' +import { generateSettingsMenu } from './SettingsMenu.utils' + +import ProjectLayout from '..' +import ProductMenu from 'components/ui/ProductMenu' + +interface Props { + title?: string + children: ReactNode +} + +const SettingsLayout: FC = ({ title, children }) => { + const { ui, meta } = useStore() + const project = useSelectedProject() + const organization = useSelectedOrganization() + const isOrgBilling = !!organization?.subscription_id + + const router = useRouter() + // billing pages live under /billing/invoices and /billing/subscription, etc + // so we need to pass the [5]th part of the url to the menu + const page = router.pathname.includes('billing') + ? router.pathname.split('/')[5] + : router.pathname.split('/')[4] + + const isVaultEnabled = useFlag('vaultExtension') + const menuRoutes = generateSettingsMenu( + project?.ref as string, + project, + isVaultEnabled, + isOrgBilling + ) + + useEffect(() => { + if (ui.selectedProjectRef) { + meta.extensions.load() + } + }, [ui.selectedProjectRef]) + + return ( + } + > +
    + {children} +
    +
    + ) +} + +export default withAuth(observer(SettingsLayout)) diff --git a/studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts b/studio/components/layouts/ProjectSettingsLayout/SettingsMenu.utils.ts similarity index 100% rename from studio/components/layouts/SettingsLayout/SettingsMenu.utils.ts rename to studio/components/layouts/ProjectSettingsLayout/SettingsMenu.utils.ts diff --git a/studio/components/layouts/SettingsLayout/AccountSettingsMenu.tsx b/studio/components/layouts/SettingsLayout/AccountSettingsMenu.tsx new file mode 100644 index 00000000000..2a6a68c633d --- /dev/null +++ b/studio/components/layouts/SettingsLayout/AccountSettingsMenu.tsx @@ -0,0 +1,5 @@ +const AccountSettingsMenu = () => { + return
    Account settings
    +} + +export default AccountSettingsMenu diff --git a/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx b/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx new file mode 100644 index 00000000000..809c030ae02 --- /dev/null +++ b/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx @@ -0,0 +1,48 @@ +import { useRouter } from 'next/router' + +import { useProjectsQuery } from 'data/projects/projects-query' +import { useSelectedOrganization } from 'hooks' +import OrganizationSettingsMenuItem from './OrganizationSettingsMenuItem' +import ProjectSettingsMenuItem from './ProjectSettingsMenuItem' + +const OrganizationSettingsMenu = () => { + const router = useRouter() + const organization = useSelectedOrganization() + const organizationSettings = [ + { label: 'Overview', pathname: `/org/[slug]/settings` }, + { label: 'Members', pathname: `/org/[slug]/team` }, + { label: 'Integrations', pathname: `/org/[slug]/integrations` }, + { label: 'Billing', pathname: `/org/[slug]/billing` }, + { label: 'Usage', pathname: `/org/[slug]/usage` }, + { label: 'Invoices', pathname: `/org/[slug]/invoices` }, + { label: 'OAuth Apps', pathname: `/org/[slug]/apps` }, + { label: 'Audit Logs', pathname: `/org/[slug]/audit` }, + ] + + const { data: allProjects } = useProjectsQuery() + const projects = allProjects?.filter((project) => project.organization_id === organization?.id) + + return ( +
    +
    +

    Organization

    + {organizationSettings.map((link) => ( + + ))} +
    +
    +

    Projects

    + {projects?.map((project) => ( + + ))} +
    +
    + ) +} + +export default OrganizationSettingsMenu diff --git a/studio/components/layouts/SettingsLayout/OrganizationSettingsMenuItem.tsx b/studio/components/layouts/SettingsLayout/OrganizationSettingsMenuItem.tsx new file mode 100644 index 00000000000..d7987ddde8c --- /dev/null +++ b/studio/components/layouts/SettingsLayout/OrganizationSettingsMenuItem.tsx @@ -0,0 +1,31 @@ +import clsx from 'clsx' +import Link from 'next/link' + +interface OrganizationSettingsMenuItemProps { + slug: string + link: { label: string; pathname: string } + isActive: boolean +} + +const OrganizationSettingsMenuItem = ({ + slug, + link, + isActive, +}: OrganizationSettingsMenuItemProps) => { + return ( + + ) +} + +export default OrganizationSettingsMenuItem diff --git a/studio/components/layouts/SettingsLayout/ProjectSettingsMenuItem.tsx b/studio/components/layouts/SettingsLayout/ProjectSettingsMenuItem.tsx new file mode 100644 index 00000000000..ce956a96365 --- /dev/null +++ b/studio/components/layouts/SettingsLayout/ProjectSettingsMenuItem.tsx @@ -0,0 +1,26 @@ +import clsx from 'clsx' +import Link from 'next/link' +import { Project } from 'types' + +interface ProjectSettingsMenuItemProps { + project: Project +} + +const ProjectSettingsMenuItem = ({ project }: ProjectSettingsMenuItemProps) => { + return ( + + ) +} + +export default ProjectSettingsMenuItem diff --git a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx index fa1edfb8c91..60b648a1a26 100644 --- a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx +++ b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx @@ -1,55 +1,22 @@ -import { FC, ReactNode, useEffect } from 'react' -import { observer } from 'mobx-react-lite' -import { useRouter } from 'next/router' -import { useFlag, useSelectedOrganization, useSelectedProject, useStore, withAuth } from 'hooks' -import { generateSettingsMenu } from './SettingsMenu.utils' +import { PropsWithChildren } from 'react' +import AppLayout from '../AppLayout/AppLayout' +import { useParams } from 'common' +import OrganizationSettingsMenu from './OrganizationSettingsMenu' +import AccountSettingsMenu from './AccountSettingsMenu' -import ProjectLayout from '../' -import ProductMenu from 'components/ui/ProductMenu' - -interface Props { - title?: string - children: ReactNode -} - -const SettingsLayout: FC = ({ title, children }) => { - const { ui, meta } = useStore() - const project = useSelectedProject() - const organization = useSelectedOrganization() - const isOrgBilling = !!organization?.subscription_id - - const router = useRouter() - // billing pages live under /billing/invoices and /billing/subscription, etc - // so we need to pass the [5]th part of the url to the menu - const page = router.pathname.includes('billing') - ? router.pathname.split('/')[5] - : router.pathname.split('/')[4] - - const isVaultEnabled = useFlag('vaultExtension') - const menuRoutes = generateSettingsMenu( - project?.ref as string, - project, - isVaultEnabled, - isOrgBilling - ) - - useEffect(() => { - if (ui.selectedProjectRef) { - meta.extensions.load() - } - }, [ui.selectedProjectRef]) +const SettingsLayout = ({ children }: PropsWithChildren<{}>) => { + const { slug } = useParams() return ( - } - > -
    - {children} -
    -
    + +
    +
    + {slug !== undefined ? : } +
    +
    {children}
    +
    +
    ) } -export default withAuth(observer(SettingsLayout)) +export default SettingsLayout diff --git a/studio/components/layouts/index.ts b/studio/components/layouts/index.ts index b5ffd3d933f..3213ebfbf7e 100644 --- a/studio/components/layouts/index.ts +++ b/studio/components/layouts/index.ts @@ -9,7 +9,7 @@ import TableEditorLayout from './TableEditorLayout/TableEditorLayout' import SQLEditorLayout from './SQLEditorLayout/SQLEditorLayout' import DatabaseLayout from './DatabaseLayout/DatabaseLayout' import DocsLayout from './DocsLayout/DocsLayout' -import SettingsLayout from './SettingsLayout/SettingsLayout' +import SettingsLayout from './ProjectSettingsLayout/SettingsLayout' import StorageLayout from './StorageLayout/StorageLayout' import AccountLayout from './AccountLayout/AccountLayout' import { AccountLayoutWithoutAuth } from './AccountLayout/AccountLayout' diff --git a/studio/next.config.js b/studio/next.config.js index d95d252877e..eb2634ce51f 100644 --- a/studio/next.config.js +++ b/studio/next.config.js @@ -135,11 +135,11 @@ const nextConfig = { destination: '/project/:ref/sql', permanent: true, }, - { - source: '/org/:slug/settings', - destination: '/org/:slug/general', - permanent: true, - }, + // { + // source: '/org/:slug/settings', + // destination: '/org/:slug/general', + // permanent: true, + // }, { source: '/project/:ref/settings/billing/update', destination: '/project/:ref/settings/billing/subscription', diff --git a/studio/pages/org/[slug]/settings/index.tsx b/studio/pages/org/[slug]/settings/index.tsx new file mode 100644 index 00000000000..f45776ebc32 --- /dev/null +++ b/studio/pages/org/[slug]/settings/index.tsx @@ -0,0 +1,21 @@ +import { GeneralSettings } from 'components/interfaces/Organization' +import { OrganizationLayout } from 'components/layouts' +import SettingsLayout from 'components/layouts/SettingsLayout/SettingsLayout' +import Loading from 'components/ui/Loading' +import { usePermissionsQuery } from 'data/permissions/permissions-query' +import { useSelectedOrganization } from 'hooks' +import { NextPageWithLayout } from 'types' + +const OrganizationSettings: NextPageWithLayout = () => { + const { isLoading: isLoadingPermissions } = usePermissionsQuery() + const selectedOrganization = useSelectedOrganization() + + return ( + <> +

    settings

    + + ) +} + +OrganizationSettings.getLayout = (page) => {page} +export default OrganizationSettings From 167326b47428286a7a30c9ab686d676f93b0fd2d Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 18 Jul 2023 17:53:38 +0800 Subject: [PATCH 10/53] Implement organization and project menu items --- packages/ui/index.tsx | 7 ++ .../ui/src/components/shadcn/ui/accordion.tsx | 6 +- .../OrganizationSettingsMenu.tsx | 25 +++++-- .../ProjectSettingsMenuItem.tsx | 71 +++++++++++++++---- 4 files changed, 89 insertions(+), 20 deletions(-) diff --git a/packages/ui/index.tsx b/packages/ui/index.tsx index f1c481e4a50..da0f15fe962 100644 --- a/packages/ui/index.tsx +++ b/packages/ui/index.tsx @@ -135,6 +135,13 @@ export { PopoverContent as PopoverContent_Shadcn_, } from './src/components/shadcn/ui/popover' +export { + Accordion as Accordion_Shadcn_, + AccordionItem as AccordionItem_Shadcn_, + AccordionTrigger as AccordionTrigger_Shadcn_, + AccordionContent as AccordionContent_Shadcn_, +} from './src/components/shadcn/ui/accordion' + // links export * from './src/components/TextLink' diff --git a/packages/ui/src/components/shadcn/ui/accordion.tsx b/packages/ui/src/components/shadcn/ui/accordion.tsx index c5950299253..c5d5ec3dc15 100644 --- a/packages/ui/src/components/shadcn/ui/accordion.tsx +++ b/packages/ui/src/components/shadcn/ui/accordion.tsx @@ -16,8 +16,8 @@ AccordionItem.displayName = 'AccordionItem' const AccordionTrigger = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( + React.ComponentPropsWithoutRef & { hideIcon?: boolean } +>(({ className, children, hideIcon, ...props }, ref) => ( {children} - + {!hideIcon && } )) diff --git a/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx b/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx index 809c030ae02..42591bae660 100644 --- a/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx +++ b/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx @@ -1,9 +1,11 @@ import { useRouter } from 'next/router' +import { Accordion_Shadcn_, Button } from 'ui' import { useProjectsQuery } from 'data/projects/projects-query' import { useSelectedOrganization } from 'hooks' import OrganizationSettingsMenuItem from './OrganizationSettingsMenuItem' import ProjectSettingsMenuItem from './ProjectSettingsMenuItem' +import Link from 'next/link' const OrganizationSettingsMenu = () => { const router = useRouter() @@ -25,7 +27,7 @@ const OrganizationSettingsMenu = () => { return (
    -

    Organization

    +

    Organization

    {organizationSettings.map((link) => ( { /> ))}
    + + {/* [Joshen] We'll implement this in the second iteration, just to keep things small */}
    -

    Projects

    - {projects?.map((project) => ( - - ))} +

    Projects

    + + {projects?.map((project) => ( + + ))} + +
    +
    ) diff --git a/studio/components/layouts/SettingsLayout/ProjectSettingsMenuItem.tsx b/studio/components/layouts/SettingsLayout/ProjectSettingsMenuItem.tsx index ce956a96365..0a40d1eaba1 100644 --- a/studio/components/layouts/SettingsLayout/ProjectSettingsMenuItem.tsx +++ b/studio/components/layouts/SettingsLayout/ProjectSettingsMenuItem.tsx @@ -1,5 +1,13 @@ import clsx from 'clsx' import Link from 'next/link' +import { + AccordionContent_Shadcn_, + AccordionItem_Shadcn_, + AccordionTrigger_Shadcn_, + IconChevronRight, +} from 'ui' + +import { useSelectedOrganization } from 'hooks' import { Project } from 'types' interface ProjectSettingsMenuItemProps { @@ -7,19 +15,58 @@ interface ProjectSettingsMenuItemProps { } const ProjectSettingsMenuItem = ({ project }: ProjectSettingsMenuItemProps) => { + const organization = useSelectedOrganization() + const isOrgBilling = !!organization?.subscription_id + + // [Joshen] Links need to be updated once we start implementing these + const projectSettings = isOrgBilling + ? [ + { label: 'General', pathname: `/org/[slug]/settings` }, + { label: 'Infrastructure', pathname: `/org/[slug]/settings` }, + { label: 'Add Ons', pathname: `/org/[slug]/settings` }, + ] + : [ + { label: 'General', pathname: `/org/[slug]/settings` }, + { label: 'Subscription', pathname: `/org/[slug]/settings` }, + { label: 'Usage', pathname: `/org/[slug]/settings` }, + { label: 'Invoices', pathname: `/org/[slug]/settings` }, + ] + return ( - + + +
    + +

    + {project.name} +

    +
    +
    + +
    + {projectSettings.map((link) => ( + + ))} +
    +
    +
    ) } From 273f1b4127795a91bee05d2dece56309022cb300 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 11:02:06 +0800 Subject: [PATCH 11/53] Implement org settings pages with new layout --- .../GeneralSettings/GeneralSettings.tsx | 2 +- .../layouts/AppLayout/AppLayout.tsx | 3 +++ .../components/layouts/OrganizationLayout.tsx | 9 +++++++- .../OrganizationSettingsMenu.tsx | 2 +- .../layouts/SettingsLayout/SettingsLayout.tsx | 6 ++++-- studio/next.config.js | 10 ++++----- studio/pages/org/[slug]/general.tsx | 4 ++-- studio/pages/org/[slug]/settings/index.tsx | 21 ------------------- 8 files changed, 24 insertions(+), 33 deletions(-) delete mode 100644 studio/pages/org/[slug]/settings/index.tsx diff --git a/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx b/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx index 435adc38493..5cdbc251e8a 100644 --- a/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx +++ b/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx @@ -7,6 +7,7 @@ import { useEffect, useState } from 'react' import { Collapsible, Form, IconChevronRight, Input, Toggle } from 'ui' import { useParams } from 'common/hooks' +import { ScaffoldContainerLegacy } from 'components/layouts/Scaffold' import { FormActions, FormPanel, @@ -19,7 +20,6 @@ import { useCheckPermissions, useFlag, useSelectedOrganization, useStore } from import { patch } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import OrganizationDeletePanel from './OrganizationDeletePanel' -import { ScaffoldContainerLegacy } from 'components/layouts/Scaffold' const GeneralSettings = () => { const queryClient = useQueryClient() diff --git a/studio/components/layouts/AppLayout/AppLayout.tsx b/studio/components/layouts/AppLayout/AppLayout.tsx index 6090c66fd64..7cefb9a6453 100644 --- a/studio/components/layouts/AppLayout/AppLayout.tsx +++ b/studio/components/layouts/AppLayout/AppLayout.tsx @@ -16,6 +16,9 @@ const AppLayout = ({ children }: PropsWithChildren<{}>) => { height: ongoingIncident ? `calc(100vh - 44px - ${appHeaderHeight}px)` : `calc(100vh - ${appHeaderHeight}px)`, + maxHeight: ongoingIncident + ? `calc(100vh - 44px - ${appHeaderHeight}px)` + : `calc(100vh - ${appHeaderHeight}px)`, }} > {children} diff --git a/studio/components/layouts/OrganizationLayout.tsx b/studio/components/layouts/OrganizationLayout.tsx index 91ec8e0880a..691f99f25b5 100644 --- a/studio/components/layouts/OrganizationLayout.tsx +++ b/studio/components/layouts/OrganizationLayout.tsx @@ -5,16 +5,23 @@ import { PropsWithChildren } from 'react' import { Tabs } from 'ui' import { AccountLayout } from './' import { ScaffoldContainer, ScaffoldDivider, ScaffoldHeader, ScaffoldTitle } from './Scaffold' +import SettingsLayout from './SettingsLayout/SettingsLayout' const OrganizationLayout = ({ children }: PropsWithChildren<{}>) => { const selectedOrganization = useSelectedOrganization() const router = useRouter() const { slug } = useParams() const id = router.asPath.split('/').at(-1)?.split('?')[0]?.split('#')[0] + const isOrgBilling = !!selectedOrganization?.subscription_id + + const navLayoutV2 = useFlag('navigationLayoutV2') const showOAuthApps = useFlag('oauthApps') const showAuditLogs = useFlag('auditLogs') const showIntegrationsV2 = useFlag('integrationsV2') - const isOrgBilling = !!selectedOrganization?.subscription_id + + if (navLayoutV2) { + return {children} + } return ( { const router = useRouter() const organization = useSelectedOrganization() const organizationSettings = [ - { label: 'Overview', pathname: `/org/[slug]/settings` }, + { label: 'General', pathname: `/org/[slug]/general` }, { label: 'Members', pathname: `/org/[slug]/team` }, { label: 'Integrations', pathname: `/org/[slug]/integrations` }, { label: 'Billing', pathname: `/org/[slug]/billing` }, diff --git a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx index 60b648a1a26..2054f542438 100644 --- a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx +++ b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx @@ -7,13 +7,15 @@ import AccountSettingsMenu from './AccountSettingsMenu' const SettingsLayout = ({ children }: PropsWithChildren<{}>) => { const { slug } = useParams() + // [Joshen] Note to self - these could contribute to a Scaffold component + return (
    -
    +
    {slug !== undefined ? : }
    -
    {children}
    +
    {children}
    ) diff --git a/studio/next.config.js b/studio/next.config.js index eb2634ce51f..d95d252877e 100644 --- a/studio/next.config.js +++ b/studio/next.config.js @@ -135,11 +135,11 @@ const nextConfig = { destination: '/project/:ref/sql', permanent: true, }, - // { - // source: '/org/:slug/settings', - // destination: '/org/:slug/general', - // permanent: true, - // }, + { + source: '/org/:slug/settings', + destination: '/org/:slug/general', + permanent: true, + }, { source: '/project/:ref/settings/billing/update', destination: '/project/:ref/settings/billing/subscription', diff --git a/studio/pages/org/[slug]/general.tsx b/studio/pages/org/[slug]/general.tsx index 245a3e23f05..d186365fad5 100644 --- a/studio/pages/org/[slug]/general.tsx +++ b/studio/pages/org/[slug]/general.tsx @@ -1,4 +1,4 @@ -import { GeneralSettings } from 'components/interfaces/Organization' +import { GeneralSettings as GeneralSettingsLegacy } from 'components/interfaces/Organization' import { OrganizationLayout } from 'components/layouts' import Loading from 'components/ui/Loading' import { usePermissionsQuery } from 'data/permissions/permissions-query' @@ -14,7 +14,7 @@ const OrgGeneralSettings: NextPageWithLayout = () => { {selectedOrganization === undefined && isLoadingPermissions ? ( ) : ( - + )} ) diff --git a/studio/pages/org/[slug]/settings/index.tsx b/studio/pages/org/[slug]/settings/index.tsx deleted file mode 100644 index f45776ebc32..00000000000 --- a/studio/pages/org/[slug]/settings/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { GeneralSettings } from 'components/interfaces/Organization' -import { OrganizationLayout } from 'components/layouts' -import SettingsLayout from 'components/layouts/SettingsLayout/SettingsLayout' -import Loading from 'components/ui/Loading' -import { usePermissionsQuery } from 'data/permissions/permissions-query' -import { useSelectedOrganization } from 'hooks' -import { NextPageWithLayout } from 'types' - -const OrganizationSettings: NextPageWithLayout = () => { - const { isLoading: isLoadingPermissions } = usePermissionsQuery() - const selectedOrganization = useSelectedOrganization() - - return ( - <> -

    settings

    - - ) -} - -OrganizationSettings.getLayout = (page) => {page} -export default OrganizationSettings From 5bdda49e77ef91425ff4193d818324a4b2db097c Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 11:16:05 +0800 Subject: [PATCH 12/53] Update account settings pages to use new layout --- .../layouts/AccountLayout/AccountLayout.tsx | 6 ++++ .../SettingsLayout/AccountSettingsMenu.tsx | 25 ++++++++++++++- .../OrganizationSettingsMenu.tsx | 8 ++--- .../OrganizationSettingsMenuItem.tsx | 31 ------------------- .../SettingsLayout/SettingsMenuItem.tsx | 27 ++++++++++++++++ 5 files changed, 61 insertions(+), 36 deletions(-) delete mode 100644 studio/components/layouts/SettingsLayout/OrganizationSettingsMenuItem.tsx create mode 100644 studio/components/layouts/SettingsLayout/SettingsMenuItem.tsx diff --git a/studio/components/layouts/AccountLayout/AccountLayout.tsx b/studio/components/layouts/AccountLayout/AccountLayout.tsx index 901cd8949b8..b862872f8d0 100644 --- a/studio/components/layouts/AccountLayout/AccountLayout.tsx +++ b/studio/components/layouts/AccountLayout/AccountLayout.tsx @@ -8,6 +8,7 @@ import { useSignOut } from 'lib/auth' import { IS_PLATFORM } from 'lib/constants' import { SidebarSection } from './AccountLayout.types' import WithSidebar from './WithSidebar' +import SettingsLayout from '../SettingsLayout/SettingsLayout' export interface AccountLayoutProps { title: string @@ -23,6 +24,7 @@ const AccountLayout = ({ children, title, breadcrumbs }: PropsWithChildren{children} + } + return ( <> diff --git a/studio/components/layouts/SettingsLayout/AccountSettingsMenu.tsx b/studio/components/layouts/SettingsLayout/AccountSettingsMenu.tsx index 2a6a68c633d..80c3b593dd3 100644 --- a/studio/components/layouts/SettingsLayout/AccountSettingsMenu.tsx +++ b/studio/components/layouts/SettingsLayout/AccountSettingsMenu.tsx @@ -1,5 +1,28 @@ +import { useRouter } from 'next/router' +import SettingsMenuItem from './SettingsMenuItem' + const AccountSettingsMenu = () => { - return
    Account settings
    + const router = useRouter() + const accountSettings = [ + { label: 'Preferences', pathname: `/account/me` }, + { label: 'Access Tokens', pathname: `/account/tokens` }, + ] + + return ( +
    +
    +

    Account Settings

    + {accountSettings.map((link) => ( + + ))} +
    +
    + ) } export default AccountSettingsMenu diff --git a/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx b/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx index c5658c17f7c..37852e45d0d 100644 --- a/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx +++ b/studio/components/layouts/SettingsLayout/OrganizationSettingsMenu.tsx @@ -3,9 +3,9 @@ import { Accordion_Shadcn_, Button } from 'ui' import { useProjectsQuery } from 'data/projects/projects-query' import { useSelectedOrganization } from 'hooks' -import OrganizationSettingsMenuItem from './OrganizationSettingsMenuItem' import ProjectSettingsMenuItem from './ProjectSettingsMenuItem' import Link from 'next/link' +import SettingsMenuItem from './SettingsMenuItem' const OrganizationSettingsMenu = () => { const router = useRouter() @@ -29,10 +29,10 @@ const OrganizationSettingsMenu = () => {

    Organization

    {organizationSettings.map((link) => ( - ))} diff --git a/studio/components/layouts/SettingsLayout/OrganizationSettingsMenuItem.tsx b/studio/components/layouts/SettingsLayout/OrganizationSettingsMenuItem.tsx deleted file mode 100644 index d7987ddde8c..00000000000 --- a/studio/components/layouts/SettingsLayout/OrganizationSettingsMenuItem.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import clsx from 'clsx' -import Link from 'next/link' - -interface OrganizationSettingsMenuItemProps { - slug: string - link: { label: string; pathname: string } - isActive: boolean -} - -const OrganizationSettingsMenuItem = ({ - slug, - link, - isActive, -}: OrganizationSettingsMenuItemProps) => { - return ( - - ) -} - -export default OrganizationSettingsMenuItem diff --git a/studio/components/layouts/SettingsLayout/SettingsMenuItem.tsx b/studio/components/layouts/SettingsLayout/SettingsMenuItem.tsx new file mode 100644 index 00000000000..e8bcf9b13a0 --- /dev/null +++ b/studio/components/layouts/SettingsLayout/SettingsMenuItem.tsx @@ -0,0 +1,27 @@ +import clsx from 'clsx' +import Link from 'next/link' + +interface SettingsMenuItemProps { + href: string + label: string + isActive: boolean +} + +const SettingsMenuItem = ({ href, label, isActive }: SettingsMenuItemProps) => { + return ( + + ) +} + +export default SettingsMenuItem From 51444cc790c08db0f615ad3032588f9fd6bcea52 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 12:22:51 +0800 Subject: [PATCH 13/53] save last visited organization to local storage --- .../interfaces/App/RouteValidationWrapper.tsx | 27 ++++++++++----- .../layouts/AccountLayout/AccountLayout.tsx | 2 +- .../layouts/AppLayout/AppHeader.tsx | 2 +- .../AppLayout/OrganizationDropdown.tsx | 8 +++-- studio/hooks/misc/useSelectedOrganization.ts | 13 ++++--- studio/pages/org/[slug]/index.tsx | 34 ++++++++++++++----- 6 files changed, 59 insertions(+), 27 deletions(-) diff --git a/studio/components/interfaces/App/RouteValidationWrapper.tsx b/studio/components/interfaces/App/RouteValidationWrapper.tsx index aacae694dc3..58e4091d73c 100644 --- a/studio/components/interfaces/App/RouteValidationWrapper.tsx +++ b/studio/components/interfaces/App/RouteValidationWrapper.tsx @@ -6,14 +6,13 @@ import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useProjectsQuery } from 'data/projects/projects-query' import { useStore } from 'hooks' import useLatest from 'hooks/misc/useLatest' +import { useParams } from 'common' // Ideally these could all be within a _middleware when we use Next 12 const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { const { ui } = useStore() - const router = useRouter() - const projectRef = router.query.ref - const orgSlug = router.query.slug + const { ref, slug } = useParams() /** * Array of urls/routes that should be ignored @@ -43,10 +42,14 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { // check if current route is excempted from route validation check if (isExceptUrl()) return - if (orgsInitialized && orgSlug) { + if (orgsInitialized && slug) { // Check validity of organization that user is trying to access const organizations = organizationsRef.current ?? [] - const isValidOrg = organizations.some((org) => org.slug === orgSlug) + const organization = organizations.find((org) => org.slug === slug) + const isValidOrg = organization !== undefined + + // Save organization slug to local storage + if (organization) localStorage.setItem('supabase-organization', organization.slug) if (!isValidOrg) { ui.setNotification({ category: 'error', message: 'This organization does not exist' }) @@ -54,7 +57,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { return } } - }, [orgsInitialized]) + }, [slug, orgsInitialized]) const { data: projects, isSuccess: projectsInitialized } = useProjectsQuery() const projectsRef = useLatest(projects) @@ -63,10 +66,16 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { // check if current route is excempted from route validation check if (isExceptUrl()) return - if (projectsInitialized && projectRef) { + if (projectsInitialized && ref) { // Check validity of project that the user is trying to access const projects = projectsRef.current ?? [] - const isValidProject = projects.some((project) => project.ref === projectRef) + const project = projects.find((project) => project.ref === ref) + const isValidProject = project !== undefined + + // Save organization slug to local storage + const organizationId = project?.organization_id + const organization = organizations?.find((organization) => organization.id === organizationId) + if (organization) localStorage.setItem('supabase-organization', organization.slug) if (!isValidProject) { ui.setNotification({ category: 'error', message: 'This project does not exist' }) @@ -74,7 +83,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { return } } - }, [projectsInitialized]) + }, [ref, projectsInitialized]) return <>{children} } diff --git a/studio/components/layouts/AccountLayout/AccountLayout.tsx b/studio/components/layouts/AccountLayout/AccountLayout.tsx index b862872f8d0..ae83fbece97 100644 --- a/studio/components/layouts/AccountLayout/AccountLayout.tsx +++ b/studio/components/layouts/AccountLayout/AccountLayout.tsx @@ -127,7 +127,7 @@ const AccountLayout = ({ children, title, breadcrumbs }: PropsWithChildren{children} } diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index f2524ad6712..ef8d9a91c38 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -35,7 +35,7 @@ const AppHeader = () => { - + ) } diff --git a/studio/hooks/misc/useSelectedOrganization.ts b/studio/hooks/misc/useSelectedOrganization.ts index c2446380fc5..282e9daf081 100644 --- a/studio/hooks/misc/useSelectedOrganization.ts +++ b/studio/hooks/misc/useSelectedOrganization.ts @@ -8,10 +8,15 @@ export function useSelectedOrganization({ enabled = true } = {}) { const { slug } = useParams() const { data } = useOrganizationsQuery({ enabled }) const selectedProject = useSelectedProject() + const localStorageSlug = + typeof window !== 'undefined' ? localStorage.getItem('supabase-organization') : undefined return useMemo(() => { - return data?.find( - (org) => org.slug === slug || (selectedProject && org.id === selectedProject.organization_id) - ) - }, [data, selectedProject, slug]) + return data?.find((org) => { + if (slug !== undefined) return org.slug === slug + if (selectedProject !== undefined) return org.id === selectedProject.organization_id + if (localStorageSlug !== undefined) return org.slug === localStorageSlug + return undefined + }) + }, [data, selectedProject, slug, localStorageSlug]) } diff --git a/studio/pages/org/[slug]/index.tsx b/studio/pages/org/[slug]/index.tsx index 263a42d7732..ba4fa711073 100644 --- a/studio/pages/org/[slug]/index.tsx +++ b/studio/pages/org/[slug]/index.tsx @@ -21,10 +21,6 @@ const ProjectsPage: NextPageWithLayout = () => { const organization = useSelectedOrganization() const projects = allProjects?.filter((project) => project.organization_id === organization?.id) - if (!organization) { - // Return a 404 page - } - return ( @@ -48,11 +44,31 @@ const ProjectsPage: NextPageWithLayout = () => { )} {isSuccessProjects && ( -
      - {projects?.map((project) => ( - - ))} -
    + <> + {(projects?.length ?? 0) === 0 ? ( +
    + ) : ( +
      + {projects?.map((project) => ( + + ))} +
    + )} + )}
    From fd5d250c2ed23957de5be5098c3ef5a8337caf99 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 12:37:06 +0800 Subject: [PATCH 14/53] Refactor settings button and fix route validation wrapper --- .../interfaces/App/RouteValidationWrapper.tsx | 36 ++++++++++++------- .../layouts/AppLayout/AppHeader.tsx | 12 ++----- .../layouts/AppLayout/SettingsButton.tsx | 36 +++++++++++++++++++ 3 files changed, 61 insertions(+), 23 deletions(-) create mode 100644 studio/components/layouts/AppLayout/SettingsButton.tsx diff --git a/studio/components/interfaces/App/RouteValidationWrapper.tsx b/studio/components/interfaces/App/RouteValidationWrapper.tsx index 58e4091d73c..9b43d4c5a8d 100644 --- a/studio/components/interfaces/App/RouteValidationWrapper.tsx +++ b/studio/components/interfaces/App/RouteValidationWrapper.tsx @@ -45,11 +45,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { if (orgsInitialized && slug) { // Check validity of organization that user is trying to access const organizations = organizationsRef.current ?? [] - const organization = organizations.find((org) => org.slug === slug) - const isValidOrg = organization !== undefined - - // Save organization slug to local storage - if (organization) localStorage.setItem('supabase-organization', organization.slug) + const isValidOrg = organizations.some((org) => org.slug === slug) if (!isValidOrg) { ui.setNotification({ category: 'error', message: 'This organization does not exist' }) @@ -57,7 +53,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { return } } - }, [slug, orgsInitialized]) + }, [orgsInitialized]) const { data: projects, isSuccess: projectsInitialized } = useProjectsQuery() const projectsRef = useLatest(projects) @@ -69,13 +65,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { if (projectsInitialized && ref) { // Check validity of project that the user is trying to access const projects = projectsRef.current ?? [] - const project = projects.find((project) => project.ref === ref) - const isValidProject = project !== undefined - - // Save organization slug to local storage - const organizationId = project?.organization_id - const organization = organizations?.find((organization) => organization.id === organizationId) - if (organization) localStorage.setItem('supabase-organization', organization.slug) + const isValidProject = projects.some((project) => project.ref === ref) if (!isValidProject) { ui.setNotification({ category: 'error', message: 'This project does not exist' }) @@ -83,6 +73,26 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { return } } + }, [projectsInitialized]) + + useEffect(() => { + if (orgsInitialized && slug) { + // Save organization slug to local storage + const organizations = organizationsRef.current ?? [] + const organization = organizations.find((org) => org.slug === slug) + if (organization) localStorage.setItem('supabase-organization', organization.slug) + } + }, [slug, orgsInitialized]) + + useEffect(() => { + if (projectsInitialized && ref) { + // Save organization slug to local storage + const projects = projectsRef.current ?? [] + const project = projects.find((project) => project.ref === ref) + const organizationId = project?.organization_id + const organization = organizations?.find((organization) => organization.id === organizationId) + if (organization) localStorage.setItem('supabase-organization', organization.slug) + } }, [ref, projectsInitialized]) return <>{children} diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index ef8d9a91c38..f25ffe0dff4 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -7,8 +7,8 @@ import HelpPopover from '../ProjectLayout/LayoutHeader/HelpPopover' import NotificationsPopover from '../ProjectLayout/LayoutHeader/NotificationsPopover' import ProjectDropdown from '../ProjectLayout/LayoutHeader/ProjectDropdown' import OrganizationDropdown from './OrganizationDropdown' +import SettingsButton from './SettingsButton' import UserSettingsDropdown from './UserSettingsDropdown' -import { Button, IconSettings } from 'ui' const AppHeader = () => { const router = useRouter() @@ -35,15 +35,7 @@ const AppHeader = () => { - - -
    diff --git a/studio/components/layouts/AppLayout/SettingsButton.tsx b/studio/components/layouts/AppLayout/SettingsButton.tsx new file mode 100644 index 00000000000..a30744a72b1 --- /dev/null +++ b/studio/components/layouts/AppLayout/SettingsButton.tsx @@ -0,0 +1,36 @@ +import Link from 'next/link' +import { Button, IconSettings } from 'ui' +import * as Tooltip from '@radix-ui/react-tooltip' + +const SettingsButton = ({ slug }: { slug: string }) => { + return ( + + + + + ) : ( diff --git a/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx b/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx index f49f876963d..f8bda210db8 100644 --- a/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx +++ b/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx @@ -1,24 +1,27 @@ -import { FC, ReactNode, useEffect } from 'react' +import { useFlag, useSelectedOrganization, useSelectedProject, useStore, withAuth } from 'hooks' import { observer } from 'mobx-react-lite' import { useRouter } from 'next/router' -import { useFlag, useSelectedOrganization, useSelectedProject, useStore, withAuth } from 'hooks' +import { PropsWithChildren, useEffect } from 'react' import { generateSettingsMenu } from './SettingsMenu.utils' +import NewSettingsLayout from '../SettingsLayout/SettingsLayout' -import ProjectLayout from '..' import ProductMenu from 'components/ui/ProductMenu' +import ProjectLayout from '..' +import { ProjectContextProvider } from '../ProjectLayout/ProjectContext' -interface Props { +interface SettingsLayoutProps { title?: string - children: ReactNode } -const SettingsLayout: FC = ({ title, children }) => { +const SettingsLayout = ({ title, children }: PropsWithChildren) => { + const router = useRouter() const { ui, meta } = useStore() const project = useSelectedProject() const organization = useSelectedOrganization() const isOrgBilling = !!organization?.subscription_id - const router = useRouter() + const navLayoutV2 = useFlag('navigationLayoutV2') + // billing pages live under /billing/invoices and /billing/subscription, etc // so we need to pass the [5]th part of the url to the menu const page = router.pathname.includes('billing') @@ -39,6 +42,14 @@ const SettingsLayout: FC = ({ title, children }) => { } }, [ui.selectedProjectRef]) + if (navLayoutV2) { + return ( + + {children} + + ) + } + return ( { + const router = useRouter() const organization = useSelectedOrganization() const isOrgBilling = !!organization?.subscription_id // [Joshen] Links need to be updated once we start implementing these const projectSettings = isOrgBilling ? [ - { label: 'General', pathname: `/org/[slug]/settings` }, - { label: 'Infrastructure', pathname: `/org/[slug]/settings` }, - { label: 'Add Ons', pathname: `/org/[slug]/settings` }, + { label: 'General', pathname: `/project/[ref]/settings/general` }, + { label: 'Infrastructure', pathname: `/project/[ref]/settings/infrastructure` }, + { label: 'Add Ons', pathname: `/project/[ref]/settings/addons` }, ] : [ - { label: 'General', pathname: `/org/[slug]/settings` }, - { label: 'Subscription', pathname: `/org/[slug]/settings` }, - { label: 'Usage', pathname: `/org/[slug]/settings` }, - { label: 'Invoices', pathname: `/org/[slug]/settings` }, + { label: 'General', pathname: `/project/[ref]/settings/general` }, + { label: 'Subscription', pathname: `/project/[ref]/settings/billing/subscription` }, + { label: 'Usage', pathname: `/project/[ref]/settings/billing/usage` }, + { label: 'Invoices', pathname: `/project/[ref]/settings/billing/invoices` }, ] return ( @@ -50,20 +51,17 @@ const ProjectSettingsMenuItem = ({ project }: ProjectSettingsMenuItemProps) => {
    - {projectSettings.map((link) => ( - - ))} + {projectSettings.map((link) => { + const href = link.pathname.replace('[ref]', project.ref) + return ( + + ) + })}
    diff --git a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx index 2054f542438..6bb553df5df 100644 --- a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx +++ b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx @@ -5,7 +5,7 @@ import OrganizationSettingsMenu from './OrganizationSettingsMenu' import AccountSettingsMenu from './AccountSettingsMenu' const SettingsLayout = ({ children }: PropsWithChildren<{}>) => { - const { slug } = useParams() + const { ref, slug } = useParams() // [Joshen] Note to self - these could contribute to a Scaffold component @@ -13,7 +13,11 @@ const SettingsLayout = ({ children }: PropsWithChildren<{}>) => {
    - {slug !== undefined ? : } + {slug === undefined && ref === undefined ? ( + + ) : ( + + )}
    {children}
    From 3476c037efd07b0eae356ec9d966707d47d4665d Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 14:27:04 +0800 Subject: [PATCH 16/53] Ensure org dropdown maintains URL --- .../layouts/AppLayout/OrganizationDropdown.tsx | 10 ++++++++-- .../ProjectLayout/LayoutHeader/ProjectDropdown.tsx | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index 54171336afe..c100cb0d8a0 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -6,8 +6,10 @@ import { useFlag, useSelectedOrganization } from 'hooks' import { IS_PLATFORM } from 'lib/constants' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import ShimmeringLoader from 'components/ui/ShimmeringLoader' +import { useRouter } from 'next/router' const OrganizationDropdown = () => { + const router = useRouter() const { data: organizations, isLoading: isLoadingOrganizations } = useOrganizationsQuery() const selectedOrganization = useSelectedOrganization() const orgCreationV2 = useFlag('orgcreationv2') @@ -29,9 +31,13 @@ const OrganizationDropdown = () => { {organizations ?.sort((a, b) => a.name.localeCompare(b.name)) .map((x) => { - // [Joshen] Improvement: maintain URL if navigating in between org pages + const href = router.pathname.includes('[slug]') + ? router.pathname.replace('[slug]', x.slug) + : router.pathname.includes('[ref]/settings') + ? `/org/${x.slug}/general` + : `/org/${x.slug}` return ( - + {x.name} diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx index 87d25cebb01..232e27d29f5 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/ProjectDropdown.tsx @@ -80,9 +80,10 @@ const ProjectDropdown = ({ alt }: { alt?: boolean }) => { const { data: allOrganizations } = useOrganizationsQuery() const selectedOrganizationSlug = selectedOrganization?.slug + const isOrgBilling = !!selectedOrganization?.subscription_id const { data: subscription, isSuccess } = useProjectSubscriptionV2Query( { projectRef: selectedProject?.ref }, - { enabled: alt } + { enabled: alt && !isOrgBilling } ) if (isLoadingProjects && alt) { @@ -118,7 +119,7 @@ const ProjectDropdown = ({ alt }: { alt?: boolean }) => { } > {selectedProject?.name} - {alt && isSuccess && ( + {alt && isSuccess && !isOrgBilling && ( {subscription?.plan.name} From 6c397b52a0ac6617126fa71daa492224bca58be1 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 14:47:48 +0800 Subject: [PATCH 17/53] Revert project settings changes and hide project settings in org settings --- .../ProjectSettingsLayout/SettingsLayout.tsx | 12 ------------ .../SettingsLayout/OrganizationSettingsMenu.tsx | 14 +++++--------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx b/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx index f8bda210db8..40b057b839b 100644 --- a/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx +++ b/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx @@ -3,11 +3,9 @@ import { observer } from 'mobx-react-lite' import { useRouter } from 'next/router' import { PropsWithChildren, useEffect } from 'react' import { generateSettingsMenu } from './SettingsMenu.utils' -import NewSettingsLayout from '../SettingsLayout/SettingsLayout' import ProductMenu from 'components/ui/ProductMenu' import ProjectLayout from '..' -import { ProjectContextProvider } from '../ProjectLayout/ProjectContext' interface SettingsLayoutProps { title?: string @@ -20,8 +18,6 @@ const SettingsLayout = ({ title, children }: PropsWithChildren - {children} - - ) - } - return ( { @@ -21,8 +17,8 @@ const OrganizationSettingsMenu = () => { { label: 'Audit Logs', pathname: `/org/[slug]/audit` }, ] - const { data: allProjects } = useProjectsQuery() - const projects = allProjects?.filter((project) => project.organization_id === organization?.id) + // const { data: allProjects } = useProjectsQuery() + // const projects = allProjects?.filter((project) => project.organization_id === organization?.id) return (
    @@ -38,8 +34,8 @@ const OrganizationSettingsMenu = () => { ))}
    - {/* [Joshen] We'll implement this in the second iteration, just to keep things small */} -
    + {/* [Joshen] Hiding below until we figure out a better UX for consolidating settings on projects */} + {/*

    Projects

    {projects?.map((project) => ( @@ -55,7 +51,7 @@ const OrganizationSettingsMenu = () => { -
    +
    */}
    ) } From f1d1249a4f46dbae4579e2c0af29279e03c52f8f Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 16:10:21 +0800 Subject: [PATCH 18/53] Add redirect on /projects if nav layout v2 is on --- .../interfaces/App/RouteValidationWrapper.tsx | 11 ++++++++--- studio/hooks/misc/useSelectedOrganization.ts | 5 ++++- studio/lib/constants/index.ts | 4 ++++ studio/pages/projects.tsx | 19 +++++++++++++++++-- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/studio/components/interfaces/App/RouteValidationWrapper.tsx b/studio/components/interfaces/App/RouteValidationWrapper.tsx index 9b43d4c5a8d..182de527f6b 100644 --- a/studio/components/interfaces/App/RouteValidationWrapper.tsx +++ b/studio/components/interfaces/App/RouteValidationWrapper.tsx @@ -4,9 +4,10 @@ import { PropsWithChildren, useEffect } from 'react' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useProjectsQuery } from 'data/projects/projects-query' -import { useStore } from 'hooks' +import { useFlag, useStore } from 'hooks' import useLatest from 'hooks/misc/useLatest' import { useParams } from 'common' +import { LOCAL_STORAGE_KEYS } from 'lib/constants' // Ideally these could all be within a _middleware when we use Next 12 const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { @@ -80,7 +81,9 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { // Save organization slug to local storage const organizations = organizationsRef.current ?? [] const organization = organizations.find((org) => org.slug === slug) - if (organization) localStorage.setItem('supabase-organization', organization.slug) + if (organization) { + localStorage.setItem(LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION, organization.slug) + } } }, [slug, orgsInitialized]) @@ -91,7 +94,9 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { const project = projects.find((project) => project.ref === ref) const organizationId = project?.organization_id const organization = organizations?.find((organization) => organization.id === organizationId) - if (organization) localStorage.setItem('supabase-organization', organization.slug) + if (organization) { + localStorage.setItem(LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION, organization.slug) + } } }, [ref, projectsInitialized]) diff --git a/studio/hooks/misc/useSelectedOrganization.ts b/studio/hooks/misc/useSelectedOrganization.ts index 282e9daf081..e10acce43e6 100644 --- a/studio/hooks/misc/useSelectedOrganization.ts +++ b/studio/hooks/misc/useSelectedOrganization.ts @@ -3,13 +3,16 @@ import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useMemo } from 'react' import { useSelectedProject } from './useSelectedProject' +import { LOCAL_STORAGE_KEYS } from 'lib/constants' export function useSelectedOrganization({ enabled = true } = {}) { const { slug } = useParams() const { data } = useOrganizationsQuery({ enabled }) const selectedProject = useSelectedProject() const localStorageSlug = - typeof window !== 'undefined' ? localStorage.getItem('supabase-organization') : undefined + typeof window !== 'undefined' + ? localStorage.getItem(LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION) + : undefined return useMemo(() => { return data?.find((org) => { diff --git a/studio/lib/constants/index.ts b/studio/lib/constants/index.ts index 24743c4904f..3e3b66f24b2 100644 --- a/studio/lib/constants/index.ts +++ b/studio/lib/constants/index.ts @@ -85,3 +85,7 @@ export const STRIPE_PUBLIC_KEY = process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY || 'pk_test_XVwg5IZH3I9Gti98hZw6KRzd00v5858heG' export const USAGE_APPROACHING_THRESHOLD = 0.75 + +export const LOCAL_STORAGE_KEYS = { + RECENTLY_VISITED_ORGANIZATION: 'supabase-organization', +} diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index 8fe0df5780d..e36beadb3d8 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -5,11 +5,15 @@ import AlertError from 'components/ui/AlertError' import Connecting from 'components/ui/Loading/Loading' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useAutoProjectsPrefetch } from 'data/projects/projects-query' -import { IS_PLATFORM } from 'lib/constants' +import { useFlag } from 'hooks' +import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' import { useProfile } from 'lib/profile' +import { useRouter } from 'next/router' +import { useEffect } from 'react' import { NextPageWithLayout } from 'types' const ProjectsPage: NextPageWithLayout = () => { + const router = useRouter() const { data: organizations, isLoading: isOrganizationLoading, @@ -19,8 +23,19 @@ const ProjectsPage: NextPageWithLayout = () => { useAutoProjectsPrefetch() const { isLoading: isProfileLoading } = useProfile() - const isLoading = isOrganizationLoading || isProfileLoading + const navLayoutV2 = useFlag('navigationLayoutV2') + + useEffect(() => { + if (navLayoutV2 && isSuccess && typeof window !== 'undefined') { + const localStorageSlug = + typeof window !== 'undefined' + ? localStorage.getItem(LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION) + : undefined + if (localStorageSlug !== undefined) router.push(`/org/${localStorageSlug}`) + else router.push(`/org/${organizations[0].slug}`) + } + }, [navLayoutV2, isSuccess, typeof window]) return ( <> From 2738fbbd3f8746c9b1dd48d39da68e5f3ebb62a4 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 16:14:38 +0800 Subject: [PATCH 19/53] Update new org link --- .../layouts/AppLayout/OrganizationDropdown.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index c100cb0d8a0..80937af89bf 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -45,18 +45,11 @@ const OrganizationDropdown = () => { ) })} - + }>New organization - {orgCreationV2 && ( - - - }>New organization V2 - - - )} } > From fab17d5d27d89cf18f9e40629d73fde89a43b507 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 16:20:49 +0800 Subject: [PATCH 20/53] Fix --- studio/pages/projects.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index e36beadb3d8..a004508fd74 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -32,7 +32,7 @@ const ProjectsPage: NextPageWithLayout = () => { typeof window !== 'undefined' ? localStorage.getItem(LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION) : undefined - if (localStorageSlug !== undefined) router.push(`/org/${localStorageSlug}`) + if (localStorageSlug) router.push(`/org/${localStorageSlug}`) else router.push(`/org/${organizations[0].slug}`) } }, [navLayoutV2, isSuccess, typeof window]) From 1edf879f38c804a84a0597c77ccb686955ac4815 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 19 Jul 2023 16:37:20 +0800 Subject: [PATCH 21/53] Fix user icon in UserSettingsDropdown --- studio/components/layouts/AppLayout/UserSettingsDropdown.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx index 64b4a42bc8c..b17cb64175f 100644 --- a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx +++ b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx @@ -1,6 +1,6 @@ import Link from 'next/link' import { useRouter } from 'next/router' -import { Dropdown, IconLogOut, Listbox, useCommandMenu } from 'ui' +import { Dropdown, IconLogOut, IconUser, Listbox, useCommandMenu } from 'ui' import { useSignOut } from 'lib/auth' import { useProfile } from 'lib/profile' @@ -90,7 +90,7 @@ const UserSettingsDropdown = () => { ]} >
    - {profile?.first_name[0]} + {profile?.first_name ? profile?.first_name?.[0] : }
    ) From 415a8b768ec016a600a343c400883fd81b518185 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 21 Jul 2023 06:39:22 +0800 Subject: [PATCH 22/53] Temp --- studio/components/layouts/AppLayout/OrganizationDropdownV2.tsx | 1 + 1 file changed, 1 insertion(+) create mode 100644 studio/components/layouts/AppLayout/OrganizationDropdownV2.tsx diff --git a/studio/components/layouts/AppLayout/OrganizationDropdownV2.tsx b/studio/components/layouts/AppLayout/OrganizationDropdownV2.tsx new file mode 100644 index 00000000000..005b571fbac --- /dev/null +++ b/studio/components/layouts/AppLayout/OrganizationDropdownV2.tsx @@ -0,0 +1 @@ +// [Joshen] TODO use Combobox From c22cbc00410c58ea2795314dea59cdaa036bf696 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 21 Jul 2023 15:01:35 +0800 Subject: [PATCH 23/53] Update AppHeader org and project selections to use combobox --- .../layouts/AppLayout/AppHeader.tsx | 2 +- .../AppLayout/OrganizationDropdown.tsx | 140 +++++++++------ .../AppLayout/OrganizationDropdownV2.tsx | 1 - .../layouts/AppLayout/ProjectDropdown.tsx | 165 ++++++++++++++++++ 4 files changed, 252 insertions(+), 56 deletions(-) delete mode 100644 studio/components/layouts/AppLayout/OrganizationDropdownV2.tsx create mode 100644 studio/components/layouts/AppLayout/ProjectDropdown.tsx diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index f25ffe0dff4..3aa96a11853 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -5,7 +5,7 @@ import { useSelectedOrganization, useSelectedProject } from 'hooks' import FeedbackDropdown from '../ProjectLayout/LayoutHeader/FeedbackDropdown' import HelpPopover from '../ProjectLayout/LayoutHeader/HelpPopover' import NotificationsPopover from '../ProjectLayout/LayoutHeader/NotificationsPopover' -import ProjectDropdown from '../ProjectLayout/LayoutHeader/ProjectDropdown' +import ProjectDropdown from './ProjectDropdown' import OrganizationDropdown from './OrganizationDropdown' import SettingsButton from './SettingsButton' import UserSettingsDropdown from './UserSettingsDropdown' diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index 80937af89bf..210df04dcb8 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -1,74 +1,106 @@ -import Link from 'next/link' -import { Badge, Button, Dropdown, IconCode, IconPlus } from 'ui' +import { useRouter } from 'next/router' +import { useState } from 'react' +import { + Badge, + Button, + CommandEmpty_Shadcn_, + CommandGroup_Shadcn_, + CommandInput_Shadcn_, + CommandItem_Shadcn_, + CommandList_Shadcn_, + Command_Shadcn_, + IconCode, + IconPlus, + Popover, + PopoverContent_Shadcn_, + PopoverTrigger_Shadcn_, + Popover_Shadcn_, +} from 'ui' import { useOrganizationsQuery } from 'data/organizations/organizations-query' -import { useFlag, useSelectedOrganization } from 'hooks' -import { IS_PLATFORM } from 'lib/constants' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' +import { useFlag, useSelectedOrganization } from 'hooks' import ShimmeringLoader from 'components/ui/ShimmeringLoader' -import { useRouter } from 'next/router' +import Link from 'next/link' const OrganizationDropdown = () => { const router = useRouter() - const { data: organizations, isLoading: isLoadingOrganizations } = useOrganizationsQuery() - const selectedOrganization = useSelectedOrganization() const orgCreationV2 = useFlag('orgcreationv2') + const selectedOrganization = useSelectedOrganization() + const { data: organizations, isLoading: isLoadingOrganizations } = useOrganizationsQuery() const slug = selectedOrganization?.slug const orgName = selectedOrganization?.name const { data, isSuccess } = useOrgSubscriptionQuery({ orgSlug: slug }) + const [open, setOpen] = useState(false) + if (isLoadingOrganizations) { return } - return IS_PLATFORM ? ( - - {organizations - ?.sort((a, b) => a.name.localeCompare(b.name)) - .map((x) => { - const href = router.pathname.includes('[slug]') - ? router.pathname.replace('[slug]', x.slug) - : router.pathname.includes('[ref]/settings') - ? `/org/${x.slug}/general` - : `/org/${x.slug}` - return ( - - - {x.name} - - - ) - })} - - - - }>New organization - - - - } - > - - - ) : ( - + return ( +
    + + +

    {orgName}

    + {isSuccess && {data?.plan.name}} +
    + + + + +
    ) } diff --git a/studio/components/layouts/AppLayout/OrganizationDropdownV2.tsx b/studio/components/layouts/AppLayout/OrganizationDropdownV2.tsx deleted file mode 100644 index 005b571fbac..00000000000 --- a/studio/components/layouts/AppLayout/OrganizationDropdownV2.tsx +++ /dev/null @@ -1 +0,0 @@ -// [Joshen] TODO use Combobox diff --git a/studio/components/layouts/AppLayout/ProjectDropdown.tsx b/studio/components/layouts/AppLayout/ProjectDropdown.tsx new file mode 100644 index 00000000000..c666c6565dd --- /dev/null +++ b/studio/components/layouts/AppLayout/ProjectDropdown.tsx @@ -0,0 +1,165 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' +import { ParsedUrlQuery } from 'querystring' +import { useState } from 'react' +import { + Badge, + Button, + CommandEmpty_Shadcn_, + CommandGroup_Shadcn_, + CommandInput_Shadcn_, + CommandItem_Shadcn_, + CommandList_Shadcn_, + Command_Shadcn_, + IconCode, + IconPlus, + PopoverContent_Shadcn_, + PopoverTrigger_Shadcn_, + Popover_Shadcn_, +} from 'ui' + +import ShimmeringLoader from 'components/ui/ShimmeringLoader' +import { useProjectsQuery } from 'data/projects/projects-query' +import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query' +import { useSelectedOrganization, useSelectedProject } from 'hooks' +import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' +import { Organization, Project } from 'types' + +// [Fran] the idea is to let users change projects without losing the current page, +// but at the same time we need to redirect correctly between urls that might be +// unique to a project e.g. '/project/projectRef/editor/tableId' +// Right now, I'm gonna assume that any router query after the projectId, +// is a unique project id/marker so we'll redirect the user to the +// highest common route with just projectRef in the router queries. + +export const sanitizeRoute = (route: string, routerQueries: ParsedUrlQuery) => { + const queryArray = Object.entries(routerQueries) + + if (queryArray.length > 1) { + // [Joshen] Ideally we shouldn't use hard coded numbers, but temp workaround + // for storage bucket route since its longer + const isStorageBucketRoute = 'bucketId' in routerQueries + return route + .split('/') + .slice(0, isStorageBucketRoute ? 5 : 4) + .join('/') + } else { + return route + } +} + +const ProjectLink = ({ + project, + organization, + setOpen, +}: { + project: Project + organization?: Organization + setOpen: (value: boolean) => void +}) => { + const router = useRouter() + const sanitizedRoute = sanitizeRoute(router.route, router.query) + const isOrgBilling = !!organization?.subscription_id + + // [Joshen] Temp while we're interim between v1 and v2 billing + let href = sanitizedRoute?.replace('[ref]', project.ref) ?? `/project/${project.ref}` + if (href.endsWith('settings/addons') && !isOrgBilling) { + href = href.replace('settings/addons', 'settings/billing/subscription') + } else if (href.endsWith('settings/billing/subscription') && isOrgBilling) { + href = href.replace('settings/billing/subscription', 'settings/addons') + } else if (href.endsWith('settings/infrastructure') && !isOrgBilling) { + href = href.replace('settings/infrastructure', 'settings/billing/usage') + } else if (href.endsWith('settings/billing/usage') && !isOrgBilling) { + href = href.replace('settings/billing/usage', 'settings/infrastructure') + } + + return ( + setOpen(false)} + > + + {project.name} + + + ) +} + +const ProjectDropdown = ({ alt }: { alt?: boolean }) => { + const selectedProject = useSelectedProject() + const selectedOrganization = useSelectedOrganization() + const { data: allProjects, isLoading: isLoadingProjects } = useProjectsQuery() + + const isOrgBilling = !!selectedOrganization?.subscription_id + const { data: subscription, isSuccess } = useProjectSubscriptionV2Query( + { projectRef: selectedProject?.ref }, + { enabled: alt && !isOrgBilling } + ) + const projects = allProjects + ?.filter((x) => x.status !== PROJECT_STATUS.INACTIVE) + .filter((x) => x.organization_id === selectedOrganization?.id) + .sort((a, b) => a.name.localeCompare(b.name)) + + const [open, setOpen] = useState(false) + + if (isLoadingProjects && alt) { + return + } + + return IS_PLATFORM ? ( +
    + + +

    {selectedProject?.name}

    + {isSuccess && !isOrgBilling && {subscription?.plan.name}} +
    + + + + +
    + ) : ( + + ) +} + +export default ProjectDropdown From e46bb132f3026745554380de233e0cff5a090982 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 21 Jul 2023 15:46:51 +0800 Subject: [PATCH 24/53] Optimize UI when redirecting from /projects --- .../layouts/AccountLayout/AccountLayout.tsx | 9 ++++++-- .../layouts/AccountLayout/WithSidebar.tsx | 15 +++++++------ .../layouts/AppLayout/AppLayout.tsx | 6 +++-- .../AppLayout/OrganizationDropdown.tsx | 5 ++--- .../layouts/SettingsLayout/SettingsLayout.tsx | 20 +++++++++-------- studio/pages/projects.tsx | 22 ++++++++++--------- 6 files changed, 44 insertions(+), 33 deletions(-) diff --git a/studio/components/layouts/AccountLayout/AccountLayout.tsx b/studio/components/layouts/AccountLayout/AccountLayout.tsx index ae83fbece97..7efdbaa5e11 100644 --- a/studio/components/layouts/AccountLayout/AccountLayout.tsx +++ b/studio/components/layouts/AccountLayout/AccountLayout.tsx @@ -127,7 +127,7 @@ const AccountLayout = ({ children, title, breadcrumbs }: PropsWithChildren{children} } @@ -142,7 +142,12 @@ const AccountLayout = ({ children, title, breadcrumbs }: PropsWithChildren - + {children} diff --git a/studio/components/layouts/AccountLayout/WithSidebar.tsx b/studio/components/layouts/AccountLayout/WithSidebar.tsx index 4413ab6a3cb..cdf48e39a65 100644 --- a/studio/components/layouts/AccountLayout/WithSidebar.tsx +++ b/studio/components/layouts/AccountLayout/WithSidebar.tsx @@ -1,12 +1,12 @@ +import { useFlag } from 'hooks' +import { isUndefined } from 'lodash' import Link from 'next/link' import { FC, ReactNode } from 'react' -import { isUndefined } from 'lodash' -import { Menu, IconArrowUpRight, Badge, IconLogOut } from 'ui' -import { useFlag } from 'hooks' +import { Badge, IconArrowUpRight, IconLogOut, Menu } from 'ui' import LayoutHeader from '../ProjectLayout/LayoutHeader' import { SidebarLink, SidebarSection } from './AccountLayout.types' -interface Props { +interface WithSidebarProps { title: string breadcrumbs: any[] sections: SidebarSection[] @@ -27,7 +27,7 @@ The information hierarchy for WithSidebar is: SidebarItem SidebarLink */ -const WithSidebar: FC = ({ +const WithSidebar = ({ title, header, breadcrumbs = [], @@ -37,9 +37,10 @@ const WithSidebar: FC = ({ subitemsParentKey, hideSidebar = false, customSidebarContent, -}) => { +}: WithSidebarProps) => { const noContent = !sections && !customSidebarContent const ongoingIncident = useFlag('ongoingIncident') + const navLayoutV2 = useFlag('navigationLayoutV2') const maxHeight = ongoingIncident ? 'calc(100vh - 44px)' : '100vh' return ( @@ -89,7 +90,7 @@ const WithSidebar: FC = ({
    )}
    - + {!navLayoutV2 && }
    {children}
    diff --git a/studio/components/layouts/AppLayout/AppLayout.tsx b/studio/components/layouts/AppLayout/AppLayout.tsx index 7cefb9a6453..5abf9b3a826 100644 --- a/studio/components/layouts/AppLayout/AppLayout.tsx +++ b/studio/components/layouts/AppLayout/AppLayout.tsx @@ -6,7 +6,9 @@ import { useFlag } from 'hooks' const AppLayout = ({ children }: PropsWithChildren<{}>) => { const navLayoutV2 = useFlag('navigationLayoutV2') const ongoingIncident = useFlag('ongoingIncident') + const appHeaderHeight = navLayoutV2 ? 49 : 0 + const incidentBannerHeight = 44 return (
    @@ -14,10 +16,10 @@ const AppLayout = ({ children }: PropsWithChildren<{}>) => {
    diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index 210df04dcb8..d8cfe64cc6d 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link' import { useRouter } from 'next/router' import { useState } from 'react' import { @@ -11,17 +12,15 @@ import { Command_Shadcn_, IconCode, IconPlus, - Popover, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_, } from 'ui' +import ShimmeringLoader from 'components/ui/ShimmeringLoader' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { useFlag, useSelectedOrganization } from 'hooks' -import ShimmeringLoader from 'components/ui/ShimmeringLoader' -import Link from 'next/link' const OrganizationDropdown = () => { const router = useRouter() diff --git a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx index 6bb553df5df..81ec13f6e89 100644 --- a/studio/components/layouts/SettingsLayout/SettingsLayout.tsx +++ b/studio/components/layouts/SettingsLayout/SettingsLayout.tsx @@ -3,22 +3,24 @@ import AppLayout from '../AppLayout/AppLayout' import { useParams } from 'common' import OrganizationSettingsMenu from './OrganizationSettingsMenu' import AccountSettingsMenu from './AccountSettingsMenu' +import { useRouter } from 'next/router' const SettingsLayout = ({ children }: PropsWithChildren<{}>) => { + const router = useRouter() const { ref, slug } = useParams() - // [Joshen] Note to self - these could contribute to a Scaffold component - return (
    -
    - {slug === undefined && ref === undefined ? ( - - ) : ( - - )} -
    + {router.pathname !== '/projects' && ( +
    + {slug === undefined && ref === undefined ? ( + + ) : ( + + )} +
    + )}
    {children}
    diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index a004508fd74..ee0c4af7441 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -25,33 +25,35 @@ const ProjectsPage: NextPageWithLayout = () => { const { isLoading: isProfileLoading } = useProfile() const isLoading = isOrganizationLoading || isProfileLoading const navLayoutV2 = useFlag('navigationLayoutV2') + const hasWindowLoaded = typeof window !== 'undefined' useEffect(() => { - if (navLayoutV2 && isSuccess && typeof window !== 'undefined') { - const localStorageSlug = - typeof window !== 'undefined' - ? localStorage.getItem(LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION) - : undefined + if (navLayoutV2 && isSuccess && hasWindowLoaded) { + const localStorageSlug = localStorage.getItem( + LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION + ) if (localStorageSlug) router.push(`/org/${localStorageSlug}`) else router.push(`/org/${organizations[0].slug}`) } - }, [navLayoutV2, isSuccess, typeof window]) + }, [navLayoutV2, isSuccess, hasWindowLoaded]) return ( <> - {isLoading && ( -
    + {navLayoutV2 && isLoading && ( +
    )} {isError && ( -
    +
    )} - {isSuccess && ( + {!navLayoutV2 && isSuccess && (
    {IS_PLATFORM && (
    From 6ce4200486f7235f3c4fb234428dcd650efc52e5 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 21 Jul 2023 16:07:19 +0800 Subject: [PATCH 25/53] Add some slug validations --- .../interfaces/App/RouteValidationWrapper.tsx | 5 ++- .../AppLayout/OrganizationDropdown.tsx | 44 +++++++++---------- .../data/organizations/organizations-query.ts | 1 - studio/pages/org/[slug]/index.tsx | 1 + studio/pages/projects.tsx | 5 ++- 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/studio/components/interfaces/App/RouteValidationWrapper.tsx b/studio/components/interfaces/App/RouteValidationWrapper.tsx index 182de527f6b..9fd0b0c8cbf 100644 --- a/studio/components/interfaces/App/RouteValidationWrapper.tsx +++ b/studio/components/interfaces/App/RouteValidationWrapper.tsx @@ -14,6 +14,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { const { ui } = useStore() const router = useRouter() const { ref, slug } = useParams() + const navLayoutV2 = useFlag('navigationLayoutV2') /** * Array of urls/routes that should be ignored @@ -50,7 +51,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { if (!isValidOrg) { ui.setNotification({ category: 'error', message: 'This organization does not exist' }) - router.push('/projects') + router.push(navLayoutV2 ? `/org/${organizations[0].slug}` : '/projects') return } } @@ -70,7 +71,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { if (!isValidProject) { ui.setNotification({ category: 'error', message: 'This project does not exist' }) - router.push('/projects') + router.push(navLayoutV2 ? `/org/${organizations?.[0].slug}` : '/projects') return } } diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index d8cfe64cc6d..b05643e8602 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -61,29 +61,27 @@ const OrganizationDropdown = () => { No results found. - {organizations - ?.sort((a, b) => a.name.localeCompare(b.name)) - .map((org) => { - const href = router.pathname.includes('[slug]') - ? router.pathname.replace('[slug]', org.slug) - : router.pathname.includes('[ref]/settings') - ? `/org/${org.slug}/general` - : `/org/${org.slug}` - return ( - { - setOpen(false) - }} - > - - {org.name} - - - ) - })} + {organizations?.map((org) => { + const href = router.pathname.includes('[slug]') + ? router.pathname.replace('[slug]', org.slug) + : router.pathname.includes('[ref]/settings') + ? `/org/${org.slug}/general` + : `/org/${org.slug}` + return ( + { + setOpen(false) + }} + > + + {org.name} + + + ) + })} setOpen(false)}> diff --git a/studio/data/organizations/organizations-query.ts b/studio/data/organizations/organizations-query.ts index 98a2795b55b..dab843414ad 100644 --- a/studio/data/organizations/organizations-query.ts +++ b/studio/data/organizations/organizations-query.ts @@ -10,7 +10,6 @@ export async function getOrganizations(signal?: AbortSignal): Promise a.name.localeCompare(b.name)) - return sorted } diff --git a/studio/pages/org/[slug]/index.tsx b/studio/pages/org/[slug]/index.tsx index ba4fa711073..b1e1245555c 100644 --- a/studio/pages/org/[slug]/index.tsx +++ b/studio/pages/org/[slug]/index.tsx @@ -18,6 +18,7 @@ const ProjectsPage: NextPageWithLayout = () => { isError: isErrorProjects, isSuccess: isSuccessProjects, } = useProjectsQuery() + const organization = useSelectedOrganization() const projects = allProjects?.filter((project) => project.organization_id === organization?.id) diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index ee0c4af7441..5b26b3ccdb1 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -32,7 +32,10 @@ const ProjectsPage: NextPageWithLayout = () => { const localStorageSlug = localStorage.getItem( LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION ) - if (localStorageSlug) router.push(`/org/${localStorageSlug}`) + const verifiedSlug = organizations.some((org) => org.slug === localStorageSlug) + + if (organizations.length === 0) router.push('/new') + else if (localStorageSlug && verifiedSlug) router.push(`/org/${localStorageSlug}`) else router.push(`/org/${organizations[0].slug}`) } }, [navLayoutV2, isSuccess, hasWindowLoaded]) From 9469b236a548d367e9abd685210dc2aeb8ef1b39 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 21 Jul 2023 16:43:47 +0800 Subject: [PATCH 26/53] Update UserSettingsDropdown to use shadcn --- .../AppLayout/UserSettingsDropdown.tsx | 197 +++++++++++------- 1 file changed, 122 insertions(+), 75 deletions(-) diff --git a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx index b17cb64175f..59b652b2b7e 100644 --- a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx +++ b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx @@ -1,11 +1,32 @@ import Link from 'next/link' import { useRouter } from 'next/router' -import { Dropdown, IconLogOut, IconUser, Listbox, useCommandMenu } from 'ui' +import { + Button, + CommandGroup_Shadcn_, + CommandItem_Shadcn_, + CommandList_Shadcn_, + Command_Shadcn_, + DropdownMenuContent_Shadcn_, + DropdownMenuGroup_Shadcn_, + DropdownMenuItem_Shadcn_, + DropdownMenuSeparator_Shadcn_, + DropdownMenuShortcut_Shadcn_, + DropdownMenuTrigger_Shadcn_, + DropdownMenu_Shadcn_, + IconCheck, + IconMoon, + IconSun, + IconUser, + PopoverContent_Shadcn_, + PopoverTrigger_Shadcn_, + Popover_Shadcn_, + useCommandMenu, +} from 'ui' +import { useTheme } from 'common' import { useSignOut } from 'lib/auth' import { useProfile } from 'lib/profile' -import { useTheme } from 'common' -import KeyMap from 'components/to-be-cleaned/KeyMap' +import { useState } from 'react' const UserSettingsDropdown = () => { const signOut = useSignOut() @@ -13,6 +34,7 @@ const UserSettingsDropdown = () => { const { profile } = useProfile() const { setIsOpen } = useCommandMenu() const { isDarkMode, toggleTheme } = useTheme() + const [openTheme, setOpenTheme] = useState(false) const onClickLogout = async () => { await signOut() @@ -20,79 +42,104 @@ const UserSettingsDropdown = () => { } return ( - -

    - {profile?.first_name} {profile?.last_name} -

    -

    {profile?.primary_email}

    -
    , - , - - - Preferences - - , - - - Access tokens - - , - , - setIsOpen(true)}> -
    -

    Quick search

    -
    - -
    -
    -
    , - // - //
    - //

    Theme

    - // toggleTheme(e === 'dark')} - // // className="w-[200px]" - // > - // - // Dark - // - // - // Light - // - // - //
    - //
    , - , - Theme, - toggleTheme(e === 'dark')} - > - {/* [Joshen] Removing system default for now, needs to be supported in useTheme from common packages */} - {/* System default */} - Dark - Light - , - , - onClickLogout()}> -
    -

    Logout

    - -
    -
    , - ]} + { + if (!open) setOpenTheme(false) + }} > -
    - {profile?.first_name ? profile?.first_name?.[0] : } -
    - + +
    + {profile?.first_name ? profile?.first_name?.[0] : } +
    +
    + + +
    +

    + {profile?.first_name} {profile?.last_name} +

    +

    {profile?.primary_email}

    +
    + + + + + Preferences + + + + + Access tokens + + + + setIsOpen(true)}> + Quick search + ⌘K + + + event.preventDefault()}> +
    +

    Theme

    + + + + + + + + + { + setOpenTheme(false) + toggleTheme(true) + }} + > +
    + Dark + {isDarkMode ? ( + + ) : null} +
    +
    + { + setOpenTheme(false) + toggleTheme(false) + }} + > +
    + Light + {!isDarkMode ? ( + + ) : null} +
    +
    +
    +
    +
    +
    +
    +
    +
    + + onClickLogout()}> + Log out + +
    +
    +
    ) } From 686213511ce667609f8d983b810a32285e1975ef Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 24 Jul 2023 14:34:40 +0800 Subject: [PATCH 27/53] Address comments --- .../layouts/AppLayout/AppHeader.tsx | 4 +- .../AppLayout/OrganizationDropdown.tsx | 34 +++++-- .../layouts/AppLayout/ProjectDropdown.tsx | 28 ++++-- .../AppLayout/UserSettingsDropdown.tsx | 99 +++++-------------- 4 files changed, 73 insertions(+), 92 deletions(-) diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index 3aa96a11853..1681d108206 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -18,7 +18,7 @@ const AppHeader = () => { return (
    - + { - {project !== undefined && } + {project !== undefined && }
    diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index b05643e8602..c84da6e9f1a 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -1,6 +1,6 @@ import Link from 'next/link' import { useRouter } from 'next/router' -import { useState } from 'react' +import { useRef, useState } from 'react' import { Badge, Button, @@ -10,6 +10,7 @@ import { CommandItem_Shadcn_, CommandList_Shadcn_, Command_Shadcn_, + IconCheck, IconCode, IconPlus, PopoverContent_Shadcn_, @@ -25,14 +26,16 @@ import { useFlag, useSelectedOrganization } from 'hooks' const OrganizationDropdown = () => { const router = useRouter() const orgCreationV2 = useFlag('orgcreationv2') + const orgNameRef = useRef(null) const selectedOrganization = useSelectedOrganization() const { data: organizations, isLoading: isLoadingOrganizations } = useOrganizationsQuery() const slug = selectedOrganization?.slug const orgName = selectedOrganization?.name - const { data, isSuccess } = useOrgSubscriptionQuery({ orgSlug: slug }) + const { data: subscription, isSuccess } = useOrgSubscriptionQuery({ orgSlug: slug }) const [open, setOpen] = useState(false) + const popoverOffset = (orgNameRef.current?.offsetWidth ?? 0) + 12 if (isLoadingOrganizations) { return @@ -40,10 +43,10 @@ const OrganizationDropdown = () => { return (
    - - + +

    {orgName}

    - {isSuccess && {data?.plan.name}} + {isSuccess && {subscription?.plan.name}}
    @@ -55,7 +58,12 @@ const OrganizationDropdown = () => { icon={} /> - + @@ -74,17 +82,27 @@ const OrganizationDropdown = () => { className="cursor-pointer" onSelect={() => { setOpen(false) + router.push(href) }} > - {org.name} + + {org.name} + {org.slug === slug && } + ) })} - setOpen(false)}> + { + setOpen(false) + router.push(orgCreationV2 ? `/new-with-subscription` : `/new`) + }} + > diff --git a/studio/components/layouts/AppLayout/ProjectDropdown.tsx b/studio/components/layouts/AppLayout/ProjectDropdown.tsx index c666c6565dd..73c0440d8ec 100644 --- a/studio/components/layouts/AppLayout/ProjectDropdown.tsx +++ b/studio/components/layouts/AppLayout/ProjectDropdown.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' import { ParsedUrlQuery } from 'querystring' -import { useState } from 'react' +import { useRef, useState } from 'react' import { Badge, Button, @@ -87,15 +87,17 @@ const ProjectLink = ({ ) } -const ProjectDropdown = ({ alt }: { alt?: boolean }) => { +const ProjectDropdown = () => { + const router = useRouter() const selectedProject = useSelectedProject() const selectedOrganization = useSelectedOrganization() + const projectNameRef = useRef(null) const { data: allProjects, isLoading: isLoadingProjects } = useProjectsQuery() const isOrgBilling = !!selectedOrganization?.subscription_id const { data: subscription, isSuccess } = useProjectSubscriptionV2Query( { projectRef: selectedProject?.ref }, - { enabled: alt && !isOrgBilling } + { enabled: !isOrgBilling } ) const projects = allProjects ?.filter((x) => x.status !== PROJECT_STATUS.INACTIVE) @@ -103,15 +105,16 @@ const ProjectDropdown = ({ alt }: { alt?: boolean }) => { .sort((a, b) => a.name.localeCompare(b.name)) const [open, setOpen] = useState(false) + const popoverOffset = (projectNameRef.current?.offsetWidth ?? 0) + 12 - if (isLoadingProjects && alt) { + if (isLoadingProjects) { return } return IS_PLATFORM ? (
    - +

    {selectedProject?.name}

    {isSuccess && !isOrgBilling && {subscription?.plan.name}}
    @@ -125,7 +128,12 @@ const ProjectDropdown = ({ alt }: { alt?: boolean }) => { icon={} /> - + @@ -141,7 +149,13 @@ const ProjectDropdown = ({ alt }: { alt?: boolean }) => { ))} - setOpen(false)}> + { + setOpen(false) + router.push(`/new/${selectedOrganization?.slug}`) + }} + > diff --git a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx index 59b652b2b7e..4d061e31805 100644 --- a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx +++ b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx @@ -1,32 +1,26 @@ import Link from 'next/link' import { useRouter } from 'next/router' import { - Button, - CommandGroup_Shadcn_, - CommandItem_Shadcn_, - CommandList_Shadcn_, - Command_Shadcn_, + DropdownMenuCheckboxItem_Shadcn_, DropdownMenuContent_Shadcn_, DropdownMenuGroup_Shadcn_, DropdownMenuItem_Shadcn_, + DropdownMenuLabel_Shadcn_, DropdownMenuSeparator_Shadcn_, DropdownMenuShortcut_Shadcn_, + DropdownMenuSubContent_Shadcn_, + DropdownMenuSubTrigger_Shadcn_, + DropdownMenuSub_Shadcn_, DropdownMenuTrigger_Shadcn_, DropdownMenu_Shadcn_, IconCheck, - IconMoon, - IconSun, IconUser, - PopoverContent_Shadcn_, - PopoverTrigger_Shadcn_, - Popover_Shadcn_, useCommandMenu, } from 'ui' import { useTheme } from 'common' import { useSignOut } from 'lib/auth' import { useProfile } from 'lib/profile' -import { useState } from 'react' const UserSettingsDropdown = () => { const signOut = useSignOut() @@ -34,7 +28,6 @@ const UserSettingsDropdown = () => { const { profile } = useProfile() const { setIsOpen } = useCommandMenu() const { isDarkMode, toggleTheme } = useTheme() - const [openTheme, setOpenTheme] = useState(false) const onClickLogout = async () => { await signOut() @@ -42,11 +35,7 @@ const UserSettingsDropdown = () => { } return ( - { - if (!open) setOpenTheme(false) - }} - > +
    {profile?.first_name ? profile?.first_name?.[0] : } @@ -58,16 +47,16 @@ const UserSettingsDropdown = () => {

    {profile?.first_name} {profile?.last_name}

    -

    {profile?.primary_email}

    +

    {profile?.primary_email}

    - + router.push('/account/me')}>
    Preferences - + router.push('/account/tokens')}> Access tokens @@ -78,61 +67,21 @@ const UserSettingsDropdown = () => { ⌘K - event.preventDefault()}> -
    -

    Theme

    - - - - - - - - - { - setOpenTheme(false) - toggleTheme(true) - }} - > -
    - Dark - {isDarkMode ? ( - - ) : null} -
    -
    - { - setOpenTheme(false) - toggleTheme(false) - }} - > -
    - Light - {!isDarkMode ? ( - - ) : null} -
    -
    -
    -
    -
    -
    -
    -
    -
    + + Theme + toggleTheme(true)} + > + Dark + + toggleTheme(false)} + > + Light + + onClickLogout()}> Log out From 86a743b9648967fbd7f645557294605d5cf45a48 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 24 Jul 2023 14:42:56 +0800 Subject: [PATCH 28/53] Small changes --- studio/components/layouts/AppLayout/ProjectDropdown.tsx | 8 +++++++- .../components/layouts/AppLayout/UserSettingsDropdown.tsx | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/studio/components/layouts/AppLayout/ProjectDropdown.tsx b/studio/components/layouts/AppLayout/ProjectDropdown.tsx index 73c0440d8ec..97c4f12ca61 100644 --- a/studio/components/layouts/AppLayout/ProjectDropdown.tsx +++ b/studio/components/layouts/AppLayout/ProjectDropdown.tsx @@ -11,6 +11,7 @@ import { CommandItem_Shadcn_, CommandList_Shadcn_, Command_Shadcn_, + IconCheck, IconCode, IconPlus, PopoverContent_Shadcn_, @@ -24,6 +25,7 @@ import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscr import { useSelectedOrganization, useSelectedProject } from 'hooks' import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' import { Organization, Project } from 'types' +import { useParams } from 'common' // [Fran] the idea is to let users change projects without losing the current page, // but at the same time we need to redirect correctly between urls that might be @@ -58,6 +60,7 @@ const ProjectLink = ({ setOpen: (value: boolean) => void }) => { const router = useRouter() + const { ref } = useParams() const sanitizedRoute = sanitizeRoute(router.route, router.query) const isOrgBilling = !!organization?.subscription_id @@ -81,7 +84,10 @@ const ProjectLink = ({ onSelect={() => setOpen(false)} > - {project.name} + + {project.name} + {project.ref === ref && } +
    ) diff --git a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx index 4d061e31805..97596d64fe9 100644 --- a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx +++ b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx @@ -63,7 +63,7 @@ const UserSettingsDropdown = () => { setIsOpen(true)}> - Quick search + Command menu ⌘K From 2ac5d6c85a505d44d0a4c52bf04f6eb4e34c735e Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 24 Jul 2023 15:03:54 +0800 Subject: [PATCH 29/53] Slight optimization in useSelectedProject hook to return project if projects query already finished --- studio/components/layouts/AppLayout/AppHeader.tsx | 9 +++++---- studio/hooks/misc/useSelectedProject.ts | 6 +++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index 1681d108206..05ba788f92a 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -1,18 +1,19 @@ import Link from 'next/link' import { useRouter } from 'next/router' -import { useSelectedOrganization, useSelectedProject } from 'hooks' +import { useParams } from 'common' +import { useSelectedOrganization } from 'hooks' import FeedbackDropdown from '../ProjectLayout/LayoutHeader/FeedbackDropdown' import HelpPopover from '../ProjectLayout/LayoutHeader/HelpPopover' import NotificationsPopover from '../ProjectLayout/LayoutHeader/NotificationsPopover' -import ProjectDropdown from './ProjectDropdown' import OrganizationDropdown from './OrganizationDropdown' +import ProjectDropdown from './ProjectDropdown' import SettingsButton from './SettingsButton' import UserSettingsDropdown from './UserSettingsDropdown' const AppHeader = () => { const router = useRouter() - const project = useSelectedProject() + const { ref } = useParams() const organization = useSelectedOrganization() return ( @@ -28,7 +29,7 @@ const AppHeader = () => { - {project !== undefined && } + {ref !== undefined && }
    diff --git a/studio/hooks/misc/useSelectedProject.ts b/studio/hooks/misc/useSelectedProject.ts index 824bb14a073..8a300429bb1 100644 --- a/studio/hooks/misc/useSelectedProject.ts +++ b/studio/hooks/misc/useSelectedProject.ts @@ -1,9 +1,13 @@ import { useParams } from 'common' import { useProjectDetailQuery } from 'data/projects/project-detail-query' +import { useProjectsQuery } from 'data/projects/projects-query' +import { useMemo } from 'react' export function useSelectedProject() { const { ref } = useParams() + const { data: allProjects } = useProjectsQuery() const { data } = useProjectDetailQuery({ ref }) - return data + const projectMinimal = useMemo(() => allProjects?.find((project) => project.ref === ref), [ref]) + return data || projectMinimal } From c05a2abf0ead5a761fb8dd42a7684c1fca2bd695 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 24 Jul 2023 15:05:01 +0800 Subject: [PATCH 30/53] Add allProjects to dep array --- studio/hooks/misc/useSelectedProject.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/studio/hooks/misc/useSelectedProject.ts b/studio/hooks/misc/useSelectedProject.ts index 8a300429bb1..e61ecbfc086 100644 --- a/studio/hooks/misc/useSelectedProject.ts +++ b/studio/hooks/misc/useSelectedProject.ts @@ -8,6 +8,9 @@ export function useSelectedProject() { const { data: allProjects } = useProjectsQuery() const { data } = useProjectDetailQuery({ ref }) - const projectMinimal = useMemo(() => allProjects?.find((project) => project.ref === ref), [ref]) + const projectMinimal = useMemo( + () => allProjects?.find((project) => project.ref === ref), + [ref, allProjects] + ) return data || projectMinimal } From eafa23d5f1b1e1805962d63dbea53c1daa9284c3 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 25 Jul 2023 00:42:51 +0800 Subject: [PATCH 31/53] Small refactor --- .../interfaces/App/AppBannerWrapper.tsx | 22 ++++--------------- .../layouts/AppLayout/IncidentBanner.tsx | 19 ++++++++++++++++ studio/pages/_app.tsx | 5 +++++ 3 files changed, 28 insertions(+), 18 deletions(-) create mode 100644 studio/components/layouts/AppLayout/IncidentBanner.tsx diff --git a/studio/components/interfaces/App/AppBannerWrapper.tsx b/studio/components/interfaces/App/AppBannerWrapper.tsx index 3f3faa61251..581ca0c6669 100644 --- a/studio/components/interfaces/App/AppBannerWrapper.tsx +++ b/studio/components/interfaces/App/AppBannerWrapper.tsx @@ -1,26 +1,12 @@ -import { FC } from 'react' -import Link from 'next/link' +import IncidentBanner from 'components/layouts/AppLayout/IncidentBanner' import { useFlag } from 'hooks' -import { IconExternalLink } from 'ui' +import { PropsWithChildren } from 'react' -const AppBannerWrapper: FC = ({ children }) => { +const AppBannerWrapper = ({ children }: PropsWithChildren<{}>) => { const ongoingIncident = useFlag('ongoingIncident') - return ( diff --git a/studio/components/layouts/AppLayout/IncidentBanner.tsx b/studio/components/layouts/AppLayout/IncidentBanner.tsx new file mode 100644 index 00000000000..4004c1922cb --- /dev/null +++ b/studio/components/layouts/AppLayout/IncidentBanner.tsx @@ -0,0 +1,19 @@ +import Link from 'next/link' +import { IconExternalLink } from 'ui' + +const IncidentBanner = () => { + return ( + + +
    +

    + We are currently investigating a technical issue, follow status.supabase.com for updates +

    + +
    +
    + + ) +} + +export default IncidentBanner diff --git a/studio/pages/_app.tsx b/studio/pages/_app.tsx index 6051ab68387..b0652d80767 100644 --- a/studio/pages/_app.tsx +++ b/studio/pages/_app.tsx @@ -62,6 +62,11 @@ dayjs.extend(relativeTime) dart(Prism) +// [Joshen TODO] Once we settle on the new nav layout - we'll need a lot of clean up in terms of our layout components +// a lot of them are unnecessary and introduce way too many cluttered CSS especially with the height styles that make +// debugging way too difficult. Ideal scenario is we just have one AppLayout to control the height and scroll areas of +// the dashboard, all other layout components should not be doing that + function CustomApp({ Component, pageProps }: AppPropsWithLayout) { const queryClient = useRootQueryClient() const [rootStore] = useState(() => new RootStore()) From e7f9c24e152f4232b533dde0c06552ee89e61c37 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 25 Jul 2023 11:24:11 +0800 Subject: [PATCH 32/53] remove bg --- .../layouts/AppLayout/UserSettingsDropdown.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx index 97596d64fe9..e8ccc497529 100644 --- a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx +++ b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx @@ -50,7 +50,7 @@ const UserSettingsDropdown = () => {

    {profile?.primary_email}

    - + router.push('/account/me')}> Preferences @@ -61,13 +61,12 @@ const UserSettingsDropdown = () => { Access tokens - + setIsOpen(true)}> Command menu ⌘K - - + Theme { Light - + onClickLogout()}> Log out From 43e6a6730f40b787b8d45f2b8a2bd072249ae727 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 25 Jul 2023 12:35:41 +0800 Subject: [PATCH 33/53] fix a11y on comboboxes --- package-lock.json | 261 ++---------------- packages/ui/package.json | 2 +- .../ui/src/components/shadcn/ui/command.tsx | 8 +- studio/.gitignore | 2 + .../AppLayout/OrganizationDropdown.tsx | 33 ++- .../layouts/AppLayout/ProjectDropdown.tsx | 50 ++-- 6 files changed, 79 insertions(+), 277 deletions(-) diff --git a/package-lock.json b/package-lock.json index 084b2d064df..22daacdc395 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17539,246 +17539,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/cmdk-supabase": { - "version": "0.2.2", - "license": "MIT", - "dependencies": { - "@radix-ui/react-dialog": "1.0.0", - "@radix-ui/react-id": "^0.0.6", - "command-score": "0.1.2", - "use-sync-external-store": "^1.2.0" - }, - "peerDependencies": { - "react": "^17.0", - "react-dom": "^17.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/primitive": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-context": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-dialog": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-dismissable-layer": "1.0.0", - "@radix-ui/react-focus-guards": "1.0.0", - "@radix-ui/react-focus-scope": "1.0.0", - "@radix-ui/react-id": "1.0.0", - "@radix-ui/react-portal": "1.0.0", - "@radix-ui/react-presence": "1.0.0", - "@radix-ui/react-primitive": "1.0.0", - "@radix-ui/react-slot": "1.0.0", - "@radix-ui/react-use-controllable-state": "1.0.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.4" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-id": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-primitive": "1.0.0", - "@radix-ui/react-use-callback-ref": "1.0.0", - "@radix-ui/react-use-escape-keydown": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-primitive": "1.0.0", - "@radix-ui/react-use-callback-ref": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-id": { - "version": "0.0.6", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-portal": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-presence": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-use-layout-effect": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-primitive": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-slot": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/cmdk-supabase/node_modules/react-remove-scroll": { - "version": "2.5.4", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/co": { "version": "4.6.0", "dev": true, @@ -17921,7 +17681,8 @@ }, "node_modules/command-score": { "version": "0.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/command-score/-/command-score-0.1.2.tgz", + "integrity": "sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==" }, "node_modules/commander": { "version": "6.2.1", @@ -40177,7 +39938,7 @@ "autoprefixer": "^10.4.14", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", - "cmdk-supabase": "^0.2.2", + "cmdk-supabase": "1.0.6", "color": "^4.2.3", "colorjs.io": "^0.4.3", "common-tags": "^1.8.2", @@ -41287,6 +41048,22 @@ "node": ">=6" } }, + "packages/ui/node_modules/cmdk-supabase": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cmdk-supabase/-/cmdk-supabase-1.0.6.tgz", + "integrity": "sha512-DPeenlnJBtSxx49gJzMbe3gmC/IYCJcE5AZbs/G82pjWtHwK/skzc6LkJqIjdt9JOSoLjsWk0gYFCQXUp3JGBQ==", + "dependencies": { + "@radix-ui/react-dialog": "^1.0.4", + "@radix-ui/react-id": "^0.0.6", + "@radix-ui/react-slot": "1.0.2", + "command-score": "0.1.2", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^17.0", + "react-dom": "^17.0" + } + }, "packages/ui/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", diff --git a/packages/ui/package.json b/packages/ui/package.json index 207a450235d..e0a036269a0 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -54,7 +54,7 @@ "autoprefixer": "^10.4.14", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", - "cmdk-supabase": "^0.2.2", + "cmdk-supabase": "1.0.6", "color": "^4.2.3", "colorjs.io": "^0.4.3", "common-tags": "^1.8.2", diff --git a/packages/ui/src/components/shadcn/ui/command.tsx b/packages/ui/src/components/shadcn/ui/command.tsx index 24f15e9e715..2f57d3bd0d7 100644 --- a/packages/ui/src/components/shadcn/ui/command.tsx +++ b/packages/ui/src/components/shadcn/ui/command.tsx @@ -70,8 +70,12 @@ CommandList.displayName = CommandPrimitive.List.displayName const CommandEmpty = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->((props, ref) => ( - +>(({ className, ...props }, ref) => ( + )) CommandEmpty.displayName = CommandPrimitive.Empty.displayName diff --git a/studio/.gitignore b/studio/.gitignore index 9dd40bd86d1..fbcd3d36150 100644 --- a/studio/.gitignore +++ b/studio/.gitignore @@ -33,3 +33,5 @@ yarn-error.log* .env.production.local .vercel + +/public/dashboard diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index c84da6e9f1a..e33d171fe59 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -46,7 +46,7 @@ const OrganizationDropdown = () => {

    {orgName}

    - {isSuccess && {subscription?.plan.name}} + {isSuccess && {subscription?.plan.name}}
    @@ -67,7 +67,7 @@ const OrganizationDropdown = () => { - No results found. + No organizations found {organizations?.map((org) => { const href = router.pathname.includes('[slug]') @@ -76,22 +76,29 @@ const OrganizationDropdown = () => { ? `/org/${org.slug}/general` : `/org/${org.slug}` return ( - { + onClick={() => { setOpen(false) - router.push(href) }} > - - - {org.name} - {org.slug === slug && } + { + setOpen(false) + router.push(href) + }} + > + + {org.name} + {org.slug === slug && } - - +
    + ) })}
    diff --git a/studio/components/layouts/AppLayout/ProjectDropdown.tsx b/studio/components/layouts/AppLayout/ProjectDropdown.tsx index 97c4f12ca61..e1bda5c145b 100644 --- a/studio/components/layouts/AppLayout/ProjectDropdown.tsx +++ b/studio/components/layouts/AppLayout/ProjectDropdown.tsx @@ -77,19 +77,24 @@ const ProjectLink = ({ } return ( + // setOpen(false)} + className="cursor-pointer w-full flex items-center justify-between" + onSelect={() => { + console.log('hello') + router.push(href) + setOpen(false) + }} > - - - {project.name} - {project.ref === ref && } - - + + {project.name} + {project.ref === ref && } + + // ) } @@ -143,7 +148,7 @@ const ProjectDropdown = () => { - No results found. + No projects found {projects?.map((project) => ( { ))} - { - setOpen(false) - router.push(`/new/${selectedOrganization?.slug}`) - }} + { + // setOpen(false) + // }} > - - + { + // setOpen(false) + // router.push(`/new/${selectedOrganization?.slug}`) + // }} + > +

    New project

    - -
    + +
    From 056e5d628ae56b44edf4e2635f73f08a68a4c76b Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 25 Jul 2023 13:09:10 +0800 Subject: [PATCH 34/53] fix combo --- package-lock.json | 45 ++++++++++------- packages/ui/package.json | 2 +- .../AppLayout/OrganizationDropdown.tsx | 32 +++++------- .../layouts/AppLayout/ProjectDropdown.tsx | 50 +++++++++---------- 4 files changed, 67 insertions(+), 62 deletions(-) diff --git a/package-lock.json b/package-lock.json index 22daacdc395..6ef3321773e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17539,6 +17539,33 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/cmdk-supabase": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cmdk-supabase/-/cmdk-supabase-1.0.7.tgz", + "integrity": "sha512-5Ts9dZTLSqNwCEFzps/dtNK6Q1baje1JuA+5qjEvtfkk+SGZG0S5D1uqxbAk7YQqF/ctGZHxe8O+0+vNPYMk2A==", + "dependencies": { + "@radix-ui/react-dialog": "^1.0.4", + "@radix-ui/react-id": "^0.0.6", + "@radix-ui/react-slot": "1.0.2", + "command-score": "0.1.2", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^17.0", + "react-dom": "^17.0" + } + }, + "node_modules/cmdk-supabase/node_modules/@radix-ui/react-id": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-0.0.6.tgz", + "integrity": "sha512-PzmraF34fYggsYvTIZVJ5S68WMp3aKUN3HkSmGnz4zn9zpRjkAbbg7Xn3ueQI3FQsLWKgyUfnpsmWFDndpcqYg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0" + } + }, "node_modules/co": { "version": "4.6.0", "dev": true, @@ -39938,7 +39965,7 @@ "autoprefixer": "^10.4.14", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", - "cmdk-supabase": "1.0.6", + "cmdk-supabase": "^1.0.7", "color": "^4.2.3", "colorjs.io": "^0.4.3", "common-tags": "^1.8.2", @@ -41048,22 +41075,6 @@ "node": ">=6" } }, - "packages/ui/node_modules/cmdk-supabase": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cmdk-supabase/-/cmdk-supabase-1.0.6.tgz", - "integrity": "sha512-DPeenlnJBtSxx49gJzMbe3gmC/IYCJcE5AZbs/G82pjWtHwK/skzc6LkJqIjdt9JOSoLjsWk0gYFCQXUp3JGBQ==", - "dependencies": { - "@radix-ui/react-dialog": "^1.0.4", - "@radix-ui/react-id": "^0.0.6", - "@radix-ui/react-slot": "1.0.2", - "command-score": "0.1.2", - "use-sync-external-store": "^1.2.0" - }, - "peerDependencies": { - "react": "^17.0", - "react-dom": "^17.0" - } - }, "packages/ui/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", diff --git a/packages/ui/package.json b/packages/ui/package.json index e0a036269a0..59f0f58500e 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -54,7 +54,7 @@ "autoprefixer": "^10.4.14", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", - "cmdk-supabase": "1.0.6", + "cmdk-supabase": "^1.0.7", "color": "^4.2.3", "colorjs.io": "^0.4.3", "common-tags": "^1.8.2", diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index e33d171fe59..b58fbd62956 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -76,14 +76,7 @@ const OrganizationDropdown = () => { ? `/org/${org.slug}/general` : `/org/${org.slug}` return ( - { - setOpen(false) - }} - > + { })} - { - setOpen(false) - router.push(orgCreationV2 ? `/new-with-subscription` : `/new`) - }} - > - - + + { + setOpen(false) + router.push(orgCreationV2 ? `/new-with-subscription` : `/new`) + }} + asChild + > +

    New organization

    - -
    +
    +
    diff --git a/studio/components/layouts/AppLayout/ProjectDropdown.tsx b/studio/components/layouts/AppLayout/ProjectDropdown.tsx index e1bda5c145b..61fbfea55b9 100644 --- a/studio/components/layouts/AppLayout/ProjectDropdown.tsx +++ b/studio/components/layouts/AppLayout/ProjectDropdown.tsx @@ -77,24 +77,24 @@ const ProjectLink = ({ } return ( - // - { - console.log('hello') - router.push(href) - setOpen(false) - }} - > - - {project.name} - {project.ref === ref && } - - - // + + { + router.push(href) + setOpen(false) + }} + onClick={() => setOpen(false)} + > + + {project.name} + {project.ref === ref && } + + + ) } @@ -163,17 +163,17 @@ const ProjectDropdown = () => { { - // setOpen(false) - // }} + onClick={() => { + setOpen(false) + }} > { - // setOpen(false) - // router.push(`/new/${selectedOrganization?.slug}`) - // }} + onSelect={() => { + setOpen(false) + router.push(`/new/${selectedOrganization?.slug}`) + }} > From 1919cf8b224bdef914c5af829746c260651a69e4 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 25 Jul 2023 13:24:19 +0800 Subject: [PATCH 35/53] Small fix --- studio/components/layouts/AppLayout/OrganizationDropdown.tsx | 4 +++- studio/components/layouts/AppLayout/ProjectDropdown.tsx | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index b58fbd62956..0c0728e6e4e 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -85,6 +85,7 @@ const OrganizationDropdown = () => { setOpen(false) router.push(href) }} + onClick={() => setOpen(false)} > {org.name} @@ -98,12 +99,13 @@ const OrganizationDropdown = () => { { setOpen(false) router.push(orgCreationV2 ? `/new-with-subscription` : `/new`) }} - asChild + onClick={() => setOpen(false)} > diff --git a/studio/components/layouts/AppLayout/ProjectDropdown.tsx b/studio/components/layouts/AppLayout/ProjectDropdown.tsx index 61fbfea55b9..be09dcbb804 100644 --- a/studio/components/layouts/AppLayout/ProjectDropdown.tsx +++ b/studio/components/layouts/AppLayout/ProjectDropdown.tsx @@ -174,6 +174,7 @@ const ProjectDropdown = () => { setOpen(false) router.push(`/new/${selectedOrganization?.slug}`) }} + onClick={() => setOpen(false)} > From f3008f17d1574a39112ea46a8ffc90c0f67c17e1 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 25 Jul 2023 14:15:43 +0800 Subject: [PATCH 36/53] Fix access tokens max width --- studio/components/interfaces/Account/AccessTokenList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/interfaces/Account/AccessTokenList.tsx b/studio/components/interfaces/Account/AccessTokenList.tsx index 5746aab2995..9b76f1c93b0 100644 --- a/studio/components/interfaces/Account/AccessTokenList.tsx +++ b/studio/components/interfaces/Account/AccessTokenList.tsx @@ -25,7 +25,7 @@ const AccessTokenList = observer(() => { return ( <> -
    +
    Token, From 7367a7af1f46b24edeeb5d7668d5f330aeb610de Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 25 Jul 2023 14:20:55 +0800 Subject: [PATCH 37/53] Fix wrong logic in NotificationsPopover --- .../LayoutHeader/NotificationsPopover/index.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx index f4e3db7e003..ebc0348ea3f 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/NotificationsPopover/index.tsx @@ -238,13 +238,9 @@ const NotificationsPopover = ({ alt = false }: NotificationsPopoverProps) => {
    - {/* hasNewNotifications */} - {true && ( + {hasNewNotifications && ( <> {alt ? null : ( - //
    - //

    5

    - //
    From 4c9729b0464f31eced5d5eb0b1dcda6f5953898a Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 25 Jul 2023 14:23:30 +0800 Subject: [PATCH 38/53] migrate feedback dropdown to shadcn --- .../FeedbackDropdown/FeedbackDropdown.tsx | 47 +++++++----- .../FeedbackDropdown/FeedbackWidget.tsx | 76 +++++++++++-------- 2 files changed, 73 insertions(+), 50 deletions(-) diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx index 00da09d71eb..be10c7d934e 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.tsx @@ -1,5 +1,12 @@ import { useState } from 'react' -import { Button, IconMessageCircle, Popover } from 'ui' +import { + Button, + IconMessageCircle, + Popover, + PopoverContent_Shadcn_, + PopoverTrigger_Shadcn_, + Popover_Shadcn_, +} from 'ui' import FeedbackWidget from './FeedbackWidget' interface FeedbackDropdownProps { @@ -22,16 +29,29 @@ const FeedbackDropdown = ({ alt = false }: FeedbackDropdownProps) => { } return ( - { setIsOpen(e) if (!e) setScreenshot(undefined) }} - size="content" - side="bottom" - align="end" - overlay={ + > + + + + { screenshot={screenshot} setScreenshot={setScreenshot} /> - } - > - - + + ) } diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackWidget.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackWidget.tsx index 0b9571b34ba..39851aa93b4 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackWidget.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackWidget.tsx @@ -2,7 +2,21 @@ import { toPng } from 'html-to-image' import Link from 'next/link' import { useRouter } from 'next/router' import { ChangeEvent, FC, useEffect, useRef, useState } from 'react' -import { Button, Dropdown, IconCamera, IconImage, IconUpload, IconX, Input, Popover } from 'ui' +import { + Button, + Dropdown, + DropdownMenuContent_Shadcn_, + DropdownMenuItem_Shadcn_, + DropdownMenuSeparator_Shadcn_, + DropdownMenuTrigger_Shadcn_, + DropdownMenu_Shadcn_, + IconCamera, + IconImage, + IconUpload, + IconX, + Input, + Popover, +} from 'ui' import { useParams } from 'common' import { useSendFeedbackMutation } from 'data/feedback/feedback-send' @@ -125,13 +139,12 @@ const FeedbackWidget: FC = ({ value={feedback} onChange={onFeedbackChange} /> - +
    - -
    {screenshot !== undefined ? (
    = ({ }} className="cursor-pointer rounded h-[26px] w-[30px] border border-scale-600 relative bg-cover bg-center bg-no-repeat" > -
    = ({ }} > -
    +
    ) : ( - + + + + + } - onClick={() => { + onSelect={() => { if (uploadButtonRef.current) (uploadButtonRef.current as any).click() }} > + Upload screenshot - , - + } - onClick={() => captureScreenshot()} + onSelect={() => captureScreenshot()} > + Capture screenshot - , - ]} - > - - + + + )} Date: Tue, 25 Jul 2023 14:23:48 +0800 Subject: [PATCH 39/53] Update user settings to allow opening in new tab --- .../AppLayout/UserSettingsDropdown.tsx | 84 ++++++++++++------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx index e8ccc497529..1867e7d9223 100644 --- a/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx +++ b/studio/components/layouts/AppLayout/UserSettingsDropdown.tsx @@ -6,6 +6,8 @@ import { DropdownMenuGroup_Shadcn_, DropdownMenuItem_Shadcn_, DropdownMenuLabel_Shadcn_, + DropdownMenuRadioGroup_Shadcn_, + DropdownMenuRadioItem_Shadcn_, DropdownMenuSeparator_Shadcn_, DropdownMenuShortcut_Shadcn_, DropdownMenuSubContent_Shadcn_, @@ -21,12 +23,14 @@ import { import { useTheme } from 'common' import { useSignOut } from 'lib/auth' import { useProfile } from 'lib/profile' +import { useState } from 'react' const UserSettingsDropdown = () => { const signOut = useSignOut() const router = useRouter() + const [open, setOpen] = useState(false) const { profile } = useProfile() - const { setIsOpen } = useCommandMenu() + const { setIsOpen: setCommandMenuOpen } = useCommandMenu() const { isDarkMode, toggleTheme } = useTheme() const onClickLogout = async () => { @@ -35,11 +39,9 @@ const UserSettingsDropdown = () => { } return ( - - -
    - {profile?.first_name ? profile?.first_name?.[0] : } -
    + setOpen(!open)} modal={false}> + + setOpen(true)} size={14} strokeWidth={2} /> @@ -51,38 +53,62 @@ const UserSettingsDropdown = () => {
    - router.push('/account/me')}> - - Preferences - - - router.push('/account/tokens')}> - - Access tokens - - + + { + router.push('/account/me') + }} + onClick={() => setOpen(false)} + asChild + > + Preferences + + + + { + router.push('/account/tokens') + }} + onClick={() => setOpen(false)} + asChild + > + Access tokens + + - setIsOpen(true)}> + { + setOpen(false) + setCommandMenuOpen(true) + }} + > Command menu ⌘K Theme - toggleTheme(true)} + { + const dark = x === 'dark' + toggleTheme(Boolean(dark)) + }} > - Dark - - toggleTheme(false)} - > - Light - + Dark + Light + - onClickLogout()}> + { + onClickLogout() + setOpen(false) + }} + > Log out From c3888b22e4df08c39dadd6542493d5459622ff7b Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 25 Jul 2023 14:23:59 +0800 Subject: [PATCH 40/53] modal behavior disabled for new dropdowns/comboboxes --- packages/ui/src/components/shadcn/ui/dropdown-menu.tsx | 2 +- studio/components/layouts/AppLayout/OrganizationDropdown.tsx | 2 +- studio/components/layouts/AppLayout/ProjectDropdown.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/components/shadcn/ui/dropdown-menu.tsx b/packages/ui/src/components/shadcn/ui/dropdown-menu.tsx index 93eb4f2b669..e4141e48221 100644 --- a/packages/ui/src/components/shadcn/ui/dropdown-menu.tsx +++ b/packages/ui/src/components/shadcn/ui/dropdown-menu.tsx @@ -153,7 +153,7 @@ const DropdownMenuSeparator = React.forwardRef< >(({ className, ...props }, ref) => ( )) diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index b58fbd62956..154776243fe 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -50,7 +50,7 @@ const OrganizationDropdown = () => { - + +
    + +
    - {alt && ( + {alt ? ( @@ -167,7 +169,7 @@ const HelpPopover = ({ alt = false }: HelpPopoverProps) => {
    - )} + ) : null} ) From f23b2d00b3c6fb51a7e0f1e5d1fdfed7f051f4a1 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 25 Jul 2023 14:35:56 +0800 Subject: [PATCH 44/53] migrate Help popover to shadcn --- .../LayoutHeader/HelpPopover.tsx | 276 +++++++++--------- 1 file changed, 136 insertions(+), 140 deletions(-) diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx index 463088d2b42..fc5d36c9f4f 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx @@ -12,6 +12,9 @@ import { IconMessageCircle, IconTool, Popover, + PopoverContent_Shadcn_, + PopoverTrigger_Shadcn_, + Popover_Shadcn_, } from 'ui' interface HelpPopoverProps { @@ -24,154 +27,147 @@ const HelpPopover = ({ alt = false }: HelpPopoverProps) => { const supportUrl = `/support/new${projectRef ? `?ref=${projectRef}` : ''}` return ( - -
    -
    Need help with your project?
    -

    - For issues with your project hosted on supabase.com, or other inquiries about our - hosted services. -

    -
    - - - - - - - - - -
    -

    - Expected response time is based on your billing plan. Pro and Pay as You Go plans are - prioritized. -

    + + + +
    - - +
    +
    + {alt ? ( + + + +
    +

    Help

    +
    +
    +
    + ) : null} +
    +
    + +
    +
    Need help with your project?
    +

    + For issues with your project hosted on supabase.com, or other inquiries about our hosted + services. +

    +
    + + + + + + + + + +
    +

    + Expected response time is based on your billing plan. Pro and Pay as You Go plans are + prioritized. +

    +
    + + + +
    +
    + +
    +
    +
    Reach out to the community
    + +

    + For other support, including questions on our client libraries, advice, or best + practices. +

    +
    + - -
    -
    -
    Reach out to the community
    - -

    - For other support, including questions on our client libraries, advice, or best - practices. -

    -
    -
    - } - > - - -
    - -
    -
    - {alt ? ( - - - -
    -

    Help

    -
    -
    -
    - ) : null} -
    - + + ) } From 7fdcef1d00c5921fca4b97a4e9a4f830b00d8f12 Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Tue, 25 Jul 2023 14:57:37 +0800 Subject: [PATCH 45/53] asChild issues --- .../layouts/AppLayout/AppHeader.tsx | 16 ++++--- .../layouts/AppLayout/SettingsButton.tsx | 14 +++--- .../LayoutHeader/HelpPopover.tsx | 43 +++++++++---------- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index 05ba788f92a..60feb13dbfe 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -32,12 +32,16 @@ const AppHeader = () => { {ref !== undefined && }
    -
    - - - - - +
    +
    + + + + +
    +
    + +
    ) diff --git a/studio/components/layouts/AppLayout/SettingsButton.tsx b/studio/components/layouts/AppLayout/SettingsButton.tsx index a30744a72b1..ef22d069887 100644 --- a/studio/components/layouts/AppLayout/SettingsButton.tsx +++ b/studio/components/layouts/AppLayout/SettingsButton.tsx @@ -5,17 +5,13 @@ import * as Tooltip from '@radix-ui/react-tooltip' const SettingsButton = ({ slug }: { slug: string }) => { return ( - - + + -
    - {alt ? ( - - - -
    -

    Help

    -
    -
    -
    - ) : null} - - + + {alt ? ( + + + +
    +

    Help

    +
    +
    +
    + ) : null} +
    Need help with your project?
    From 588bbb4a4072a1440f1b0fe9d8ba0a178c22c129 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 26 Jul 2023 11:47:38 +0800 Subject: [PATCH 46/53] Revert changes in useSelectedProject, add error state for schema picker in tbale editor --- .../components/layouts/AppLayout/ProjectDropdown.tsx | 6 ++++-- .../layouts/TableEditorLayout/TableEditorMenu.tsx | 9 ++++++++- studio/hooks/misc/useSelectedProject.ts | 10 +--------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/studio/components/layouts/AppLayout/ProjectDropdown.tsx b/studio/components/layouts/AppLayout/ProjectDropdown.tsx index 2d467b1864a..9a882441d23 100644 --- a/studio/components/layouts/AppLayout/ProjectDropdown.tsx +++ b/studio/components/layouts/AppLayout/ProjectDropdown.tsx @@ -100,20 +100,22 @@ const ProjectLink = ({ const ProjectDropdown = () => { const router = useRouter() - const selectedProject = useSelectedProject() + const { ref } = useParams() + const projectDetails = useSelectedProject() const selectedOrganization = useSelectedOrganization() const projectNameRef = useRef(null) const { data: allProjects, isLoading: isLoadingProjects } = useProjectsQuery() const isOrgBilling = !!selectedOrganization?.subscription_id const { data: subscription, isSuccess } = useProjectSubscriptionV2Query( - { projectRef: selectedProject?.ref }, + { projectRef: ref }, { enabled: !isOrgBilling } ) const projects = allProjects ?.filter((x) => x.status !== PROJECT_STATUS.INACTIVE) .filter((x) => x.organization_id === selectedOrganization?.id) .sort((a, b) => a.name.localeCompare(b.name)) + const selectedProject = projectDetails || projects?.find((project) => project.ref === ref) const [open, setOpen] = useState(false) const popoverOffset = (projectNameRef.current?.offsetWidth ?? 0) + 12 diff --git a/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx b/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx index 1e8f7e114e6..7e7190a73e9 100644 --- a/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx +++ b/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx @@ -2,6 +2,7 @@ import { useMemo, useState } from 'react' import { noop, partition } from 'lodash' import { observer } from 'mobx-react-lite' import { + Alert, Button, Dropdown, IconCheck, @@ -83,7 +84,6 @@ const TableEditorMenu = ({ ) const schemas: PostgresSchema[] = meta.schemas.list() - const schema = schemas.find((schema) => schema.name === selectedSchema) const canCreateTables = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables') @@ -110,6 +110,13 @@ const TableEditorMenu = ({ Loading schemas...
    + ) : meta.schemas.hasError ? ( + +

    Error: {meta.schemas?.error?.message}

    + +
    ) : ( allProjects?.find((project) => project.ref === ref), - [ref, allProjects] - ) - return data || projectMinimal + return data } From 1fbd7c8240c25b276057020d658b3bf75487ee3c Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 26 Jul 2023 12:04:20 +0800 Subject: [PATCH 47/53] Small refactor in OrganizationDropdown --- studio/components/layouts/AppLayout/AppHeader.tsx | 2 +- .../layouts/AppLayout/OrganizationDropdown.tsx | 6 +++++- .../components/layouts/AppLayout/SettingsButton.tsx | 12 ++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/studio/components/layouts/AppLayout/AppHeader.tsx b/studio/components/layouts/AppLayout/AppHeader.tsx index 60feb13dbfe..5ea83c6e56a 100644 --- a/studio/components/layouts/AppLayout/AppHeader.tsx +++ b/studio/components/layouts/AppLayout/AppHeader.tsx @@ -37,7 +37,7 @@ const AppHeader = () => { - +
    diff --git a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx index f5408e47440..035e4073307 100644 --- a/studio/components/layouts/AppLayout/OrganizationDropdown.tsx +++ b/studio/components/layouts/AppLayout/OrganizationDropdown.tsx @@ -32,7 +32,11 @@ const OrganizationDropdown = () => { const slug = selectedOrganization?.slug const orgName = selectedOrganization?.name - const { data: subscription, isSuccess } = useOrgSubscriptionQuery({ orgSlug: slug }) + const isOrgBilling = !!selectedOrganization?.subscription_id + const { data: subscription, isSuccess } = useOrgSubscriptionQuery( + { orgSlug: slug }, + { enabled: isOrgBilling } + ) const [open, setOpen] = useState(false) const popoverOffset = (orgNameRef.current?.offsetWidth ?? 0) + 12 diff --git a/studio/components/layouts/AppLayout/SettingsButton.tsx b/studio/components/layouts/AppLayout/SettingsButton.tsx index ef22d069887..7576d779903 100644 --- a/studio/components/layouts/AppLayout/SettingsButton.tsx +++ b/studio/components/layouts/AppLayout/SettingsButton.tsx @@ -1,11 +1,15 @@ -import Link from 'next/link' -import { Button, IconSettings } from 'ui' import * as Tooltip from '@radix-ui/react-tooltip' +import { useSelectedOrganization } from 'hooks' +import Link from 'next/link' +import { IconSettings } from 'ui' + +const SettingsButton = () => { + const selectedOrganization = useSelectedOrganization() + const slug = selectedOrganization?.slug -const SettingsButton = ({ slug }: { slug: string }) => { return ( - + From 6a40595b5573152228837a5bedaf7df09ddf7c79 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 26 Jul 2023 12:05:38 +0800 Subject: [PATCH 48/53] Small style fix in HelpPopover --- .../layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx index 4c2cab4c241..05f28ff10ef 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/HelpPopover.tsx @@ -64,8 +64,8 @@ const HelpPopover = ({ alt = false }: HelpPopoverProps) => { ) : null} - -
    + +
    Need help with your project?

    For issues with your project hosted on supabase.com, or other inquiries about our hosted From fb4254e0a32f69f610c0d380eb29f2728655b0b7 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 26 Jul 2023 12:34:29 +0800 Subject: [PATCH 49/53] Small fixes --- .../interfaces/Settings/Infrastructure/InfrastructureInfo.tsx | 2 +- studio/components/to-be-cleaned/ProductEmptyState.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/studio/components/interfaces/Settings/Infrastructure/InfrastructureInfo.tsx b/studio/components/interfaces/Settings/Infrastructure/InfrastructureInfo.tsx index 5e81d11cadc..ddd6c3498f2 100644 --- a/studio/components/interfaces/Settings/Infrastructure/InfrastructureInfo.tsx +++ b/studio/components/interfaces/Settings/Infrastructure/InfrastructureInfo.tsx @@ -82,7 +82,7 @@ const InfrastructureInfo = () => { label="Current version" actions={[ isOnLatestVersion && ( - + Latest diff --git a/studio/components/to-be-cleaned/ProductEmptyState.tsx b/studio/components/to-be-cleaned/ProductEmptyState.tsx index be23e0edc04..33ee6b1e1a8 100644 --- a/studio/components/to-be-cleaned/ProductEmptyState.tsx +++ b/studio/components/to-be-cleaned/ProductEmptyState.tsx @@ -39,7 +39,7 @@ const ProductEmptyState: FC = ({

    {ctaButtonLabel && onClickCta && ( - + From f8d05e68271ec07a7c32ee6195a0abc321a55ac1 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 27 Jul 2023 15:40:26 +0800 Subject: [PATCH 50/53] Update studio/.gitignore --- studio/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/studio/.gitignore b/studio/.gitignore index fbcd3d36150..f9df00b939b 100644 --- a/studio/.gitignore +++ b/studio/.gitignore @@ -34,4 +34,3 @@ yarn-error.log* .vercel -/public/dashboard From 589b95c3d74bef1647433cf6f6bd744aeff5b19d Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 27 Jul 2023 15:40:44 +0800 Subject: [PATCH 51/53] Update studio/.gitignore --- studio/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/studio/.gitignore b/studio/.gitignore index f9df00b939b..9dd40bd86d1 100644 --- a/studio/.gitignore +++ b/studio/.gitignore @@ -33,4 +33,3 @@ yarn-error.log* .env.production.local .vercel - From b0c0961d462e117f5c7276c64cd2453832f1460c Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 28 Jul 2023 10:30:40 +0800 Subject: [PATCH 52/53] Use memo for localStorageSlug in useSelectedOrganization --- studio/hooks/misc/useSelectedOrganization.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/studio/hooks/misc/useSelectedOrganization.ts b/studio/hooks/misc/useSelectedOrganization.ts index e10acce43e6..6a0e204b968 100644 --- a/studio/hooks/misc/useSelectedOrganization.ts +++ b/studio/hooks/misc/useSelectedOrganization.ts @@ -9,10 +9,12 @@ export function useSelectedOrganization({ enabled = true } = {}) { const { slug } = useParams() const { data } = useOrganizationsQuery({ enabled }) const selectedProject = useSelectedProject() - const localStorageSlug = - typeof window !== 'undefined' + + const localStorageSlug = useMemo(() => { + return typeof window !== 'undefined' ? localStorage.getItem(LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION) : undefined + }, []) return useMemo(() => { return data?.find((org) => { From bb8fca19b3e511d0edf5b29e9a07cf6701c481b9 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 28 Jul 2023 10:36:47 +0800 Subject: [PATCH 53/53] Change defualt value to null --- studio/hooks/misc/useSelectedOrganization.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/hooks/misc/useSelectedOrganization.ts b/studio/hooks/misc/useSelectedOrganization.ts index 6a0e204b968..bc6be1b3710 100644 --- a/studio/hooks/misc/useSelectedOrganization.ts +++ b/studio/hooks/misc/useSelectedOrganization.ts @@ -13,7 +13,7 @@ export function useSelectedOrganization({ enabled = true } = {}) { const localStorageSlug = useMemo(() => { return typeof window !== 'undefined' ? localStorage.getItem(LOCAL_STORAGE_KEYS.RECENTLY_VISITED_ORGANIZATION) - : undefined + : null }, []) return useMemo(() => {