refactor(timesheets): refacatored query-service

This commit is contained in:
Matthieu Haineault 2025-10-10 13:29:26 -04:00
parent 2de8db6212
commit 408e52b4f5
15 changed files with 114 additions and 202 deletions

View File

@ -5,7 +5,8 @@ import { EmployeesArchivalService } from './services/employees-archival.service'
import { SharedModule } from '../shared/shared.module'; import { SharedModule } from '../shared/shared.module';
@Module({ @Module({
controllers: [EmployeesController, SharedModule], imports: [SharedModule],
controllers: [EmployeesController],
providers: [EmployeesService, EmployeesArchivalService], providers: [EmployeesService, EmployeesArchivalService],
exports: [EmployeesService, EmployeesArchivalService], exports: [EmployeesService, EmployeesArchivalService],
}) })

View File

@ -1,8 +1,8 @@
import { Injectable, NotFoundException } from "@nestjs/common"; import { Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
import { DayExpensesDto as ExpenseListResponseDto, ExpenseDto } from "src/modules/timesheets/dtos/timesheet-period.dto"; import { DayExpensesDto as ExpenseListResponseDto, ExpenseDto } from "src/modules/timesheets/dtos/timesheet-period.dto";
import { round2, toUTCDateOnly } from "src/modules/timesheets/timesheet.helpers"; import { round2, toUTCDateOnly } from "src/modules/timesheets/utils-helpers-others/timesheet.helpers";
import { EXPENSE_TYPES } from "src/modules/timesheets/timesheet.types"; import { EXPENSE_TYPES } from "src/modules/timesheets/utils-helpers-others/timesheet.types";
import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils"; import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
@Injectable() @Injectable()

View File

@ -0,0 +1,2 @@
export const MS_PER_DAY = 86_400_000;
export const MS_PER_HOUR = 3_600_000;

View File

@ -5,7 +5,8 @@ import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { Roles as RoleEnum } from '.prisma/client'; import { Roles as RoleEnum } from '.prisma/client';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { TimesheetsCommandService } from '../services/timesheets-command.service'; import { TimesheetsCommandService } from '../services/timesheets-command.service';
import { TimesheetDto, TimesheetPeriodDto } from '../dtos/timesheet-period.dto'; import { TimesheetMap } from '../utils-helpers-others/timesheet.types';
import { TimesheetPeriodDto } from '../dtos/timesheet-period.dto';
@ApiTags('Timesheets') @ApiTags('Timesheets')
@ -33,7 +34,7 @@ export class TimesheetsController {
async getByEmail( async getByEmail(
@Param('email') email: string, @Param('email') email: string,
@Query('offset') offset?: string, @Query('offset') offset?: string,
): Promise<TimesheetDto> { ): Promise<TimesheetMap> {
const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0; const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0;
return this.timesheetsQuery.getTimesheetByEmail(email, week_offset); return this.timesheetsQuery.getTimesheetByEmail(email, week_offset);
} }
@ -43,37 +44,8 @@ export class TimesheetsController {
@Param('email') email: string, @Param('email') email: string,
@Body() dto: CreateWeekShiftsDto, @Body() dto: CreateWeekShiftsDto,
@Query('offset') offset?: string, @Query('offset') offset?: string,
): Promise<TimesheetDto> { ): Promise<TimesheetMap> {
const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0; const week_offset = Number.isFinite(Number(offset)) ? Number(offset) : 0;
return this.timesheetsCommand.createWeekShiftsAndReturnOverview(email, dto.shifts, week_offset); return this.timesheetsCommand.createWeekShiftsAndReturnOverview(email, dto.shifts, week_offset);
} }
//_____________________________________________________________________________________________
// Deprecated or unused methods
//_____________________________________________________________________________________________
// @Patch('approval/:id')
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
// async approve(@Param('id', ParseIntPipe) id: number, @Body('is_approved', ParseBoolPipe) isApproved: boolean) {
// return this.timesheetsCommand.updateApproval(id, isApproved);
// }
// @Get(':id')
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
// @ApiOperation({ summary: 'Find timesheet' })
// @ApiResponse({ status: 201, description: 'Timesheet found', type: CreateTimesheetDto })
// @ApiResponse({ status: 400, description: 'Timesheet not found' })
// findOne(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> {
// return this.timesheetsQuery.findOne(id);
// }
// @Delete(':id')
// // @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
// @ApiOperation({ summary: 'Delete timesheet' })
// @ApiResponse({ status: 201, description: 'Timesheet deleted', type: CreateTimesheetDto })
// @ApiResponse({ status: 400, description: 'Timesheet not found' })
// remove(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> {
// return this.timesheetsQuery.remove(id);
// }
} }

