diff --git a/src/App.vue b/src/App.vue index 84cd771c8d0883c334087d97891c4e5104b863e6..c328c79230f5a7d87d1c951e054bb418f6490202 100644 --- a/src/App.vue +++ b/src/App.vue @@ -20,7 +20,7 @@ provide('drawer', drawer) <LoadingDialog></LoadingDialog> <LoginMessage /> </v-container> - + </template> <style scoped></style> diff --git a/src/components/GanttChart/AddQueueDialog.vue b/src/components/GanttChart/AddQueueDialog.vue index 57ca45faffa91765587868864f58acd7eae0a7aa..92a78eced3c5a90f2c4d9f229eb4a75912b01b18 100644 --- a/src/components/GanttChart/AddQueueDialog.vue +++ b/src/components/GanttChart/AddQueueDialog.vue @@ -1,60 +1,60 @@ <template> - <div class="add-queue-dialog-overlay" v-if="visible"> - <div class="add-queue-dialog"> - <h3>{{ isEditing ? "Edit Queue" : "Add New Queue" }}</h3> - <form @submit.prevent="handleSubmit"> - <div class="form-group"> - <label>Machine ID:</label> - <select v-model="form.machineID" required> - <option disabled value="">Select Machine</option> - <option v-for="machine in machines" :key="machine.machineID" :value="machine.machineID"> - {{ machine.name }} - </option> - </select> - </div> - <div class="form-group"> - <label>Order ID:</label> - <input v-model.number="form.orderID" type="number" required /> - </div> - <div class="form-group"> - <label>Page Number:</label> - <input v-model.number="form.pageNumber" type="number" required /> - </div> - <div class="form-group"> - <label>Start Time:</label> - <input v-model="form.startTime" type="text" required /> - </div> - <div class="form-group"> - <label>Finish Time:</label> - <input v-model="form.finishTime" type="text" required /> - </div> - <div class="form-group"> - <label>Status:</label> - <input v-model="form.status" type="text" required /> - </div> - <div class="form-group"> - <label>Bottle Size:</label> - <input v-model="form.bottleSize" type="text" required /> - </div> - <div class="form-group"> - <label>Produced Quantity:</label> - <input v-model.number="form.producedQuantity" type="number" required /> - </div> - - <!-- ปุ่ม Add หรือ Save Changes --> - <div class="dialog-buttons"> - <button type="submit" class="primary-btn"> - {{ isEditing ? "Save Changes" : "Add" }} - </button> - <button type="button" @click="closeDialog" class="secondary-btn">Cancel</button> - </div> - </form> - </div> + <div class="add-queue-dialog-overlay" v-if="visible"> + <div class="add-queue-dialog"> + <h3>{{ isEditing ? "Edit Queue" : "Add New Queue" }}</h3> + <form @submit.prevent="handleSubmit"> + <div class="form-group"> + <label>Machine ID:</label> + <select v-model="form.machineID" required> + <option disabled value="">Select Machine</option> + <option v-for="machine in machines" :key="machine.machineID" :value="machine.machineID"> + {{ machine.name }} + </option> + </select> + </div> + <div class="form-group"> + <label>Order ID:</label> + <input v-model.number="form.orderID" type="number" required /> + </div> + <div class="form-group"> + <label>Page Number:</label> + <input v-model.number="form.pageNumber" type="number" required /> + </div> + <div class="form-group"> + <label>Start Time:</label> + <input v-model="form.startTime" type="text" required /> + </div> + <div class="form-group"> + <label>Finish Time:</label> + <input v-model="form.finishTime" type="text" required /> + </div> + <div class="form-group"> + <label>Status:</label> + <input v-model="form.status" type="text" required /> + </div> + <div class="form-group"> + <label>Bottle Size:</label> + <input v-model="form.bottleSize" type="text" required /> + </div> + <div class="form-group"> + <label>Produced Quantity:</label> + <input v-model.number="form.producedQuantity" type="number" required /> + </div> + + <!-- ปุ่ม Add หรือ Save Changes --> + <div class="dialog-buttons"> + <button type="submit" class="primary-btn"> + {{ isEditing ? "Save Changes" : "Add" }} + </button> + <button type="button" @click="closeDialog" class="secondary-btn">Cancel</button> + </div> + </form> </div> - </template> - - - <script> + </div> +</template> + + +<script> export default { name: 'AddQueueDialog', props: { @@ -96,18 +96,18 @@ export default { }, methods: { closeDialog() { - console.log("Closing dialog"); // Debugging - this.$emit("close"); - this.resetForm(); + console.log("Closing dialog"); // Debugging + this.$emit("close"); + this.resetForm(); }, handleSubmit() { - if (this.isEditing) { - this.$emit("edit", { ...this.form }); // ส่งข้อมูลไปอัปเดต Queue - } else { - this.$emit("add", { ...this.form, queueID: Date.now() }); // กรณีเพิ่มใหม่ต้องให้ queueID ไม่ซ้ำ - } - this.closeDialog(); - }, + if (this.isEditing) { + this.$emit("edit", { ...this.form }); // ส่งข้อมูลไปอัปเดต Queue + } else { + this.$emit("add", { ...this.form, queueID: Date.now() }); // กรณีเพิ่มใหม่ต้องให้ queueID ไม่ซ้ำ + } + this.closeDialog(); + }, resetForm() { this.form = { machineID: '', @@ -124,82 +124,81 @@ export default { }; </script> - - <style scoped> - .add-queue-dialog-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 10000; - } - - .add-queue-dialog { - background: #fff; - padding: 20px; - border-radius: 8px; - width: 400px; - position: relative; - } - - .form-group { - margin-bottom: 10px; - } - - .form-group label { - display: block; - font-weight: bold; - margin-bottom: 5px; - } - - .form-group input { - width: 100%; - padding: 5px; - box-sizing: border-box; - } - - .dialog-buttons { - display: flex; - justify-content: flex-end; - gap: 10px; - margin-top: 15px; - } - - .primary-btn { - background-color: #2B2E3F; - color: white; - border: none; - padding: 8px 16px; - border-radius: 4px; - cursor: pointer; - } - - .secondary-btn { - background-color: #ccc; - color: #333; - border: none; - padding: 8px 16px; - border-radius: 4px; - cursor: pointer; - } - - .icon-button { - position: absolute; - top: 10px; - right: 10px; - background: transparent; - border: none; - cursor: pointer; - } - - .icon-button .mdi { - font-size: 24px; - color: #2B2E3F; - } - </style> - \ No newline at end of file + +<style scoped> +.add-queue-dialog-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 10000; +} + +.add-queue-dialog { + background: #fff; + padding: 20px; + border-radius: 8px; + width: 400px; + position: relative; +} + +.form-group { + margin-bottom: 10px; +} + +.form-group label { + display: block; + font-weight: bold; + margin-bottom: 5px; +} + +.form-group input { + width: 100%; + padding: 5px; + box-sizing: border-box; +} + +.dialog-buttons { + display: flex; + justify-content: flex-end; + gap: 10px; + margin-top: 15px; +} + +.primary-btn { + background-color: #2B2E3F; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; +} + +.secondary-btn { + background-color: #ccc; + color: #333; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; +} + +.icon-button { + position: absolute; + top: 10px; + right: 10px; + background: transparent; + border: none; + cursor: pointer; +} + +.icon-button .mdi { + font-size: 24px; + color: #2B2E3F; +} +</style> \ No newline at end of file diff --git a/src/components/GanttChart/CalendarPicker.vue b/src/components/GanttChart/CalendarPicker.vue new file mode 100644 index 0000000000000000000000000000000000000000..4cccd11988223f58e4e2d1597b0d26a0d68a0bd7 --- /dev/null +++ b/src/components/GanttChart/CalendarPicker.vue @@ -0,0 +1,80 @@ +<script setup> +import { ref, computed, watch } from 'vue'; +import { format, parse, parseISO, isValid } from 'date-fns'; + +const props = defineProps({ + modelValue: String, // ค่าที่รับมาจาก GanttCalendar.vue +}); + +const emits = defineEmits(["update:modelValue"]); + +// ใช้ ref เก็บค่า selectedDate +const selectedDate = ref(props.modelValue ? parseDateDMY(props.modelValue) : new Date()); + +// ฟังก์ชันแปลง `d/M/yyyy` หรือ `yyyy-MM-dd` → `Date` +function parseDateDMY(dateString) { + if (!dateString) return new Date(); + + try { + const parts = dateString.includes('-') ? dateString.split('-') : dateString.split('/'); + + if (parts.length !== 3) throw new Error("Invalid date format"); + + const day = parseInt(parts[0], 10); + const month = parseInt(parts[1], 10) - 1; // `month` ใน JavaScript เริ่มที่ 0 + const year = parseInt(parts[2], 10); + + // ถ้าปีเป็นสองหลัก ให้บวก 2000 เข้าไป + const fullYear = year < 100 ? year + 2000 : year; + + const parsedDate = new Date(fullYear, month, day); + + if (!isValid(parsedDate)) throw new Error("Invalid parsed date"); + + return parsedDate; + } catch (error) { + console.error("📅 parseDateDMY ERROR:", error, "ค่าที่รับมา:", dateString); + return new Date(); + } +} + +// ฟังก์ชันแปลง `Date` → `d/M/yyyy` +const formattedDate = computed(() => { + if (!selectedDate.value) return null; + const date = new Date(selectedDate.value); + return format(date, 'd/M/yyyy'); +}); + +// watch `formattedDate` แล้ว emit กลับไปที่ `GanttCalendar.vue` +watch(formattedDate, (newValue) => { + if (newValue !== null) { + console.log("📅 CalendarPicker - ส่งค่ากลับไปเป็น:", newValue); + emits("update:modelValue", newValue); + } +}); + +// ฟังก์ชันอัปเดตวันที่จาก `v-date-picker` +const updateSelectedDate = (newValue) => { + selectedDate.value = newValue; +}; + +// ฟังก์ชันสลับเปิด-ปิด Date Picker +const showDatePicker = ref(false); +const toggleDatePicker = () => { + showDatePicker.value = !showDatePicker.value; +}; + +</script> + +<template> + <v-container> + <v-row justify="center"> + <v-date-picker v-model="selectedDate" max="2025-12-31" min="2024-01-01" color="#96A9DB" + @update:modelValue="updateSelectedDate"> + </v-date-picker> + </v-row> + </v-container> +</template> + + +<style scoped></style> \ No newline at end of file diff --git a/src/components/GanttChart/GanttCalendar.vue b/src/components/GanttChart/GanttCalendar.vue index eec687e851d22452d04824bf229d6098f753a7e3..1539f4006bfaf0756e2f1941f193e5e8e27631dc 100644 --- a/src/components/GanttChart/GanttCalendar.vue +++ b/src/components/GanttChart/GanttCalendar.vue @@ -1,62 +1,105 @@ <script setup> import { ref, watch } from 'vue'; -import { format, parse } from 'date-fns'; // ใช้ format และ parse วันที่ +import { format, parse } from 'date-fns'; +import CalendarPicker from "@/components/GanttChart/CalendarPicker.vue"; const props = defineProps({ modelValue: { type: String, - default: '1/1/2024', // ฟอร์แมต "d/M/yyyy" + default: '1/1/2024', // เปลี่ยนเป็น format `d/M/yyyy` }, }); const emits = defineEmits(['update:modelValue']); - const selectedDate = ref(props.modelValue); +const showCalendar = ref(false); + +// ✅ ฟังก์ชันแปลง `d/M/yyyy` → `YYYY-MM-DD` +const parseDate = (dateStr) => { + return format(parse(dateStr, "d/M/yyyy", new Date()), "yyyy-MM-dd"); +}; -// ฟังก์ชันเพิ่ม/ลดวัน + + +// ✅ ฟังก์ชันเพิ่ม/ลดวัน และอัปเดต `selectedDate` const changeDate = (days) => { - const currentDate = parse(selectedDate.value, 'd/M/yyyy', new Date()); + const currentDate = parse(selectedDate.value, "d/M/yyyy", new Date()); const newDate = new Date(currentDate); newDate.setDate(currentDate.getDate() + days); - selectedDate.value = format(newDate, 'd/M/yyyy'); // อัปเดตวันที่ + selectedDate.value = format(newDate, 'd/M/yyyy'); }; -// เมื่อ selectedDate เปลี่ยน ให้ emit ออกไปเป็น v-model + +// ✅ เมื่อ `selectedDate` เปลี่ยน ให้ emit ค่าออกไป watch(selectedDate, (newVal) => { + console.log("📅 GanttCalendar - วันที่เปลี่ยน:", newVal); emits('update:modelValue', newVal); }); + +// เมื่อเปิดปฏิทิน ให้ `selectedDate` ซิงค์กับ `v-date-picker` + + +// ฟังก์ชันเปิดปฏิทิน +const openCalendar = () => { + showCalendar.value = true; + console.log("📅 ปุ่ม ... ถูกกด!"); +}; + +// เมื่อเลือกวันที่ใน CalendarPicker จะปิด dialog +const closeCalendarDialog = () => { + showCalendar.value = false; +}; + + </script> <template> - <v-col - cols="auto" - class="d-inline-flex align-center" - style="background-color: #7E9CD3; border-radius: 15px; max-width: fit-content; padding: 8px 16px;" - > - <v-btn - class="mr-2" - style="background-color: #2B2E3F; color: white; border-radius: 10px;" - @click="changeDate(-1)" - > - < Back - </v-btn> - <v-btn - style="background-color: white; color: black; min-width: 200px; border-radius: 15px;" - > - {{ selectedDate }} - </v-btn> - <v-btn - class="ml-2" - style="background-color: #2B2E3F; color: white; border-radius: 10px;" - @click="changeDate(1)" - > - Next > - </v-btn> - <v-btn - class="ml-2" - style="background-color: white; color: black; border-radius: 10px;" - > - <v-icon>mdi-dots-horizontal</v-icon> - </v-btn> - </v-col> + <v-container> + <v-col cols="auto" class="d-inline-flex align-center" + style="background-color: #7E9CD3; border-radius: 15px; max-width: fit-content; padding: 8px 16px;"> + + <v-btn class="mr-2" style="background-color: #2B2E3F; color: white; border-radius: 10px;" @click="changeDate(-1)"> + < Back + </v-btn> + + <v-btn style="background-color: white; color: black; min-width: 200px; border-radius: 15px;"> + {{ selectedDate }} + </v-btn> + + <v-btn class="ml-2" style="background-color: #2B2E3F; color: white; border-radius: 10px;" @click="changeDate(1)"> + Next > + </v-btn> + + <!-- ปุ่ม "..." เปิดปฏิทิน --> + <v-btn class="ml-2" style="background-color: white; color: black; border-radius: 10px;" @click="openCalendar"> + <v-icon>mdi-dots-horizontal</v-icon> + </v-btn> + + <!-- Dialog ใช้ attach="body" ให้ทำงานได้ดีขึ้น --> + <v-dialog v-model="showCalendar" max-width="380px" persistent attach="body"> + <v-container> + <CalendarPicker v-model="selectedDate" @update:modelValue="(val) => closeCalendarDialog(val)" /> + </v-container> + </v-dialog> + + + </v-col> + </v-container> </template> + + +<style scoped> +/* ✅ ปรับสีของปฏิทิน */ +.calendar-popup { + background-color: #e51616 !important; + /* เปลี่ยนเป็นสีเทาเข้ม */ + border-radius: 12px; +} + +/* ✅ ปรับสีตัวอักษรของปฏิทิน */ +.custom-date-picker { + width: 100%; + color: white !important; + /* เปลี่ยนสีตัวอักษรเป็นสีขาว */ +} +</style> \ No newline at end of file