From a6bd46ea9a87573f659ea8f4e832e6927a095cbf Mon Sep 17 00:00:00 2001
From: Kritkhanin Anantakul <65160144@go.buu.ac.th>
Date: Fri, 28 Feb 2025 23:02:23 +0700
Subject: [PATCH] roll back fix

---
 src/components/GanttChart/GanttChart.vue      | 264 ++++++++++++++----
 src/components/GanttChart/GanttPagination.vue | 107 -------
 src/components/GanttChart/OrderItem.vue       | 101 -------
 3 files changed, 212 insertions(+), 260 deletions(-)
 delete mode 100644 src/components/GanttChart/GanttPagination.vue
 delete mode 100644 src/components/GanttChart/OrderItem.vue

diff --git a/src/components/GanttChart/GanttChart.vue b/src/components/GanttChart/GanttChart.vue
index 4da3f5e..054f5d9 100644
--- a/src/components/GanttChart/GanttChart.vue
+++ b/src/components/GanttChart/GanttChart.vue
@@ -12,36 +12,32 @@
 
     <!-- Rows: เครื่องจักรแต่ละตัว -->
     <div class="rows">
-      <div
-        v-for="machine in machines"
-        :key="machine.id"
-        class="row"
-        @dragover.prevent="onDragOver"
-        @drop="onDrop($event, machine.name)"
-      >
+      <div v-for="machine in machines" :key="machine.id" class="row" @dragover.prevent="onDragOver($event)"
+        @drop="onDrop($event, machine.name)">
         <div class="machine-label">
           {{ machine.name }}
         </div>
         <div class="row-timeline">
           <!-- เส้นแนวตั้ง (Grid Lines) -->
+          <div v-for="hour in hours" :key="'line-' + hour" class="vertical-line" :style="getLineStyle(hour)"></div>
+
+          <!-- แสดง Order -->
           <div
-            v-for="hour in hours"
-            :key="'line-' + hour"
-            class="vertical-line"
-            :style="getLineStyle(hour)"
-          ></div>
-
-          <!-- แสดง Order โดยใช้ OrderItem Component -->
-          <OrderItem
             v-for="order in orders.filter(o => pages.includes(o.page) && o.page === currentPage && o.machine === machine.name)"
-            :key="order.id"
-            :order="order"
-            :draggingOrder="draggingOrder"
-            :getOrderStyle="getOrderStyle"
-            @resizeStart="onResizeStart"
-            @dragStart="onDragStart"
-            @dragEnd="onDragEnd"
-          />
+            :key="order.id" class="order" :class="{ 'faded': draggingOrder && draggingOrder.id === order.id }"
+            :style="getOrderStyle(order)">
+            <!-- Handle สำหรับ Resize ด้านซ้าย -->
+            <div class="resize-handle left" @mousedown="onResizeStart($event, order, 'left')"></div>
+
+            <!-- ส่วนกลางของ Order ใช้สำหรับลาก -->
+            <div class="order-content" draggable="true" @dragstart="onDragStart($event, order)"
+              @dragend="onDragEnd($event, order)">
+              {{ order.name }} ({{ order.start }} - {{ order.end }})
+            </div>
+
+            <!-- Handle สำหรับ Resize ด้านขวา -->
+            <div class="resize-handle right" @mousedown="onResizeStart($event, order, 'right')"></div>
+          </div>
         </div>
       </div>
     </div>
@@ -53,27 +49,28 @@
 
     <v-divider :thickness="7"></v-divider>
 
-    <!-- Pagination Component -->
-    <GanttPagination
-      :pages="pages"
-      :currentPage="currentPage"
-      @updateCurrentPage="updateCurrentPage"
-      @addPage="addPage"
-      @deletePage="deletePage"
-    />
+    <!-- Pagination -->
+    <div class="pagination">
+      <button
+        v-for="p in pages"
+        :key="p"
+        :class="['page-btn', { active: p === currentPage }]"
+        @click="currentPage = p"
+        @contextmenu.prevent="onPageRightClick(p, $event)"
+      >
+        {{ p }}
+        <!-- ปุ่ม Delete จะแสดงเฉพาะเมื่อคลิกขวาที่ปุ่มนั้น -->
+        <button v-if="pageToShowDelete === p" class="delete-btn" @click.stop="deletePage(p)">Delete</button>
+      </button>
+      <!-- ปุ่ม + สำหรับเพิ่มหน้าใหม่ -->
+      <button class="page-btn add-page" @click="addPage">+</button>
+    </div>
   </div>
 </template>
 
 <script>
