chore: New standard fields on People (#1104)

* Add New standard fields on People

Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>

* Add requested changes

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>

---------

Co-authored-by: Thiago Nascimbeni <tnascimbeni@gmail.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>
This commit is contained in:
gitstart-twenty
2023-08-10 02:36:03 +08:00
committed by GitHub
parent b557766eb0
commit fc17a0639a
15 changed files with 196 additions and 22 deletions

View File

@ -10,6 +10,7 @@ import {
} from '@/ui/editable-field/types/ViewField';
import {
IconBrandLinkedin,
IconBrandX,
IconBriefcase,
IconBuildingSkyscraper,
IconCalendarEvent,
@ -120,4 +121,16 @@ export const peopleViewFields: ViewFieldDefinition<ViewFieldMetadata>[] = [
placeHolder: 'LinkedIn',
},
} satisfies ViewFieldDefinition<ViewFieldURLMetadata>,
{
id: 'x',
columnLabel: 'X',
columnIcon: <IconBrandX />,
columnSize: 150,
columnOrder: 9,
metadata: {
type: 'url',
fieldName: 'xUrl',
placeHolder: 'X',
},
} satisfies ViewFieldDefinition<ViewFieldURLMetadata>,
];

View File

@ -30,6 +30,7 @@ export const GET_PEOPLE = gql`
displayName
jobTitle
linkedinUrl
xUrl
avatarUrl
createdAt
_activityCount

View File

@ -14,6 +14,7 @@ export const GET_PERSON = gql`
city
jobTitle
linkedinUrl
xUrl
avatarUrl
phone
_activityCount

View File

@ -16,6 +16,7 @@ export const UPDATE_ONE_PERSON = gql`
email
jobTitle
linkedinUrl
xUrl
firstName
lastName
displayName
@ -40,6 +41,7 @@ export const INSERT_ONE_PERSON = gql`
lastName
jobTitle
linkedinUrl
xUrl
displayName
phone
createdAt

View File

@ -51,5 +51,6 @@ export { IconUserCircle } from '@tabler/icons-react';
export { IconCalendar } from '@tabler/icons-react';
export { IconPencil } from '@tabler/icons-react';
export { IconCircleDot } from '@tabler/icons-react';
export { IconBrandX } from '@tabler/icons-react';
export { IconTag } from '@tabler/icons-react';
export { IconHelpCircle } from '@tabler/icons-react';

View File

