Skip to content

Commit 3a785e1

Browse files
author
Nevo David
committed
feat: timing
1 parent 6de19e4 commit 3a785e1

File tree

20 files changed

+801
-177
lines changed

20 files changed

+801
-177
lines changed

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { ApiTags } from '@nestjs/swagger';
2626
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
2727
import { NotEnoughScopesFilter } from '@gitroom/nestjs-libraries/integrations/integration.missing.scopes';
2828
import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service';
29+
import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.time.dto';
2930

3031
@ApiTags('Integrations')
3132
@Controller('/integrations')
@@ -55,6 +56,7 @@ export class IntegrationsController {
5556
inBetweenSteps: p.inBetweenSteps,
5657
refreshNeeded: p.refreshNeeded,
5758
type: p.type,
59+
time: JSON.parse(p.postingTimes)
5860
})),
5961
};
6062
}
@@ -97,6 +99,14 @@ export class IntegrationsController {
9799
return { url };
98100
}
99101

102+
@Post('/:id/time')
103+
async setTime(
104+
@GetOrgFromRequest() org: Organization,
105+
@Param('id') id: string,
106+
@Body() body: IntegrationTimeDto
107+
) {
108+
return this._integrationService.setTimes(org.id, id, body);
109+
}
100110
@Post('/function')
101111
async functionIntegration(
102112
@GetOrgFromRequest() org: Organization,
@@ -238,7 +248,8 @@ export class IntegrationsController {
238248
expiresIn,
239249
username,
240250
integrationProvider.isBetweenSteps,
241-
body.refresh
251+
body.refresh,
252+
+body.timezone
242253
);
243254
}
244255

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.reque
1313
import { Organization, User } from '@prisma/client';
1414
import { CreatePostDto } from '@gitroom/nestjs-libraries/dtos/posts/create.post.dto';
1515
import { GetPostsDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.dto';
16-
import { CommentsService } from '@gitroom/nestjs-libraries/database/prisma/comments/comments.service';
1716
import { StarsService } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.service';
1817
import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability';
1918
import {
@@ -30,7 +29,6 @@ import { CreateGeneratedPostsDto } from '@gitroom/nestjs-libraries/dtos/generato
3029
export class PostsController {
3130
constructor(
3231
private _postsService: PostsService,
33-
private _commentsService: CommentsService,
3432
private _starsService: StarsService,
3533
private _messagesService: MessagesService
3634
) {}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { HttpStatusCode } from 'axios';
2+
3+
export const dynamic = 'force-dynamic';
4+
5+
import { internalFetch } from '@gitroom/helpers/utils/internal.fetch';
6+
import { redirect } from 'next/navigation';
7+
8+
export default async function Page({
9+
params: { provider },
10+
searchParams,
11+
}: {
12+
params: { provider: string };
13+
searchParams: any;
14+
}) {
15+
if (provider === 'x') {
16+
searchParams = {
17+
...searchParams,
18+
state: searchParams.oauth_token || '',
19+
code: searchParams.oauth_verifier || '',
20+
refresh: searchParams.refresh || '',
21+
};
22+
}
23+
24+
const data = await internalFetch(`/integrations/social/${provider}/connect`, {
25+
method: 'POST',
26+
body: JSON.stringify(searchParams),
27+
});
28+
29+
if (data.status === HttpStatusCode.NotAcceptable) {
30+
return redirect(`/launches?scope=missing`);
31+
}
32+
33+
const { inBetweenSteps, id } = await data.json();
34+
35+
if (inBetweenSteps && !searchParams.refresh) {
36+
return redirect(`/launches?added=${provider}&continue=${id}`);
37+
}
38+
39+
return redirect(`/launches?added=${provider}`);
40+
}
Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,13 @@
1-
import { HttpStatusCode } from 'axios';
1+
import { IntegrationRedirectComponent } from '@gitroom/frontend/components/launches/integration.redirect.component';
22

33
export const dynamic = 'force-dynamic';
44

5-
import { internalFetch } from '@gitroom/helpers/utils/internal.fetch';
6-
import { redirect } from 'next/navigation';
7-
85
export default async function Page({
96
params: { provider },
107
searchParams,
118
}: {
129
params: { provider: string };
1310
searchParams: any;
1411
}) {
15-
if (provider === 'x') {
16-
searchParams = {
17-
...searchParams,
18-
state: searchParams.oauth_token || '',
19-
code: searchParams.oauth_verifier || '',
20-
refresh: searchParams.refresh || '',
21-
};
22-
}
23-
24-
const data = await internalFetch(`/integrations/social/${provider}/connect`, {
25-
method: 'POST',
26-
body: JSON.stringify(searchParams),
27-
});
28-
29-
if (data.status === HttpStatusCode.NotAcceptable) {
30-
return redirect(`/launches?scope=missing`);
31-
}
32-
33-
const { inBetweenSteps, id } = await data.json();
34-
35-
if (inBetweenSteps && !searchParams.refresh) {
36-
return redirect(`/launches?added=${provider}&continue=${id}`);
37-
}
38-
39-
return redirect(`/launches?added=${provider}`);
12+
return <IntegrationRedirectComponent />;
4013
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { ReactNode } from 'react';
2+
3+
export default async function IntegrationLayout({
4+
children,
5+
}: {
6+
children: ReactNode;
7+
}) {
8+
return (
9+
<div className="text-6xl text-center mt-[50px]">
10+
Adding channel, Redirecting You{children}
11+
</div>
12+
);
13+
}

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

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { useStateCallback } from '@gitroom/react/helpers/use.state.callback';
4848
import { CopilotPopup } from '@copilotkit/react-ui';
4949
import { useUser } from '@gitroom/frontend/components/layout/user.context';
5050
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
51+
import Image from 'next/image';
5152

5253
export const AddEditModal: FC<{
5354
date: dayjs.Dayjs;
@@ -111,6 +112,8 @@ export const AddEditModal: FC<{
111112
setSelectedIntegrations([
112113
integrations.find((p) => p.id === existingData.integration)!,
113114
]);
115+
} else if (integrations.length === 1) {
116+
setSelectedIntegrations([integrations[0]]);
114117
}
115118
}, [existingData.integration]);
116119

@@ -279,7 +282,8 @@ export const AddEditModal: FC<{
279282
) {
280283
if (
281284
!(await deleteDialog(
282-
`${key?.integration?.name} post is too long, it will be cropped, do you want to continue?`, 'Yes, continue'
285+
`${key?.integration?.name} post is too long, it will be cropped, do you want to continue?`,
286+
'Yes, continue'
283287
))
284288
) {
285289
await key.trigger();
@@ -381,7 +385,9 @@ export const AddEditModal: FC<{
381385
instructions="You are an assistant that help the user to schedule their social media posts, everytime somebody write something, try to use a function call, if not prompt the user that the request is invalid and you are here to assists with social media posts"
382386
/>
383387
)}
384-
<div className={clsx('flex p-[10px] rounded-[4px] bg-primary gap-[20px]')}>
388+
<div
389+
className={clsx('flex p-[10px] rounded-[4px] bg-primary gap-[20px]')}
390+
>
385391
<div
386392
className={clsx(
387393
'flex flex-col gap-[16px] transition-all duration-700 whitespace-nowrap',
@@ -402,14 +408,43 @@ export const AddEditModal: FC<{
402408
</div>
403409
</TopTitle>
404410

405-
{!existingData.integration && (
411+
{!existingData.integration && integrations.length > 1 ? (
406412
<PickPlatforms
407413
integrations={integrations.filter((f) => !f.disabled)}
408414
selectedIntegrations={[]}
409415
singleSelect={false}
410416
onChange={setSelectedIntegrations}
411417
isMain={true}
412418
/>
419+
) : (
420+
<div
421+
className={clsx(
422+
'relative w-[34px] h-[34px] rounded-full flex justify-center items-center bg-fifth filter transition-all duration-500',
423+
)}
424+
>
425+
<Image
426+
src={selectedIntegrations?.[0]?.picture}
427+
className="rounded-full"
428+
alt={selectedIntegrations?.[0]?.identifier}
429+
width={32}
430+
height={32}
431+
/>
432+
{selectedIntegrations?.[0]?.identifier === 'youtube' ? (
433+
<img
434+
src="/icons/platforms/youtube.svg"
435+
className="absolute z-10 -bottom-[5px] -right-[5px]"
436+
width={20}
437+
/>
438+
) : (
439+
<Image
440+
src={`/icons/platforms/${selectedIntegrations?.[0]?.identifier}.png`}
441+
className="rounded-full absolute z-10 -bottom-[5px] -right-[5px] border border-fifth"
442+
alt={selectedIntegrations?.[0]?.identifier}
443+
width={20}
444+
height={20}
445+
/>
446+
)}
447+
</div>
413448
)}
414449
<div
415450
id="renderEditor"
@@ -426,13 +461,15 @@ export const AddEditModal: FC<{
426461
<Editor
427462
order={index}
428463
height={value.length > 1 ? 150 : 250}
429-
commands={[
430-
// ...commands
431-
// .getCommands()
432-
// .filter((f) => f.name === 'image'),
433-
// newImage,
434-
// postSelector(dateState),
435-
]}
464+
commands={
465+
[
466+
// ...commands
467+
// .getCommands()
468+
// .filter((f) => f.name === 'image'),
469+
// newImage,
470+
// postSelector(dateState),
471+
]
472+
}
436473
value={p.content}
437474
preview="edit"
438475
// @ts-ignore

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

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,25 @@ import weekOfYear from 'dayjs/plugin/weekOfYear';
2222
dayjs.extend(isoWeek);
2323
dayjs.extend(weekOfYear);
2424

25-
const CalendarContext = createContext({
25+
export const CalendarContext = createContext({
26+
currentDay: dayjs().day() as 0 | 1 | 2 | 3 | 4 | 5 | 6,
2627
currentWeek: dayjs().week(),
2728
currentYear: dayjs().year(),
2829
currentMonth: dayjs().month(),
2930
comments: [] as Array<{ date: string; total: number }>,
3031
integrations: [] as Integrations[],
3132
trendings: [] as string[],
3233
posts: [] as Array<Post & { integration: Integration }>,
33-
reloadCalendarView: () => {/** empty **/},
34+
reloadCalendarView: () => {
35+
/** empty **/
36+
},
3437
display: 'week',
3538
setFilters: (filters: {
3639
currentWeek: number;
3740
currentYear: number;
41+
currentDay: 0 | 1 | 2 | 3 | 4 | 5 | 6;
3842
currentMonth: number;
39-
display: 'week' | 'month';
43+
display: 'week' | 'month' | 'day';
4044
}) => {
4145
/** empty **/
4246
},
@@ -53,6 +57,7 @@ export interface Integrations {
5357
identifier: string;
5458
type: string;
5559
picture: string;
60+
time: { time: number }[];
5661
}
5762

5863
function getWeekNumber(date: Date) {
@@ -82,39 +87,37 @@ export const CalendarWeekProvider: FC<{
8287
const [trendings] = useState<string[]>([]);
8388
const searchParams = useSearchParams();
8489

85-
const display = searchParams.get('month') ? 'month' : 'week';
90+
const display = searchParams.get('display') || 'week';
91+
8692
const [filters, setFilters] = useState({
87-
currentWeek:
88-
display === 'week'
89-
? +(searchParams.get('week') || getWeekNumber(new Date()))
90-
: 0,
91-
currentMonth:
92-
display === 'week' ? 0 : +(searchParams.get('month') || dayjs().month()),
93+
currentDay: +(searchParams.get('day') || dayjs().day()) as
94+
| 0
95+
| 1
96+
| 2
97+
| 3
98+
| 4
99+
| 5
100+
| 6,
101+
currentWeek: +(searchParams.get('week') || getWeekNumber(new Date())),
102+
currentMonth: +(searchParams.get('month') || dayjs().month()),
93103
currentYear: +(searchParams.get('year') || dayjs().year()),
94104
display,
95105
});
96106

97107
const params = useMemo(() => {
98-
return new URLSearchParams(
99-
filters.currentWeek
100-
? {
101-
week: filters.currentWeek.toString(),
102-
year: filters.currentYear.toString(),
103-
}
104-
: {
105-
year: filters.currentYear.toString(),
106-
month: (filters.currentMonth + 1).toString(),
107-
}
108-
).toString();
109-
}, [filters]);
110-
111-
const loadData = useCallback(
112-
async () => {
113-
const data = (await fetch(`/posts?${params}`)).json();
114-
return data;
115-
},
116-
[filters, params]
117-
);
108+
return new URLSearchParams({
109+
display: filters.display,
110+
day: filters.currentDay.toString(),
111+
week: filters.currentWeek.toString(),
112+
month: (filters.currentMonth + 1).toString(),
113+
year: filters.currentYear.toString(),
114+
}).toString();
115+
}, [filters, display]);
116+
117+
const loadData = useCallback(async () => {
118+
const data = (await fetch(`/posts?${params}`)).json();
119+
return data;
120+
}, [filters, params]);
118121

119122
const swr = useSWR(`/posts-${params}`, loadData, {
120123
refreshInterval: 3600000,
@@ -125,22 +128,23 @@ export const CalendarWeekProvider: FC<{
125128

126129
const setFiltersWrapper = useCallback(
127130
(filters: {
131+
currentDay: 0 | 1 | 2 | 3 | 4 | 5 | 6;
128132
currentWeek: number;
129133
currentYear: number;
130134
currentMonth: number;
131-
display: 'week' | 'month';
135+
display: 'week' | 'month' | 'day';
132136
}) => {
133137
setFilters(filters);
134138
setInternalData([]);
135-
window.history.replaceState(
136-
null,
137-
'',
138-
`/launches?${
139-
filters.currentWeek
140-
? `week=${filters.currentWeek}`
141-
: `month=${filters.currentMonth}`
142-
}&year=${filters.currentYear}`
143-
);
139+
140+
const path = [
141+
`day=${filters.currentDay}`,
142+
`week=${filters.currentWeek}`,
143+
`month=${filters.currentMonth}`,
144+
`year=${filters.currentYear}`,
145+
`display=${filters.display}`,
146+
].filter((f) => f);
147+
window.history.replaceState(null, '', `/launches?${path.join('&')}`);
144148
},
145149
[filters, swr.mutate]
146150
);

0 commit comments

Comments
 (0)