mirror of
https://github.com/twentyhq/twenty
synced 2026-04-21 13:37:22 +00:00
fix(settings): force display "Standard" and "Custom" for app chips (#19912)
## Summary - Override the `AppChip` label so the Twenty standard application always renders as `Standard` and the workspace custom application always renders as `Custom`, instead of leaking each app's underlying name (e.g. `Twenty Eng's custom application`). - Detection mirrors the logic already used in `useApplicationAvatarColors`, relying on `TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER` / `TWENTY_STANDARD_APPLICATION_NAME` and `currentWorkspace.workspaceCustomApplication.id`. - The `This app` label for the current application context and the original `application.name` fallback for any other installed app are preserved. ## Affected UI - Settings → Data model → Existing objects (App column). - Anywhere else `AppChip` / `useApplicationChipData` is used.
This commit is contained in:
parent
34e3b1b90b
commit
8d24551e71
3 changed files with 70 additions and 63 deletions
|
|
@ -1,64 +1,48 @@
|
|||
import { useApplicationChipData } from '@/applications/hooks/useApplicationChipData';
|
||||
import { styled } from '@linaria/react';
|
||||
import { Avatar } from 'twenty-ui/display';
|
||||
import {
|
||||
Chip,
|
||||
ChipAccent,
|
||||
type ChipSize,
|
||||
ChipVariant,
|
||||
LinkChip,
|
||||
} from 'twenty-ui/components';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
type AppChipProps = {
|
||||
applicationId: string;
|
||||
variant?: ChipVariant;
|
||||
size?: ChipSize;
|
||||
to?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const AppChip = ({
|
||||
applicationId,
|
||||
variant,
|
||||
size,
|
||||
to,
|
||||
className,
|
||||
}: AppChipProps) => {
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
display: inline-flex;
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
font-weight: ${themeCssVariables.font.weight.regular};
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledLabel = styled.span`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export const AppChip = ({ applicationId, className }: AppChipProps) => {
|
||||
const { applicationChipData } = useApplicationChipData({ applicationId });
|
||||
|
||||
const leftComponent = (
|
||||
<Avatar
|
||||
type="app"
|
||||
size="md"
|
||||
placeholder={applicationChipData.name}
|
||||
placeholderColorSeed={applicationChipData.seed}
|
||||
color={applicationChipData.colors?.color}
|
||||
backgroundColor={applicationChipData.colors?.backgroundColor}
|
||||
borderColor={applicationChipData.colors?.borderColor}
|
||||
/>
|
||||
);
|
||||
|
||||
if (to) {
|
||||
return (
|
||||
<LinkChip
|
||||
className={className}
|
||||
label={applicationChipData.name}
|
||||
leftComponent={leftComponent}
|
||||
size={size}
|
||||
variant={variant ?? ChipVariant.Highlighted}
|
||||
accent={ChipAccent.TextPrimary}
|
||||
to={to}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Chip
|
||||
className={className}
|
||||
label={applicationChipData.name}
|
||||
leftComponent={leftComponent}
|
||||
size={size}
|
||||
variant={variant ?? ChipVariant.Transparent}
|
||||
accent={ChipAccent.TextPrimary}
|
||||
/>
|
||||
<StyledContainer className={className}>
|
||||
<Avatar
|
||||
type="app"
|
||||
size="md"
|
||||
placeholder={applicationChipData.name}
|
||||
placeholderColorSeed={applicationChipData.seed}
|
||||
color={applicationChipData.colors?.color}
|
||||
backgroundColor={applicationChipData.colors?.backgroundColor}
|
||||
borderColor={applicationChipData.colors?.borderColor}
|
||||
/>
|
||||
<StyledLabel title={applicationChipData.name}>
|
||||
{applicationChipData.name}
|
||||
</StyledLabel>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useContext } from 'react';
|
||||
import {
|
||||
TWENTY_STANDARD_APPLICATION_NAME,
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
} from 'twenty-shared/application';
|
||||
import { TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER } from 'twenty-shared/application';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ThemeContext } from 'twenty-ui/theme-constants';
|
||||
|
||||
|
|
@ -20,6 +17,18 @@ type UseApplicationAvatarColorsArgs = {
|
|||
universalIdentifier?: string | null;
|
||||
};
|
||||
|
||||
const STANDARD_APPLICATION_AVATAR_COLORS: ApplicationAvatarColors = {
|
||||
// The standard application avatar follows the Figma `Colors/Blue` palette,
|
||||
// which is Radix's pure blue and not Twenty's `theme.color.blue*` tokens
|
||||
// (those map to the indigo palette).
|
||||
// oxlint-disable-next-line twenty/no-hardcoded-colors
|
||||
backgroundColor: '#CEE7FE',
|
||||
// oxlint-disable-next-line twenty/no-hardcoded-colors
|
||||
borderColor: '#B7D9F8',
|
||||
// oxlint-disable-next-line twenty/no-hardcoded-colors
|
||||
color: '#113264',
|
||||
};
|
||||
|
||||
export const useApplicationAvatarColors = (
|
||||
application: UseApplicationAvatarColorsArgs | null | undefined,
|
||||
): ApplicationAvatarColors | undefined => {
|
||||
|
|
@ -31,20 +40,16 @@ export const useApplicationAvatarColors = (
|
|||
}
|
||||
|
||||
const isStandard =
|
||||
isDefined(application.universalIdentifier) &&
|
||||
application.universalIdentifier ===
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER ||
|
||||
application.name === TWENTY_STANDARD_APPLICATION_NAME;
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER;
|
||||
|
||||
const isCustom =
|
||||
isDefined(currentWorkspace?.workspaceCustomApplication?.id) &&
|
||||
currentWorkspace.workspaceCustomApplication.id === application.id;
|
||||
|
||||
if (isStandard) {
|
||||
return {
|
||||
backgroundColor: theme.color.blue5,
|
||||
borderColor: theme.color.blue6,
|
||||
color: theme.color.blue12,
|
||||
};
|
||||
return STANDARD_APPLICATION_AVATAR_COLORS;
|
||||
}
|
||||
|
||||
if (isCustom) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
|||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext } from 'react';
|
||||
import { TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER } from 'twenty-shared/application';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type UseApplicationChipDataArgs = {
|
||||
|
|
@ -47,9 +48,26 @@ export const useApplicationChipData = ({
|
|||
const isCurrent =
|
||||
isDefined(currentApplicationId) && currentApplicationId === applicationId;
|
||||
|
||||
const isStandard =
|
||||
isDefined(application.universalIdentifier) &&
|
||||
application.universalIdentifier ===
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER;
|
||||
|
||||
const isCustom =
|
||||
isDefined(currentWorkspace?.workspaceCustomApplication?.id) &&
|
||||
currentWorkspace.workspaceCustomApplication.id === application.id;
|
||||
|
||||
const displayName = isCurrent
|
||||
? t`This app`
|
||||
: isStandard
|
||||
? t`Standard`
|
||||
: isCustom
|
||||
? t`Custom`
|
||||
: application.name;
|
||||
|
||||
return {
|
||||
applicationChipData: {
|
||||
name: isCurrent ? t`This app` : application.name,
|
||||
name: displayName,
|
||||
seed: application.universalIdentifier ?? application.name,
|
||||
colors,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue