From 238f40c9835bb4a1666bd5e7ac7c4dcc306e832e Mon Sep 17 00:00:00 2001
From: Kritkhanin Anantakul <65160144@go.buu.ac.th>
Date: Mon, 24 Mar 2025 15:00:46 +0700
Subject: [PATCH] fix queue color and cus name

---
 src/components/GanttChart/GanttChart.vue | 143 +++++++++++++++++------
 src/components/ProductionTargetTable.vue | 106 +++++++++++------
 2 files changed, 182 insertions(+), 67 deletions(-)

diff --git a/src/components/GanttChart/GanttChart.vue b/src/components/GanttChart/GanttChart.vue
index d30c1c8..fb9a322 100644
--- a/src/components/GanttChart/GanttChart.vue
+++ b/src/components/GanttChart/GanttChart.vue
@@ -63,7 +63,7 @@
                 @dragend="onDragEnd($event, item)"
                 @click.stop="openQueueDialog(item)"
               >
-                {{ item.orderID }} ({{ getTimeString(item.startTime) }} - {{ getTimeString(item.finishTime) }})
+                {{ item.label }} ({{ getTimeString(item.startTime) }} - {{ getTimeString(item.finishTime) }})
               </div>
               <!-- Handle สำหรับ Resize ด้านขวา -->
               <div class="resize-handle right" @mousedown="onResizeStart($event, item, 'right')"></div>
@@ -190,23 +190,45 @@ const ghostStyle = reactive({
   zIndex: 9999,
 });
 
+const originalLabelMap: Record<number, string> = {};
+const originalColorMap: Record<number, string> = {};
+
 // แปลงข้อมูล Queue จาก backend ให้เข้ากับรูปแบบที่ Gantt Chart ใช้
 const formattedQueues = computed(() => {
-  return queueStore.queues.map(q => ({
-    QueueID: q.QueueID,
-    orderID: q.order?.OrderID || "Unknown",
-    machineID: q.machine?.MachineID || 0,
-    machineName: q.machine?.name || "Unknown Machine",
-    startTime: q.startTime ? convertToLocalTime(q.startTime) : "Unknown",
-    finishTime: q.finishTime ? convertToLocalTime(q.finishTime) : "Unknown",
-    status: q.status || "Unknown",
-    bottleSize: q.bottleSize || "Unknown",
-    producedQuantity: q.producedQuantity || 0,
-    // เปลี่ยนจาก PageID เป็น pagenum
-    pageNumber: q.page?.pagenum || 0,
-  }));
+  return queueStore.queues.map(q => {
+    const label =
+      originalLabelMap[q.QueueID] ??
+      (q.QueueType?.QueueTypeID === 2 ? 'ผลิตเผื่อ' :
+       q.QueueType?.QueueTypeID === 3 ? 'เปลี่ยนขนาด' :
+       q.order?.customer?.name || 'Unknown');
+
+    const color = originalColorMap[q.QueueID] ?? getQueueColor(label);
+
+    return {
+      QueueID: q.QueueID,
+      orderID: q.order?.OrderID || "Unknown",
+      label,
+      color, // เพิ่มตรงนี้ไว้ใช้ใน getQueueStyle
+      machineID: q.machine?.MachineID || 0,
+      machineName: q.machine?.name || "Unknown Machine",
+      startTime: q.startTime ? convertToLocalTime(q.startTime) : "Unknown",
+      finishTime: q.finishTime ? convertToLocalTime(q.finishTime) : "Unknown",
+      status: q.status || "Unknown",
+      bottleSize: q.bottleSize || "Unknown",
+      producedQuantity: q.producedQuantity || 0,
+      pageNumber: q.page?.pagenum || 0,
+      QueueTypeID: q.QueueType?.QueueTypeID || 0,
+    };
+  });
 });
 
