2

In the code below, I have a weekly calendar displayed as shown below:

enter image description here When I drag and drop these draggable boxes from one date to another, for example, if I drag the Morooka from Tuesday 9th to Sunday 7th in column 8th, it will drop it into column 1st. The same happens with other draggable boxes; if I drop them from the 9th in the 1st column to the 10th in the 6th column, they still end up in column 1.

I expect the dragbox to be dropped into the column where I release it. So if I drop it into column 5, it should stay in column 5. However, currently, everything is being dropped into column 1. Is there any way to fix this?

I tried printing in my console.log when the drag box is dropped, and it seems to identify the correct column and print the date and timeslot correctly. However, it still doesn't drop into the correct column

enter image description here

    import React, { useState, useRef } from "react";
import { startOfWeek, endOfWeek, format, addDays, subDays, set } from "date-fns";
import axios from 'axios';

import { fetchCustomers } from '../../api/customer';
const DraggableBox = ({
  day,
  name: initialName,
  suburb: initialSuburb,
  calendarData,
  setCalendarData,
  changeName,
  setChangeName,
  changeSuburb,
  setChangeSuburb,
  setChangeDay,
  setChangeData,
  orderData,
  setOrderData,
  timeSlotIndex,
}) => {
  const [showTooltip, setShowTooltip] = useState(false);
  const boxRef = useRef(null);

  const filteredOrders = orderData.order.filter(
    (order) => new Date(order.RequiredDateAndTime).getDate() === parseInt(day)
  );

  const handleDragStart = (e) => {
    setShowTooltip(false);
    setChangeData(filteredOrders[timeSlotIndex]);
    boxRef.current.classList.add("dragging");
  };

  const handleDragEnd = () => {
    boxRef.current.classList.remove("dragging");
  };

  const handleDragOver = (e) => {
    e.preventDefault();
  };
     

  return (
    <div
      className="drag-box"
      ref={boxRef}
      draggable
      onDragStart={handleDragStart}
      onDragEnd={handleDragEnd}
      onDragOver={handleDragOver}
      onMouseEnter={() => setShowTooltip(true)}
      onMouseLeave={() => setShowTooltip(false)}
      style={{
        width: "70px",
        height: "70px",
        textAlign: "center",
        backgroundColor: "#F5F5F5",
        color: "#333333",
        marginTop: "0px",
        position: "relative",
        cursor: "move",
        fontSize: "10px",
      }}
      data-day={day}
      data-name={initialName}
    >
      <div>
        {filteredOrders[timeSlotIndex] && (
          <div key={timeSlotIndex}>
            <p>{filteredOrders[timeSlotIndex].DelCity}</p>
          </div>
        )}
      </div>
    </div>
  );
};




