HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux spn-python 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
User: arjun (1000)
PHP: 8.1.2-1ubuntu2.20
Disabled: NONE
Upload Files
File: //home/arjun/projects/good-life-be/api/Event/utils.js
/* eslint-disable function-paren-newline */
/* eslint-disable implicit-arrow-linebreak */
import moment from 'moment';
import { Op } from 'sequelize';

import Category from '../../models/Category.js';
import EventModel from '../../models/Event.js';

export const assignColorToEvents = async (events) => {
  const categories = await Category.findAll({
    attributes: ['name', 'color', 'lightColor'],
    raw: true,
  });

  // Create a map from category name to color
  const categoryColorMap = categories.reduce(
    (acc, { name, color, lightColor }) => {
      acc[name] = { color, lightColor };
      return acc;
    },
    {}
  );

  return events.map((event) => {
    const categoryColors = categoryColorMap[event.category] || {}; // Get color set, default to empty object
    return {
      ...event,
      color: categoryColors.color || '#FFFFFF', // Default color
      lightColor: categoryColors.lightColor || '#F0F0F0', // Default lightColor
    };
  });
};

export const groupEventsByWeek = async (eventsData) => {
  const grouped = {};
  eventsData.forEach((event) => {
    // Determine the week start and end dates
    const eventDate = moment(event.date);
    const weekStart = moment(eventDate).startOf('week'); // Default: Sunday
    const weekEnd = moment(eventDate).endOf('week');
    // Check if the week spans two months
    const isSameMonth = weekStart.month() === weekEnd.month();
    // Format the week range accordingly
    const weekRange = isSameMonth
      ? `${weekStart.format('MMMM YYYY')}`
      : `${weekStart.format('MMMM YYYY')} - ${weekEnd.format('MMMM YYYY')}`;
    // Initialize the group if not already present
    if (!grouped[weekRange]) {
      grouped[weekRange] = { events: [], weekStart, weekEnd };
    }
    // Convert start_time and end_time from "HH:mm:ss" to "hh:mm A"
    const formattedEvent = {
      ...event,
      start_time: moment(event.start_time, 'HH:mm:ss').format('hh:mm A'),
      end_time: moment(event.end_time, 'HH:mm:ss').format('hh:mm A'),
    };
    // Add the event to the corresponding week range
    grouped[weekRange].events.push(formattedEvent);
  });
  // Transform grouped object into an array format
  return Object.keys(grouped).map((weekRange) => {
    const { weekStart, weekEnd, events } = grouped[weekRange];
    // Generate an array of week dates
    const weekDates = [];
    const currentDate = weekStart.clone();
    while (currentDate.isSameOrBefore(weekEnd)) {
      weekDates.push(currentDate.format('YYYY-MM-DD'));
      currentDate.add(1, 'day');
    }
    const orderedWeekDates = weekDates.sort((a, b) =>
      moment(a).isBefore(moment(b)) ? -1 : 1
    );
    return {
      weekRange,
      hours: [
        '12:00 AM',
        '01:00 AM',
        '02:00 AM',
        '03:00 AM',
        '04:00 AM',
        '05:00 AM',
        '06:00 AM',
        '07:00 AM',
        '08:00 AM',
        '09:00 AM',
        '10:00 AM',
        '11:00 AM',
        '12:00 PM',
        '01:00 PM',
        '02:00 PM',
        '03:00 PM',
        '04:00 PM',
        '05:00 PM',
        '06:00 PM',
        '07:00 PM',
        '08:00 PM',
        '09:00 PM',
        '10:00 PM',
        '11:00 PM',
      ],
      weekDates: orderedWeekDates, // Add weekDates array here
      events,
    };
  });
};

const hours = [
  '12:00 AM',
  '01:00 AM',
  '02:00 AM',
  '03:00 AM',
  '04:00 AM',
  '05:00 AM',
  '06:00 AM',
  '07:00 AM',
  '08:00 AM',
  '09:00 AM',
  '10:00 AM',
  '11:00 AM',
  '12:00 PM',
  '01:00 PM',
  '02:00 PM',
  '03:00 PM',
  '04:00 PM',
  '05:00 PM',
  '06:00 PM',
  '07:00 PM',
  '08:00 PM',
  '09:00 PM',
  '10:00 PM',
  '11:00 PM',
];

