feat: email and calendar events for opportunities (#13613)
This commit is contained in:
@@ -2109,8 +2109,10 @@ export type Query = {
|
||||
getServerlessFunctionSourceCode?: Maybe<Scalars['JSON']>;
|
||||
getSystemHealthStatus: SystemHealth;
|
||||
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
|
||||
getTimelineCalendarEventsFromOpportunityId: TimelineCalendarEventsWithTotal;
|
||||
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
|
||||
getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal;
|
||||
getTimelineThreadsFromOpportunityId: TimelineThreadsWithTotal;
|
||||
getTimelineThreadsFromPersonId: TimelineThreadsWithTotal;
|
||||
index: Index;
|
||||
indexMetadatas: IndexConnection;
|
||||
@@ -2269,6 +2271,13 @@ export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineCalendarEventsFromOpportunityIdArgs = {
|
||||
opportunityId: Scalars['UUID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineCalendarEventsFromPersonIdArgs = {
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
@@ -2283,6 +2292,13 @@ export type QueryGetTimelineThreadsFromCompanyIdArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineThreadsFromOpportunityIdArgs = {
|
||||
opportunityId: Scalars['UUID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineThreadsFromPersonIdArgs = {
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
|
||||
@@ -2017,8 +2017,10 @@ export type Query = {
|
||||
getServerlessFunctionSourceCode?: Maybe<Scalars['JSON']>;
|
||||
getSystemHealthStatus: SystemHealth;
|
||||
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
|
||||
getTimelineCalendarEventsFromOpportunityId: TimelineCalendarEventsWithTotal;
|
||||
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
|
||||
getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal;
|
||||
getTimelineThreadsFromOpportunityId: TimelineThreadsWithTotal;
|
||||
getTimelineThreadsFromPersonId: TimelineThreadsWithTotal;
|
||||
index: Index;
|
||||
indexMetadatas: IndexConnection;
|
||||
@@ -2151,6 +2153,13 @@ export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineCalendarEventsFromOpportunityIdArgs = {
|
||||
opportunityId: Scalars['UUID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineCalendarEventsFromPersonIdArgs = {
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
@@ -2165,6 +2174,13 @@ export type QueryGetTimelineThreadsFromCompanyIdArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineThreadsFromOpportunityIdArgs = {
|
||||
opportunityId: Scalars['UUID'];
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryGetTimelineThreadsFromPersonIdArgs = {
|
||||
page: Scalars['Int'];
|
||||
pageSize: Scalars['Int'];
|
||||
|
||||
@@ -5,6 +5,7 @@ import { CalendarMonthCard } from '@/activities/calendar/components/CalendarMont
|
||||
import { TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE } from '@/activities/calendar/constants/Calendar';
|
||||
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
||||
import { getTimelineCalendarEventsFromCompanyId } from '@/activities/calendar/graphql/queries/getTimelineCalendarEventsFromCompanyId';
|
||||
import { getTimelineCalendarEventsFromOpportunityId } from '@/activities/calendar/graphql/queries/getTimelineCalendarEventsFromOpportunityId';
|
||||
import { getTimelineCalendarEventsFromPersonId } from '@/activities/calendar/graphql/queries/getTimelineCalendarEventsFromPersonId';
|
||||
import { useCalendarEvents } from '@/activities/calendar/hooks/useCalendarEvents';
|
||||
import { CustomResolverFetchMoreLoader } from '@/activities/components/CustomResolverFetchMoreLoader';
|
||||
@@ -53,10 +54,16 @@ export const Calendar = ({
|
||||
getTimelineCalendarEventsFromPersonId,
|
||||
'getTimelineCalendarEventsFromPersonId',
|
||||
]
|
||||
: [
|
||||
getTimelineCalendarEventsFromCompanyId,
|
||||
'getTimelineCalendarEventsFromCompanyId',
|
||||
];
|
||||
: targetableObject.targetObjectNameSingular ===
|
||||
CoreObjectNameSingular.Company
|
||||
? [
|
||||
getTimelineCalendarEventsFromCompanyId,
|
||||
'getTimelineCalendarEventsFromCompanyId',
|
||||
]
|
||||
: [
|
||||
getTimelineCalendarEventsFromOpportunityId,
|
||||
'getTimelineCalendarEventsFromOpportunityId',
|
||||
];
|
||||
|
||||
const { data, firstQueryLoading, isFetchingMore, fetchMoreRecords } =
|
||||
useCustomResolver<TimelineCalendarEventsWithTotal>(
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineCalendarEventWithTotalFragment } from '@/activities/calendar/graphql/queries/fragments/timelineCalendarEventWithTotalFragment';
|
||||
|
||||
export const getTimelineCalendarEventsFromOpportunityId = gql`
|
||||
query GetTimelineCalendarEventsFromOpportunityId(
|
||||
$opportunityId: UUID!
|
||||
$page: Int!
|
||||
$pageSize: Int!
|
||||
) {
|
||||
getTimelineCalendarEventsFromOpportunityId(
|
||||
opportunityId: $opportunityId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineCalendarEventsWithTotalFragment
|
||||
}
|
||||
}
|
||||
${timelineCalendarEventWithTotalFragment}
|
||||
`;
|
||||
@@ -7,6 +7,7 @@ import { EmailThreadPreview } from '@/activities/emails/components/EmailThreadPr
|
||||
import { TIMELINE_THREADS_DEFAULT_PAGE_SIZE } from '@/activities/emails/constants/Messaging';
|
||||
import { getTimelineThreadsFromCompanyId } from '@/activities/emails/graphql/queries/getTimelineThreadsFromCompanyId';
|
||||
import { getTimelineThreadsFromPersonId } from '@/activities/emails/graphql/queries/getTimelineThreadsFromPersonId';
|
||||
import { getTimelineThreadsFromOpportunityId } from '@/activities/emails/graphql/queries/getTimelineThreadsFromOpportunityId';
|
||||
import { useCustomResolver } from '@/activities/hooks/useCustomResolver';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@@ -48,7 +49,13 @@ export const EmailThreads = ({
|
||||
const [query, queryName] =
|
||||
targetableObject.targetObjectNameSingular === CoreObjectNameSingular.Person
|
||||
? [getTimelineThreadsFromPersonId, 'getTimelineThreadsFromPersonId']
|
||||
: [getTimelineThreadsFromCompanyId, 'getTimelineThreadsFromCompanyId'];
|
||||
: targetableObject.targetObjectNameSingular ===
|
||||
CoreObjectNameSingular.Company
|
||||
? [getTimelineThreadsFromCompanyId, 'getTimelineThreadsFromCompanyId']
|
||||
: [
|
||||
getTimelineThreadsFromOpportunityId,
|
||||
'getTimelineThreadsFromOpportunityId',
|
||||
];
|
||||
|
||||
const { data, firstQueryLoading, isFetchingMore, fetchMoreRecords } =
|
||||
useCustomResolver<TimelineThreadsWithTotal>(
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineThreadWithTotalFragment } from '@/activities/emails/graphql/queries/fragments/timelineThreadWithTotalFragment';
|
||||
|
||||
export const getTimelineThreadsFromOpportunityId = gql`
|
||||
query GetTimelineThreadsFromOpportunityId(
|
||||
$opportunityId: UUID!
|
||||
$page: Int!
|
||||
$pageSize: Int!
|
||||
) {
|
||||
getTimelineThreadsFromOpportunityId(
|
||||
opportunityId: $opportunityId
|
||||
page: $page
|
||||
pageSize: $pageSize
|
||||
) {
|
||||
...TimelineThreadsWithTotalFragment
|
||||
}
|
||||
}
|
||||
${timelineThreadWithTotalFragment}
|
||||
`;
|
||||
@@ -49,7 +49,10 @@ export const useCustomResolver = <
|
||||
...(activityTargetableObject.targetObjectNameSingular ===
|
||||
CoreObjectNameSingular.Person
|
||||
? { personId: activityTargetableObject.id }
|
||||
: { companyId: activityTargetableObject.id }),
|
||||
: activityTargetableObject.targetObjectNameSingular ===
|
||||
CoreObjectNameSingular.Opportunity
|
||||
? { opportunityId: activityTargetableObject.id }
|
||||
: { companyId: activityTargetableObject.id }),
|
||||
page: 1,
|
||||
pageSize,
|
||||
};
|
||||
|
||||
+32
@@ -142,6 +142,38 @@ export const useRecordShowContainerTabs = (
|
||||
},
|
||||
},
|
||||
},
|
||||
[CoreObjectNameSingular.Opportunity]: {
|
||||
tabs: {
|
||||
emails: {
|
||||
title: 'Emails',
|
||||
position: 600,
|
||||
Icon: IconMail,
|
||||
cards: [{ type: CardType.EmailCard }],
|
||||
hide: {
|
||||
ifMobile: false,
|
||||
ifDesktop: false,
|
||||
ifInRightDrawer: false,
|
||||
ifFeaturesDisabled: [],
|
||||
ifRequiredObjectsInactive: [],
|
||||
ifRelationsMissing: [],
|
||||
},
|
||||
},
|
||||
calendar: {
|
||||
title: 'Calendar',
|
||||
position: 700,
|
||||
Icon: IconCalendarEvent,
|
||||
cards: [{ type: CardType.CalendarCard }],
|
||||
hide: {
|
||||
ifMobile: false,
|
||||
ifDesktop: false,
|
||||
ifInRightDrawer: false,
|
||||
ifFeaturesDisabled: [],
|
||||
ifRequiredObjectsInactive: [],
|
||||
ifRelationsMissing: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
[CoreObjectNameSingular.Workflow]: {
|
||||
hideSummaryAndFields: true,
|
||||
tabs: {
|
||||
|
||||
+36
@@ -36,6 +36,19 @@ class GetTimelineCalendarEventsFromCompanyIdArgs {
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
class GetTimelineCalendarEventsFromOpportunityIdArgs {
|
||||
@Field(() => UUIDScalarType)
|
||||
opportunityId: string;
|
||||
|
||||
@Field(() => Int)
|
||||
page: number;
|
||||
|
||||
@Field(() => Int)
|
||||
@Max(TIMELINE_CALENDAR_EVENTS_MAX_PAGE_SIZE)
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard)
|
||||
@Resolver(() => TimelineCalendarEventsWithTotal)
|
||||
export class TimelineCalendarEventResolver {
|
||||
@@ -76,4 +89,27 @@ export class TimelineCalendarEventResolver {
|
||||
|
||||
return timelineCalendarEvents;
|
||||
}
|
||||
|
||||
@Query(() => TimelineCalendarEventsWithTotal)
|
||||
async getTimelineCalendarEventsFromOpportunityId(
|
||||
@Args()
|
||||
{
|
||||
opportunityId,
|
||||
page,
|
||||
pageSize,
|
||||
}: GetTimelineCalendarEventsFromOpportunityIdArgs,
|
||||
@AuthWorkspaceMemberId() workspaceMemberId: string,
|
||||
) {
|
||||
const timelineCalendarEvents =
|
||||
await this.timelineCalendarEventService.getCalendarEventsFromOpportunityId(
|
||||
{
|
||||
currentWorkspaceMemberId: workspaceMemberId,
|
||||
opportunityId,
|
||||
page,
|
||||
pageSize,
|
||||
},
|
||||
);
|
||||
|
||||
return timelineCalendarEvents;
|
||||
}
|
||||
}
|
||||
|
||||
+43
@@ -9,6 +9,7 @@ import { TimelineCalendarEventsWithTotal } from 'src/engine/core-modules/calenda
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { CalendarChannelVisibility } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
|
||||
import { CalendarEventWorkspaceEntity } from 'src/modules/calendar/common/standard-objects/calendar-event.workspace-entity';
|
||||
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@@ -199,4 +200,46 @@ export class TimelineCalendarEventService {
|
||||
|
||||
return calendarEvents;
|
||||
}
|
||||
|
||||
async getCalendarEventsFromOpportunityId({
|
||||
currentWorkspaceMemberId,
|
||||
opportunityId,
|
||||
page = 1,
|
||||
pageSize = TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE,
|
||||
}: {
|
||||
currentWorkspaceMemberId: string;
|
||||
opportunityId: string;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}): Promise<TimelineCalendarEventsWithTotal> {
|
||||
const opportunityRepository =
|
||||
await this.twentyORMManager.getRepository<OpportunityWorkspaceEntity>(
|
||||
'opportunity',
|
||||
);
|
||||
|
||||
const opportunity = await opportunityRepository.findOne({
|
||||
where: {
|
||||
id: opportunityId,
|
||||
},
|
||||
select: {
|
||||
companyId: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!opportunity?.companyId) {
|
||||
return {
|
||||
totalNumberOfCalendarEvents: 0,
|
||||
timelineCalendarEvents: [],
|
||||
};
|
||||
}
|
||||
|
||||
const calendarEvents = await this.getCalendarEventsFromCompanyId({
|
||||
currentWorkspaceMemberId,
|
||||
companyId: opportunity.companyId,
|
||||
page,
|
||||
pageSize,
|
||||
});
|
||||
|
||||
return calendarEvents;
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -5,6 +5,7 @@ import { TimelineThreadsWithTotal } from 'src/engine/core-modules/messaging/dtos
|
||||
import { TimelineMessagingService } from 'src/engine/core-modules/messaging/services/timeline-messaging.service';
|
||||
import { formatThreads } from 'src/engine/core-modules/messaging/utils/format-threads.util';
|
||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||
import { OpportunityWorkspaceEntity } from 'src/modules/opportunity/standard-objects/opportunity.workspace-entity';
|
||||
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
|
||||
|
||||
@Injectable()
|
||||
@@ -98,4 +99,41 @@ export class GetMessagesService {
|
||||
|
||||
return messageThreads;
|
||||
}
|
||||
|
||||
async getMessagesFromOpportunityId(
|
||||
workspaceMemberId: string,
|
||||
opportunityId: string,
|
||||
page = 1,
|
||||
pageSize: number = TIMELINE_THREADS_DEFAULT_PAGE_SIZE,
|
||||
): Promise<TimelineThreadsWithTotal> {
|
||||
const opportunityRepository =
|
||||
await this.twentyORMManager.getRepository<OpportunityWorkspaceEntity>(
|
||||
'opportunity',
|
||||
);
|
||||
|
||||
const opportunity = await opportunityRepository.findOne({
|
||||
where: {
|
||||
id: opportunityId,
|
||||
},
|
||||
select: {
|
||||
companyId: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!opportunity?.companyId) {
|
||||
return {
|
||||
totalNumberOfThreads: 0,
|
||||
timelineThreads: [],
|
||||
};
|
||||
}
|
||||
|
||||
const messageThreads = await this.getMessagesFromCompanyId(
|
||||
workspaceMemberId,
|
||||
opportunity.companyId,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return messageThreads;
|
||||
}
|
||||
}
|
||||
|
||||
+40
@@ -41,6 +41,19 @@ class GetTimelineThreadsFromCompanyIdArgs {
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
class GetTimelineThreadsFromOpportunityIdArgs {
|
||||
@Field(() => UUIDScalarType)
|
||||
opportunityId: string;
|
||||
|
||||
@Field(() => Int)
|
||||
page: number;
|
||||
|
||||
@Field(() => Int)
|
||||
@Max(TIMELINE_THREADS_MAX_PAGE_SIZE)
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard, UserAuthGuard)
|
||||
@Resolver(() => TimelineThreadsWithTotal)
|
||||
export class TimelineMessagingResolver {
|
||||
@@ -100,4 +113,31 @@ export class TimelineMessagingResolver {
|
||||
|
||||
return timelineThreads;
|
||||
}
|
||||
|
||||
@Query(() => TimelineThreadsWithTotal)
|
||||
async getTimelineThreadsFromOpportunityId(
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Args()
|
||||
{ opportunityId, page, pageSize }: GetTimelineThreadsFromOpportunityIdArgs,
|
||||
) {
|
||||
const workspaceMember = await this.userService.loadWorkspaceMember(
|
||||
user,
|
||||
workspace,
|
||||
);
|
||||
|
||||
if (!workspaceMember) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timelineThreads =
|
||||
await this.getMessagesFromPersonIdsService.getMessagesFromOpportunityId(
|
||||
workspaceMember.id,
|
||||
opportunityId,
|
||||
page,
|
||||
pageSize,
|
||||
);
|
||||
|
||||
return timelineThreads;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user