mirror of
https://github.com/twentyhq/twenty
synced 2026-04-21 13:37:22 +00:00
fix(ui): make CardPicker hover cover the whole card and align content left (#19884)
## Summary Two small visual issues with the shared `CardPicker` (used in the Enterprise plan modal and the onboarding plan picker): - Labels like \`Monthly\` / \`Yearly\` were center-aligned inside their cards while the subtitle (\`\$25 / seat / month\`) stayed left-aligned, because the underlying \`<button>\` element's default \`text-align: center\` was leaking into the children. - The hover background was painted on the same element that owned the inner padding, so the hover surface didn't visually feel like the whole card. This PR: - Moves the content padding into a new \`StyledCardInner\` so the outer \`<button>\` is just the card chrome (border + radius + background + hover). - Adds \`text-align: left\` so titles align with their subtitles. - Hoists \`cursor: pointer\` out of \`:hover\` (it should be on by default for the card). Affects: - \`EnterprisePlanModal\` (Settings → Enterprise) - \`ChooseYourPlanContent\` (onboarding trial picker)
This commit is contained in:
parent
4ecde0e161
commit
13c4a71594
4 changed files with 58 additions and 34 deletions
|
|
@ -9,9 +9,9 @@ export const AgentChatProvider = ({
|
|||
}) => {
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
|
||||
if (!isAiEnabled) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return <AgentChatProviderContent>{children}</AgentChatProviderContent>;
|
||||
return (
|
||||
<AgentChatProviderContent isAiEnabled={isAiEnabled}>
|
||||
{children}
|
||||
</AgentChatProviderContent>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,20 +10,26 @@ import { Suspense } from 'react';
|
|||
|
||||
export const AgentChatProviderContent = ({
|
||||
children,
|
||||
isAiEnabled,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
isAiEnabled: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<AgentChatComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'agentChatComponentInstance' }}
|
||||
>
|
||||
<AgentChatThreadInitializationEffect />
|
||||
<AgentChatMessagesFetchEffect />
|
||||
<AgentChatStreamSubscriptionEffect />
|
||||
<AgentChatStreamingPartsDiffSyncEffect />
|
||||
<AgentChatSessionStartTimeEffect />
|
||||
<AgentChatStreamingAutoScrollEffect />
|
||||
{isAiEnabled && (
|
||||
<>
|
||||
<AgentChatThreadInitializationEffect />
|
||||
<AgentChatMessagesFetchEffect />
|
||||
<AgentChatStreamSubscriptionEffect />
|
||||
<AgentChatStreamingPartsDiffSyncEffect />
|
||||
<AgentChatSessionStartTimeEffect />
|
||||
<AgentChatStreamingAutoScrollEffect />
|
||||
</>
|
||||
)}
|
||||
{children}
|
||||
</AgentChatComponentInstanceContext.Provider>
|
||||
</Suspense>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { SettingsPath } from 'twenty-shared/types';
|
|||
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState';
|
||||
import { AI_ADMIN_PATH } from '@/settings/admin-panel/ai/constants/AiAdminPath';
|
||||
import { useApolloAdminClient } from '@/settings/admin-panel/apollo/hooks/useApolloAdminClient';
|
||||
|
|
@ -64,6 +65,7 @@ export const SettingsAdminWorkspaceDetail = () => {
|
|||
);
|
||||
|
||||
const currentUser = useAtomStateValue(currentUserState);
|
||||
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
|
||||
const canManageFeatureFlags = useAtomStateValue(canManageFeatureFlagsState);
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const { updateFeatureFlagState } = useFeatureFlagState();
|
||||
|
|
@ -262,25 +264,34 @@ export const SettingsAdminWorkspaceDetail = () => {
|
|||
<TableHeader>{t`Feature Flag`}</TableHeader>
|
||||
<TableHeader align="right">{t`Status`}</TableHeader>
|
||||
</TableRow>
|
||||
{workspace.featureFlags?.map((flag) => (
|
||||
<TableRow
|
||||
gridAutoColumns="1fr 100px"
|
||||
mobileGridAutoColumns="1fr 80px"
|
||||
key={flag.key}
|
||||
>
|
||||
<TableCell>{flag.key}</TableCell>
|
||||
<TableCell align="right">
|
||||
{isDefined(flag.key) && (
|
||||
<Toggle
|
||||
value={flag.value}
|
||||
onChange={(newValue) =>
|
||||
handleFeatureFlagUpdate(flag.key!, newValue)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{workspace.featureFlags?.map((flag) => {
|
||||
const currentWorkspaceValue =
|
||||
currentWorkspace?.id === workspaceId
|
||||
? currentWorkspace?.featureFlags?.find(
|
||||
(f) => f.key === flag.key,
|
||||
)?.value
|
||||
: undefined;
|
||||
const displayedValue = currentWorkspaceValue ?? flag.value;
|
||||
return (
|
||||
<TableRow
|
||||
gridAutoColumns="1fr 100px"
|
||||
mobileGridAutoColumns="1fr 80px"
|
||||
key={flag.key}
|
||||
>
|
||||
<TableCell>{flag.key}</TableCell>
|
||||
<TableCell align="right">
|
||||
{isDefined(flag.key) && (
|
||||
<Toggle
|
||||
value={displayedValue}
|
||||
onChange={(newValue) =>
|
||||
handleFeatureFlagUpdate(flag.key!, newValue)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Section>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { styled } from '@linaria/react';
|
||||
import React from 'react';
|
||||
|
||||
import { themeCssVariables } from '@ui/theme-constants';
|
||||
import { Radio } from './Radio';
|
||||
|
|
@ -8,16 +8,23 @@ const StyledSubscriptionCardContainer = styled.button`
|
|||
background-color: ${themeCssVariables.background.secondary};
|
||||
border: 1px solid ${themeCssVariables.border.color.medium};
|
||||
border-radius: ${themeCssVariables.border.radius.md};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: ${themeCssVariables.spacing[4]} ${themeCssVariables.spacing[3]};
|
||||
padding: 0;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
background: ${themeCssVariables.background.tertiary};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledCardInner = styled.div`
|
||||
display: flex;
|
||||
padding: ${themeCssVariables.spacing[4]} ${themeCssVariables.spacing[3]};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledRadioContainer = styled.div`
|
||||
position: absolute;
|
||||
right: ${themeCssVariables.spacing[2]};
|
||||
|
|
@ -40,7 +47,7 @@ export const CardPicker = ({
|
|||
<StyledRadioContainer>
|
||||
<Radio checked={checked} />
|
||||
</StyledRadioContainer>
|
||||
{children}
|
||||
<StyledCardInner>{children}</StyledCardInner>
|
||||
</StyledSubscriptionCardContainer>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue