- Updated SQL queries in reporting controllers to join with the timezones table for accurate timezone retrieval. - Refactored ReportingMembersController to extend ReportingControllerBaseWithTimezone for centralized timezone logic. - Removed obsolete migration file that added a timezone column to the users table, as it is no longer needed.
117 lines
4.3 KiB
TypeScript
117 lines
4.3 KiB
TypeScript
import WorklenzControllerBase from "../worklenz-controller-base";
|
|
import { IWorkLenzRequest } from "../../interfaces/worklenz-request";
|
|
import db from "../../config/db";
|
|
import moment from "moment-timezone";
|
|
import { DATE_RANGES } from "../../shared/constants";
|
|
|
|
export default abstract class ReportingControllerBaseWithTimezone extends WorklenzControllerBase {
|
|
|
|
/**
|
|
* Get the user's timezone from the database or request
|
|
* @param userId - The user ID
|
|
* @returns The user's timezone or 'UTC' as default
|
|
*/
|
|
protected static async getUserTimezone(userId: string): Promise<string> {
|
|
const q = `SELECT tz.name as timezone
|
|
FROM users u
|
|
JOIN timezones tz ON u.timezone_id = tz.id
|
|
WHERE u.id = $1`;
|
|
const result = await db.query(q, [userId]);
|
|
return result.rows[0]?.timezone || 'UTC';
|
|
}
|
|
|
|
/**
|
|
* Generate date range clause with timezone support
|
|
* @param key - Date range key (e.g., YESTERDAY, LAST_WEEK)
|
|
* @param dateRange - Array of date strings
|
|
* @param userTimezone - User's timezone (e.g., 'America/New_York')
|
|
* @returns SQL clause for date filtering
|
|
*/
|
|
protected static getDateRangeClauseWithTimezone(key: string, dateRange: string[], userTimezone: string) {
|
|
// For custom date ranges
|
|
if (dateRange.length === 2) {
|
|
// Convert dates to user's timezone start/end of day
|
|
const start = moment.tz(dateRange[0], userTimezone).startOf('day');
|
|
const end = moment.tz(dateRange[1], userTimezone).endOf('day');
|
|
|
|
// Convert to UTC for database comparison
|
|
const startUtc = start.utc().format("YYYY-MM-DD HH:mm:ss");
|
|
const endUtc = end.utc().format("YYYY-MM-DD HH:mm:ss");
|
|
|
|
if (start.isSame(end, 'day')) {
|
|
// Single day selection
|
|
return `AND task_work_log.created_at >= '${startUtc}'::TIMESTAMP AND task_work_log.created_at <= '${endUtc}'::TIMESTAMP`;
|
|
}
|
|
|
|
return `AND task_work_log.created_at >= '${startUtc}'::TIMESTAMP AND task_work_log.created_at <= '${endUtc}'::TIMESTAMP`;
|
|
}
|
|
|
|
// For predefined ranges, calculate based on user's timezone
|
|
const now = moment.tz(userTimezone);
|
|
let startDate, endDate;
|
|
|
|
switch (key) {
|
|
case DATE_RANGES.YESTERDAY:
|
|
startDate = now.clone().subtract(1, 'day').startOf('day');
|
|
endDate = now.clone().subtract(1, 'day').endOf('day');
|
|
break;
|
|
case DATE_RANGES.LAST_WEEK:
|
|
startDate = now.clone().subtract(1, 'week').startOf('week');
|
|
endDate = now.clone().subtract(1, 'week').endOf('week');
|
|
break;
|
|
case DATE_RANGES.LAST_MONTH:
|
|
startDate = now.clone().subtract(1, 'month').startOf('month');
|
|
endDate = now.clone().subtract(1, 'month').endOf('month');
|
|
break;
|
|
case DATE_RANGES.LAST_QUARTER:
|
|
startDate = now.clone().subtract(3, 'months').startOf('day');
|
|
endDate = now.clone().endOf('day');
|
|
break;
|
|
default:
|
|
return "";
|
|
}
|
|
|
|
if (startDate && endDate) {
|
|
const startUtc = startDate.utc().format("YYYY-MM-DD HH:mm:ss");
|
|
const endUtc = endDate.utc().format("YYYY-MM-DD HH:mm:ss");
|
|
return `AND task_work_log.created_at >= '${startUtc}'::TIMESTAMP AND task_work_log.created_at <= '${endUtc}'::TIMESTAMP`;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* Format dates for display in user's timezone
|
|
* @param date - Date to format
|
|
* @param userTimezone - User's timezone
|
|
* @param format - Moment format string
|
|
* @returns Formatted date string
|
|
*/
|
|
protected static formatDateInTimezone(date: string | Date, userTimezone: string, format: string = "YYYY-MM-DD HH:mm:ss") {
|
|
return moment.tz(date, userTimezone).format(format);
|
|
}
|
|
|
|
/**
|
|
* Get working days count between two dates in user's timezone
|
|
* @param startDate - Start date
|
|
* @param endDate - End date
|
|
* @param userTimezone - User's timezone
|
|
* @returns Number of working days
|
|
*/
|
|
protected static getWorkingDaysInTimezone(startDate: string, endDate: string, userTimezone: string): number {
|
|
const start = moment.tz(startDate, userTimezone);
|
|
const end = moment.tz(endDate, userTimezone);
|
|
let workingDays = 0;
|
|
|
|
const current = start.clone();
|
|
while (current.isSameOrBefore(end, 'day')) {
|
|
// Monday = 1, Friday = 5
|
|
if (current.isoWeekday() >= 1 && current.isoWeekday() <= 5) {
|
|
workingDays++;
|
|
}
|
|
current.add(1, 'day');
|
|
}
|
|
|
|
return workingDays;
|
|
}
|
|
} |