export const groupEventsByDay = async (eventsData) => {
  const rowHeight = 60; // Each row height in pixels
  const grouped = {};

  function truncateText(text, maxLength) {
    if (text.length <= maxLength) return text;

    const trimmedText = text.substring(0, maxLength).trim();
    const lastSpaceIndex = trimmedText.lastIndexOf(' ');

    // If no space found, return full trimmed text
    if (lastSpaceIndex === -1) return trimmedText;

    return `${trimmedText.substring(0, lastSpaceIndex)}...`;
  }

  eventsData.forEach((event) => {
    const eventDate = moment(event.date).format('YYYY-MM-DD');

    if (!grouped[eventDate]) {
      grouped[eventDate] = {
        date: eventDate,
        dayFormat: moment(event.date).format('MMMM-DD-YYYY'),
        events: [],
        hours,
      };
    }

    const startMoment = moment(event.start_time, 'hh:mm A');
    const endMoment = moment(event.end_time, 'hh:mm A');

    const startMinutes = startMoment.minutes();
    const endMinutes = endMoment.minutes();
    const startHour = startMoment.hours();
    const endHour = endMoment.hours();

    // Calculate percentage within the hour
    const startPercentage = Math.floor((startMinutes / 60) * 100);
    const endPercentage = Math.floor(
      startPercentage +
        (endHour - startHour) * 100 +
        ((endMinutes - startMinutes) / 60) * 100
    );

    // Convert percentage to pixels
    let height = Math.floor(
      ((endPercentage - startPercentage) / 100) * rowHeight
    );

    height = height <= 15 ? 17 : height;

    // Calculate top position
    const top = Math.floor((startPercentage / 100) * rowHeight);
    // top = height <= 15 ? top - 15 : top;

    const formattedEvent = {
      ...event,
      event_title: truncateText(event.event_title, 40),
      start_time: startMoment.format('hh:mm A'),
      end_time: endMoment.format('hh:mm A'),
      heightPercentage: [startPercentage, endPercentage],
      eventHour: startMoment.format('HH'),
      height: height - 2, // Height in pixels
      top, // Position from the top in pixels
    };

    grouped[eventDate].events.push(formattedEvent);
  });

  const returnss = Object.values(grouped).sort((a, b) =>
    moment(a.date).diff(moment(b.date))
  );
  // const groupEventsByHour = (data) =>
  //   data.map((day) => {
  //     let same = [];
  //     let different = [];

  //     const groupeds = {};

  //     // Group events by hour
  //     day.events.forEach((event) => {
  //       const eventHour = `${event.start_time.split(':')[0]}:00 ${
  //         event.start_time.split(' ')[1]
  //       }`;

  //       if (!groupeds[eventHour]) {
  //         groupeds[eventHour] = [];
  //       }
  //       groupeds[eventHour].push(event);
  //     });

  //     // Separate into "same" and "different"
  //     Object.values(groupeds).forEach((eventGroup) => {
  //       if (eventGroup.length > 1) {
  //         same = [...same, ...eventGroup];
  //       } else {
  //         different = [...different, ...eventGroup];
  //       }
  //     });

  //     return {
  //       ...day,
  //       events: { same, different },
  //     };
  //   });

  // const groupedEventsData = groupEventsByHour(returnss);

  const parseTime = (timeStr) => {
    const [time, modifier] = timeStr.split(' ');
    let [hours, minutes] = time.split(':').map(Number);
    if (modifier === 'PM' && hours !== 12) hours += 12;
    if (modifier === 'AM' && hours === 12) hours = 0;
    return { hours, minutes, raw: timeStr };
  };

  const isAnyTimeWithinBounds = (startTime, endTime, boundStart, boundEnd) => {
    const start = parseTime(startTime);
    const end = parseTime(endTime);
    const boundS = parseTime(boundStart);
    const boundE = parseTime(boundEnd);

    return (
      (start.hours < boundE.hours && end.hours > boundS.hours) ||
      (start.hours === boundS.hours && start.minutes >= boundS.minutes) ||
      (end.hours === boundE.hours && end.minutes <= boundE.minutes) ||
      (start.hours < boundE.hours &&
        end.hours === boundE.hours &&
        end.minutes > boundS.minutes) ||
      (start.hours < boundS.hours && end.hours >= boundS.hours) // Ensures overlap into next hour
    );
  };

  const groupEventsByHours = (data) =>
    data.map((day) => {
      const formattedHours = day.hours
        .slice(0, -1)
        .map((hour, i) => [hour, day.hours[i + 1]]);
      const groupedEvents = {};

      formattedHours.forEach(([startHour, endHour], index) => {
        day.events.forEach((event) => {
          if (
            isAnyTimeWithinBounds(
              event.start_time,
              event.end_time,
              startHour,
              endHour
            )
          ) {
            const key = startHour;

            const hasAlreadyBeenAdded = Object.values(groupedEvents ?? {}).find(
              (e) =>
                e?.some(
                  (each) =>
                    each?.event_title === event?.event_title && !each?.isHidden
                )
            );
            const findIndexs = hasAlreadyBeenAdded?.findIndex(
              (e) => e?.event_title === event?.event_title
            );

            const wasAlreadyAddedToEarlierInterval = Object.entries(
              groupedEvents
            ).some(([hourKey, events]) =>
              events.some(
                (e) =>
                  e.event_title === event.event_title &&
                  e.start_time === event.start_time &&
                  e.end_time === key // Prevent if end time matches the start of this interval
              )
            );

            if (wasAlreadyAddedToEarlierInterval) {
              return; // Skip adding this event to avoid duplication in a later interval
            }

            if (findIndexs !== 1) {
              groupedEvents[key] = [
                ...(groupedEvents[key] || []),
                hasAlreadyBeenAdded ? { ...event, isHidden: true } : event,
              ];
              groupedEvents[key] = groupedEvents[key].sort((a, b) =>
                a?.isHidden === b?.isHidden ? 0 : a?.isHidden ? -1 : 1
              );
            }
          }
        });
      });

      return { ...day, groupedEvents };
    });

  const groupedEventsData = groupEventsByHours(returnss);
  return groupedEventsData;
};

