diff --git a/worklenz-frontend/src/api/tasks/task-time-logs.api.service.ts b/worklenz-frontend/src/api/tasks/task-time-logs.api.service.ts index 1a9191ee..038b8584 100644 --- a/worklenz-frontend/src/api/tasks/task-time-logs.api.service.ts +++ b/worklenz-frontend/src/api/tasks/task-time-logs.api.service.ts @@ -2,6 +2,7 @@ import { API_BASE_URL } from '@/shared/constants'; import apiClient from '../api-client'; import { IServerResponse } from '@/types/common.types'; import { ITaskLogViewModel } from '@/types/tasks/task-log-view.types'; +import { getUserSession } from '@/utils/session-helper'; const rootUrl = `${API_BASE_URL}/task-time-log`; @@ -17,7 +18,11 @@ export interface IRunningTimer { export const taskTimeLogsApiService = { getByTask: async (id: string): Promise> => { - const response = await apiClient.get(`${rootUrl}/task/${id}`); + const session = getUserSession(); + const timezone = session?.timezone_name || 'UTC'; + const response = await apiClient.get(`${rootUrl}/task/${id}`, { + params: { time_zone_name: timezone } + }); return response.data; }, diff --git a/worklenz-frontend/src/components/task-drawer/shared/time-log/time-log-item.tsx b/worklenz-frontend/src/components/task-drawer/shared/time-log/time-log-item.tsx index 8e1fcbe9..e97129d2 100644 --- a/worklenz-frontend/src/components/task-drawer/shared/time-log/time-log-item.tsx +++ b/worklenz-frontend/src/components/task-drawer/shared/time-log/time-log-item.tsx @@ -3,7 +3,7 @@ import { Button, Divider, Flex, Popconfirm, Typography, Space } from '@/shared/a import { colors } from '@/styles/colors'; import { ITaskLogViewModel } from '@/types/tasks/task-log-view.types'; import SingleAvatar from '@/components/common/single-avatar/single-avatar'; -import { formatDateTimeWithLocale } from '@/utils/format-date-time-with-locale'; +import { formatDateTimeWithUserTimezone } from '@/utils/format-date-time-with-user-timezone'; import { calculateTimeGap } from '@/utils/calculate-time-gap'; import './time-log-item.css'; import { taskTimeLogsApiService } from '@/api/tasks/task-time-logs.api.service'; @@ -101,7 +101,7 @@ const TimeLogItem = ({ log, onDelete }: TimeLogItemProps) => { {renderLoggedByTimer()} {calculateTimeGap(created_at || '')} - {formatDateTimeWithLocale(created_at || '')} + {formatDateTimeWithUserTimezone(created_at || '', currentSession?.timezone_name)} {renderActionButtons()} diff --git a/worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts b/worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts new file mode 100644 index 00000000..876dbf94 --- /dev/null +++ b/worklenz-frontend/src/utils/format-date-time-with-user-timezone.ts @@ -0,0 +1,94 @@ +import { format } from 'date-fns'; +import { enUS, es, pt } from 'date-fns/locale'; +import { getLanguageFromLocalStorage } from './language-utils'; + +/** + * Formats a date/time string using the user's profile timezone + * This ensures consistency between time logs display and reporting filters + * + * @param dateString - The date string to format (typically in UTC) + * @param userTimezone - The user's timezone from their profile (e.g., 'America/New_York') + * @returns Formatted date string in user's timezone + */ +export const formatDateTimeWithUserTimezone = ( + dateString: string, + userTimezone?: string | null +): string => { + if (!dateString) return ''; + + try { + const date = new Date(dateString); + + // If timezone is provided, use it for formatting + if (userTimezone && userTimezone !== 'UTC') { + // Use the browser's toLocaleString with timezone option + const options: Intl.DateTimeFormatOptions = { + timeZone: userTimezone, + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: '2-digit', + second: '2-digit', + hour12: true + }; + + // Get the appropriate locale + const localeString = getLanguageFromLocalStorage(); + const localeMap = { + 'en': 'en-US', + 'es': 'es-ES', + 'pt': 'pt-PT' + }; + const locale = localeMap[localeString as keyof typeof localeMap] || 'en-US'; + + return date.toLocaleString(locale, options); + } + + // Fallback to date-fns formatting for UTC or when no timezone + const localeString = getLanguageFromLocalStorage(); + const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : pt; + return format(date, 'MMM d, yyyy, h:mm:ss a', { locale }); + } catch (error) { + console.error('Error formatting date with user timezone:', error); + // Fallback to original date string if formatting fails + return dateString; + } +}; + +/** + * Checks if a date is "yesterday" in the user's timezone + * This is used to ensure consistency with reporting filters + * + * @param dateString - The date string to check + * @param userTimezone - The user's timezone from their profile + * @returns true if the date is yesterday in user's timezone + */ +export const isYesterdayInUserTimezone = (dateString: string, userTimezone?: string | null): boolean => { + if (!dateString || !userTimezone) return false; + + try { + const date = new Date(dateString); + + // Get current date in user's timezone + const nowInTimezone = new Date().toLocaleString('en-US', { timeZone: userTimezone }); + const now = new Date(nowInTimezone); + + // Get yesterday in user's timezone + const yesterday = new Date(now); + yesterday.setDate(yesterday.getDate() - 1); + + // Convert the input date to user's timezone + const dateInTimezone = new Date(date.toLocaleString('en-US', { timeZone: userTimezone })); + + // Compare dates (ignoring time) + return ( + dateInTimezone.getFullYear() === yesterday.getFullYear() && + dateInTimezone.getMonth() === yesterday.getMonth() && + dateInTimezone.getDate() === yesterday.getDate() + ); + } catch (error) { + console.error('Error checking if date is yesterday:', error); + return false; + } +}; \ No newline at end of file