Skip to content

Commit d2ba248

Browse files
authored
Merge pull request #543 from gitroomhq/feat/short-linking
Added shortlinking option
2 parents 9f6a211 + 772ff4c commit d2ba248

File tree

12 files changed

+480
-27
lines changed

12 files changed

+480
-27
lines changed

apps/backend/src/api/api.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { AgenciesController } from '@gitroom/backend/api/routes/agencies.control
2626
import { PublicController } from '@gitroom/backend/api/routes/public.controller';
2727
import { RootController } from '@gitroom/backend/api/routes/root.controller';
2828
import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
29+
import { ShortLinkService } from '@gitroom/nestjs-libraries/short-linking/short.link.service';
2930

3031
const authenticatedController = [
3132
UsersController,
@@ -63,6 +64,7 @@ const authenticatedController = [
6364
CodesService,
6465
IntegrationManager,
6566
TrackService,
67+
ShortLinkService,
6668
],
6769
get exports() {
6870
return [...this.imports, ...this.providers];

apps/backend/src/api/routes/posts.controller.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { CreateGeneratedPostsDto } from '@gitroom/nestjs-libraries/dtos/generato
2727
import { AgentGraphService } from '@gitroom/nestjs-libraries/agent/agent.graph.service';
2828
import { Response } from 'express';
2929
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
30+
import { ShortLinkService } from '@gitroom/nestjs-libraries/short-linking/short.link.service';
3031

3132
@ApiTags('Posts')
3233
@Controller('/posts')
@@ -35,9 +36,23 @@ export class PostsController {
3536
private _postsService: PostsService,
3637
private _starsService: StarsService,
3738
private _messagesService: MessagesService,
38-
private _agentGraphService: AgentGraphService
39+
private _agentGraphService: AgentGraphService,
40+
private _shortLinkService: ShortLinkService
3941
) {}
4042

43+
@Get('/:id/statistics')
44+
async getStatistics(
45+
@GetOrgFromRequest() org: Organization,
46+
@Param('id') id: string
47+
) {
48+
return this._postsService.getStatistics(org.id, id);
49+
}
50+
51+
@Post('/should-shortlink')
52+
async shouldShortlink(@Body() body: { messages: string[] }) {
53+
return { ask: this._shortLinkService.askShortLinkedin(body.messages) };
54+
}
55+
4156
@Get('/marketplace/:id?')
4257
async getMarketplacePosts(
4358
@GetOrgFromRequest() org: Organization,
@@ -61,26 +76,16 @@ export class PostsController {
6176
@GetOrgFromRequest() org: Organization,
6277
@Query() query: GetPostsDto
6378
) {
64-
const [posts] = await Promise.all([
65-
this._postsService.getPosts(org.id, query),
66-
// this._commentsService.getAllCommentsByWeekYear(
67-
// org.id,
68-
// query.year,
69-
// query.week
70-
// ),
71-
]);
79+
const posts = await this._postsService.getPosts(org.id, query);
7280

7381
return {
7482
posts,
75-
// comments,
7683
};
7784
}
7885

7986
@Get('/find-slot')
80-
async findSlot(
81-
@GetOrgFromRequest() org: Organization,
82-
) {
83-
return {date: await this._postsService.findFreeDateTime(org.id)}
87+
async findSlot(@GetOrgFromRequest() org: Organization) {
88+
return { date: await this._postsService.findFreeDateTime(org.id) };
8489
}
8590

8691
@Get('/predict-trending')
@@ -128,10 +133,7 @@ export class PostsController {
128133
@Res({ passthrough: false }) res: Response
129134
) {
130135
res.setHeader('Content-Type', 'application/json; charset=utf-8');
131-
for await (const event of this._agentGraphService.start(
132-
org.id,
133-
body,
134-
)) {
136+
for await (const event of this._agentGraphService.start(org.id, body)) {
135137
res.write(JSON.stringify(event) + '\n');
136138
}
137139

apps/frontend/src/components/launches/add.edit.model.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
'use client';
22

33
import React, {
4-
ClipboardEventHandler,
54
FC,
65
Fragment,
76
MouseEventHandler,
87
useCallback,
98
useEffect,
109
useMemo,
11-
useRef,
1210
ClipboardEvent,
1311
useState,
1412
memo,
@@ -363,12 +361,33 @@ export const AddEditModal: FC<{
363361
}
364362
}
365363

364+
const shortLinkUrl = await (
365+
await fetch('/posts/should-shortlink', {
366+
method: 'POST',
367+
body: JSON.stringify({
368+
messages: allKeys.flatMap((p) =>
369+
p.value.flatMap((a) =>
370+
a.content.slice(0, p.maximumCharacters || 1000000)
371+
)
372+
),
373+
}),
374+
})
375+
).json();
376+
377+
const shortLink = !shortLinkUrl.ask
378+
? false
379+
: await deleteDialog(
380+
'Do you want to shortlink the URLs? it will let you get statistics over clicks',
381+
'Yes, shortlink it!'
382+
);
383+
366384
setLoading(true);
367385
await fetch('/posts', {
368386
method: 'POST',
369387
body: JSON.stringify({
370388
...(postFor ? { order: postFor.id } : {}),
371389
type,
390+
shortLink,
372391
date: dateState.utc().format('YYYY-MM-DDTHH:mm:ss'),
373392
posts: allKeys.map((p) => ({
374393
...p,

apps/frontend/src/components/launches/calendar.tsx

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { extend } from 'dayjs';
3535
import { isUSCitizen } from './helpers/isuscitizen.utils';
3636
import removeMd from 'remove-markdown';
3737
import { useInterval } from '@mantine/hooks';
38+
import { StatisticsModal } from '@gitroom/frontend/components/launches/statistics';
3839
extend(isSameOrAfter);
3940
extend(isSameOrBefore);
4041

@@ -508,6 +509,23 @@ export const CalendarColumn: FC<{
508509
});
509510
}, [integrations, getDate]);
510511

512+
const openStatistics = useCallback(
513+
(id: string) => () => {
514+
modal.openModal({
515+
closeOnClickOutside: true,
516+
closeOnEscape: true,
517+
withCloseButton: false,
518+
classNames: {
519+
modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor',
520+
},
521+
children: <StatisticsModal postId={id} />,
522+
size: '80%',
523+
// title: `Adding posts for ${getDate.format('DD/MM/YYYY HH:mm')}`,
524+
});
525+
},
526+
[]
527+
);
528+
511529
const addProvider = useAddProvider();
512530

513531
return (
@@ -551,6 +569,7 @@ export const CalendarColumn: FC<{
551569
isBeforeNow={isBeforeNow}
552570
date={getDate}
553571
state={post.state}
572+
statistics={openStatistics(post.id)}
554573
editPost={editPost(post, false)}
555574
duplicatePost={editPost(post, true)}
556575
post={post}
@@ -654,13 +673,22 @@ const CalendarItem: FC<{
654673
isBeforeNow: boolean;
655674
editPost: () => void;
656675
duplicatePost: () => void;
676+
statistics: () => void;
657677
integrations: Integrations[];
658678
state: State;
659679
display: 'day' | 'week' | 'month';
660680
post: Post & { integration: Integration };
661681
}> = memo((props) => {
662-
const { editPost, duplicatePost, post, date, isBeforeNow, state, display } =
663-
props;
682+
const {
683+
editPost,
684+
statistics,
685+
duplicatePost,
686+
post,
687+
date,
688+
isBeforeNow,
689+
state,
690+
display,
691+
} = props;
664692

665693
const preview = useCallback(() => {
666694
window.open(`/p/` + post.id + '?share=true', '_blank');
@@ -683,18 +711,24 @@ const CalendarItem: FC<{
683711
className={clsx('w-full flex h-full flex-1 flex-col group', 'relative')}
684712
style={{ opacity }}
685713
>
686-
<div className="text-primary bg-forth text-[11px] h-[15px] w-full rounded-tr-[10px] rounded-tl-[10px] flex justify-center gap-[10px] px-[5px]">
714+
<div className="text-white bg-forth text-[11px] h-[15px] w-full rounded-tr-[10px] rounded-tl-[10px] flex justify-center gap-[10px] px-[5px]">
687715
<div
688716
className="hidden group-hover:block hover:underline cursor-pointer"
689717
onClick={duplicatePost}
690718
>
691-
Duplicate
719+
<Duplicate />
692720
</div>
693721
<div
694722
className="hidden group-hover:block hover:underline cursor-pointer"
695723
onClick={preview}
696724
>
697-
Preview
725+
<Preview />
726+
</div>{' '}
727+
<div
728+
className="hidden group-hover:block hover:underline cursor-pointer"
729+
onClick={statistics}
730+
>
731+
<Statistics />
698732
</div>
699733
</div>
700734
<div
@@ -730,3 +764,60 @@ const CalendarItem: FC<{
730764
</div>
731765
);
732766
});
767+
768+
const Duplicate = () => {
769+
return (
770+
<svg
771+
xmlns="http://www.w3.org/2000/svg"
772+
width="15"
773+
height="15"
774+
viewBox="0 0 32 32"
775+
fill="none"
776+
data-tooltip-id="tooltip"
777+
data-tooltip-content="Duplicate Post"
778+
>
779+
<path
780+
d="M27 5H9C8.46957 5 7.96086 5.21071 7.58579 5.58579C7.21071 5.96086 7 6.46957 7 7V9H5C4.46957 9 3.96086 9.21071 3.58579 9.58579C3.21071 9.96086 3 10.4696 3 11V25C3 25.5304 3.21071 26.0391 3.58579 26.4142C3.96086 26.7893 4.46957 27 5 27H23C23.5304 27 24.0391 26.7893 24.4142 26.4142C24.7893 26.0391 25 25.5304 25 25V23H27C27.5304 23 28.0391 22.7893 28.4142 22.4142C28.7893 22.0391 29 21.5304 29 21V7C29 6.46957 28.7893 5.96086 28.4142 5.58579C28.0391 5.21071 27.5304 5 27 5ZM23 11V13H5V11H23ZM23 25H5V15H23V25ZM27 21H25V11C25 10.4696 24.7893 9.96086 24.4142 9.58579C24.0391 9.21071 23.5304 9 23 9H9V7H27V21Z"
781+
fill="white"
782+
/>
783+
</svg>
784+
);
785+
};
786+
787+
const Preview = () => {
788+
return (
789+
<svg
790+
xmlns="http://www.w3.org/2000/svg"
791+
width="15"
792+
height="15"
793+
viewBox="0 0 32 32"
794+
fill="none"
795+
data-tooltip-id="tooltip"
796+
data-tooltip-content="Preview Post"
797+
>
798+
<path
799+
d="M30.9137 15.595C30.87 15.4963 29.8112 13.1475 27.4575 10.7937C24.3212 7.6575 20.36 6 16 6C11.64 6 7.67874 7.6575 4.54249 10.7937C2.18874 13.1475 1.12499 15.5 1.08624 15.595C1.02938 15.7229 1 15.8613 1 16.0012C1 16.1412 1.02938 16.2796 1.08624 16.4075C1.12999 16.5062 2.18874 18.8538 4.54249 21.2075C7.67874 24.3425 11.64 26 16 26C20.36 26 24.3212 24.3425 27.4575 21.2075C29.8112 18.8538 30.87 16.5062 30.9137 16.4075C30.9706 16.2796 31 16.1412 31 16.0012C31 15.8613 30.9706 15.7229 30.9137 15.595ZM16 24C12.1525 24 8.79124 22.6012 6.00874 19.8438C4.86704 18.7084 3.89572 17.4137 3.12499 16C3.89551 14.5862 4.86686 13.2915 6.00874 12.1562C8.79124 9.39875 12.1525 8 16 8C19.8475 8 23.2087 9.39875 25.9912 12.1562C27.1352 13.2912 28.1086 14.5859 28.8812 16C27.98 17.6825 24.0537 24 16 24ZM16 10C14.8133 10 13.6533 10.3519 12.6666 11.0112C11.6799 11.6705 10.9108 12.6075 10.4567 13.7039C10.0026 14.8003 9.88377 16.0067 10.1153 17.1705C10.3468 18.3344 10.9182 19.4035 11.7573 20.2426C12.5965 21.0818 13.6656 21.6532 14.8294 21.8847C15.9933 22.1162 17.1997 21.9974 18.2961 21.5433C19.3924 21.0892 20.3295 20.3201 20.9888 19.3334C21.6481 18.3467 22 17.1867 22 16C21.9983 14.4092 21.3657 12.884 20.2408 11.7592C19.1159 10.6343 17.5908 10.0017 16 10ZM16 20C15.2089 20 14.4355 19.7654 13.7777 19.3259C13.1199 18.8864 12.6072 18.2616 12.3045 17.5307C12.0017 16.7998 11.9225 15.9956 12.0768 15.2196C12.2312 14.4437 12.6122 13.731 13.1716 13.1716C13.731 12.6122 14.4437 12.2312 15.2196 12.0769C15.9956 11.9225 16.7998 12.0017 17.5307 12.3045C18.2616 12.6072 18.8863 13.1199 19.3259 13.7777C19.7654 14.4355 20 15.2089 20 16C20 17.0609 19.5786 18.0783 18.8284 18.8284C18.0783 19.5786 17.0609 20 16 20Z"
800+
fill="white"
801+
/>
802+
</svg>
803+
);
804+
};
805+
806+
export const Statistics = () => {
807+
return (
808+
<svg
809+
xmlns="http://www.w3.org/2000/svg"
810+
width="15"
811+
height="15"
812+
viewBox="0 0 32 32"
813+
fill="none"
814+
data-tooltip-id="tooltip"
815+
data-tooltip-content="Post Statistics"
816+
>
817+
<path
818+
d="M28 25H27V5C27 4.73478 26.8946 4.48043 26.7071 4.29289C26.5196 4.10536 26.2652 4 26 4H19C18.7348 4 18.4804 4.10536 18.2929 4.29289C18.1054 4.48043 18 4.73478 18 5V10H12C11.7348 10 11.4804 10.1054 11.2929 10.2929C11.1054 10.4804 11 10.7348 11 11V16H6C5.73478 16 5.48043 16.1054 5.29289 16.2929C5.10536 16.4804 5 16.7348 5 17V25H4C3.73478 25 3.48043 25.1054 3.29289 25.2929C3.10536 25.4804 3 25.7348 3 26C3 26.2652 3.10536 26.5196 3.29289 26.7071C3.48043 26.8946 3.73478 27 4 27H28C28.2652 27 28.5196 26.8946 28.7071 26.7071C28.8946 26.5196 29 26.2652 29 26C29 25.7348 28.8946 25.4804 28.7071 25.2929C28.5196 25.1054 28.2652 25 28 25ZM20 6H25V25H20V6ZM13 12H18V25H13V12ZM7 18H11V25H7V18Z"
819+
fill="white"
820+
/>
821+
</svg>
822+
);
823+
};
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React, { FC, Fragment, useCallback } from 'react';
2+
import { useModals } from '@mantine/modals';
3+
import useSWR from 'swr';
4+
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
5+
6+
export const StatisticsModal: FC<{ postId: string }> = (props) => {
7+
const { postId } = props;
8+
const modals = useModals();
9+
const fetch = useFetch();
10+
11+
const loadStatistics = useCallback(async () => {
12+
return (await fetch(`/posts/${postId}/statistics`)).json();
13+
}, [postId]);
14+
15+
const closeAll = useCallback(() => {
16+
modals.closeAll();
17+
}, []);
18+
19+
const { data, isLoading } = useSWR(
20+
`/posts/${postId}/statistics`,
21+
loadStatistics
22+
);
23+
24+
return (
25+
<div className="bg-sixth p-[32px] w-full max-w-[920px] mx-auto flex flex-col rounded-[4px] border border-customColor6 relative">
26+
<button
27+
onClick={closeAll}
28+
className="outline-none absolute right-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
29+
type="button"
30+
>
31+
<svg
32+
viewBox="0 0 15 15"
33+
fill="none"
34+
xmlns="http://www.w3.org/2000/svg"
35+
width="16"
36+
height="16"
37+
>
38+
<path
39+
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
40+
fill="currentColor"
41+
fillRule="evenodd"
42+
clipRule="evenodd"
43+
/>
44+
</svg>
45+
</button>
46+
<h1 className="text-[24px]">Statistics</h1>
47+
{isLoading ? (
48+
<div>Loading</div>
49+
) : (
50+
<>
51+
{data.clicks.length === 0 ? (
52+
'No Results'
53+
) : (
54+
<>
55+
<div className="grid grid-cols-3 mt-[20px]">
56+
<div className="bg-forth p-[4px] rounded-tl-lg">Short Link</div>
57+
<div className="bg-forth p-[4px]">Original Link</div>
58+
<div className="bg-forth p-[4px] rounded-tr-lg">Clicks</div>
59+
{data.clicks.map((p: any) => (
60+
<Fragment key={p.short}>
61+
<div className="p-[4px] py-[10px] bg-customColor6">{p.short}</div>
62+
<div className="p-[4px] py-[10px] bg-customColor6">{p.original}</div>
63+
<div className="p-[4px] py-[10px] bg-customColor6">{p.clicks}</div>
64+
</Fragment>
65+
))}
66+
</div>
67+
</>
68+
)}
69+
</>
70+
)}
71+
</div>
72+
);
73+
};

libraries/nestjs-libraries/src/database/prisma/database.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { OpenaiService } from '@gitroom/nestjs-libraries/openai/openai.service';
2828
import { AgenciesService } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.service';
2929
import { AgenciesRepository } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.repository';
3030
import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
31+
import { ShortLinkService } from '@gitroom/nestjs-libraries/short-linking/short.link.service';
3132

3233
@Global()
3334
@Module({
@@ -64,6 +65,7 @@ import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
6465
OpenaiService,
6566
EmailService,
6667
TrackService,
68+
ShortLinkService,
6769
],
6870
get exports() {
6971
return this.providers;

0 commit comments

Comments
 (0)