<template>
    <div class="container">
      <!-- Timeline Header -->
      <div class="timeline-header" ref="timelineHeader">
        <!-- Time labels will be injected here via JavaScript -->
      </div>
  
      <!-- Grid Area with Horizontal Scroll -->
      <div class="grid-area-container">
        <div class="grid-area" ref="gridArea">
          <!-- Draggable and Resizable Boxes -->
          <div
            v-for="box in boxes"
            :key="box.id"
            class="draggable-box"
            :style="{ left: box.left + 'px', top: box.top + 'px', width: box.width + 'px' }"
            @mousedown="onBoxMouseDown($event, box)"
          >
            {{ box.label }}
            <div
              class="resize-handle left"
              @mousedown.stop.prevent="onResizeMouseDown($event, box, 'left')"
            ></div>
            <div
              class="resize-handle right"
              @mousedown.stop.prevent="onResizeMouseDown($event, box, 'right')"
            ></div>
          </div>
  
          <!-- Vertical guide line for resizing -->
          <div class="vertical-guide" ref="verticalGuide"></div>
          <!-- Time label for resizing -->
          <div class="time-label-resize" ref="timeLabelResize"></div>
          <!-- Current Time Line -->
          <div class="current-time-line" ref="currentTimeLine">
            <div class="current-time-label" ref="currentTimeLabel">Now</div>
          </div>
        </div>
      </div>
    </div>
  </template>
  
  <script>
  export default {
    name: "TimelineComponent",
    data() {
      return {
        // Define the draggable and resizable boxes
        boxes: [
          { id: "box1", label: "Box 1", left: 60, top: 20, width: 60 },
          { id: "box2", label: "Box 2", left: 180, top: 50, width: 60 },
        ],
        // References to handle dragging and resizing
        draggingBox: null,
        dragOffsetX: 0,
        dragOffsetY: 0,
        resizingBox: null,
        resizeDirection: null,
        initialBoxState: {},
        // Base start time to center the current time within the timeline
        baseStartTime: null,
        // Interval ID for updating the current time line
        currentTimeInterval: null,
      };
    },
    mounted() {
      this.initializeTimeline();
     // this.setupCurrentTimeLine();
      window.addEventListener("resize", this.onWindowResize);
    },
    beforeUnmount() {
      window.removeEventListener("resize", this.onWindowResize);
      if (this.currentTimeInterval) {
        clearInterval(this.currentTimeInterval);
      }
      // Clean up any lingering event listeners
      document.removeEventListener("mousemove", this.onBoxMouseMove);
      document.removeEventListener("mouseup", this.onBoxMouseUp);
      document.removeEventListener("mousemove", this.onResizeMouseMove);
      document.removeEventListener("mouseup", this.onResizeMouseUp);
    },
    methods: {
      /**
       * Initializes the timeline by setting the base start time
       * and creating the time labels every 5 minutes.
       */
      initializeTimeline() {
        this.calculateBaseStartTime();
        this.createTimelineHeaders();
      },
  
      /**
       * Calculates the base start time to center the current time
       * within the visible grid area.
       */
      calculateBaseStartTime() {
        const now = new Date();
        const gridAreaWidth = this.$refs.gridArea.offsetWidth;
        const totalMinutesVisible = gridAreaWidth / 10; // 10px per minute
        const halfVisibleMinutes = Math.floor(totalMinutesVisible / 2);
        this.baseStartTime = new Date(now.getTime());
        this.baseStartTime.setMinutes(now.getMinutes() - halfVisibleMinutes);
        this.baseStartTime.setSeconds(0, 0); // Reset seconds and milliseconds
        console.log("Calculated Base Start Time:", this.baseStartTime);
      },
  
      /**
       * Creates time labels every 5 minutes based on the base start time.
       */
      createTimelineHeaders() {
        const timelineHeader = this.$refs.timelineHeader;
        const gridAreaWidth = this.$refs.gridArea.offsetWidth;
        const numberOfMarkers = Math.ceil(gridAreaWidth / (10 * 5)) + 1; // Every 5 minutes
  
        for (let i = 0; i < numberOfMarkers; i++) {
          const markerTime = new Date(this.baseStartTime.getTime());
          markerTime.setMinutes(this.baseStartTime.getMinutes() + i * 5);
  
          const timeLabel = document.createElement("div");
          timeLabel.classList.add("time-label");
          timeLabel.style.left = `${i * 5 * 10}px`; // 10px per minute
          timeLabel.textContent = markerTime.toLocaleTimeString([], {
            hour: "2-digit",
            minute: "2-digit",
          });
          timelineHeader.appendChild(timeLabel);
        }
      },
  
      /**
       * Handles the mousedown event on a draggable box to initiate dragging.
       * @param {MouseEvent} e - The mousedown event.
       * @param {Object} box - The box data object.
       */
       onBoxMouseDown(e, box) {
    this.draggingBox = box;
    this.dragOffsetX = e.clientX - box.left;
    this.dragOffsetY = e.clientY - box.top;

    // Use arrow function to maintain 'this' context
    this.handleBoxMouseMove = (e) => {
      this.onBoxMouseMove(e);
    };
    this.handleBoxMouseUp = () => {
      this.onBoxMouseUp();
    };

    document.addEventListener("mousemove", this.handleBoxMouseMove);
    document.addEventListener("mouseup", this.handleBoxMouseUp);
},

onBoxMouseMove(e) {
    if (!this.draggingBox) return;
    const gridAreaRect = this.$refs.gridArea.getBoundingClientRect();

    // Adjust mouse position relative to the grid area
    let newLeft = e.clientX - gridAreaRect.left - this.dragOffsetX;
    let newTop = e.clientY - gridAreaRect.top - this.dragOffsetY;

    // Snap to grid (10px per minute)
    newLeft = this.snapToGrid(newLeft, 10);
    newTop = this.snapToGrid(newTop, 5);

    // Boundaries
    newLeft = Math.max(0, Math.min(this.$refs.gridArea.offsetWidth - this.draggingBox.width, newLeft));
    newTop = Math.max(0, Math.min(this.$refs.gridArea.offsetHeight - this.draggingBox.height, newTop));

    // Update box position
    this.draggingBox.left = newLeft;
    this.draggingBox.top = newTop;
},

  
      /**
       * Handles the mouseup event to stop dragging.
       */
      onBoxMouseUp() {
        this.draggingBox = null;
        document.removeEventListener("mousemove", this.handleBoxMouseMove);
        document.removeEventListener("mouseup", this.handleBoxMouseUp);
      },
  
      /**
       * Handles the mousedown event on a resize handle to initiate resizing.
       * @param {MouseEvent} e - The mousedown event.
       * @param {Object} box - The box data object.
       * @param {string} direction - The resize direction ('left' or 'right').
       */
      onResizeMouseDown(e, box, direction) {
        this.resizingBox = box;
        this.resizeDirection = direction;
        this.initialBoxState = { ...box };
        this.startX = e.clientX;
  
        // Show the vertical guide and time label
        this.$refs.verticalGuide.style.display = "block";
        this.$refs.timeLabelResize.style.display = "block";
  
        // Initial position of the vertical guide
        if (direction === "right") {
          const currentRight = box.left + box.width;
          this.$refs.verticalGuide.style.left = `${currentRight}px`;
          this.$refs.timeLabelResize.textContent = this.getTimeFromPosition(
            currentRight / 10
          );
          this.$refs.timeLabelResize.style.left = `${currentRight + 5}px`; // 5px offset
          this.$refs.timeLabelResize.style.top = `${
            box.top + box.height / 2
          }px`;
        } else if (direction === "left") {
          const currentLeft = box.left;
          this.$refs.verticalGuide.style.left = `${currentLeft}px`;
          this.$refs.timeLabelResize.textContent = this.getTimeFromPosition(
            currentLeft / 10
          );
          // Position the time label to the left of the guide
          this.$refs.timeLabelResize.style.left = `${
            currentLeft - 5 - this.$refs.timeLabelResize.offsetWidth
          }px`;
          this.$refs.timeLabelResize.style.top = `${
            box.top + box.height / 2
          }px`;
        }
  
        // Use arrow functions to maintain 'this' context
        this.handleResizeMouseMove = (e) => {
          this.onResizeMouseMove(e);
        };
        this.handleResizeMouseUp = () => {
          this.onResizeMouseUp();
        };
  
        document.addEventListener("mousemove", this.handleResizeMouseMove);
        document.addEventListener("mouseup", this.handleResizeMouseUp);
      },
  
      /**
       * Handles the mousemove event during resizing.
       * @param {MouseEvent} e - The mousemove event.
       */
      onResizeMouseMove(e) {
        if (!this.resizingBox) return;
        const deltaX = e.clientX - this.startX;
        let newWidth = this.resizingBox.width;
        let newLeft = this.resizingBox.left;
  
        if (this.resizeDirection === "right") {
          newWidth = this.snapToGrid(this.initialBoxState.width + deltaX, 10);
          newWidth = Math.max(10, newWidth); // Minimum width of 1 minute (10px)
        } else if (this.resizeDirection === "left") {
          newWidth = this.snapToGrid(this.initialBoxState.width - deltaX, 10);
          newWidth = Math.max(10, newWidth); // Minimum width of 1 minute (10px)
          newLeft = this.snapToGrid(
            this.initialBoxState.left + deltaX,
            10
          );
        }
  
        // Boundaries
        if (newLeft < 0) {
          newLeft = 0;
          newWidth = this.snapToGrid(this.initialBoxState.left + this.initialBoxState.width, 10);
        }
        if (newLeft + newWidth > this.$refs.gridArea.offsetWidth) {
          newWidth = this.snapToGrid(this.$refs.gridArea.offsetWidth - newLeft, 10);
        }
  
        // Update box dimensions
        this.resizingBox.width = newWidth;
        this.resizingBox.left = newLeft;
  
        // Update the vertical guide position
        const guidePosition =
          this.resizeDirection === "right" ? newLeft + newWidth : newLeft;
        this.$refs.verticalGuide.style.left = `${guidePosition}px`;
  
        // Update the time label
        const displayTime = this.getTimeFromPosition(guidePosition / 10);
        this.$refs.timeLabelResize.textContent = displayTime;
  
        if (this.resizeDirection === "right") {
          this.$refs.timeLabelResize.style.left = `${guidePosition + 5}px`; // 5px offset
        } else if (this.resizeDirection === "left") {
          this.$refs.timeLabelResize.style.left = `${
            guidePosition - 5 - this.$refs.timeLabelResize.offsetWidth
          }px`; // 5px left offset
        }
        this.$refs.timeLabelResize.style.top = `${
          this.resizingBox.top + this.resizingBox.height / 2
        }px`;
      },
  
      /**
       * Handles the mouseup event to stop resizing.
       */
      onResizeMouseUp() {
        this.resizingBox = null;
        this.resizeDirection = null;
        this.initialBoxState = {};
        this.$refs.verticalGuide.style.display = "none";
        this.$refs.timeLabelResize.style.display = "none";
  
        document.removeEventListener("mousemove", this.handleResizeMouseMove);
        document.removeEventListener("mouseup", this.handleResizeMouseUp);
      },
      generateTimelineLabels() {
    const timelineHeader = this.$refs.timelineHeader; // Reference to the timeline header element
    timelineHeader.innerHTML = ''; // Clear any existing labels

    const gridAreaWidth = this.$refs.gridArea.offsetWidth; // Get the width of the grid area
    const totalMinutesVisible = Math.ceil(gridAreaWidth / 10); // Define how many minutes to display
    const halfVisibleMinutes = Math.floor(totalMinutesVisible / 2);
    
    const startTime = new Date(); // Get the current time
    startTime.setMinutes(startTime.getMinutes() - halfVisibleMinutes); // Calculate the starting time

    // Loop to create labels
    for (let i = 0; i <= totalMinutesVisible; i++) {
      const markerTime = new Date(startTime.getTime());
      markerTime.setMinutes(startTime.getMinutes() + i); // Increment minutes for each label

      const timeLabel = document.createElement("div");
      timeLabel.classList.add("time-label");
      timeLabel.style.left = `${i * 10}px`; // Position the label based on the index
      timeLabel.textContent = markerTime.toLocaleTimeString([], {
        hour: "2-digit",
        minute: "2-digit",
      });
      timelineHeader.appendChild(timeLabel); // Append the label to the timeline header
    }
  },
      /**
       * Sets up the current time line and updates its position every minute.
       */
      setupCurrentTimeLine() {
        this.generateTimelineLabels();
        this.updateCurrentTimeLine();
        this.currentTimeInterval = setInterval(this.updateCurrentTimeLine, 60000); // Update every minute
      },
  
      /**
       * Updates the current time line position based on the current time.
       */
      updateCurrentTimeLine() {
        const now = new Date();
        const totalMinutes = now.getHours() * 60 + now.getMinutes();
        const differenceInMinutes =
          (now.getHours() * 60 + now.getMinutes()) -
          (this.baseStartTime.getHours() * 60 + this.baseStartTime.getMinutes());
        const currentX = this.snapToGrid(differenceInMinutes * 10, 10); // 10px per minute
  
        console.log(`Current X Position: ${currentX}px`);
  
        // Define the start and end times based on gridArea
        const totalGridWidth = this.$refs.gridArea.offsetWidth; // in pixels
  
        if (currentX >= 0 && currentX <= totalGridWidth) {
          this.$refs.currentTimeLine.style.display = "block";
          this.$refs.currentTimeLine.style.left = `${currentX}px`;
  
          // Update label
          const currentTime = now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
          this.$refs.currentTimeLabel.textContent = `Now - ${currentTime}`;
  
          // Position the label above the timeline header
          this.$refs.currentTimeLabel.style.left = `${currentX + 5}px`; // 5px offset to the right
          this.$refs.currentTimeLabel.style.top = `-${this.$refs.timelineHeader.offsetHeight}px`; // Position above the timeline header
        } else {
          // Hide the line if current time is outside the container's timeline range
          this.$refs.currentTimeLine.style.display = "none";
        }
      },
  
      /**
       * Snaps a given value to the nearest grid size.
       * @param {number} value - The value to snap.
       * @param {number} gridSize - The grid size in pixels.
       * @returns {number} - The snapped value.
       */
      snapToGrid(value, gridSize) {
        return Math.round(value / gridSize) * gridSize;
      },
  
      /**
       * Handles window resize events to recalculate the timeline and current time line.
       */
      onWindowResize() {
        // Recalculate base start time
        this.calculateBaseStartTime();
        // Recreate timeline headers
        this.createTimelineHeaders();
        // Reposition current time line
        this.updateCurrentTimeLine();
      },
  
      /**
       * Converts grid position in minutes to a formatted time string.
       * @param {number} minutes - Minutes since midnight.
       * @returns {string} - Formatted time string.
       */
      getTimeFromPosition(minutes) {
        const date = new Date();
        date.setHours(0, 0, 0, 0); // Start at midnight
        date.setMinutes(minutes);
        return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
      },
    },
  };
  </script>
  
  <style scoped>
