All files / packages/utils/src timeSlotRange.ts

84% Statements 21/25
83.33% Branches 15/18
83.33% Functions 5/6
86.95% Lines 20/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94                        1x             1x       8x 8x 8x     1x 4x       1x 4x       1x     5x 1x           4x 4x                         5x         5x   5x 1x           4x                   1x              
export type TimeSlotValue = "all_day" | "morning" | "afternoon";
 
export interface DateLikeRange {
  startDate: Date | null;
  endDate: Date | null;
}
 
interface SlotBoundary {
  hours: number;
  minutes: number;
}
 
const RANGE_TIME_BOUNDARY = {
  WORK_START: { hours: 8, minutes: 0 },
  MORNING_END: { hours: 12, minutes: 0 },
  AFTERNOON_START: { hours: 13, minutes: 30 },
  WORK_END: { hours: 17, minutes: 30 },
} as const;
 
const withBoundaryTime = (
  date: Date,
  { hours, minutes }: SlotBoundary,
): Date => {
  const nextDate = new Date(date);
  nextDate.setHours(hours, minutes, 0, 0);
  return nextDate;
};
 
const getRangeStartBoundary = (slot: TimeSlotValue): SlotBoundary =>
  slot === "afternoon"
    ? RANGE_TIME_BOUNDARY.AFTERNOON_START
    : RANGE_TIME_BOUNDARY.WORK_START;
 
const getRangeEndBoundary = (slot: TimeSlotValue): SlotBoundary =>
  slot === "morning"
    ? RANGE_TIME_BOUNDARY.MORNING_END
    : RANGE_TIME_BOUNDARY.WORK_END;
 
const resolveRangeBounds = (
  range: DateLikeRange,
): { startDate: Date | null; endDate: Date | null } => {
  if (!range.startDate && !range.endDate) {
    return {
      startDate: null,
      endDate: null,
    };
  }
 
  Eif (range.startDate && range.endDate) {
    return {
      startDate: range.startDate,
      endDate: range.endDate,
    };
  }
 
  const date = range.startDate || range.endDate;
  return {
    startDate: date,
    endDate: date,
  };
};
 
export const applyTimeSlotsToRange = (
  range: DateLikeRange,
  startSlot: TimeSlotValue,
  endSlot: TimeSlotValue,
): DateLikeRange => {
  const { startDate, endDate } = resolveRangeBounds(range);
 
  if (!startDate || !endDate) {
    return {
      startDate: null,
      endDate: null,
    };
  }
 
  return {
    startDate: withBoundaryTime(startDate, getRangeStartBoundary(startSlot)),
    endDate: withBoundaryTime(endDate, getRangeEndBoundary(endSlot)),
  };
};
 
/**
 * Normalizes a range where both start and end dates use the same time slot.
 * Useful for single-day shift normalization or quick selection presets.
 */
export const normalizeRangeBySlot = (
  startDate: Date | null,
  endDate: Date | null,
  slot: TimeSlotValue,
): DateLikeRange => {
  return applyTimeSlotsToRange({ startDate, endDate }, slot, slot);
};