Merge commit 'cd3a32e55503dc1e6b9873d812dd401bf7d51045' into context-menu-vertical
This commit is contained in:
@ -55,8 +55,5 @@ Close and reopen your terminal to start using nvm or run the following to use it
|
|||||||
|
|
||||||
### Install Twenty project
|
### Install Twenty project
|
||||||
|
|
||||||
You are ready to install Twenty project. Follow the [Yarn install guide](/developer/local-setup#yarn-install) instructions.
|
You are ready to install Twenty project. Follow the [Yarn install guide](/developer/local-setup#yarn-install-recommended) instructions.
|
||||||
We don't recommend to use Docker on WSL as it adds an extra layer of complexity.
|
We don't recommend to use Docker on WSL as it adds an extra layer of complexity.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -83,7 +83,8 @@ CREATE DATABASE "test";
|
|||||||
Create a `twenty` user with password `twenty`:
|
Create a `twenty` user with password `twenty`:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE USER twenty PASSWORD ‘twenty’
|
CREATE USER twenty PASSWORD 'twenty';
|
||||||
|
ALTER USER twenty CREATEDB;
|
||||||
```
|
```
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|||||||
@ -12,8 +12,8 @@ The easiest way to quickly try the app is to signup on [app.twenty.com](https://
|
|||||||
|
|
||||||
The signup is free.
|
The signup is free.
|
||||||
|
|
||||||
<ThemedImage sources={{light: "../../img/light-sign-in.png", dark:"../../img/dark-sign-in.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
<ThemedImage sources={{light: "/img/light-sign-in.png", dark:"/img/dark-sign-in.png"}} style={{width:'100%', maxWidth:'800px'}}/>
|
||||||
|
|
||||||
## Developer documentation
|
## Developer documentation
|
||||||
|
|
||||||
If you are looking to install the project locally, either to try it or to contribute, check out our [developer guide](../developer/local-setup).
|
If you are looking to install the project locally, either to try it or to contribute, check out our [developer guide](/developer/local-setup).
|
||||||
|
|||||||
@ -21,6 +21,7 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
|
files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
|
||||||
rules: {
|
rules: {
|
||||||
|
'no-control-regex': 0,
|
||||||
'simple-import-sort/imports': [
|
'simple-import-sort/imports': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
|
|||||||
@ -117,8 +117,6 @@ export type ActivityTarget = {
|
|||||||
__typename?: 'ActivityTarget';
|
__typename?: 'ActivityTarget';
|
||||||
activity: Activity;
|
activity: Activity;
|
||||||
activityId: Scalars['String'];
|
activityId: Scalars['String'];
|
||||||
commentableId?: Maybe<Scalars['String']>;
|
|
||||||
commentableType?: Maybe<CommentableType>;
|
|
||||||
company?: Maybe<Company>;
|
company?: Maybe<Company>;
|
||||||
companyId?: Maybe<Scalars['String']>;
|
companyId?: Maybe<Scalars['String']>;
|
||||||
createdAt: Scalars['DateTime'];
|
createdAt: Scalars['DateTime'];
|
||||||
@ -129,8 +127,6 @@ export type ActivityTarget = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ActivityTargetCreateManyActivityInput = {
|
export type ActivityTargetCreateManyActivityInput = {
|
||||||
commentableId?: InputMaybe<Scalars['String']>;
|
|
||||||
commentableType?: InputMaybe<CommentableType>;
|
|
||||||
companyId?: InputMaybe<Scalars['String']>;
|
companyId?: InputMaybe<Scalars['String']>;
|
||||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
@ -145,8 +141,6 @@ export type ActivityTargetCreateManyActivityInputEnvelope = {
|
|||||||
|
|
||||||
export type ActivityTargetCreateManyCompanyInput = {
|
export type ActivityTargetCreateManyCompanyInput = {
|
||||||
activityId: Scalars['String'];
|
activityId: Scalars['String'];
|
||||||
commentableId?: InputMaybe<Scalars['String']>;
|
|
||||||
commentableType?: InputMaybe<CommentableType>;
|
|
||||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
personId?: InputMaybe<Scalars['String']>;
|
personId?: InputMaybe<Scalars['String']>;
|
||||||
@ -160,8 +154,6 @@ export type ActivityTargetCreateManyCompanyInputEnvelope = {
|
|||||||
|
|
||||||
export type ActivityTargetCreateManyPersonInput = {
|
export type ActivityTargetCreateManyPersonInput = {
|
||||||
activityId: Scalars['String'];
|
activityId: Scalars['String'];
|
||||||
commentableId?: InputMaybe<Scalars['String']>;
|
|
||||||
commentableType?: InputMaybe<CommentableType>;
|
|
||||||
companyId?: InputMaybe<Scalars['String']>;
|
companyId?: InputMaybe<Scalars['String']>;
|
||||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
@ -175,8 +167,6 @@ export type ActivityTargetCreateManyPersonInputEnvelope = {
|
|||||||
|
|
||||||
export type ActivityTargetCreateManyWorkspaceInput = {
|
export type ActivityTargetCreateManyWorkspaceInput = {
|
||||||
activityId: Scalars['String'];
|
activityId: Scalars['String'];
|
||||||
commentableId?: InputMaybe<Scalars['String']>;
|
|
||||||
commentableType?: InputMaybe<CommentableType>;
|
|
||||||
companyId?: InputMaybe<Scalars['String']>;
|
companyId?: InputMaybe<Scalars['String']>;
|
||||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
@ -231,8 +221,6 @@ export type ActivityTargetCreateOrConnectWithoutWorkspaceInput = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ActivityTargetCreateWithoutActivityInput = {
|
export type ActivityTargetCreateWithoutActivityInput = {
|
||||||
commentableId?: InputMaybe<Scalars['String']>;
|
|
||||||
commentableType?: InputMaybe<CommentableType>;
|
|
||||||
company?: InputMaybe<CompanyCreateNestedOneWithoutActivityTargetInput>;
|
company?: InputMaybe<CompanyCreateNestedOneWithoutActivityTargetInput>;
|
||||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
@ -242,8 +230,6 @@ export type ActivityTargetCreateWithoutActivityInput = {
|
|||||||
|
|
||||||
export type ActivityTargetCreateWithoutCompanyInput = {
|
export type ActivityTargetCreateWithoutCompanyInput = {
|
||||||
activity: ActivityCreateNestedOneWithoutActivityTargetsInput;
|
activity: ActivityCreateNestedOneWithoutActivityTargetsInput;
|
||||||
commentableId?: InputMaybe<Scalars['String']>;
|
|
||||||
commentableType?: InputMaybe<CommentableType>;
|
|
||||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
person?: InputMaybe<PersonCreateNestedOneWithoutActivityTargetInput>;
|
person?: InputMaybe<PersonCreateNestedOneWithoutActivityTargetInput>;
|
||||||
@ -252,8 +238,6 @@ export type ActivityTargetCreateWithoutCompanyInput = {
|
|||||||
|
|
||||||
export type ActivityTargetCreateWithoutPersonInput = {
|
export type ActivityTargetCreateWithoutPersonInput = {
|
||||||
activity: ActivityCreateNestedOneWithoutActivityTargetsInput;
|
activity: ActivityCreateNestedOneWithoutActivityTargetsInput;
|
||||||
commentableId?: InputMaybe<Scalars['String']>;
|
|
||||||
commentableType?: InputMaybe<CommentableType>;
|
|
||||||
company?: InputMaybe<CompanyCreateNestedOneWithoutActivityTargetInput>;
|
company?: InputMaybe<CompanyCreateNestedOneWithoutActivityTargetInput>;
|
||||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
@ -262,8 +246,6 @@ export type ActivityTargetCreateWithoutPersonInput = {
|
|||||||
|
|
||||||
export type ActivityTargetCreateWithoutWorkspaceInput = {
|
export type ActivityTargetCreateWithoutWorkspaceInput = {
|
||||||
activity: ActivityCreateNestedOneWithoutActivityTargetsInput;
|
activity: ActivityCreateNestedOneWithoutActivityTargetsInput;
|
||||||
commentableId?: InputMaybe<Scalars['String']>;
|
|
||||||
commentableType?: InputMaybe<CommentableType>;
|
|
||||||
company?: InputMaybe<CompanyCreateNestedOneWithoutActivityTargetInput>;
|
company?: InputMaybe<CompanyCreateNestedOneWithoutActivityTargetInput>;
|
||||||
createdAt?: InputMaybe<Scalars['DateTime']>;
|
createdAt?: InputMaybe<Scalars['DateTime']>;
|
||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
@ -286,8 +268,6 @@ export type ActivityTargetScalarWhereInput = {
|
|||||||
NOT?: InputMaybe<Array<ActivityTargetScalarWhereInput>>;
|
NOT?: InputMaybe<Array<ActivityTargetScalarWhereInput>>;
|
||||||
OR?: InputMaybe<Array<ActivityTargetScalarWhereInput>>;
|
OR?: InputMaybe<Array<ActivityTargetScalarWhereInput>>;
|
||||||
activityId?: InputMaybe<StringFilter>;
|
activityId?: InputMaybe<StringFilter>;
|
||||||
commentableId?: InputMaybe<StringNullableFilter>;
|
|
||||||
commentableType?: InputMaybe<EnumCommentableTypeNullableFilter>;
|
|
||||||
companyId?: InputMaybe<StringNullableFilter>;
|
companyId?: InputMaybe<StringNullableFilter>;
|
||||||
createdAt?: InputMaybe<DateTimeFilter>;
|
createdAt?: InputMaybe<DateTimeFilter>;
|
||||||
id?: InputMaybe<StringFilter>;
|
id?: InputMaybe<StringFilter>;
|
||||||
@ -345,8 +325,6 @@ export type ActivityTargetWhereInput = {
|
|||||||
OR?: InputMaybe<Array<ActivityTargetWhereInput>>;
|
OR?: InputMaybe<Array<ActivityTargetWhereInput>>;
|
||||||
activity?: InputMaybe<ActivityRelationFilter>;
|
activity?: InputMaybe<ActivityRelationFilter>;
|
||||||
activityId?: InputMaybe<StringFilter>;
|
activityId?: InputMaybe<StringFilter>;
|
||||||
commentableId?: InputMaybe<StringNullableFilter>;
|
|
||||||
commentableType?: InputMaybe<EnumCommentableTypeNullableFilter>;
|
|
||||||
company?: InputMaybe<CompanyRelationFilter>;
|
company?: InputMaybe<CompanyRelationFilter>;
|
||||||
companyId?: InputMaybe<StringNullableFilter>;
|
companyId?: InputMaybe<StringNullableFilter>;
|
||||||
createdAt?: InputMaybe<DateTimeFilter>;
|
createdAt?: InputMaybe<DateTimeFilter>;
|
||||||
@ -632,11 +610,6 @@ export type CommentWhereUniqueInput = {
|
|||||||
id?: InputMaybe<Scalars['String']>;
|
id?: InputMaybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum CommentableType {
|
|
||||||
Company = 'Company',
|
|
||||||
Person = 'Person'
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Company = {
|
export type Company = {
|
||||||
__typename?: 'Company';
|
__typename?: 'Company';
|
||||||
ActivityTarget?: Maybe<Array<ActivityTarget>>;
|
ActivityTarget?: Maybe<Array<ActivityTarget>>;
|
||||||
@ -837,13 +810,6 @@ export type EnumColorSchemeFilter = {
|
|||||||
notIn?: InputMaybe<Array<ColorScheme>>;
|
notIn?: InputMaybe<Array<ColorScheme>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EnumCommentableTypeNullableFilter = {
|
|
||||||
equals?: InputMaybe<CommentableType>;
|
|
||||||
in?: InputMaybe<Array<CommentableType>>;
|
|
||||||
not?: InputMaybe<NestedEnumCommentableTypeNullableFilter>;
|
|
||||||
notIn?: InputMaybe<Array<CommentableType>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type EnumPipelineProgressableTypeFilter = {
|
export type EnumPipelineProgressableTypeFilter = {
|
||||||
equals?: InputMaybe<PipelineProgressableType>;
|
equals?: InputMaybe<PipelineProgressableType>;
|
||||||
in?: InputMaybe<Array<PipelineProgressableType>>;
|
in?: InputMaybe<Array<PipelineProgressableType>>;
|
||||||
@ -1286,13 +1252,6 @@ export type NestedEnumColorSchemeFilter = {
|
|||||||
notIn?: InputMaybe<Array<ColorScheme>>;
|
notIn?: InputMaybe<Array<ColorScheme>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NestedEnumCommentableTypeNullableFilter = {
|
|
||||||
equals?: InputMaybe<CommentableType>;
|
|
||||||
in?: InputMaybe<Array<CommentableType>>;
|
|
||||||
not?: InputMaybe<NestedEnumCommentableTypeNullableFilter>;
|
|
||||||
notIn?: InputMaybe<Array<CommentableType>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type NestedEnumPipelineProgressableTypeFilter = {
|
export type NestedEnumPipelineProgressableTypeFilter = {
|
||||||
equals?: InputMaybe<PipelineProgressableType>;
|
equals?: InputMaybe<PipelineProgressableType>;
|
||||||
in?: InputMaybe<Array<PipelineProgressableType>>;
|
in?: InputMaybe<Array<PipelineProgressableType>>;
|
||||||
@ -2694,7 +2653,9 @@ export type CreateActivityMutationVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type CreateActivityMutation = { __typename?: 'Mutation', createOneActivity: { __typename?: 'Activity', id: string, createdAt: string, updatedAt: string, authorId: string, type: ActivityType, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, createdAt: string, updatedAt: string, activityId: string, commentableType?: CommentableType | null, commentableId?: string | null, companyId?: string | null, personId?: string | null }> | null, comments?: Array<{ __typename?: 'Comment', id: string, createdAt: string, updatedAt: string, body: string, author: { __typename?: 'User', id: string } }> | null } };
|
export type CreateActivityMutation = { __typename?: 'Mutation', createOneActivity: { __typename?: 'Activity', id: string, createdAt: string, updatedAt: string, authorId: string, type: ActivityType, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, createdAt: string, updatedAt: string, activityId: string, companyId?: string | null, personId?: string | null }> | null, comments?: Array<{ __typename?: 'Comment', id: string, createdAt: string, updatedAt: string, body: string, author: { __typename?: 'User', id: string } }> | null } };
|
||||||
|
|
||||||
|
export type ActivityQueryFragmentFragment = { __typename?: 'Activity', id: string, createdAt: string, title?: string | null, body?: string | null, type: ActivityType, completedAt?: string | null, dueAt?: string | null, assignee?: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string, avatarUrl?: string | null } | null, author: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string }, comments?: Array<{ __typename?: 'Comment', id: string, body: string, createdAt: string, updatedAt: string, author: { __typename?: 'User', id: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null } }> | null, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, companyId?: string | null, personId?: string | null, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null, person?: { __typename?: 'Person', id: string, displayName: string, avatarUrl?: string | null } | null }> | null };
|
||||||
|
|
||||||
export type GetActivitiesByTargetsQueryVariables = Exact<{
|
export type GetActivitiesByTargetsQueryVariables = Exact<{
|
||||||
activityTargetIds: Array<Scalars['String']> | Scalars['String'];
|
activityTargetIds: Array<Scalars['String']> | Scalars['String'];
|
||||||
@ -2702,7 +2663,7 @@ export type GetActivitiesByTargetsQueryVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type GetActivitiesByTargetsQuery = { __typename?: 'Query', findManyActivities: Array<{ __typename?: 'Activity', id: string, createdAt: string, title?: string | null, body?: string | null, type: ActivityType, completedAt?: string | null, dueAt?: string | null, assignee?: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string, avatarUrl?: string | null } | null, author: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string }, comments?: Array<{ __typename?: 'Comment', id: string, body: string, createdAt: string, updatedAt: string, author: { __typename?: 'User', id: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null } }> | null, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, commentableType?: CommentableType | null, commentableId?: string | null, companyId?: string | null, personId?: string | null }> | null }> };
|
export type GetActivitiesByTargetsQuery = { __typename?: 'Query', findManyActivities: Array<{ __typename?: 'Activity', id: string, createdAt: string, title?: string | null, body?: string | null, type: ActivityType, completedAt?: string | null, dueAt?: string | null, assignee?: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string, avatarUrl?: string | null } | null, author: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string }, comments?: Array<{ __typename?: 'Comment', id: string, body: string, createdAt: string, updatedAt: string, author: { __typename?: 'User', id: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null } }> | null, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, companyId?: string | null, personId?: string | null, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null, person?: { __typename?: 'Person', id: string, displayName: string, avatarUrl?: string | null } | null }> | null }> };
|
||||||
|
|
||||||
export type GetActivitiesQueryVariables = Exact<{
|
export type GetActivitiesQueryVariables = Exact<{
|
||||||
where: ActivityWhereInput;
|
where: ActivityWhereInput;
|
||||||
@ -2710,14 +2671,14 @@ export type GetActivitiesQueryVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type GetActivitiesQuery = { __typename?: 'Query', findManyActivities: Array<{ __typename?: 'Activity', id: string, createdAt: string, title?: string | null, body?: string | null, type: ActivityType, completedAt?: string | null, dueAt?: string | null, assignee?: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string, avatarUrl?: string | null } | null, author: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string }, comments?: Array<{ __typename?: 'Comment', id: string }> | null, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, commentableType?: CommentableType | null, commentableId?: string | null, companyId?: string | null, personId?: string | null }> | null }> };
|
export type GetActivitiesQuery = { __typename?: 'Query', findManyActivities: Array<{ __typename?: 'Activity', id: string, createdAt: string, title?: string | null, body?: string | null, type: ActivityType, completedAt?: string | null, dueAt?: string | null, assignee?: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string, avatarUrl?: string | null } | null, author: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string }, comments?: Array<{ __typename?: 'Comment', id: string, body: string, createdAt: string, updatedAt: string, author: { __typename?: 'User', id: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null } }> | null, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, companyId?: string | null, personId?: string | null, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null, person?: { __typename?: 'Person', id: string, displayName: string, avatarUrl?: string | null } | null }> | null }> };
|
||||||
|
|
||||||
export type GetActivityQueryVariables = Exact<{
|
export type GetActivityQueryVariables = Exact<{
|
||||||
activityId: Scalars['String'];
|
activityId: Scalars['String'];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type GetActivityQuery = { __typename?: 'Query', findManyActivities: Array<{ __typename?: 'Activity', id: string, createdAt: string, body?: string | null, title?: string | null, type: ActivityType, completedAt?: string | null, dueAt?: string | null, assignee?: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string, avatarUrl?: string | null } | null, author: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string }, comments?: Array<{ __typename?: 'Comment', id: string, body: string, createdAt: string, updatedAt: string, author: { __typename?: 'User', id: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null } }> | null, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, commentableType?: CommentableType | null, commentableId?: string | null, companyId?: string | null, personId?: string | null }> | null }> };
|
export type GetActivityQuery = { __typename?: 'Query', findManyActivities: Array<{ __typename?: 'Activity', id: string, createdAt: string, title?: string | null, body?: string | null, type: ActivityType, completedAt?: string | null, dueAt?: string | null, assignee?: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string, avatarUrl?: string | null } | null, author: { __typename?: 'User', id: string, firstName?: string | null, lastName?: string | null, displayName: string }, comments?: Array<{ __typename?: 'Comment', id: string, body: string, createdAt: string, updatedAt: string, author: { __typename?: 'User', id: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null } }> | null, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, companyId?: string | null, personId?: string | null, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null, person?: { __typename?: 'Person', id: string, displayName: string, avatarUrl?: string | null } | null }> | null }> };
|
||||||
|
|
||||||
export type AddActivityTargetsOnActivityMutationVariables = Exact<{
|
export type AddActivityTargetsOnActivityMutationVariables = Exact<{
|
||||||
activityId: Scalars['String'];
|
activityId: Scalars['String'];
|
||||||
@ -2725,7 +2686,7 @@ export type AddActivityTargetsOnActivityMutationVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type AddActivityTargetsOnActivityMutation = { __typename?: 'Mutation', updateOneActivity: { __typename?: 'Activity', id: string, createdAt: string, updatedAt: string, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, createdAt: string, updatedAt: string, commentableType?: CommentableType | null, commentableId?: string | null, companyId?: string | null, personId?: string | null }> | null } };
|
export type AddActivityTargetsOnActivityMutation = { __typename?: 'Mutation', updateOneActivity: { __typename?: 'Activity', id: string, createdAt: string, updatedAt: string, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, createdAt: string, updatedAt: string, companyId?: string | null, personId?: string | null }> | null } };
|
||||||
|
|
||||||
export type RemoveActivityTargetsOnActivityMutationVariables = Exact<{
|
export type RemoveActivityTargetsOnActivityMutationVariables = Exact<{
|
||||||
activityId: Scalars['String'];
|
activityId: Scalars['String'];
|
||||||
@ -2733,7 +2694,7 @@ export type RemoveActivityTargetsOnActivityMutationVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type RemoveActivityTargetsOnActivityMutation = { __typename?: 'Mutation', updateOneActivity: { __typename?: 'Activity', id: string, createdAt: string, updatedAt: string, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, createdAt: string, updatedAt: string, commentableType?: CommentableType | null, commentableId?: string | null, companyId?: string | null, personId?: string | null }> | null } };
|
export type RemoveActivityTargetsOnActivityMutation = { __typename?: 'Mutation', updateOneActivity: { __typename?: 'Activity', id: string, createdAt: string, updatedAt: string, activityTargets?: Array<{ __typename?: 'ActivityTarget', id: string, createdAt: string, updatedAt: string, companyId?: string | null, personId?: string | null }> | null } };
|
||||||
|
|
||||||
export type DeleteActivityMutationVariables = Exact<{
|
export type DeleteActivityMutationVariables = Exact<{
|
||||||
activityId: Scalars['String'];
|
activityId: Scalars['String'];
|
||||||
@ -2833,22 +2794,22 @@ export type GetCompanyQueryVariables = Exact<{
|
|||||||
|
|
||||||
export type GetCompanyQuery = { __typename?: 'Query', findUniqueCompany: { __typename?: 'Company', id: string, domainName: string, name: string, createdAt: string, address: string, linkedinUrl?: string | null, employees?: number | null, _activityCount: number, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null, Favorite?: Array<{ __typename?: 'Favorite', id: string, person?: { __typename?: 'Person', id: string } | null, company?: { __typename?: 'Company', id: string } | null }> | null } };
|
export type GetCompanyQuery = { __typename?: 'Query', findUniqueCompany: { __typename?: 'Company', id: string, domainName: string, name: string, createdAt: string, address: string, linkedinUrl?: string | null, employees?: number | null, _activityCount: number, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null, Favorite?: Array<{ __typename?: 'Favorite', id: string, person?: { __typename?: 'Person', id: string } | null, company?: { __typename?: 'Company', id: string } | null }> | null } };
|
||||||
|
|
||||||
|
export type CompanyFieldsFragmentFragment = { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null };
|
||||||
|
|
||||||
export type UpdateOneCompanyMutationVariables = Exact<{
|
export type UpdateOneCompanyMutationVariables = Exact<{
|
||||||
where: CompanyWhereUniqueInput;
|
where: CompanyWhereUniqueInput;
|
||||||
data: CompanyUpdateInput;
|
data: CompanyUpdateInput;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type UpdateOneCompanyMutation = { __typename?: 'Mutation', updateOneCompany?: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null } | null } | null };
|
export type UpdateOneCompanyMutation = { __typename?: 'Mutation', updateOneCompany?: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null } | null };
|
||||||
|
|
||||||
export type InsertCompanyFragmentFragment = { __typename?: 'Company', domainName: string, address: string, id: string, name: string, createdAt: string };
|
|
||||||
|
|
||||||
export type InsertOneCompanyMutationVariables = Exact<{
|
export type InsertOneCompanyMutationVariables = Exact<{
|
||||||
data: CompanyCreateInput;
|
data: CompanyCreateInput;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type InsertOneCompanyMutation = { __typename?: 'Mutation', createOneCompany: { __typename?: 'Company', domainName: string, address: string, id: string, name: string, createdAt: string } };
|
export type InsertOneCompanyMutation = { __typename?: 'Mutation', createOneCompany: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null } };
|
||||||
|
|
||||||
export type DeleteManyCompaniesMutationVariables = Exact<{
|
export type DeleteManyCompaniesMutationVariables = Exact<{
|
||||||
ids?: InputMaybe<Array<Scalars['String']> | Scalars['String']>;
|
ids?: InputMaybe<Array<Scalars['String']> | Scalars['String']>;
|
||||||
@ -3073,7 +3034,7 @@ export type SearchCompanyQueryVariables = Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type SearchCompanyQuery = { __typename?: 'Query', searchResults: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> };
|
export type SearchCompanyQuery = { __typename?: 'Query', searchResults: Array<{ __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null }> };
|
||||||
|
|
||||||
export type SearchActivityQueryVariables = Exact<{
|
export type SearchActivityQueryVariables = Exact<{
|
||||||
where?: InputMaybe<ActivityWhereInput>;
|
where?: InputMaybe<ActivityWhereInput>;
|
||||||
@ -3128,13 +3089,6 @@ export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }
|
|||||||
|
|
||||||
export type DeleteUserAccountMutation = { __typename?: 'Mutation', deleteUserAccount: { __typename?: 'User', id: string } };
|
export type DeleteUserAccountMutation = { __typename?: 'Mutation', deleteUserAccount: { __typename?: 'User', id: string } };
|
||||||
|
|
||||||
export type CreateViewFieldMutationVariables = Exact<{
|
|
||||||
data: ViewFieldCreateInput;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type CreateViewFieldMutation = { __typename?: 'Mutation', createOneViewField: { __typename?: 'ViewField', id: string, fieldName: string, isVisible: boolean, sizeInPx: number, index: number } };
|
|
||||||
|
|
||||||
export type CreateViewFieldsMutationVariables = Exact<{
|
export type CreateViewFieldsMutationVariables = Exact<{
|
||||||
data: Array<ViewFieldCreateManyInput> | ViewFieldCreateManyInput;
|
data: Array<ViewFieldCreateManyInput> | ViewFieldCreateManyInput;
|
||||||
}>;
|
}>;
|
||||||
@ -3230,6 +3184,58 @@ export type DeleteCurrentWorkspaceMutationVariables = Exact<{ [key: string]: nev
|
|||||||
|
|
||||||
export type DeleteCurrentWorkspaceMutation = { __typename?: 'Mutation', deleteCurrentWorkspace: { __typename?: 'Workspace', id: string } };
|
export type DeleteCurrentWorkspaceMutation = { __typename?: 'Mutation', deleteCurrentWorkspace: { __typename?: 'Workspace', id: string } };
|
||||||
|
|
||||||
|
export const ActivityQueryFragmentFragmentDoc = gql`
|
||||||
|
fragment ActivityQueryFragment on Activity {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
title
|
||||||
|
body
|
||||||
|
type
|
||||||
|
completedAt
|
||||||
|
dueAt
|
||||||
|
assignee {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
displayName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
author {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
displayName
|
||||||
|
}
|
||||||
|
comments {
|
||||||
|
id
|
||||||
|
body
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
author {
|
||||||
|
id
|
||||||
|
displayName
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activityTargets {
|
||||||
|
id
|
||||||
|
companyId
|
||||||
|
personId
|
||||||
|
company {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
domainName
|
||||||
|
}
|
||||||
|
person {
|
||||||
|
id
|
||||||
|
displayName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
export const ActivityUpdatePartsFragmentDoc = gql`
|
export const ActivityUpdatePartsFragmentDoc = gql`
|
||||||
fragment ActivityUpdateParts on Activity {
|
fragment ActivityUpdateParts on Activity {
|
||||||
id
|
id
|
||||||
@ -3246,13 +3252,21 @@ export const ActivityUpdatePartsFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const InsertCompanyFragmentFragmentDoc = gql`
|
export const CompanyFieldsFragmentFragmentDoc = gql`
|
||||||
fragment InsertCompanyFragment on Company {
|
fragment CompanyFieldsFragment on Company {
|
||||||
domainName
|
accountOwner {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
displayName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
address
|
address
|
||||||
|
createdAt
|
||||||
|
domainName
|
||||||
|
employees
|
||||||
|
linkedinUrl
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
createdAt
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const InsertPersonFragmentFragmentDoc = gql`
|
export const InsertPersonFragmentFragmentDoc = gql`
|
||||||
@ -3326,8 +3340,6 @@ export const CreateActivityDocument = gql`
|
|||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
activityId
|
activityId
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
companyId
|
||||||
personId
|
personId
|
||||||
}
|
}
|
||||||
@ -3373,51 +3385,12 @@ export const GetActivitiesByTargetsDocument = gql`
|
|||||||
query GetActivitiesByTargets($activityTargetIds: [String!]!, $orderBy: [ActivityOrderByWithRelationInput!]) {
|
query GetActivitiesByTargets($activityTargetIds: [String!]!, $orderBy: [ActivityOrderByWithRelationInput!]) {
|
||||||
findManyActivities(
|
findManyActivities(
|
||||||
orderBy: $orderBy
|
orderBy: $orderBy
|
||||||
where: {activityTargets: {some: {commentableId: {in: $activityTargetIds}}}}
|
where: {activityTargets: {some: {OR: [{personId: {in: $activityTargetIds}}, {companyId: {in: $activityTargetIds}}]}}}
|
||||||
) {
|
) {
|
||||||
id
|
...ActivityQueryFragment
|
||||||
createdAt
|
|
||||||
title
|
|
||||||
body
|
|
||||||
type
|
|
||||||
completedAt
|
|
||||||
dueAt
|
|
||||||
assignee {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
}
|
|
||||||
comments {
|
|
||||||
id
|
|
||||||
body
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
displayName
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
activityTargets {
|
|
||||||
id
|
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
|
||||||
personId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
${ActivityQueryFragmentFragmentDoc}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useGetActivitiesByTargetsQuery__
|
* __useGetActivitiesByTargetsQuery__
|
||||||
@ -3450,39 +3423,10 @@ export type GetActivitiesByTargetsQueryResult = Apollo.QueryResult<GetActivities
|
|||||||
export const GetActivitiesDocument = gql`
|
export const GetActivitiesDocument = gql`
|
||||||
query GetActivities($where: ActivityWhereInput!, $orderBy: [ActivityOrderByWithRelationInput!]) {
|
query GetActivities($where: ActivityWhereInput!, $orderBy: [ActivityOrderByWithRelationInput!]) {
|
||||||
findManyActivities(orderBy: $orderBy, where: $where) {
|
findManyActivities(orderBy: $orderBy, where: $where) {
|
||||||
id
|
...ActivityQueryFragment
|
||||||
createdAt
|
|
||||||
title
|
|
||||||
body
|
|
||||||
type
|
|
||||||
completedAt
|
|
||||||
dueAt
|
|
||||||
assignee {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
}
|
|
||||||
comments {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
activityTargets {
|
|
||||||
id
|
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
|
||||||
personId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
${ActivityQueryFragmentFragmentDoc}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useGetActivitiesQuery__
|
* __useGetActivitiesQuery__
|
||||||
@ -3515,49 +3459,10 @@ export type GetActivitiesQueryResult = Apollo.QueryResult<GetActivitiesQuery, Ge
|
|||||||
export const GetActivityDocument = gql`
|
export const GetActivityDocument = gql`
|
||||||
query GetActivity($activityId: String!) {
|
query GetActivity($activityId: String!) {
|
||||||
findManyActivities(where: {id: {equals: $activityId}}) {
|
findManyActivities(where: {id: {equals: $activityId}}) {
|
||||||
id
|
...ActivityQueryFragment
|
||||||
createdAt
|
|
||||||
body
|
|
||||||
title
|
|
||||||
type
|
|
||||||
completedAt
|
|
||||||
dueAt
|
|
||||||
assignee {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
}
|
|
||||||
comments {
|
|
||||||
id
|
|
||||||
body
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
displayName
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
activityTargets {
|
|
||||||
id
|
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
|
||||||
personId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
${ActivityQueryFragmentFragmentDoc}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useGetActivityQuery__
|
* __useGetActivityQuery__
|
||||||
@ -3599,8 +3504,6 @@ export const AddActivityTargetsOnActivityDocument = gql`
|
|||||||
id
|
id
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
companyId
|
||||||
personId
|
personId
|
||||||
}
|
}
|
||||||
@ -3647,8 +3550,6 @@ export const RemoveActivityTargetsOnActivityDocument = gql`
|
|||||||
id
|
id
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
companyId
|
||||||
personId
|
personId
|
||||||
}
|
}
|
||||||
@ -4263,23 +4164,10 @@ export type GetCompanyQueryResult = Apollo.QueryResult<GetCompanyQuery, GetCompa
|
|||||||
export const UpdateOneCompanyDocument = gql`
|
export const UpdateOneCompanyDocument = gql`
|
||||||
mutation UpdateOneCompany($where: CompanyWhereUniqueInput!, $data: CompanyUpdateInput!) {
|
mutation UpdateOneCompany($where: CompanyWhereUniqueInput!, $data: CompanyUpdateInput!) {
|
||||||
updateOneCompany(data: $data, where: $where) {
|
updateOneCompany(data: $data, where: $where) {
|
||||||
accountOwner {
|
...CompanyFieldsFragment
|
||||||
id
|
|
||||||
email
|
|
||||||
displayName
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
}
|
|
||||||
address
|
|
||||||
createdAt
|
|
||||||
domainName
|
|
||||||
employees
|
|
||||||
linkedinUrl
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
${CompanyFieldsFragmentFragmentDoc}`;
|
||||||
export type UpdateOneCompanyMutationFn = Apollo.MutationFunction<UpdateOneCompanyMutation, UpdateOneCompanyMutationVariables>;
|
export type UpdateOneCompanyMutationFn = Apollo.MutationFunction<UpdateOneCompanyMutation, UpdateOneCompanyMutationVariables>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4310,10 +4198,10 @@ export type UpdateOneCompanyMutationOptions = Apollo.BaseMutationOptions<UpdateO
|
|||||||
export const InsertOneCompanyDocument = gql`
|
export const InsertOneCompanyDocument = gql`
|
||||||
mutation InsertOneCompany($data: CompanyCreateInput!) {
|
mutation InsertOneCompany($data: CompanyCreateInput!) {
|
||||||
createOneCompany(data: $data) {
|
createOneCompany(data: $data) {
|
||||||
...InsertCompanyFragment
|
...CompanyFieldsFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${InsertCompanyFragmentFragmentDoc}`;
|
${CompanyFieldsFragmentFragmentDoc}`;
|
||||||
export type InsertOneCompanyMutationFn = Apollo.MutationFunction<InsertOneCompanyMutation, InsertOneCompanyMutationVariables>;
|
export type InsertOneCompanyMutationFn = Apollo.MutationFunction<InsertOneCompanyMutation, InsertOneCompanyMutationVariables>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -5489,12 +5377,10 @@ export type EmptyQueryQueryResult = Apollo.QueryResult<EmptyQueryQuery, EmptyQue
|
|||||||
export const SearchCompanyDocument = gql`
|
export const SearchCompanyDocument = gql`
|
||||||
query SearchCompany($where: CompanyWhereInput, $limit: Int, $orderBy: [CompanyOrderByWithRelationInput!]) {
|
query SearchCompany($where: CompanyWhereInput, $limit: Int, $orderBy: [CompanyOrderByWithRelationInput!]) {
|
||||||
searchResults: findManyCompany(where: $where, take: $limit, orderBy: $orderBy) {
|
searchResults: findManyCompany(where: $where, take: $limit, orderBy: $orderBy) {
|
||||||
id
|
...CompanyFieldsFragment
|
||||||
name
|
|
||||||
domainName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
${CompanyFieldsFragmentFragmentDoc}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useSearchCompanyQuery__
|
* __useSearchCompanyQuery__
|
||||||
@ -5848,43 +5734,6 @@ export function useDeleteUserAccountMutation(baseOptions?: Apollo.MutationHookOp
|
|||||||
export type DeleteUserAccountMutationHookResult = ReturnType<typeof useDeleteUserAccountMutation>;
|
export type DeleteUserAccountMutationHookResult = ReturnType<typeof useDeleteUserAccountMutation>;
|
||||||
export type DeleteUserAccountMutationResult = Apollo.MutationResult<DeleteUserAccountMutation>;
|
export type DeleteUserAccountMutationResult = Apollo.MutationResult<DeleteUserAccountMutation>;
|
||||||
export type DeleteUserAccountMutationOptions = Apollo.BaseMutationOptions<DeleteUserAccountMutation, DeleteUserAccountMutationVariables>;
|
export type DeleteUserAccountMutationOptions = Apollo.BaseMutationOptions<DeleteUserAccountMutation, DeleteUserAccountMutationVariables>;
|
||||||
export const CreateViewFieldDocument = gql`
|
|
||||||
mutation CreateViewField($data: ViewFieldCreateInput!) {
|
|
||||||
createOneViewField(data: $data) {
|
|
||||||
id
|
|
||||||
fieldName
|
|
||||||
isVisible
|
|
||||||
sizeInPx
|
|
||||||
index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
export type CreateViewFieldMutationFn = Apollo.MutationFunction<CreateViewFieldMutation, CreateViewFieldMutationVariables>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useCreateViewFieldMutation__
|
|
||||||
*
|
|
||||||
* To run a mutation, you first call `useCreateViewFieldMutation` within a React component and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useCreateViewFieldMutation` returns a tuple that includes:
|
|
||||||
* - A mutate function that you can call at any time to execute the mutation
|
|
||||||
* - An object with fields that represent the current status of the mutation's execution
|
|
||||||
*
|
|
||||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* const [createViewFieldMutation, { data, loading, error }] = useCreateViewFieldMutation({
|
|
||||||
* variables: {
|
|
||||||
* data: // value for 'data'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useCreateViewFieldMutation(baseOptions?: Apollo.MutationHookOptions<CreateViewFieldMutation, CreateViewFieldMutationVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useMutation<CreateViewFieldMutation, CreateViewFieldMutationVariables>(CreateViewFieldDocument, options);
|
|
||||||
}
|
|
||||||
export type CreateViewFieldMutationHookResult = ReturnType<typeof useCreateViewFieldMutation>;
|
|
||||||
export type CreateViewFieldMutationResult = Apollo.MutationResult<CreateViewFieldMutation>;
|
|
||||||
export type CreateViewFieldMutationOptions = Apollo.BaseMutationOptions<CreateViewFieldMutation, CreateViewFieldMutationVariables>;
|
|
||||||
export const CreateViewFieldsDocument = gql`
|
export const CreateViewFieldsDocument = gql`
|
||||||
mutation CreateViewFields($data: [ViewFieldCreateManyInput!]!) {
|
mutation CreateViewFields($data: [ViewFieldCreateManyInput!]!) {
|
||||||
createManyViewField(data: $data) {
|
createManyViewField(data: $data) {
|
||||||
|
|||||||
@ -72,9 +72,7 @@ type OwnProps = {
|
|||||||
'id' | 'firstName' | 'lastName' | 'displayName'
|
'id' | 'firstName' | 'lastName' | 'displayName'
|
||||||
> | null;
|
> | null;
|
||||||
} & {
|
} & {
|
||||||
activityTargets?: Array<
|
activityTargets?: Array<Pick<ActivityTarget, 'id'>> | null;
|
||||||
Pick<ActivityTarget, 'id' | 'commentableId' | 'commentableType'>
|
|
||||||
> | null;
|
|
||||||
};
|
};
|
||||||
showComment?: boolean;
|
showComment?: boolean;
|
||||||
autoFillTitle?: boolean;
|
autoFillTitle?: boolean;
|
||||||
|
|||||||
@ -1,251 +0,0 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import {
|
|
||||||
autoUpdate,
|
|
||||||
flip,
|
|
||||||
offset,
|
|
||||||
size,
|
|
||||||
useFloating,
|
|
||||||
} from '@floating-ui/react';
|
|
||||||
|
|
||||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
|
||||||
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
|
|
||||||
import { PersonChip } from '@/people/components/PersonChip';
|
|
||||||
import { useFilteredSearchPeopleQuery } from '@/people/queries';
|
|
||||||
import { MultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect';
|
|
||||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { Activity, ActivityTarget, CommentableType } from '~/generated/graphql';
|
|
||||||
import { assertNotNull } from '~/utils/assert';
|
|
||||||
|
|
||||||
import { useHandleCheckableActivityTargetChange } from '../hooks/useHandleCheckableActivityTargetChange';
|
|
||||||
import { flatMapAndSortEntityForSelectArrayOfArrayByName } from '../utils/flatMapAndSortEntityForSelectArrayByName';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
activity?: Pick<Activity, 'id'> & {
|
|
||||||
activityTargets: Array<
|
|
||||||
Pick<
|
|
||||||
ActivityTarget,
|
|
||||||
'id' | 'commentableId' | 'commentableType' | 'companyId' | 'personId'
|
|
||||||
>
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
|
||||||
align-items: flex-start;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledRelationContainer = styled.div`
|
|
||||||
--horizontal-padding: ${({ theme }) => theme.spacing(1)};
|
|
||||||
--vertical-padding: ${({ theme }) => theme.spacing(1.5)};
|
|
||||||
|
|
||||||
border: 1px solid transparent;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: ${({ theme }) => theme.background.secondary};
|
|
||||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
|
||||||
}
|
|
||||||
|
|
||||||
min-height: calc(32px - 2 * var(--vertical-padding));
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
padding: var(--vertical-padding) var(--horizontal-padding);
|
|
||||||
width: calc(100% - 2 * var(--horizontal-padding));
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledMenuWrapper = styled.div`
|
|
||||||
z-index: ${({ theme }) => theme.lastLayerZIndex};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export function ActivityRelationPicker({ activity }: OwnProps) {
|
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
||||||
const [searchFilter, setSearchFilter] = useState('');
|
|
||||||
const [selectedEntityIds, setSelectedEntityIds] = useState<
|
|
||||||
Record<string, boolean>
|
|
||||||
>({});
|
|
||||||
const {
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope,
|
|
||||||
goBackToPreviousHotkeyScope,
|
|
||||||
} = usePreviousHotkeyScope();
|
|
||||||
|
|
||||||
const initialPeopleIds = useMemo(
|
|
||||||
() =>
|
|
||||||
activity?.activityTargets
|
|
||||||
?.filter((relation) => relation.commentableType === 'Person')
|
|
||||||
.map((relation) => relation.personId || relation.commentableId)
|
|
||||||
.filter(assertNotNull) ?? [],
|
|
||||||
[activity?.activityTargets],
|
|
||||||
);
|
|
||||||
|
|
||||||
const initialCompanyIds = useMemo(
|
|
||||||
() =>
|
|
||||||
activity?.activityTargets
|
|
||||||
?.filter((relation) => relation.commentableType === 'Company')
|
|
||||||
.map((relation) => relation.companyId || relation.commentableId)
|
|
||||||
.filter(assertNotNull) ?? [],
|
|
||||||
[activity?.activityTargets],
|
|
||||||
);
|
|
||||||
|
|
||||||
const initialSelectedEntityIds = useMemo(
|
|
||||||
() =>
|
|
||||||
[...initialPeopleIds, ...initialCompanyIds].reduce<
|
|
||||||
Record<string, boolean>
|
|
||||||
>((result, entityId) => ({ ...result, [entityId]: true }), {}),
|
|
||||||
[initialPeopleIds, initialCompanyIds],
|
|
||||||
);
|
|
||||||
|
|
||||||
const personsForMultiSelect = useFilteredSearchPeopleQuery({
|
|
||||||
searchFilter,
|
|
||||||
selectedIds: initialPeopleIds,
|
|
||||||
});
|
|
||||||
|
|
||||||
const companiesForMultiSelect = useFilteredSearchCompanyQuery({
|
|
||||||
searchFilter,
|
|
||||||
selectedIds: initialCompanyIds,
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectedEntities = flatMapAndSortEntityForSelectArrayOfArrayByName([
|
|
||||||
personsForMultiSelect.selectedEntities,
|
|
||||||
companiesForMultiSelect.selectedEntities,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const filteredSelectedEntities =
|
|
||||||
flatMapAndSortEntityForSelectArrayOfArrayByName([
|
|
||||||
personsForMultiSelect.filteredSelectedEntities,
|
|
||||||
companiesForMultiSelect.filteredSelectedEntities,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const entitiesToSelect = flatMapAndSortEntityForSelectArrayOfArrayByName([
|
|
||||||
personsForMultiSelect.entitiesToSelect,
|
|
||||||
companiesForMultiSelect.entitiesToSelect,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleCheckItemsChange = useHandleCheckableActivityTargetChange({
|
|
||||||
activity,
|
|
||||||
});
|
|
||||||
|
|
||||||
const exitEditMode = useCallback(() => {
|
|
||||||
goBackToPreviousHotkeyScope();
|
|
||||||
setIsMenuOpen(false);
|
|
||||||
setSearchFilter('');
|
|
||||||
|
|
||||||
if (Object.values(selectedEntityIds).some((value) => !!value)) {
|
|
||||||
handleCheckItemsChange(selectedEntityIds, entitiesToSelect);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
entitiesToSelect,
|
|
||||||
selectedEntityIds,
|
|
||||||
goBackToPreviousHotkeyScope,
|
|
||||||
handleCheckItemsChange,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const handleRelationContainerClick = useCallback(() => {
|
|
||||||
if (isMenuOpen) {
|
|
||||||
exitEditMode();
|
|
||||||
} else {
|
|
||||||
setIsMenuOpen(true);
|
|
||||||
setSelectedEntityIds(initialSelectedEntityIds);
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope(
|
|
||||||
RelationPickerHotkeyScope.RelationPicker,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
initialSelectedEntityIds,
|
|
||||||
exitEditMode,
|
|
||||||
isMenuOpen,
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useScopedHotkeys(
|
|
||||||
['esc', 'enter'],
|
|
||||||
() => {
|
|
||||||
exitEditMode();
|
|
||||||
},
|
|
||||||
RelationPickerHotkeyScope.RelationPicker,
|
|
||||||
[exitEditMode],
|
|
||||||
);
|
|
||||||
|
|
||||||
const { refs, floatingStyles } = useFloating({
|
|
||||||
strategy: 'absolute',
|
|
||||||
middleware: [
|
|
||||||
offset(({ rects }) => {
|
|
||||||
return -rects.reference.height;
|
|
||||||
}),
|
|
||||||
flip(),
|
|
||||||
size(),
|
|
||||||
],
|
|
||||||
whileElementsMounted: autoUpdate,
|
|
||||||
open: isMenuOpen,
|
|
||||||
placement: 'bottom-start',
|
|
||||||
});
|
|
||||||
|
|
||||||
useListenClickOutside({
|
|
||||||
refs: [refs.floating, refs.domReference],
|
|
||||||
callback: () => {
|
|
||||||
exitEditMode();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledContainer>
|
|
||||||
<StyledRelationContainer
|
|
||||||
ref={refs.setReference}
|
|
||||||
onClick={handleRelationContainerClick}
|
|
||||||
>
|
|
||||||
{selectedEntities?.map((entity) =>
|
|
||||||
entity.entityType === CommentableType.Company ? (
|
|
||||||
<CompanyChip
|
|
||||||
key={entity.id}
|
|
||||||
id={entity.id}
|
|
||||||
name={entity.name}
|
|
||||||
pictureUrl={entity.avatarUrl}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<PersonChip
|
|
||||||
key={entity.id}
|
|
||||||
name={entity.name}
|
|
||||||
id={entity.id}
|
|
||||||
pictureUrl={entity.avatarUrl ?? ''}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</StyledRelationContainer>
|
|
||||||
{isMenuOpen && (
|
|
||||||
<RecoilScope>
|
|
||||||
<StyledMenuWrapper ref={refs.setFloating} style={floatingStyles}>
|
|
||||||
<MultipleEntitySelect
|
|
||||||
entities={{
|
|
||||||
entitiesToSelect,
|
|
||||||
filteredSelectedEntities,
|
|
||||||
selectedEntities,
|
|
||||||
loading: false, // TODO implement skeleton loading
|
|
||||||
}}
|
|
||||||
onChange={setSelectedEntityIds}
|
|
||||||
onSearchFilterChange={setSearchFilter}
|
|
||||||
searchFilter={searchFilter}
|
|
||||||
value={selectedEntityIds}
|
|
||||||
/>
|
|
||||||
</StyledMenuWrapper>
|
|
||||||
</RecoilScope>
|
|
||||||
)}
|
|
||||||
</StyledContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -2,7 +2,7 @@ import styled from '@emotion/styled';
|
|||||||
|
|
||||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||||
import { PersonChip } from '@/people/components/PersonChip';
|
import { PersonChip } from '@/people/components/PersonChip';
|
||||||
import { GetCompaniesQuery, GetPeopleQuery } from '~/generated/graphql';
|
import { ActivityTarget, Company, Person } from '~/generated/graphql';
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -12,32 +12,44 @@ const StyledContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function ActivityTargetChips({
|
export function ActivityTargetChips({
|
||||||
targetCompanies,
|
targets,
|
||||||
targetPeople,
|
|
||||||
}: {
|
}: {
|
||||||
targetCompanies?: GetCompaniesQuery;
|
targets?: Array<
|
||||||
targetPeople?: GetPeopleQuery;
|
Pick<ActivityTarget, 'id'> & {
|
||||||
|
person?: Pick<Person, 'id' | 'displayName' | 'avatarUrl'> | null;
|
||||||
|
company?: Pick<Company, 'id' | 'domainName' | 'name'> | null;
|
||||||
|
}
|
||||||
|
> | null;
|
||||||
}) {
|
}) {
|
||||||
|
if (!targets) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
{targetCompanies?.companies &&
|
{targets.map(({ company, person }) => {
|
||||||
targetCompanies.companies.map((company) => (
|
if (company) {
|
||||||
<CompanyChip
|
return (
|
||||||
key={company.id}
|
<CompanyChip
|
||||||
id={company.id}
|
key={company.id}
|
||||||
name={company.name}
|
id={company.id}
|
||||||
pictureUrl={getLogoUrlFromDomainName(company.domainName)}
|
name={company.name}
|
||||||
/>
|
pictureUrl={getLogoUrlFromDomainName(company.domainName)}
|
||||||
))}
|
/>
|
||||||
{targetPeople?.people &&
|
);
|
||||||
targetPeople.people.map((person) => (
|
}
|
||||||
<PersonChip
|
if (person) {
|
||||||
key={person.id}
|
return (
|
||||||
id={person.id}
|
<PersonChip
|
||||||
name={person.displayName}
|
key={person.id}
|
||||||
pictureUrl={person.avatarUrl ?? ''}
|
id={person.id}
|
||||||
/>
|
name={person.displayName}
|
||||||
))}
|
pictureUrl={person.avatarUrl ?? undefined}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <></>;
|
||||||
|
})}
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import {
|
|||||||
CheckboxShape,
|
CheckboxShape,
|
||||||
} from '@/ui/input/checkbox/components/Checkbox';
|
} from '@/ui/input/checkbox/components/Checkbox';
|
||||||
import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip';
|
import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip';
|
||||||
import { useGetCompaniesQuery, useGetPeopleQuery } from '~/generated/graphql';
|
|
||||||
import { beautifyExactDate } from '~/utils/date-utils';
|
import { beautifyExactDate } from '~/utils/date-utils';
|
||||||
|
|
||||||
import { useCompleteTask } from '../hooks/useCompleteTask';
|
import { useCompleteTask } from '../hooks/useCompleteTask';
|
||||||
@ -34,10 +33,13 @@ const StyledTaskBody = styled.div`
|
|||||||
width: 1px;
|
width: 1px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledTaskTitle = styled.div`
|
const StyledTaskTitle = styled.div<{
|
||||||
|
completed: boolean;
|
||||||
|
}>`
|
||||||
color: ${({ theme }) => theme.font.color.primary};
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||||
padding: 0 ${({ theme }) => theme.spacing(2)};
|
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||||
|
text-decoration: ${({ completed }) => (completed ? 'line-through' : 'none')};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledCommentIcon = styled.div`
|
const StyledCommentIcon = styled.div`
|
||||||
@ -62,37 +64,7 @@ const StyledFieldsContainer = styled.div`
|
|||||||
export function TaskRow({ task }: { task: TaskForList }) {
|
export function TaskRow({ task }: { task: TaskForList }) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
||||||
const { data: targetPeople } = useGetPeopleQuery({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: {
|
|
||||||
in: task?.activityTargets
|
|
||||||
? task?.activityTargets
|
|
||||||
.filter((target) => target.commentableType === 'Person')
|
|
||||||
.map(
|
|
||||||
(target) => (target.personId || target.commentableId) ?? '',
|
|
||||||
)
|
|
||||||
: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data: targetCompanies } = useGetCompaniesQuery({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: {
|
|
||||||
in: task?.activityTargets
|
|
||||||
? task?.activityTargets
|
|
||||||
.filter((target) => target.commentableType === 'Company')
|
|
||||||
.map(
|
|
||||||
(target) => (target.companyId || target.commentableId) ?? '',
|
|
||||||
)
|
|
||||||
: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const body = JSON.parse(task.body ?? '{}')[0]?.content[0]?.text;
|
const body = JSON.parse(task.body ?? '{}')[0]?.content[0]?.text;
|
||||||
const { completeTask } = useCompleteTask(task);
|
const { completeTask } = useCompleteTask(task);
|
||||||
|
|
||||||
@ -113,7 +85,9 @@ export function TaskRow({ task }: { task: TaskForList }) {
|
|||||||
onChange={completeTask}
|
onChange={completeTask}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<StyledTaskTitle>{task.title ?? '(No title)'}</StyledTaskTitle>
|
<StyledTaskTitle completed={task.completedAt !== null}>
|
||||||
|
{task.title ?? '(No title)'}
|
||||||
|
</StyledTaskTitle>
|
||||||
<StyledTaskBody>
|
<StyledTaskBody>
|
||||||
<OverflowingTextWithTooltip text={body} />
|
<OverflowingTextWithTooltip text={body} />
|
||||||
{task.comments && task.comments.length > 0 && (
|
{task.comments && task.comments.length > 0 && (
|
||||||
@ -123,10 +97,7 @@ export function TaskRow({ task }: { task: TaskForList }) {
|
|||||||
)}
|
)}
|
||||||
</StyledTaskBody>
|
</StyledTaskBody>
|
||||||
<StyledFieldsContainer>
|
<StyledFieldsContainer>
|
||||||
<ActivityTargetChips
|
<ActivityTargetChips targets={task.activityTargets} />
|
||||||
targetCompanies={targetCompanies}
|
|
||||||
targetPeople={targetPeople}
|
|
||||||
/>
|
|
||||||
<StyledDueDate>
|
<StyledDueDate>
|
||||||
<IconCalendar size={theme.icon.size.md} />
|
<IconCalendar size={theme.icon.size.md} />
|
||||||
{task.dueAt && beautifyExactDate(task.dueAt)}
|
{task.dueAt && beautifyExactDate(task.dueAt)}
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
|
||||||
|
|
||||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
|
||||||
import { mockedActivities } from '~/testing/mock-data/activities';
|
|
||||||
|
|
||||||
import { ActivityRelationPicker } from '../ActivityRelationPicker';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
|
||||||
width: 400px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const meta: Meta<typeof ActivityRelationPicker> = {
|
|
||||||
title: 'Modules/Comments/ActivityRelationPicker',
|
|
||||||
component: ActivityRelationPicker,
|
|
||||||
decorators: [
|
|
||||||
(Story) => (
|
|
||||||
<MemoryRouter>
|
|
||||||
<StyledContainer>
|
|
||||||
<Story />
|
|
||||||
</StyledContainer>
|
|
||||||
</MemoryRouter>
|
|
||||||
),
|
|
||||||
ComponentDecorator,
|
|
||||||
],
|
|
||||||
args: { activity: mockedActivities[0] },
|
|
||||||
parameters: {
|
|
||||||
msw: graphqlMocks,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default meta;
|
|
||||||
type Story = StoryObj<typeof ActivityRelationPicker>;
|
|
||||||
|
|
||||||
export const Default: Story = {};
|
|
||||||
@ -4,59 +4,22 @@ import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
|||||||
import { IconArrowUpRight } from '@/ui/icon';
|
import { IconArrowUpRight } from '@/ui/icon';
|
||||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
import {
|
import { Activity, ActivityTarget, Company, Person } from '~/generated/graphql';
|
||||||
Activity,
|
|
||||||
ActivityTarget,
|
|
||||||
useGetCompaniesQuery,
|
|
||||||
useGetPeopleQuery,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { ActivityRelationEditableFieldEditMode } from './ActivityRelationEditableFieldEditMode';
|
import { ActivityRelationEditableFieldEditMode } from './ActivityRelationEditableFieldEditMode';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
activity?: Pick<Activity, 'id'> & {
|
activity?: Pick<Activity, 'id'> & {
|
||||||
activityTargets?: Array<
|
activityTargets?: Array<
|
||||||
Pick<
|
Pick<ActivityTarget, 'id' | 'personId' | 'companyId'> & {
|
||||||
ActivityTarget,
|
person?: Pick<Person, 'id' | 'displayName'>;
|
||||||
'id' | 'commentableId' | 'commentableType' | 'personId' | 'companyId'
|
company?: Pick<Company, 'id' | 'domainName' | 'name'>;
|
||||||
>
|
}
|
||||||
> | null;
|
> | null;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ActivityRelationEditableField({ activity }: OwnProps) {
|
export function ActivityRelationEditableField({ activity }: OwnProps) {
|
||||||
const { data: targetPeople } = useGetPeopleQuery({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: {
|
|
||||||
in: activity?.activityTargets
|
|
||||||
? activity?.activityTargets
|
|
||||||
.filter((target) => target.commentableType === 'Person')
|
|
||||||
.map(
|
|
||||||
(target) => (target.personId || target.commentableId) ?? '',
|
|
||||||
)
|
|
||||||
: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data: targetCompanies } = useGetCompaniesQuery({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: {
|
|
||||||
in: activity?.activityTargets
|
|
||||||
? activity?.activityTargets
|
|
||||||
.filter((target) => target.commentableType === 'Company')
|
|
||||||
.map(
|
|
||||||
(target) => (target.companyId || target.commentableId) ?? '',
|
|
||||||
)
|
|
||||||
: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
<RecoilScope>
|
<RecoilScope>
|
||||||
@ -71,10 +34,7 @@ export function ActivityRelationEditableField({ activity }: OwnProps) {
|
|||||||
}
|
}
|
||||||
label="Relations"
|
label="Relations"
|
||||||
displayModeContent={
|
displayModeContent={
|
||||||
<ActivityTargetChips
|
<ActivityTargetChips targets={activity?.activityTargets} />
|
||||||
targetCompanies={targetCompanies}
|
|
||||||
targetPeople={targetPeople}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
|
|||||||
@ -13,10 +13,7 @@ import { assertNotNull } from '~/utils/assert';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
activity?: Pick<Activity, 'id'> & {
|
activity?: Pick<Activity, 'id'> & {
|
||||||
activityTargets?: Array<
|
activityTargets?: Array<
|
||||||
Pick<
|
Pick<ActivityTarget, 'id' | 'personId' | 'companyId'>
|
||||||
ActivityTarget,
|
|
||||||
'id' | 'commentableId' | 'commentableType' | 'personId' | 'companyId'
|
|
||||||
>
|
|
||||||
> | null;
|
> | null;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -33,8 +30,8 @@ export function ActivityRelationEditableFieldEditMode({ activity }: OwnProps) {
|
|||||||
const initialPeopleIds = useMemo(
|
const initialPeopleIds = useMemo(
|
||||||
() =>
|
() =>
|
||||||
activity?.activityTargets
|
activity?.activityTargets
|
||||||
?.filter((relation) => relation.commentableType === 'Person')
|
?.filter((relation) => relation.personId !== null)
|
||||||
.map((relation) => relation.personId || relation.commentableId)
|
.map((relation) => relation.personId)
|
||||||
.filter(assertNotNull) ?? [],
|
.filter(assertNotNull) ?? [],
|
||||||
[activity?.activityTargets],
|
[activity?.activityTargets],
|
||||||
);
|
);
|
||||||
@ -42,8 +39,8 @@ export function ActivityRelationEditableFieldEditMode({ activity }: OwnProps) {
|
|||||||
const initialCompanyIds = useMemo(
|
const initialCompanyIds = useMemo(
|
||||||
() =>
|
() =>
|
||||||
activity?.activityTargets
|
activity?.activityTargets
|
||||||
?.filter((relation) => relation.commentableType === 'Company')
|
?.filter((relation) => relation.companyId !== null)
|
||||||
.map((relation) => relation.companyId || relation.commentableId)
|
.map((relation) => relation.companyId)
|
||||||
.filter(assertNotNull) ?? [],
|
.filter(assertNotNull) ?? [],
|
||||||
[activity?.activityTargets],
|
[activity?.activityTargets],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,20 +6,20 @@ import { GET_PEOPLE } from '@/people/queries';
|
|||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
ActivityTarget,
|
ActivityTarget,
|
||||||
CommentableType,
|
|
||||||
useAddActivityTargetsOnActivityMutation,
|
useAddActivityTargetsOnActivityMutation,
|
||||||
useRemoveActivityTargetsOnActivityMutation,
|
useRemoveActivityTargetsOnActivityMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
import { GET_ACTIVITY } from '../queries';
|
import { GET_ACTIVITY } from '../queries';
|
||||||
import { CommentableEntityForSelect } from '../types/CommentableEntityForSelect';
|
import { ActivityTargetableEntityType } from '../types/ActivityTargetableEntity';
|
||||||
|
import { ActivityTargetableEntityForSelect } from '../types/ActivityTargetableEntityForSelect';
|
||||||
|
|
||||||
export function useHandleCheckableActivityTargetChange({
|
export function useHandleCheckableActivityTargetChange({
|
||||||
activity,
|
activity,
|
||||||
}: {
|
}: {
|
||||||
activity?: Pick<Activity, 'id'> & {
|
activity?: Pick<Activity, 'id'> & {
|
||||||
activityTargets?: Array<
|
activityTargets?: Array<
|
||||||
Pick<ActivityTarget, 'id' | 'commentableId' | 'commentableType'>
|
Pick<ActivityTarget, 'id' | 'personId' | 'companyId'>
|
||||||
> | null;
|
> | null;
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
@ -43,14 +43,16 @@ export function useHandleCheckableActivityTargetChange({
|
|||||||
|
|
||||||
return async function handleCheckItemsChange(
|
return async function handleCheckItemsChange(
|
||||||
entityValues: Record<string, boolean>,
|
entityValues: Record<string, boolean>,
|
||||||
entities: CommentableEntityForSelect[],
|
entities: ActivityTargetableEntityForSelect[],
|
||||||
) {
|
) {
|
||||||
if (!activity) {
|
if (!activity) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentEntityIds = activity.activityTargets
|
const currentEntityIds = activity.activityTargets
|
||||||
? activity.activityTargets.map(({ commentableId }) => commentableId)
|
? activity.activityTargets.map(
|
||||||
|
({ personId, companyId }) => personId ?? companyId,
|
||||||
|
)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const entitiesToAdd = entities.filter(
|
const entitiesToAdd = entities.filter(
|
||||||
@ -64,12 +66,14 @@ export function useHandleCheckableActivityTargetChange({
|
|||||||
activityTargetInputs: entitiesToAdd.map((entity) => ({
|
activityTargetInputs: entitiesToAdd.map((entity) => ({
|
||||||
id: v4(),
|
id: v4(),
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
commentableType: entity.entityType,
|
|
||||||
commentableId: entity.id,
|
|
||||||
companyId:
|
companyId:
|
||||||
entity.entityType === CommentableType.Company ? entity.id : null,
|
entity.entityType === ActivityTargetableEntityType.Company
|
||||||
|
? entity.id
|
||||||
|
: null,
|
||||||
personId:
|
personId:
|
||||||
entity.entityType === CommentableType.Person ? entity.id : null,
|
entity.entityType === ActivityTargetableEntityType.Person
|
||||||
|
? entity.id
|
||||||
|
: null,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -77,8 +81,9 @@ export function useHandleCheckableActivityTargetChange({
|
|||||||
const activityTargetIdsToDelete = activity.activityTargets
|
const activityTargetIdsToDelete = activity.activityTargets
|
||||||
? activity.activityTargets
|
? activity.activityTargets
|
||||||
.filter(
|
.filter(
|
||||||
({ commentableId }) =>
|
({ personId, companyId }) =>
|
||||||
commentableId && !entityValues[commentableId],
|
(personId ?? companyId) &&
|
||||||
|
!entityValues[personId ?? companyId ?? ''],
|
||||||
)
|
)
|
||||||
.map(({ id }) => id)
|
.map(({ id }) => id)
|
||||||
: [];
|
: [];
|
||||||
|
|||||||
@ -9,20 +9,19 @@ import { useRightDrawer } from '@/ui/right-drawer/hooks/useRightDrawer';
|
|||||||
import { RightDrawerHotkeyScope } from '@/ui/right-drawer/types/RightDrawerHotkeyScope';
|
import { RightDrawerHotkeyScope } from '@/ui/right-drawer/types/RightDrawerHotkeyScope';
|
||||||
import { RightDrawerPages } from '@/ui/right-drawer/types/RightDrawerPages';
|
import { RightDrawerPages } from '@/ui/right-drawer/types/RightDrawerPages';
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
import {
|
import { ActivityType, useCreateActivityMutation } from '~/generated/graphql';
|
||||||
ActivityType,
|
|
||||||
CommentableType,
|
|
||||||
useCreateActivityMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GET_ACTIVITIES,
|
GET_ACTIVITIES,
|
||||||
GET_ACTIVITIES_BY_TARGETS,
|
GET_ACTIVITIES_BY_TARGETS,
|
||||||
GET_ACTIVITY,
|
GET_ACTIVITY,
|
||||||
} from '../queries';
|
} from '../queries';
|
||||||
import { commentableEntityArrayState } from '../states/commentableEntityArrayState';
|
import { activityTargetableEntityArrayState } from '../states/activityTargetableEntityArrayState';
|
||||||
import { viewableActivityIdState } from '../states/viewableActivityIdState';
|
import { viewableActivityIdState } from '../states/viewableActivityIdState';
|
||||||
import { CommentableEntity } from '../types/CommentableEntity';
|
import {
|
||||||
|
ActivityTargetableEntity,
|
||||||
|
ActivityTargetableEntityType,
|
||||||
|
} from '../types/ActivityTargetableEntity';
|
||||||
|
|
||||||
export function useOpenCreateActivityDrawer() {
|
export function useOpenCreateActivityDrawer() {
|
||||||
const { openRightDrawer } = useRightDrawer();
|
const { openRightDrawer } = useRightDrawer();
|
||||||
@ -30,14 +29,14 @@ export function useOpenCreateActivityDrawer() {
|
|||||||
const currentUser = useRecoilValue(currentUserState);
|
const currentUser = useRecoilValue(currentUserState);
|
||||||
const setHotkeyScope = useSetHotkeyScope();
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
const [, setCommentableEntityArray] = useRecoilState(
|
const [, setActivityTargetableEntityArray] = useRecoilState(
|
||||||
commentableEntityArrayState,
|
activityTargetableEntityArrayState,
|
||||||
);
|
);
|
||||||
const [, setViewableActivityId] = useRecoilState(viewableActivityIdState);
|
const [, setViewableActivityId] = useRecoilState(viewableActivityIdState);
|
||||||
|
|
||||||
return function openCreateActivityDrawer(
|
return function openCreateActivityDrawer(
|
||||||
type: ActivityType,
|
type: ActivityType,
|
||||||
entities?: CommentableEntity[],
|
entities?: ActivityTargetableEntity[],
|
||||||
) {
|
) {
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
|
|
||||||
@ -54,14 +53,14 @@ export function useOpenCreateActivityDrawer() {
|
|||||||
createMany: {
|
createMany: {
|
||||||
data: entities
|
data: entities
|
||||||
? entities.map((entity) => ({
|
? entities.map((entity) => ({
|
||||||
commentableId: entity.id,
|
|
||||||
commentableType: entity.type,
|
|
||||||
companyId:
|
companyId:
|
||||||
entity.type === CommentableType.Company
|
entity.type === ActivityTargetableEntityType.Company
|
||||||
? entity.id
|
? entity.id
|
||||||
: null,
|
: null,
|
||||||
personId:
|
personId:
|
||||||
entity.type === CommentableType.Person ? entity.id : null,
|
entity.type === ActivityTargetableEntityType.Person
|
||||||
|
? entity.id
|
||||||
|
: null,
|
||||||
id: v4(),
|
id: v4(),
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
}))
|
}))
|
||||||
@ -81,7 +80,7 @@ export function useOpenCreateActivityDrawer() {
|
|||||||
onCompleted(data) {
|
onCompleted(data) {
|
||||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||||
setViewableActivityId(data.createOneActivity.id);
|
setViewableActivityId(data.createOneActivity.id);
|
||||||
setCommentableEntityArray(entities ?? []);
|
setActivityTargetableEntityArray(entities ?? []);
|
||||||
openRightDrawer(RightDrawerPages.CreateActivity);
|
openRightDrawer(RightDrawerPages.CreateActivity);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector';
|
import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector';
|
||||||
import { ActivityType, CommentableType } from '~/generated/graphql';
|
import { ActivityType } from '~/generated/graphql';
|
||||||
|
|
||||||
import { CommentableEntity } from '../types/CommentableEntity';
|
import {
|
||||||
|
ActivityTargetableEntity,
|
||||||
|
ActivityTargetableEntityType,
|
||||||
|
} from '../types/ActivityTargetableEntity';
|
||||||
|
|
||||||
import { useOpenCreateActivityDrawer } from './useOpenCreateActivityDrawer';
|
import { useOpenCreateActivityDrawer } from './useOpenCreateActivityDrawer';
|
||||||
|
|
||||||
@ -14,14 +17,13 @@ export function useOpenCreateActivityDrawerForSelectedRowIds() {
|
|||||||
|
|
||||||
return function openCreateCommentDrawerForSelectedRowIds(
|
return function openCreateCommentDrawerForSelectedRowIds(
|
||||||
type: ActivityType,
|
type: ActivityType,
|
||||||
entityType: CommentableType,
|
entityType: ActivityTargetableEntityType,
|
||||||
) {
|
) {
|
||||||
const commentableEntityArray: CommentableEntity[] = selectedEntityIds.map(
|
const activityTargetableEntityArray: ActivityTargetableEntity[] =
|
||||||
(id) => ({
|
selectedEntityIds.map((id) => ({
|
||||||
type: entityType,
|
type: entityType,
|
||||||
id,
|
id,
|
||||||
}),
|
}));
|
||||||
);
|
openCreateActivityDrawer(type, activityTargetableEntityArray);
|
||||||
openCreateActivityDrawer(type, commentableEntityArray);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,22 +5,22 @@ import { RightDrawerHotkeyScope } from '@/ui/right-drawer/types/RightDrawerHotke
|
|||||||
import { RightDrawerPages } from '@/ui/right-drawer/types/RightDrawerPages';
|
import { RightDrawerPages } from '@/ui/right-drawer/types/RightDrawerPages';
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
|
||||||
import { commentableEntityArrayState } from '../states/commentableEntityArrayState';
|
import { activityTargetableEntityArrayState } from '../states/activityTargetableEntityArrayState';
|
||||||
import { CommentableEntity } from '../types/CommentableEntity';
|
import { ActivityTargetableEntity } from '../types/ActivityTargetableEntity';
|
||||||
|
|
||||||
// TODO: refactor with recoil callback to avoid rerender
|
// TODO: refactor with recoil callback to avoid rerender
|
||||||
export function useOpenTimelineRightDrawer() {
|
export function useOpenTimelineRightDrawer() {
|
||||||
const { openRightDrawer } = useRightDrawer();
|
const { openRightDrawer } = useRightDrawer();
|
||||||
const [, setCommentableEntityArray] = useRecoilState(
|
const [, setActivityTargetableEntityArray] = useRecoilState(
|
||||||
commentableEntityArrayState,
|
activityTargetableEntityArrayState,
|
||||||
);
|
);
|
||||||
const setHotkeyScope = useSetHotkeyScope();
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
return function openTimelineRightDrawer(
|
return function openTimelineRightDrawer(
|
||||||
commentableEntityArray: CommentableEntity[],
|
activityTargetableEntityArray: ActivityTargetableEntity[],
|
||||||
) {
|
) {
|
||||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||||
setCommentableEntityArray(commentableEntityArray);
|
setActivityTargetableEntityArray(activityTargetableEntityArray);
|
||||||
openRightDrawer(RightDrawerPages.Timeline);
|
openRightDrawer(RightDrawerPages.Timeline);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,8 +45,6 @@ export const CREATE_ACTIVITY_WITH_COMMENT = gql`
|
|||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
activityId
|
activityId
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
companyId
|
||||||
personId
|
personId
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,58 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const ACTIVITY_QUERY_FRAGMENT = gql`
|
||||||
|
fragment ActivityQueryFragment on Activity {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
title
|
||||||
|
body
|
||||||
|
type
|
||||||
|
completedAt
|
||||||
|
dueAt
|
||||||
|
assignee {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
displayName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
author {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
displayName
|
||||||
|
}
|
||||||
|
comments {
|
||||||
|
id
|
||||||
|
body
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
author {
|
||||||
|
id
|
||||||
|
displayName
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activityTargets {
|
||||||
|
id
|
||||||
|
companyId
|
||||||
|
personId
|
||||||
|
company {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
domainName
|
||||||
|
}
|
||||||
|
person {
|
||||||
|
id
|
||||||
|
displayName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const GET_ACTIVITIES_BY_TARGETS = gql`
|
export const GET_ACTIVITIES_BY_TARGETS = gql`
|
||||||
query GetActivitiesByTargets(
|
query GetActivitiesByTargets(
|
||||||
$activityTargetIds: [String!]!
|
$activityTargetIds: [String!]!
|
||||||
@ -8,49 +61,17 @@ export const GET_ACTIVITIES_BY_TARGETS = gql`
|
|||||||
findManyActivities(
|
findManyActivities(
|
||||||
orderBy: $orderBy
|
orderBy: $orderBy
|
||||||
where: {
|
where: {
|
||||||
activityTargets: { some: { commentableId: { in: $activityTargetIds } } }
|
activityTargets: {
|
||||||
}
|
some: {
|
||||||
) {
|
OR: [
|
||||||
id
|
{ personId: { in: $activityTargetIds } }
|
||||||
createdAt
|
{ companyId: { in: $activityTargetIds } }
|
||||||
title
|
]
|
||||||
body
|
}
|
||||||
type
|
|
||||||
completedAt
|
|
||||||
dueAt
|
|
||||||
assignee {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
}
|
|
||||||
comments {
|
|
||||||
id
|
|
||||||
body
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
displayName
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
avatarUrl
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
activityTargets {
|
) {
|
||||||
id
|
...ActivityQueryFragment
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
|
||||||
personId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -61,36 +82,7 @@ export const GET_ACTIVITIES = gql`
|
|||||||
$orderBy: [ActivityOrderByWithRelationInput!]
|
$orderBy: [ActivityOrderByWithRelationInput!]
|
||||||
) {
|
) {
|
||||||
findManyActivities(orderBy: $orderBy, where: $where) {
|
findManyActivities(orderBy: $orderBy, where: $where) {
|
||||||
id
|
...ActivityQueryFragment
|
||||||
createdAt
|
|
||||||
title
|
|
||||||
body
|
|
||||||
type
|
|
||||||
completedAt
|
|
||||||
dueAt
|
|
||||||
assignee {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
}
|
|
||||||
comments {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
activityTargets {
|
|
||||||
id
|
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
|
||||||
personId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -98,46 +90,7 @@ export const GET_ACTIVITIES = gql`
|
|||||||
export const GET_ACTIVITY = gql`
|
export const GET_ACTIVITY = gql`
|
||||||
query GetActivity($activityId: String!) {
|
query GetActivity($activityId: String!) {
|
||||||
findManyActivities(where: { id: { equals: $activityId } }) {
|
findManyActivities(where: { id: { equals: $activityId } }) {
|
||||||
id
|
...ActivityQueryFragment
|
||||||
createdAt
|
|
||||||
body
|
|
||||||
title
|
|
||||||
type
|
|
||||||
completedAt
|
|
||||||
dueAt
|
|
||||||
assignee {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
displayName
|
|
||||||
}
|
|
||||||
comments {
|
|
||||||
id
|
|
||||||
body
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
author {
|
|
||||||
id
|
|
||||||
displayName
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
avatarUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
activityTargets {
|
|
||||||
id
|
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
|
||||||
personId
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -16,8 +16,6 @@ export const ADD_ACTIVITY_TARGETS = gql`
|
|||||||
id
|
id
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
companyId
|
||||||
personId
|
personId
|
||||||
}
|
}
|
||||||
@ -43,8 +41,6 @@ export const REMOVE_ACTIVITY_TARGETS = gql`
|
|||||||
id
|
id
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
commentableType
|
|
||||||
commentableId
|
|
||||||
companyId
|
companyId
|
||||||
personId
|
personId
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,21 @@
|
|||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { commentableEntityArrayState } from '@/activities/states/commentableEntityArrayState';
|
import { activityTargetableEntityArrayState } from '@/activities/states/activityTargetableEntityArrayState';
|
||||||
import { Timeline } from '@/activities/timeline/components/Timeline';
|
import { Timeline } from '@/activities/timeline/components/Timeline';
|
||||||
|
|
||||||
export function RightDrawerTimeline() {
|
export function RightDrawerTimeline() {
|
||||||
const [commentableEntityArray] = useRecoilState(commentableEntityArrayState);
|
const activityTargetableEntityArray = useRecoilValue(
|
||||||
|
activityTargetableEntityArrayState,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{commentableEntityArray.map((commentableEntity) => (
|
{activityTargetableEntityArray.map((targetableEntity) => (
|
||||||
<Timeline
|
<Timeline
|
||||||
key={commentableEntity.id}
|
key={targetableEntity.id}
|
||||||
entity={{
|
entity={{
|
||||||
id: commentableEntity?.id ?? '',
|
id: targetableEntity?.id ?? '',
|
||||||
type: commentableEntity.type,
|
type: targetableEntity.type,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
|
import { ActivityTargetableEntity } from '../types/ActivityTargetableEntity';
|
||||||
|
|
||||||
|
export const activityTargetableEntityArrayState = atom<
|
||||||
|
ActivityTargetableEntity[]
|
||||||
|
>({
|
||||||
|
key: 'activities/targetable-entity-array',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
import { CommentableEntity } from '../types/CommentableEntity';
|
|
||||||
|
|
||||||
export const commentableEntityArrayState = atom<CommentableEntity[]>({
|
|
||||||
key: 'activities/commentable-entity-array',
|
|
||||||
default: [],
|
|
||||||
});
|
|
||||||
@ -5,7 +5,7 @@ import styled from '@emotion/styled';
|
|||||||
import { ActivityCreateButton } from '@/activities/components/ActivityCreateButton';
|
import { ActivityCreateButton } from '@/activities/components/ActivityCreateButton';
|
||||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||||
import { ActivityForDrawer } from '@/activities/types/ActivityForDrawer';
|
import { ActivityForDrawer } from '@/activities/types/ActivityForDrawer';
|
||||||
import { CommentableEntity } from '@/activities/types/CommentableEntity';
|
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { IconCircleDot } from '@/ui/icon';
|
import { IconCircleDot } from '@/ui/icon';
|
||||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||||
import {
|
import {
|
||||||
@ -93,7 +93,7 @@ const StyledStartIcon = styled.div`
|
|||||||
width: 20px;
|
width: 20px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function Timeline({ entity }: { entity: CommentableEntity }) {
|
export function Timeline({ entity }: { entity: ActivityTargetableEntity }) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const { data: queryResult, loading } = useGetActivitiesByTargetsQuery({
|
const { data: queryResult, loading } = useGetActivitiesByTargetsQuery({
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
export enum ActivityTargetableEntityType {
|
||||||
|
Person = 'Person',
|
||||||
|
Company = 'Company',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ActivityTargetableEntity = {
|
||||||
|
id: string;
|
||||||
|
type: ActivityTargetableEntityType;
|
||||||
|
};
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||||
|
|
||||||
|
import { ActivityTargetableEntityType } from './ActivityTargetableEntity';
|
||||||
|
|
||||||
|
export type ActivityTargetableEntityForSelect = EntityForSelect & {
|
||||||
|
entityType: ActivityTargetableEntityType;
|
||||||
|
};
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { CommentableType } from '~/generated/graphql';
|
|
||||||
|
|
||||||
export type CommentableEntity = {
|
|
||||||
id: string;
|
|
||||||
type: CommentableType;
|
|
||||||
};
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
|
||||||
import { CommentableType } from '~/generated/graphql';
|
|
||||||
|
|
||||||
export type CommentableEntityForSelect = EntityForSelect & {
|
|
||||||
entityType: CommentableType;
|
|
||||||
};
|
|
||||||
@ -92,43 +92,6 @@ export function CommandMenu() {
|
|||||||
cmd.type === CommandType.Create,
|
cmd.type === CommandType.Create,
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: Allow performing actions on page through CommandBar
|
|
||||||
|
|
||||||
import { useMatch, useResolvedPath } from 'react-router-dom';
|
|
||||||
import { IconBuildingSkyscraper, IconUser } from '@/ui/icon';
|
|
||||||
|
|
||||||
const createSection = (
|
|
||||||
<StyledGroup heading="Create">
|
|
||||||
<CommandMenuItem
|
|
||||||
label="Create People"
|
|
||||||
onClick={createPeople}
|
|
||||||
icon={<IconUser />}
|
|
||||||
shortcuts={
|
|
||||||
!!useMatch({
|
|
||||||
path: useResolvedPath('/people').pathname,
|
|
||||||
end: true,
|
|
||||||
})
|
|
||||||
? ['C']
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<CommandMenuItem
|
|
||||||
label="Create Company"
|
|
||||||
onClick={createCompany}
|
|
||||||
icon={<IconBuildingSkyscraper />}
|
|
||||||
shortcuts={
|
|
||||||
!!useMatch({
|
|
||||||
path: useResolvedPath('/companies').pathname,
|
|
||||||
end: true,
|
|
||||||
})
|
|
||||||
? ['C']
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</StyledGroup>
|
|
||||||
);*/
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledDialog
|
<StyledDialog
|
||||||
open={isCommandMenuOpened}
|
open={isCommandMenuOpened}
|
||||||
@ -181,6 +144,7 @@ export function CommandMenu() {
|
|||||||
to={matchingNavigateCommand.to}
|
to={matchingNavigateCommand.to}
|
||||||
label={matchingNavigateCommand.label}
|
label={matchingNavigateCommand.label}
|
||||||
shortcuts={matchingNavigateCommand.shortcuts}
|
shortcuts={matchingNavigateCommand.shortcuts}
|
||||||
|
key={matchingNavigateCommand.label}
|
||||||
/>
|
/>
|
||||||
</StyledGroup>
|
</StyledGroup>
|
||||||
)}
|
)}
|
||||||
@ -245,7 +209,7 @@ export function CommandMenu() {
|
|||||||
)
|
)
|
||||||
.map((cmd) => (
|
.map((cmd) => (
|
||||||
<CommandMenuItem
|
<CommandMenuItem
|
||||||
key={cmd.shortcuts?.join('')}
|
key={cmd.shortcuts?.join('') ?? ''}
|
||||||
to={cmd.to}
|
to={cmd.to}
|
||||||
label={cmd.label}
|
label={cmd.label}
|
||||||
shortcuts={cmd.shortcuts}
|
shortcuts={cmd.shortcuts}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
label: string;
|
label: string;
|
||||||
to?: string;
|
to?: string;
|
||||||
|
key: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
shortcuts?: Array<string>;
|
shortcuts?: Array<string>;
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { ReactNode, useContext } from 'react';
|
import { ReactNode, useContext } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { useCurrentCardSelected } from '@/ui/board/hooks/useCurrentCardSelected';
|
||||||
import { BoardCardIdContext } from '@/ui/board/states/BoardCardIdContext';
|
import { BoardCardIdContext } from '@/ui/board/states/BoardCardIdContext';
|
||||||
import { selectedBoardCardIdsState } from '@/ui/board/states/selectedBoardCardIdsState';
|
|
||||||
import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState';
|
import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState';
|
||||||
import { EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
import { EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
||||||
import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField';
|
import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField';
|
||||||
@ -14,7 +14,6 @@ import {
|
|||||||
Checkbox,
|
Checkbox,
|
||||||
CheckboxVariant,
|
CheckboxVariant,
|
||||||
} from '@/ui/input/checkbox/components/Checkbox';
|
} from '@/ui/input/checkbox/components/Checkbox';
|
||||||
import { actionBarOpenState } from '@/ui/table/states/ActionBarIsOpenState';
|
|
||||||
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
|
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ const StyledBoardCard = styled.div<{ selected: boolean }>`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.checkbox-container {
|
.checkbox-container {
|
||||||
opacity: 0;
|
opacity: ${({ selected }) => (selected ? 1 : 0)};
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .checkbox-container {
|
&:hover .checkbox-container {
|
||||||
@ -103,6 +102,8 @@ const StyledFieldContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function CompanyBoardCard() {
|
export function CompanyBoardCard() {
|
||||||
|
const { currentCardSelected, setCurrentCardSelected } =
|
||||||
|
useCurrentCardSelected();
|
||||||
const boardCardId = useContext(BoardCardIdContext);
|
const boardCardId = useContext(BoardCardIdContext);
|
||||||
|
|
||||||
const [companyProgress] = useRecoilState(
|
const [companyProgress] = useRecoilState(
|
||||||
@ -110,25 +111,8 @@ export function CompanyBoardCard() {
|
|||||||
);
|
);
|
||||||
const { pipelineProgress, company } = companyProgress ?? {};
|
const { pipelineProgress, company } = companyProgress ?? {};
|
||||||
|
|
||||||
const [selectedBoardCards, setSelectedBoardCards] = useRecoilState(
|
|
||||||
selectedBoardCardIdsState,
|
|
||||||
);
|
|
||||||
const viewFieldsDefinitions = useRecoilValue(viewFieldsDefinitionsState);
|
const viewFieldsDefinitions = useRecoilValue(viewFieldsDefinitionsState);
|
||||||
|
|
||||||
const selected = selectedBoardCards.includes(boardCardId ?? '');
|
|
||||||
const setActionBarOpenState = useSetRecoilState(actionBarOpenState);
|
|
||||||
|
|
||||||
function setSelected(isSelected: boolean) {
|
|
||||||
if (isSelected) {
|
|
||||||
setSelectedBoardCards([...selectedBoardCards, boardCardId ?? '']);
|
|
||||||
setActionBarOpenState(true);
|
|
||||||
} else {
|
|
||||||
setSelectedBoardCards(
|
|
||||||
selectedBoardCards.filter((id) => id !== boardCardId),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// boardCardId check can be moved to a wrapper to avoid unnecessary logic above
|
// boardCardId check can be moved to a wrapper to avoid unnecessary logic above
|
||||||
if (!company || !pipelineProgress || !boardCardId) {
|
if (!company || !pipelineProgress || !boardCardId) {
|
||||||
return null;
|
return null;
|
||||||
@ -153,8 +137,8 @@ export function CompanyBoardCard() {
|
|||||||
return (
|
return (
|
||||||
<StyledBoardCardWrapper>
|
<StyledBoardCardWrapper>
|
||||||
<StyledBoardCard
|
<StyledBoardCard
|
||||||
selected={selected}
|
selected={currentCardSelected}
|
||||||
onClick={() => setSelected(!selected)}
|
onClick={() => setCurrentCardSelected(!currentCardSelected)}
|
||||||
>
|
>
|
||||||
<StyledBoardCardHeader>
|
<StyledBoardCardHeader>
|
||||||
<CompanyChip
|
<CompanyChip
|
||||||
@ -165,8 +149,8 @@ export function CompanyBoardCard() {
|
|||||||
/>
|
/>
|
||||||
<StyledCheckboxContainer className="checkbox-container">
|
<StyledCheckboxContainer className="checkbox-container">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selected}
|
checked={currentCardSelected}
|
||||||
onChange={() => setSelected(!selected)}
|
onChange={() => setCurrentCardSelected(!currentCardSelected)}
|
||||||
variant={CheckboxVariant.Secondary}
|
variant={CheckboxVariant.Secondary}
|
||||||
/>
|
/>
|
||||||
</StyledCheckboxContainer>
|
</StyledCheckboxContainer>
|
||||||
|
|||||||
41
front/src/modules/companies/components/CompanyPicker.tsx
Normal file
41
front/src/modules/companies/components/CompanyPicker.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||||
|
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||||
|
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
|
|
||||||
|
import { useFilteredSearchCompanyQuery } from '../queries';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
companyId: string | null;
|
||||||
|
onSubmit: (newCompanyId: EntityForSelect | null) => void;
|
||||||
|
onCancel?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CompanyPicker({ companyId, onSubmit, onCancel }: OwnProps) {
|
||||||
|
const [searchFilter] = useRecoilScopedState(
|
||||||
|
relationPickerSearchFilterScopedState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const companies = useFilteredSearchCompanyQuery({
|
||||||
|
searchFilter,
|
||||||
|
selectedIds: companyId ? [companyId] : [],
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleEntitySelected(
|
||||||
|
selectedCompany: EntityForSelect | null | undefined,
|
||||||
|
) {
|
||||||
|
onSubmit(selectedCompany ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SingleEntitySelect
|
||||||
|
onEntitySelected={handleEntitySelected}
|
||||||
|
onCancel={onCancel}
|
||||||
|
entities={{
|
||||||
|
loading: companies.loading,
|
||||||
|
entitiesToSelect: companies.entitiesToSelect,
|
||||||
|
selectedEntity: companies.selectedEntities[0],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -4,8 +4,10 @@ import { useRecoilState, useSetRecoilState } from 'recoil';
|
|||||||
import { pipelineViewFields } from '@/pipeline/constants/pipelineViewFields';
|
import { pipelineViewFields } from '@/pipeline/constants/pipelineViewFields';
|
||||||
import { isBoardLoadedState } from '@/ui/board/states/isBoardLoadedState';
|
import { isBoardLoadedState } from '@/ui/board/states/isBoardLoadedState';
|
||||||
import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState';
|
import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState';
|
||||||
|
import { availableFiltersScopedState } from '@/ui/filter-n-sort/states/availableFiltersScopedState';
|
||||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||||
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import {
|
import {
|
||||||
PipelineProgressableType,
|
PipelineProgressableType,
|
||||||
@ -17,6 +19,7 @@ import {
|
|||||||
useGetPipelineProgressQuery,
|
useGetPipelineProgressQuery,
|
||||||
useGetPipelinesQuery,
|
useGetPipelinesQuery,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
|
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
||||||
|
|
||||||
import { useUpdateCompanyBoardCardIds } from '../hooks/useUpdateBoardCardIds';
|
import { useUpdateCompanyBoardCardIds } from '../hooks/useUpdateBoardCardIds';
|
||||||
import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns';
|
import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns';
|
||||||
@ -30,8 +33,13 @@ export function HooksCompanyBoard({
|
|||||||
const setFieldsDefinitionsState = useSetRecoilState(
|
const setFieldsDefinitionsState = useSetRecoilState(
|
||||||
viewFieldsDefinitionsState,
|
viewFieldsDefinitionsState,
|
||||||
);
|
);
|
||||||
|
const [, setAvailableFilters] = useRecoilScopedState(
|
||||||
|
availableFiltersScopedState,
|
||||||
|
CompanyBoardContext,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setAvailableFilters(opportunitiesBoardOptions.filters);
|
||||||
setFieldsDefinitionsState(pipelineViewFields);
|
setFieldsDefinitionsState(pipelineViewFields);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,46 +0,0 @@
|
|||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
|
||||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
|
||||||
import { IconUserCircle } from '@/ui/icon';
|
|
||||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { UserChip } from '@/users/components/UserChip';
|
|
||||||
import { Company, User } from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { CompanyAccountOwnerPickerFieldEditMode } from './CompanyAccountOwnerPickerFieldEditMode';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
company: Pick<Company, 'id' | 'accountOwnerId'> & {
|
|
||||||
accountOwner?: Pick<User, 'id' | 'displayName' | 'avatarUrl'> | null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CompanyAccountOwnerEditableField({ company }: OwnProps) {
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<RecoilScope>
|
|
||||||
<EditableField
|
|
||||||
customEditHotkeyScope={{
|
|
||||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
|
||||||
}}
|
|
||||||
iconLabel={<IconUserCircle />}
|
|
||||||
editModeContent={
|
|
||||||
<CompanyAccountOwnerPickerFieldEditMode company={company} />
|
|
||||||
}
|
|
||||||
displayModeContent={
|
|
||||||
company.accountOwner?.displayName ? (
|
|
||||||
<UserChip
|
|
||||||
id={company.accountOwner.id}
|
|
||||||
name={company.accountOwner?.displayName ?? ''}
|
|
||||||
pictureUrl={company.accountOwner?.avatarUrl ?? ''}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
isDisplayModeContentEmpty={!company.accountOwner}
|
|
||||||
isDisplayModeFixHeight={true}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import { CompanyAccountOwnerPicker } from '@/companies/components/CompanyAccountOwnerPicker';
|
|
||||||
import { useEditableField } from '@/ui/editable-field/hooks/useEditableField';
|
|
||||||
import { Company, User } from '~/generated/graphql';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
|
||||||
left: 0px;
|
|
||||||
position: absolute;
|
|
||||||
top: -8px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export type OwnProps = {
|
|
||||||
company: Pick<Company, 'id'> & {
|
|
||||||
accountOwner?: Pick<User, 'id' | 'displayName'> | null;
|
|
||||||
};
|
|
||||||
onSubmit?: () => void;
|
|
||||||
onCancel?: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CompanyAccountOwnerPickerFieldEditMode({
|
|
||||||
company,
|
|
||||||
onSubmit,
|
|
||||||
onCancel,
|
|
||||||
}: OwnProps) {
|
|
||||||
const { closeEditableField } = useEditableField();
|
|
||||||
|
|
||||||
function handleSubmit() {
|
|
||||||
closeEditableField();
|
|
||||||
onSubmit?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCancel() {
|
|
||||||
closeEditableField();
|
|
||||||
onCancel?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledContainer>
|
|
||||||
<CompanyAccountOwnerPicker
|
|
||||||
company={company}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
/>
|
|
||||||
</StyledContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
|
||||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
|
||||||
import { IconMap } from '@/ui/icon';
|
|
||||||
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
company: Pick<Company, 'id' | 'address'>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CompanyAddressEditableField({ company }: OwnProps) {
|
|
||||||
const [internalValue, setInternalValue] = useState(company.address);
|
|
||||||
|
|
||||||
const [updateCompany] = useUpdateOneCompanyMutation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setInternalValue(company.address);
|
|
||||||
}, [company.address]);
|
|
||||||
|
|
||||||
async function handleChange(newValue: string) {
|
|
||||||
setInternalValue(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await updateCompany({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: company.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
address: internalValue ?? '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCancel() {
|
|
||||||
setInternalValue(company.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<EditableField
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
iconLabel={<IconMap />}
|
|
||||||
editModeContent={
|
|
||||||
<TextInputEdit
|
|
||||||
placeholder={'Address'}
|
|
||||||
autoFocus
|
|
||||||
value={internalValue}
|
|
||||||
onChange={(newValue: string) => {
|
|
||||||
handleChange(newValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={internalValue ?? ''}
|
|
||||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
|
||||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
|
||||||
import { EditableFieldEditModeDate } from '@/ui/editable-field/variants/components/EditableFieldEditModeDate';
|
|
||||||
import { IconCalendar } from '@/ui/icon';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql';
|
|
||||||
import { formatToHumanReadableDate } from '~/utils';
|
|
||||||
import { parseDate } from '~/utils/date-utils';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
company: Pick<Company, 'id' | 'createdAt'>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CompanyCreatedAtEditableField({ company }: OwnProps) {
|
|
||||||
const [internalValue, setInternalValue] = useState(company.createdAt);
|
|
||||||
|
|
||||||
const [updateCompany] = useUpdateOneCompanyMutation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setInternalValue(company.createdAt);
|
|
||||||
}, [company.createdAt]);
|
|
||||||
|
|
||||||
// TODO: refactor change and submit
|
|
||||||
async function handleChange(newValue: string) {
|
|
||||||
setInternalValue(newValue);
|
|
||||||
|
|
||||||
await updateCompany({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: company.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
createdAt: newValue ?? '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await updateCompany({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: company.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
createdAt: internalValue ?? '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCancel() {
|
|
||||||
setInternalValue(company.createdAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<EditableField
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
iconLabel={<IconCalendar />}
|
|
||||||
editModeContent={
|
|
||||||
<EditableFieldEditModeDate
|
|
||||||
value={internalValue}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={
|
|
||||||
internalValue !== ''
|
|
||||||
? formatToHumanReadableDate(parseDate(internalValue).toJSDate())
|
|
||||||
: 'No date'
|
|
||||||
}
|
|
||||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
|
||||||
import { FieldDisplayURL } from '@/ui/editable-field/components/FieldDisplayURL';
|
|
||||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
|
||||||
import { IconLink } from '@/ui/icon';
|
|
||||||
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
company: Pick<Company, 'id' | 'domainName'>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CompanyDomainNameEditableField({ company }: OwnProps) {
|
|
||||||
const [internalValue, setInternalValue] = useState(company.domainName);
|
|
||||||
|
|
||||||
const [updateCompany] = useUpdateOneCompanyMutation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setInternalValue(company.domainName);
|
|
||||||
}, [company.domainName]);
|
|
||||||
|
|
||||||
async function handleChange(newValue: string) {
|
|
||||||
setInternalValue(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await updateCompany({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: company.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
domainName: internalValue ?? '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCancel() {
|
|
||||||
setInternalValue(company.domainName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<EditableField
|
|
||||||
iconLabel={<IconLink />}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
editModeContent={
|
|
||||||
<TextInputEdit
|
|
||||||
placeholder={'URL'}
|
|
||||||
autoFocus
|
|
||||||
value={internalValue}
|
|
||||||
onChange={(newValue: string) => {
|
|
||||||
handleChange(newValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={<FieldDisplayURL URL={internalValue} />}
|
|
||||||
useEditButton
|
|
||||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
|
||||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
|
||||||
import { IconUsers } from '@/ui/icon';
|
|
||||||
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql';
|
|
||||||
import {
|
|
||||||
canBeCastAsIntegerOrNull,
|
|
||||||
castAsIntegerOrNull,
|
|
||||||
} from '~/utils/cast-as-integer-or-null';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
company: Pick<Company, 'id' | 'employees'>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CompanyEmployeesEditableField({ company }: OwnProps) {
|
|
||||||
const [internalValue, setInternalValue] = useState(
|
|
||||||
company.employees?.toString(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const [updateCompany] = useUpdateOneCompanyMutation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setInternalValue(company.employees?.toString());
|
|
||||||
}, [company.employees]);
|
|
||||||
|
|
||||||
async function handleChange(newValue: string) {
|
|
||||||
setInternalValue(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
if (!canBeCastAsIntegerOrNull(internalValue)) {
|
|
||||||
handleCancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const valueCastedAsNumberOrNull = castAsIntegerOrNull(internalValue);
|
|
||||||
|
|
||||||
await updateCompany({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: company.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
employees: valueCastedAsNumberOrNull,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
setInternalValue(valueCastedAsNumberOrNull?.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCancel() {
|
|
||||||
setInternalValue(company.employees?.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<EditableField
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
iconLabel={<IconUsers />}
|
|
||||||
editModeContent={
|
|
||||||
<TextInputEdit
|
|
||||||
placeholder={'Employees'}
|
|
||||||
autoFocus
|
|
||||||
value={internalValue ?? ''}
|
|
||||||
onChange={(newValue: string) => {
|
|
||||||
handleChange(newValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={internalValue}
|
|
||||||
isDisplayModeContentEmpty={!(internalValue && internalValue !== '0')}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -2,6 +2,7 @@ import { getOperationName } from '@apollo/client/utilities';
|
|||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
||||||
|
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { GET_PIPELINES } from '@/pipeline/queries';
|
import { GET_PIPELINES } from '@/pipeline/queries';
|
||||||
import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry';
|
import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry';
|
||||||
import { IconCheckbox, IconNotes, IconTrash } from '@/ui/icon';
|
import { IconCheckbox, IconNotes, IconTrash } from '@/ui/icon';
|
||||||
@ -11,7 +12,6 @@ import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector
|
|||||||
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
||||||
import {
|
import {
|
||||||
ActivityType,
|
ActivityType,
|
||||||
CommentableType,
|
|
||||||
useDeleteManyCompaniesMutation,
|
useDeleteManyCompaniesMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export function useOpenActionBar() {
|
|||||||
useOpenCreateActivityDrawerForSelectedRowIds();
|
useOpenCreateActivityDrawerForSelectedRowIds();
|
||||||
|
|
||||||
async function handleActivityClick(type: ActivityType) {
|
async function handleActivityClick(type: ActivityType) {
|
||||||
openCreateActivityRightDrawer(type, CommentableType.Company);
|
openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Company);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
|
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { IconCheckbox, IconNotes, IconTrash } from '@tabler/icons-react';
|
|||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
||||||
|
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { GET_PIPELINES } from '@/pipeline/queries';
|
import { GET_PIPELINES } from '@/pipeline/queries';
|
||||||
import { ContextMenuEntry } from '@/ui/context-menu/components/ContextMenuEntry';
|
import { ContextMenuEntry } from '@/ui/context-menu/components/ContextMenuEntry';
|
||||||
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
|
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
|
||||||
@ -11,7 +12,6 @@ import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector
|
|||||||
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
||||||
import {
|
import {
|
||||||
ActivityType,
|
ActivityType,
|
||||||
CommentableType,
|
|
||||||
useDeleteManyCompaniesMutation,
|
useDeleteManyCompaniesMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export function useOpenContextMenu() {
|
|||||||
useOpenCreateActivityDrawerForSelectedRowIds();
|
useOpenCreateActivityDrawerForSelectedRowIds();
|
||||||
|
|
||||||
async function handleButtonClick(type: ActivityType) {
|
async function handleButtonClick(type: ActivityType) {
|
||||||
openCreateActivityRightDrawer(type, CommentableType.Company);
|
openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Company);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
|
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
import { CommentableEntityForSelect } from '@/activities/types/CommentableEntityForSelect';
|
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
import { ActivityTargetableEntityForSelect } from '@/activities/types/ActivityTargetableEntityForSelect';
|
||||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||||
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
|
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
|
||||||
import {
|
import {
|
||||||
CommentableType,
|
|
||||||
CompanyOrderByWithRelationInput as Companies_Order_By,
|
CompanyOrderByWithRelationInput as Companies_Order_By,
|
||||||
CompanyWhereInput as Companies_Bool_Exp,
|
CompanyWhereInput as Companies_Bool_Exp,
|
||||||
SortOrder as Order_By,
|
SortOrder as Order_By,
|
||||||
@ -65,11 +65,11 @@ export function useFilteredSearchCompanyQuery({
|
|||||||
mappingFunction: (company) =>
|
mappingFunction: (company) =>
|
||||||
({
|
({
|
||||||
id: company.id,
|
id: company.id,
|
||||||
entityType: CommentableType.Company,
|
entityType: ActivityTargetableEntityType.Company,
|
||||||
name: company.name,
|
name: company.name,
|
||||||
avatarUrl: getLogoUrlFromDomainName(company.domainName),
|
avatarUrl: getLogoUrlFromDomainName(company.domainName),
|
||||||
avatarType: 'squared',
|
avatarType: 'squared',
|
||||||
} as CommentableEntityForSelect),
|
} as ActivityTargetableEntityForSelect),
|
||||||
searchFilter,
|
searchFilter,
|
||||||
limit,
|
limit,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState';
|
||||||
import { useGetCompanyQuery } from '~/generated/graphql';
|
import { useGetCompanyQuery } from '~/generated/graphql';
|
||||||
|
|
||||||
export const GET_COMPANY = gql`
|
export const GET_COMPANY = gql`
|
||||||
@ -33,5 +35,13 @@ export const GET_COMPANY = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function useCompanyQuery(id: string) {
|
export function useCompanyQuery(id: string) {
|
||||||
return useGetCompanyQuery({ variables: { where: { id } } });
|
const updateCompanyShowPage = useSetRecoilState(
|
||||||
|
genericEntitiesFamilyState(id),
|
||||||
|
);
|
||||||
|
return useGetCompanyQuery({
|
||||||
|
variables: { where: { id } },
|
||||||
|
onCompleted: (data) => {
|
||||||
|
updateCompanyShowPage(data?.findUniqueCompany);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,43 +1,38 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const COMPANY_FIELDS_FRAGMENT = gql`
|
||||||
|
fragment CompanyFieldsFragment on Company {
|
||||||
|
accountOwner {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
displayName
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
address
|
||||||
|
createdAt
|
||||||
|
domainName
|
||||||
|
employees
|
||||||
|
linkedinUrl
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const UPDATE_ONE_COMPANY = gql`
|
export const UPDATE_ONE_COMPANY = gql`
|
||||||
mutation UpdateOneCompany(
|
mutation UpdateOneCompany(
|
||||||
$where: CompanyWhereUniqueInput!
|
$where: CompanyWhereUniqueInput!
|
||||||
$data: CompanyUpdateInput!
|
$data: CompanyUpdateInput!
|
||||||
) {
|
) {
|
||||||
updateOneCompany(data: $data, where: $where) {
|
updateOneCompany(data: $data, where: $where) {
|
||||||
accountOwner {
|
...CompanyFieldsFragment
|
||||||
id
|
|
||||||
email
|
|
||||||
displayName
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
}
|
|
||||||
address
|
|
||||||
createdAt
|
|
||||||
domainName
|
|
||||||
employees
|
|
||||||
linkedinUrl
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const INSERT_COMPANY_FRAGMENT = gql`
|
|
||||||
fragment InsertCompanyFragment on Company {
|
|
||||||
domainName
|
|
||||||
address
|
|
||||||
id
|
|
||||||
name
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const INSERT_ONE_COMPANY = gql`
|
export const INSERT_ONE_COMPANY = gql`
|
||||||
mutation InsertOneCompany($data: CompanyCreateInput!) {
|
mutation InsertOneCompany($data: CompanyCreateInput!) {
|
||||||
createOneCompany(data: $data) {
|
createOneCompany(data: $data) {
|
||||||
...InsertCompanyFragment
|
...CompanyFieldsFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -8,11 +8,14 @@ import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIn
|
|||||||
import { IconList } from '@/ui/icon';
|
import { IconList } from '@/ui/icon';
|
||||||
import { EntityTable } from '@/ui/table/components/EntityTable';
|
import { EntityTable } from '@/ui/table/components/EntityTable';
|
||||||
import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTableData';
|
import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTableData';
|
||||||
|
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
|
||||||
import { TableContext } from '@/ui/table/states/TableContext';
|
import { TableContext } from '@/ui/table/states/TableContext';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
import { useTableViewFields } from '@/views/hooks/useTableViewFields';
|
||||||
import { useViewSorts } from '@/views/hooks/useViewSorts';
|
import { useViewSorts } from '@/views/hooks/useViewSorts';
|
||||||
import { currentViewIdState } from '@/views/states/currentViewIdState';
|
import { currentViewIdState } from '@/views/states/currentViewIdState';
|
||||||
import {
|
import {
|
||||||
|
UpdateOneCompanyMutationVariables,
|
||||||
useGetCompaniesQuery,
|
useGetCompaniesQuery,
|
||||||
useUpdateOneCompanyMutation,
|
useUpdateOneCompanyMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
@ -24,6 +27,13 @@ import { defaultOrderBy } from '../../queries';
|
|||||||
export function CompanyTable() {
|
export function CompanyTable() {
|
||||||
const currentViewId = useRecoilValue(currentViewIdState);
|
const currentViewId = useRecoilValue(currentViewIdState);
|
||||||
const orderBy = useRecoilScopedValue(sortsOrderByScopedState, TableContext);
|
const orderBy = useRecoilScopedValue(sortsOrderByScopedState, TableContext);
|
||||||
|
const [updateEntityMutation] = useUpdateOneCompanyMutation();
|
||||||
|
const upsertEntityTableItem = useUpsertEntityTableItem();
|
||||||
|
|
||||||
|
const { handleColumnsChange } = useTableViewFields({
|
||||||
|
objectName: 'company',
|
||||||
|
viewFieldDefinitions: companyViewFields,
|
||||||
|
});
|
||||||
const { updateSorts } = useViewSorts({
|
const { updateSorts } = useViewSorts({
|
||||||
availableSorts,
|
availableSorts,
|
||||||
Context: TableContext,
|
Context: TableContext,
|
||||||
@ -38,20 +48,33 @@ export function CompanyTable() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<GenericEntityTableData
|
<GenericEntityTableData
|
||||||
objectName="company"
|
|
||||||
getRequestResultKey="companies"
|
getRequestResultKey="companies"
|
||||||
useGetRequest={useGetCompaniesQuery}
|
useGetRequest={useGetCompaniesQuery}
|
||||||
orderBy={orderBy.length ? orderBy : defaultOrderBy}
|
orderBy={orderBy.length ? orderBy : defaultOrderBy}
|
||||||
whereFilters={whereFilters}
|
whereFilters={whereFilters}
|
||||||
viewFieldDefinitions={companyViewFields}
|
|
||||||
filterDefinitionArray={companiesFilters}
|
filterDefinitionArray={companiesFilters}
|
||||||
/>
|
/>
|
||||||
<EntityTable
|
<EntityTable
|
||||||
viewName="All Companies"
|
viewName="All Companies"
|
||||||
viewIcon={<IconList size={16} />}
|
viewIcon={<IconList size={16} />}
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
|
onColumnsChange={handleColumnsChange}
|
||||||
onSortsUpdate={currentViewId ? updateSorts : undefined}
|
onSortsUpdate={currentViewId ? updateSorts : undefined}
|
||||||
useUpdateEntityMutation={useUpdateOneCompanyMutation}
|
updateEntityMutation={({
|
||||||
|
variables,
|
||||||
|
}: {
|
||||||
|
variables: UpdateOneCompanyMutationVariables;
|
||||||
|
}) =>
|
||||||
|
updateEntityMutation({
|
||||||
|
variables,
|
||||||
|
onCompleted: (data) => {
|
||||||
|
if (!data.updateOneCompany) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
upsertEntityTableItem(data.updateOneCompany);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,32 +2,21 @@ import { useEffect } from 'react';
|
|||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
|
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
|
||||||
import { entityTableDimensionsState } from '@/ui/table/states/entityTableDimensionsState';
|
import { tableColumnsState } from '@/ui/table/states/tableColumnsState';
|
||||||
import { viewFieldsState } from '@/ui/table/states/viewFieldsState';
|
|
||||||
|
|
||||||
import { companyViewFields } from '../../constants/companyViewFields';
|
import { companyViewFields } from '../../constants/companyViewFields';
|
||||||
|
|
||||||
import { mockedCompaniesData } from './companies-mock-data';
|
import { mockedCompaniesData } from './companies-mock-data';
|
||||||
|
|
||||||
export function CompanyTableMockData() {
|
export function CompanyTableMockData() {
|
||||||
const setEntityTableDimensions = useSetRecoilState(
|
const setColumns = useSetRecoilState(tableColumnsState);
|
||||||
entityTableDimensionsState,
|
|
||||||
);
|
|
||||||
const setViewFieldsState = useSetRecoilState(viewFieldsState);
|
|
||||||
const setEntityTableData = useSetEntityTableData();
|
const setEntityTableData = useSetEntityTableData();
|
||||||
|
|
||||||
setEntityTableData(mockedCompaniesData, []);
|
setEntityTableData(mockedCompaniesData, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setViewFieldsState({
|
setColumns(companyViewFields);
|
||||||
objectName: 'company',
|
}, [setColumns]);
|
||||||
viewFields: companyViewFields,
|
|
||||||
});
|
|
||||||
setEntityTableDimensions((prevState) => ({
|
|
||||||
...prevState,
|
|
||||||
numberOfColumns: companyViewFields.length,
|
|
||||||
}));
|
|
||||||
}, [setEntityTableDimensions, setViewFieldsState]);
|
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export function CompanyTableMockMode() {
|
|||||||
viewName="All Companies"
|
viewName="All Companies"
|
||||||
viewIcon={<IconList size={16} />}
|
viewIcon={<IconList size={16} />}
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
useUpdateEntityMutation={useUpdateOneCompanyMutation}
|
updateEntityMutation={[useUpdateOneCompanyMutation()]}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,73 +0,0 @@
|
|||||||
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
|
|
||||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
|
||||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
|
||||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
|
||||||
import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
|
|
||||||
import { isCreateModeScopedState } from '@/ui/table/editable-cell/states/isCreateModeScopedState';
|
|
||||||
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
|
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
import {
|
|
||||||
Company,
|
|
||||||
Person,
|
|
||||||
useUpdateOnePersonMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
export type OwnProps = {
|
|
||||||
people: Pick<Person, 'id'> & { company?: Pick<Company, 'id'> | null };
|
|
||||||
};
|
|
||||||
|
|
||||||
export function PeopleCompanyPicker({ people }: OwnProps) {
|
|
||||||
const [, setIsCreating] = useRecoilScopedState(isCreateModeScopedState);
|
|
||||||
|
|
||||||
const [searchFilter] = useRecoilScopedState(
|
|
||||||
relationPickerSearchFilterScopedState,
|
|
||||||
);
|
|
||||||
const [updatePerson] = useUpdateOnePersonMutation();
|
|
||||||
|
|
||||||
const { closeEditableCell } = useEditableCell();
|
|
||||||
|
|
||||||
const addToScopeStack = useSetHotkeyScope();
|
|
||||||
|
|
||||||
const companies = useFilteredSearchCompanyQuery({
|
|
||||||
searchFilter,
|
|
||||||
selectedIds: people.company?.id ? [people.company.id] : [],
|
|
||||||
});
|
|
||||||
|
|
||||||
async function handleEntitySelected(
|
|
||||||
entity: EntityForSelect | null | undefined,
|
|
||||||
) {
|
|
||||||
if (entity) {
|
|
||||||
await updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: people.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
company: { connect: { id: entity.id } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeEditableCell();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCreate() {
|
|
||||||
setIsCreating(true);
|
|
||||||
addToScopeStack(TableHotkeyScope.CellDoubleTextInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SingleEntitySelect
|
|
||||||
onCreate={handleCreate}
|
|
||||||
onCancel={() => closeEditableCell()}
|
|
||||||
onEntitySelected={handleEntitySelected}
|
|
||||||
entities={{
|
|
||||||
entitiesToSelect: companies.entitiesToSelect,
|
|
||||||
selectedEntity: companies.selectedEntities[0],
|
|
||||||
loading: companies.loading,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
import {
|
|
||||||
IconCalendar,
|
|
||||||
IconMail,
|
|
||||||
IconMap,
|
|
||||||
IconPhone,
|
|
||||||
} from '@tabler/icons-react';
|
|
||||||
|
|
||||||
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
|
|
||||||
import { DateEditableField } from '@/ui/editable-field/variants/components/DateEditableField';
|
|
||||||
import { PhoneEditableField } from '@/ui/editable-field/variants/components/PhoneEditableField';
|
|
||||||
import { TextEditableField } from '@/ui/editable-field/variants/components/TextEditableField';
|
|
||||||
import {
|
|
||||||
Company,
|
|
||||||
Person,
|
|
||||||
useUpdateOnePersonMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { PeopleCompanyEditableField } from '../editable-field/components/PeopleCompanyEditableField';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
person: Pick<
|
|
||||||
Person,
|
|
||||||
'id' | 'city' | 'email' | 'displayName' | 'phone' | 'createdAt'
|
|
||||||
> & {
|
|
||||||
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function PersonPropertyBox({ person }: OwnProps) {
|
|
||||||
const [updatePerson] = useUpdateOnePersonMutation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PropertyBox extraPadding={true}>
|
|
||||||
<TextEditableField
|
|
||||||
value={person.email}
|
|
||||||
icon={<IconMail />}
|
|
||||||
placeholder={'Email'}
|
|
||||||
onSubmit={(newEmail) => {
|
|
||||||
updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: person.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
email: newEmail,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PhoneEditableField
|
|
||||||
value={person.phone}
|
|
||||||
icon={<IconPhone />}
|
|
||||||
placeholder={'Phone'}
|
|
||||||
onSubmit={(newPhone) => {
|
|
||||||
updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: person.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
phone: newPhone,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<DateEditableField
|
|
||||||
value={person.createdAt}
|
|
||||||
icon={<IconCalendar />}
|
|
||||||
onSubmit={(newDate) => {
|
|
||||||
updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: person.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
createdAt: newDate,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PeopleCompanyEditableField people={person} />
|
|
||||||
<TextEditableField
|
|
||||||
value={person.city}
|
|
||||||
icon={<IconMap />}
|
|
||||||
placeholder={'City'}
|
|
||||||
onSubmit={(newCity) => {
|
|
||||||
updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: person.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
city: newCity,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</PropertyBox>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import { BrowserRouter } from 'react-router-dom';
|
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
|
||||||
|
|
||||||
import { mockedPeopleData } from '~/testing/mock-data/people';
|
|
||||||
|
|
||||||
import { PeopleCompanyEditableField } from '../../editable-field/components/PeopleCompanyEditableField';
|
|
||||||
|
|
||||||
const meta: Meta<typeof PeopleCompanyEditableField> = {
|
|
||||||
title: 'Modules/People/EditableFields/PeopleCompanyEditableField',
|
|
||||||
component: PeopleCompanyEditableField,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default meta;
|
|
||||||
type Story = StoryObj<typeof PeopleCompanyEditableField>;
|
|
||||||
|
|
||||||
export const Default: Story = {
|
|
||||||
render: () => (
|
|
||||||
<BrowserRouter>
|
|
||||||
<PeopleCompanyEditableField people={mockedPeopleData[0]} />
|
|
||||||
</BrowserRouter>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
import { IconBuildingSkyscraper } from '@tabler/icons-react';
|
|
||||||
|
|
||||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
|
||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
|
||||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
|
||||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { Company, Person } from '~/generated/graphql';
|
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
|
||||||
|
|
||||||
import { PeopleCompanyEditableFieldEditMode } from './PeopleCompanyEditableFieldEditMode';
|
|
||||||
|
|
||||||
export type OwnProps = {
|
|
||||||
people: Pick<Person, 'id'> & {
|
|
||||||
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function PeopleCompanyEditableField({ people }: OwnProps) {
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<RecoilScope>
|
|
||||||
<EditableField
|
|
||||||
useEditButton
|
|
||||||
customEditHotkeyScope={{
|
|
||||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
|
||||||
}}
|
|
||||||
iconLabel={<IconBuildingSkyscraper />}
|
|
||||||
editModeContent={
|
|
||||||
<PeopleCompanyEditableFieldEditMode people={people} />
|
|
||||||
}
|
|
||||||
displayModeContent={
|
|
||||||
people.company ? (
|
|
||||||
<CompanyChip
|
|
||||||
id={people.company.id}
|
|
||||||
name={people.company.name}
|
|
||||||
pictureUrl={getLogoUrlFromDomainName(people.company.domainName)}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
isDisplayModeContentEmpty={!people.company}
|
|
||||||
isDisplayModeFixHeight
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
|
|
||||||
import { useEditableField } from '@/ui/editable-field/hooks/useEditableField';
|
|
||||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
|
||||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
|
||||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
import {
|
|
||||||
Company,
|
|
||||||
Person,
|
|
||||||
useUpdateOnePersonMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
export type OwnProps = {
|
|
||||||
people: Pick<Person, 'id'> & { company?: Pick<Company, 'id'> | null };
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
|
||||||
left: 0px;
|
|
||||||
position: absolute;
|
|
||||||
top: -8px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export function PeopleCompanyEditableFieldEditMode({ people }: OwnProps) {
|
|
||||||
const { closeEditableField } = useEditableField();
|
|
||||||
|
|
||||||
const [searchFilter] = useRecoilScopedState(
|
|
||||||
relationPickerSearchFilterScopedState,
|
|
||||||
);
|
|
||||||
const [updatePerson] = useUpdateOnePersonMutation();
|
|
||||||
|
|
||||||
const companies = useFilteredSearchCompanyQuery({
|
|
||||||
searchFilter,
|
|
||||||
selectedIds: people.company?.id ? [people.company.id] : [],
|
|
||||||
});
|
|
||||||
|
|
||||||
async function handleEntitySelected(
|
|
||||||
entity: EntityForSelect | null | undefined,
|
|
||||||
) {
|
|
||||||
if (entity) {
|
|
||||||
await updatePerson({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: people.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
company: { connect: { id: entity.id } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeEditableField();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCancel() {
|
|
||||||
closeEditableField();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledContainer>
|
|
||||||
<SingleEntitySelect
|
|
||||||
onEntitySelected={handleEntitySelected}
|
|
||||||
entities={{
|
|
||||||
entitiesToSelect: companies.entitiesToSelect,
|
|
||||||
selectedEntity: companies.selectedEntities[0],
|
|
||||||
loading: companies.loading,
|
|
||||||
}}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
/>
|
|
||||||
</StyledContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -2,17 +2,14 @@ import { getOperationName } from '@apollo/client/utilities';
|
|||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
||||||
|
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry';
|
import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry';
|
||||||
import { IconCheckbox, IconNotes, IconTrash } from '@/ui/icon';
|
import { IconCheckbox, IconNotes, IconTrash } from '@/ui/icon';
|
||||||
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
|
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
|
||||||
import { actionBarEntriesState } from '@/ui/table/states/ActionBarEntriesState';
|
import { actionBarEntriesState } from '@/ui/table/states/ActionBarEntriesState';
|
||||||
import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector';
|
import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector';
|
||||||
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
||||||
import {
|
import { ActivityType, useDeleteManyPersonMutation } from '~/generated/graphql';
|
||||||
ActivityType,
|
|
||||||
CommentableType,
|
|
||||||
useDeleteManyPersonMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { GET_PEOPLE } from '../queries';
|
import { GET_PEOPLE } from '../queries';
|
||||||
|
|
||||||
@ -23,7 +20,7 @@ export function useOpenActionBar() {
|
|||||||
useOpenCreateActivityDrawerForSelectedRowIds();
|
useOpenCreateActivityDrawerForSelectedRowIds();
|
||||||
|
|
||||||
async function handleActivityClick(type: ActivityType) {
|
async function handleActivityClick(type: ActivityType) {
|
||||||
openCreateActivityRightDrawer(type, CommentableType.Person);
|
openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Person);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
|
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
|
||||||
|
|||||||
@ -3,16 +3,13 @@ import { IconCheckbox, IconNotes, IconTrash } from '@tabler/icons-react';
|
|||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
||||||
|
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
|
||||||
import { ContextMenuEntry } from '@/ui/context-menu/components/ContextMenuEntry';
|
import { ContextMenuEntry } from '@/ui/context-menu/components/ContextMenuEntry';
|
||||||
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
|
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
|
||||||
import { contextMenuEntriesState } from '@/ui/table/states/ContextMenuEntriesState';
|
import { contextMenuEntriesState } from '@/ui/table/states/ContextMenuEntriesState';
|
||||||
import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector';
|
import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector';
|
||||||
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
||||||
import {
|
import { ActivityType, useDeleteManyPersonMutation } from '~/generated/graphql';
|
||||||
ActivityType,
|
|
||||||
CommentableType,
|
|
||||||
useDeleteManyPersonMutation,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { GET_PEOPLE } from '../queries';
|
import { GET_PEOPLE } from '../queries';
|
||||||
|
|
||||||
@ -23,7 +20,7 @@ export function useOpenContextMenu() {
|
|||||||
useOpenCreateActivityDrawerForSelectedRowIds();
|
useOpenCreateActivityDrawerForSelectedRowIds();
|
||||||
|
|
||||||
async function handleActivityClick(type: ActivityType) {
|
async function handleActivityClick(type: ActivityType) {
|
||||||
openCreateActivityRightDrawer(type, CommentableType.Person);
|
openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Person);
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
|
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { availableFiltersScopedState } from '@/ui/filter-n-sort/states/availableFiltersScopedState';
|
||||||
|
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
|
||||||
|
import { isFetchingEntityTableDataState } from '@/ui/table/states/isFetchingEntityTableDataState';
|
||||||
|
import { numberOfTableRowsState } from '@/ui/table/states/numberOfTableRowsState';
|
||||||
|
import { TableContext } from '@/ui/table/states/TableContext';
|
||||||
|
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
||||||
import { currentPageLocationState } from '@/ui/utilities/loading-state/states/currentPageLocationState';
|
import { currentPageLocationState } from '@/ui/utilities/loading-state/states/currentPageLocationState';
|
||||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
||||||
import { GetPeopleQuery } from '~/generated/graphql';
|
import { GetPeopleQuery } from '~/generated/graphql';
|
||||||
|
import { peopleFilters } from '~/pages/people/people-filters';
|
||||||
|
|
||||||
import { peopleFilters } from '../../../pages/people/people-filters';
|
|
||||||
import { availableFiltersScopedState } from '../../ui/filter-n-sort/states/availableFiltersScopedState';
|
|
||||||
import { useResetTableRowSelection } from '../../ui/table/hooks/useResetTableRowSelection';
|
|
||||||
import { entityTableDimensionsState } from '../../ui/table/states/entityTableDimensionsState';
|
|
||||||
import { isFetchingEntityTableDataState } from '../../ui/table/states/isFetchingEntityTableDataState';
|
|
||||||
import { TableContext } from '../../ui/table/states/TableContext';
|
|
||||||
import { tableRowIdsState } from '../../ui/table/states/tableRowIdsState';
|
|
||||||
import { peopleCityFamilyState } from '../states/peopleCityFamilyState';
|
import { peopleCityFamilyState } from '../states/peopleCityFamilyState';
|
||||||
import { peopleCompanyFamilyState } from '../states/peopleCompanyFamilyState';
|
import { peopleCompanyFamilyState } from '../states/peopleCompanyFamilyState';
|
||||||
import { peopleCreatedAtFamilyState } from '../states/peopleCreatedAtFamilyState';
|
import { peopleCreatedAtFamilyState } from '../states/peopleCreatedAtFamilyState';
|
||||||
@ -124,10 +124,7 @@ export function useSetPeopleEntityTable() {
|
|||||||
|
|
||||||
resetTableRowSelection();
|
resetTableRowSelection();
|
||||||
|
|
||||||
set(entityTableDimensionsState, {
|
set(numberOfTableRowsState, peopleIds.length);
|
||||||
numberOfColumns: 10,
|
|
||||||
numberOfRows: peopleIds.length,
|
|
||||||
});
|
|
||||||
|
|
||||||
set(availableFiltersScopedState(tableContextScopeId), peopleFilters);
|
set(availableFiltersScopedState(tableContextScopeId), peopleFilters);
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
import { CommentableEntityForSelect } from '@/activities/types/CommentableEntityForSelect';
|
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
|
||||||
|
import { ActivityTargetableEntityForSelect } from '@/activities/types/ActivityTargetableEntityForSelect';
|
||||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||||
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
|
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
|
||||||
import {
|
import {
|
||||||
CommentableType,
|
|
||||||
PersonOrderByWithRelationInput as People_Order_By,
|
PersonOrderByWithRelationInput as People_Order_By,
|
||||||
PersonWhereInput as People_Bool_Exp,
|
PersonWhereInput as People_Bool_Exp,
|
||||||
SortOrder,
|
SortOrder,
|
||||||
@ -69,11 +69,11 @@ export function useFilteredSearchPeopleQuery({
|
|||||||
mappingFunction: (entity) =>
|
mappingFunction: (entity) =>
|
||||||
({
|
({
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
entityType: CommentableType.Person,
|
entityType: ActivityTargetableEntityType.Person,
|
||||||
name: `${entity.firstName} ${entity.lastName}`,
|
name: `${entity.firstName} ${entity.lastName}`,
|
||||||
avatarUrl: entity.avatarUrl,
|
avatarUrl: entity.avatarUrl,
|
||||||
avatarType: 'rounded',
|
avatarType: 'rounded',
|
||||||
} as CommentableEntityForSelect),
|
} as ActivityTargetableEntityForSelect),
|
||||||
searchFilter,
|
searchFilter,
|
||||||
limit,
|
limit,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState';
|
||||||
import { useGetPersonQuery } from '~/generated/graphql';
|
import { useGetPersonQuery } from '~/generated/graphql';
|
||||||
|
|
||||||
export const GET_PERSON = gql`
|
export const GET_PERSON = gql`
|
||||||
@ -37,5 +39,13 @@ export const GET_PERSON = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function usePersonQuery(id: string) {
|
export function usePersonQuery(id: string) {
|
||||||
return useGetPersonQuery({ variables: { id } });
|
const updatePersonShowPage = useSetRecoilState(
|
||||||
|
genericEntitiesFamilyState(id),
|
||||||
|
);
|
||||||
|
return useGetPersonQuery({
|
||||||
|
variables: { id },
|
||||||
|
onCompleted: (data) => {
|
||||||
|
updatePersonShowPage(data?.findUniquePerson);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,14 @@ import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIn
|
|||||||
import { IconList } from '@/ui/icon';
|
import { IconList } from '@/ui/icon';
|
||||||
import { EntityTable } from '@/ui/table/components/EntityTable';
|
import { EntityTable } from '@/ui/table/components/EntityTable';
|
||||||
import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTableData';
|
import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTableData';
|
||||||
|
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
|
||||||
import { TableContext } from '@/ui/table/states/TableContext';
|
import { TableContext } from '@/ui/table/states/TableContext';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
import { useTableViewFields } from '@/views/hooks/useTableViewFields';
|
||||||
import { useViewSorts } from '@/views/hooks/useViewSorts';
|
import { useViewSorts } from '@/views/hooks/useViewSorts';
|
||||||
import { currentViewIdState } from '@/views/states/currentViewIdState';
|
import { currentViewIdState } from '@/views/states/currentViewIdState';
|
||||||
import {
|
import {
|
||||||
|
UpdateOnePersonMutationVariables,
|
||||||
useGetPeopleQuery,
|
useGetPeopleQuery,
|
||||||
useUpdateOnePersonMutation,
|
useUpdateOnePersonMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
@ -24,6 +27,13 @@ import { defaultOrderBy } from '../../queries';
|
|||||||
export function PeopleTable() {
|
export function PeopleTable() {
|
||||||
const currentViewId = useRecoilValue(currentViewIdState);
|
const currentViewId = useRecoilValue(currentViewIdState);
|
||||||
const orderBy = useRecoilScopedValue(sortsOrderByScopedState, TableContext);
|
const orderBy = useRecoilScopedValue(sortsOrderByScopedState, TableContext);
|
||||||
|
const [updateEntityMutation] = useUpdateOnePersonMutation();
|
||||||
|
const upsertEntityTableItem = useUpsertEntityTableItem();
|
||||||
|
|
||||||
|
const { handleColumnsChange } = useTableViewFields({
|
||||||
|
objectName: 'person',
|
||||||
|
viewFieldDefinitions: peopleViewFields,
|
||||||
|
});
|
||||||
const { updateSorts } = useViewSorts({
|
const { updateSorts } = useViewSorts({
|
||||||
availableSorts,
|
availableSorts,
|
||||||
Context: TableContext,
|
Context: TableContext,
|
||||||
@ -38,20 +48,33 @@ export function PeopleTable() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<GenericEntityTableData
|
<GenericEntityTableData
|
||||||
objectName="person"
|
|
||||||
getRequestResultKey="people"
|
getRequestResultKey="people"
|
||||||
useGetRequest={useGetPeopleQuery}
|
useGetRequest={useGetPeopleQuery}
|
||||||
orderBy={orderBy.length ? orderBy : defaultOrderBy}
|
orderBy={orderBy.length ? orderBy : defaultOrderBy}
|
||||||
whereFilters={whereFilters}
|
whereFilters={whereFilters}
|
||||||
viewFieldDefinitions={peopleViewFields}
|
|
||||||
filterDefinitionArray={peopleFilters}
|
filterDefinitionArray={peopleFilters}
|
||||||
/>
|
/>
|
||||||
<EntityTable
|
<EntityTable
|
||||||
viewName="All People"
|
viewName="All People"
|
||||||
viewIcon={<IconList size={16} />}
|
viewIcon={<IconList size={16} />}
|
||||||
availableSorts={availableSorts}
|
availableSorts={availableSorts}
|
||||||
|
onColumnsChange={handleColumnsChange}
|
||||||
onSortsUpdate={currentViewId ? updateSorts : undefined}
|
onSortsUpdate={currentViewId ? updateSorts : undefined}
|
||||||
useUpdateEntityMutation={useUpdateOnePersonMutation}
|
updateEntityMutation={({
|
||||||
|
variables,
|
||||||
|
}: {
|
||||||
|
variables: UpdateOnePersonMutationVariables;
|
||||||
|
}) =>
|
||||||
|
updateEntityMutation({
|
||||||
|
variables,
|
||||||
|
onCompleted: (data) => {
|
||||||
|
if (!data.updateOnePerson) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
upsertEntityTableItem(data.updateOnePerson);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -61,6 +61,7 @@ export const pipelineViewFields: ViewFieldDefinition<ViewFieldMetadata>[] = [
|
|||||||
type: 'relation',
|
type: 'relation',
|
||||||
fieldName: 'pointOfContact',
|
fieldName: 'pointOfContact',
|
||||||
relationType: Entity.Person,
|
relationType: Entity.Person,
|
||||||
|
useEditButton: true,
|
||||||
},
|
},
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
} satisfies ViewFieldDefinition<ViewFieldRelationMetadata>,
|
} satisfies ViewFieldDefinition<ViewFieldRelationMetadata>,
|
||||||
|
|||||||
@ -64,9 +64,7 @@ export const SEARCH_COMPANY_QUERY = gql`
|
|||||||
take: $limit
|
take: $limit
|
||||||
orderBy: $orderBy
|
orderBy: $orderBy
|
||||||
) {
|
) {
|
||||||
id
|
...CompanyFieldsFragment
|
||||||
name
|
|
||||||
domainName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
import { ReactElement } from 'react';
|
|
||||||
|
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
|
|
||||||
import { BoardCardFieldContext } from '../states/BoardCardFieldContext';
|
|
||||||
|
|
||||||
import { BoardCardEditableFieldInternal } from './BoardCardEditableFieldInternal';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
editModeContent: ReactElement;
|
|
||||||
nonEditModeContent: ReactElement;
|
|
||||||
editModeHorizontalAlign?: 'left' | 'right';
|
|
||||||
editModeVerticalPosition?: 'over' | 'below';
|
|
||||||
editHotkeyScope?: HotkeyScope;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function BoardCardEditableField(props: OwnProps) {
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={BoardCardFieldContext}>
|
|
||||||
<BoardCardEditableFieldInternal {...props} />
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
import { useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import { DateInputDisplay } from '@/ui/input/date/components/DateInputDisplay';
|
|
||||||
import { debounce } from '~/utils/debounce';
|
|
||||||
|
|
||||||
import { BoardCardEditableField } from './BoardCardEditableField';
|
|
||||||
import { BoardCardEditableFieldDateEditMode } from './BoardCardEditableFieldDateEditMode';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
value: Date;
|
|
||||||
onChange: (newValue: Date) => void;
|
|
||||||
editModeHorizontalAlign?: 'left' | 'right';
|
|
||||||
};
|
|
||||||
|
|
||||||
export function BoardCardEditableFieldDate({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
editModeHorizontalAlign,
|
|
||||||
}: OwnProps) {
|
|
||||||
const [internalValue, setInternalValue] = useState(value);
|
|
||||||
const debouncedOnChange = useMemo(() => {
|
|
||||||
return debounce(onChange, 200);
|
|
||||||
}, [onChange]);
|
|
||||||
return (
|
|
||||||
<BoardCardEditableField
|
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
|
||||||
editModeContent={
|
|
||||||
<BoardCardEditableFieldDateEditMode
|
|
||||||
value={internalValue}
|
|
||||||
onChange={(date: Date) => {
|
|
||||||
setInternalValue(date);
|
|
||||||
debouncedOnChange(date);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
nonEditModeContent={<DateInputDisplay value={value} />}
|
|
||||||
></BoardCardEditableField>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { DateInputEdit } from '@/ui/input/date/components/DateInputEdit';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
value: Date;
|
|
||||||
onChange: (newValue: Date) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function BoardCardEditableFieldDateEditMode({
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
}: OwnProps) {
|
|
||||||
function handleDateChange(newDate: Date) {
|
|
||||||
onChange(newDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <DateInputEdit value={value} onChange={handleDateChange} />;
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
export const BoardCardFieldDisplayModeOuterContainer = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
|
||||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const BoardCardFieldDisplayModeInnerContainer = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export function BoardCardEditableFieldDisplayMode({
|
|
||||||
children,
|
|
||||||
}: React.PropsWithChildren<unknown>) {
|
|
||||||
return (
|
|
||||||
<BoardCardFieldDisplayModeOuterContainer>
|
|
||||||
<BoardCardFieldDisplayModeInnerContainer>
|
|
||||||
{children}
|
|
||||||
</BoardCardFieldDisplayModeInnerContainer>
|
|
||||||
</BoardCardFieldDisplayModeOuterContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
import { ReactElement, useRef } from 'react';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import { overlayBackground } from '@/ui/theme/constants/effects';
|
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
|
||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
|
||||||
|
|
||||||
import { BoardCardFieldHotkeyScope } from '../types/BoardCardFieldHotkeyScope';
|
|
||||||
|
|
||||||
export const BoardCardFieldEditModeContainer = styled.div<
|
|
||||||
Omit<OwnProps, 'onExit'>
|
|
||||||
>`
|
|
||||||
align-items: center;
|
|
||||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
|
||||||
display: flex;
|
|
||||||
left: ${(props) =>
|
|
||||||
props.editModeHorizontalAlign === 'right' ? 'auto' : '0'};
|
|
||||||
margin-left: -2px;
|
|
||||||
min-height: 100%;
|
|
||||||
min-width: calc(100% + 20px);
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
right: ${(props) =>
|
|
||||||
props.editModeHorizontalAlign === 'right' ? '0' : 'auto'};
|
|
||||||
top: ${(props) => (props.editModeVerticalPosition === 'over' ? '0' : '100%')};
|
|
||||||
z-index: 1;
|
|
||||||
${overlayBackground}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
children: ReactElement;
|
|
||||||
editModeHorizontalAlign?: 'left' | 'right';
|
|
||||||
editModeVerticalPosition?: 'over' | 'below';
|
|
||||||
onExit: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function BoardCardEditableFieldEditMode({
|
|
||||||
editModeHorizontalAlign,
|
|
||||||
editModeVerticalPosition,
|
|
||||||
children,
|
|
||||||
onExit,
|
|
||||||
}: OwnProps) {
|
|
||||||
const wrapperRef = useRef(null);
|
|
||||||
|
|
||||||
useListenClickOutside({
|
|
||||||
refs: [wrapperRef],
|
|
||||||
callback: () => {
|
|
||||||
onExit();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
useScopedHotkeys(
|
|
||||||
'enter',
|
|
||||||
() => {
|
|
||||||
onExit();
|
|
||||||
},
|
|
||||||
BoardCardFieldHotkeyScope.BoardCardFieldEditMode,
|
|
||||||
[onExit],
|
|
||||||
);
|
|
||||||
|
|
||||||
useScopedHotkeys(
|
|
||||||
'esc',
|
|
||||||
() => {
|
|
||||||
onExit();
|
|
||||||
},
|
|
||||||
BoardCardFieldHotkeyScope.BoardCardFieldEditMode,
|
|
||||||
[onExit],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BoardCardFieldEditModeContainer
|
|
||||||
data-testid="editable-cell-edit-mode-container"
|
|
||||||
ref={wrapperRef}
|
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
|
||||||
editModeVerticalPosition={editModeVerticalPosition}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</BoardCardFieldEditModeContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
import { ReactElement } from 'react';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
|
||||||
|
|
||||||
import { useBoardCardField } from '../hooks/useBoardCardField';
|
|
||||||
import { BoardCardFieldHotkeyScope } from '../types/BoardCardFieldHotkeyScope';
|
|
||||||
|
|
||||||
import { BoardCardEditableFieldDisplayMode } from './BoardCardEditableFieldDisplayMode';
|
|
||||||
import { BoardCardEditableFieldEditMode } from './BoardCardEditableFieldEditMode';
|
|
||||||
|
|
||||||
export const BoardCardFieldContainer = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
height: 32px;
|
|
||||||
position: relative;
|
|
||||||
user-select: none;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
editModeContent: ReactElement;
|
|
||||||
nonEditModeContent: ReactElement;
|
|
||||||
editModeHorizontalAlign?: 'left' | 'right';
|
|
||||||
editModeVerticalPosition?: 'over' | 'below';
|
|
||||||
editHotkeyScope?: HotkeyScope;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function BoardCardEditableFieldInternal({
|
|
||||||
editModeHorizontalAlign = 'left',
|
|
||||||
editModeVerticalPosition = 'over',
|
|
||||||
editModeContent,
|
|
||||||
nonEditModeContent,
|
|
||||||
editHotkeyScope,
|
|
||||||
}: OwnProps) {
|
|
||||||
const { openBoardCardField, isBoardCardFieldInEditMode } =
|
|
||||||
useBoardCardField();
|
|
||||||
|
|
||||||
const { closeBoardCardField } = useBoardCardField();
|
|
||||||
|
|
||||||
const {
|
|
||||||
goBackToPreviousHotkeyScope,
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope,
|
|
||||||
} = usePreviousHotkeyScope();
|
|
||||||
|
|
||||||
function handleOnClick() {
|
|
||||||
if (!isBoardCardFieldInEditMode) {
|
|
||||||
openBoardCardField();
|
|
||||||
setHotkeyScopeAndMemorizePreviousScope(
|
|
||||||
editHotkeyScope?.scope ??
|
|
||||||
BoardCardFieldHotkeyScope.BoardCardFieldEditMode,
|
|
||||||
editHotkeyScope?.customScopes ?? {},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleEditModeExit() {
|
|
||||||
goBackToPreviousHotkeyScope();
|
|
||||||
closeBoardCardField();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BoardCardFieldContainer onClick={handleOnClick}>
|
|
||||||
{isBoardCardFieldInEditMode ? (
|
|
||||||
<BoardCardEditableFieldEditMode
|
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
|
||||||
editModeVerticalPosition={editModeVerticalPosition}
|
|
||||||
onExit={handleEditModeExit}
|
|
||||||
>
|
|
||||||
{editModeContent}
|
|
||||||
</BoardCardEditableFieldEditMode>
|
|
||||||
) : (
|
|
||||||
<BoardCardEditableFieldDisplayMode>
|
|
||||||
{nonEditModeContent}
|
|
||||||
</BoardCardEditableFieldDisplayMode>
|
|
||||||
)}
|
|
||||||
</BoardCardFieldContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
import { ChangeEvent, useMemo, useState } from 'react';
|
|
||||||
|
|
||||||
import { TextInputDisplay } from '@/ui/input/text/components/TextInputDisplay';
|
|
||||||
import { StyledInput } from '@/ui/table/editable-cell/type/components/TextCellEdit';
|
|
||||||
import { debounce } from '~/utils/debounce';
|
|
||||||
|
|
||||||
import { BoardCardEditableField } from './BoardCardEditableField';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
placeholder?: string;
|
|
||||||
value: string;
|
|
||||||
onChange: (newValue: string) => void;
|
|
||||||
editModeHorizontalAlign?: 'left' | 'right';
|
|
||||||
};
|
|
||||||
|
|
||||||
export function BoardCardEditableFieldText({
|
|
||||||
value,
|
|
||||||
placeholder,
|
|
||||||
onChange,
|
|
||||||
editModeHorizontalAlign,
|
|
||||||
}: OwnProps) {
|
|
||||||
const [internalValue, setInternalValue] = useState(value);
|
|
||||||
|
|
||||||
const debouncedOnChange = useMemo(() => {
|
|
||||||
return debounce(onChange, 200);
|
|
||||||
}, [onChange]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BoardCardEditableField
|
|
||||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
|
||||||
editModeContent={
|
|
||||||
<StyledInput
|
|
||||||
placeholder={placeholder || ''}
|
|
||||||
autoFocus
|
|
||||||
value={internalValue}
|
|
||||||
autoComplete="off"
|
|
||||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setInternalValue(event.target.value);
|
|
||||||
debouncedOnChange(event.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
nonEditModeContent={<TextInputDisplay>{value}</TextInputDisplay>}
|
|
||||||
></BoardCardEditableField>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
|
|
||||||
import { BoardCardFieldContext } from '../states/BoardCardFieldContext';
|
|
||||||
import { isBoardCardFieldInEditModeScopedState } from '../states/isBoardCardFieldInEditModeScopedState';
|
|
||||||
|
|
||||||
export function useBoardCardField() {
|
|
||||||
const [isBoardCardFieldInEditMode, setIsBoardCardFieldInEditMode] =
|
|
||||||
useRecoilScopedState(
|
|
||||||
isBoardCardFieldInEditModeScopedState,
|
|
||||||
BoardCardFieldContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
function openBoardCardField() {
|
|
||||||
setIsBoardCardFieldInEditMode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeBoardCardField() {
|
|
||||||
setIsBoardCardFieldInEditMode(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isBoardCardFieldInEditMode,
|
|
||||||
openBoardCardField,
|
|
||||||
closeBoardCardField,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
import { createContext } from 'react';
|
|
||||||
|
|
||||||
export const BoardCardFieldContext = createContext<string | null>(null);
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { atomFamily } from 'recoil';
|
|
||||||
|
|
||||||
export const isBoardCardFieldInEditModeScopedState = atomFamily<
|
|
||||||
boolean,
|
|
||||||
string
|
|
||||||
>({
|
|
||||||
key: 'isBoardCardFieldInEditModeScopedState',
|
|
||||||
default: false,
|
|
||||||
});
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export enum BoardCardFieldHotkeyScope {
|
|
||||||
BoardCardFieldEditMode = 'board-card-field-edit-mode',
|
|
||||||
}
|
|
||||||
@ -1,53 +1,40 @@
|
|||||||
import { useRecoilCallback } from 'recoil';
|
import { getOperationName } from '@apollo/client/utilities';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { GET_PIPELINES } from '@/pipeline/queries';
|
||||||
import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry';
|
import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry';
|
||||||
import { boardCardIdsByColumnIdFamilyState } from '@/ui/board/states/boardCardIdsByColumnIdFamilyState';
|
|
||||||
import { boardColumnsState } from '@/ui/board/states/boardColumnsState';
|
|
||||||
import { selectedBoardCardIdsState } from '@/ui/board/states/selectedBoardCardIdsState';
|
|
||||||
import { IconTrash } from '@/ui/icon/index';
|
import { IconTrash } from '@/ui/icon/index';
|
||||||
|
import { useDeleteManyPipelineProgressMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
export function BoardActionBarButtonDeleteBoardCard({
|
import { useRemoveCardIds } from '../hooks/useRemoveCardIds';
|
||||||
onDelete,
|
import { selectedCardIdsSelector } from '../states/selectedCardIdsSelector';
|
||||||
}: {
|
|
||||||
onDelete: (deletedCardIds: string[]) => void;
|
|
||||||
}) {
|
|
||||||
const deleteBoardCardIds = useRecoilCallback(
|
|
||||||
({ set, snapshot }) =>
|
|
||||||
() => {
|
|
||||||
const boardCardIdsToDelete = snapshot
|
|
||||||
.getLoadable(selectedBoardCardIdsState)
|
|
||||||
.getValue();
|
|
||||||
|
|
||||||
const boardColumns = snapshot.getLoadable(boardColumnsState).getValue();
|
export function BoardActionBarButtonDeleteBoardCard() {
|
||||||
|
const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
|
||||||
|
const removeCardIds = useRemoveCardIds();
|
||||||
|
|
||||||
for (const boardColumn of boardColumns) {
|
const [deletePipelineProgress] = useDeleteManyPipelineProgressMutation({
|
||||||
const boardColumnCardIds = snapshot
|
refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
|
||||||
.getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
|
});
|
||||||
.getValue();
|
|
||||||
|
|
||||||
const newBoardColumnCardIds = boardColumnCardIds.filter(
|
async function handleDelete() {
|
||||||
(cardId) => !boardCardIdsToDelete.includes(cardId),
|
await deletePipelineProgress({
|
||||||
);
|
variables: {
|
||||||
|
ids: selectedCardIds,
|
||||||
if (newBoardColumnCardIds.length !== boardColumnCardIds.length) {
|
|
||||||
set(
|
|
||||||
boardCardIdsByColumnIdFamilyState(boardColumn.id),
|
|
||||||
newBoardColumnCardIds,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set(selectedBoardCardIdsState, []);
|
|
||||||
|
|
||||||
return boardCardIdsToDelete;
|
|
||||||
},
|
},
|
||||||
[],
|
optimisticResponse: {
|
||||||
);
|
__typename: 'Mutation',
|
||||||
|
deleteManyPipelineProgress: {
|
||||||
async function handleDeleteClick() {
|
count: selectedCardIds.length,
|
||||||
const deletedCardIds = deleteBoardCardIds();
|
},
|
||||||
|
},
|
||||||
onDelete(deletedCardIds);
|
update: (cache) => {
|
||||||
|
removeCardIds(selectedCardIds);
|
||||||
|
selectedCardIds.forEach((id) => {
|
||||||
|
cache.evict({ id: `PipelineProgress:${id}` });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -55,7 +42,7 @@ export function BoardActionBarButtonDeleteBoardCard({
|
|||||||
label="Delete"
|
label="Delete"
|
||||||
icon={<IconTrash size={16} />}
|
icon={<IconTrash size={16} />}
|
||||||
type="danger"
|
type="danger"
|
||||||
onClick={handleDeleteClick}
|
onClick={handleDelete}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,16 +4,14 @@ import { useTheme } from '@emotion/react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
|
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
|
||||||
import { IconList } from '@tabler/icons-react';
|
import { IconList } from '@tabler/icons-react';
|
||||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
|
import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
|
||||||
import { GET_PIPELINE_PROGRESS } from '@/pipeline/queries';
|
import { GET_PIPELINE_PROGRESS } from '@/pipeline/queries';
|
||||||
import { BoardHeader } from '@/ui/board/components/BoardHeader';
|
import { BoardHeader } from '@/ui/board/components/BoardHeader';
|
||||||
import { StyledBoard } from '@/ui/board/components/StyledBoard';
|
import { StyledBoard } from '@/ui/board/components/StyledBoard';
|
||||||
import { useUpdateBoardCardIds } from '@/ui/board/hooks/useUpdateBoardCardIds';
|
|
||||||
import { BoardColumnIdContext } from '@/ui/board/states/BoardColumnIdContext';
|
import { BoardColumnIdContext } from '@/ui/board/states/BoardColumnIdContext';
|
||||||
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
|
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
|
||||||
import { actionBarOpenState } from '@/ui/table/states/ActionBarIsOpenState';
|
|
||||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
import {
|
import {
|
||||||
@ -23,9 +21,10 @@ import {
|
|||||||
useUpdateOnePipelineProgressStageMutation,
|
useUpdateOnePipelineProgressStageMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
|
import { useSetCardSelected } from '../hooks/useSetCardSelected';
|
||||||
|
import { useUpdateBoardCardIds } from '../hooks/useUpdateBoardCardIds';
|
||||||
import { BoardColumnContext } from '../states/BoardColumnContext';
|
import { BoardColumnContext } from '../states/BoardColumnContext';
|
||||||
import { boardColumnsState } from '../states/boardColumnsState';
|
import { boardColumnsState } from '../states/boardColumnsState';
|
||||||
import { selectedBoardCardIdsState } from '../states/selectedBoardCardIdsState';
|
|
||||||
import { BoardOptions } from '../types/BoardOptions';
|
import { BoardOptions } from '../types/BoardOptions';
|
||||||
|
|
||||||
import { EntityBoardColumn } from './EntityBoardColumn';
|
import { EntityBoardColumn } from './EntityBoardColumn';
|
||||||
@ -49,6 +48,7 @@ export function EntityBoard({
|
|||||||
onEditColumnTitle: (columnId: string, title: string, color: string) => void;
|
onEditColumnTitle: (columnId: string, title: string, color: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const [boardColumns] = useRecoilState(boardColumnsState);
|
const [boardColumns] = useRecoilState(boardColumnsState);
|
||||||
|
const setCardSelected = useSetCardSelected();
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [updatePipelineProgressStage] =
|
const [updatePipelineProgressStage] =
|
||||||
@ -105,21 +105,6 @@ export function EntityBoard({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const boardRef = useRef(null);
|
const boardRef = useRef(null);
|
||||||
const [selectedBoardCards, setSelectedBoardCards] = useRecoilState(
|
|
||||||
selectedBoardCardIdsState,
|
|
||||||
);
|
|
||||||
const setActionBarOpenState = useSetRecoilState(actionBarOpenState);
|
|
||||||
|
|
||||||
function setRowSelectedState(boardCardId: string, selected: boolean) {
|
|
||||||
if (selected && !selectedBoardCards.includes(boardCardId)) {
|
|
||||||
setSelectedBoardCards([...selectedBoardCards, boardCardId ?? '']);
|
|
||||||
setActionBarOpenState(true);
|
|
||||||
} else if (!selected && selectedBoardCards.includes(boardCardId)) {
|
|
||||||
setSelectedBoardCards(
|
|
||||||
selectedBoardCards.filter((id) => id !== boardCardId),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (boardColumns?.length ?? 0) > 0 ? (
|
return (boardColumns?.length ?? 0) > 0 ? (
|
||||||
<StyledBoardWithHeader>
|
<StyledBoardWithHeader>
|
||||||
@ -147,7 +132,7 @@ export function EntityBoard({
|
|||||||
</StyledBoard>
|
</StyledBoard>
|
||||||
<DragSelect
|
<DragSelect
|
||||||
dragSelectable={boardRef}
|
dragSelectable={boardRef}
|
||||||
onDragSelectionChange={setRowSelectedState}
|
onDragSelectionChange={setCardSelected}
|
||||||
/>
|
/>
|
||||||
</StyledBoardWithHeader>
|
</StyledBoardWithHeader>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import { useRecoilValue } from 'recoil';
|
|||||||
|
|
||||||
import { ActionBar } from '@/ui/action-bar/components/ActionBar';
|
import { ActionBar } from '@/ui/action-bar/components/ActionBar';
|
||||||
|
|
||||||
import { selectedBoardCardIdsState } from '../states/selectedBoardCardIdsState';
|
import { selectedCardIdsSelector } from '../states/selectedCardIdsSelector';
|
||||||
|
|
||||||
export function EntityBoardActionBar() {
|
export function EntityBoardActionBar() {
|
||||||
const selectedBoardCards = useRecoilValue(selectedBoardCardIdsState);
|
const selectedBoardCards = useRecoilValue(selectedCardIdsSelector);
|
||||||
return <ActionBar selectedIds={selectedBoardCards}></ActionBar>;
|
return <ActionBar selectedIds={selectedBoardCards}></ActionBar>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
// TODO: refactor this test with Recoil
|
|
||||||
describe('getOptimisticlyUpdatedBoard', () => {
|
|
||||||
it('should return a new board with the updated cell', () => {
|
|
||||||
// const initialColumn1: string[] = ['item-1', 'item-2', 'item-3'];
|
|
||||||
// const initialColumn2: string[] = ['item-4', 'item-5'];
|
|
||||||
// const finalColumn1: string[] = ['item-2', 'item-3'];
|
|
||||||
// const finalColumn2: string[] = ['item-4', 'item-1', 'item-5'];
|
|
||||||
// const dropResult = {
|
|
||||||
// source: {
|
|
||||||
// droppableId: 'column-1',
|
|
||||||
// index: 0,
|
|
||||||
// },
|
|
||||||
// destination: {
|
|
||||||
// droppableId: 'column-2',
|
|
||||||
// index: 1,
|
|
||||||
// },
|
|
||||||
// } as DropResult;
|
|
||||||
// const initialBoard = [
|
|
||||||
// {
|
|
||||||
// id: 'column-1',
|
|
||||||
// title: 'My Column',
|
|
||||||
// pipelineStageId: 'column-1',
|
|
||||||
// pipelineProgressIds: initialColumn1,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: 'column-2',
|
|
||||||
// title: 'My Column',
|
|
||||||
// pipelineStageId: 'column-2',
|
|
||||||
// pipelineProgressIds: initialColumn2,
|
|
||||||
// },
|
|
||||||
// ];
|
|
||||||
// const updatedBoard = u(
|
|
||||||
// initialBoard,
|
|
||||||
// dropResult,
|
|
||||||
// );
|
|
||||||
// const finalBoard = [
|
|
||||||
// {
|
|
||||||
// id: 'column-1',
|
|
||||||
// title: 'My Column',
|
|
||||||
// pipelineStageId: 'column-1',
|
|
||||||
// pipelineProgressIds: finalColumn1,
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: 'column-2',
|
|
||||||
// title: 'My Column',
|
|
||||||
// pipelineStageId: 'column-2',
|
|
||||||
// pipelineProgressIds: finalColumn2,
|
|
||||||
// },
|
|
||||||
// ];
|
|
||||||
// expect(updatedBoard).toEqual(finalBoard);
|
|
||||||
// expect(updatedBoard).not.toBe(initialBoard);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,30 +1,13 @@
|
|||||||
import { getOperationName } from '@apollo/client/utilities';
|
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { GET_PIPELINES } from '@/pipeline/queries';
|
|
||||||
import { actionBarEntriesState } from '@/ui/table/states/ActionBarEntriesState';
|
import { actionBarEntriesState } from '@/ui/table/states/ActionBarEntriesState';
|
||||||
import { useDeleteManyPipelineProgressMutation } from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { BoardActionBarButtonDeleteBoardCard } from '../components/BoardActionBarButtonDeleteBoardCard';
|
import { BoardActionBarButtonDeleteBoardCard } from '../components/BoardActionBarButtonDeleteBoardCard';
|
||||||
|
|
||||||
export function useOpenActionBar() {
|
export function useOpenActionBar() {
|
||||||
const setActionBarEntries = useSetRecoilState(actionBarEntriesState);
|
const setActionBarEntries = useSetRecoilState(actionBarEntriesState);
|
||||||
|
|
||||||
const [deletePipelineProgress] = useDeleteManyPipelineProgressMutation({
|
|
||||||
refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
|
|
||||||
});
|
|
||||||
|
|
||||||
async function handleDelete(cardIdsToDelete: string[]) {
|
|
||||||
await deletePipelineProgress({
|
|
||||||
variables: {
|
|
||||||
ids: cardIdsToDelete,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
setActionBarEntries([
|
setActionBarEntries([<BoardActionBarButtonDeleteBoardCard />]);
|
||||||
<BoardActionBarButtonDeleteBoardCard onDelete={handleDelete} />,
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
28
front/src/modules/ui/board/hooks/useCurrentCardSelected.ts
Normal file
28
front/src/modules/ui/board/hooks/useCurrentCardSelected.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { BoardCardIdContext } from '../states/BoardCardIdContext';
|
||||||
|
import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
|
||||||
|
|
||||||
|
export function useCurrentCardSelected() {
|
||||||
|
const currentCardId = useContext(BoardCardIdContext);
|
||||||
|
|
||||||
|
const [isCardSelected] = useRecoilState(
|
||||||
|
isCardSelectedFamilyState(currentCardId ?? ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
const setCurrentCardSelected = useRecoilCallback(
|
||||||
|
({ set }) =>
|
||||||
|
(selected: boolean) => {
|
||||||
|
if (!currentCardId) return;
|
||||||
|
|
||||||
|
set(isCardSelectedFamilyState(currentCardId), selected);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentCardSelected: isCardSelected,
|
||||||
|
setCurrentCardSelected,
|
||||||
|
};
|
||||||
|
}
|
||||||
27
front/src/modules/ui/board/hooks/useRemoveCardIds.ts
Normal file
27
front/src/modules/ui/board/hooks/useRemoveCardIds.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
|
||||||
|
import { boardColumnsState } from '../states/boardColumnsState';
|
||||||
|
|
||||||
|
export function useRemoveCardIds() {
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ snapshot, set }) =>
|
||||||
|
(cardIdToRemove: string[]) => {
|
||||||
|
const boardColumns = snapshot
|
||||||
|
.getLoadable(boardColumnsState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
boardColumns.forEach((boardColumn) => {
|
||||||
|
const columnCardIds = snapshot
|
||||||
|
.getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
set(
|
||||||
|
boardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||||
|
columnCardIds.filter((cardId) => !cardIdToRemove.includes(cardId)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
27
front/src/modules/ui/board/hooks/useResetCardSelection.ts
Normal file
27
front/src/modules/ui/board/hooks/useResetCardSelection.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
|
||||||
|
import { boardColumnsState } from '../states/boardColumnsState';
|
||||||
|
import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
|
||||||
|
|
||||||
|
export function useResetCardSelection() {
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ snapshot, set }) =>
|
||||||
|
() => {
|
||||||
|
const boardColumns = snapshot
|
||||||
|
.getLoadable(boardColumnsState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
const cardIds = boardColumns.flatMap((boardColumn) =>
|
||||||
|
snapshot
|
||||||
|
.getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
|
||||||
|
.valueOrThrow(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const cardId of cardIds) {
|
||||||
|
set(isCardSelectedFamilyState(cardId), false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
9
front/src/modules/ui/board/hooks/useSetCardSelected.ts
Normal file
9
front/src/modules/ui/board/hooks/useSetCardSelected.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
|
||||||
|
|
||||||
|
export function useSetCardSelected() {
|
||||||
|
return useRecoilCallback(({ set }) => (cardId: string, selected: boolean) => {
|
||||||
|
set(isCardSelectedFamilyState(cardId), selected);
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,7 +1,10 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
import { FieldDefinition } from '@/ui/editable-field/types/FieldDefinition';
|
import { FieldDefinition } from '@/ui/editable-field/types/FieldDefinition';
|
||||||
import { FieldMetadata } from '@/ui/editable-field/types/FieldMetadata';
|
import {
|
||||||
|
FieldMetadata,
|
||||||
|
FieldType,
|
||||||
|
} from '@/ui/editable-field/types/FieldMetadata';
|
||||||
|
|
||||||
export const FieldDefinitionContext = createContext<
|
export const FieldDefinitionContext = createContext<
|
||||||
FieldDefinition<FieldMetadata>
|
FieldDefinition<FieldMetadata>
|
||||||
@ -9,6 +12,6 @@ export const FieldDefinitionContext = createContext<
|
|||||||
id: '',
|
id: '',
|
||||||
label: '',
|
label: '',
|
||||||
icon: undefined,
|
icon: undefined,
|
||||||
type: '',
|
type: 'unknown' satisfies FieldType,
|
||||||
metadata: {} as FieldMetadata,
|
metadata: {} as FieldMetadata,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const isCardSelectedFamilyState = atomFamily<boolean, string>({
|
||||||
|
key: 'isCardSelectedFamilyState',
|
||||||
|
default: false,
|
||||||
|
});
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
export const selectedBoardCardIdsState = atom<string[]>({
|
|
||||||
key: 'selectedBoardCardIdsState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
||||||
22
front/src/modules/ui/board/states/selectedCardIdsSelector.ts
Normal file
22
front/src/modules/ui/board/states/selectedCardIdsSelector.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { selector } from 'recoil';
|
||||||
|
|
||||||
|
import { boardCardIdsByColumnIdFamilyState } from './boardCardIdsByColumnIdFamilyState';
|
||||||
|
import { boardColumnsState } from './boardColumnsState';
|
||||||
|
import { isCardSelectedFamilyState } from './isCardSelectedFamilyState';
|
||||||
|
|
||||||
|
export const selectedCardIdsSelector = selector<string[]>({
|
||||||
|
key: 'selectedCardIdsSelector',
|
||||||
|
get: ({ get }) => {
|
||||||
|
const boardColumns = get(boardColumnsState);
|
||||||
|
|
||||||
|
const cardIds = boardColumns.flatMap((boardColumn) =>
|
||||||
|
get(boardCardIdsByColumnIdFamilyState(boardColumn.id)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedCardIds = cardIds.filter(
|
||||||
|
(cardId) => get(isCardSelectedFamilyState(cardId)) === true,
|
||||||
|
);
|
||||||
|
|
||||||
|
return selectedCardIds;
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -18,5 +18,5 @@ export const DropdownMenu = styled.div<{
|
|||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
width: ${({ width }) => width ?? 160}px;
|
width: ${({ width }) => (width && width > 160 ? width : 160)}px;
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { motion } from 'framer-motion';
|
|||||||
|
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
|
|
||||||
import { useBindFieldHotkeyScope } from '../hooks/useBindFieldHotkeyScope';
|
|
||||||
import { useEditableField } from '../hooks/useEditableField';
|
import { useEditableField } from '../hooks/useEditableField';
|
||||||
|
|
||||||
import { EditableFieldDisplayMode } from './EditableFieldDisplayMode';
|
import { EditableFieldDisplayMode } from './EditableFieldDisplayMode';
|
||||||
@ -74,7 +73,6 @@ type OwnProps = {
|
|||||||
displayModeContentOnly?: boolean;
|
displayModeContentOnly?: boolean;
|
||||||
disableHoverEffect?: boolean;
|
disableHoverEffect?: boolean;
|
||||||
displayModeContent: React.ReactNode;
|
displayModeContent: React.ReactNode;
|
||||||
parentHotkeyScope?: HotkeyScope;
|
|
||||||
customEditHotkeyScope?: HotkeyScope;
|
customEditHotkeyScope?: HotkeyScope;
|
||||||
isDisplayModeContentEmpty?: boolean;
|
isDisplayModeContentEmpty?: boolean;
|
||||||
isDisplayModeFixHeight?: boolean;
|
isDisplayModeFixHeight?: boolean;
|
||||||
@ -89,7 +87,6 @@ export function EditableField({
|
|||||||
useEditButton,
|
useEditButton,
|
||||||
editModeContent,
|
editModeContent,
|
||||||
displayModeContent,
|
displayModeContent,
|
||||||
parentHotkeyScope,
|
|
||||||
customEditHotkeyScope,
|
customEditHotkeyScope,
|
||||||
disableHoverEffect,
|
disableHoverEffect,
|
||||||
isDisplayModeContentEmpty,
|
isDisplayModeContentEmpty,
|
||||||
@ -100,11 +97,6 @@ export function EditableField({
|
|||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
useBindFieldHotkeyScope({
|
|
||||||
customEditHotkeyScope,
|
|
||||||
parentHotkeyScope,
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleContainerMouseEnter() {
|
function handleContainerMouseEnter() {
|
||||||
setIsHovered(true);
|
setIsHovered(true);
|
||||||
}
|
}
|
||||||
@ -116,7 +108,7 @@ export function EditableField({
|
|||||||
const { isFieldInEditMode, openEditableField } = useEditableField();
|
const { isFieldInEditMode, openEditableField } = useEditableField();
|
||||||
|
|
||||||
function handleDisplayModeClick() {
|
function handleDisplayModeClick() {
|
||||||
openEditableField();
|
openEditableField(customEditHotkeyScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
const showEditButton = !isFieldInEditMode && isHovered && useEditButton;
|
const showEditButton = !isFieldInEditMode && isHovered && useEditButton;
|
||||||
|
|||||||
@ -1,64 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
|
||||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
|
||||||
import { IconMap } from '@/ui/icon';
|
|
||||||
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
company: Pick<Company, 'id' | 'address'>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CompanyEditableFieldAddress({ company }: OwnProps) {
|
|
||||||
const [internalValue, setInternalValue] = useState(company.address);
|
|
||||||
|
|
||||||
const [updateCompany] = useUpdateOneCompanyMutation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setInternalValue(company.address);
|
|
||||||
}, [company.address]);
|
|
||||||
|
|
||||||
async function handleChange(newValue: string) {
|
|
||||||
setInternalValue(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSubmit() {
|
|
||||||
await updateCompany({
|
|
||||||
variables: {
|
|
||||||
where: {
|
|
||||||
id: company.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
address: internalValue ?? '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCancel() {
|
|
||||||
setInternalValue(company.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<EditableField
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
iconLabel={<IconMap />}
|
|
||||||
editModeContent={
|
|
||||||
<TextInputEdit
|
|
||||||
placeholder={'Address'}
|
|
||||||
autoFocus
|
|
||||||
value={internalValue}
|
|
||||||
onChange={(newValue: string) => {
|
|
||||||
handleChange(newValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={internalValue !== '' ? internalValue : 'No address'}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -3,12 +3,18 @@ import { useContext } from 'react';
|
|||||||
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||||
|
import { isFieldPhone } from '../types/guards/isFieldPhone';
|
||||||
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
||||||
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
||||||
|
import { isFieldText } from '../types/guards/isFieldText';
|
||||||
|
import { isFieldURL } from '../types/guards/isFieldURL';
|
||||||
|
|
||||||
import { GenericEditableDateField } from './GenericEditableDateField';
|
import { GenericEditableDateField } from './GenericEditableDateField';
|
||||||
import { GenericEditableNumberField } from './GenericEditableNumberField';
|
import { GenericEditableNumberField } from './GenericEditableNumberField';
|
||||||
|
import { GenericEditablePhoneField } from './GenericEditablePhoneField';
|
||||||
import { GenericEditableRelationField } from './GenericEditableRelationField';
|
import { GenericEditableRelationField } from './GenericEditableRelationField';
|
||||||
|
import { GenericEditableTextField } from './GenericEditableTextField';
|
||||||
|
import { GenericEditableURLField } from './GenericEditableURLField';
|
||||||
import { ProbabilityEditableField } from './ProbabilityEditableField';
|
import { ProbabilityEditableField } from './ProbabilityEditableField';
|
||||||
|
|
||||||
export function GenericEditableField() {
|
export function GenericEditableField() {
|
||||||
@ -22,9 +28,15 @@ export function GenericEditableField() {
|
|||||||
return <GenericEditableNumberField />;
|
return <GenericEditableNumberField />;
|
||||||
} else if (isFieldProbability(fieldDefinition)) {
|
} else if (isFieldProbability(fieldDefinition)) {
|
||||||
return <ProbabilityEditableField />;
|
return <ProbabilityEditableField />;
|
||||||
|
} else if (isFieldURL(fieldDefinition)) {
|
||||||
|
return <GenericEditableURLField />;
|
||||||
|
} else if (isFieldText(fieldDefinition)) {
|
||||||
|
return <GenericEditableTextField />;
|
||||||
|
} else if (isFieldPhone(fieldDefinition)) {
|
||||||
|
return <GenericEditablePhoneField />;
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown field metadata type: ${fieldDefinition.metadata.type} in GenericEditableCell`,
|
`Unknown field metadata type: ${fieldDefinition.type} in GenericEditableField`,
|
||||||
);
|
);
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,6 +69,7 @@ export function GenericEditableNumberFieldEditMode() {
|
|||||||
<div ref={wrapperRef}>
|
<div ref={wrapperRef}>
|
||||||
<TextInputEdit
|
<TextInputEdit
|
||||||
autoFocus
|
autoFocus
|
||||||
|
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||||
value={internalValue ? internalValue.toString() : ''}
|
value={internalValue ? internalValue.toString() : ''}
|
||||||
onChange={(newValue: string) => {
|
onChange={(newValue: string) => {
|
||||||
handleChange(newValue);
|
handleChange(newValue);
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { PhoneInputDisplay } from '@/ui/input/phone/components/PhoneInputDisplay';
|
||||||
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
|
|
||||||
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
|
import { FieldContext } from '../states/FieldContext';
|
||||||
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
|
import { FieldPhoneMetadata } from '../types/FieldMetadata';
|
||||||
|
|
||||||
|
import { EditableField } from './EditableField';
|
||||||
|
import { GenericEditablePhoneFieldEditMode } from './GenericEditablePhoneFieldEditMode';
|
||||||
|
|
||||||
|
export function GenericEditablePhoneField() {
|
||||||
|
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||||
|
const currentEditableFieldDefinition = useContext(
|
||||||
|
EditableFieldDefinitionContext,
|
||||||
|
) as FieldDefinition<FieldPhoneMetadata>;
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
|
<EditableField
|
||||||
|
useEditButton
|
||||||
|
iconLabel={currentEditableFieldDefinition.icon}
|
||||||
|
editModeContent={<GenericEditablePhoneFieldEditMode />}
|
||||||
|
displayModeContent={<PhoneInputDisplay value={fieldValue} />}
|
||||||
|
isDisplayModeContentEmpty={!fieldValue}
|
||||||
|
/>
|
||||||
|
</RecoilScope>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
import { useContext, useRef, useState } from 'react';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
||||||
|
|
||||||
|
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers';
|
||||||
|
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||||
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
|
import { FieldPhoneMetadata } from '../types/FieldMetadata';
|
||||||
|
|
||||||
|
export function GenericEditablePhoneFieldEditMode() {
|
||||||
|
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||||
|
const currentEditableFieldDefinition = useContext(
|
||||||
|
EditableFieldDefinitionContext,
|
||||||
|
) as FieldDefinition<FieldPhoneMetadata>;
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [internalValue, setInternalValue] = useState(fieldValue);
|
||||||
|
|
||||||
|
const updateField = useUpdateGenericEntityField();
|
||||||
|
|
||||||
|
const wrapperRef = useRef(null);
|
||||||
|
|
||||||
|
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel);
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
if (internalValue === fieldValue) return;
|
||||||
|
|
||||||
|
setFieldValue(internalValue);
|
||||||
|
|
||||||
|
if (currentEditableFieldEntityId && updateField) {
|
||||||
|
updateField(
|
||||||
|
currentEditableFieldEntityId,
|
||||||
|
currentEditableFieldDefinition,
|
||||||
|
internalValue,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancel() {
|
||||||
|
setFieldValue(fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(newValue: string) {
|
||||||
|
setInternalValue(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={wrapperRef}>
|
||||||
|
<TextInputEdit
|
||||||
|
autoFocus
|
||||||
|
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||||
|
value={internalValue}
|
||||||
|
onChange={(newValue: string) => {
|
||||||
|
handleChange(newValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -34,7 +34,7 @@ export function GenericEditableRelationField() {
|
|||||||
<RecoilScope SpecificContext={FieldContext}>
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
<RecoilScope>
|
<RecoilScope>
|
||||||
<EditableField
|
<EditableField
|
||||||
useEditButton
|
useEditButton={currentEditableFieldDefinition.metadata.useEditButton}
|
||||||
customEditHotkeyScope={{
|
customEditHotkeyScope={{
|
||||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
scope: RelationPickerHotkeyScope.RelationPicker,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||||
import { PersonChip } from '@/people/components/PersonChip';
|
import { PersonChip } from '@/people/components/PersonChip';
|
||||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
|
import { UserChip } from '@/users/components/UserChip';
|
||||||
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
@ -35,6 +38,28 @@ export function GenericEditableRelationFieldDisplayMode() {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case Entity.User: {
|
||||||
|
return (
|
||||||
|
<UserChip
|
||||||
|
id={fieldValue?.id ?? ''}
|
||||||
|
name={fieldValue?.displayName ?? ''}
|
||||||
|
pictureUrl={fieldValue?.avatarUrl ?? ''}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case Entity.Company: {
|
||||||
|
return (
|
||||||
|
<CompanyChip
|
||||||
|
id={fieldValue?.id ?? ''}
|
||||||
|
name={fieldValue?.name ?? ''}
|
||||||
|
pictureUrl={
|
||||||
|
fieldValue?.domainName
|
||||||
|
? getLogoUrlFromDomainName(fieldValue.domainName)
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown relation type: "${currentEditableFieldDefinition.metadata.relationType}"
|
`Unknown relation type: "${currentEditableFieldDefinition.metadata.relationType}"
|
||||||
|
|||||||
@ -2,10 +2,11 @@ import { useContext } from 'react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { CompanyPicker } from '@/companies/components/CompanyPicker';
|
||||||
import { PeoplePicker } from '@/people/components/PeoplePicker';
|
import { PeoplePicker } from '@/people/components/PeoplePicker';
|
||||||
import { ViewFieldRelationValue } from '@/ui/editable-field/types/ViewField';
|
|
||||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
|
import { UserPicker } from '@/users/components/UserPicker';
|
||||||
|
|
||||||
import { useEditableField } from '../hooks/useEditableField';
|
import { useEditableField } from '../hooks/useEditableField';
|
||||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||||
@ -13,7 +14,10 @@ import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitio
|
|||||||
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
import { FieldDefinition } from '../types/FieldDefinition';
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
import { FieldRelationMetadata } from '../types/FieldMetadata';
|
import {
|
||||||
|
FieldRelationMetadata,
|
||||||
|
FieldRelationValue,
|
||||||
|
} from '../types/FieldMetadata';
|
||||||
|
|
||||||
const RelationPickerContainer = styled.div`
|
const RelationPickerContainer = styled.div`
|
||||||
left: 0px;
|
left: 0px;
|
||||||
@ -28,7 +32,7 @@ function RelationPicker({
|
|||||||
handleCancel,
|
handleCancel,
|
||||||
}: {
|
}: {
|
||||||
fieldDefinition: FieldDefinition<FieldRelationMetadata>;
|
fieldDefinition: FieldDefinition<FieldRelationMetadata>;
|
||||||
fieldValue: ViewFieldRelationValue;
|
fieldValue: FieldRelationValue;
|
||||||
handleEntitySubmit: (newRelationId: EntityForSelect | null) => void;
|
handleEntitySubmit: (newRelationId: EntityForSelect | null) => void;
|
||||||
handleCancel: () => void;
|
handleCancel: () => void;
|
||||||
}) {
|
}) {
|
||||||
@ -36,7 +40,25 @@ function RelationPicker({
|
|||||||
case Entity.Person: {
|
case Entity.Person: {
|
||||||
return (
|
return (
|
||||||
<PeoplePicker
|
<PeoplePicker
|
||||||
personId={fieldValue?.id ?? null}
|
personId={fieldValue ? fieldValue.id : ''}
|
||||||
|
onSubmit={handleEntitySubmit}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case Entity.User: {
|
||||||
|
return (
|
||||||
|
<UserPicker
|
||||||
|
userId={fieldValue ? fieldValue.id : ''}
|
||||||
|
onSubmit={handleEntitySubmit}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case Entity.Company: {
|
||||||
|
return (
|
||||||
|
<CompanyPicker
|
||||||
|
companyId={fieldValue ? fieldValue.id : ''}
|
||||||
onSubmit={handleEntitySubmit}
|
onSubmit={handleEntitySubmit}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
/>
|
/>
|
||||||
@ -46,7 +68,7 @@ function RelationPicker({
|
|||||||
console.warn(
|
console.warn(
|
||||||
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationField`,
|
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationField`,
|
||||||
);
|
);
|
||||||
return <> </>;
|
return <></>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
|
|
||||||
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
|
import { FieldContext } from '../states/FieldContext';
|
||||||
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
|
import { FieldNumberMetadata } from '../types/FieldMetadata';
|
||||||
|
|
||||||
|
import { EditableField } from './EditableField';
|
||||||
|
import { GenericEditableTextFieldEditMode } from './GenericEditableTextFieldEditMode';
|
||||||
|
|
||||||
|
export function GenericEditableTextField() {
|
||||||
|
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||||
|
const currentEditableFieldDefinition = useContext(
|
||||||
|
EditableFieldDefinitionContext,
|
||||||
|
) as FieldDefinition<FieldNumberMetadata>;
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
|
<EditableField
|
||||||
|
iconLabel={currentEditableFieldDefinition.icon}
|
||||||
|
editModeContent={<GenericEditableTextFieldEditMode />}
|
||||||
|
displayModeContent={fieldValue}
|
||||||
|
isDisplayModeContentEmpty={!fieldValue}
|
||||||
|
/>
|
||||||
|
</RecoilScope>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
import { useContext, useRef, useState } from 'react';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
||||||
|
|
||||||
|
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers';
|
||||||
|
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||||
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
|
import { FieldTextMetadata } from '../types/FieldMetadata';
|
||||||
|
|
||||||
|
export function GenericEditableTextFieldEditMode() {
|
||||||
|
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||||
|
const currentEditableFieldDefinition = useContext(
|
||||||
|
EditableFieldDefinitionContext,
|
||||||
|
) as FieldDefinition<FieldTextMetadata>;
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [internalValue, setInternalValue] = useState(fieldValue);
|
||||||
|
|
||||||
|
const updateField = useUpdateGenericEntityField();
|
||||||
|
|
||||||
|
const wrapperRef = useRef(null);
|
||||||
|
|
||||||
|
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel);
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
if (internalValue === fieldValue) return;
|
||||||
|
|
||||||
|
setFieldValue(internalValue);
|
||||||
|
|
||||||
|
if (currentEditableFieldEntityId && updateField) {
|
||||||
|
updateField(
|
||||||
|
currentEditableFieldEntityId,
|
||||||
|
currentEditableFieldDefinition,
|
||||||
|
internalValue,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancel() {
|
||||||
|
setFieldValue(fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(newValue: string) {
|
||||||
|
setInternalValue(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={wrapperRef}>
|
||||||
|
<TextInputEdit
|
||||||
|
autoFocus
|
||||||
|
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||||
|
value={internalValue}
|
||||||
|
onChange={(newValue: string) => {
|
||||||
|
handleChange(newValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
|
|
||||||
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
|
import { FieldContext } from '../states/FieldContext';
|
||||||
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
|
import { FieldNumberMetadata } from '../types/FieldMetadata';
|
||||||
|
|
||||||
|
import { EditableField } from './EditableField';
|
||||||
|
import { FieldDisplayURL } from './FieldDisplayURL';
|
||||||
|
import { GenericEditableURLFieldEditMode } from './GenericEditableURLFieldEditMode';
|
||||||
|
|
||||||
|
export function GenericEditableURLField() {
|
||||||
|
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||||
|
const currentEditableFieldDefinition = useContext(
|
||||||
|
EditableFieldDefinitionContext,
|
||||||
|
) as FieldDefinition<FieldNumberMetadata>;
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
|
<EditableField
|
||||||
|
useEditButton
|
||||||
|
iconLabel={currentEditableFieldDefinition.icon}
|
||||||
|
editModeContent={<GenericEditableURLFieldEditMode />}
|
||||||
|
displayModeContent={<FieldDisplayURL URL={fieldValue} />}
|
||||||
|
isDisplayModeContentEmpty={!fieldValue}
|
||||||
|
/>
|
||||||
|
</RecoilScope>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
import { useContext, useRef, useState } from 'react';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit';
|
||||||
|
|
||||||
|
import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers';
|
||||||
|
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||||
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
|
import { FieldURLMetadata } from '../types/FieldMetadata';
|
||||||
|
|
||||||
|
// This one is very similar to GenericEditableTextFieldEditMode
|
||||||
|
// We could probably merge them since FieldURLMetadata is basically a FieldTextMetadata
|
||||||
|
export function GenericEditableURLFieldEditMode() {
|
||||||
|
const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext);
|
||||||
|
const currentEditableFieldDefinition = useContext(
|
||||||
|
EditableFieldDefinitionContext,
|
||||||
|
) as FieldDefinition<FieldURLMetadata>;
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [internalValue, setInternalValue] = useState(fieldValue);
|
||||||
|
|
||||||
|
const updateField = useUpdateGenericEntityField();
|
||||||
|
|
||||||
|
const wrapperRef = useRef(null);
|
||||||
|
|
||||||
|
useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel);
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
if (internalValue === fieldValue) return;
|
||||||
|
|
||||||
|
setFieldValue(internalValue);
|
||||||
|
|
||||||
|
if (currentEditableFieldEntityId && updateField) {
|
||||||
|
updateField(
|
||||||
|
currentEditableFieldEntityId,
|
||||||
|
currentEditableFieldDefinition,
|
||||||
|
internalValue,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancel() {
|
||||||
|
setFieldValue(fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(newValue: string) {
|
||||||
|
setInternalValue(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={wrapperRef}>
|
||||||
|
<TextInputEdit
|
||||||
|
autoFocus
|
||||||
|
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||||
|
value={internalValue}
|
||||||
|
onChange={(newValue: string) => {
|
||||||
|
handleChange(newValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,75 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import { useRecoilCallback } from 'recoil';
|
|
||||||
|
|
||||||
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
|
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
|
||||||
import { isSameHotkeyScope } from '@/ui/utilities/hotkey/utils/isSameHotkeyScope';
|
|
||||||
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
import { getSnapshotScopedState } from '@/ui/utilities/recoil-scope/utils/getSnapshotScopedState';
|
|
||||||
import { getSnapshotState } from '@/ui/utilities/recoil-scope/utils/getSnapshotState';
|
|
||||||
|
|
||||||
import { customEditHotkeyScopeForFieldScopedState } from '../states/customEditHotkeyScopeForFieldScopedState';
|
|
||||||
import { FieldContext } from '../states/FieldContext';
|
|
||||||
import { parentHotkeyScopeForFieldScopedState } from '../states/parentHotkeyScopeForFieldScopedState';
|
|
||||||
|
|
||||||
export function useBindFieldHotkeyScope({
|
|
||||||
customEditHotkeyScope,
|
|
||||||
parentHotkeyScope,
|
|
||||||
}: {
|
|
||||||
customEditHotkeyScope?: HotkeyScope;
|
|
||||||
parentHotkeyScope?: HotkeyScope;
|
|
||||||
}) {
|
|
||||||
const [customEditHotkeyScopeForField, setCustomEditHotkeyScopeForField] =
|
|
||||||
useRecoilScopedState(
|
|
||||||
customEditHotkeyScopeForFieldScopedState,
|
|
||||||
FieldContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
const fieldContextScopeId = useContextScopeId(FieldContext);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
customEditHotkeyScope &&
|
|
||||||
!isSameHotkeyScope(customEditHotkeyScope, customEditHotkeyScopeForField)
|
|
||||||
) {
|
|
||||||
setCustomEditHotkeyScopeForField(customEditHotkeyScope);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
customEditHotkeyScope,
|
|
||||||
customEditHotkeyScopeForField,
|
|
||||||
setCustomEditHotkeyScopeForField,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const setParentHotkeyScopeForField = useRecoilCallback(
|
|
||||||
({ snapshot, set }) =>
|
|
||||||
(parentHotkeyScopeToSet: HotkeyScope | null | undefined) => {
|
|
||||||
const currentHotkeyScope = getSnapshotState({
|
|
||||||
snapshot,
|
|
||||||
state: currentHotkeyScopeState,
|
|
||||||
});
|
|
||||||
|
|
||||||
const parentHotkeyScopeForField = getSnapshotScopedState({
|
|
||||||
snapshot,
|
|
||||||
state: parentHotkeyScopeForFieldScopedState,
|
|
||||||
contextScopeId: fieldContextScopeId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!parentHotkeyScopeToSet) {
|
|
||||||
set(
|
|
||||||
parentHotkeyScopeForFieldScopedState(fieldContextScopeId),
|
|
||||||
currentHotkeyScope,
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
!isSameHotkeyScope(parentHotkeyScopeToSet, parentHotkeyScopeForField)
|
|
||||||
) {
|
|
||||||
setParentHotkeyScopeForField(parentHotkeyScopeToSet);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[fieldContextScopeId],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setParentHotkeyScopeForField(parentHotkeyScope);
|
|
||||||
}, [parentHotkeyScope, setParentHotkeyScopeForField]);
|
|
||||||
}
|
|
||||||
@ -1,10 +1,9 @@
|
|||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||||
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
|
|
||||||
import { customEditHotkeyScopeForFieldScopedState } from '../states/customEditHotkeyScopeForFieldScopedState';
|
|
||||||
import { FieldContext } from '../states/FieldContext';
|
import { FieldContext } from '../states/FieldContext';
|
||||||
import { isFieldInEditModeScopedState } from '../states/isFieldInEditModeScopedState';
|
import { isFieldInEditModeScopedState } from '../states/isFieldInEditModeScopedState';
|
||||||
import { parentHotkeyScopeForFieldScopedState } from '../states/parentHotkeyScopeForFieldScopedState';
|
|
||||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||||
|
|
||||||
export function useEditableField() {
|
export function useEditableField() {
|
||||||
@ -13,39 +12,29 @@ export function useEditableField() {
|
|||||||
FieldContext,
|
FieldContext,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [customEditHotkeyScopeForField] = useRecoilScopedState(
|
const {
|
||||||
customEditHotkeyScopeForFieldScopedState,
|
setHotkeyScopeAndMemorizePreviousScope,
|
||||||
FieldContext,
|
goBackToPreviousHotkeyScope,
|
||||||
);
|
} = usePreviousHotkeyScope();
|
||||||
|
|
||||||
const [parentHotkeyScopeForField] = useRecoilScopedState(
|
|
||||||
parentHotkeyScopeForFieldScopedState,
|
|
||||||
FieldContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
const setHotkeyScope = useSetHotkeyScope();
|
|
||||||
|
|
||||||
function closeEditableField() {
|
function closeEditableField() {
|
||||||
setIsFieldInEditMode(false);
|
setIsFieldInEditMode(false);
|
||||||
|
|
||||||
if (parentHotkeyScopeForField) {
|
goBackToPreviousHotkeyScope();
|
||||||
setHotkeyScope(
|
|
||||||
parentHotkeyScopeForField.scope,
|
|
||||||
parentHotkeyScopeForField.customScopes,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEditableField() {
|
function openEditableField(customEditHotkeyScopeForField?: HotkeyScope) {
|
||||||
setIsFieldInEditMode(true);
|
setIsFieldInEditMode(true);
|
||||||
|
|
||||||
if (customEditHotkeyScopeForField) {
|
if (customEditHotkeyScopeForField) {
|
||||||
setHotkeyScope(
|
setHotkeyScopeAndMemorizePreviousScope(
|
||||||
customEditHotkeyScopeForField.scope,
|
customEditHotkeyScopeForField.scope,
|
||||||
customEditHotkeyScopeForField.customScopes,
|
customEditHotkeyScopeForField.customScopes,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setHotkeyScope(EditableFieldHotkeyScope.EditableField);
|
setHotkeyScopeAndMemorizePreviousScope(
|
||||||
|
EditableFieldHotkeyScope.EditableField,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
import { isFieldChip } from '@/ui/editable-field/types/guards/isFieldChip';
|
|
||||||
|
|
||||||
import { EditableFieldMutationContext } from '../states/EditableFieldMutationContext';
|
import { EditableFieldMutationContext } from '../states/EditableFieldMutationContext';
|
||||||
import { FieldDefinition } from '../types/FieldDefinition';
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
import {
|
import {
|
||||||
@ -27,6 +25,7 @@ import {
|
|||||||
FieldURLMetadata,
|
FieldURLMetadata,
|
||||||
FieldURLValue,
|
FieldURLValue,
|
||||||
} from '../types/FieldMetadata';
|
} from '../types/FieldMetadata';
|
||||||
|
import { isFieldChip } from '../types/guards/isFieldChip';
|
||||||
import { isFieldChipValue } from '../types/guards/isFieldChipValue';
|
import { isFieldChipValue } from '../types/guards/isFieldChipValue';
|
||||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||||
import { isFieldDateValue } from '../types/guards/isFieldDateValue';
|
import { isFieldDateValue } from '../types/guards/isFieldDateValue';
|
||||||
@ -53,31 +52,30 @@ export function useUpdateGenericEntityField() {
|
|||||||
const [updateEntity] = useUpdateEntityMutation();
|
const [updateEntity] = useUpdateEntityMutation();
|
||||||
|
|
||||||
return function updateEntityField<
|
return function updateEntityField<
|
||||||
MetadataType extends FieldMetadata,
|
ValueType extends FieldMetadata extends FieldDoubleTextMetadata
|
||||||
ValueType extends MetadataType extends FieldDoubleTextMetadata
|
|
||||||
? FieldDoubleTextValue
|
? FieldDoubleTextValue
|
||||||
: MetadataType extends FieldTextMetadata
|
: FieldMetadata extends FieldTextMetadata
|
||||||
? FieldTextValue
|
? FieldTextValue
|
||||||
: MetadataType extends FieldPhoneMetadata
|
: FieldMetadata extends FieldPhoneMetadata
|
||||||
? FieldPhoneValue
|
? FieldPhoneValue
|
||||||
: MetadataType extends FieldURLMetadata
|
: FieldMetadata extends FieldURLMetadata
|
||||||
? FieldURLValue
|
? FieldURLValue
|
||||||
: MetadataType extends FieldNumberMetadata
|
: FieldMetadata extends FieldNumberMetadata
|
||||||
? FieldNumberValue
|
? FieldNumberValue
|
||||||
: MetadataType extends FieldDateMetadata
|
: FieldMetadata extends FieldDateMetadata
|
||||||
? FieldDateValue
|
? FieldDateValue
|
||||||
: MetadataType extends FieldChipMetadata
|
: FieldMetadata extends FieldChipMetadata
|
||||||
? FieldChipValue
|
? FieldChipValue
|
||||||
: MetadataType extends FieldDoubleTextChipMetadata
|
: FieldMetadata extends FieldDoubleTextChipMetadata
|
||||||
? FieldDoubleTextChipValue
|
? FieldDoubleTextChipValue
|
||||||
: MetadataType extends FieldRelationMetadata
|
: FieldMetadata extends FieldRelationMetadata
|
||||||
? FieldRelationValue
|
? FieldRelationValue
|
||||||
: MetadataType extends FieldProbabilityMetadata
|
: FieldMetadata extends FieldProbabilityMetadata
|
||||||
? FieldProbabilityValue
|
? FieldProbabilityValue
|
||||||
: unknown,
|
: unknown,
|
||||||
>(
|
>(
|
||||||
currentEntityId: string,
|
currentEntityId: string,
|
||||||
field: FieldDefinition<MetadataType>,
|
field: FieldDefinition<FieldMetadata>,
|
||||||
newFieldValue: ValueType,
|
newFieldValue: ValueType,
|
||||||
) {
|
) {
|
||||||
const newFieldValueUnknown = newFieldValue as unknown;
|
const newFieldValueUnknown = newFieldValue as unknown;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user