Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { SettingsAdminConfigVariables } from '@/settings/admin-panel/config-vari
import { SETTINGS_ADMIN_TABS } from '@/settings/admin-panel/constants/SettingsAdminTabs';
import { SETTINGS_ADMIN_TABS_ID } from '@/settings/admin-panel/constants/SettingsAdminTabsId';
import { SettingsAdminHealthStatus } from '@/settings/admin-panel/health-status/components/SettingsAdminHealthStatus';
import { SettingsSkeletonLoader } from '@/settings/components/SettingsSkeletonLoader';
import { SettingsSectionSkeletonLoader } from '@/settings/components/SettingsSectionSkeletonLoader';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { lazy, Suspense } from 'react';
Expand Down Expand Up @@ -34,7 +34,7 @@ export const SettingsAdminTabContent = () => {
return <SettingsAdminHealthStatus />;
case SETTINGS_ADMIN_TABS.ENTERPRISE:
return (
<Suspense fallback={<SettingsSkeletonLoader />}>
<Suspense fallback={<SettingsSectionSkeletonLoader />}>
<SettingsEnterprise isAdminPanelTab />
</Suspense>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const StyledRows = styled.div`
width: 100%;
`;

// Body-only settings loading state: the rows that fill a settings page body
// while its data loads. Render this directly when the settings chrome is
// already on screen (e.g. a tab inside an already-rendered page). For a whole
// page with no chrome yet, use SettingsSkeletonLoader, which wraps this in the
// page chrome.
export const SettingsSectionSkeletonLoader = ({
rowCount = 4,
}: SettingsSectionSkeletonLoaderProps) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,83 @@
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
import { PageBody } from '@/ui/layout/page/components/PageBody';
import { PageHeader } from '@/ui/layout/page/components/PageHeader';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SettingsSectionSkeletonLoader } from '@/settings/components/SettingsSectionSkeletonLoader';
import { SettingsPageHeader } from '@/settings/components/layout/SettingsPageHeader';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';

const StyledContainer = styled.div`
// Full-page settings loading state: the rounded-card chrome (header + body)
Comment thread
FelixMalfait marked this conversation as resolved.
Outdated
// wrapped around the shared SettingsSectionSkeletonLoader.
//
// Use this only when no settings chrome is on screen yet — the route-level
// Suspense fallback, or a page that must load its data (and its data-dependent
// title/breadcrumb) before it can render SettingsPageLayout. When the chrome is
// already rendered above (e.g. a tab inside an already-rendered page), render
// SettingsSectionSkeletonLoader directly so the body matches without nesting a
// second card.
//
// The card is re-created here rather than reusing SettingsPageLayout to keep the
// skeleton free of the layout's page-level side effects (command-menu hotkeys,
// the desktop side panel and the information banner).
const StyledRoot = styled.div<{ isMobile: boolean }>`
display: flex;
flex-direction: column;
width: 100%;
flex: 1;
min-height: 0;
min-width: 0;
padding: ${({ isMobile }) =>
isMobile ? themeCssVariables.spacing[1] : themeCssVariables.spacing[2]};
`;

const StyledTitleLoaderContainer = styled.div`
margin: ${themeCssVariables.spacing[8]} ${themeCssVariables.spacing[8]}
${themeCssVariables.spacing[2]};
const StyledCard = styled.div`
background: ${themeCssVariables.background.primary};
border: 1px solid ${themeCssVariables.border.color.medium};
border-radius: ${themeCssVariables.border.radius.md};
box-sizing: border-box;
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
overflow: hidden;
width: 100%;
`;

export const SettingsSkeletonLoader = () => {
const isMobile = useIsMobile();
const { theme } = useContext(ThemeContext);

return (
<StyledContainer>
<PageHeader
title={
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={4}
>
<Skeleton
height={SKELETON_LOADER_HEIGHT_SIZES.standard.m}
width={120}
/>{' '}
</SkeletonTheme>
}
/>
<PageBody>
<StyledTitleLoaderContainer>
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={4}
>
<Skeleton
height={SKELETON_LOADER_HEIGHT_SIZES.standard.m}
width={200}
/>
</SkeletonTheme>
</StyledTitleLoaderContainer>
</PageBody>
</StyledContainer>
<StyledRoot isMobile={isMobile}>
<StyledCard>
<SkeletonTheme
baseColor={theme.background.tertiary}
highlightColor={theme.background.transparent.lighter}
borderRadius={4}
>
<SettingsPageHeader
links={[
{
children: (
<Skeleton
width={64}
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
/>
),
},
]}
title={
<Skeleton
width={120}
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
/>
}
/>
<SettingsPageContainer>
<SettingsSectionSkeletonLoader />
</SettingsPageContainer>
</SkeletonTheme>
</StyledCard>
</StyledRoot>
);
};
Loading