feat: month view
This commit is contained in:
parent
aab1ef1c3a
commit
9fb979687d
|
|
@ -48,18 +48,18 @@ export class PostsController {
|
|||
@GetOrgFromRequest() org: Organization,
|
||||
@Query() query: GetPostsDto
|
||||
) {
|
||||
const [posts, comments] = await Promise.all([
|
||||
const [posts] = await Promise.all([
|
||||
this._postsService.getPosts(org.id, query),
|
||||
this._commentsService.getAllCommentsByWeekYear(
|
||||
org.id,
|
||||
query.year,
|
||||
query.week
|
||||
),
|
||||
// this._commentsService.getAllCommentsByWeekYear(
|
||||
// org.id,
|
||||
// query.year,
|
||||
// query.week
|
||||
// ),
|
||||
]);
|
||||
|
||||
return {
|
||||
posts,
|
||||
comments,
|
||||
// comments,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
--color-custom20: #121b2c;
|
||||
--color-custom21: #506490;
|
||||
--color-custom22: #b91c1c;
|
||||
--color-custom23: #06080d;
|
||||
--color-custom23: #000000;
|
||||
--color-custom24: #eaff00;
|
||||
--color-custom25: #2e3336;
|
||||
--color-custom26: #1d9bf0;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import React, {
|
|||
import dayjs from 'dayjs';
|
||||
import { Integrations } from '@gitroom/frontend/components/launches/calendar.context';
|
||||
import clsx from 'clsx';
|
||||
import { commands } from '@uiw/react-md-editor';
|
||||
import { usePreventWindowUnload } from '@gitroom/react/helpers/use.prevent.window.unload';
|
||||
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
|
||||
import { useModals } from '@mantine/modals';
|
||||
|
|
@ -27,7 +26,6 @@ import {
|
|||
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
|
||||
import { useMoveToIntegration } from '@gitroom/frontend/components/launches/helpers/use.move.to.integration';
|
||||
import { useExistingData } from '@gitroom/frontend/components/launches/helpers/use.existing.data';
|
||||
import { newImage } from '@gitroom/frontend/components/launches/helpers/new.image.component';
|
||||
import { MultiMediaComponent } from '@gitroom/frontend/components/media/media.component';
|
||||
import { useExpend } from '@gitroom/frontend/components/launches/helpers/use.expend';
|
||||
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
|
||||
|
|
@ -36,7 +34,6 @@ import { ProvidersOptions } from '@gitroom/frontend/components/launches/provider
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
import useSWR, { useSWRConfig } from 'swr';
|
||||
import { useToaster } from '@gitroom/react/toaster/toaster';
|
||||
import { postSelector } from '@gitroom/frontend/components/post-url-selector/post.url.selector';
|
||||
import { UpDownArrow } from '@gitroom/frontend/components/launches/up.down.arrow';
|
||||
import { DatePicker } from '@gitroom/frontend/components/launches/helpers/date.picker';
|
||||
import { arrayMoveImmutable } from 'array-move';
|
||||
|
|
|
|||
|
|
@ -21,12 +21,23 @@ import { isGeneral } from '@gitroom/react/helpers/is.general';
|
|||
const CalendarContext = createContext({
|
||||
currentWeek: dayjs().week(),
|
||||
currentYear: dayjs().year(),
|
||||
currentMonth: dayjs().month(),
|
||||
comments: [] as Array<{ date: string; total: number }>,
|
||||
integrations: [] as Integrations[],
|
||||
trendings: [] as string[],
|
||||
posts: [] as Array<Post & { integration: Integration }>,
|
||||
setFilters: (filters: { currentWeek: number; currentYear: number }) => {},
|
||||
changeDate: (id: string, date: dayjs.Dayjs) => {},
|
||||
display: 'week',
|
||||
setFilters: (filters: {
|
||||
currentWeek: number;
|
||||
currentYear: number;
|
||||
currentMonth: number;
|
||||
display: 'week' | 'month';
|
||||
}) => {
|
||||
/** empty **/
|
||||
},
|
||||
changeDate: (id: string, date: dayjs.Dayjs) => {
|
||||
/** empty **/
|
||||
},
|
||||
});
|
||||
|
||||
export interface Integrations {
|
||||
|
|
@ -40,28 +51,21 @@ export interface Integrations {
|
|||
}
|
||||
|
||||
function getWeekNumber(date: Date) {
|
||||
// Copy date so don't modify original
|
||||
const targetDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
||||
// Set to nearest Thursday: current date + 4 - current day number
|
||||
// Make Sunday's day number 7
|
||||
targetDate.setUTCDate(targetDate.getUTCDate() + 4 - (targetDate.getUTCDay() || 7));
|
||||
// Get first day of year
|
||||
const yearStart = new Date(Date.UTC(targetDate.getUTCFullYear(), 0, 1));
|
||||
// Calculate full weeks to nearest Thursday
|
||||
return Math.ceil((((targetDate.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
|
||||
}
|
||||
|
||||
function isISOWeek(date: Date, weekNumber: number): boolean {
|
||||
// Copy date so don't modify original
|
||||
const targetDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
||||
// Set to nearest Thursday: current date + 4 - current day number
|
||||
// Make Sunday's day number 7
|
||||
targetDate.setUTCDate(targetDate.getUTCDate() + 4 - (targetDate.getUTCDay() || 7));
|
||||
// Get first day of year
|
||||
const yearStart = new Date(Date.UTC(targetDate.getUTCFullYear(), 0, 1));
|
||||
// Calculate full weeks to nearest Thursday
|
||||
const isoWeekNo = Math.ceil((((targetDate.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
|
||||
return isoWeekNo === weekNumber;
|
||||
// Copy date so don't modify original
|
||||
const targetDate = new Date(
|
||||
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())
|
||||
);
|
||||
// Set to nearest Thursday: current date + 4 - current day number
|
||||
// Make Sunday's day number 7
|
||||
targetDate.setUTCDate(
|
||||
targetDate.getUTCDate() + 4 - (targetDate.getUTCDay() || 7)
|
||||
);
|
||||
// Get first day of year
|
||||
const yearStart = new Date(Date.UTC(targetDate.getUTCFullYear(), 0, 1));
|
||||
// Calculate full weeks to nearest Thursday
|
||||
return Math.ceil(
|
||||
((targetDate.getTime() - yearStart.getTime()) / 86400000 + 1) / 7
|
||||
);
|
||||
}
|
||||
|
||||
export const CalendarWeekProvider: FC<{
|
||||
|
|
@ -84,20 +88,32 @@ export const CalendarWeekProvider: FC<{
|
|||
})();
|
||||
}, []);
|
||||
|
||||
const display = searchParams.get('month') ? 'month' : 'week';
|
||||
const [filters, setFilters] = useState({
|
||||
currentWeek: +(searchParams.get('week') || getWeekNumber(new Date())),
|
||||
currentWeek:
|
||||
display === 'week'
|
||||
? +(searchParams.get('week') || getWeekNumber(new Date()))
|
||||
: 0,
|
||||
currentMonth:
|
||||
display === 'week' ? 0 : +(searchParams.get('month') || dayjs().month()),
|
||||
currentYear: +(searchParams.get('year') || dayjs().year()),
|
||||
display,
|
||||
});
|
||||
|
||||
const isIsoWeek = useMemo(() => {
|
||||
return isISOWeek(new Date(), filters.currentWeek);
|
||||
}, [filters]);
|
||||
|
||||
const setFiltersWrapper = useCallback(
|
||||
(filters: { currentWeek: number; currentYear: number }) => {
|
||||
(filters: {
|
||||
currentWeek: number;
|
||||
currentYear: number;
|
||||
currentMonth: number;
|
||||
display: 'week' | 'month';
|
||||
}) => {
|
||||
setFilters(filters);
|
||||
router.replace(
|
||||
`/launches?week=${filters.currentWeek}&year=${filters.currentYear}`
|
||||
`/launches?${
|
||||
filters.currentWeek
|
||||
? `week=${filters.currentWeek}`
|
||||
: `month=${filters.currentMonth}`
|
||||
}&year=${filters.currentYear}`
|
||||
);
|
||||
setTimeout(() => {
|
||||
mutate('/posts');
|
||||
|
|
@ -107,14 +123,22 @@ export const CalendarWeekProvider: FC<{
|
|||
);
|
||||
|
||||
const params = useMemo(() => {
|
||||
return new URLSearchParams({
|
||||
week: filters.currentWeek.toString(),
|
||||
year: filters.currentYear.toString()
|
||||
}).toString();
|
||||
return new URLSearchParams(
|
||||
filters.currentWeek
|
||||
? {
|
||||
week: filters.currentWeek.toString(),
|
||||
year: filters.currentYear.toString(),
|
||||
}
|
||||
: {
|
||||
year: filters.currentYear.toString(),
|
||||
month: (filters.currentMonth + 1).toString(),
|
||||
}
|
||||
).toString();
|
||||
}, [filters]);
|
||||
|
||||
const loadData = useCallback(
|
||||
async (url: string) => {
|
||||
setInternalData([]);
|
||||
const data = (await fetch(`${url}?${params}`)).json();
|
||||
return data;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import React, { FC, useCallback, useMemo } from 'react';
|
||||
import React, { FC, Fragment, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Integrations,
|
||||
useCalendar,
|
||||
|
|
@ -17,14 +17,12 @@ import { Integration, Post, State } from '@prisma/client';
|
|||
import { useAddProvider } from '@gitroom/frontend/components/launches/add.provider.component';
|
||||
import { CommentComponent } from '@gitroom/frontend/components/launches/comments/comment.component';
|
||||
import { useSWRConfig } from 'swr';
|
||||
import { useIntersectionObserver } from '@uidotdev/usehooks';
|
||||
import { useToaster } from '@gitroom/react/toaster/toaster';
|
||||
import { useUser } from '@gitroom/frontend/components/layout/user.context';
|
||||
import { IntegrationContext } from '@gitroom/frontend/components/launches/helpers/use.integration';
|
||||
import { PreviewPopup } from '@gitroom/frontend/components/marketplace/special.message';
|
||||
|
||||
export const days = [
|
||||
'',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
|
|
@ -33,192 +31,143 @@ export const days = [
|
|||
'Saturday',
|
||||
'Sunday',
|
||||
];
|
||||
export const hours = [
|
||||
'00:00',
|
||||
'01:00',
|
||||
'02:00',
|
||||
'03:00',
|
||||
'04:00',
|
||||
'05:00',
|
||||
'06:00',
|
||||
'07:00',
|
||||
'08:00',
|
||||
'09:00',
|
||||
'10:00',
|
||||
'11:00',
|
||||
'12:00',
|
||||
'13:00',
|
||||
'14:00',
|
||||
'15:00',
|
||||
'16:00',
|
||||
'17:00',
|
||||
'18:00',
|
||||
'19:00',
|
||||
'20:00',
|
||||
'21:00',
|
||||
'22:00',
|
||||
'23:00',
|
||||
];
|
||||
export const hours = Array.from({ length: 24 }, (_, i) => i);
|
||||
|
||||
export const Calendar = () => {
|
||||
const { currentWeek, currentYear, comments } = useCalendar();
|
||||
|
||||
const firstDay = useMemo(() => {
|
||||
return dayjs().year(currentYear).isoWeek(currentWeek).isoWeekday(1);
|
||||
}, [currentYear, currentWeek]);
|
||||
export const WeekView = () => {
|
||||
const { currentYear, currentWeek } = useCalendar();
|
||||
|
||||
return (
|
||||
<DNDProvider>
|
||||
<div className="select-none">
|
||||
<div className="grid grid-cols-8 text-center border-tableBorder border-r">
|
||||
<div className="flex flex-col h-screen overflow-hidden text-textColor flex-1">
|
||||
<div className="flex-1">
|
||||
<div className="grid grid-cols-8 bg-customColor31 gap-[1px] border-customColor31 border rounded-[10px]">
|
||||
<div className="bg-customColor20 sticky top-0 z-10 bg-gray-900"></div>
|
||||
{days.map((day, index) => (
|
||||
<div
|
||||
className="border-tableBorder gap-[4px] border-l border-b h-[36px] border-t flex items-center justify-center bg-input text-[14px] sticky top-0 z-[100]"
|
||||
key={day}
|
||||
className="sticky top-0 z-10 bg-customColor20 p-2 text-center"
|
||||
>
|
||||
<div>{day} </div>
|
||||
<div className="text-[12px]">
|
||||
{day && `(${firstDay.add(index - 1, 'day').format('DD/MM')})`}
|
||||
</div>
|
||||
<div>{day}</div>
|
||||
</div>
|
||||
))}
|
||||
{hours.map((hour) =>
|
||||
days.map((day, index) => (
|
||||
<>
|
||||
{index === 0 ? (
|
||||
<div
|
||||
className="border-tableBorder border-l border-b h-[216px]"
|
||||
key={day + hour}
|
||||
>
|
||||
{['00', '10', '20', '30', '40', '50'].map((num) => (
|
||||
<div
|
||||
key={day + hour + num}
|
||||
className="h-[calc(216px/6)] text-[12px] flex justify-center items-center"
|
||||
>
|
||||
{hour.split(':')[0] + ':' + num}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="group relative border-tableBorder border-l border-b h-[216px] flex flex-col overflow-hidden"
|
||||
key={day + hour}
|
||||
>
|
||||
<CommentBox
|
||||
totalComments={
|
||||
comments.find(
|
||||
(p) =>
|
||||
dayjs
|
||||
.utc(p.date)
|
||||
.local()
|
||||
.format('YYYY-MM-DD HH:mm') ===
|
||||
dayjs()
|
||||
.isoWeek(currentWeek)
|
||||
.isoWeekday(index + 1)
|
||||
.hour(+hour.split(':')[0] - 1)
|
||||
.minute(0)
|
||||
.format('YYYY-MM-DD HH:mm')
|
||||
)?.total || 0
|
||||
}
|
||||
date={dayjs()
|
||||
.isoWeek(currentWeek)
|
||||
.isoWeekday(index + 1)
|
||||
.hour(+hour.split(':')[0] - 1)
|
||||
.minute(0)}
|
||||
{hours.map((hour) => (
|
||||
<Fragment key={hour}>
|
||||
<div className="p-2 pr-4 bg-secondary text-center items-center justify-center flex">
|
||||
{hour.toString().padStart(2, '0')}:00
|
||||
</div>
|
||||
{days.map((day, indexDay) => (
|
||||
<Fragment key={`${day}-${hour}`}>
|
||||
<div className="relative bg-secondary">
|
||||
<CalendarColumn
|
||||
getDate={dayjs()
|
||||
.year(currentYear)
|
||||
.week(currentWeek)
|
||||
.day(indexDay + 1)
|
||||
.hour(hour)
|
||||
.startOf('hour')}
|
||||
/>
|
||||
{['00', '10', '20', '30', '40', '50'].map((num) => (
|
||||
<CalendarColumn
|
||||
key={day + hour + num + currentWeek + currentYear}
|
||||
day={index}
|
||||
hour={hour.split(':')[0] + ':' + num}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
))
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MonthView = () => {
|
||||
const { currentYear, currentMonth } = useCalendar();
|
||||
|
||||
const calendarDays = useMemo(() => {
|
||||
const startOfMonth = dayjs(new Date(currentYear, currentMonth, 1));
|
||||
|
||||
// Calculate the day offset for Monday (isoWeekday() returns 1 for Monday)
|
||||
const startDayOfWeek = startOfMonth.isoWeekday(); // 1 for Monday, 7 for Sunday
|
||||
const daysBeforeMonth = startDayOfWeek - 1; // Days to show from the previous month
|
||||
|
||||
// Get the start date (Monday of the first week that includes this month)
|
||||
const startDate = startOfMonth.subtract(daysBeforeMonth, 'day');
|
||||
|
||||
// Create an array to hold the calendar days (6 weeks * 7 days = 42 days max)
|
||||
const calendarDays = [];
|
||||
let currentDay = startDate;
|
||||
|
||||
for (let i = 0; i < 42; i++) {
|
||||
let label = 'current-month';
|
||||
if (currentDay.month() < currentMonth) label = 'previous-month';
|
||||
if (currentDay.month() > currentMonth) label = 'next-month';
|
||||
|
||||
calendarDays.push({
|
||||
day: currentDay,
|
||||
label,
|
||||
});
|
||||
|
||||
// Move to the next day
|
||||
currentDay = currentDay.add(1, 'day');
|
||||
}
|
||||
|
||||
return calendarDays;
|
||||
}, [currentYear, currentMonth]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-screen overflow-hidden text-textColor flex-1">
|
||||
<div className="flex-1 flex">
|
||||
<div className="grid grid-cols-7 grid-rows-[40px_auto] bg-customColor31 gap-[1px] border-customColor31 border rounded-[10px] flex-1">
|
||||
{days.map((day) => (
|
||||
<div
|
||||
key={day}
|
||||
className="sticky top-0 z-10 bg-customColor20 p-2 text-center"
|
||||
>
|
||||
<div>{day}</div>
|
||||
</div>
|
||||
))}
|
||||
{calendarDays.map((date, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="bg-secondary text-center items-center justify-center flex min-h-[100px]"
|
||||
>
|
||||
<CalendarColumn
|
||||
getDate={dayjs(date.day).endOf('day')}
|
||||
randomHour={true}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Calendar = () => {
|
||||
const { display } = useCalendar();
|
||||
return (
|
||||
<DNDProvider>
|
||||
{display === 'week' ? <WeekView /> : <MonthView />}
|
||||
</DNDProvider>
|
||||
);
|
||||
};
|
||||
|
||||
// export const CalendarColumn: FC<{ day: number; hour: string }> = (props) => {
|
||||
// const { day, hour } = props;
|
||||
// const { currentWeek, currentYear } = useCalendar();
|
||||
//
|
||||
// const getDate = useMemo(() => {
|
||||
// const date =
|
||||
// dayjs()
|
||||
// .year(currentYear)
|
||||
// .isoWeek(currentWeek)
|
||||
// .isoWeekday(day)
|
||||
// .format('YYYY-MM-DD') +
|
||||
// 'T' +
|
||||
// hour +
|
||||
// ':00';
|
||||
// return dayjs(date);
|
||||
// }, [currentWeek]);
|
||||
//
|
||||
// const isBeforeNow = useMemo(() => {
|
||||
// return getDate.isBefore(dayjs());
|
||||
// }, [getDate]);
|
||||
//
|
||||
// const [ref, entry] = useIntersectionObserver({
|
||||
// threshold: 0.5,
|
||||
// root: null,
|
||||
// rootMargin: '0px',
|
||||
// });
|
||||
//
|
||||
// return (
|
||||
// <div className="w-full h-full" ref={ref}>
|
||||
// {!entry?.isIntersecting ? (
|
||||
// <div />
|
||||
// ) : (
|
||||
// <CalendarColumnRender {...props} />
|
||||
// )}
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
export const CalendarColumn: FC<{ day: number; hour: string }> = (props) => {
|
||||
const { day, hour } = props;
|
||||
export const CalendarColumn: FC<{
|
||||
getDate: dayjs.Dayjs;
|
||||
randomHour?: boolean;
|
||||
}> = (props) => {
|
||||
const { getDate, randomHour } = props;
|
||||
const user = useUser();
|
||||
const {
|
||||
currentWeek,
|
||||
currentYear,
|
||||
integrations,
|
||||
posts,
|
||||
trendings,
|
||||
changeDate,
|
||||
} = useCalendar();
|
||||
const { integrations, posts, trendings, changeDate, display } = useCalendar();
|
||||
|
||||
const toaster = useToaster();
|
||||
const modal = useModals();
|
||||
const fetch = useFetch();
|
||||
|
||||
const getDate = useMemo(() => {
|
||||
const date =
|
||||
dayjs()
|
||||
.year(currentYear)
|
||||
.week(currentWeek)
|
||||
.day(day + 1)
|
||||
.format('YYYY-MM-DD') +
|
||||
'T' +
|
||||
hour +
|
||||
':00';
|
||||
|
||||
return dayjs(date);
|
||||
}, [currentWeek]);
|
||||
|
||||
const postList = useMemo(() => {
|
||||
return posts.filter((post) => {
|
||||
return dayjs
|
||||
.utc(post.publishDate)
|
||||
.local()
|
||||
.isBetween(getDate, getDate.add(59, 'minute'), 'minute', '[)');
|
||||
const pList = dayjs.utc(post.publishDate).local();
|
||||
return display === 'week'
|
||||
? pList.isBetween(getDate.startOf('hour'), getDate.endOf('hour'))
|
||||
: pList.format('DD/MM/YYYY') === getDate.format('DD/MM/YYYY');
|
||||
});
|
||||
}, [posts]);
|
||||
}, [posts, display, getDate]);
|
||||
|
||||
const canBeTrending = useMemo(() => {
|
||||
return !!trendings.find((trend) => {
|
||||
|
|
@ -351,7 +300,9 @@ export const CalendarColumn: FC<{ day: number; hour: string }> = (props) => {
|
|||
children: (
|
||||
<AddEditModal
|
||||
integrations={integrations.slice(0).map((p) => ({ ...p }))}
|
||||
date={getDate}
|
||||
date={
|
||||
randomHour ? getDate.hour(Math.floor(Math.random() * 24)) : getDate
|
||||
}
|
||||
reopenModal={() => ({})}
|
||||
/>
|
||||
),
|
||||
|
|
@ -363,70 +314,85 @@ export const CalendarColumn: FC<{ day: number; hour: string }> = (props) => {
|
|||
const addProvider = useAddProvider();
|
||||
|
||||
return (
|
||||
<div className={clsx("relative flex flex-col w-full min-h-full", canDrop && 'bg-white/80')} ref={drop}>
|
||||
<div className="flex flex-col w-full min-h-full">
|
||||
{display === 'month' && (
|
||||
<div className={clsx('pt-[5px]', isBeforeNow && 'bg-customColor23')}>
|
||||
{getDate.date()}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
{...(canBeTrending
|
||||
? {
|
||||
'data-tooltip-id': 'tooltip',
|
||||
'data-tooltip-content': 'Predicted GitHub Trending Change',
|
||||
}
|
||||
: {})}
|
||||
className={clsx(
|
||||
'flex-col flex-1 text-[12px] pointer w-full cursor-pointer overflow-hidden justify-center overflow-x-auto flex scrollbar scrollbar-thumb-tableBorder scrollbar-track-secondary',
|
||||
isBeforeNow && 'bg-customColor23',
|
||||
canBeTrending && 'bg-customColor24'
|
||||
'relative flex flex-col flex-1',
|
||||
canDrop && 'bg-white/80'
|
||||
)}
|
||||
ref={drop}
|
||||
>
|
||||
{postList.map((post) => (
|
||||
<div
|
||||
key={post.id}
|
||||
className={clsx(
|
||||
'text-textColor p-[2.5px] relative flex flex-col justify-center items-center'
|
||||
)}
|
||||
>
|
||||
<div className="relative w-full flex flex-col items-center p-[2.5px]">
|
||||
<CalendarItem
|
||||
date={getDate}
|
||||
state={post.state}
|
||||
editPost={editPost(post)}
|
||||
post={post}
|
||||
integrations={integrations}
|
||||
<div
|
||||
{...(canBeTrending
|
||||
? {
|
||||
'data-tooltip-id': 'tooltip',
|
||||
'data-tooltip-content': 'Predicted GitHub Trending Change',
|
||||
}
|
||||
: {})}
|
||||
className={clsx(
|
||||
'flex-col text-[12px] pointer w-full cursor-pointer overflow-hidden overflow-x-auto flex scrollbar scrollbar-thumb-tableBorder scrollbar-track-secondary',
|
||||
isBeforeNow && 'bg-customColor23 flex-1',
|
||||
canBeTrending && 'bg-customColor24'
|
||||
)}
|
||||
>
|
||||
{postList.map((post) => (
|
||||
<div
|
||||
key={post.id}
|
||||
className={clsx(
|
||||
'text-textColor p-[2.5px] relative flex flex-col justify-center items-center'
|
||||
)}
|
||||
>
|
||||
<div className="relative w-full flex flex-col items-center p-[2.5px]">
|
||||
<CalendarItem
|
||||
isBeforeNow={isBeforeNow}
|
||||
date={getDate}
|
||||
state={post.state}
|
||||
editPost={editPost(post)}
|
||||
post={post}
|
||||
integrations={integrations}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{!isBeforeNow && (
|
||||
<div className="pb-[2.5px] px-[5px] flex-1 flex" onClick={integrations.length ? addModal : addProvider}>
|
||||
<div
|
||||
className={clsx(
|
||||
display === 'month' ? 'flex-1 min-h-[40px]' :
|
||||
!postList.length
|
||||
? 'h-full w-full absolute left-0 top-0 p-[5px]'
|
||||
: 'h-[40px]',
|
||||
'flex items-center justify-center cursor-pointer pb-[2.5px]'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
'hover:before:content-["+"] w-full h-full text-seventh rounded-[10px] hover:border hover:border-seventh flex justify-center items-center'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
</div>
|
||||
{!isBeforeNow && (
|
||||
<div className="pb-[2.5px] px-[5px]">
|
||||
<div
|
||||
className={clsx(
|
||||
!postList.length
|
||||
? 'h-full w-full absolute left-0 top-0 p-[5px]'
|
||||
: 'h-[40px]',
|
||||
'flex items-center justify-center cursor-pointer pb-[2.5px]'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
onClick={integrations.length ? addModal : addProvider}
|
||||
className={clsx(
|
||||
'hover:before:content-["+"] w-full h-full text-seventh rounded-[10px] hover:border hover:border-seventh flex justify-center items-center'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CalendarItem: FC<{
|
||||
date: dayjs.Dayjs;
|
||||
isBeforeNow: boolean;
|
||||
editPost: () => void;
|
||||
integrations: Integrations[];
|
||||
state: State;
|
||||
post: Post & { integration: Integration };
|
||||
}> = (props) => {
|
||||
const { editPost, post, date, integrations, state } = props;
|
||||
const { editPost, post, date, isBeforeNow, state } = props;
|
||||
const [{ opacity }, dragRef] = useDrag(
|
||||
() => ({
|
||||
type: 'post',
|
||||
|
|
@ -444,7 +410,7 @@ const CalendarItem: FC<{
|
|||
className={clsx(
|
||||
'gap-[5px] w-full flex h-full flex-1 rounded-[10px] border border-seventh px-[5px] p-[2.5px]',
|
||||
'relative',
|
||||
state === 'DRAFT' && '!grayscale'
|
||||
(state === 'DRAFT' || isBeforeNow) && '!grayscale'
|
||||
)}
|
||||
style={{ opacity }}
|
||||
>
|
||||
|
|
@ -458,7 +424,10 @@ const CalendarItem: FC<{
|
|||
src={`/icons/platforms/${post.integration?.providerIdentifier}.png`}
|
||||
/>
|
||||
</div>
|
||||
<div className="whitespace-pre-wrap line-clamp-3">{post.content}</div>
|
||||
<div className="whitespace-pre-wrap line-clamp-3">
|
||||
{state === 'DRAFT' ? 'Draft: ' : ''}
|
||||
{post.content}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,32 +1,109 @@
|
|||
'use client';
|
||||
import { useCalendar } from '@gitroom/frontend/components/launches/calendar.context';
|
||||
import clsx from 'clsx';
|
||||
import dayjs from 'dayjs';
|
||||
import {useCallback} from "react";
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const Filters = () => {
|
||||
const week = useCalendar();
|
||||
const betweenDates =
|
||||
dayjs().year(week.currentYear).isoWeek(week.currentWeek).startOf('isoWeek').format('DD/MM/YYYY') +
|
||||
' - ' +
|
||||
dayjs().year(week.currentYear).isoWeek(week.currentWeek).endOf('isoWeek').format('DD/MM/YYYY');
|
||||
week.display === 'week'
|
||||
? dayjs()
|
||||
.year(week.currentYear)
|
||||
.isoWeek(week.currentWeek)
|
||||
.startOf('isoWeek')
|
||||
.format('DD/MM/YYYY') +
|
||||
' - ' +
|
||||
dayjs()
|
||||
.year(week.currentYear)
|
||||
.isoWeek(week.currentWeek)
|
||||
.endOf('isoWeek')
|
||||
.format('DD/MM/YYYY')
|
||||
: dayjs()
|
||||
.year(week.currentYear)
|
||||
.month(week.currentMonth)
|
||||
.startOf('month')
|
||||
.format('DD/MM/YYYY') +
|
||||
' - ' +
|
||||
dayjs()
|
||||
.year(week.currentYear)
|
||||
.month(week.currentMonth)
|
||||
.endOf('month')
|
||||
.format('DD/MM/YYYY');
|
||||
|
||||
const nextWeek = useCallback(() => {
|
||||
week.setFilters({
|
||||
currentWeek: week.currentWeek === 52 ? 1 : week.currentWeek + 1,
|
||||
currentYear: week.currentWeek === 52 ? week.currentYear + 1 : week.currentYear,
|
||||
});
|
||||
}, [week.currentWeek, week.currentYear]);
|
||||
const setWeek = useCallback(() => {
|
||||
week.setFilters({
|
||||
currentWeek: dayjs().isoWeek(),
|
||||
currentYear: dayjs().year(),
|
||||
currentMonth: 0,
|
||||
display: 'week',
|
||||
});
|
||||
}, [week]);
|
||||
|
||||
const previousWeek = useCallback(() => {
|
||||
week.setFilters({
|
||||
currentWeek: week.currentWeek === 1 ? 52 : week.currentWeek - 1,
|
||||
currentYear: week.currentWeek === 1 ? week.currentYear - 1 : week.currentYear,
|
||||
});
|
||||
}, [week.currentWeek, week.currentYear]);
|
||||
const setMonth = useCallback(() => {
|
||||
week.setFilters({
|
||||
currentMonth: dayjs().month(),
|
||||
currentWeek: 0,
|
||||
currentYear: dayjs().year(),
|
||||
display: 'month',
|
||||
});
|
||||
}, [week]);
|
||||
|
||||
const next = useCallback(() => {
|
||||
week.setFilters({
|
||||
currentWeek:
|
||||
week.display === 'week'
|
||||
? week.currentWeek === 52
|
||||
? 1
|
||||
: week.currentWeek + 1
|
||||
: 0,
|
||||
currentYear:
|
||||
week.display === 'week'
|
||||
? week.currentWeek === 52
|
||||
? week.currentYear + 1
|
||||
: week.currentYear
|
||||
: week.currentMonth === 11
|
||||
? week.currentYear + 1
|
||||
: week.currentYear,
|
||||
display: week.display as any,
|
||||
currentMonth:
|
||||
week.display === 'week'
|
||||
? 0
|
||||
: week.currentMonth === 11
|
||||
? 0
|
||||
: week.currentMonth + 1,
|
||||
});
|
||||
}, [week.display, week.currentMonth, week.currentWeek, week.currentYear]);
|
||||
|
||||
const previous = useCallback(() => {
|
||||
week.setFilters({
|
||||
currentWeek:
|
||||
week.display === 'week'
|
||||
? week.currentWeek === 1
|
||||
? 52
|
||||
: week.currentWeek - 1
|
||||
: 0,
|
||||
currentYear:
|
||||
week.display === 'week'
|
||||
? week.currentWeek === 1
|
||||
? week.currentYear - 1
|
||||
: week.currentYear
|
||||
: week.currentMonth === 0
|
||||
? week.currentYear - 1
|
||||
: week.currentYear,
|
||||
display: week.display as any,
|
||||
currentMonth:
|
||||
week.display === 'week'
|
||||
? 0
|
||||
: week.currentMonth === 0
|
||||
? 11
|
||||
: week.currentMonth - 1,
|
||||
});
|
||||
}, [week.display, week.currentMonth, week.currentWeek, week.currentYear]);
|
||||
|
||||
return (
|
||||
<div className="h-[20px] text-textColor flex gap-[8px] items-center select-none">
|
||||
<div onClick={previousWeek}>
|
||||
<div className="text-textColor flex gap-[8px] items-center select-none">
|
||||
<div onClick={previous}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
|
|
@ -40,8 +117,12 @@ export const Filters = () => {
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>Week {week.currentWeek}</div>
|
||||
<div onClick={nextWeek}>
|
||||
<div className="w-[80px] text-center">
|
||||
{week.display === 'week'
|
||||
? `Week ${week.currentWeek}`
|
||||
: `${dayjs().month(week.currentMonth).format('MMMM')}`}
|
||||
</div>
|
||||
<div onClick={next}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
|
|
@ -55,7 +136,25 @@ export const Filters = () => {
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>{betweenDates}</div>
|
||||
<div className="flex-1">{betweenDates}</div>
|
||||
<div
|
||||
className={clsx(
|
||||
'border border-tableBorder p-[10px]',
|
||||
week.display === 'week' && 'bg-tableBorder'
|
||||
)}
|
||||
onClick={setWeek}
|
||||
>
|
||||
Week
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
'border border-tableBorder p-[10px]',
|
||||
week.display === 'month' && 'bg-tableBorder'
|
||||
)}
|
||||
onClick={setMonth}
|
||||
>
|
||||
Month
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,13 +13,12 @@ import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
|
|||
import clsx from 'clsx';
|
||||
import { useUser } from '../layout/user.context';
|
||||
import { Menu } from '@gitroom/frontend/components/launches/menu/menu';
|
||||
import { GeneratorComponent } from '@gitroom/frontend/components/launches/generator/generator';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { Integration } from '@prisma/client';
|
||||
import ImageWithFallback from '@gitroom/react/helpers/image.with.fallback';
|
||||
import { useToaster } from '@gitroom/react/toaster/toaster';
|
||||
import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events';
|
||||
import { NewCalendarComponent } from '@gitroom/frontend/components/launches/new.calendar.component';
|
||||
import { Calendar } from './calendar';
|
||||
|
||||
export const LaunchesComponent = () => {
|
||||
const fetch = useFetch();
|
||||
|
|
@ -117,7 +116,7 @@ export const LaunchesComponent = () => {
|
|||
<CalendarWeekProvider integrations={sortedIntegrations}>
|
||||
<div className="flex flex-1 flex-col">
|
||||
<div className="flex flex-1 relative">
|
||||
<div className="outline-none absolute w-full h-full grid grid-cols-[220px_minmax(0,1fr)] gap-[30px] overflow-hidden overflow-y-auto scrollbar scrollbar-thumb-tableBorder scrollbar-track-secondary">
|
||||
<div className="outline-none w-full h-full grid grid-cols-[220px_minmax(0,1fr)] gap-[30px] scrollbar scrollbar-thumb-tableBorder scrollbar-track-secondary">
|
||||
<div className="w-[220px] bg-third p-[16px] flex flex-col gap-[24px] min-h-[100%]">
|
||||
<h2 className="text-[20px]">Channels</h2>
|
||||
<div className="gap-[16px] flex flex-col">
|
||||
|
|
@ -213,8 +212,7 @@ export const LaunchesComponent = () => {
|
|||
</div>
|
||||
<div className="flex-1 flex flex-col gap-[14px]">
|
||||
<Filters />
|
||||
<NewCalendarComponent />
|
||||
{/*<Calendar />*/}
|
||||
<Calendar />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
'use client';
|
||||
import { Fragment } from 'react';
|
||||
import { CalendarColumn } from '@gitroom/frontend/components/launches/calendar';
|
||||
import { DNDProvider } from '@gitroom/frontend/components/launches/helpers/dnd.provider';
|
||||
|
||||
export const days = [
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
'Sunday',
|
||||
];
|
||||
export const hours = Array.from({ length: 24 }, (_, i) => i);
|
||||
|
||||
export const NewCalendarComponent = () => {
|
||||
return (
|
||||
<DNDProvider>
|
||||
<div className="flex flex-col h-screen overflow-hidden text-textColor flex-1">
|
||||
<div className="flex-1">
|
||||
<div className="grid grid-cols-8 bg-customColor31 gap-[1px] border-customColor31 border rounded-[10px]">
|
||||
<div className="bg-customColor20 sticky top-0 z-10 bg-gray-900"></div>
|
||||
{days.map((day, index) => (
|
||||
<div
|
||||
key={day}
|
||||
className="sticky top-0 z-10 bg-customColor20 p-2 text-center"
|
||||
>
|
||||
<div>{day}</div>
|
||||
</div>
|
||||
))}
|
||||
{hours.map((hour) => (
|
||||
<Fragment key={hour}>
|
||||
<div className="p-2 pr-4 bg-secondary text-center items-center justify-center flex">
|
||||
{hour.toString().padStart(2, '0')}:00
|
||||
</div>
|
||||
{days.map((day, indexDay) => (
|
||||
<Fragment key={`${day}-${hour}`}>
|
||||
<div className="relative bg-secondary">
|
||||
<CalendarColumn
|
||||
day={indexDay}
|
||||
hour={`${hour.toString().padStart(2, '0')}:00`}
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
))}
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DNDProvider>
|
||||
);
|
||||
};
|
||||
|
|
@ -64,10 +64,10 @@ export class PostsRepository {
|
|||
|
||||
getPosts(orgId: string, query: GetPostsDto) {
|
||||
const dateYear = dayjs().year(query.year);
|
||||
const date = dateYear.isoWeek(query.week);
|
||||
const date = query.week ? dateYear.isoWeek(query.week) : dateYear.month(query.month-1);
|
||||
|
||||
const startDate = date.startOf('isoWeek').subtract(2, 'days').toDate();
|
||||
const endDate = date.endOf('isoWeek').add(2, 'days').toDate();
|
||||
const startDate = (query.week ? date.startOf('isoWeek') : date.startOf('month')).subtract(2, 'days').toDate();
|
||||
const endDate = (query.week ? date.endOf('isoWeek') : date.endOf('month')).add(2, 'days').toDate();
|
||||
|
||||
return this._post.model.post.findMany({
|
||||
where: {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,22 @@
|
|||
import { Type } from 'class-transformer';
|
||||
import { IsIn, IsNumber, IsString, Max, Min } from 'class-validator';
|
||||
import { IsIn, IsNumber, IsString, Max, Min, ValidateIf } from 'class-validator';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export class GetPostsDto {
|
||||
@ValidateIf((o) => !o.month)
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Max(52)
|
||||
@Min(1)
|
||||
week: number;
|
||||
|
||||
@ValidateIf((o) => !o.week)
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Max(52)
|
||||
@Min(1)
|
||||
month: number;
|
||||
|
||||
@Type(() => Number)
|
||||
@IsNumber()
|
||||
@Max(dayjs().add(10, 'year').year())
|
||||
|
|
|
|||
Loading…
Reference in New Issue