mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Scope last visited organization local storage to profile (#47071)
## Context If a user switches account without an explicit log out via the dashboard, landing back on `/org` will redirect users to the last visited organization as stored in local storage, in which it can result in the following state if the last visited organization does not exist in the current account <img width="2538" height="1060" alt="image" src="https://github.com/user-attachments/assets/270e482a-3515-48ef-898b-87e76fce80d6" /> ## Changes involved Am opting to scope the last visited organization to the user profile instead - this would be a bit more cleaner than trying to actively clear the last visited org slug from local storage with implicit account changes as there's no deterministic way to track that (afaik) from FE side of things ## To test Can reproduce the problem as such - Ensure that you have 2 accounts to log in with, and one account has an org that the other is not a part of - For the organization that has the "extra" org, ensure that you click into it so that the last visited org slug is saved in local storage - Mimic changing accounts by visiting `/auth/v1/authorize?provider=github` (using the domain for the env that you're testing on - e.g localhost:8000 for local, or green for staging preview) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Unified “last visited organization” handling across the Studio UI with a shared hook, improving consistency for home/dashboard return, cancel/back navigation, and account routing. * **Bug Fixes** * Updated redirects to only route to an organization when a valid last-visited value is available; otherwise users go to the general organizations page. * Kept MFA enrollment and factor delete/leave flows aligned to the unified last-visited organization value. * **Tests** * Updated onboarding and layout tests to match the new last-visited organization storage key format and hook/query success behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
acbec2949d
commit
fd0f2dd459
20 files changed
+73
-108
No files matched your search
@@ -1,6 +1,5 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useForm, type SubmitHandler } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
@@ -16,7 +15,7 @@ import { organizationKeys } from '@/data/organizations/keys'
|
||||
import { useMfaChallengeAndVerifyMutation } from '@/data/profile/mfa-challenge-and-verify-mutation'
|
||||
import { useMfaEnrollMutation } from '@/data/profile/mfa-enroll-mutation'
|
||||
import { useMfaUnenrollMutation } from '@/data/profile/mfa-unenroll-mutation'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
|
||||
type TOTP = { qr_code: string; secret: string; uri: string }
|
||||
|
||||
@@ -140,10 +139,7 @@ const SecondStep = ({
|
||||
onClose,
|
||||
}: SecondStepProps) => {
|
||||
const queryClient = useQueryClient()
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const FormSchema = z.object({
|
||||
code: z.string().min(1, 'Please provide a code from your authenticator app'),
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { toast } from 'sonner'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
|
||||
import { organizationKeys } from '@/data/organizations/keys'
|
||||
import { useMfaUnenrollMutation } from '@/data/profile/mfa-unenroll-mutation'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
|
||||
interface DeleteFactorModalProps {
|
||||
visible: boolean
|
||||
@@ -21,10 +20,7 @@ const DeleteFactorModal = ({
|
||||
onClose,
|
||||
}: DeleteFactorModalProps) => {
|
||||
const queryClient = useQueryClient()
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const { mutate: unenroll, isPending } = useMfaUnenrollMutation({
|
||||
onSuccess: async () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LOCAL_STORAGE_KEYS, useIsLoggedIn, useIsMFAEnabled, useParams } from 'common'
|
||||
import { useIsLoggedIn, useIsMFAEnabled, useParams } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { PropsWithChildren, useEffect } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
@@ -6,8 +6,8 @@ import { toast } from 'sonner'
|
||||
import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
|
||||
import { useProjectDetailQuery } from '@/data/projects/project-detail-query'
|
||||
import { useDashboardHistory } from '@/hooks/misc/useDashboardHistory'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import useLatest from '@/hooks/misc/useLatest'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
import { IS_PLATFORM } from '@/lib/constants'
|
||||
|
||||
@@ -21,10 +21,7 @@ export const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => {
|
||||
const isUserMFAEnabled = useIsMFAEnabled()
|
||||
|
||||
const { setLastVisitedSnippet, setLastVisitedTable } = useDashboardHistory()
|
||||
const [lastVisitedOrganization, setLastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization, setLastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const DEFAULT_HOME = IS_PLATFORM
|
||||
? !!lastVisitedOrganization
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { Check, ChevronDown } from 'lucide-react'
|
||||
import { useMemo, useState, type ReactNode } from 'react'
|
||||
import { cn, Collapsible, CollapsibleContent, CollapsibleTrigger } from 'ui'
|
||||
@@ -7,7 +6,7 @@ import {
|
||||
CreateOrganizationCard,
|
||||
OrganizationCard,
|
||||
} from '@/components/interfaces/Organization/OrganizationCard'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import type { Organization } from '@/types'
|
||||
|
||||
const VISIBLE_ORGANIZATIONS_LIMIT = 3
|
||||
@@ -46,10 +45,7 @@ export const OrganizationSelector = ({
|
||||
unavailableReason?: ReactNode
|
||||
}) => {
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const { visibleOrganizations, overflowOrganizations } = useMemo(() => {
|
||||
const lastVisitedOrg = organizations.find(({ slug }) => slug === lastVisitedOrganization)
|
||||
|
||||
+2
-6
@@ -1,5 +1,4 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
@@ -11,7 +10,7 @@ import { TextConfirmModal } from '@/components/ui/TextConfirmModalWrapper'
|
||||
import { useOrganizationDeleteMutation } from '@/data/organizations/organization-delete-mutation'
|
||||
import { useOrgProjectsInfiniteQuery } from '@/data/projects/org-projects-infinite-query'
|
||||
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
|
||||
const MAX_PROJECT_ACKNOWLEDGEMENTS = 10
|
||||
@@ -90,10 +89,7 @@ export const DeleteOrganizationButton = () => {
|
||||
|
||||
const allChecked = isDeletionConfirmed()
|
||||
|
||||
const [_, setLastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { setLastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const { can: canDeleteOrganization } = useAsyncCheckPermissions(
|
||||
PermissionAction.UPDATE,
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Elements } from '@stripe/react-stripe-js'
|
||||
import type { PaymentIntentResult, PaymentMethod, StripeElementsOptions } from '@stripe/stripe-js'
|
||||
import { loadStripe } from '@stripe/stripe-js'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { groupBy } from 'lodash'
|
||||
import { HelpCircle } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
@@ -55,7 +54,7 @@ import { useProjectsInfiniteQuery } from '@/data/projects/projects-infinite-quer
|
||||
import { SetupIntentResponse } from '@/data/stripe/setup-intent-mutation'
|
||||
import { useConfirmPendingSubscriptionCreateMutation } from '@/data/subscriptions/org-subscription-confirm-pending-create'
|
||||
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { PRICING_TIER_LABELS_ORG, STRIPE_PUBLIC_KEY } from '@/lib/constants'
|
||||
import { validateReturnTo } from '@/lib/gotrue'
|
||||
import { useProfile } from '@/lib/profile'
|
||||
@@ -94,6 +93,7 @@ export const NewOrgForm = ({
|
||||
const router = useRouter()
|
||||
const user = useProfile()
|
||||
const { resolvedTheme } = useTheme()
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const isBillingEnabled = useIsFeatureEnabled('billing:all')
|
||||
|
||||
@@ -101,11 +101,6 @@ export const NewOrgForm = ({
|
||||
const { data } = useProjectsInfiniteQuery({})
|
||||
const projects = useMemo(() => data?.pages.flatMap((page) => page.projects) ?? [], [data?.pages])
|
||||
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
|
||||
const freeOrgs = (organizations || []).filter((it) => it.plan.id === 'free')
|
||||
|
||||
// [Joshen] JFYI because we're now using a paginated endpoint, there's a chance that not all projects will be
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
|
||||
import { useParams } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
@@ -11,7 +11,7 @@ import { useOrganizationMemberDeleteMutation } from '@/data/organizations/organi
|
||||
import { useOrganizationMembersQuery } from '@/data/organizations/organization-members-query'
|
||||
import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
|
||||
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
import { useProfile } from '@/lib/profile'
|
||||
|
||||
@@ -20,6 +20,7 @@ export const LeaveTeamButton = () => {
|
||||
const { slug } = useParams()
|
||||
const { profile } = useProfile()
|
||||
const { data: selectedOrganization } = useSelectedOrganizationQuery()
|
||||
const { setLastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
// if organizationMembersDeletionEnabled is false, you also can't delete yourself
|
||||
const { organizationMembersDelete: organizationMembersDeletionEnabled } = useIsFeatureEnabled([
|
||||
@@ -28,10 +29,6 @@ export const LeaveTeamButton = () => {
|
||||
|
||||
const [isLeaving, setIsLeaving] = useState(false)
|
||||
const [isLeaveTeamModalOpen, setIsLeaveTeamModalOpen] = useState(false)
|
||||
const [_, setLastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
|
||||
const { refetch: refetchOrganizations } = useOrganizationsQuery()
|
||||
const { data: members } = useOrganizationMembersQuery({ slug })
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LOCAL_STORAGE_KEYS, useFlag } from 'common'
|
||||
import { useFlag } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { UseFormReturn } from 'react-hook-form'
|
||||
import {
|
||||
@@ -18,7 +18,7 @@ import { CreateProjectForm } from './ProjectCreation.schema'
|
||||
import { instanceLabel, monthlyInstancePrice } from './ProjectCreation.utils'
|
||||
import { InlineLink } from '@/components/ui/InlineLink'
|
||||
import { OrgProject } from '@/data/projects/org-projects-infinite-query'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
import { DOCS_URL } from '@/lib/constants'
|
||||
|
||||
@@ -42,14 +42,10 @@ export const ProjectCreationFooter = ({
|
||||
const router = useRouter()
|
||||
const { data: currentOrg } = useSelectedOrganizationQuery()
|
||||
const isFreePlan = currentOrg?.plan?.id === 'free'
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const projectCreationDisabled = useFlag('disableProjectCreationAndUpdate')
|
||||
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
|
||||
const availableComputeCredits = organizationProjects.length === 0 ? 10 : 0
|
||||
const additionalMonthlySpend = isFreePlan
|
||||
? 0
|
||||
|
||||
+2
-6
@@ -1,4 +1,3 @@
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
@@ -12,7 +11,7 @@ import { useSendDowngradeFeedbackMutation } from '@/data/feedback/exit-survey-se
|
||||
import type { OrgProject } from '@/data/projects/org-projects-infinite-query'
|
||||
import { useProjectDeleteMutation } from '@/data/projects/project-delete-mutation'
|
||||
import { useOrgSubscriptionQuery } from '@/data/subscriptions/org-subscription-query'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
||||
import type { Organization } from '@/types'
|
||||
@@ -36,10 +35,7 @@ export const DeleteProjectModal = ({
|
||||
const project = projectProp || projectFromQuery
|
||||
const organization = organizationProp || organizationFromQuery
|
||||
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const projectRef = project?.ref
|
||||
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })
|
||||
|
||||
@@ -55,7 +55,7 @@ vi.mock('@/hooks/misc/useIsFeatureEnabled', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/misc/useLocalStorage', () => ({
|
||||
useLocalStorageQuery: () => [''],
|
||||
useLocalStorageQuery: () => ['', vi.fn(), { isLoading: false, isSuccess: true }],
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/misc/withAuth', () => ({
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import Head from 'next/head'
|
||||
import { useRouter } from 'next/router'
|
||||
import type { PropsWithChildren } from 'react'
|
||||
@@ -10,7 +9,7 @@ import { AccountMenuContent } from './AccountMenuContent'
|
||||
import { WithSidebar } from './WithSidebar'
|
||||
import { useCustomContent } from '@/hooks/custom-content/useCustomContent'
|
||||
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { withAuth } from '@/hooks/misc/withAuth'
|
||||
import { IS_PLATFORM } from '@/lib/constants'
|
||||
import { buildStudioPageTitle } from '@/lib/page-title'
|
||||
@@ -23,6 +22,7 @@ export interface AccountLayoutProps {
|
||||
const AccountLayout = ({ children, title }: PropsWithChildren<AccountLayoutProps>) => {
|
||||
const router = useRouter()
|
||||
const appSnap = useAppStateSnapshot()
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
const { setContent: setMobileSheetContent, registerOpenMenu } = useMobileSheet()
|
||||
const currentPath = router.pathname
|
||||
|
||||
@@ -32,11 +32,6 @@ const AccountLayout = ({ children, title }: PropsWithChildren<AccountLayoutProps
|
||||
const brandTitle = appTitle || 'Supabase'
|
||||
const surfaceLabel = IS_PLATFORM ? 'Account' : 'Preferences'
|
||||
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
|
||||
const backToDashboardURL =
|
||||
appSnap.lastRouteBeforeVisitingAccountPage.length > 0
|
||||
? appSnap.lastRouteBeforeVisitingAccountPage
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LOCAL_STORAGE_KEYS, useBreakpoint, useParams } from 'common'
|
||||
import { useBreakpoint, useParams } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { PropsWithChildren, useEffect, useState } from 'react'
|
||||
import { ResizablePanel, ResizablePanelGroup, SidebarProvider } from 'ui'
|
||||
@@ -13,7 +13,7 @@ import { LayoutSidebarProvider } from './ProjectLayout/LayoutSidebar/LayoutSideb
|
||||
import { ProjectContextProvider } from './ProjectLayout/ProjectContext'
|
||||
import { AppBannerWrapper } from '@/components/interfaces/App/AppBannerWrapper'
|
||||
import { Sidebar } from '@/components/interfaces/Sidebar'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { useCheckLatestDeploy } from '@/hooks/use-check-latest-deploy'
|
||||
import { IS_PLATFORM } from '@/lib/constants'
|
||||
import { useAppStateSnapshot } from '@/state/app-state'
|
||||
@@ -42,10 +42,7 @@ export const DefaultLayout = ({
|
||||
const router = useRouter()
|
||||
const appSnap = useAppStateSnapshot()
|
||||
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const backToDashboardURL = router.pathname.startsWith('/account')
|
||||
? appSnap.lastRouteBeforeVisitingAccountPage.length > 0
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { cn } from 'ui'
|
||||
|
||||
import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
|
||||
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
import { IS_PLATFORM } from '@/lib/constants'
|
||||
import { useTrack } from '@/lib/telemetry/track'
|
||||
@@ -18,10 +17,7 @@ export const HomeIcon = ({ className }: { className?: string }) => {
|
||||
const largeLogo = useIsFeatureEnabled('branding:large_logo')
|
||||
|
||||
const router = useRouter()
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const getDefaultOrgRedirect = () => {
|
||||
if (lastVisitedOrganization) return `/org/${lastVisitedOrganization}`
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
|
||||
import { useLocalStorageQuery } from './useLocalStorage'
|
||||
import { useProfile } from '@/lib/profile'
|
||||
|
||||
export const useLastVisitedOrganization = () => {
|
||||
const { profile, isLoading: isLoadingProfile, isSuccess: isSuccessProfile } = useProfile()
|
||||
const [
|
||||
lastVisitedOrganization,
|
||||
setLastVisitedOrganization,
|
||||
{ isLoading: isLoadingLocalStorage, isSuccess: isSuccessLocalStorage },
|
||||
] = useLocalStorageQuery(LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION(profile?.id), '')
|
||||
|
||||
const isSuccess = isSuccessProfile && isSuccessLocalStorage
|
||||
const isPending = isLoadingProfile || isLoadingLocalStorage
|
||||
|
||||
return {
|
||||
isSuccess,
|
||||
isPending,
|
||||
lastVisitedOrganization: isSuccess ? lastVisitedOrganization : undefined,
|
||||
setLastVisitedOrganization,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { NextPage } from 'next'
|
||||
import { useTheme } from 'next-themes'
|
||||
import Image from 'next/legacy/image'
|
||||
@@ -7,7 +6,7 @@ import { useRouter } from 'next/router'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { SupportLink } from '@/components/interfaces/Support/SupportLink'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { useSignOut } from '@/lib/auth'
|
||||
|
||||
const Error500: NextPage = () => {
|
||||
@@ -15,10 +14,7 @@ const Error500: NextPage = () => {
|
||||
const signOut = useSignOut()
|
||||
const { resolvedTheme } = useTheme()
|
||||
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const onClickLogout = async () => {
|
||||
await signOut()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { LOCAL_STORAGE_KEYS, useFlag, useParams } from 'common'
|
||||
import { useFlag, useParams } from 'common'
|
||||
import Head from 'next/head'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -64,7 +64,7 @@ import {
|
||||
useDataApiRevokeOnCreateDefaultEnabled,
|
||||
} from '@/hooks/misc/useDataApiRevokeOnCreateDefault'
|
||||
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
||||
import { withAuth } from '@/hooks/misc/withAuth'
|
||||
import { usePHFlag } from '@/hooks/ui/useFlag'
|
||||
@@ -92,10 +92,7 @@ const Wizard: NextPageWithLayout = () => {
|
||||
const isFreePlan = currentOrg?.plan?.id === 'free'
|
||||
const canChooseInstanceSize = !isFreePlan
|
||||
|
||||
const [lastVisitedOrganization] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
const { can: isAdmin } = useAsyncCheckPermissions(PermissionAction.CREATE, 'projects')
|
||||
const { can: canCreateGitHubConnection } = useAsyncCheckPermissions(
|
||||
PermissionAction.CREATE,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
@@ -6,20 +5,20 @@ import { ShimmeringCard } from '@/components/interfaces/Home/ProjectList/Shimmer
|
||||
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
|
||||
import OrganizationLayout from '@/components/layouts/OrganizationLayout'
|
||||
import { ScaffoldContainerLegacy } from '@/components/layouts/Scaffold'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
const OrgIndexPage: NextPageWithLayout = () => {
|
||||
const router = useRouter()
|
||||
const [lastVisitedOrganization, _, { isSuccess }] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization, isSuccess } = useLastVisitedOrganization()
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) {
|
||||
if (lastVisitedOrganization.length > 0) router.push(`/org/${lastVisitedOrganization}`)
|
||||
else router.push('/organizations')
|
||||
if (lastVisitedOrganization) {
|
||||
router.push(`/org/${lastVisitedOrganization}`)
|
||||
} else {
|
||||
router.push('/organizations')
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSuccess])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IS_PLATFORM, LOCAL_STORAGE_KEYS, useParams } from 'common'
|
||||
import { IS_PLATFORM, useParams } from 'common'
|
||||
import { AlertTriangleIcon } from 'lucide-react'
|
||||
import { NextPage } from 'next'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -25,7 +25,7 @@ import { HomePageActions } from '@/components/interfaces/HomePageActions'
|
||||
import { PageLayout } from '@/components/layouts/PageLayout/PageLayout'
|
||||
import { ScaffoldContainer, ScaffoldSection } from '@/components/layouts/Scaffold'
|
||||
import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useLastVisitedOrganization } from '@/hooks/misc/useLastVisitedOrganization'
|
||||
import { withAuth } from '@/hooks/misc/withAuth'
|
||||
|
||||
// [Joshen] I'd say we don't do route validation here, this page will act more
|
||||
@@ -37,10 +37,7 @@ const GenericProjectPage: NextPage = () => {
|
||||
const { slug } = useParams()
|
||||
const { routeSlug, ...queryParams } = router.query
|
||||
|
||||
const [lastVisitedOrgSlug] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
''
|
||||
)
|
||||
const { lastVisitedOrganization } = useLastVisitedOrganization()
|
||||
|
||||
const {
|
||||
data: organizations = [],
|
||||
@@ -52,7 +49,7 @@ const GenericProjectPage: NextPage = () => {
|
||||
})
|
||||
|
||||
const [selectedSlug, setSlug] = useState<string>(
|
||||
slug || lastVisitedOrgSlug || organizations[0]?.slug
|
||||
slug || lastVisitedOrganization || organizations[0]?.slug
|
||||
)
|
||||
const selectedOrganization = organizations.find((x) => x.slug === selectedSlug)
|
||||
|
||||
@@ -74,13 +71,13 @@ const GenericProjectPage: NextPage = () => {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!!lastVisitedOrgSlug) {
|
||||
setSlug(lastVisitedOrgSlug)
|
||||
if (!!lastVisitedOrganization) {
|
||||
setSlug(lastVisitedOrganization)
|
||||
} else if (isSuccessOrganizations) {
|
||||
setSlug(organizations[0]?.slug)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [lastVisitedOrgSlug, isSuccessOrganizations])
|
||||
}, [lastVisitedOrganization, isSuccessOrganizations])
|
||||
|
||||
return (
|
||||
<div className="h-screen flex flex-col">
|
||||
|
||||
@@ -273,7 +273,7 @@ describe('AwsMarketplaceOnboardingScreen', () => {
|
||||
]
|
||||
|
||||
window.localStorage.setItem(
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
|
||||
LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION(DEFAULT_PROFILE_CONTEXT.profile?.id),
|
||||
JSON.stringify('zeta-team')
|
||||
)
|
||||
mockAwsEndpoints({
|
||||
|
||||
@@ -87,7 +87,7 @@ export const LOCAL_STORAGE_KEYS = {
|
||||
// Shortcut preferences
|
||||
SHORTCUT_STORAGE_KEY: 'supabase-shortcut-preferences',
|
||||
|
||||
LAST_VISITED_ORGANIZATION: 'last-visited-organization',
|
||||
LAST_VISITED_ORGANIZATION: (uid?: number) => `last-visited-organization-${uid}`,
|
||||
|
||||
// user impersonation selector previous searches
|
||||
USER_IMPERSONATION_SELECTOR_PREVIOUS_SEARCHES: (ref: string) =>
|
||||
|
||||
Reference in new issue
Block a user