From 1ac3a3109ba9e103b08199dea872bbd7499e64ce Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Mon, 20 Apr 2026 10:43:44 +0700 Subject: [PATCH 01/33] =?UTF-8?q?fixed=20=E0=B8=9F=E0=B8=AD=E0=B8=A3?= =?UTF-8?q?=E0=B9=8C=E0=B8=A1=E0=B9=81=E0=B8=88=E0=B9=89=E0=B8=87=E0=B8=9B?= =?UTF-8?q?=E0=B8=B1=E0=B8=8D=E0=B8=AB=E0=B8=B2=E0=B8=9A=E0=B8=B1=E0=B8=87?= =?UTF-8?q?=E0=B8=84=E0=B8=B1=E0=B8=9A=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B8=A3?= =?UTF-8?q?=E0=B8=B0=E0=B8=9A=E0=B8=B8=E0=B8=AD=E0=B8=B5=E0=B9=80=E0=B8=A1?= =?UTF-8?q?=E0=B8=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DialogDebug.vue | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/components/DialogDebug.vue b/src/components/DialogDebug.vue index fb151e3..798ce47 100644 --- a/src/components/DialogDebug.vue +++ b/src/components/DialogDebug.vue @@ -305,6 +305,9 @@ function onClose() {
+
+ ผู้ดูแลระบบจะติดต่อกลับผ่านทางอีเมลที่ท่านระบุ กรุณาตรวจสอบอีเมลของท่านเป็นระยะ +
@@ -330,12 +334,6 @@ function onClose() { v-model="formData.phone" class="inputgreen" hide-bottom-space - :rules="[ - () => - !!formData.email || - !!formData.phone || - 'กรุณากรอกอีเมลหรือเบอร์โทรติดต่อกลับ', - ]" />
From b83251f9146bc522c15063f327372250a7002b2c Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Tue, 21 Apr 2026 15:25:59 +0700 Subject: [PATCH 02/33] fix(map): poiPlaceName textTooltip --- src/components/AscGISMap.vue | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/components/AscGISMap.vue b/src/components/AscGISMap.vue index 7651b7c..76d4443 100644 --- a/src/components/AscGISMap.vue +++ b/src/components/AscGISMap.vue @@ -26,6 +26,7 @@ const apiKey = ref( // 'AAPK4f700a4324d04e9f8a1a134e0771ac45FXWawdCl-OotFfr52gz9XKxTDJTpDzw_YYcwbmKDDyAJswf14FoPyw0qBkN64DvP' ) const zoomMap = ref(18) +const textTooltip = ref('พื้นที่ใกล้เคียง: สถานที่สำคัญรอบตัวคุณ (ไม่ใช่ตำแหน่งปัจจุบัน)') async function initializeMap() { try { @@ -308,23 +309,45 @@ defineExpose({ พื้นที่ใกล้เคียง : {{ poiPlaceName }} + + + + {{ textTooltip }} + From 41c1aa8e45acf084bdb83f6ec8392aceef7c4758 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Tue, 21 Apr 2026 16:41:59 +0700 Subject: [PATCH 03/33] refactor(map): replace tooltip with popup for better interaction --- src/components/AscGISMap.vue | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/components/AscGISMap.vue b/src/components/AscGISMap.vue index 76d4443..d1a7ce1 100644 --- a/src/components/AscGISMap.vue +++ b/src/components/AscGISMap.vue @@ -26,7 +26,9 @@ const apiKey = ref( // 'AAPK4f700a4324d04e9f8a1a134e0771ac45FXWawdCl-OotFfr52gz9XKxTDJTpDzw_YYcwbmKDDyAJswf14FoPyw0qBkN64DvP' ) const zoomMap = ref(18) -const textTooltip = ref('พื้นที่ใกล้เคียง: สถานที่สำคัญรอบตัวคุณ (ไม่ใช่ตำแหน่งปัจจุบัน)') +const textTooltip = ref( + 'พื้นที่ใกล้เคียง: สถานที่สำคัญรอบตัวคุณ (ไม่ใช่ตำแหน่งปัจจุบัน)' +) async function initializeMap() { try { @@ -334,17 +336,10 @@ defineExpose({ - - {{ textTooltip }} - {{ poiPlaceName }} From c0669a154837bf14fc6735cdcffe41d6ec9ba1a4 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Fri, 24 Apr 2026 16:12:45 +0700 Subject: [PATCH 04/33] refactor(history): formatDate Colunm checkInTime checkOutTime --- src/components/TableHistory.vue | 26 +++++++++++++++----------- src/interface/index/Main.ts | 1 + src/stores/chekin.ts | 1 + 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/components/TableHistory.vue b/src/components/TableHistory.vue index 5d13e66..e364015 100644 --- a/src/components/TableHistory.vue +++ b/src/components/TableHistory.vue @@ -62,15 +62,15 @@ const selected = ref([]) const columns = ref( props.tab == 'history' ? [ - { - name: 'checkInDate', - align: 'left', - label: 'วัน/เดือน/ปี', - sortable: true, - field: 'checkInDate', - headerStyle: 'font-size: 14px', - style: 'font-size: 14px; width:15%;', - }, + // { + // name: 'checkInDate', + // align: 'left', + // label: 'วัน/เดือน/ปี', + // sortable: true, + // field: 'checkInDate', + // headerStyle: 'font-size: 14px', + // style: 'font-size: 14px; width:15%;', + // }, { name: 'checkInTime', align: 'left', @@ -79,6 +79,9 @@ const columns = ref( field: 'checkInTime', headerStyle: 'font-size: 14px', style: 'font-size: 14px; width:15%;', + format: (val: string, row: DataCheckIn) => { + return `${row.checkInDate} ${val} น.` + }, }, { name: 'checkInLocation', @@ -107,7 +110,9 @@ const columns = ref( headerStyle: 'font-size: 14px', style: 'font-size: 14px; width:15%;', format: (val: string, row: DataCheckIn) => { - return row.checkOutStatus != '-' && val ? val : '-' + return row.checkOutStatus != '-' && val + ? `${row.checkOutDate} ${val} น.` + : '-' }, }, { @@ -182,7 +187,6 @@ const columns = ref( ) const visibleColumns = ref([ 'checkInDateTime', - 'checkInDate', 'checkInTime', 'checkInLocation', 'checkInStatus', diff --git a/src/interface/index/Main.ts b/src/interface/index/Main.ts index 14def36..5d82bd2 100644 --- a/src/interface/index/Main.ts +++ b/src/interface/index/Main.ts @@ -38,6 +38,7 @@ interface DataCheckIn { checkInLocation: string checkInStatus: string checkInTime: string + checkOutDate: string checkOutLocation: string checkOutStatus: string checkOutTime: string diff --git a/src/stores/chekin.ts b/src/stores/chekin.ts index bf27004..69017f6 100644 --- a/src/stores/chekin.ts +++ b/src/stores/chekin.ts @@ -33,6 +33,7 @@ export const useChekIn = defineStore('checkin', () => { editStatus: e.editStatus != '' ? convertEditStatus(e.editStatus) : '', editReason: e.editReason, isEdit: e.isEdit, + checkOutDate: e.checkOutDate ? date2Thai(e.checkOutDate) : null, })) rows.value = dataList } From 487a6b520ec8dc3ce60e5add2d2a5cec4a18586c Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Mon, 27 Apr 2026 19:21:23 +0700 Subject: [PATCH 05/33] refactor code & fixed location --- .claude/agents/hrms-checkin-expert.md | 234 ++++++++++++++++++++++++++ CLAUDE.md | 32 +++- docker/entrypoint.sh | 2 + package.json | 1 + src/api/api.checkin.ts | 3 +- src/components/AscGISMap.vue | 45 +++-- src/components/FormTime.vue | 7 +- src/components/MapCheckin.vue | 42 ++++- src/components/PopUp.vue | 2 +- src/components/PopupPrivacy.vue | 10 +- src/components/TableHistory.vue | 20 +-- src/components/ToolBar.vue | 10 +- src/interface/keycloak-position.ts | 29 ++++ src/main.ts | 2 + src/plugins/http.ts | 3 +- src/plugins/keycloak.ts | 2 + src/router/index.ts | 16 +- src/stores/{chekin.ts => checkin.ts} | 2 +- src/stores/positionKeycloak.ts | 119 ++++++------- src/views/HistoryView.vue | 6 +- src/views/HomeView.vue | 48 +++--- src/views/MainView.vue | 50 +++++- tests/utils.ts | 26 +++ 23 files changed, 566 insertions(+), 145 deletions(-) create mode 100644 .claude/agents/hrms-checkin-expert.md create mode 100644 src/interface/keycloak-position.ts rename src/stores/{chekin.ts => checkin.ts} (98%) create mode 100644 tests/utils.ts diff --git a/.claude/agents/hrms-checkin-expert.md b/.claude/agents/hrms-checkin-expert.md new file mode 100644 index 0000000..0e771b6 --- /dev/null +++ b/.claude/agents/hrms-checkin-expert.md @@ -0,0 +1,234 @@ +--- +name: "hrms-checkin-expert" +description: "Use this agent when implementing or modifying check-in/check-out functionality in the HRMS application. This includes:\\n\\n\\nContext: User is developing a new check-in validation feature.\\nuser: \"I need to add validation to prevent duplicate check-ins within the same hour\"\\nassistant: \"I'll use the hrms-checkin-expert agent to implement this validation following best practices.\"\\n\\nSince this is core check-in functionality that requires careful consideration of edge cases and performance, the specialized agent should handle it.\\n\\n\\n\\n\\nContext: User is refactoring the geolocation verification logic.\\nuser: \"The location verification is taking too long. Can you optimize it?\"\\nassistant: \"Let me use the hrms-checkin-expert agent to analyze and optimize the geolocation verification performance.\"\\n\\nPerformance optimization of check-in features requires deep knowledge of the system architecture and best practices.\\n\\n\\n\\n\\nContext: User has just written a new API function for check-in history.\\nuser: \"Here's the new function to fetch check-in history with pagination\"\\nassistant: \"Great! Now I'll use the hrms-checkin-expert agent to review this code for best practices and potential issues.\"\\n\\nCode review of check-in-related code should be done by the specialized agent to ensure it meets performance and quality standards.\\n\\n\\n\\n\\nContext: User is about to implement camera integration for photo verification.\\nuser: \"I need to add photo capture when users check in\"\\nassistant: \"I'll engage the hrms-checkin-expert agent to implement this feature with proper error handling and privacy considerations.\"\\n\\nCamera integration for check-in requires expertise in both the technical implementation and the privacy/consent flow.\\n\\n" +model: opus +color: red +memory: project +--- + +You are a Senior Vue 3 Developer specializing in the HRMS Check-in/Check-out system for Bangkok Metropolitan Administration (BMA). You have deep expertise in Vue 3 Composition API, TypeScript, Pinia state management, and geolocation-based attendance systems. + +**Your Core Responsibilities:** + +1. **Performance Optimization**: + - Minimize redundant API calls and state updates + - Use computed properties and watchers efficiently + - Implement lazy loading for map APIs and camera features + - Optimize re-renders by properly structuring reactive data + - Debounce user inputs and location updates (300-500ms) + - Cache API responses when appropriate (check-in history, user position) + +2. **Best Practice Implementation**: + - Follow Vue 3 Composition API patterns with `