const RunSheetPreview = ({
orderData,  
setOrderData,
  selectedDate,

  initialName,
  initialSuburb,
  setShowTooltip,
  }) => {
  const boxRef = useRef(null);
  const [changeName, setChangeName] = useState("");
  const [changeDay, setChangeDay] = useState(null);
  const [changeData, setChangeData] = useState(null);
 
  const [changeSuburb, setChangeSuburb] = useState(null);
  const [selectedDateState, setSelectedDate] = useState(new Date());
  const handleNextWeek = () => {

    setSelectedDate(addDays(selectedDateState, 7));
  };

  const handlePreviousWeek = () => {
    setSelectedDate(subDays(selectedDateState, 7));
  };
  const formatDate = (date) => {
  const dateVal = new Date(date);
  const year = dateVal.getFullYear();
  const month = String(dateVal.getMonth() + 1).padStart(2, "0");
  const day = String(dateVal.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
};
const handleDragOver = (e) => {
  e.preventDefault();
};

const handleDrop = (e, timeSlotIndex, dayOfMonth, year, month) => {
  e.preventDefault();
  e.stopPropagation();
  
  let d = { ...changeData };
  d = new Date(d.OrderDate);
  
  const number = e.target.tabIndex.toString();
  console.log("Number:", number);
  
  const currentYear = e.target.getAttribute('data-year');
  let currentMonth = e.target.getAttribute('data-month');
  
  // Log the day, month, and year
  console.log("Day:", dayOfMonth);
  
  // Format the month with leading zero if it's a single digit
  currentMonth = currentMonth.padStart(2, "0");
  console.log("Month:", currentMonth);
  
  console.log("Year:", currentYear);
  
  // Format the day with leading zero
  const formattedDay = dayOfMonth.toString().padStart(2, "0");
  
  const formattedDate = `${currentYear}-${currentMonth}-${formattedDay}`;
  console.log("Formatted Date:", formattedDate);
  
  d.setDate(dayOfMonth);
  d.setMonth(currentMonth);
  d.setFullYear(currentYear);
  d = d.toISOString();
  
  let fi = { ...changeData };
  fi.OrderDate = d;
  fi.RequiredDateAndTime = d;
  
  let newDate = new Date(changeData.OrderDate);
  newDate = new Date(currentYear, currentMonth, dayOfMonth, newDate.getHours(), newDate.getMinutes(), newDate.getSeconds());

  // Update the order data
  let newOrderData = orderData.order.filter(item => item !== changeData);

  // Update the order date for the dragged item to its new position
  changeData.OrderDate = newDate.toISOString();
  changeData.RequiredDateAndTime = newDate.toISOString();
  const timeSlot = `${timeSlotIndex + 1}`;
  console.log("Updated changeData:", changeData); // Log the updated changeData

  console.log("Time Slot:", timeSlot);

  // Push the updated dragged item to the new position
  newOrderData.push({ ...changeData, timeSlot });
  console.log("Updated newOrderData:", newOrderData);



  setCalendarData(newOrderData);
};


// Remove the filter based on selectedDate
const dateStrings = orderData.order.map((listItem) => {
  const date = new Date(listItem.RequiredDateAndTime);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
});



const initialCalendarData = orderData.order.map((order, index) => {
  const name = order.DelCity;
  const suburb = order.DelPostalCode;
  return {
    day: dateStrings[index], // Use dateStrings array here
    name,
    suburb,
    initialName: name, // Assign the value to initialName
    initialSuburb: suburb, // Assign the value to initialSuburb
  };
});
const [calendarData, setCalendarData] = useState(initialCalendarData);
 
const generateCalendar = () => {
  const startDate = startOfWeek(selectedDateState, { weekStartsOn: 0 });
  const endDate = endOfWeek(selectedDateState, { weekStartsOn: 0 });

  const rows = [];
  let day = startDate;

  while (day <= endDate) {
    const formattedDate = formatDate(day);
    const dateObject = new Date(formattedDate);
    const year = dateObject.getFullYear(); // Extract year (e.g., 2024)
    const month = dateObject.getMonth() + 1;
    const dayName = format(day, "EEEE");
    const dayOfMonth = format(day, "d");

    const isTargetDate = dateStrings.includes(formattedDate);

    const ordersForDay = orderData.order.filter((order) => {
      const orderDate = new Date(order.RequiredDateAndTime).getDate();
      return orderDate === parseInt(dayOfMonth);
    });

    // Find the first available time slot for the current day
    const firstAvailableSlot = ordersForDay.findIndex(
      (order) => !order.isAssigned
    );

    const row = (
      <tr key={day}>
        <td className="calendar-cell">
          <div className="day-name">{dayName}</div>
          <div className="day-number">{dayOfMonth}</div>
        </td>
        {[...Array(15)].map((_, index) => (
          <td
            key={index}
            className={`grid-cell`}
            data-has-data={ordersForDay[index] ? "true" : "false"}
          >
            {/* Render DraggableBox only if there is an order for this column */}
            {ordersForDay[index] && (
              <DraggableBox
                day={parseInt(dayOfMonth)}
                initialName={ordersForDay[index].DelCity}
                initialSuburb={ordersForDay[index].DelPostalCode}
                calendarData={calendarData}
                setCalendarData={setCalendarData}
                changeName={changeName}
                setOrderData={setOrderData} // Pass the setOrderData function
                setChangeName={setChangeName}
                changeDay={changeDay}
                setChangeDay={setChangeDay}
                changeSuburb={changeSuburb}
                setChangeSuburb={setChangeSuburb}
                setChangeData={setChangeData}
                orderData={orderData}
                timeSlotIndex={index}
              />
            )}
            {!ordersForDay[index] && (
              <p
                style={{
                  height: "40px",
                  width: "60px",
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                }}
                onDragOver={handleDragOver}
                onDrop={(e) =>
                  handleDrop(
                    e,
                    index,
                    parseInt(dayOfMonth),
                    year,
                    month
                  )
                }
                tabIndex={dayOfMonth}
                data-year={year}
                data-month={month}
              ></p>
            )}
          </td>
        ))}
      </tr>
    );

    rows.push(row);

    day = addDays(day, 1);
  }

  return rows;
};

      
     
return (
  <div>
  <div className="delivery-schedule-heading">
      <h2 style={{ color: "", margin: "0 0px 0 0px", textAlign: "left" }}></h2>
      <div style={{ textAlign: "left", margin: "0 0px 0 0px" }}>
  <h2 style={{ margin: 0, color: "#08315b", fontWeight: "bold" }}>
    Delivery Schedule : {format(startOfWeek(selectedDateState), "MMMM d")} -{" "}
    {format(endOfWeek(selectedDateState), "MMMM d, yyyy")}
  </h2>
</div>

      <div style={{ textAlign: "right" }}>
          {/* Button for moving to the previous week */}
          <button onClick={handlePreviousWeek}>&lt; Previous Week</button>
          {/* Button for moving to the next week */}
          <button onClick={handleNextWeek}>Next Week &gt;</button>
        </div>
    </div>
  
<div className="calendar-container">
<table className="calendar-table">
<thead>
<tr>
<th>{/* Empty header cell for labels */}</th>

{[...Array(15)].map((_, index) => (
<th key={index}>{index + 1}</th>
))}

</tr>
</thead>
<tbody>
{generateCalendar()}
</tbody>
</table>
</div>
</div>
);
};

export default RunSheetPreview;
2
  • It looks like you didn't include any code to actually handle the drop—unless that's what your changeData code is supposed to do. If that's it, you likely just need to debug that. Usually, I'd expect to see something like e.dataTransfer.setData("text", e.currentTarget.id) in the handleDrop function. This probably won't make a difference with you current code, but in general, it's best to use e.currentTarget, instead of e.target. The currentTarget always refers to the element that has the event listener. target refers to the child of the currentTarget that the cursor is on.
    – andrilla
    2 days ago
  • Oh, and <table> related elements are very outdated and can be very challenging to work with. You might want to look into using <div> elements with table related role attributes. Do research first to know whether changing this will benefit you, but usually when I've worked with <table> elements, I end up needing to switch to <div> elements to accomplish my goals, as it related to CSS and JavaScript.
    – andrilla
    2 days ago

1 Answer 1

0

The problem seems to be that you're using ordersForDay index to identify the time slot, instead of using your own timeSlot property.

When you filter an array in javascript, it doesn't maintain it's original indexes. That's why all the order end up "grouped together" in the starting columns, instead of leaving empty spaces as you expect.

Instead of filtering by day, like you did in your code:

const ordersForDay = orderData.order.filter((order) => {
    const orderDate = new Date(order.RequiredDateAndTime).getDate();
    return orderDate === parseInt(dayOfMonth);
});

You could filter by time slot, inside the row creation:

{[...Array(15)].map((_, index) => {
    const ordersForTimeSlot = orderData.order.filter((order) => {
        const orderDate = new Date(order.RequiredDateAndTime).getDate();
        return orderDate === parseInt(dayOfMonth) && order.timeSlot == index;
    });
    return (<td ...

Then you can just check if there is an order in that time slot or not, to show it as a DraggableBox:

{ordersForTimeSlot[0] && (
    <DraggableBox ...

This code is not tested, but I hope this can help point you at the right direction!

New contributor
Cristóbal Peñaloza is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.