From ffe44318d55456b3e31f7ceb3c4cf1679b1d0bcc Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 3 Jul 2025 13:03:37 +0700 Subject: [PATCH] feat: focused channel --- .../src/api/routes/posts.controller.ts | 12 +++- .../src/components/launches/menu/menu.tsx | 71 ++++++++++++++++++- .../components/new-launch/add.edit.modal.tsx | 15 ++++ .../integrations/integration.repository.ts | 3 +- .../integrations/integration.service.ts | 12 +++- .../database/prisma/posts/posts.service.ts | 4 +- 6 files changed, 107 insertions(+), 10 deletions(-) diff --git a/apps/backend/src/api/routes/posts.controller.ts b/apps/backend/src/api/routes/posts.controller.ts index c07f3b5e..534e2642 100644 --- a/apps/backend/src/api/routes/posts.controller.ts +++ b/apps/backend/src/api/routes/posts.controller.ts @@ -38,7 +38,7 @@ export class PostsController { private _starsService: StarsService, private _messagesService: MessagesService, private _agentGraphService: AgentGraphService, - private _shortLinkService: ShortLinkService, + private _shortLinkService: ShortLinkService ) {} @Get('/:id/statistics') @@ -111,6 +111,14 @@ export class PostsController { return { date: await this._postsService.findFreeDateTime(org.id) }; } + @Get('/find-slot/:id') + async findSlotIntegration( + @GetOrgFromRequest() org: Organization, + @Param('id') id?: string + ) { + return { date: await this._postsService.findFreeDateTime(org.id, id) }; + } + @Get('/predict-trending') predictTrending() { return this._starsService.predictTrending(); @@ -183,7 +191,7 @@ export class PostsController { @Post('/separate-posts') async separatePosts( @GetOrgFromRequest() org: Organization, - @Body() body: { content: string, len: number } + @Body() body: { content: string; len: number } ) { return this._postsService.separatePosts(body.content, body.len); } diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx index 2a43acaf..41892a93 100644 --- a/apps/frontend/src/components/launches/menu/menu.tsx +++ b/apps/frontend/src/components/launches/menu/menu.tsx @@ -14,7 +14,10 @@ import { useToaster } from '@gitroom/react/toaster/toaster'; import interClass from '@gitroom/react/helpers/inter.font'; import { useModals } from '@mantine/modals'; import { TimeTable } from '@gitroom/frontend/components/launches/time.table'; -import { useCalendar } from '@gitroom/frontend/components/launches/calendar.context'; +import { + Integrations, + useCalendar, +} from '@gitroom/frontend/components/launches/calendar.context'; import { BotPicture } from '@gitroom/frontend/components/launches/bot.picture'; import { CustomerModal } from '@gitroom/frontend/components/launches/customer.modal'; import { Integration } from '@prisma/client'; @@ -22,6 +25,8 @@ import { SettingsModal } from '@gitroom/frontend/components/launches/settings.mo import { CustomVariables } from '@gitroom/frontend/components/launches/add.provider.component'; import { useRouter } from 'next/navigation'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal'; +import dayjs from 'dayjs'; export const Menu: FC<{ canEnable: boolean; canDisable: boolean; @@ -50,7 +55,7 @@ export const Menu: FC<{ const fetch = useFetch(); const router = useRouter(); - const { integrations } = useCalendar(); + const { integrations, reloadCalendarView } = useCalendar(); const toast = useToaster(); const modal = useModals(); const [show, setShow] = useState(false); @@ -112,6 +117,7 @@ export const Menu: FC<{ setShow(false); onChange(true); }, []); + const enableChannel = useCallback(async () => { await fetch('/integrations/enable', { method: 'POST', @@ -123,6 +129,7 @@ export const Menu: FC<{ setShow(false); onChange(false); }, []); + const editTimeTable = useCallback(() => { const findIntegration = integrations.find( (integration) => integration.id === id @@ -139,6 +146,42 @@ export const Menu: FC<{ }); setShow(false); }, [integrations]); + + const createPost = useCallback( + (integration: Integrations) => async () => { + setShow(false); + + const { date } = await ( + await fetch(`/posts/find-slot/${integration.id}`) + ).json(); + + modal.openModal({ + closeOnClickOutside: false, + closeOnEscape: false, + withCloseButton: false, + classNames: { + modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor', + }, + children: ( + ({ + ...p, + }))} + reopenModal={createPost(integration)} + mutate={reloadCalendarView} + integrations={integrations} + selectedChannels={[integration.id]} + focusedChannel={integration.id} + date={dayjs.utc(date).local()} + /> + ), + size: '80%', + title: ``, + }); + }, + [integrations] + ); + const changeBotPicture = useCallback(() => { const findIntegration = integrations.find( (integration) => integration.id === id @@ -251,6 +294,30 @@ export const Menu: FC<{ onClick={(e) => e.stopPropagation()} className={`absolute top-[100%] start-0 p-[8px] px-[20px] bg-fifth flex flex-col gap-[16px] z-[100] rounded-[8px] border border-tableBorder ${interClass} text-nowrap`} > + {canDisable && !findIntegration?.refreshNeeded && ( +
+
+ + + +
+
+ {t('create_new_post', 'Create a new post')} +
+
+ )} {canDisable && findIntegration?.refreshNeeded && !findIntegration.customFields && ( diff --git a/apps/frontend/src/components/new-launch/add.edit.modal.tsx b/apps/frontend/src/components/new-launch/add.edit.modal.tsx index 1649fa1a..aae6811a 100644 --- a/apps/frontend/src/components/new-launch/add.edit.modal.tsx +++ b/apps/frontend/src/components/new-launch/add.edit.modal.tsx @@ -14,7 +14,9 @@ export interface AddEditModalProps { date: dayjs.Dayjs; integrations: Integrations[]; allIntegrations?: Integrations[]; + selectedChannels?: string[]; set?: CreatePostDto; + focusedChannel?: string; addEditSets?: (data: any) => void; reopenModal: () => void; mutate: () => void; @@ -82,6 +84,15 @@ export const AddEditModalInner: FC = (props) => { ); addOrRemoveSelectedIntegration(integration, existingData.settings); } + + if (props?.selectedChannels?.length) { + for (const channel of props.selectedChannels) { + const integration = integrations.find((i) => i.id === channel); + if (integration) { + addOrRemoveSelectedIntegration(integration, {}); + } + } + } }, []); if (existingData.integration && selectedIntegrations.length === 0) { @@ -135,6 +146,10 @@ export const AddEditModalInnerInner: FC = (props) => { setCurrent(existingData.integration); } + if (props.focusedChannel) { + setCurrent(props.focusedChannel); + } + addGlobalValue( 0, props.onlyValues?.length diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts index b16a74d8..c6541cdc 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts @@ -549,9 +549,10 @@ export class IntegrationRepository { }); } - async getPostingTimes(orgId: string) { + async getPostingTimes(orgId: string, integrationsId?: string) { return this._integration.model.integration.findMany({ where: { + ...(integrationsId ? { id: integrationsId } : {}), organizationId: orgId, disabled: false, deletedAt: null, diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 4f574a37..60be9a2d 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -33,7 +33,7 @@ export class IntegrationService { private _autopostsRepository: AutopostRepository, private _integrationManager: IntegrationManager, private _notificationService: NotificationService, - private _workerServiceProducer: BullMqClient, + private _workerServiceProducer: BullMqClient ) {} async changeActiveCron(orgId: string) { @@ -673,8 +673,14 @@ export class IntegrationService { return difference(id, loadOnlyIds); } - async findFreeDateTime(orgId: string): Promise { - const findTimes = await this._integrationRepository.getPostingTimes(orgId); + async findFreeDateTime( + orgId: string, + integrationsId?: string + ): Promise { + const findTimes = await this._integrationRepository.getPostingTimes( + orgId, + integrationsId + ); return uniq( findTimes.reduce((all: any, current: any) => { return [ diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 7455b84c..793022de 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -928,8 +928,8 @@ export class PostsService { return this._postRepository.findPopularPosts(category, topic); } - async findFreeDateTime(orgId: string) { - const findTimes = await this._integrationService.findFreeDateTime(orgId); + async findFreeDateTime(orgId: string, integrationId?: string) { + const findTimes = await this._integrationService.findFreeDateTime(orgId, integrationId); return this.findFreeDateTimeRecursive( orgId, findTimes,