From ed8bb97188c6cb82b108f26961428974f6fdabdf Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 27 Jul 2025 13:08:30 +0700 Subject: [PATCH] feat: simplified the between dates get request, fixed bug with fetching all the posts --- .../components/launches/calendar.context.tsx | 123 +++--- .../src/components/launches/calendar.tsx | 56 +-- .../src/components/launches/filters.tsx | 351 +++++++++--------- .../database/prisma/posts/posts.repository.ts | 29 +- .../src/dtos/posts/get.posts.dto.ts | 31 +- 5 files changed, 285 insertions(+), 305 deletions(-) diff --git a/apps/frontend/src/components/launches/calendar.context.tsx b/apps/frontend/src/components/launches/calendar.context.tsx index 648e318b..3272fd0c 100644 --- a/apps/frontend/src/components/launches/calendar.context.tsx +++ b/apps/frontend/src/components/launches/calendar.context.tsx @@ -22,11 +22,10 @@ import { extend } from 'dayjs'; import useCookie from 'react-use-cookie'; extend(isoWeek); extend(weekOfYear); + export const CalendarContext = createContext({ - currentDay: dayjs().day() as 0 | 1 | 2 | 3 | 4 | 5 | 6, - currentWeek: dayjs().week(), - currentYear: dayjs().year(), - currentMonth: dayjs().month(), + startDate: dayjs().startOf('isoWeek').format('YYYY-MM-DD'), + endDate: dayjs().endOf('isoWeek').format('YYYY-MM-DD'), customer: null as string | null, sets: [] as { name: string; id: string; content: string[] }[], signature: undefined as any, @@ -51,10 +50,8 @@ export const CalendarContext = createContext({ }, display: 'week', setFilters: (filters: { - currentWeek: number; - currentYear: number; - currentDay: 0 | 1 | 2 | 3 | 4 | 5 | 6; - currentMonth: number; + startDate: string; + endDate: string; display: 'week' | 'month' | 'day'; customer: string | null; }) => { @@ -64,6 +61,7 @@ export const CalendarContext = createContext({ /** empty **/ }, }); + export interface Integrations { name: string; id: string; @@ -85,23 +83,35 @@ export interface Integrations { id?: string; }; } -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 - ); + +// Helper function to get start and end dates based on display type +function getDateRange(display: string, referenceDate?: string) { + const date = referenceDate ? dayjs(referenceDate) : dayjs(); + + switch (display) { + case 'day': + return { + startDate: date.format('YYYY-MM-DD'), + endDate: date.format('YYYY-MM-DD'), + }; + case 'week': + return { + startDate: date.startOf('isoWeek').format('YYYY-MM-DD'), + endDate: date.endOf('isoWeek').format('YYYY-MM-DD'), + }; + case 'month': + return { + startDate: date.startOf('month').format('YYYY-MM-DD'), + endDate: date.endOf('month').format('YYYY-MM-DD'), + }; + default: + return { + startDate: date.startOf('isoWeek').format('YYYY-MM-DD'), + endDate: date.endOf('isoWeek').format('YYYY-MM-DD'), + }; + } } + export const CalendarWeekProvider: FC<{ children: ReactNode; integrations: Integrations[]; @@ -112,35 +122,45 @@ export const CalendarWeekProvider: FC<{ const searchParams = useSearchParams(); const [displaySaved, setDisplaySaved] = useCookie('calendar-display', 'week'); const display = searchParams.get('display') || displaySaved; + + // Initialize with current date range based on URL params or defaults + const initStartDate = searchParams.get('startDate'); + const initEndDate = searchParams.get('endDate'); + const initCustomer = searchParams.get('customer'); + + const initialRange = + initStartDate && initEndDate + ? { startDate: initStartDate, endDate: initEndDate } + : getDateRange(display); + const [filters, setFilters] = useState({ - currentDay: +(searchParams.get('day') || dayjs().day()) as - | 0 - | 1 - | 2 - | 3 - | 4 - | 5 - | 6, - currentWeek: +(searchParams.get('week') || getWeekNumber(new Date())), - currentMonth: +(searchParams.get('month') || dayjs().month()), - currentYear: +(searchParams.get('year') || dayjs().year()), - customer: (searchParams.get('customer') as string) || null, + startDate: initialRange.startDate, + endDate: initialRange.endDate, + customer: initCustomer || null, display, }); + const params = useMemo(() => { return new URLSearchParams({ display: filters.display, - day: filters.currentDay.toString(), - week: filters.currentWeek.toString(), - month: (filters.currentMonth + 1).toString(), - year: filters.currentYear.toString(), + startDate: filters.startDate, + endDate: filters.endDate, customer: filters?.customer?.toString() || '', }).toString(); - }, [filters, display]); + }, [filters]); + const loadData = useCallback(async () => { - const data = (await fetch(`/posts?${params}`)).json(); + const modifiedParams = new URLSearchParams({ + display: filters.display, + customer: filters?.customer?.toString() || '', + startDate: dayjs(filters.startDate).startOf('day').utc().format(), + endDate: dayjs(filters.endDate).endOf('day').utc().format(), + }).toString(); + + const data = (await fetch(`/posts?${modifiedParams}`)).json(); return data; }, [filters, params]); + const swr = useSWR(`/posts-${params}`, loadData, { refreshInterval: 3600000, refreshWhenOffline: false, @@ -157,14 +177,12 @@ export const CalendarWeekProvider: FC<{ }, []); const { data: sets, mutate } = useSWR('sets', setList); - const { data: sign} = useSWR('default-sign', defaultSign); + const { data: sign } = useSWR('default-sign', defaultSign); const setFiltersWrapper = useCallback( (filters: { - currentDay: 0 | 1 | 2 | 3 | 4 | 5 | 6; - currentWeek: number; - currentYear: number; - currentMonth: number; + startDate: string; + endDate: string; display: 'week' | 'month' | 'day'; customer: string | null; }) => { @@ -172,10 +190,8 @@ export const CalendarWeekProvider: FC<{ setFilters(filters); setInternalData([]); const path = [ - `day=${filters.currentDay}`, - `week=${filters.currentWeek}`, - `month=${filters.currentMonth}`, - `year=${filters.currentYear}`, + `startDate=${filters.startDate}`, + `endDate=${filters.endDate}`, `display=${filters.display}`, filters.customer ? `customer=${filters.customer}` : ``, ].filter((f) => f); @@ -183,11 +199,13 @@ export const CalendarWeekProvider: FC<{ }, [filters, swr.mutate] ); + const { isLoading } = swr; const { posts, comments } = swr?.data || { posts: [], comments: [], }; + const changeDate = useCallback( (id: string, date: dayjs.Dayjs) => { setInternalData((d) => @@ -204,11 +222,13 @@ export const CalendarWeekProvider: FC<{ }, [posts, internalData] ); + useEffect(() => { if (posts) { setInternalData(posts); } }, [posts]); + return ( ); }; + export const useCalendar = () => useContext(CalendarContext); diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 97266c6c..fb96b570 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -102,13 +102,14 @@ export const hours = Array.from( ); export const DayView = () => { const calendar = useCalendar(); - const { integrations, posts, currentYear, currentDay, currentWeek } = - calendar; + const { integrations, posts, startDate } = calendar; // Set dayjs locale based on current language const currentLanguage = i18next.resolvedLanguage || 'en'; dayjs.locale(currentLanguage); + const currentDay = dayjs(startDate); + const options = useMemo(() => { const createdPosts = posts.map((post) => ({ integration: [integrations.find((i) => i.id === post.integration.id)!], @@ -142,6 +143,7 @@ export const DayView = () => { (p) => p[0].time ); }, [integrations, posts]); + return (
{options.map((option) => ( @@ -165,11 +167,7 @@ export const DayView = () => { }} > { ); }; export const WeekView = () => { - const { currentYear, currentWeek } = useCalendar(); + const { startDate, endDate } = useCalendar(); const t = useT(); // Use dayjs to get localized day names @@ -191,16 +189,17 @@ export const WeekView = () => { dayjs.locale(currentLanguage); const days = []; - const yearWeek = dayjs() - .year(currentYear) - .week(currentWeek) - .startOf('week'); - for (let i = 1; i <= 7; i++) { - const yearWeekFormat = yearWeek.add(i, 'day').format('L'); - days.push({ name: dayjs().day(i).format('dddd'), day: yearWeekFormat }); + const weekStart = dayjs(startDate); + for (let i = 0; i < 7; i++) { + const day = weekStart.add(i, 'day'); + days.push({ + name: day.format('dddd'), + day: day.format('L'), + date: day, + }); } return days; - }, [i18next.resolvedLanguage, currentYear, currentWeek]); + }, [i18next.resolvedLanguage, startDate]); return (
@@ -233,16 +232,13 @@ export const WeekView = () => {
{convertTimeFormatBasedOnLocality(hour)}
- {days.map((day, indexDay) => ( - + {localizedDays.map((day, indexDay) => ( +
@@ -255,7 +251,7 @@ export const WeekView = () => { ); }; export const MonthView = () => { - const { currentYear, currentMonth } = useCalendar(); + const { startDate } = useCalendar(); const t = useT(); // Use dayjs to get localized day names @@ -272,6 +268,10 @@ export const MonthView = () => { }, [i18next.resolvedLanguage]); const calendarDays = useMemo(() => { + const monthStart = dayjs(startDate); + const currentMonth = monthStart.month(); + const currentYear = monthStart.year(); + const startOfMonth = dayjs(new Date(currentYear, currentMonth, 1)); // Calculate the day offset for Monday (isoWeekday() returns 1 for Monday) @@ -279,11 +279,11 @@ export const MonthView = () => { 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'); + const calendarStartDate = startOfMonth.subtract(daysBeforeMonth, 'day'); // Create an array to hold the calendar days (6 weeks * 7 days = 42 days max) const calendarDays = []; - let currentDay = startDate; + let currentDay = calendarStartDate; for (let i = 0; i < 42; i++) { let label = 'current-month'; if (currentDay.month() < currentMonth) label = 'previous-month'; @@ -297,7 +297,7 @@ export const MonthView = () => { currentDay = currentDay.add(1, 'day'); } return calendarDays; - }, [currentYear, currentMonth]); + }, [startDate]); return (
diff --git a/apps/frontend/src/components/launches/filters.tsx b/apps/frontend/src/components/launches/filters.tsx index e69bea58..048e66de 100644 --- a/apps/frontend/src/components/launches/filters.tsx +++ b/apps/frontend/src/components/launches/filters.tsx @@ -8,191 +8,207 @@ import { SelectCustomer } from '@gitroom/frontend/components/launches/select.cus import { useT } from '@gitroom/react/translation/get.transation.service.client'; import i18next from 'i18next'; +// Helper function to get start and end dates based on display type +function getDateRange( + display: 'day' | 'week' | 'month', + referenceDate?: string +) { + const date = referenceDate ? dayjs(referenceDate) : dayjs(); + + switch (display) { + case 'day': + return { + startDate: date.format('YYYY-MM-DD'), + endDate: date.format('YYYY-MM-DD'), + }; + case 'week': + return { + startDate: date.startOf('isoWeek').format('YYYY-MM-DD'), + endDate: date.endOf('isoWeek').format('YYYY-MM-DD'), + }; + case 'month': + return { + startDate: date.startOf('month').format('YYYY-MM-DD'), + endDate: date.endOf('month').format('YYYY-MM-DD'), + }; + } +} + export const Filters = () => { - const week = useCalendar(); + const calendar = useCalendar(); const t = useT(); // Set dayjs locale based on current language const currentLanguage = i18next.resolvedLanguage || 'en'; dayjs.locale(); - const betweenDates = - week.display === 'day' - ? dayjs() - .year(week.currentYear) - .isoWeek(week.currentWeek) - .day(week.currentDay) - .format('L') - : week.display === 'week' - ? dayjs() - .year(week.currentYear) - .isoWeek(week.currentWeek) - .startOf('isoWeek') - .format('L') + - ' - ' + - dayjs() - .year(week.currentYear) - .isoWeek(week.currentWeek) - .endOf('isoWeek') - .format('L') - : dayjs() - .year(week.currentYear) - .month(week.currentMonth) - .startOf('month') - .format('L') + - ' - ' + - dayjs() - .year(week.currentYear) - .month(week.currentMonth) - .endOf('month') - .format('L'); - const setDay = useCallback(() => { + // Calculate display date range text + const getDisplayText = () => { + const startDate = dayjs(calendar.startDate); + const endDate = dayjs(calendar.endDate); + + switch (calendar.display) { + case 'day': + return startDate.format('dddd (L)'); + case 'week': + return `${startDate.format('L')} - ${endDate.format('L')}`; + case 'month': + return startDate.format('MMMM YYYY'); + default: + return ''; + } + }; + + const setToday = useCallback(() => { + const today = dayjs(); + const currentRange = getDateRange( + calendar.display as 'day' | 'week' | 'month' + ); + + // Check if we're already showing today's range if ( - week.display === 'day' && - week.currentDay === +dayjs().day() && - week.currentWeek === dayjs().isoWeek() && - week.currentYear === dayjs().year() && - week.currentMonth === dayjs().month() + calendar.startDate === currentRange.startDate && + calendar.endDate === currentRange.endDate ) { - return; // No need to set the same day + return; // No need to set the same range } - week.setFilters({ - currentDay: +dayjs().day() as 0 | 1 | 2 | 3 | 4 | 5 | 6, - currentWeek: dayjs().isoWeek(), - currentYear: dayjs().year(), - currentMonth: dayjs().month(), + calendar.setFilters({ + startDate: currentRange.startDate, + endDate: currentRange.endDate, + display: calendar.display as 'day' | 'week' | 'month', + customer: calendar.customer, + }); + }, [calendar]); + + const setDay = useCallback(() => { + // If already in day view and showing today, don't change + if (calendar.display === 'day') { + const todayRange = getDateRange('day'); + if (calendar.startDate === todayRange.startDate) { + return; + } + } + + const range = getDateRange('day'); + calendar.setFilters({ + startDate: range.startDate, + endDate: range.endDate, display: 'day', - customer: week.customer, + customer: calendar.customer, }); - }, [week]); + }, [calendar]); + const setWeek = useCallback(() => { - if ( - week.display === 'week' && - week.currentWeek === dayjs().isoWeek() && - week.currentYear === dayjs().year() && - week.currentMonth === dayjs().month() - ) { - return; // No need to set the same week + // If already in week view and showing current week, don't change + if (calendar.display === 'week') { + const currentWeekRange = getDateRange('week'); + if (calendar.startDate === currentWeekRange.startDate) { + return; + } } - week.setFilters({ - currentDay: +dayjs().day() as 0 | 1 | 2 | 3 | 4 | 5 | 6, - currentWeek: dayjs().isoWeek(), - currentYear: dayjs().year(), - currentMonth: dayjs().month(), + + const range = getDateRange('week'); + calendar.setFilters({ + startDate: range.startDate, + endDate: range.endDate, display: 'week', - customer: week.customer, + customer: calendar.customer, }); - }, [week]); + }, [calendar]); + const setMonth = useCallback(() => { - if ( - week.display === 'month' && - week.currentMonth === dayjs().month() && - week.currentYear === dayjs().year() - ) { - return; // No need to set the same month + // If already in month view and showing current month, don't change + if (calendar.display === 'month') { + const currentMonthRange = getDateRange('month'); + if (calendar.startDate === currentMonthRange.startDate) { + return; + } } - week.setFilters({ - currentDay: +dayjs().day() as 0 | 1 | 2 | 3 | 4 | 5 | 6, - currentMonth: dayjs().month(), - currentWeek: dayjs().isoWeek(), - currentYear: dayjs().year(), + + const range = getDateRange('month'); + calendar.setFilters({ + startDate: range.startDate, + endDate: range.endDate, display: 'month', - customer: week.customer, + customer: calendar.customer, }); - }, [week]); + }, [calendar]); + const setCustomer = useCallback( (customer: string) => { - if (week.customer === customer) { + if (calendar.customer === customer) { return; // No need to set the same customer } - week.setFilters({ - currentDay: week.currentDay, - currentMonth: week.currentMonth, - currentWeek: week.currentWeek, - currentYear: week.currentYear, - display: week.display as any, + calendar.setFilters({ + startDate: calendar.startDate, + endDate: calendar.endDate, + display: calendar.display as 'day' | 'week' | 'month', customer: customer, }); }, - [week] + [calendar] ); + const next = useCallback(() => { - const increaseDay = week.display === 'day'; - const increaseWeek = - week.display === 'week' || - (week.display === 'day' && week.currentDay === 6); - const increaseMonth = - week.display === 'month' || (increaseWeek && week.currentWeek === 52); - week.setFilters({ - customer: week.customer, - currentDay: (!increaseDay - ? 0 - : week.currentDay === 6 - ? 0 - : week.currentDay + 1) as 0 | 1 | 2 | 3 | 4 | 5 | 6, - currentWeek: !increaseWeek - ? week.currentWeek - : week.currentWeek === 52 - ? 1 - : week.currentWeek + 1, - currentYear: !increaseMonth - ? week.currentYear - : week.currentMonth === 11 - ? week.currentYear + 1 - : week.currentYear, - display: week.display as any, - currentMonth: !increaseMonth - ? week.currentMonth - : week.currentMonth === 11 - ? 0 - : week.currentMonth + 1, + const currentStart = dayjs(calendar.startDate); + let nextStart: dayjs.Dayjs; + + switch (calendar.display) { + case 'day': + nextStart = currentStart.add(1, 'day'); + break; + case 'week': + nextStart = currentStart.add(1, 'week'); + break; + case 'month': + nextStart = currentStart.add(1, 'month'); + break; + default: + nextStart = currentStart.add(1, 'week'); + } + + const range = getDateRange( + calendar.display as 'day' | 'week' | 'month', + nextStart.format('YYYY-MM-DD') + ); + calendar.setFilters({ + startDate: range.startDate, + endDate: range.endDate, + display: calendar.display as 'day' | 'week' | 'month', + customer: calendar.customer, }); - }, [ - week.display, - week.currentMonth, - week.currentWeek, - week.currentYear, - week.currentDay, - ]); + }, [calendar]); + const previous = useCallback(() => { - const decreaseDay = week.display === 'day'; - const decreaseWeek = - week.display === 'week' || - (week.display === 'day' && week.currentDay === 0); - const decreaseMonth = - week.display === 'month' || (decreaseWeek && week.currentWeek === 1); - week.setFilters({ - customer: week.customer, - currentDay: (!decreaseDay - ? 0 - : week.currentDay === 0 - ? 6 - : week.currentDay - 1) as 0 | 1 | 2 | 3 | 4 | 5 | 6, - currentWeek: !decreaseWeek - ? week.currentWeek - : week.currentWeek === 1 - ? 52 - : week.currentWeek - 1, - currentYear: !decreaseMonth - ? week.currentYear - : week.currentMonth === 0 - ? week.currentYear - 1 - : week.currentYear, - display: week.display as any, - currentMonth: !decreaseMonth - ? week.currentMonth - : week.currentMonth === 0 - ? 11 - : week.currentMonth - 1, + const currentStart = dayjs(calendar.startDate); + let prevStart: dayjs.Dayjs; + + switch (calendar.display) { + case 'day': + prevStart = currentStart.subtract(1, 'day'); + break; + case 'week': + prevStart = currentStart.subtract(1, 'week'); + break; + case 'month': + prevStart = currentStart.subtract(1, 'month'); + break; + default: + prevStart = currentStart.subtract(1, 'week'); + } + + const range = getDateRange( + calendar.display as 'day' | 'week' | 'month', + prevStart.format('YYYY-MM-DD') + ); + calendar.setFilters({ + startDate: range.startDate, + endDate: range.endDate, + display: calendar.display as 'day' | 'week' | 'month', + customer: calendar.customer, }); - }, [ - week.display, - week.currentMonth, - week.currentWeek, - week.currentYear, - week.currentDay, - ]); + }, [calendar]); const setCurrent = useCallback( (type: 'day' | 'week' | 'month') => () => { @@ -204,14 +220,9 @@ export const Filters = () => { setMonth(); } }, - [ - week.display, - week.currentMonth, - week.currentWeek, - week.currentYear, - week.currentDay, - ] + [setDay, setWeek, setMonth] ); + return (
@@ -238,15 +249,7 @@ export const Filters = () => {
- {week.display === 'day' - ? `${dayjs() - .month(week.currentMonth) - .week(week.currentWeek) - .day(week.currentDay) - .format('dddd (L)')}` - : week.display === 'week' - ? betweenDates - : dayjs().month(week.currentMonth).format('MMMM YYYY')} + {getDisplayText()}
{
Today @@ -282,15 +285,15 @@ export const Filters = () => {
setCustomer(customer)} - integrations={week.integrations} + integrations={calendar.integrations} />
@@ -299,7 +302,7 @@ export const Filters = () => {
@@ -308,7 +311,7 @@ export const Filters = () => {
diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index d6e1a274..8e18f9e9 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -86,32 +86,9 @@ export class PostsRepository { } async getPosts(orgId: string, query: GetPostsDto) { - const dateYear = dayjs().year(query.year); - const date = - query.display === 'day' - ? dateYear.isoWeek(query.week).day(query.day) - : query.display === 'week' - ? dateYear.isoWeek(query.week) - : dateYear.month(query.month - 1); - - const startDate = ( - query.display === 'day' - ? date.startOf('day') - : query.display === 'week' - ? date.startOf('isoWeek') - : date.startOf('month') - ) - .subtract(2, 'hours') - .toDate(); - const endDate = ( - query.display === 'day' - ? date.endOf('day') - : query.display === 'week' - ? date.endOf('isoWeek') - : date.endOf('month') - ) - .add(2, 'hours') - .toDate(); + // Use the provided start and end dates directly + const startDate = dayjs.utc(query.startDate).toDate(); + const endDate = dayjs.utc(query.endDate).toDate(); const list = await this._post.model.post.findMany({ where: { diff --git a/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts index 95ca87eb..d11a6d34 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts @@ -2,43 +2,22 @@ import { Type } from 'class-transformer'; import { IsDefined, IsIn, - IsNumber, IsOptional, IsString, - Max, - Min, + IsDateString, } from 'class-validator'; -import dayjs from 'dayjs'; export class GetPostsDto { - @Type(() => Number) - @IsNumber() - @Max(52) - @Min(1) - week: number; + @IsDateString() + startDate: string; - @Type(() => Number) - @IsNumber() - @Max(6) - @Min(0) - day: number; + @IsDateString() + endDate: string; @IsDefined() @IsIn(['day', 'week', 'month']) display: 'day' | 'week' | 'month'; - @Type(() => Number) - @IsNumber() - @Max(12) - @Min(1) - month: number; - - @Type(() => Number) - @IsNumber() - @Max(dayjs().add(10, 'year').year()) - @Min(2022) - year: number; - @IsOptional() @IsString() customer: string;