+
+
+function clearOriginalMaps() {
+  Object.keys(originalLabelMap).forEach((k) => delete originalLabelMap[+k]);
+  Object.keys(originalColorMap).forEach((k) => delete originalColorMap[+k]);
+}
+
 function convertToLocalTime(utcString: string): string {
   const date = new Date(utcString);
   date.setHours(date.getHours() + 7);
@@ -285,20 +307,23 @@ function filteredQueue(machineID: number) {
 }
 
 const colorMap: Record<string, string> = {};
-function getQueueColor(orderID: string | number): string {
-  const key = String(orderID);
-  if (key === "ผลิตเผื่อ") return "#FFC1C1";
-  if (key === "เปลี่ยนขนาด") return "#E1BEE7";
-  if (colorMap[key]) return colorMap[key];
+  function getQueueColor(label: string): string {
+  if (label === "ผลิตเผื่อ") return "#FFC1C1";
+  if (label === "เปลี่ยนขนาด") return "#E1BEE7";
+
+  if (colorMap[label]) return colorMap[label];
+
   const newColor = generateWaterPastel();
-  colorMap[key] = newColor;
+  colorMap[label] = newColor;
   return newColor;
 }
+
 function generateWaterPastel(): string {
   const waterHues = [180, 190, 200, 210, 220, 230, 240];
   const hue = waterHues[Math.floor(Math.random() * waterHues.length)];
   return `hsl(${hue}, 60%, 80%)`;
 }
+
 function getQueueStyle(item: any) {
   const start = getTimeString(item.startTime);
   const end = getTimeString(item.finishTime);
@@ -307,13 +332,11 @@ function getQueueStyle(item: any) {
   const totalHours = hours.value.length;
   const leftPercent = ((startDecimal - timeToDecimal(startTime.value)) / totalHours) * 100;
   const widthPercent = ((endDecimal - startDecimal) / totalHours) * 100;
-  const orderID = item.orderID || "Unknown";
-  const backgroundColor = getQueueColor(orderID);
-  console.log(`Queue ${item.QueueID}: orderID=${orderID}, color=${backgroundColor}`);
+
   return {
     left: `${leftPercent}%`,
     width: `${widthPercent}%`,
-    backgroundColor,
+    backgroundColor: item.color, // ✅ ใช้ field color
     color: '#333',
     borderRadius: '10px',
     textAlign: 'center',
@@ -326,6 +349,10 @@ function getQueueStyle(item: any) {
     boxShadow: '0px 4px 6px rgba(0, 0, 0, 0.1)',
   };
 }
+
+
+
+
 function getLineStyle(hour: number) {
   const totalHours = hours.value.length;
   const leftPercent = ((hour - timeToDecimal(startTime.value)) / totalHours) * 100;
@@ -350,12 +377,16 @@ function onDragStart(event: DragEvent, item: any) {
   const emptyImg = new Image();
   emptyImg.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
   event.dataTransfer?.setDragImage(emptyImg, 0, 0);
-  ghostQueue.value = { ...item };
+
+  // 🟢 Copy พร้อม label
+  ghostQueue.value = { ...item, label: item.label };
   ghostStyle.display = 'block';
-  ghostStyle.backgroundColor = getQueueColor(item.order?.OrderID || "Unknown");
+  ghostStyle.backgroundColor = getQueueColor(item.label); // เปลี่ยนจาก orderID เป็น label
+
   document.addEventListener('dragover', onDragOverGlobal);
   document.addEventListener('dragend', onDragEndGlobal);
 }
+
 function onDragOver(event: DragEvent) {
   event.preventDefault();
 }
@@ -430,51 +461,79 @@ function onDragEndGlobal() {
   draggingQueue.value = null;
   document.removeEventListener('dragend', onDragEndGlobal);
 }
+
 async function onDragEnd(event: DragEvent, item: any) {
   if (!ghostQueue.value) return;
-  console.log(`🔄 Updating Queue ${item.QueueID} after drag...`);
+
   item.startTime = ghostQueue.value.startTime;
   item.finishTime = ghostQueue.value.finishTime;
   const newMachineID = ghostQueue.value.machine?.MachineID || item.machineID;
+
+  // 🟡 Save original label & color
+  originalLabelMap[item.QueueID] = item.label;
+  originalColorMap[item.QueueID] = getQueueColor(item.label);
+
   await queueStore.updateQueue(item.QueueID, {
     startTime: item.startTime,
     finishTime: item.finishTime,
     MachineID: newMachineID,
   });
+
+  await queueStore.fetchQueues();
+  clearOriginalMaps();
+
   ghostQueue.value = null;
   ghostStyle.display = 'none';
   draggingQueue.value = null;
 }
+
+
+
 async function onDrop(event: DragEvent, newMachineID: number) {
   event.preventDefault();
   if (!draggingQueue.value || !ghostQueue.value) return;
-  console.log(`🔄 Dropped Queue ${draggingQueue.value.QueueID} to Machine ${newMachineID}`);
+
+  const queueID = draggingQueue.value.QueueID;
+
+  // 🟡 Save label & color
+  originalLabelMap[queueID] = draggingQueue.value.label;
+  originalColorMap[queueID] = getQueueColor(draggingQueue.value.label);
+
   draggingQueue.value.startTime = ghostQueue.value.startTime;
   draggingQueue.value.finishTime = ghostQueue.value.finishTime;
   draggingQueue.value.machineID = newMachineID;
+
   try {
-    await queueStore.updateQueue(draggingQueue.value.QueueID, {
+    await queueStore.updateQueue(queueID, {
       startTime: draggingQueue.value.startTime,
       finishTime: draggingQueue.value.finishTime,
       MachineID: newMachineID,
     });
-    console.log(`✅ Queue ${draggingQueue.value.QueueID} updated to Machine ${newMachineID}`);
+
+    await queueStore.fetchQueues();
+    clearOriginalMaps();
+
+    delete originalLabelMap[queueID];
+    delete originalColorMap[queueID];
   } catch (error) {
     console.error(`❌ Error updating queue:`, error);
   }
+
   ghostQueue.value = null;
   ghostStyle.display = 'none';
   draggingQueue.value = null;
 }
 
+
 function onResizeStart(event: MouseEvent, item: any, direction: string) {
   event.preventDefault();
-  ghostQueue.value = { ...item };
+  ghostQueue.value = { ...item, label: item.label }; // 🟢 คง label เดิมไว้
   resizingQueue.value = item;
   resizeDirection.value = direction;
   document.addEventListener('mousemove', onResizing);
   document.addEventListener('mouseup', onResizeEnd);
 }
+
 function onResizing(event: MouseEvent) {
   if (!resizingQueue.value) return;
   const rowTimeline = document.querySelector('.row-timeline') as HTMLElement;
@@ -501,21 +560,36 @@ function onResizing(event: MouseEvent) {
     ghostQueue.value.finishTime = newEndStr;
   }
 }
+
 async function onResizeEnd() {
-  if (!resizingQueue.value) return;
-  console.log(`🔄 Updating Queue ${resizingQueue.value.QueueID} after resize...`);
-  await queueStore.updateQueue(resizingQueue.value.QueueID, {
+  if (!resizingQueue.value || !ghostQueue.value) return;
+
+  const queueID = resizingQueue.value.QueueID;
+
+  // 🟡 Save original label & color
+  originalLabelMap[queueID] = resizingQueue.value.label;
+  originalColorMap[queueID] = getQueueColor(resizingQueue.value.label);
+
+  await queueStore.updateQueue(queueID, {
     startTime: ghostQueue.value.startTime,
     finishTime: ghostQueue.value.finishTime,
   });
+
+  await queueStore.fetchQueues();
+  clearOriginalMaps();
+
   resizingQueue.value = null;
   resizeDirection.value = null;
   ghostQueue.value = null;
   ghostStyle.display = 'none';
+
   document.removeEventListener('mousemove', onResizing);
   document.removeEventListener('mouseup', onResizeEnd);
 }
 
+
+
+
 // Pagination Functions
 async function addPage() {
   if (pageStore.pages.length < 10) {
@@ -530,6 +604,7 @@ function onPageRightClick(page: number, event: MouseEvent) {
   pageToShowDelete.value = page;
 }
 
+
 async function deletePage(pageNum: number) {
   await pageStore.removePage(pageNum);
   await pageStore.fetchPages(); // 👈 โหลดหน้าทั้งหมดใหม่
diff --git a/src/components/ProductionTargetTable.vue b/src/components/ProductionTargetTable.vue
index 7c7b139..75fbc1a 100644
--- a/src/components/ProductionTargetTable.vue
+++ b/src/components/ProductionTargetTable.vue
@@ -15,7 +15,7 @@ const currentPage = computed(() => pageContext.currentPage)
 
 const headers = ref([
   { title: "รหัส", key: "id" },
-  { title: "ชื่อ", key: "name" },
+  { title: "ชื่อ", key: "name" },            
   { title: "ขนาด", key: "size" },
   { title: "แบรนด์", key: "brand" },
   { title: "ชนิด", key: "type" },
@@ -25,7 +25,7 @@ const headers = ref([
   { title: "เหลือ", key: "remaining" },
 ])
 
-const groupBy = [{ key: 'order', order: 'asc' }]
+const groupBy = [{ key: 'customerGroup' }]
 const savingRowIds = ref<number[]>([])
 const recentlySavedIds = ref<number[]>([])
 
@@ -34,25 +34,60 @@ onMounted(async () => {
 })
 
 // ✅ กรองตาม PageID + selectedDate จาก store
-const productionData = computed(() =>
-  productionTargetStore.productionTargets
+const productionData = computed(() => {
+  const rawData = productionTargetStore.productionTargets
     .filter((target) =>
       target.page?.PageID === currentPage.value &&
       target.Date?.slice(0, 10) === selectedDate.value
     )
-    .map((target) => ({
-      id: target.ProductionTargetID,
-      name: target.item?.name || target.item?.brand || 'ไม่พบข้อมูล',
-      size: target.item?.size || '-',
-      brand: target.item?.brand || '-',
-      type: target.itemType === 'PRODUCT' ? 'Product' : 'Material',
-      target: target.TargetProduced,
-      produced: target.ActualProduced,
-      unit: target.item?.unit || '-',
-      remaining: target.Status,
-      order: target.order?.OrderID || `Order #${target.order?.OrderID}` || 'ไม่ระบุ',
-    }))
-)
+    .map((target) => {
+      const customerName = target.order?.customer?.name || 'ไม่ระบุลูกค้า'
+      return {
+        id: target.ProductionTargetID,
+        name: target.item?.name || target.item?.brand || 'ไม่พบข้อมูล',
+        size: target.item?.size || '-',
+        brand: target.item?.brand || '-',
+        type: target.itemType === 'PRODUCT' ? 'Product' : 'Material',
+        target: target.TargetProduced,
+        produced: target.ActualProduced,
+        unit: target.item?.unit || '-',
+        remaining: target.Status,
+        order: target.order?.OrderID || 'ไม่ระบุ',
+        customer: customerName,
+      }
+    })
+
+  // 🔄 รวมรายการซ้ำในกลุ่ม "วันนี้"
+  const todayGroupMap = new Map<string, any>()
+
+  rawData.forEach(item => {
+    const key = `${item.name}|${item.size}|${item.brand}|${item.type}`
+    if (todayGroupMap.has(key)) {
+      const existing = todayGroupMap.get(key)
+      existing.target += item.target
+      existing.produced += item.produced
+    } else {
+      todayGroupMap.set(key, {
+        ...item,
+        customerGroup: '0-รวมทั้งหมด', // กลุ่ม "วันนี้"
+      })
+    }
+  })
+
+  const todayItems = Array.from(todayGroupMap.values())
+
+  // ✅ กลุ่มรายลูกค้า (ไม่ยุบ)
+  const customerItems = rawData.map(item => ({
+    ...item,
+    customerGroup: `1-${item.customer}`,
+  }))
+
+  return [...todayItems, ...customerItems]
+})
+
+
+
+
 
 async function saveActualProduced(item: any) {
   const id = item.id
@@ -97,22 +132,25 @@ function blockInvalidKeys(event: KeyboardEvent) {
     item-value="name"
     hide-default-footer
   >
-    <template v-slot:group-header="{ item, columns, toggleGroup, isGroupOpen }">
-      <tr>
-        <td :colspan="columns.length">
-          <div class="d-flex align-center">
-            <v-btn
-              :icon="isGroupOpen(item) ? '$expand' : '$next'"
-              density="comfortable"
-              size="x-small"
-              variant="outlined"
-              @click="toggleGroup(item)"
-            ></v-btn>
-            <span class="ms-2 text-caption">Order {{ item.value }}</span>
-          </div>
-        </td>
-      </tr>
-    </template>
+  <template v-slot:group-header="{ item, columns, toggleGroup, isGroupOpen }">
+    <tr>
+      <td :colspan="columns.length">
+        <div class="d-flex align-center">
+          <v-btn
+            :icon="isGroupOpen(item) ? '$expand' : '$next'"
+            density="comfortable"
+            size="x-small"
+            variant="outlined"
+            @click="toggleGroup(item)"
+          />
+          <span class="ms-2 text-caption">
+            {{ item.value.startsWith('0-') ? 'วันนี้' : 'ลูกค้า: ' + item.value.slice(2) }}
+          </span>
+        </div>
+      </td>
+    </tr>
+  </template>
+
 
     <template v-slot:item.produced="{ item }">
       <div class="save-cell-wrapper" style="position: relative; display: inline-block;">
@@ -146,6 +184,7 @@ function blockInvalidKeys(event: KeyboardEvent) {
 <style scoped>
 .custom-table {
   font-size: 12px;
+  font-family: 'Kanit';
 }
 
 .fade-check {
@@ -156,4 +195,5 @@ function blockInvalidKeys(event: KeyboardEvent) {
   80% { opacity: 1; transform: scale(1.2); }
   100% { opacity: 0; transform: scale(1); }
 }
+
 </style>
-- 
GitLab