JSFiddle - React, Tailwind, and code Playground

by neonDog

JavaScript

import { isWithinInterval, addDays, addMinutes, isSameDay, setHours, setMinutes, setSeconds } from 'https://esm.run/date-fns';

function nextDayOfWeek(date, dayOfWeek) {
  date = new Date(date.getTime());
  date.setDate(date.getDate() + (dayOfWeek + 7 - date.getDay()) % 7);
  return date;
}

const getNextDesirableDates = function (topTimes, date) {
	if (!date) date = new Date();
  const desiredTimes = [];
  for (let i=0, l=topTimes.length; i < l; i++) {
    const time = topTimes[i];
    let when = nextDayOfWeek(date, time[0]);
    if (isSameDay(date, when)) {
    	//If this hour is not in the future, must add 7 days (schedule 1 week later)
      if (date.getHours() > time[1]) {
      	when = addDays(when, 7);
      }
    }
    when = setHours(when, time[1]);
    when = setMinutes(when, time[2]);
    desiredTimes.push(when);
  }
  return desiredTimes;
};

const findAvailableDate = function(results, desired) {
  //Loop desired dates
  for (let i=0, l=desired.length; i < l; i++) {
    //Can we find one that doesn't conflict with results?
    if (results.findIndex(date => isSameDay(date, desired[i])) === -1) {
      return desired[i];
    }
  }
};


const getNextAvailableDate = function(results, topTimes) {
	let desiredTimes;
  let finalDate;
  let lastDate = new Date();
	
  while (!finalDate) { //This will loop forever until it finds a free date
    desiredTimes = getNextDesirableDates(topTimes, lastDate).sort((a, b) => a - b);
    finalDate = findAvailableDate(results, desiredTimes);

    if (!finalDate) {
      //If we still have conflicts, we need to get more desired dates. Save the last one we'll use it to page
      lastDate = addDays(desiredTimes[desiredTimes.length-1], 1);
    }
  }
  
  return finalDate;
  
}

const topTimes = [
	[0,1,30], //Sunday
  [1,9,20], //Monday
  [3,15,44], //Wednesday
  [5,12,30] //Friday
];

//Feb 1st Mon
//Feb 2nd Tue
//Feb 3rd Wed

//These are already scheduled times
const results = [new Date('2021-02-01 09:00'), new...