mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-24 14:53:52 +03:00
Update Seeds while pre-fi
lling a new workspace
This commit is contained in:
parent
e90beef91f
commit
aa2596c572
@ -415,6 +415,11 @@ export type DeleteOneObjectInput = {
|
||||
id: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type DeleteOneRelationInput = {
|
||||
/** The id of the record to delete. */
|
||||
id: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type Favorite = {
|
||||
__typename?: 'Favorite';
|
||||
company?: Maybe<Company>;
|
||||
@ -463,6 +468,7 @@ export enum FieldMetadataType {
|
||||
Date = 'DATE',
|
||||
Email = 'EMAIL',
|
||||
Enum = 'ENUM',
|
||||
FullName = 'FULL_NAME',
|
||||
Link = 'LINK',
|
||||
Number = 'NUMBER',
|
||||
Phone = 'PHONE',
|
||||
@ -496,6 +502,7 @@ export type Mutation = {
|
||||
createOneRelation: Relation;
|
||||
deleteOneField: FieldDeleteResponse;
|
||||
deleteOneObject: ObjectDeleteResponse;
|
||||
deleteOneRelation: RelationDeleteResponse;
|
||||
updateOneField: Field;
|
||||
updateOneObject: Object;
|
||||
};
|
||||
@ -526,6 +533,11 @@ export type MutationDeleteOneObjectArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteOneRelationArgs = {
|
||||
input: DeleteOneRelationInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateOneFieldArgs = {
|
||||
input: UpdateOneFieldInput;
|
||||
};
|
||||
@ -711,6 +723,18 @@ export type RelationConnection = {
|
||||
totalCount: Scalars['Int']['output'];
|
||||
};
|
||||
|
||||
export type RelationDeleteResponse = {
|
||||
__typename?: 'RelationDeleteResponse';
|
||||
createdAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
fromFieldMetadataId?: Maybe<Scalars['String']['output']>;
|
||||
fromObjectMetadataId?: Maybe<Scalars['String']['output']>;
|
||||
id?: Maybe<Scalars['ID']['output']>;
|
||||
relationType?: Maybe<RelationMetadataType>;
|
||||
toFieldMetadataId?: Maybe<Scalars['String']['output']>;
|
||||
toObjectMetadataId?: Maybe<Scalars['String']['output']>;
|
||||
updatedAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
};
|
||||
|
||||
/** Type of the relation */
|
||||
export enum RelationMetadataType {
|
||||
ManyToMany = 'MANY_TO_MANY',
|
||||
|
@ -1336,6 +1336,7 @@ export enum FieldMetadataType {
|
||||
Date = 'DATE',
|
||||
Email = 'EMAIL',
|
||||
Enum = 'ENUM',
|
||||
FullName = 'FULL_NAME',
|
||||
Link = 'LINK',
|
||||
Number = 'NUMBER',
|
||||
Phone = 'PHONE',
|
||||
@ -1443,6 +1444,7 @@ export type Mutation = {
|
||||
deleteOneField: FieldDeleteResponse;
|
||||
deleteOneObject: ObjectDeleteResponse;
|
||||
deleteOnePipelineStage: PipelineStage;
|
||||
deleteOneRelation: RelationDeleteResponse;
|
||||
deleteOneWebHook: WebHook;
|
||||
deleteUserAccount: User;
|
||||
deleteUserV2: UserV2;
|
||||
@ -2626,6 +2628,18 @@ export type RelationConnection = {
|
||||
totalCount: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type RelationDeleteResponse = {
|
||||
__typename?: 'RelationDeleteResponse';
|
||||
createdAt?: Maybe<Scalars['DateTime']>;
|
||||
fromFieldMetadataId?: Maybe<Scalars['String']>;
|
||||
fromObjectMetadataId?: Maybe<Scalars['String']>;
|
||||
id?: Maybe<Scalars['ID']>;
|
||||
relationType?: Maybe<RelationMetadataType>;
|
||||
toFieldMetadataId?: Maybe<Scalars['String']>;
|
||||
toObjectMetadataId?: Maybe<Scalars['String']>;
|
||||
updatedAt?: Maybe<Scalars['DateTime']>;
|
||||
};
|
||||
|
||||
/** Type of the relation */
|
||||
export enum RelationMetadataType {
|
||||
ManyToMany = 'MANY_TO_MANY',
|
||||
|
@ -67,7 +67,7 @@ export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => {
|
||||
const showDate = beautifiedCreatedAt !== '';
|
||||
|
||||
const author = comment.author;
|
||||
const authorName = author.firstName + ' ' + author.lastName;
|
||||
const authorName = author.name.firstName + ' ' + author.name.lastName;
|
||||
const avatarUrl = author.avatarUrl;
|
||||
const commentId = comment.id;
|
||||
|
||||
|
@ -10,8 +10,10 @@ export const mockComment: Pick<
|
||||
body: 'Hello, this is a comment.',
|
||||
author: {
|
||||
id: 'fake_comment_1_author_uuid',
|
||||
firstName: 'Jony' ?? '',
|
||||
lastName: 'Ive' ?? '',
|
||||
name: {
|
||||
firstName: 'Jony' ?? '',
|
||||
lastName: 'Ive' ?? '',
|
||||
},
|
||||
avatarUrl: null,
|
||||
},
|
||||
createdAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '',
|
||||
@ -26,8 +28,10 @@ export const mockCommentWithLongValues: Pick<
|
||||
body: 'Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment.',
|
||||
author: {
|
||||
id: 'fake_comment_1_author_uuid',
|
||||
firstName: 'Jony' ?? '',
|
||||
lastName: 'Ive' ?? '',
|
||||
name: {
|
||||
firstName: 'Jony' ?? '',
|
||||
lastName: 'Ive' ?? '',
|
||||
},
|
||||
avatarUrl: null,
|
||||
},
|
||||
createdAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '',
|
||||
|
@ -14,10 +14,7 @@ import {
|
||||
|
||||
export type ActivityAssigneePickerProps = {
|
||||
activity: Pick<Activity, 'id'> & {
|
||||
accountOwner?: Pick<
|
||||
WorkspaceMember,
|
||||
'id' | 'firstName' | 'lastName'
|
||||
> | null;
|
||||
accountOwner?: Pick<WorkspaceMember, 'id' | 'name'> | null;
|
||||
};
|
||||
onSubmit?: () => void;
|
||||
onCancel?: () => void;
|
||||
|
@ -60,10 +60,7 @@ type ActivityEditorProps = {
|
||||
> & {
|
||||
comments?: Array<Comment> | null;
|
||||
} & {
|
||||
assignee?: Pick<
|
||||
WorkspaceMember,
|
||||
'id' | 'firstName' | 'lastName' | 'avatarUrl'
|
||||
> | null;
|
||||
assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
|
||||
} & {
|
||||
activityTargets?: Array<
|
||||
Pick<ActivityTarget, 'id' | 'companyId' | 'personId'>
|
||||
|
@ -13,10 +13,7 @@ import { Company, User } from '~/generated/graphql';
|
||||
|
||||
type ActivityAssigneeEditableFieldProps = {
|
||||
activity: Pick<Company, 'id' | 'accountOwnerId'> & {
|
||||
assignee?: Pick<
|
||||
WorkspaceMember,
|
||||
'id' | 'firstName' | 'lastName' | 'avatarUrl'
|
||||
> | null;
|
||||
assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { Activity } from '@/activities/types/Activity';
|
||||
import { IconNotes } from '@/ui/display/icon';
|
||||
import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
import {
|
||||
beautifyExactDateTime,
|
||||
beautifyPastDateRelativeToNow,
|
||||
@ -127,11 +128,8 @@ type TimelineActivityProps = {
|
||||
| 'type'
|
||||
| 'comments'
|
||||
| 'dueAt'
|
||||
> & { author: Pick<Activity['author'], 'firstName' | 'lastName'> } & {
|
||||
assignee?: Pick<
|
||||
Activity['author'],
|
||||
'id' | 'firstName' | 'lastName' | 'avatarUrl'
|
||||
> | null;
|
||||
> & { author: Pick<WorkspaceMember, 'name'> } & {
|
||||
assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
|
||||
};
|
||||
};
|
||||
|
||||
@ -151,7 +149,9 @@ export const TimelineActivity = ({ activity }: TimelineActivityProps) => {
|
||||
</StyledIconContainer>
|
||||
<StyledItemTitleContainer>
|
||||
<span>
|
||||
{activity.author.firstName + ' ' + activity.author.lastName}
|
||||
{activity.author.name.firstName +
|
||||
' ' +
|
||||
activity.author.name.lastName}
|
||||
</span>
|
||||
created a {activity.type.toLowerCase()}
|
||||
</StyledItemTitleContainer>
|
||||
|
@ -9,10 +9,7 @@ import { beautifyExactDate } from '~/utils/date-utils';
|
||||
|
||||
type TimelineActivityCardFooterProps = {
|
||||
activity: Pick<Activity, 'id' | 'dueAt' | 'comments'> & {
|
||||
assignee?: Pick<
|
||||
WorkspaceMember,
|
||||
'id' | 'firstName' | 'lastName' | 'avatarUrl'
|
||||
> | null;
|
||||
assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
|
||||
};
|
||||
};
|
||||
|
||||
@ -48,9 +45,9 @@ export const TimelineActivityCardFooter = ({
|
||||
<UserChip
|
||||
id={activity.assignee.id}
|
||||
name={
|
||||
activity.assignee.firstName +
|
||||
activity.assignee.name.firstName +
|
||||
' ' +
|
||||
activity.assignee.lastName ?? ''
|
||||
activity.assignee.name.lastName ?? ''
|
||||
}
|
||||
pictureUrl={activity.assignee.avatarUrl ?? ''}
|
||||
/>
|
||||
|
@ -15,12 +15,9 @@ export type Activity = {
|
||||
type: ActivityType;
|
||||
title: string;
|
||||
body: string;
|
||||
author: Pick<WorkspaceMember, 'id' | 'firstName' | 'lastName' | 'avatarUrl'>;
|
||||
author: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
|
||||
authorId: string;
|
||||
assignee: Pick<
|
||||
WorkspaceMember,
|
||||
'id' | 'firstName' | 'lastName' | 'avatarUrl'
|
||||
> | null;
|
||||
assignee: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
|
||||
assigneeId: string | null;
|
||||
comments: Comment[];
|
||||
};
|
||||
|
@ -6,5 +6,5 @@ export type Comment = {
|
||||
body: string;
|
||||
updatedAt: string;
|
||||
activityId: string;
|
||||
author: Pick<WorkspaceMember, 'id' | 'firstName' | 'lastName' | 'avatarUrl'>;
|
||||
author: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
|
||||
};
|
||||
|
@ -169,8 +169,10 @@ export const useAuth = () => {
|
||||
mutation: CREATE_ONE_WORKSPACE_MEMBER_V2,
|
||||
variables: {
|
||||
input: {
|
||||
firstName: user.firstName ?? '',
|
||||
lastName: user.lastName ?? '',
|
||||
name: {
|
||||
firstName: user.firstName ?? '',
|
||||
lastName: user.lastName ?? '',
|
||||
},
|
||||
colorScheme: 'Light',
|
||||
userId: user.id,
|
||||
allowImpersonation: true,
|
||||
|
@ -25,7 +25,10 @@ export const getOnboardingStatus = (
|
||||
if (!currentWorkspace?.displayName) {
|
||||
return OnboardingStatus.OngoingWorkspaceCreation;
|
||||
}
|
||||
if (!currentWorkspaceMember.firstName || !currentWorkspaceMember.lastName) {
|
||||
if (
|
||||
!currentWorkspaceMember.name.firstName ||
|
||||
!currentWorkspaceMember.name.lastName
|
||||
) {
|
||||
return OnboardingStatus.OngoingProfileCreation;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { Company, Favorite, User } from '../../../../generated/graphql';
|
||||
import { Favorite } from '@/favorites/types/Favorite';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
import { Company } from '../../../../generated/graphql';
|
||||
|
||||
type MockedCompany = Pick<
|
||||
Company,
|
||||
@ -15,16 +18,7 @@ type MockedCompany = Pick<
|
||||
| 'idealCustomerProfile'
|
||||
| '_activityCount'
|
||||
> & {
|
||||
accountOwner: Pick<
|
||||
User,
|
||||
| 'id'
|
||||
| 'email'
|
||||
| 'displayName'
|
||||
| 'avatarUrl'
|
||||
| '__typename'
|
||||
| 'firstName'
|
||||
| 'lastName'
|
||||
> | null;
|
||||
accountOwner: Pick<WorkspaceMember, 'id' | 'avatarUrl' | 'name'> | null;
|
||||
} & { Favorite: Pick<Favorite, 'id'> | null };
|
||||
|
||||
export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
@ -42,13 +36,12 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
Favorite: null,
|
||||
_activityCount: 0,
|
||||
accountOwner: {
|
||||
email: 'charles@test.com',
|
||||
displayName: 'Charles Test',
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
name: {
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
},
|
||||
avatarUrl: null,
|
||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
|
||||
__typename: 'User',
|
||||
},
|
||||
__typename: 'Company',
|
||||
},
|
||||
|
@ -91,6 +91,14 @@ export const useMapFieldMetadataToGraphQLQuery = () => {
|
||||
currencyCode
|
||||
}
|
||||
`;
|
||||
} else if (fieldType === 'FULL_NAME') {
|
||||
return `
|
||||
${field.name}
|
||||
{
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -91,9 +91,10 @@ export const RecordShowPage = () => {
|
||||
if (isFavorite) deleteFavorite(object?.id);
|
||||
else {
|
||||
const additionalData =
|
||||
objectNameSingular === 'peopleV2'
|
||||
objectNameSingular === 'personV2'
|
||||
? {
|
||||
labelIdentifier: object.firstName + ' ' + object.lastName,
|
||||
labelIdentifier:
|
||||
object.name.firstName + ' ' + object.name.lastName,
|
||||
avatarUrl: object.avatarUrl,
|
||||
avatarType: 'rounded',
|
||||
link: `/object/personV2/${object.id}`,
|
||||
@ -114,11 +115,16 @@ export const RecordShowPage = () => {
|
||||
|
||||
if (!object) return <></>;
|
||||
|
||||
const pageName =
|
||||
objectNameSingular === 'personV2'
|
||||
? object.name.firstName + ' ' + object.name.lastName
|
||||
: object.name;
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<PageTitle title={object.name || 'No Name'} />
|
||||
<PageTitle title={pageName} />
|
||||
<PageHeader
|
||||
title={object.name ?? ''}
|
||||
title={pageName ?? ''}
|
||||
hasBackButton
|
||||
Icon={IconBuildingSkyscraper}
|
||||
>
|
||||
|
@ -4,8 +4,10 @@ export const CREATE_ONE_WORKSPACE_MEMBER_V2 = gql`
|
||||
mutation CreateOneWorkspaceMemberV2($input: WorkspaceMemberV2CreateInput!) {
|
||||
createWorkspaceMemberV2(data: $input) {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -6,8 +6,10 @@ export const FIND_ONE_WORKSPACE_MEMBER_V2 = gql`
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
colorScheme
|
||||
avatarUrl
|
||||
locale
|
||||
|
@ -29,7 +29,6 @@ export const useRecordTableContextMenuEntries = () => {
|
||||
const { scopeId: objectNamePlural, resetTableRowSelection } =
|
||||
useRecordTable();
|
||||
|
||||
const { data } = useGetFavoritesQuery();
|
||||
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
@ -31,7 +31,7 @@ export const useGenerateFindManyCustomObjectsQuery = ({
|
||||
node {
|
||||
id
|
||||
${objectMetadataItem.fields
|
||||
.map(mapFieldMetadataToGraphQLQuery)
|
||||
.map((field) => mapFieldMetadataToGraphQLQuery(field))
|
||||
.join('\n')}
|
||||
}
|
||||
cursor
|
||||
|
@ -16,7 +16,7 @@ const PeopleTableEffect = () => {
|
||||
setViewObjectMetadataId,
|
||||
} = useView();
|
||||
|
||||
const { setAvailableTableColumns, setTableColumns } = useRecordTable();
|
||||
const { setAvailableTableColumns } = useRecordTable();
|
||||
|
||||
useEffect(() => {
|
||||
setAvailableSortDefinitions?.(personTableSortDefinitions);
|
||||
@ -31,7 +31,6 @@ const PeopleTableEffect = () => {
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableTableColumns,
|
||||
setTableColumns,
|
||||
setViewObjectMetadataId,
|
||||
setViewType,
|
||||
]);
|
||||
|
@ -62,13 +62,26 @@ export const useFilteredSearchEntityQueryV2 = ({
|
||||
}
|
||||
|
||||
return {
|
||||
or: fieldNames.map((fieldName) => ({
|
||||
[fieldName]: {
|
||||
like: `%${filter}%`,
|
||||
// TODO: fix mode
|
||||
// mode: QueryMode.Insensitive,
|
||||
},
|
||||
})),
|
||||
or: fieldNames.map((fieldName) => {
|
||||
const fieldNameParts = fieldName.split('.');
|
||||
|
||||
if (fieldNameParts.length > 1) {
|
||||
// Composite field
|
||||
|
||||
return {
|
||||
[fieldNameParts[0]]: {
|
||||
[fieldNameParts[1]]: {
|
||||
ilike: `%${filter}%`,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
[fieldName]: {
|
||||
ilike: `%${filter}%`,
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
IconPhone,
|
||||
IconPlug,
|
||||
IconTextSize,
|
||||
IconUser,
|
||||
} from '@/ui/display/icon';
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { CurrencyCode, FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
@ -64,5 +65,6 @@ export const dataTypes: Record<
|
||||
Icon: IconNumbers,
|
||||
defaultValue: 50,
|
||||
},
|
||||
[FieldMetadataType.FullName]: { label: 'Full Name', Icon: IconUser },
|
||||
[FieldMetadataType.Enum]: { label: 'Enum', Icon: IconPlug },
|
||||
};
|
||||
|
@ -34,10 +34,10 @@ export const NameFields = ({
|
||||
);
|
||||
|
||||
const [firstName, setFirstName] = useState(
|
||||
currentWorkspaceMember?.firstName ?? '',
|
||||
currentWorkspaceMember?.name.firstName ?? '',
|
||||
);
|
||||
const [lastName, setLastName] = useState(
|
||||
currentWorkspaceMember?.lastName ?? '',
|
||||
currentWorkspaceMember?.name.lastName ?? '',
|
||||
);
|
||||
|
||||
const { updateOneObject, objectNotFoundInMetadata } =
|
||||
@ -65,15 +65,19 @@ export const NameFields = ({
|
||||
await updateOneObject({
|
||||
idToUpdate: currentWorkspaceMember?.id,
|
||||
input: {
|
||||
firstName,
|
||||
lastName,
|
||||
name: {
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
setCurrentWorkspaceMember({
|
||||
...currentWorkspaceMember,
|
||||
firstName,
|
||||
lastName,
|
||||
name: {
|
||||
firstName,
|
||||
lastName,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@ -87,8 +91,8 @@ export const NameFields = ({
|
||||
}
|
||||
|
||||
if (
|
||||
currentWorkspaceMember.firstName !== firstName ||
|
||||
currentWorkspaceMember.lastName !== lastName
|
||||
currentWorkspaceMember.name.firstName !== firstName ||
|
||||
currentWorkspaceMember.name.lastName !== lastName
|
||||
) {
|
||||
debouncedUpdate();
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ const SupportChat = () => {
|
||||
(
|
||||
chatId: string,
|
||||
currentUser: Pick<User, 'email' | 'supportUserHash'>,
|
||||
currentWorkspaceMember: Pick<WorkspaceMember, 'firstName' | 'lastName'>,
|
||||
currentWorkspaceMember: Pick<WorkspaceMember, 'name'>,
|
||||
) => {
|
||||
const url = 'https://chat-assets.frontapp.com/v1/chat.bundle.js';
|
||||
const script = document.querySelector(`script[src="${url}"]`);
|
||||
@ -54,9 +54,9 @@ const SupportChat = () => {
|
||||
useDefaultLauncher: false,
|
||||
email: currentUser.email,
|
||||
name:
|
||||
currentWorkspaceMember.firstName +
|
||||
currentWorkspaceMember.name.firstName +
|
||||
' ' +
|
||||
currentWorkspaceMember.lastName,
|
||||
currentWorkspaceMember.name.lastName,
|
||||
userHash: currentUser?.supportUserHash,
|
||||
});
|
||||
setIsFrontChatLoaded(true);
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { FullNameFieldDisplay } from '@/ui/object/field/meta-types/display/components/FullNameFieldDisplay';
|
||||
import { RelationFieldDisplay } from '@/ui/object/field/meta-types/display/components/RelationFieldDisplay';
|
||||
import { UuidFieldDisplay } from '@/ui/object/field/meta-types/display/components/UuidFieldDisplay';
|
||||
import { isFieldFullName } from '@/ui/object/field/types/guards/isFieldFullName';
|
||||
import { isFieldUuid } from '@/ui/object/field/types/guards/isFieldUuid';
|
||||
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
@ -9,7 +11,6 @@ import { ChipFieldDisplay } from '../meta-types/display/components/ChipFieldDisp
|
||||
import { CurrencyFieldDisplay } from '../meta-types/display/components/CurrencyFieldDisplay';
|
||||
import { DateFieldDisplay } from '../meta-types/display/components/DateFieldDisplay';
|
||||
import { DoubleTextChipFieldDisplay } from '../meta-types/display/components/DoubleTextChipFieldDisplay';
|
||||
import { DoubleTextFieldDisplay } from '../meta-types/display/components/DoubleTextFieldDisplay';
|
||||
import { EmailFieldDisplay } from '../meta-types/display/components/EmailFieldDisplay';
|
||||
import { LinkFieldDisplay } from '../meta-types/display/components/LinkFieldDisplay';
|
||||
import { MoneyFieldDisplay } from '../meta-types/display/components/MoneyFieldDisplay';
|
||||
@ -20,7 +21,6 @@ import { URLFieldDisplay } from '../meta-types/display/components/URLFieldDispla
|
||||
import { isFieldChip } from '../types/guards/isFieldChip';
|
||||
import { isFieldCurrency } from '../types/guards/isFieldCurrency';
|
||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||
import { isFieldDoubleText } from '../types/guards/isFieldDoubleText';
|
||||
import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip';
|
||||
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||
import { isFieldLink } from '../types/guards/isFieldLink';
|
||||
@ -56,14 +56,14 @@ export const FieldDisplay = () => {
|
||||
<LinkFieldDisplay />
|
||||
) : isFieldCurrency(fieldDefinition) ? (
|
||||
<CurrencyFieldDisplay />
|
||||
) : isFieldFullName(fieldDefinition) ? (
|
||||
<FullNameFieldDisplay />
|
||||
) : isFieldPhone(fieldDefinition) ? (
|
||||
<PhoneFieldDisplay />
|
||||
) : isFieldChip(fieldDefinition) ? (
|
||||
<ChipFieldDisplay />
|
||||
) : isFieldDoubleTextChip(fieldDefinition) ? (
|
||||
<DoubleTextChipFieldDisplay />
|
||||
) : isFieldDoubleText(fieldDefinition) ? (
|
||||
<DoubleTextFieldDisplay />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
@ -8,7 +8,6 @@ import { ChipFieldInput } from '../meta-types/input/components/ChipFieldInput';
|
||||
import { CurrencyFieldInput } from '../meta-types/input/components/CurrencyFieldInput';
|
||||
import { DateFieldInput } from '../meta-types/input/components/DateFieldInput';
|
||||
import { DoubleTextChipFieldInput } from '../meta-types/input/components/DoubleTextChipFieldInput';
|
||||
import { DoubleTextFieldInput } from '../meta-types/input/components/DoubleTextFieldInput';
|
||||
import { EmailFieldInput } from '../meta-types/input/components/EmailFieldInput';
|
||||
import { LinkFieldInput } from '../meta-types/input/components/LinkFieldInput';
|
||||
import { MoneyFieldInput } from '../meta-types/input/components/MoneyFieldInput';
|
||||
@ -23,7 +22,6 @@ import { isFieldBoolean } from '../types/guards/isFieldBoolean';
|
||||
import { isFieldChip } from '../types/guards/isFieldChip';
|
||||
import { isFieldCurrency } from '../types/guards/isFieldCurrency';
|
||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||
import { isFieldDoubleText } from '../types/guards/isFieldDoubleText';
|
||||
import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip';
|
||||
import { isFieldEmail } from '../types/guards/isFieldEmail';
|
||||
import { isFieldLink } from '../types/guards/isFieldLink';
|
||||
@ -146,14 +144,6 @@ export const FieldInput = ({
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldDoubleText(fieldDefinition) ? (
|
||||
<DoubleTextFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldMoney(fieldDefinition) ? (
|
||||
<MoneyFieldInput
|
||||
onEnter={onEnter}
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { useDoubleTextField } from '../../hooks/useDoubleTextField';
|
||||
import { TextDisplay } from '../content-display/components/TextDisplay';
|
||||
|
||||
export const DoubleTextFieldDisplay = () => {
|
||||
const { firstValue, secondValue } = useDoubleTextField();
|
||||
|
||||
const content = [firstValue, secondValue].filter(Boolean).join(' ');
|
||||
|
||||
return <TextDisplay text={content} />;
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
import { useFullNameField } from '@/ui/object/field/meta-types/hooks/useFullNameField';
|
||||
|
||||
import { TextDisplay } from '../content-display/components/TextDisplay';
|
||||
|
||||
export const FullNameFieldDisplay = () => {
|
||||
const { fieldValue } = useFullNameField();
|
||||
|
||||
const content = [fieldValue.firstName, fieldValue.lastName]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
|
||||
return <TextDisplay text={content} />;
|
||||
};
|
@ -1,79 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
|
||||
import { FieldContext } from '../../../../contexts/FieldContext';
|
||||
import { useDoubleTextField } from '../../../hooks/useDoubleTextField';
|
||||
import { DoubleTextFieldDisplay } from '../DoubleTextFieldDisplay'; // Import your component
|
||||
|
||||
const DoubleTextFieldDisplayValueSetterEffect = ({
|
||||
firstValue,
|
||||
secondValue,
|
||||
}: {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
}) => {
|
||||
const { setFirstValue, setSecondValue } = useDoubleTextField();
|
||||
|
||||
useEffect(() => {
|
||||
setFirstValue(firstValue);
|
||||
setSecondValue(secondValue);
|
||||
}, [setFirstValue, setSecondValue, firstValue, secondValue]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'UI/Data/Field/Display/DoubleTextFieldDisplay',
|
||||
decorators: [
|
||||
(Story, { args }) => (
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
entityId: '',
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'double-text',
|
||||
label: 'Double-Text',
|
||||
type: 'DOUBLE_TEXT',
|
||||
metadata: {
|
||||
firstValueFieldName: 'First-text',
|
||||
firstValuePlaceholder: 'First-text',
|
||||
secondValueFieldName: 'Second-text',
|
||||
secondValuePlaceholder: 'Second-text',
|
||||
},
|
||||
},
|
||||
hotkeyScope: 'hotkey-scope',
|
||||
}}
|
||||
>
|
||||
<DoubleTextFieldDisplayValueSetterEffect
|
||||
firstValue={args.firstValue}
|
||||
secondValue={args.secondValue}
|
||||
/>
|
||||
<Story />
|
||||
</FieldContext.Provider>
|
||||
),
|
||||
ComponentDecorator,
|
||||
],
|
||||
component: DoubleTextFieldDisplay,
|
||||
args: {
|
||||
firstValue: 'Lorem',
|
||||
secondValue: 'ipsum',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof DoubleTextFieldDisplay>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const Elipsis: Story = {
|
||||
args: {
|
||||
firstValue:
|
||||
'Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
|
||||
secondValue: 'ipsum dolor sit amet, consectetur adipiscing elit.',
|
||||
},
|
||||
parameters: {
|
||||
container: { width: 100 },
|
||||
},
|
||||
};
|
@ -21,12 +21,12 @@ export const getEntityChipFromFieldMetadata = (
|
||||
|
||||
// TODO: use every
|
||||
if (fieldName === 'accountOwner' && fieldValue) {
|
||||
chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName;
|
||||
chipValue.name = fieldValue.name.firstName + ' ' + fieldValue.name.lastName;
|
||||
} else if (fieldName === 'company' && fieldValue) {
|
||||
chipValue.name = fieldValue.name;
|
||||
chipValue.pictureUrl = getLogoUrlFromDomainName(fieldValue.domainName);
|
||||
} else if (fieldName === 'person' && fieldValue) {
|
||||
chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName;
|
||||
chipValue.name = fieldValue.name.firstName + ' ' + fieldValue.name.lastName;
|
||||
}
|
||||
|
||||
return chipValue;
|
||||
|
@ -1,54 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
|
||||
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
|
||||
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
|
||||
import { isFieldDoubleText } from '../../types/guards/isFieldDoubleText';
|
||||
|
||||
export const useDoubleTextField = () => {
|
||||
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
|
||||
assertFieldMetadata('DOUBLE_TEXT', isFieldDoubleText, fieldDefinition);
|
||||
|
||||
const [firstValue, setFirstValue] = useRecoilState<string>(
|
||||
entityFieldsFamilySelector({
|
||||
entityId: entityId,
|
||||
fieldName: fieldDefinition.metadata.firstValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const [secondValue, setSecondValue] = useRecoilState<string>(
|
||||
entityFieldsFamilySelector({
|
||||
entityId: entityId,
|
||||
fieldName: fieldDefinition.metadata.secondValueFieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const fieldInitialValue = useFieldInitialValue();
|
||||
|
||||
const initialFirstValue = fieldInitialValue?.isEmpty
|
||||
? ''
|
||||
: fieldInitialValue?.value ?? firstValue;
|
||||
|
||||
const initialSecondValue = fieldInitialValue?.isEmpty
|
||||
? ''
|
||||
: fieldInitialValue?.value
|
||||
? ''
|
||||
: secondValue;
|
||||
|
||||
const fullValue = [firstValue, secondValue].filter(Boolean).join(' ');
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
secondValue,
|
||||
setSecondValue,
|
||||
firstValue,
|
||||
initialFirstValue,
|
||||
initialSecondValue,
|
||||
setFirstValue,
|
||||
fullValue,
|
||||
hotkeyScope,
|
||||
};
|
||||
};
|
@ -0,0 +1,51 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { FieldContext } from '../../contexts/FieldContext';
|
||||
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
|
||||
import { usePersistField } from '../../hooks/usePersistField';
|
||||
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
|
||||
import { FieldFullNameValue } from '../../types/FieldMetadata';
|
||||
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
|
||||
import { isFieldFullName } from '../../types/guards/isFieldFullName';
|
||||
import { isFieldFullNameValue } from '../../types/guards/isFieldFullNameValue';
|
||||
|
||||
export const useFullNameField = () => {
|
||||
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
|
||||
|
||||
assertFieldMetadata('FULL_NAME', isFieldFullName, fieldDefinition);
|
||||
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<FieldFullNameValue>(
|
||||
entityFieldsFamilySelector({
|
||||
entityId: entityId,
|
||||
fieldName: fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const persistFullNameField = (newValue: FieldFullNameValue) => {
|
||||
if (!isFieldFullNameValue(newValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
persistField(newValue);
|
||||
};
|
||||
|
||||
const fieldInitialValue = useFieldInitialValue();
|
||||
|
||||
const initialValue: FieldFullNameValue = fieldInitialValue?.isEmpty
|
||||
? { firstName: '', lastName: '' }
|
||||
: fieldValue;
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
initialValue,
|
||||
setFieldValue,
|
||||
hotkeyScope,
|
||||
persistFullNameField,
|
||||
};
|
||||
};
|
@ -1,73 +0,0 @@
|
||||
import { DoubleTextInput } from '@/ui/object/field/meta-types/input/components/internal/DoubleTextInput';
|
||||
import { FieldDoubleText } from '@/ui/object/field/types/FieldDoubleText';
|
||||
|
||||
import { usePersistField } from '../../../hooks/usePersistField';
|
||||
import { useDoubleTextField } from '../../hooks/useDoubleTextField';
|
||||
|
||||
import { FieldInputOverlay } from './internal/FieldInputOverlay';
|
||||
import { FieldInputEvent } from './DateFieldInput';
|
||||
|
||||
export type DoubleTextFieldInputProps = {
|
||||
onClickOutside?: FieldInputEvent;
|
||||
onEnter?: FieldInputEvent;
|
||||
onEscape?: FieldInputEvent;
|
||||
onTab?: FieldInputEvent;
|
||||
onShiftTab?: FieldInputEvent;
|
||||
};
|
||||
|
||||
export const DoubleTextFieldInput = ({
|
||||
onEnter,
|
||||
onEscape,
|
||||
onClickOutside,
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: DoubleTextFieldInputProps) => {
|
||||
const {
|
||||
fieldDefinition,
|
||||
initialFirstValue,
|
||||
initialSecondValue,
|
||||
hotkeyScope,
|
||||
} = useDoubleTextField();
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const handleEnter = (newDoubleText: FieldDoubleText) => {
|
||||
onEnter?.(() => persistField(newDoubleText));
|
||||
};
|
||||
|
||||
const handleEscape = (newDoubleText: FieldDoubleText) => {
|
||||
onEscape?.(() => persistField(newDoubleText));
|
||||
};
|
||||
|
||||
const handleClickOutside = (
|
||||
event: MouseEvent | TouchEvent,
|
||||
newDoubleText: FieldDoubleText,
|
||||
) => {
|
||||
onClickOutside?.(() => persistField(newDoubleText));
|
||||
};
|
||||
|
||||
const handleTab = (newDoubleText: FieldDoubleText) => {
|
||||
onTab?.(() => persistField(newDoubleText));
|
||||
};
|
||||
|
||||
const handleShiftTab = (newDoubleText: FieldDoubleText) => {
|
||||
onShiftTab?.(() => persistField(newDoubleText));
|
||||
};
|
||||
|
||||
return (
|
||||
<FieldInputOverlay>
|
||||
<DoubleTextInput
|
||||
firstValue={initialFirstValue}
|
||||
secondValue={initialSecondValue}
|
||||
firstValuePlaceholder={fieldDefinition.metadata.firstValuePlaceholder}
|
||||
secondValuePlaceholder={fieldDefinition.metadata.secondValuePlaceholder}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onShiftTab={handleShiftTab}
|
||||
onTab={handleTab}
|
||||
hotkeyScope={hotkeyScope}
|
||||
/>
|
||||
</FieldInputOverlay>
|
||||
);
|
||||
};
|
@ -1,191 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { expect, jest } from '@storybook/jest';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
||||
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
|
||||
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
|
||||
import { useDoubleTextField } from '../../../hooks/useDoubleTextField';
|
||||
import {
|
||||
DoubleTextFieldInput,
|
||||
DoubleTextFieldInputProps,
|
||||
} from '../DoubleTextFieldInput';
|
||||
|
||||
const DoubleTextFieldValueSetterEffect = ({
|
||||
firstValue,
|
||||
secondValue,
|
||||
}: {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
}) => {
|
||||
const { setFirstValue, setSecondValue } = useDoubleTextField();
|
||||
|
||||
useEffect(() => {
|
||||
setFirstValue(firstValue);
|
||||
setSecondValue(secondValue);
|
||||
}, [firstValue, secondValue, setFirstValue, setSecondValue]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
type DoubleTextFieldInputWithContextProps = DoubleTextFieldInputProps & {
|
||||
firstValue: string;
|
||||
secondValue: string;
|
||||
entityId?: string;
|
||||
};
|
||||
|
||||
const DoubleTextFieldInputWithContext = ({
|
||||
entityId,
|
||||
firstValue,
|
||||
secondValue,
|
||||
onClickOutside,
|
||||
onEnter,
|
||||
onEscape,
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: DoubleTextFieldInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
|
||||
useEffect(() => {
|
||||
setHotKeyScope('hotkey-scope');
|
||||
}, [setHotKeyScope]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'double-text',
|
||||
label: 'Double-Text',
|
||||
type: 'DOUBLE_TEXT',
|
||||
metadata: {
|
||||
firstValueFieldName: 'First-text',
|
||||
firstValuePlaceholder: 'First-text',
|
||||
secondValueFieldName: 'Second-text',
|
||||
secondValuePlaceholder: 'Second-text',
|
||||
},
|
||||
}}
|
||||
entityId={entityId}
|
||||
>
|
||||
<DoubleTextFieldValueSetterEffect {...{ firstValue, secondValue }} />
|
||||
<DoubleTextFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
</FieldContextProvider>
|
||||
<div data-testid="data-field-input-click-outside-div" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const enterJestFn = jest.fn();
|
||||
const escapeJestfn = jest.fn();
|
||||
const clickOutsideJestFn = jest.fn();
|
||||
const tabJestFn = jest.fn();
|
||||
const shiftTabJestFn = jest.fn();
|
||||
|
||||
const clearMocksDecorator: Decorator = (Story, context) => {
|
||||
if (context.parameters.clearMocks) {
|
||||
enterJestFn.mockClear();
|
||||
escapeJestfn.mockClear();
|
||||
clickOutsideJestFn.mockClear();
|
||||
tabJestFn.mockClear();
|
||||
shiftTabJestFn.mockClear();
|
||||
}
|
||||
return <Story />;
|
||||
};
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'UI/Data/Field/Input/DoubleTextFieldInput',
|
||||
component: DoubleTextFieldInputWithContext,
|
||||
args: {
|
||||
firstValue: 'first value',
|
||||
secondValue: 'second value',
|
||||
onEnter: enterJestFn,
|
||||
onEscape: escapeJestfn,
|
||||
onClickOutside: clickOutsideJestFn,
|
||||
onTab: tabJestFn,
|
||||
onShiftTab: shiftTabJestFn,
|
||||
},
|
||||
argTypes: {
|
||||
onEnter: { control: false },
|
||||
onEscape: { control: false },
|
||||
onClickOutside: { control: false },
|
||||
onTab: { control: false },
|
||||
onShiftTab: { control: false },
|
||||
},
|
||||
decorators: [clearMocksDecorator],
|
||||
parameters: {
|
||||
clearMocks: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof DoubleTextFieldInputWithContext>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const Enter: Story = {
|
||||
play: async () => {
|
||||
expect(enterJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{enter}');
|
||||
expect(enterJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const Escape: Story = {
|
||||
play: async () => {
|
||||
expect(escapeJestfn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{esc}');
|
||||
expect(escapeJestfn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const ClickOutside: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
expect(clickOutsideJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
const emptyDiv = await canvas.findByTestId(
|
||||
'data-field-input-click-outside-div',
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.click(emptyDiv);
|
||||
expect(clickOutsideJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const Tab: Story = {
|
||||
play: async () => {
|
||||
expect(tabJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{tab}');
|
||||
expect(tabJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const ShiftTab: Story = {
|
||||
play: async () => {
|
||||
expect(shiftTabJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.keyboard('{shift>}{tab}');
|
||||
expect(shiftTabJestFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
},
|
||||
};
|
@ -1,5 +1,7 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { isFieldFullName } from '@/ui/object/field/types/guards/isFieldFullName';
|
||||
import { isFieldFullNameValue } from '@/ui/object/field/types/guards/isFieldFullNameValue';
|
||||
import { isFieldUuid } from '@/ui/object/field/types/guards/isFieldUuid';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
@ -108,6 +110,16 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
||||
);
|
||||
}
|
||||
|
||||
if (isFieldFullName(fieldDefinition)) {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
|
||||
|
||||
return (
|
||||
!isFieldFullNameValue(fieldValue) ||
|
||||
isValueEmpty(fieldValue?.firstName + fieldValue?.lastName)
|
||||
);
|
||||
}
|
||||
|
||||
if (isFieldLink(fieldDefinition)) {
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
|
||||
|
@ -48,6 +48,10 @@ export type FieldCurrencyMetadata = {
|
||||
isPositive?: boolean;
|
||||
};
|
||||
|
||||
export type FieldFullnameMetadata = {
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
export type FieldEmailMetadata = {
|
||||
fieldName: string;
|
||||
placeHolder: string;
|
||||
@ -119,6 +123,7 @@ export type FieldLinkValue = { url: string; label: string };
|
||||
export type FieldNumberValue = number | null;
|
||||
export type FieldMoneyValue = number | null;
|
||||
export type FieldCurrencyValue = { currencyCode: string; amountMicros: number };
|
||||
export type FieldFullNameValue = { firstName: string; lastName: string };
|
||||
|
||||
export type FieldEmailValue = string;
|
||||
export type FieldProbabilityValue = number;
|
||||
|
@ -15,4 +15,5 @@ export type FieldType =
|
||||
| 'PROBABILITY'
|
||||
| 'CURRENCY'
|
||||
| 'MONEY_AMOUNT'
|
||||
| 'MONEY';
|
||||
| 'MONEY'
|
||||
| 'FULL_NAME';
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
FieldDoubleTextChipMetadata,
|
||||
FieldDoubleTextMetadata,
|
||||
FieldEmailMetadata,
|
||||
FieldFullnameMetadata,
|
||||
FieldLinkMetadata,
|
||||
FieldMetadata,
|
||||
FieldMoneyMetadata,
|
||||
@ -28,6 +29,8 @@ type AssertFieldMetadataFunction = <
|
||||
? FieldChipMetadata
|
||||
: E extends 'CURRENCY'
|
||||
? FieldCurrencyMetadata
|
||||
: E extends 'FULL_NAME'
|
||||
? FieldFullnameMetadata
|
||||
: E extends 'DATE'
|
||||
? FieldDateMetadata
|
||||
: E extends 'DOUBLE_TEXT'
|
||||
|
@ -0,0 +1,7 @@
|
||||
import { FieldDefinition } from '../FieldDefinition';
|
||||
import { FieldCurrencyMetadata, FieldMetadata } from '../FieldMetadata';
|
||||
|
||||
export const isFieldFullName = (
|
||||
field: FieldDefinition<FieldMetadata>,
|
||||
): field is FieldDefinition<FieldCurrencyMetadata> =>
|
||||
field.type === 'FULL_NAME';
|
@ -0,0 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { FieldFullNameValue } from '../FieldMetadata';
|
||||
|
||||
const currencySchema = z.object({
|
||||
firstName: z.string(),
|
||||
lastName: z.string(),
|
||||
});
|
||||
|
||||
export const isFieldFullNameValue = (
|
||||
fieldValue: unknown,
|
||||
): fieldValue is FieldFullNameValue =>
|
||||
currencySchema.safeParse(fieldValue).success;
|
@ -6,7 +6,9 @@ import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObje
|
||||
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
export const useColorScheme = () => {
|
||||
const [currentWorkspaceMember] = useRecoilState(currentWorkspaceMemberState);
|
||||
const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
const { updateOneObject: updateOneWorkspaceMember } =
|
||||
useUpdateOneObjectRecord({
|
||||
@ -19,6 +21,15 @@ export const useColorScheme = () => {
|
||||
if (!currentWorkspaceMember) {
|
||||
return;
|
||||
}
|
||||
setCurrentWorkspaceMember((current) => {
|
||||
if (!current) {
|
||||
return current;
|
||||
}
|
||||
return {
|
||||
...current,
|
||||
colorScheme: value,
|
||||
};
|
||||
});
|
||||
await updateOneWorkspaceMember?.({
|
||||
idToUpdate: currentWorkspaceMember?.id,
|
||||
input: {
|
||||
@ -26,7 +37,11 @@ export const useColorScheme = () => {
|
||||
},
|
||||
});
|
||||
},
|
||||
[currentWorkspaceMember, updateOneWorkspaceMember],
|
||||
[
|
||||
currentWorkspaceMember,
|
||||
setCurrentWorkspaceMember,
|
||||
updateOneWorkspaceMember,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -33,25 +33,26 @@ export const UserPicker = ({
|
||||
}, [initialSearchFilter, setRelationPickerSearchFilter]);
|
||||
|
||||
const { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNamePlural: 'workspaceMembersV2',
|
||||
objectNameSingular: 'workspaceMemberV2',
|
||||
});
|
||||
|
||||
const useFindManyWorkspaceMembers = (options: any) =>
|
||||
useQuery(findManyQuery, options);
|
||||
|
||||
const users = useFilteredSearchEntityQueryV2({
|
||||
const workspaceMembers = useFilteredSearchEntityQueryV2({
|
||||
queryHook: useFindManyWorkspaceMembers,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: ['firstName', 'lastName'],
|
||||
fieldNames: ['name.firstName', 'name.lastName'],
|
||||
filter: relationPickerSearchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'firstName',
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: (workspaceMember) => ({
|
||||
entityType: Entity.WorkspaceMember,
|
||||
id: workspaceMember.id,
|
||||
name: workspaceMember.firstName,
|
||||
name:
|
||||
workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName,
|
||||
avatarType: 'rounded',
|
||||
avatarUrl: '',
|
||||
originalEntity: workspaceMember,
|
||||
@ -68,11 +69,11 @@ export const UserPicker = ({
|
||||
<SingleEntitySelect
|
||||
EmptyIcon={IconUserCircle}
|
||||
emptyLabel="No Owner"
|
||||
entitiesToSelect={users.entitiesToSelect}
|
||||
loading={users.loading}
|
||||
entitiesToSelect={workspaceMembers.entitiesToSelect}
|
||||
loading={workspaceMembers.loading}
|
||||
onCancel={onCancel}
|
||||
onEntitySelected={handleEntitySelected}
|
||||
selectedEntity={users.selectedEntities[0]}
|
||||
selectedEntity={workspaceMembers.selectedEntities[0]}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
|
@ -67,7 +67,7 @@ export const ViewBarEffect = () => {
|
||||
useFindManyObjectRecords({
|
||||
skip: !currentViewId,
|
||||
objectNamePlural: 'viewFieldsV2',
|
||||
filter: { view: { eq: currentViewId } },
|
||||
filter: { viewId: { eq: currentViewId } },
|
||||
onCompleted: useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (data: PaginatedObjectTypeResults<ViewField>) => {
|
||||
|
@ -41,7 +41,7 @@ export const useViewFields = (viewScopeId: string) => {
|
||||
variables: {
|
||||
input: {
|
||||
fieldMetadataId: viewField.fieldMetadataId,
|
||||
view: viewIdToPersist,
|
||||
viewId: viewIdToPersist,
|
||||
isVisible: viewField.isVisible,
|
||||
size: viewField.size,
|
||||
position: viewField.position,
|
||||
|
@ -2,8 +2,10 @@ export type ColorScheme = 'Dark' | 'Light' | 'System';
|
||||
|
||||
export type WorkspaceMember = {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
name: {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
};
|
||||
avatarUrl: string | null;
|
||||
locale: string;
|
||||
colorScheme: ColorScheme;
|
||||
|
@ -41,16 +41,18 @@ export const WorkspaceMemberCard = ({
|
||||
<Avatar
|
||||
avatarUrl={workspaceMember.avatarUrl}
|
||||
colorId={workspaceMember.id}
|
||||
placeholder={workspaceMember.firstName || ''}
|
||||
placeholder={workspaceMember.name.firstName || ''}
|
||||
type="squared"
|
||||
size="xl"
|
||||
/>
|
||||
<StyledContent>
|
||||
<OverflowingTextWithTooltip
|
||||
text={workspaceMember.firstName + ' ' + workspaceMember.lastName}
|
||||
text={
|
||||
workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName
|
||||
}
|
||||
/>
|
||||
<StyledEmailText>
|
||||
{workspaceMember.firstName + ' ' + workspaceMember.lastName}
|
||||
{workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName}
|
||||
</StyledEmailText>
|
||||
</StyledContent>
|
||||
|
||||
|
@ -73,8 +73,8 @@ export const CreateProfile = () => {
|
||||
} = useForm<Form>({
|
||||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
firstName: currentWorkspaceMember?.firstName ?? '',
|
||||
lastName: currentWorkspaceMember?.lastName ?? '',
|
||||
firstName: currentWorkspaceMember?.name.firstName ?? '',
|
||||
lastName: currentWorkspaceMember?.name.lastName ?? '',
|
||||
},
|
||||
resolver: zodResolver(validationSchema),
|
||||
});
|
||||
@ -95,8 +95,10 @@ export const CreateProfile = () => {
|
||||
await updateOneObject({
|
||||
idToUpdate: currentWorkspaceMember?.id,
|
||||
input: {
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
name: {
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -104,8 +106,10 @@ export const CreateProfile = () => {
|
||||
(current) =>
|
||||
({
|
||||
...current,
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
name: {
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
},
|
||||
} as any),
|
||||
);
|
||||
|
||||
|
@ -3,10 +3,12 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||
@ -41,6 +43,7 @@ export const CreateWorkspace = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
|
||||
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||
|
||||
@ -68,6 +71,10 @@ export const CreateWorkspace = () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
setCurrentWorkspace({
|
||||
id: result.data?.updateWorkspace?.id ?? '',
|
||||
displayName: data.name,
|
||||
});
|
||||
|
||||
if (result.errors || !result.data?.updateWorkspace) {
|
||||
throw result.errors ?? new Error('Unknown error');
|
||||
@ -82,7 +89,7 @@ export const CreateWorkspace = () => {
|
||||
});
|
||||
}
|
||||
},
|
||||
[enqueueSnackBar, navigate, updateWorkspace],
|
||||
[enqueueSnackBar, navigate, setCurrentWorkspace, updateWorkspace],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
|
@ -44,7 +44,9 @@ export const SettingsDevelopersApiKeys = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [apiKeys, setApiKeys] = useState<Array<ApiFieldItem>>([]);
|
||||
const { registerOptimisticEffect } = useOptimisticEffect('apiKeyV2');
|
||||
const { registerOptimisticEffect } = useOptimisticEffect({
|
||||
objectNameSingular: 'apiKeyV2',
|
||||
});
|
||||
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
|
||||
objectNameSingular: 'apiKeyV2',
|
||||
});
|
||||
|
@ -22,9 +22,9 @@ export const TasksEffect = () => {
|
||||
value: currentWorkspaceMember.id,
|
||||
operand: ViewFilterOperand.Is,
|
||||
displayValue:
|
||||
currentWorkspaceMember.firstName +
|
||||
currentWorkspaceMember.name.firstName +
|
||||
' ' +
|
||||
currentWorkspaceMember.lastName,
|
||||
currentWorkspaceMember.name.lastName,
|
||||
displayAvatarUrl: currentWorkspaceMember.avatarUrl ?? undefined,
|
||||
definition: tasksFilterDefinitions[0],
|
||||
});
|
||||
|
@ -17,11 +17,8 @@ type MockedActivity = Pick<
|
||||
| 'dueAt'
|
||||
| 'completedAt'
|
||||
> & {
|
||||
author: Pick<WorkspaceMember, 'id' | 'firstName' | 'lastName' | 'avatarUrl'>;
|
||||
assignee: Pick<
|
||||
WorkspaceMember,
|
||||
'id' | 'firstName' | 'lastName' | 'avatarUrl'
|
||||
>;
|
||||
author: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
|
||||
assignee: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
|
||||
comments: Comment[];
|
||||
activityTargets: Array<
|
||||
Pick<
|
||||
@ -53,14 +50,18 @@ export const mockedTasks: Array<MockedActivity> = [
|
||||
completedAt: null,
|
||||
author: {
|
||||
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
name: {
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
},
|
||||
avatarUrl: '',
|
||||
},
|
||||
assignee: {
|
||||
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
name: {
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
},
|
||||
avatarUrl: '',
|
||||
},
|
||||
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
@ -82,14 +83,18 @@ export const mockedActivities: Array<MockedActivity> = [
|
||||
completedAt: null,
|
||||
author: {
|
||||
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
name: {
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
},
|
||||
avatarUrl: '',
|
||||
},
|
||||
assignee: {
|
||||
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
name: {
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
},
|
||||
avatarUrl: '',
|
||||
},
|
||||
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
@ -149,14 +154,18 @@ export const mockedActivities: Array<MockedActivity> = [
|
||||
dueAt: '2029-08-26T10:12:42.33625+00:00',
|
||||
author: {
|
||||
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
name: {
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
},
|
||||
avatarUrl: '',
|
||||
},
|
||||
assignee: {
|
||||
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
name: {
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
},
|
||||
avatarUrl: '',
|
||||
},
|
||||
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
|
@ -15,7 +15,7 @@ export enum SeedPersonFieldMetadataIds {
|
||||
|
||||
Email = '20202020-8a96-4e4b-86fd-ea126530e0c1',
|
||||
LinkedinLink = '20202020-dcf6-445a-b543-37e55de43c25',
|
||||
XUrl = '20202020-a3a7-4f63-9303-10226f6055be',
|
||||
XLink = '20202020-a3a7-4f63-9303-10226f6055be',
|
||||
JobTitle = '20202020-3b86-413e-ab56-0ebd1a583ff3',
|
||||
Phone = '20202020-486f-45f9-bbdf-aac18b1831c0',
|
||||
City = '20202020-78f8-4b4c-90ff-86adf77590f5',
|
||||
@ -170,7 +170,7 @@ export const seedPersonFieldMetadata = async (
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
id: SeedPersonFieldMetadataIds.XUrl,
|
||||
id: SeedPersonFieldMetadataIds.XLink,
|
||||
objectMetadataId: SeedObjectMetadataIds.Person,
|
||||
isCustom: false,
|
||||
workspaceId: SeedWorkspaceId,
|
||||
|
@ -16,6 +16,7 @@ export enum SeedViewFieldFieldMetadataIds {
|
||||
Size = '20202020-b9a1-4c2e-a5af-3a6b4fef4af6',
|
||||
Position = '20202020-a4bb-440a-add2-81dbd9a74517',
|
||||
View = '20202020-8788-4508-b771-719807b60e61',
|
||||
ViewForeignKey = '20202020-c852-4c28-b13a-07788c845d6a',
|
||||
}
|
||||
|
||||
export const seedViewFieldFieldMetadata = async (
|
||||
@ -126,13 +127,29 @@ export const seedViewFieldFieldMetadata = async (
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'view',
|
||||
label: 'View Id',
|
||||
targetColumnMap: { value: 'viewId' },
|
||||
targetColumnMap: {},
|
||||
description: 'View Field related view',
|
||||
icon: 'IconLayoutCollage',
|
||||
isNullable: false,
|
||||
isNullable: true,
|
||||
isSystem: false,
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
id: SeedViewFieldFieldMetadataIds.ViewForeignKey,
|
||||
objectMetadataId: SeedObjectMetadataIds.ViewField,
|
||||
isCustom: false,
|
||||
workspaceId: SeedWorkspaceId,
|
||||
isActive: true,
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'viewId',
|
||||
label: 'View ID (foreign key)',
|
||||
targetColumnMap: {},
|
||||
description: 'Foreign key for view',
|
||||
icon: undefined,
|
||||
isNullable: false,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
id: SeedViewFieldFieldMetadataIds.IsVisible,
|
||||
objectMetadataId: SeedObjectMetadataIds.ViewField,
|
||||
|
@ -13,6 +13,7 @@ export enum SeedViewFilterFieldMetadataIds {
|
||||
|
||||
FieldMetadataId = '20202020-78bb-4f2b-a052-260bc8efd694',
|
||||
View = '20202020-65e5-4082-829d-8c634c20e7b5',
|
||||
ViewForeignKey = '20202020-c852-4c28-b13a-07788c845d6b',
|
||||
Operand = '20202020-1d12-465d-ab2c-8af008182730',
|
||||
Value = '20202020-8b37-46ae-86b8-14287ec06802',
|
||||
DisplayValue = '20202020-ed89-4892-83fa-d2b2929c6d52',
|
||||
@ -126,15 +127,29 @@ export const seedViewFilterFieldMetadata = async (
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'viewId',
|
||||
label: 'View Id',
|
||||
targetColumnMap: {
|
||||
value: 'viewId',
|
||||
},
|
||||
targetColumnMap: {},
|
||||
description: 'View Filter related view',
|
||||
icon: 'IconLayoutCollage',
|
||||
isNullable: false,
|
||||
isSystem: false,
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
id: SeedViewFilterFieldMetadataIds.ViewForeignKey,
|
||||
objectMetadataId: SeedObjectMetadataIds.ViewField,
|
||||
isCustom: false,
|
||||
workspaceId: SeedWorkspaceId,
|
||||
isActive: true,
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'viewId',
|
||||
label: 'View ID (foreign key)',
|
||||
targetColumnMap: {},
|
||||
description: 'Foreign key for view',
|
||||
icon: undefined,
|
||||
isNullable: false,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
id: SeedViewFilterFieldMetadataIds.Operand,
|
||||
objectMetadataId: SeedObjectMetadataIds.ViewFilter,
|
||||
|
@ -13,6 +13,7 @@ export enum SeedViewSortFieldMetadataIds {
|
||||
|
||||
FieldMetadataId = '20202020-cb2c-4c8f-a289-c9851b23d064',
|
||||
View = '20202020-f5d0-467f-a3d8-395ba16b8ebf',
|
||||
ViewForeignKey = '20202020-c852-4c28-b13a-07788c845d6c',
|
||||
Direction = '20202020-077e-4451-b1d8-e602c956ebd2',
|
||||
}
|
||||
|
||||
@ -124,15 +125,29 @@ export const seedViewSortFieldMetadata = async (
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'viewId',
|
||||
label: 'View Id',
|
||||
targetColumnMap: {
|
||||
value: 'viewId',
|
||||
},
|
||||
targetColumnMap: {},
|
||||
description: 'View Sort related view',
|
||||
icon: 'IconLayoutCollage',
|
||||
isNullable: false,
|
||||
isSystem: false,
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
id: SeedViewSortFieldMetadataIds.ViewForeignKey,
|
||||
objectMetadataId: SeedObjectMetadataIds.ViewField,
|
||||
isCustom: false,
|
||||
workspaceId: SeedWorkspaceId,
|
||||
isActive: true,
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'viewId',
|
||||
label: 'View ID (foreign key)',
|
||||
targetColumnMap: {},
|
||||
description: 'Foreign key for view',
|
||||
icon: undefined,
|
||||
isNullable: false,
|
||||
isSystem: true,
|
||||
defaultValue: undefined,
|
||||
},
|
||||
{
|
||||
id: SeedViewSortFieldMetadataIds.Direction,
|
||||
objectMetadataId: SeedObjectMetadataIds.ViewSort,
|
||||
|
@ -3,7 +3,6 @@ import { DataSource } from 'typeorm';
|
||||
import { SeedObjectMetadataIds } from 'src/database/typeorm-seeds/metadata/object-metadata';
|
||||
import { SeedWorkspaceId } from 'src/database/seeds/metadata';
|
||||
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import { SeedPersonFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/person';
|
||||
|
||||
const fieldMetadataTableName = 'fieldMetadata';
|
||||
|
||||
@ -12,8 +11,7 @@ export enum SeedWorkspaceMemberFieldMetadataIds {
|
||||
CreatedAt = '20202020-1cbf-4b32-8c33-fbfedcd9afa8',
|
||||
UpdatedAt = '20202020-1ba3-4c24-b2cd-b0789633e8d4',
|
||||
|
||||
FirstName = '20202020-1fa8-4d38-9fa4-0cf696909298',
|
||||
LastName = '20202020-8c37-4163-ba06-1dada334ce3e',
|
||||
Name = '20202020-8c37-4163-ba06-1dada334ce3e',
|
||||
AvatarUrl = '20202020-7ba6-40d5-934b-17146183a212',
|
||||
Locale = '20202020-10f6-4df9-8d6f-a760b65bd800',
|
||||
ColorScheme = '20202020-83f2-4c5f-96b0-0c51ecc160e3',
|
||||
@ -110,7 +108,7 @@ export const seedWorkspaceMemberFieldMetadata = async (
|
||||
},
|
||||
// Scalar fields
|
||||
{
|
||||
id: SeedPersonFieldMetadataIds.Name,
|
||||
id: SeedWorkspaceMemberFieldMetadataIds.Name,
|
||||
objectMetadataId: SeedObjectMetadataIds.WorkspaceMember,
|
||||
isCustom: false,
|
||||
workspaceId: SeedWorkspaceId,
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { SeedViewIds } from 'src/database/typeorm-seeds/workspace/views';
|
||||
import { SeedCompanyFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/company';
|
||||
import { SeedPersonFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/person';
|
||||
import { SeedOpportunityFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/opportunity';
|
||||
|
||||
const tableName = 'viewField';
|
||||
|
||||
@ -21,112 +24,112 @@ export const seedViewFields = async (
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
fieldMetadataId: 'name',
|
||||
fieldMetadataId: SeedCompanyFieldMetadataIds.Name,
|
||||
viewId: SeedViewIds.Company,
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 180,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'domainName',
|
||||
fieldMetadataId: SeedCompanyFieldMetadataIds.DomainName,
|
||||
viewId: SeedViewIds.Company,
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'accountOwner',
|
||||
fieldMetadataId: SeedCompanyFieldMetadataIds.AccountOwner,
|
||||
viewId: SeedViewIds.Company,
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'createdAt',
|
||||
fieldMetadataId: SeedCompanyFieldMetadataIds.CreatedAt,
|
||||
viewId: SeedViewIds.Company,
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'employees',
|
||||
fieldMetadataId: SeedCompanyFieldMetadataIds.Employees,
|
||||
viewId: SeedViewIds.Company,
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'linkedin',
|
||||
fieldMetadataId: SeedCompanyFieldMetadataIds.LinkedinLink,
|
||||
viewId: SeedViewIds.Company,
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 170,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'address',
|
||||
fieldMetadataId: SeedCompanyFieldMetadataIds.Address,
|
||||
viewId: SeedViewIds.Company,
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 170,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'displayName',
|
||||
fieldMetadataId: SeedPersonFieldMetadataIds.Name,
|
||||
viewId: SeedViewIds.Person,
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 210,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'email',
|
||||
fieldMetadataId: SeedPersonFieldMetadataIds.Email,
|
||||
viewId: SeedViewIds.Person,
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'company',
|
||||
fieldMetadataId: SeedPersonFieldMetadataIds.Company,
|
||||
viewId: SeedViewIds.Person,
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'phone',
|
||||
fieldMetadataId: SeedPersonFieldMetadataIds.Phone,
|
||||
viewId: SeedViewIds.Person,
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'createdAt',
|
||||
fieldMetadataId: SeedPersonFieldMetadataIds.CreatedAt,
|
||||
viewId: SeedViewIds.Person,
|
||||
position: 4,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'city',
|
||||
fieldMetadataId: SeedPersonFieldMetadataIds.City,
|
||||
viewId: SeedViewIds.Person,
|
||||
position: 5,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'jobTitle',
|
||||
fieldMetadataId: SeedPersonFieldMetadataIds.JobTitle,
|
||||
viewId: SeedViewIds.Person,
|
||||
position: 6,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'linkedin',
|
||||
fieldMetadataId: SeedPersonFieldMetadataIds.LinkedinLink,
|
||||
viewId: SeedViewIds.Person,
|
||||
position: 7,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'x',
|
||||
fieldMetadataId: SeedPersonFieldMetadataIds.XLink,
|
||||
viewId: SeedViewIds.Person,
|
||||
position: 8,
|
||||
isVisible: true,
|
||||
@ -134,28 +137,28 @@ export const seedViewFields = async (
|
||||
},
|
||||
|
||||
{
|
||||
fieldMetadataId: 'amount',
|
||||
fieldMetadataId: SeedOpportunityFieldMetadataIds.Amount,
|
||||
viewId: SeedViewIds.Opportunity,
|
||||
position: 0,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'closeDate',
|
||||
fieldMetadataId: SeedOpportunityFieldMetadataIds.CloseDate,
|
||||
viewId: SeedViewIds.Opportunity,
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'probability',
|
||||
fieldMetadataId: SeedOpportunityFieldMetadataIds.Probability,
|
||||
viewId: SeedViewIds.Opportunity,
|
||||
position: 2,
|
||||
isVisible: true,
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'pointOfContact',
|
||||
fieldMetadataId: SeedOpportunityFieldMetadataIds.PointOfContact,
|
||||
viewId: SeedViewIds.Opportunity,
|
||||
position: 3,
|
||||
isVisible: true,
|
||||
|
@ -1,33 +0,0 @@
|
||||
import { EntityManager } from 'typeorm';
|
||||
|
||||
export const opportunityPrefillData = async (
|
||||
entityManager: EntityManager,
|
||||
schemaName: string,
|
||||
) => {
|
||||
await entityManager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(`${schemaName}.opportunity`, [
|
||||
'amount',
|
||||
'closeDate',
|
||||
'probability',
|
||||
'pipelineStepId',
|
||||
'pointOfContactId',
|
||||
'personId',
|
||||
'companyId',
|
||||
])
|
||||
.orIgnore()
|
||||
.values([
|
||||
{
|
||||
amount: 100000,
|
||||
closeDate: new Date(),
|
||||
probability: 0.5,
|
||||
pipelineStepId: '73ac09c6-2b90-4874-9e5d-553ea76912ee',
|
||||
pointOfContactId: 'bb757987-ae38-4d16-96ec-b25b595e7bd8',
|
||||
personId: 'a4a2b8e9-7a2b-4b6a-8c8b-7e9a0a0a0a0a',
|
||||
companyId: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408',
|
||||
},
|
||||
])
|
||||
.returning('*')
|
||||
.execute();
|
||||
};
|
@ -5,7 +5,6 @@ import { viewPrefillData } from 'src/workspace/workspace-manager/standard-object
|
||||
import { companyPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/company';
|
||||
import { personPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/person';
|
||||
import { pipelineStepPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/pipeline-step';
|
||||
import { opportunityPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/opportunity';
|
||||
|
||||
export const standardObjectsPrefillData = async (
|
||||
workspaceDataSource: DataSource,
|
||||
@ -28,6 +27,5 @@ export const standardObjectsPrefillData = async (
|
||||
await personPrefillData(entityManager, schemaName);
|
||||
await viewPrefillData(entityManager, schemaName, objectMetadataMap);
|
||||
await pipelineStepPrefillData(entityManager, schemaName);
|
||||
await opportunityPrefillData(entityManager, schemaName);
|
||||
});
|
||||
};
|
||||
|
@ -125,12 +125,23 @@ const personMetadata = {
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'company',
|
||||
label: 'Company',
|
||||
targetColumnMap: {
|
||||
value: 'companyId',
|
||||
},
|
||||
targetColumnMap: {},
|
||||
description: 'Contact’s company',
|
||||
icon: 'IconBuildingSkyscraper',
|
||||
isNullable: false,
|
||||
isSystem: false,
|
||||
},
|
||||
{
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'companyId',
|
||||
label: 'Company ID (foreign key)',
|
||||
targetColumnMap: {},
|
||||
description: 'Foreign key for company',
|
||||
icon: undefined,
|
||||
isNullable: false,
|
||||
isSystem: true,
|
||||
},
|
||||
{
|
||||
isCustom: false,
|
||||
|
@ -73,16 +73,15 @@ const viewFieldMetadata = {
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'view',
|
||||
label: 'View',
|
||||
targetColumnMap: { value: 'viewId' },
|
||||
targetColumnMap: {},
|
||||
description: 'View Field related view',
|
||||
icon: 'IconLayoutCollage',
|
||||
isNullable: false,
|
||||
isNullable: true,
|
||||
},
|
||||
// Temporary hack?
|
||||
{
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: FieldMetadataType.TEXT,
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'viewId',
|
||||
label: 'View Id',
|
||||
targetColumnMap: {
|
||||
|
@ -73,16 +73,15 @@ const viewFilterMetadata = {
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'view',
|
||||
label: 'View',
|
||||
targetColumnMap: { value: 'viewId' },
|
||||
targetColumnMap: {},
|
||||
description: 'View Filter related view',
|
||||
icon: 'IconLayoutCollage',
|
||||
isNullable: false,
|
||||
isNullable: true,
|
||||
},
|
||||
// Temporary hack?
|
||||
{
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: FieldMetadataType.TEXT,
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'viewId',
|
||||
label: 'View Id',
|
||||
targetColumnMap: {
|
||||
|
@ -45,18 +45,15 @@ const viewSortMetadata = {
|
||||
type: FieldMetadataType.RELATION,
|
||||
name: 'view',
|
||||
label: 'View',
|
||||
targetColumnMap: {
|
||||
value: 'viewId',
|
||||
},
|
||||
targetColumnMap: {},
|
||||
description: 'View Sort related view',
|
||||
icon: 'IconLayoutCollage',
|
||||
isNullable: false,
|
||||
isNullable: true,
|
||||
},
|
||||
// Temporary Hack?
|
||||
{
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: FieldMetadataType.TEXT,
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'viewId',
|
||||
label: 'View Id',
|
||||
targetColumnMap: {
|
||||
|
@ -11,19 +11,6 @@ const workspaceMemberMetadata = {
|
||||
isActive: true,
|
||||
isSystem: true,
|
||||
fields: [
|
||||
{
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: FieldMetadataType.TEXT,
|
||||
name: 'firstName',
|
||||
label: 'First name',
|
||||
targetColumnMap: {
|
||||
value: 'firstName',
|
||||
},
|
||||
description: 'Workspace member first name',
|
||||
icon: 'IconCircleUser',
|
||||
isNullable: false,
|
||||
},
|
||||
{
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
@ -93,6 +80,20 @@ const workspaceMemberMetadata = {
|
||||
isNullable: true,
|
||||
isSystem: false,
|
||||
},
|
||||
{
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
type: FieldMetadataType.UUID,
|
||||
name: 'userId',
|
||||
label: 'User Id',
|
||||
targetColumnMap: {
|
||||
value: 'userId',
|
||||
},
|
||||
description: 'Associated User Id',
|
||||
icon: 'IconCircleUsers',
|
||||
isNullable: false,
|
||||
isSystem: false,
|
||||
},
|
||||
// Relations
|
||||
{
|
||||
isCustom: false,
|
||||
|
@ -81,23 +81,21 @@ export class WorkspaceManagerService {
|
||||
workspaceId: string,
|
||||
): Promise<ObjectMetadataEntity[]> {
|
||||
const createdObjectMetadata = await this.objectMetadataService.createMany(
|
||||
Object.values(standardObjectsMetadata).map(
|
||||
(objectMetadata: ObjectMetadataEntity) => ({
|
||||
...objectMetadata,
|
||||
dataSourceId,
|
||||
workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
fields: [...basicFieldsMetadata, ...objectMetadata.fields].map(
|
||||
(field) => ({
|
||||
...field,
|
||||
workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
),
|
||||
Object.values(standardObjectsMetadata).map((objectMetadata: any) => ({
|
||||
...objectMetadata,
|
||||
dataSourceId,
|
||||
workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
fields: [...basicFieldsMetadata, ...objectMetadata.fields].map(
|
||||
(field) => ({
|
||||
...field,
|
||||
workspaceId,
|
||||
isCustom: false,
|
||||
isActive: true,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
);
|
||||
|
||||
await this.relationMetadataService.createMany(
|
||||
|
Loading…
Reference in New Issue
Block a user