Files
twenty/packages/twenty-front/src/modules/ui/field/display/components/PhonesDisplay.tsx
Paul Rastoin 4a4e65fe4a [REFACTOR] Twenty UI multi barrel (#11301)
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/591
Same than for `twenty-shared` made in
https://github.com/twentyhq/twenty/pull/11083.

## TODO
- [x] Manual migrate twenty-website twenty-ui imports

## What's next:
- Generate barrel and migration script factorization within own package
+ tests
- Refactoring using preconstruct ? TimeBox
- Lint circular dependencies
- Lint import from barrel and forbid them

### Preconstruct
We need custom rollup plugins addition, but preconstruct does not expose
its rollup configuration. It might be possible to handle this using the
babel overrides. But was a big tunnel.
We could give it a try afterwards ! ( allowing cjs interop and stuff
like that )
Stuck to vite lib app

Closed related PRs:
- https://github.com/twentyhq/twenty/pull/11294
- https://github.com/twentyhq/twenty/pull/11203
2025-04-03 09:47:55 +00:00

144 lines
3.8 KiB
TypeScript

import styled from '@emotion/styled';
import React, { useMemo } from 'react';
import { FieldPhonesValue } from '@/object-record/record-field/types/FieldMetadata';
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
import { DEFAULT_PHONE_CALLING_CODE } from '@/object-record/record-field/meta-types/input/components/PhonesFieldInput';
import { parsePhoneNumber } from 'libphonenumber-js';
import { logError } from '~/utils/logError';
import { isDefined } from 'twenty-shared/utils';
import { RoundedLink } from 'twenty-ui/navigation';
import { THEME_COMMON } from 'twenty-ui/theme';
type PhonesDisplayProps = {
value?: FieldPhonesValue;
isFocused?: boolean;
onPhoneNumberClick?: (
phoneNumber: string,
event: React.MouseEvent<HTMLElement>,
) => void;
};
const themeSpacing = THEME_COMMON.spacingMultiplicator;
const StyledContainer = styled.div`
align-items: center;
display: flex;
gap: ${themeSpacing * 1}px;
justify-content: flex-start;
max-width: 100%;
overflow: hidden;
width: 100%;
`;
export const PhonesDisplay = ({
value,
isFocused,
onPhoneNumberClick,
}: PhonesDisplayProps) => {
const phones = useMemo(
() =>
[
value?.primaryPhoneNumber
? {
number: value.primaryPhoneNumber,
callingCode:
value.primaryPhoneCallingCode ||
value.primaryPhoneCountryCode ||
`+${DEFAULT_PHONE_CALLING_CODE}`,
}
: null,
...parseAdditionalPhones(value?.additionalPhones),
]
.filter(isDefined)
.map(({ number, callingCode }) => {
return {
number,
callingCode,
};
}),
[
value?.primaryPhoneNumber,
value?.primaryPhoneCallingCode,
value?.primaryPhoneCountryCode,
value?.additionalPhones,
],
);
const parsePhoneNumberOrReturnInvalidValue = (number: string) => {
try {
return { parsedPhone: parsePhoneNumber(number) };
} catch (e) {
return { invalidPhone: number };
}
};
const handleClick = (
number: string,
event: React.MouseEvent<HTMLElement>,
) => {
onPhoneNumberClick?.(number, event);
};
return isFocused ? (
<ExpandableList isChipCountDisplayed>
{phones.map(({ number, callingCode }, index) => {
const { parsedPhone, invalidPhone } =
parsePhoneNumberOrReturnInvalidValue(callingCode + number);
const URI = parsedPhone?.getURI();
return (
<RoundedLink
key={index}
href={URI || ''}
label={
parsedPhone ? parsedPhone.formatInternational() : invalidPhone
}
onClick={(event) => handleClick(callingCode + number, event)}
/>
);
})}
</ExpandableList>
) : (
<StyledContainer>
{phones.map(({ number, callingCode }, index) => {
const { parsedPhone, invalidPhone } =
parsePhoneNumberOrReturnInvalidValue(callingCode + number);
const URI = parsedPhone?.getURI();
return (
<RoundedLink
key={index}
href={URI || ''}
label={
parsedPhone ? parsedPhone.formatInternational() : invalidPhone
}
onClick={(event) => handleClick(callingCode + number, event)}
/>
);
})}
</StyledContainer>
);
};
const parseAdditionalPhones = (additionalPhones?: any) => {
if (!additionalPhones) {
return [];
}
if (typeof additionalPhones === 'object') {
return additionalPhones;
}
if (typeof additionalPhones === 'string') {
try {
return JSON.parse(additionalPhones);
} catch (error) {
logError(`Error parsing additional phones' : ` + error);
}
}
return [];
};