export const eventUpdate = async (ids, data) => {
  await EventModel.update(data, { where: { id: ids } });
};

export const MatchingEvents = async (event, userid) => {
  const matchingEvents = await EventModel.findAll({
    where: {
      category: event.category,
      subCategory: event.subCategory,
      event_title: event.event_title,
      user_id: userid,
      status: 'active',
      [Op.and]: [
        { start_time: event.start_time }, // Direct comparison with start_time
        { end_time: event.end_time }, // Direct comparison with end_time
      ],
    },
    attributes: ['id', 'recurring'], // Fetch only the IDs for performance
    raw: true,
  });

  return matchingEvents;
};

const updateConflict = async (ids) => {
  if (ids?.length) {
    await EventModel.update({ conflict: false }, { where: { id: ids } });
  }
};

export const findAndEditOverlapEvents = async (body, type, user) => {
  if (type === 'selected') {
    for (const eachData of body.overlapping_events) {
      await updateConflict(eachData?.id);
    }
  }
  if (type === 'all') {
    for (const eachData of body.overlapping_events) {
      const events = await EventModel.findAll({
        where: {
          user_id: user.id,
          event_title: eachData.title,
          start_time: eachData.start_time,
          end_time: eachData.end_time,
        },
      });
      const recurringEventIds = events
        .filter((e) => !e.recurring)
        .map((e) => e.id);
      await updateConflict(recurringEventIds);
    }
  }
};

const incrementDate = (dateStr) =>
  moment(dateStr, 'YYYY-MM-DD').add(1, 'days').format('YYYY-MM-DD');

export const processEventsByday = (eventsData) => {
  const groupedEvents = [];

  eventsData.forEach((event) => {
    const eventDate = event.date;
    const startTime = moment(event.start_time, 'HH:mm:ss');
    const endTime = moment(event.end_time, 'HH:mm:ss');

    // If the event spans across midnight, split it into two
    if (endTime.isBefore(startTime)) {
      // First part: Ends at "23:59" on the same day
      const eventPart1 = {
        ...event,
        end_time: '23:59:00',
      };

      // Second part: Starts at "00:00" the next day
      const nextDate = incrementDate(eventDate);
      const eventPart2 = {
        ...event,
        date: nextDate,
        start_time: '00:00:00',
      };

      groupedEvents.push(eventPart1, eventPart2);
    } else {
      // If the event does not cross midnight, just add it normally
      groupedEvents.push(event);
    }
  });

  // Return the final sorted result inside an "events" object
  return {
    events: groupedEvents.sort((a, b) => moment(a.date).diff(moment(b.date))),
  };
};