@ -2,6 +2,7 @@ import { MouseEvent } from 'react';
import styled from '@emotion/styled';
import { RoundedLink } from '@/ui/link/components/RoundedLink';
import { LinkType, SocialLink } from '@/ui/link/components/SocialLink';
const StyledRawLink = styled(RoundedLink)`
overflow: hidden;
@ -17,17 +18,40 @@ type OwnProps = {
value: string;
};
const checkUrlType = (url: string) => {
if (
/^(http|https):\/\/(?:www\.)?linkedin.com(\w+:{0,1}\w*@)?(\S+)(:([0-9])+)?(\/|\/([\w#!:.?+=&%@!\-/]))?$/.test(
url,
)
) {
return LinkType.LinkedIn;
}
if (url.match(/^((http|https):\/\/)?(?:www\.)?twitter\.com\/(\w+)?$/i)) {
return LinkType.Twitter;
}
return LinkType.Url;
};
export function InplaceInputURLDisplayMode({ value }: OwnProps) {
function handleClick(event: MouseEvent<HTMLElement>) {
event.stopPropagation();
}
const absoluteUrl = value
? value.startsWith('http')
? value
: 'https://' + value
: '';
const type = checkUrlType(absoluteUrl);
if (type === LinkType.LinkedIn || type === LinkType.Twitter) {
return (
<SocialLink href={absoluteUrl} onClick={handleClick} type={type}>
{value}
</SocialLink>
);
}
return (
<StyledRawLink href={absoluteUrl} onClick={handleClick}>
{value}

View File

@ -0,0 +1,50 @@
import * as React from 'react';
import styled from '@emotion/styled';
import { RoundedLink } from './RoundedLink';
export enum LinkType {
Url = 'url',
LinkedIn = 'linkedin',
Twitter = 'twitter',
}
type OwnProps = {
href: string;
children?: React.ReactNode;
type?: LinkType;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
};
const StyledRawLink = styled(RoundedLink)`
overflow: hidden;
a {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
`;
export function SocialLink({ children, href, onClick, type }: OwnProps) {
let displayValue = children;
if (type === 'linkedin') {
const splitUrl = href.split('/');
const splitName = splitUrl[4].split('-');
displayValue = splitName[2]
? `${splitName[0]}-${splitName[1]}`
: splitName[0];
}
if (type === 'twitter') {
const splitUrl = href.split('/');
displayValue = `@${splitUrl[3]}`;
}
return (
<StyledRawLink href={href} onClick={onClick}>
{displayValue}
</StyledRawLink>
);
}

View File

@ -1,6 +1,9 @@
import { ReactElement } from 'react';
import { ReactElement, useState } from 'react';
import styled from '@emotion/styled';
import { IconPencil } from '@tabler/icons-react';
import { motion } from 'framer-motion';
import { IconButton } from '@/ui/button/components/IconButton';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { CellHotkeyScopeContext } from '../../states/CellHotkeyScopeContext';
@ -12,6 +15,11 @@ import { EditableCellDisplayMode } from './EditableCellDisplayMode';
import { EditableCellEditMode } from './EditableCellEditMode';
import { EditableCellSoftFocusMode } from './EditableCellSoftFocusMode';
const StyledEditButtonContainer = styled(motion.div)`
position: absolute;
right: 5px;
`;
export const CellBaseContainer = styled.div`
align-items: center;
box-sizing: border-box;
@ -48,7 +56,38 @@ export function EditableCell({
transparent = false,
maxContentWidth,
}: OwnProps) {
const { isCurrentCellInEditMode } = useCurrentCellEditMode();
const { isCurrentCellInEditMode, setCurrentCellInEditMode } =
useCurrentCellEditMode();
const [isHovered, setIsHovered] = useState(false);
function isValidUrl(value: string) {
let testUrl = value;
if (testUrl && !testUrl.startsWith('http')) {
testUrl = 'http://' + testUrl;
}
try {
new URL(testUrl);
return true;
} catch (err) {
return false;
}
}
const handleClick = () => {
setCurrentCellInEditMode();
};
function handleContainerMouseEnter() {
setIsHovered(true);
}
function handleContainerMouseLeave() {
setIsHovered(false);
}
const value = nonEditModeContent.props.value;
const showEditButton =
!isCurrentCellInEditMode && isValidUrl(value) && isHovered;
const hasSoftFocus = useIsSoftFocusOnCurrentCell();
@ -56,7 +95,10 @@ export function EditableCell({
<CellHotkeyScopeContext.Provider
value={editHotkeyScope ?? DEFAULT_CELL_SCOPE}
>
<CellBaseContainer>
<CellBaseContainer
onMouseEnter={handleContainerMouseEnter}
onMouseLeave={handleContainerMouseLeave}
>
{isCurrentCellInEditMode ? (
<EditableCellEditMode
maxContentWidth={maxContentWidth}
@ -71,9 +113,27 @@ export function EditableCell({
{nonEditModeContent}
</EditableCellSoftFocusMode>
) : (
<EditableCellDisplayMode>
{nonEditModeContent}
</EditableCellDisplayMode>
<>
{showEditButton && (
<StyledEditButtonContainer
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.1 }}
whileHover={{ scale: 1.04 }}
>
<IconButton
variant="shadow"
size="small"
onClick={handleClick}
icon={<IconPencil size={14} />}
/>
</StyledEditButtonContainer>
)}
<EditableCellDisplayMode>
{nonEditModeContent}
</EditableCellDisplayMode>
</>
)}
</CellBaseContainer>
</CellHotkeyScopeContext.Provider>