-import OrderItem from './OrderItem.vue';
-import GanttPagination from './GanttPagination.vue';
-
 export default {
   name: 'GanttChart',
-  components: {
-    OrderItem,
-    GanttPagination
-  },
   data() {
     return {
       // เวลางาน
@@ -92,7 +89,7 @@ export default {
       // รายการ Order
       orders: [
         { page: 1, id: 1, name: 'Order 1', start: '13:00', end: '15:00', machine: 'MC1', color: 'blue' },
-        { page: 1, id: 2, name: 'Order 2', start: '09:00', end: '11:00', machine: 'MC2', color: 'red' },
+        { page: 1, id: 2, name: 'Order 2', start: '09:00', end: '11:00', machine: 'MC2' },
         { page: 2, id: 3, name: 'Order 3', start: '10:00', end: '12:00', machine: 'MC1' },
         { page: 2, id: 4, name: 'Order 4', start: '14:00', end: '16:00', machine: 'MC5' },
       ],
@@ -103,8 +100,8 @@ export default {
       resizingOrder: null,
       resizeDirection: null,
 
-      // Ghost Order
-      ghostOrder: null,
+      // Ghost Order (ตัวอย่างที่ขยับตามเม้าส์)
+      ghostOrder: null, 
       ghostStyle: {
         position: 'fixed',
         top: '0px',
@@ -121,9 +118,12 @@ export default {
         zIndex: 9999,
       },
 
-      // Pagination Data
+      // จัดการ Pagination
       pages: [1, 2],
       currentPage: 1,
+
+      // สำหรับการแสดงปุ่ม delete เมื่อคลิกขวาที่ page
+      pageToShowDelete: null,
     }
   },
   computed: {
@@ -135,62 +135,89 @@ export default {
     }
   },
   methods: {
+    // แปลงเลขชั่วโมงเป็น "HH:00"
     formatHour(hour) {
       return (hour < 10 ? '0' + hour : hour) + ':00';
     },
+
+    // สไตล์การวาง Order (ตำแหน่ง left, width, และ backgroundColor)
     getOrderStyle(order) {
       const startDecimal = this.timeToDecimal(order.start);
       const endDecimal = this.timeToDecimal(order.end);
       const timelineStart = this.timeToDecimal(this.startTime);
       const timelineEnd = this.timeToDecimal(this.endTime);
+
       const ratioStart = (startDecimal - timelineStart) / (timelineEnd - timelineStart);
       const ratioEnd = (endDecimal - timelineStart) / (timelineEnd - timelineStart);
+
+      const left = ratioStart * 100 + '%';
+      const width = (ratioEnd - ratioStart) * 100 + '%';
+
       return {
-        left: ratioStart * 100 + '%',
-        width: (ratioEnd - ratioStart) * 100 + '%',
+        left,
+        width,
         backgroundColor: order.color || '#4caf50'
       };
     },
+
+    // สไตล์ของเส้นแนวตั้ง (grid lines) ตามชั่วโมง
     getLineStyle(hour) {
-      const timelineStart = this.timeToDecimal(this.startTime);
-      const timelineEnd = this.timeToDecimal(this.endTime);
-      const ratio = (hour - timelineStart) / (timelineEnd - timelineStart);
+      const startDecimal = this.timeToDecimal(this.startTime);
+      const endDecimal = this.timeToDecimal(this.endTime);
+      const ratio = (hour - startDecimal) / (endDecimal - startDecimal);
       return {
         left: ratio * 100 + '%'
       };
     },
+
+    // แปลงเวลา "HH:MM" -> เลขทศนิยม (เช่น 9:30 => 9.5)
     timeToDecimal(timeStr) {
       const [hours, minutes] = timeStr.split(':').map(Number);
       return hours + minutes / 60;
     },
+
+    // แปลงเลขทศนิยม -> "HH:MM"
     decimalToTime(decimal) {
       const hours = Math.floor(decimal);
       const minutes = Math.round((decimal - hours) * 60);
       return (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes);
     },
     onDragEndGlobal() {
+      // ลบ Ghost Order ออกจาก UI
       this.ghostOrder = null;
       this.ghostStyle.display = 'none';
       this.draggingOrder = null;
+
+      // เอา event listener ออก (ไม่งั้นมันจะถูกเรียกทุกครั้งที่ drag)
       document.removeEventListener('dragend', this.onDragEndGlobal);
     },
+
+    // เริ่มลาก Order (DragStart)
     onDragStart(event, order) {
       this.draggingOrder = order;
       const rect = event.target.getBoundingClientRect();
-      this.dragOffset = rect.width / 2;
-      // ปิด preview ของ HTML5 Drag & Drop
+      this.dragOffset = event.target.getBoundingClientRect().width / 2;
+
+      // ปิด preview เริ่มต้นของ HTML5 Drag & Drop
       const emptyImg = document.createElement('img');
       emptyImg.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
       event.dataTransfer.setDragImage(emptyImg, 0, 0);
+
+      // สร้าง Ghost Order
       this.ghostOrder = { ...order };
       this.ghostStyle.display = 'block';
       this.ghostStyle.backgroundColor = order.color || '#4caf50';
+
       document.addEventListener('dragover', this.onDragOverGlobal);
       document.addEventListener('dragend', this.onDragEndGlobal);
     },
+
+    // ขณะลาก (DragOver) ที่ผูกใน template
     onDragOver(event) {
       event.preventDefault();
     },
+
+    // onDragOverGlobal - อัปเดตตำแหน่ง Ghost และเวลา
     onDragOverGlobal(event) {
       event.preventDefault();
       const rowTimeline = document.querySelector('.row-timeline');
@@ -198,28 +225,36 @@ export default {
       const timelineRect = rowTimeline.getBoundingClientRect();
       const timelineWidth = timelineRect.width;
       const offsetX = event.clientX - timelineRect.left;
+
       let ratio = offsetX / timelineWidth;
       ratio = Math.min(Math.max(ratio, 0), 1);
+      
       const timelineStart = this.timeToDecimal(this.startTime);
       const timelineEnd = this.timeToDecimal(this.endTime);
       let newStartDecimal = timelineStart + ratio * (timelineEnd - timelineStart);
+
       newStartDecimal = Math.round(newStartDecimal * 2) / 2;
+
       const duration = this.timeToDecimal(this.draggingOrder.end) - this.timeToDecimal(this.draggingOrder.start);
       let newEndDecimal = newStartDecimal + duration;
       if (newEndDecimal > timelineEnd) {
         newEndDecimal = timelineEnd;
         newStartDecimal = newEndDecimal - duration;
       }
+
       this.ghostOrder.start = this.decimalToTime(newStartDecimal);
       this.ghostOrder.end = this.decimalToTime(newEndDecimal);
+
       const snappedRatioStart = (newStartDecimal - timelineStart) / (timelineEnd - timelineStart);
       const snappedLeft = timelineRect.left + snappedRatioStart * timelineWidth;
+
       const snappedRatioEnd = (newEndDecimal - timelineStart) / (timelineEnd - timelineStart);
       const snappedWidth = (snappedRatioEnd - snappedRatioStart) * timelineWidth;
-      // คำนวณ snap ตามแนวตั้ง (เครื่องจักร)
+
       const rows = document.querySelectorAll('.row');
       let closestRow = null;
       let minDistance = Infinity;
+
       rows.forEach(row => {
         const rect = row.getBoundingClientRect();
         const distance = Math.abs(event.clientY - rect.top);
@@ -228,34 +263,46 @@ export default {
           closestRow = row;
         }
       });
+
       if (closestRow) {
         this.ghostOrder.machine = closestRow.querySelector('.machine-label').textContent.trim();
         this.ghostStyle.top = closestRow.getBoundingClientRect().top + 'px';
       }
+      
       this.ghostStyle.left = snappedLeft + 'px';
       this.ghostStyle.width = snappedWidth + 'px';
     },
+
+    // จบการลาก Order (DragEnd) => คำนวณตำแหน่งใหม่
     onDragEnd(event, order) {
       if (!this.ghostOrder) return;
+
       order.start = this.ghostOrder.start;
       order.end = this.ghostOrder.end;
       order.machine = this.ghostOrder.machine;
+
       document.removeEventListener('dragover', this.onDragOverGlobal);
       this.ghostOrder = null;
       this.ghostStyle.display = 'none';
       this.draggingOrder = null;
     },
+
+    // เมื่อปล่อย Drag บนเครื่องจักรใหม่ => เปลี่ยน machine ของ Order
     onDrop(event, newMachine) {
       event.preventDefault();
+      
       if (this.draggingOrder) {
         this.draggingOrder.start = this.ghostOrder.start;
         this.draggingOrder.end = this.ghostOrder.end;
         this.draggingOrder.machine = newMachine;
       }
+
       this.ghostOrder = null;
       this.ghostStyle.display = 'none';
       this.draggingOrder = null;
     },
+
+    // เริ่ม Resize (mousedown ที่ handle)
     onResizeStart(event, order, direction) {
       event.preventDefault();
       this.resizingOrder = order;
@@ -263,19 +310,26 @@ export default {
       document.addEventListener("mousemove", this.onResizing);
       document.addEventListener("mouseup", this.onResizeEnd);
     },
+
+    // ขณะ Resize
     onResizing(event) {
       if (!this.resizingOrder) return;
+
       const rowTimeline = document.querySelector('.row-timeline');
       if (!rowTimeline) return;
+
       const timelineRect = rowTimeline.getBoundingClientRect();
       const timelineWidth = timelineRect.width;
       const offsetX = event.clientX - timelineRect.left;
       let ratio = offsetX / timelineWidth;
       ratio = Math.min(Math.max(ratio, 0), 1);
+
       const timelineStart = this.timeToDecimal(this.startTime);
       const timelineEnd = this.timeToDecimal(this.endTime);
       let newTimeDecimal = timelineStart + ratio * (timelineEnd - timelineStart);
+
       newTimeDecimal = Math.round(newTimeDecimal * 2) / 2;
+
       if (this.resizeDirection === 'left') {
         if (newTimeDecimal >= this.timeToDecimal(this.resizingOrder.end)) return;
         this.resizingOrder.start = this.decimalToTime(newTimeDecimal);
@@ -284,19 +338,28 @@ export default {
         this.resizingOrder.end = this.decimalToTime(newTimeDecimal);
       }
     },
+
+    // จบการ Resize
     onResizeEnd() {
       this.resizingOrder = null;
       this.resizeDirection = null;
       document.removeEventListener("mousemove", this.onResizing);
       document.removeEventListener("mouseup", this.onResizeEnd);
     },
+
+    // เพิ่มหน้าใหม่
     addPage() {
       const newPage = this.pages.length + 1;
       this.pages.push(newPage);
     },
-    updateCurrentPage(page) {
-      this.currentPage = page;
+
+    // เมื่อคลิกขวาที่ปุ่ม page
+    onPageRightClick(page, event) {
+      event.preventDefault();
+      this.pageToShowDelete = page;
     },
+
+    // ลบหน้า
     deletePage(page) {
       const index = this.pages.indexOf(page);
       if (index !== -1) {
@@ -305,7 +368,21 @@ export default {
           this.currentPage = this.pages.length > 0 ? this.pages[0] : 1;
         }
       }
+      this.pageToShowDelete = null;
+    },
+
+    // ซ่อนปุ่ม delete เมื่อคลิกนอก page-btn
+    onDocumentClick(event) {
+      if (!event.target.closest('.page-btn')) {
+        this.pageToShowDelete = null;
+      }
     }
+  },
+  mounted() {
+    document.addEventListener('click', this.onDocumentClick);
+  },
+  beforeUnmount() {
+    document.removeEventListener('click', this.onDocumentClick);
   }
 }
 </script>
@@ -371,9 +448,92 @@ export default {
   background-color: #ddd;
 }
 
+.order {
+  position: absolute;
+  top: 10px;
+  height: 40px;
+  color: #fff;
+  border-radius: 20px;
+  user-select: none;
+  display: flex;
+  align-items: center;
+  z-index: 1;
+}
+
+.order-content {
+  flex: 1;
+  text-align: center;
+  line-height: 40px;
+  cursor: grab;
+  padding: 0 10px;
+}
+
+.resize-handle {
+  width: 5px;
+  height: 100%;
+  cursor: ew-resize;
+  position: absolute;
+}
+
+.resize-handle.left {
+  left: 0;
+  border-top-left-radius: 10px;
+  border-bottom-left-radius: 10px;
+}
+
+.resize-handle.right {
+  right: 0;
+  border-top-right-radius: 10px;
+  border-bottom-right-radius: 10px;
+}
+
+.order.faded {
+  opacity: 0.3;
+}
+
 .drag-ghost {
   box-sizing: border-box;
   text-align: center;
   pointer-events: none;
 }
+
+.pagination {
+  display: flex;
+  justify-content: flex-start;
+  align-items: center;
+  gap: 5px;
+  width: 100%;
+  margin-top: 10px;
+}
+
+.page-btn {
+  background: #ffffff;
+  border: 1px solid #ffffff;
+  padding: 5px 10px;
+  cursor: pointer;
+  font-size: 14px;
+  position: relative;
+}
+
+.page-btn.active {
+  background: #007bff;
+  color: #ffffff;
+}
+
+.page-btn.add-page {
+  font-weight: bold;
+  background: #ffffff;
+}
+
+/* สไตล์สำหรับปุ่ม Delete ที่แสดงเมื่อคลิกขวา */
+.delete-btn {
+  margin-left: 5px;
+  background-color: red;
+  color: #fff;
+  border: none;
+  padding: 2px 5px;
+  cursor: pointer;
+  border-radius: 3px;
+  font-size: 12px;
+}
 </style>
diff --git a/src/components/GanttChart/GanttPagination.vue b/src/components/GanttChart/GanttPagination.vue
deleted file mode 100644
index 97f3b7d..0000000
--- a/src/components/GanttChart/GanttPagination.vue
+++ /dev/null
@@ -1,107 +0,0 @@
-<template>
-    <div class="pagination">
-      <button
-        v-for="p in pages"
-        :key="p"
-        :class="['page-btn', { active: p === currentPage }]"
-        @click="$emit('updateCurrentPage', p)"
-        @contextmenu.prevent="onPageRightClick(p, $event)"
-      >
-        {{ p }}
-        <!-- แสดงปุ่ม Delete เมื่อคลิกขวาที่ปุ่มนั้น -->
-        <button
-          v-if="pageToShowDelete === p"
-          class="delete-btn"
-          @click.stop="deletePage(p)"
-        >
-          Delete
-        </button>
-      </button>
-      <!-- ปุ่ม + สำหรับเพิ่มหน้าใหม่ -->
-      <button class="page-btn add-page" @click="$emit('addPage')">+</button>
-    </div>
-  </template>
-  
-  <script>
-  export default {
-    name: 'GanttPagination',
-    props: {
-      pages: {
-        type: Array,
-        required: true
-      },
-      currentPage: {
-        type: Number,
-        required: true
-      }
-    },
-    data() {
-      return {
-        pageToShowDelete: null
-      }
-    },
-    methods: {
-      onPageRightClick(page, event) {
-        event.preventDefault();
-        this.pageToShowDelete = page;
-      },
-      deletePage(page) {
-        this.$emit('deletePage', page);
-        this.pageToShowDelete = null;
-      },
-      onDocumentClick(event) {
-        if (!event.target.closest('.page-btn')) {
-          this.pageToShowDelete = null;
-        }
-      }
-    },
-    mounted() {
-      document.addEventListener('click', this.onDocumentClick);
-    },
-    beforeUnmount() {
-      document.removeEventListener('click', this.onDocumentClick);
-    }
-  }
-  </script>
-  
-  <style scoped>
-  .pagination {
-    display: flex;
-    justify-content: flex-start;
-    align-items: center;
-    gap: 5px;
-    width: 100%;
-    margin-top: 10px;
-  }
-  
-  .page-btn {
-    background: #ffffff;
-    border: 1px solid #ffffff;
-    padding: 5px 10px;
-    cursor: pointer;
-    font-size: 14px;
-    position: relative;
-  }
-  
-  .page-btn.active {
-    background: #007bff;
-    color: #ffffff;
-  }
-  
-  .page-btn.add-page {
-    font-weight: bold;
-    background: #ffffff;
-  }
-  
-  .delete-btn {
-    margin-left: 5px;
-    background-color: red;
-    color: #fff;
-    border: none;
-    padding: 2px 5px;
-    cursor: pointer;
-    border-radius: 3px;
-    font-size: 12px;
-  }
-  </style>
-  
\ No newline at end of file
diff --git a/src/components/GanttChart/OrderItem.vue b/src/components/GanttChart/OrderItem.vue
deleted file mode 100644
index 4500653..0000000
--- a/src/components/GanttChart/OrderItem.vue
+++ /dev/null
@@ -1,101 +0,0 @@
-<template>
-    <div
-      class="order"
-      :class="{ 'faded': draggingOrder && draggingOrder.id === order.id }"
-      :style="getOrderStyle(order)"
-    >
-      <!-- Handle สำหรับ Resize ด้านซ้าย -->
-      <div class="resize-handle left" @mousedown="handleResizeStart('left', $event)"></div>
-  
-      <!-- ส่วนกลางของ Order ใช้สำหรับลาก -->
-      <div
-        class="order-content"
-        draggable="true"
-        @dragstart="handleDragStart($event)"
-        @dragend="handleDragEnd($event)"
-      >
-        {{ order.name }} ({{ order.start }} - {{ order.end }})
-      </div>
-  
-      <!-- Handle สำหรับ Resize ด้านขวา -->
-      <div class="resize-handle right" @mousedown="handleResizeStart('right', $event)"></div>
-    </div>
-  </template>
-  
-  <script>
-  export default {
-    name: 'OrderItem',
-    props: {
-      order: {
-        type: Object,
-        required: true
-      },
-      draggingOrder: {
-        type: Object,
-        default: null
-      },
-      getOrderStyle: {
-        type: Function,
-        required: true
-      }
-    },
-    methods: {
-      handleResizeStart(direction, event) {
-        // ส่ง event พร้อม order และทิศทางไปยัง parent
-        this.$emit('resizeStart', event, this.order, direction);
-      },
-      handleDragStart(event) {
-        this.$emit('dragStart', event, this.order);
-      },
-      handleDragEnd(event) {
-        this.$emit('dragEnd', event, this.order);
-      }
-    }
-  }
-  </script>
-  
-  <style scoped>
-  .order {
-    position: absolute;
-    top: 10px;
-    height: 40px;
-    color: #fff;
-    border-radius: 20px;
-    user-select: none;
-    display: flex;
-    align-items: center;
-    z-index: 1;
-  }
-  
-  .order-content {
-    flex: 1;
-    text-align: center;
-    line-height: 40px;
-    cursor: grab;
-    padding: 0 10px;
-  }
-  
-  .resize-handle {
-    width: 5px;
-    height: 100%;
-    cursor: ew-resize;
-    position: absolute;
-  }
-  
-  .resize-handle.left {
-    left: 0;
-    border-top-left-radius: 10px;
-    border-bottom-left-radius: 10px;
-  }
-  
-  .resize-handle.right {
-    right: 0;
-    border-top-right-radius: 10px;
-    border-bottom-right-radius: 10px;
-  }
-  
-  .faded {
-    opacity: 0.3;
-  }
-  </style>
-  
\ No newline at end of file
-- 
GitLab