Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/app/guards/TenantGuard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useUserInfoSummaryStore } from 'src/context/UserInfoSummary/useUserInfo
import useGlobalSearchParams, {
GlobalSearchParams,
} from 'src/hooks/searchParams/useGlobalSearchParams';
import { useInitializeSelectedTenant } from 'src/hooks/useInitializeSelectedTenant';

// This is a way to very simply "hide" the flow where anyone
// can create a tenant but allow us to test it out in prod.
Expand All @@ -27,6 +28,8 @@ function TenantGuard({ children }: BaseComponentProps) {
const mutate = useUserInfoSummaryStore((state) => state.mutate);
const usedSSO = useUserStore((state) => state.userDetails?.usedSSO);

useInitializeSelectedTenant();

const showOnboarding = !hasAnyAccess || showBeta;
if (showOnboarding) {
if (usedSSO && !showBeta) {
Expand Down
16 changes: 0 additions & 16 deletions src/components/admin/Billing/TenantOptions.tsx

This file was deleted.

11 changes: 3 additions & 8 deletions src/components/admin/Billing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import BillingLoadError from 'src/components/admin/Billing/LoadError';
import PaymentMethods from 'src/components/admin/Billing/PaymentMethods';
import PricingTierDetails from 'src/components/admin/Billing/PricingTierDetails';
import { INVOICE_ROW_HEIGHT } from 'src/components/admin/Billing/shared';
import TenantOptions from 'src/components/admin/Billing/TenantOptions';
import { useTenantChangeReset } from 'src/components/admin/Billing/useTenantChangeReset';
import AdminTabs from 'src/components/admin/Tabs';
import GraphLoadingState from 'src/components/graphs/states/Loading';
import GraphStateWrapper from 'src/components/graphs/states/Wrapper';
Expand Down Expand Up @@ -50,6 +50,8 @@ function AdminBilling({ showAddPayment }: AdminBillingProps) {
headerLink: 'https://www.estuary.dev/pricing/',
});

useTenantChangeReset();

const intl = useIntl();

const selectedTenant = useTenantStore((state) => state.selectedTenant);
Expand Down Expand Up @@ -138,13 +140,6 @@ function AdminBilling({ showAddPayment }: AdminBillingProps) {

<PricingTierDetails />
</Grid>

<Grid
size={{ xs: 12, md: 3 }}
sx={{ display: 'flex', alignItems: 'end' }}
>
<TenantOptions />
</Grid>
</Grid>

<Grid container spacing={{ xs: 3, md: 2 }} sx={{ p: 2 }}>
Expand Down
22 changes: 22 additions & 0 deletions src/components/admin/Billing/useTenantChangeReset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useCallback, useEffect, useRef } from 'react';

import { useBillingStore } from 'src/stores/Billing';
import { useTenantStore } from 'src/stores/Tenant';

export function useTenantChangeReset() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hook is transitional - it extracts the billing reset logic from the deleted TenantOptions component.

A follow-up PR will key billing data on the selected tenant making this reset unnecessary.

const selectedTenant = useTenantStore((state) => state.selectedTenant);

const resetBillingState = useBillingStore((state) => state.resetState);

const resetStores = useCallback(() => {
resetBillingState();
}, [resetBillingState]);

const previousTenant = useRef(selectedTenant);
useEffect(() => {
if (previousTenant.current !== selectedTenant) {
previousTenant.current = selectedTenant;
resetStores();
}
}, [selectedTenant, resetStores]);
}
16 changes: 1 addition & 15 deletions src/components/admin/Settings/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { Divider, Grid, Stack } from '@mui/material';
import { Divider, Stack } from '@mui/material';

import { authenticatedRoutes } from 'src/app/routes';
import DataPlanes from 'src/components/admin/Settings/DataPlanes';
import PrefixAlerts from 'src/components/admin/Settings/PrefixAlerts';
import { StorageMappings } from 'src/components/admin/Settings/StorageMappings';
import AdminTabs from 'src/components/admin/Tabs';
import TenantSelector from 'src/components/shared/TenantSelector';
import usePageTitle from 'src/hooks/usePageTitle';

function Settings() {
Expand All @@ -17,19 +16,6 @@ function Settings() {
<>
<AdminTabs />

<Grid
container
spacing={{ xs: 3, md: 2 }}
sx={{ p: 2, justifyContent: 'flex-end' }}
>
<Grid
size={{ xs: 12, md: 3 }}
sx={{ mt: 2.5, display: 'flex', alignItems: 'end' }}
>
<TenantSelector />
</Grid>
</Grid>

<PrefixAlerts />

<Stack>
Expand Down
24 changes: 23 additions & 1 deletion src/components/fullPage/Error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import type { ErrorDetails } from 'src/components/shared/Error/types';

import { useMemo } from 'react';

import { Divider, Stack, Typography } from '@mui/material';
import { Button, Divider, Stack, Typography } from '@mui/material';

import { FormattedMessage } from 'react-intl';
import { useMount } from 'react-use';

import FullPageWrapper from 'src/app/FullPageWrapper';
import MessageWithLink from 'src/components/content/MessageWithLink';
import Error from 'src/components/shared/Error';
import { supabaseClient } from 'src/context/GlobalProviders';
import { logRocketEvent } from 'src/services/shared';
import { CustomEvents } from 'src/services/types';

Expand Down Expand Up @@ -56,6 +57,27 @@ function FullPageError({
<Typography variant="subtitle1">
<MessageWithLink messageID="fullPage.instructions" />
</Typography>

<Stack direction="row" spacing={1}>
<Button
variant="contained"
onClick={() => {
window.location.reload();
}}
>
<FormattedMessage id="cta.reload" />
</Button>

<Button
variant="outlined"
onClick={() => {
void supabaseClient.auth.signOut();
}}
>
<FormattedMessage id="cta.logout" />
</Button>
</Stack>
Comment on lines +61 to +79

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user menu (and logout menu item) moved to the sidenav, which the FullPageError omits, so putting a a logout button directly in the error dialog


<Divider />
<Error error={error} condensed />
</Stack>
Expand Down
12 changes: 1 addition & 11 deletions src/components/home/dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
import { Box, Grid } from '@mui/material';
import { Grid } from '@mui/material';

import EntityStatOverview from 'src/components/home/dashboard/EntityStatOverview';
import TenantSelector from 'src/components/shared/TenantSelector';

export default function Dashboard() {
return (
<Grid container spacing={{ xs: 2 }} style={{ marginTop: 16 }}>
<Grid
size={{ xs: 12 }}
sx={{ display: 'flex', justifyContent: 'flex-end' }}
>
<Box style={{ width: 300 }}>
<TenantSelector />
</Box>
</Grid>

<EntityStatOverview />
</Grid>
);
Expand Down
31 changes: 20 additions & 11 deletions src/components/menus/HelpMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
import { HelpCircle } from 'iconoir-react';
import { Menu } from '@mui/material';

import { FormattedMessage, useIntl } from 'react-intl';

import IconMenu from 'src/components/menus/IconMenu';
import {
sideNavMenuAnchorOrigin,
sideNavMenuTransformOrigin,
} from 'src/components/menus/shared';
import ExternalLinkMenuItem from 'src/components/shared/ExternalLinkMenuItem';

function HelpMenu() {
interface HelpMenuProps {
anchorEl: HTMLElement | null;
onClose: () => void;
}

export function HelpMenu({ anchorEl, onClose }: HelpMenuProps) {
const intl = useIntl();

return (
<IconMenu
ariaLabel={intl.formatMessage({ id: 'helpMenu.ariaLabel' })}
icon={<HelpCircle />}
identifier="help-menu"
tooltip={intl.formatMessage({ id: 'helpMenu.tooltip' })}
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={onClose}
onClick={onClose}
anchorOrigin={sideNavMenuAnchorOrigin}
transformOrigin={sideNavMenuTransformOrigin}
>
<ExternalLinkMenuItem
link={intl.formatMessage({ id: 'helpMenu.docs.link' })}
Expand All @@ -39,8 +50,6 @@ function HelpMenu() {
>
<FormattedMessage id="helpMenu.contact" />
</ExternalLinkMenuItem>
</IconMenu>
</Menu>
);
}

export default HelpMenu;
132 changes: 132 additions & 0 deletions src/components/menus/OrgMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import {
Dialog,
DialogContent,
DialogTitle,
MenuItem,
Popover,
Typography,
} from '@mui/material';

import { Check } from 'iconoir-react';
import { FormattedMessage, useIntl } from 'react-intl';

import PrefixSelector from 'src/components/inputs/PrefixedName/PrefixSelector';
import {
sideNavMenuAnchorOrigin,
sideNavMenuTransformOrigin,
} from 'src/components/menus/shared';
import { useUserInfoSummaryStore } from 'src/context/UserInfoSummary/useUserInfoSummaryStore';
import { useEntitiesStore_tenantsWithAdmin } from 'src/stores/Entities/hooks';
import { useTenantStore } from 'src/stores/Tenant';

interface OrgMenuProps {
anchorEl: HTMLElement | null;
onClose: () => void;
}

export const OrgMenu = ({ anchorEl, onClose }: OrgMenuProps) => {
const intl = useIntl();
const selectedTenant = useTenantStore((state) => state.selectedTenant);
const setSelectedTenant = useTenantStore(
(state) => state.setSelectedTenant
);
// Same tenant list the old TenantSelector showed everyone (incl. estuary_support).
const tenantNames = useEntitiesStore_tenantsWithAdmin();
const hasSupportAccess = useUserInfoSummaryStore(
(state) => state.hasSupportAccess
);

// Support users get the searchable dialog (every prefix); everyone else
// gets the popover list anchored to the trigger.
if (hasSupportAccess) {
return (
<Dialog
open={Boolean(anchorEl)}
onClose={onClose}
fullWidth
maxWidth="xs"
>
<DialogTitle>
<FormattedMessage id="tenant.organization" />
</DialogTitle>
<DialogContent>
<PrefixSelector
disabled={false}
error={false}
label={intl.formatMessage({
id: 'common.tenant',
})}
labelId="org-switcher"
onChange={(newValue) => {
setSelectedTenant(newValue);
onClose();
}}
options={tenantNames}
value={selectedTenant}
variantString="outlined"
/>
</DialogContent>
</Dialog>
);
}

return (
<Popover
open={Boolean(anchorEl)}
anchorEl={anchorEl}
onClose={onClose}
anchorOrigin={sideNavMenuAnchorOrigin}
transformOrigin={sideNavMenuTransformOrigin}
slotProps={{
paper: {
sx: {
width: 240,
p: 1,
borderRadius: 2,
},
},
}}
>
<Typography
sx={{
px: 1,
pt: 0.5,
pb: 1,
fontSize: 11,
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: 0.5,
color: 'text.secondary',
}}
>
<FormattedMessage id="tenant.organization" />
</Typography>

{tenantNames.map((tenant) => {
const label = tenant.replace(/\/$/, '');
const isSelected = tenant === selectedTenant;

return (
<MenuItem
key={tenant}
selected={isSelected}
onClick={() => {
setSelectedTenant(tenant);
onClose();
}}
sx={{
borderRadius: 1,
fontSize: 13,
py: 0.75,
justifyContent: 'space-between',
}}
>
{label}

{isSelected ? <Check /> : null}
</MenuItem>
);
})}
</Popover>
);
};
Loading
Loading