View File

@ -1,7 +1,6 @@
import { TimesheetsArchive } from "@prisma/client"; import { TimesheetsArchive } from "@prisma/client";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
export class TimesheetArchiveService { export class TimesheetArchiveService {
constructor(private readonly prisma: PrismaService){} constructor(private readonly prisma: PrismaService){}
@ -21,9 +20,7 @@ export class TimesheetArchiveService {
is_approved: true, is_approved: true,
}, },
}); });
if( oldSheets.length === 0) { if( oldSheets.length === 0) return;
return;
}
//preping data for archivation //preping data for archivation
const archiveDate = oldSheets.map(sheet => ({ const archiveDate = oldSheets.map(sheet => ({

View File

@ -1,15 +1,15 @@
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { Prisma, Timesheets } from "@prisma/client";
import { BaseApprovalService } from "src/common/shared/base-approval.service";
import { PrismaService } from "src/prisma/prisma.service";
import { TimesheetsQueryService } from "./timesheets-query.service";
import { CreateTimesheetDto } from "../dtos/create-timesheet.dto";
import { getWeekEnd, getWeekStart } from "src/common/utils/date-utils";
import { parseISODate, parseHHmm } from "../timesheet.helpers";
import { TimesheetDto } from "../dtos/timesheet-period.dto";
import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
import { EmployeeTimesheetResolver } from "src/modules/shared/utils/resolve-employee-timesheet.utils"; import { EmployeeTimesheetResolver } from "src/modules/shared/utils/resolve-employee-timesheet.utils";
import { getWeekEnd, getWeekStart } from "src/common/utils/date-utils";
import { parseISODate, parseHHmm } from "../utils-helpers-others/timesheet.helpers";
import { TimesheetsQueryService } from "./timesheets-query.service";
import { BaseApprovalService } from "src/common/shared/base-approval.service";
import { Prisma, Timesheets } from "@prisma/client";
import { CreateTimesheetDto } from "../dtos/create-timesheet.dto";
import { EmailToIdResolver } from "src/modules/shared/utils/resolve-email-id.utils";
import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils"; import { BankCodesResolver } from "src/modules/shared/utils/resolve-bank-type-id.utils";
import { PrismaService } from "src/prisma/prisma.service";
import { TimesheetMap } from "../utils-helpers-others/timesheet.types";
@Injectable() @Injectable()
export class TimesheetsCommandService extends BaseApprovalService<Timesheets>{ export class TimesheetsCommandService extends BaseApprovalService<Timesheets>{
@ -58,7 +58,7 @@ export class TimesheetsCommandService extends BaseApprovalService<Timesheets>{
email:string, email:string,
shifts: CreateTimesheetDto[], shifts: CreateTimesheetDto[],
week_offset = 0, week_offset = 0,
): Promise<TimesheetDto> { ): Promise<TimesheetMap> {
//fetchs employee matchint user's email //fetchs employee matchint user's email
const employee_id = await this.emailResolver.findIdByEmail(email); const employee_id = await this.emailResolver.findIdByEmail(email);
if(!employee_id) throw new NotFoundException(`employee for ${ email } not found`); if(!employee_id) throw new NotFoundException(`employee for ${ email } not found`);

View File

@ -1,14 +1,13 @@
import { endOfDayUTC, toHHmm, toNum, toRangeFromPeriod, toUTCDateOnly } from '../timesheet.helpers'; import { makeEmptyTimesheet, mapExpenseRow, mapShiftRow } from '../utils-helpers-others/timesheet.mappers';
import { Injectable, NotFoundException } from '@nestjs/common'; import { buildPeriod, computeWeekRange } from '../utils-helpers-others/timesheet.utils';
import { formatDateISO, getWeekEnd, getWeekStart } from 'src/common/utils/date-utils'; import { TimesheetSelectorsService } from '../utils-helpers-others/timesheet.selectors';
import { PrismaService } from 'src/prisma/prisma.service'; import { TimesheetPeriodDto } from '../dtos/timesheet-period.dto';
import { TimesheetDto, TimesheetPeriodDto } from '../dtos/timesheet-period.dto'; import { toRangeFromPeriod } from '../utils-helpers-others/timesheet.helpers';
import { ShiftRow, ExpenseRow } from '../timesheet.types';
import { buildPeriod } from '../timesheet.utils';
import { EmailToIdResolver } from 'src/modules/shared/utils/resolve-email-id.utils'; import { EmailToIdResolver } from 'src/modules/shared/utils/resolve-email-id.utils';
import { FullNameResolver } from 'src/modules/shared/utils/resolve-full-name.utils'; import { FullNameResolver } from 'src/modules/shared/utils/resolve-full-name.utils';
import { TimesheetSelectors } from '../timesheet.selectors'; import { PrismaService } from 'src/prisma/prisma.service';
import { mapExpenseRow, mapShiftRow } from '../timesheet.mappers'; import { TimesheetMap } from '../utils-helpers-others/timesheet.types';
import { Injectable } from '@nestjs/common';
@Injectable() @Injectable()
@ -17,28 +16,19 @@ export class TimesheetsQueryService {
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly emailResolver: EmailToIdResolver, private readonly emailResolver: EmailToIdResolver,
private readonly fullNameResolver: FullNameResolver, private readonly fullNameResolver: FullNameResolver,
private readonly selectors: TimesheetSelectors, private readonly selectors: TimesheetSelectorsService,
) {} ) {}
async findAll(year: number, period_no: number, email: string): Promise<TimesheetPeriodDto> { async findAll(year: number, period_no: number, email: string): Promise<TimesheetPeriodDto> {
//finds the employee using email const employee_id = await this.emailResolver.findIdByEmail(email); //finds the employee using email
const employee_id = await this.emailResolver.findIdByEmail(email); const full_name = await this.fullNameResolver.resolveFullName(employee_id); //finds the employee full name using employee_id
const period = await this.selectors.getPayPeriod(year, period_no);//finds the pay period using year and period_no
//finds the employee full name using employee_id const{ from, to } = toRangeFromPeriod(period); //finds start and end dates
const full_name = await this.fullNameResolver.resolveFullName(employee_id);
//finds the pay period using year and period_no
const period = await this.selectors.getPayPeriod(year, period_no);
//finds start and end dates
const{ from, to } = toRangeFromPeriod(period);
//finds all shifts from selected period //finds all shifts from selected period
const [raw_shifts, raw_expenses] = await Promise.all([ const [raw_shifts, raw_expenses] = await Promise.all([
this.selectors.getShifts(employee_id, from, to), this.selectors.getShifts(employee_id, from, to),
this.selectors.getExpenses(employee_id, from, to), this.selectors.getExpenses(employee_id, from, to),
]); ]);
// data mapping // data mapping
const shifts = raw_shifts.map(mapShiftRow); const shifts = raw_shifts.map(mapShiftRow);
const expenses = raw_expenses.map(mapExpenseRow); const expenses = raw_expenses.map(mapExpenseRow);
@ -46,121 +36,19 @@ export class TimesheetsQueryService {
return buildPeriod(period.period_start, period.period_end, shifts , expenses, full_name); return buildPeriod(period.period_start, period.period_end, shifts , expenses, full_name);
} }
async getTimesheetByEmail(email: string, week_offset = 0): Promise<TimesheetDto> {
const employee_id = await this.emailResolver.findIdByEmail(email);
if(!employee_id) throw new NotFoundException(`Employee with email: ${email} not found`);
//sets current week Sunday -> Saturday
const base = new Date();
const offset = new Date(base);
offset.setDate(offset.getDate() + (week_offset * 7));
const start_date_week = getWeekStart(offset, 0); async getTimesheetByEmail(email: string, week_offset = 0): Promise<TimesheetMap> {
const end_date_week = getWeekEnd(start_date_week); const employee_id = await this.emailResolver.findIdByEmail(email); //finds the employee using email
const start_day = formatDateISO(start_date_week); const { start, start_day, end_day, label } = computeWeekRange(week_offset);
const end_day = formatDateISO(end_date_week); const timesheet = await this.selectors.getTimesheetWithShiftsAndExpenses(employee_id, start); //fetch timesheet shifts and expenses
if(!timesheet) return makeEmptyTimesheet({ start_day, end_day, label});
//build the label MM/DD/YYYY.MM/DD.YYYY
const mm_dd = (date: Date) => `${String(date.getFullYear())}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2,'0')}`;
const label = `${mm_dd(start_date_week)}.${mm_dd(end_date_week)}`;
//fetch timesheet shifts and expenses
const timesheet = await this.prisma.timesheets.findUnique({
where: {
employee_id_start_date: {
employee_id: employee_id,
start_date: start_date_week,
},
},
include: {
shift: {
include: { bank_code: true },
orderBy: [{ date: 'asc'}, { start_time: 'asc'}],
},
expense: {
include: { bank_code: true },
orderBy: [{date: 'asc'}],
},
},
});
//returns an empty timesheet if not found
if(!timesheet) {
return {
is_approved: false,
start_day,
end_day,
label,
shifts:[],
expenses: [],
} as TimesheetDto;
}
//maps all shifts of selected timesheet //maps all shifts of selected timesheet
const shifts = timesheet.shift.map((shift_row) => ({ const shifts = timesheet.shift.map(mapShiftRow);
type: shift_row.bank_code?.type ?? '', const expenses = timesheet.expense.map(mapExpenseRow);
date: formatDateISO(shift_row.date),
start_time: toHHmm(shift_row.start_time),
end_time: toHHmm(shift_row.end_time),
comment: shift_row.comment ?? '',
is_approved: shift_row.is_approved ?? false,
is_remote: shift_row.is_remote ?? false,
}));
//maps all expenses of selected timsheet
const expenses = timesheet.expense.map((exp) => ({
type: exp.bank_code?.type ?? '',
date: formatDateISO(exp.date),
amount: Number(exp.amount) || 0,
mileage: exp.mileage != null ? Number(exp.mileage) : 0,
comment: exp.comment ?? '',
is_approved: exp.is_approved ?? false,
supervisor_comment: exp.supervisor_comment ?? '',
}));
return { return { start_day, end_day, label, shifts, expenses, is_approved: timesheet.is_approved};
start_day,
end_day,
label,
shifts,
expenses,
is_approved: timesheet.is_approved,
} as TimesheetDto;
} }
//_____________________________________________________________________________________________
// Deprecated or unused methods
//_____________________________________________________________________________________________
// async findOne(id: number): Promise<any> {
// const timesheet = await this.prisma.timesheets.findUnique({
// where: { id },
// include: {
// shift: { include: { bank_code: true } },
// expense: { include: { bank_code: true } },
// employee: { include: { user: true } },
// },
// });
// if(!timesheet) {
// throw new NotFoundException(`Timesheet #${id} not found`);
// }
// const detailedShifts = timesheet.shift.map( s => {
// const hours = computeHours(s.start_time, s.end_time);
// const regularHours = Math.min(8, hours);
// const dailyOvertime = this.overtime.getDailyOvertimeHours(s.start_time, s.end_time);
// const payRegular = regularHours * s.bank_code.modifier;
// const payOvertime = this.overtime.calculateOvertimePay(dailyOvertime, s.bank_code.modifier);
// return { ...s, hours, payRegular, payOvertime };
// });
// const weeklyOvertimeHours = detailedShifts.length
// ? await this.overtime.getWeeklyOvertimeHours(
// timesheet.employee_id,
// timesheet.shift[0].date): 0;
// return { ...timesheet, shift: detailedShifts, weeklyOvertimeHours };
// }
// async remove(id: number): Promise<Timesheets> {
// await this.findOne(id);
// return this.prisma.timesheets.delete({ where: { id } });
// }
} }

View File

@ -7,11 +7,12 @@ import { ExpensesCommandService } from '../expenses/services/expenses-command.
import { BusinessLogicsModule } from 'src/modules/business-logics/business-logics.module'; import { BusinessLogicsModule } from 'src/modules/business-logics/business-logics.module';
import { SharedModule } from '../shared/shared.module'; import { SharedModule } from '../shared/shared.module';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TimesheetSelectorsService } from './utils-helpers-others/timesheet.selectors';
@Module({ @Module({
imports: [ imports: [
BusinessLogicsModule, BusinessLogicsModule,
SharedModule SharedModule,
], ],
controllers: [TimesheetsController], controllers: [TimesheetsController],
providers: [ providers: [
@ -20,6 +21,7 @@ import { Module } from '@nestjs/common';
ShiftsCommandService, ShiftsCommandService,
ExpensesCommandService, ExpensesCommandService,
TimesheetArchiveService, TimesheetArchiveService,
TimesheetSelectorsService,
], ],
exports: [ exports: [
TimesheetsQueryService, TimesheetsQueryService,

View File

@ -1,4 +1,5 @@
import { MS_PER_DAY, DayKey, DAY_KEYS } from "./timesheet.types"; import { MS_PER_DAY } from "src/modules/shared/constants/date-time.constant";
import { DAY_KEYS, DayKey } from "././timesheet.types";
export function toUTCDateOnly(date: Date | string): Date { export function toUTCDateOnly(date: Date | string): Date {
const d = new Date(date); const d = new Date(date);

View File

@ -1,5 +1,5 @@
import { DayExpensesDto, WeekDto, DetailedShifts, TimesheetPeriodDto } from "./dtos/timesheet-period.dto"; import { DayExpensesDto, WeekDto, DetailedShifts, TimesheetPeriodDto } from "../dtos/timesheet-period.dto";
import { ExpenseRow, ExpensesAmount, ShiftRow } from "./timesheet.types"; import { ShiftRow, ExpenseRow, ExpensesAmount, TimesheetMap } from "./timesheet.types";
import { addDays, shortDate, toNum, upper } from "./timesheet.helpers"; import { addDays, shortDate, toNum, upper } from "./timesheet.helpers";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
@ -41,7 +41,6 @@ export const mapExpenseRow = (expense: {
type: upper(expense.bank_code.type), type: upper(expense.bank_code.type),
}); });
// Factories // Factories
export function makeEmptyDayExpenses(): DayExpensesDto { export function makeEmptyDayExpenses(): DayExpensesDto {
return { return {
@ -93,3 +92,20 @@ export const makeAmounts = (): ExpensesAmount => ({
expense: 0, expense: 0,
mileage: 0, mileage: 0,
}); });
export function makeEmptyTimesheet(params: {
start_day: string;
end_day: string;
label: string;
is_approved?: boolean;
}): TimesheetMap {
const { start_day, end_day, label, is_approved = false } = params;
return {
start_day,
end_day,
label,
shifts: [],
expenses: [],
is_approved,
};
}

View File

@ -1,11 +1,11 @@
import { EXPENSE_ASC_ORDER, EXPENSE_SELECT } from "../../shared/selects/expenses.select";
import { Injectable, NotFoundException } from "@nestjs/common"; import { Injectable, NotFoundException } from "@nestjs/common";
import { SHIFT_ASC_ORDER, SHIFT_SELECT } from "../../shared/selects/shifts.select";
import { PAY_PERIOD_SELECT } from "../../shared/selects/pay-periods.select";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
import { SHIFT_ASC_ORDER, SHIFT_SELECT } from "../shared/selects/shifts.select";
import { PAY_PERIOD_SELECT } from "../shared/selects/pay-periods.select";
import { EXPENSE_ASC_ORDER, EXPENSE_SELECT } from "../shared/selects/expenses.select";
@Injectable() @Injectable()
export class TimesheetSelectors { export class TimesheetSelectorsService {
constructor(readonly prisma: PrismaService){} constructor(readonly prisma: PrismaService){}
async getPayPeriod(pay_year: number, pay_period_no: number) { async getPayPeriod(pay_year: number, pay_period_no: number) {
@ -32,4 +32,15 @@ export class TimesheetSelectors {
orderBy: EXPENSE_ASC_ORDER, orderBy: EXPENSE_ASC_ORDER,
}); });
} }
async getTimesheetWithShiftsAndExpenses(employee_id: number, start_date_week: Date) {
return this.prisma.timesheets.findUnique({
where: { employee_id_start_date: { employee_id, start_date: start_date_week } },
select: {
is_approved: true,
shift: { select: SHIFT_SELECT, orderBy: SHIFT_ASC_ORDER },
expense: { select: EXPENSE_SELECT, orderBy: EXPENSE_ASC_ORDER },
},
});
}
} }

View File

@ -17,9 +17,14 @@ export type ExpenseRow = {
supervisor_comment: string; supervisor_comment: string;
}; };
//Date & Format export type TimesheetMap = {
export const MS_PER_DAY = 86_400_000; start_day: string;
export const MS_PER_HOUR = 3_600_000; end_day: string;
label: string;
shifts: ShiftRow[];
expenses: ExpenseRow[]
is_approved: boolean;
}
// Types // Types
export const SHIFT_TYPES = { export const SHIFT_TYPES = {

View File

@ -1,14 +1,31 @@
import { import {
DayKey, DAY_KEYS, EXPENSE_TYPES, ExpenseRow, MS_PER_HOUR, DayKey, DAY_KEYS, EXPENSE_TYPES, ExpenseRow,
SHIFT_TYPES, ShiftRow, make_hours, ShiftsHours, ExpensesAmount SHIFT_TYPES, ShiftRow, make_hours, ShiftsHours, ExpensesAmount
} from "./timesheet.types"; } from "./timesheet.types";
import { import {
isBetweenUTC, dayKeyFromDate, toTimeString, round2, isBetweenUTC, dayKeyFromDate, toTimeString, round2,
toUTCDateOnly, endOfDayUTC, addDays toUTCDateOnly, endOfDayUTC, addDays
} from "./timesheet.helpers"; } from "./timesheet.helpers";
import { WeekDto, ShiftDto, TimesheetPeriodDto, DayExpensesDto, ExpenseDto } from "./dtos/timesheet-period.dto"; import { WeekDto, ShiftDto, TimesheetPeriodDto, DayExpensesDto, ExpenseDto } from "../dtos/timesheet-period.dto";
import { getWeekStart, getWeekEnd, formatDateISO } from "src/common/utils/date-utils";
import { makeAmounts, makeEmptyWeek } from "./timesheet.mappers"; import { makeAmounts, makeEmptyWeek } from "./timesheet.mappers";
import { toDateString } from "src/modules/pay-periods/utils/pay-year.util"; import { toDateString } from "src/modules/pay-periods/utils/pay-year.util";
import { MS_PER_HOUR } from "src/modules/shared/constants/date-time.constant";
export function computeWeekRange(week_offset = 0){
//sets current week Sunday -> Saturday
const base = new Date();
const offset = new Date(base);
offset.setDate(offset.getDate() + (week_offset * 7));
const start = getWeekStart(offset, 0);
const end = getWeekEnd(start);
const start_day = formatDateISO(start);
const end_day = formatDateISO(end);
const label = `${(start_day)}.${(end_day)}`;
return { start, end, start_day, end_day, label }
};
export function buildWeek( export function buildWeek(
week_start: Date, week_start: Date,