.container {
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #fff;
  border: 1px solid #ddd;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Timeline Header */
.timeline-header {
  position: relative;
  height: 40px;
  background-color: #fafafa;
  border-bottom: 1px solid #ddd;
  display: flex;
  align-items: center;
  padding-left: 10px;
  box-sizing: border-box;
  flex-shrink: 0;
  white-space: nowrap;
  overflow-x: auto;
}

.timeline-header .time-label {
  position: absolute;
  top: 5px;
  font-size: 12px;
  color: #555;
  white-space: nowrap;
  transform: translateX(-50%);
  pointer-events: none;
}

/* Grid Area with Horizontal Scroll */
.grid-area-container {
  position: relative;
  flex-grow: 1;
  overflow-x: auto;
  overflow-y: hidden;
}

.grid-area {
  position: relative;
  height: 100%;
  width: 14400px; /* 24 hours * 60 minutes * 10px per minute */
  background-image:
    linear-gradient(to right, #ddd 1px, transparent 1px),
    linear-gradient(to bottom, #ddd 1px, transparent 1px),
    linear-gradient(to right, #aaa 2px, transparent 2px),
    linear-gradient(to bottom, #aaa 2px, transparent 2px);
  background-size: 10px 10px, 10px 10px, 50px 10px, 50px 10px;
}

/* Draggable and Resizable Boxes */
.draggable-box {
  position: absolute;
  background-color: #4caf50;
  color: #fff;
  padding: 5px;
  cursor: move;
  box-sizing: border-box;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border: 1px solid #000;
}

.draggable-box .resize-handle {
  width: 8px;
  background-color: #555;
  cursor: col-resize;
}

.resize-handle.left {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
}

.resize-handle.right {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
}

/* Vertical Guide Line for Resizing */
.vertical-guide {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background-color: red;
  display: none;
}

/* Time Label for Resizing */
.time-label-resize {
  position: absolute;
  background-color: #fff;
  padding: 2px 5px;
  border: 1px solid #ddd;
  display: none;
}

/* Current Time Line */
.current-time-line {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background-color: blue;
  display: none;
}

.current-time-label {
  position: absolute;
  background-color: #fff;
  padding: 2px 5px;
  border: 1px solid #ddd;
}

</style>
but the time line labels are not generating also the realtime current vertical line is not proper. also find out other issues in it and fix them all