From 6c819a609718aaad729a5be4019980817d0859ab Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 1 May 2026 12:50:50 +0700 Subject: [PATCH] =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88=E0=B8=A1?= =?UTF-8?q?=E0=B9=80=E0=B8=8A=E0=B9=87=E0=B8=84=E0=B8=AA=E0=B8=B1=E0=B8=87?= =?UTF-8?q?=E0=B8=81=E0=B8=B1=E0=B8=94=20=E0=B8=96=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B9=84=E0=B8=A1=E0=B9=88=E0=B8=A1=E0=B8=B5=E0=B8=AA=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B8=81=E0=B8=B1=E0=B8=94=E0=B8=88=E0=B8=B0=E0=B9=81?= =?UTF-8?q?=E0=B8=AA=E0=B8=94=E0=B8=87=E0=B8=AB=E0=B8=99=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=20no-position?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hrms-checkin-expert/MEMORY.md | 4 + .../feature_no_position_page.md | 68 +++++++++++ src/router/index.ts | 49 +++++++- src/views/MainView.vue | 34 ++++++ src/views/NoPositionView.vue | 106 ++++++++++++++++++ 5 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 .claude/agent-memory/hrms-checkin-expert/feature_no_position_page.md create mode 100644 src/views/NoPositionView.vue diff --git a/.claude/agent-memory/hrms-checkin-expert/MEMORY.md b/.claude/agent-memory/hrms-checkin-expert/MEMORY.md index f5b3ed9..7897460 100644 --- a/.claude/agent-memory/hrms-checkin-expert/MEMORY.md +++ b/.claude/agent-memory/hrms-checkin-expert/MEMORY.md @@ -1,5 +1,9 @@ # HRMS Check-in Expert Memory +## Features + +- [No Position Assignment Page](feature_no_position_page.md) - Implementation for users without organization position assignment + ## Project Issues & Fixes - [Position Orientation Change Fix](issue_position_orientation_change_fix.md) - Fix for position map not displaying on screen orientation changes diff --git a/.claude/agent-memory/hrms-checkin-expert/feature_no_position_page.md b/.claude/agent-memory/hrms-checkin-expert/feature_no_position_page.md new file mode 100644 index 0000000..c72acc5 --- /dev/null +++ b/.claude/agent-memory/hrms-checkin-expert/feature_no_position_page.md @@ -0,0 +1,68 @@ +--- +name: No Position Assignment Page +description: Feature implementation for users without organization position assignment +type: reference +--- + +## No Position Assignment Page Feature + +When a user doesn't have a position assignment (สังกัด), they are redirected to a dedicated page explaining they are not part of the organization structure. + +### Files Created/Modified + +1. **Created:** `/Users/waruneeta/Desktop/ChamomindWorking/HRMSProject/hrms-checkin/src/views/NoPositionView.vue` + - Displays message in Thai: "ไม่พบข้อมูลสังกัด" + - Shows icon and explanation text + - "ตกลง" (OK) button that shows confirmation dialog + - After confirmation, performs logout and clears positionKeycloak store + +2. **Modified:** `/Users/waruneeta/Desktop/ChamomindWorking/HRMSProject/hrms-checkin/src/router/index.ts` + - Added new route `/no-position` with `Auth: false` + - Enhanced router guard to check for position data + - Redirects to `/no-position` if user has no organization assignment + +3. **Modified:** `/Users/waruneeta/Desktop/ChamomindWorking/HRMSProject/hrms-checkin/src/views/MainView.vue` + - Updated `fetchKeycloakPosition()` function + - Checks if `organization` object exists and has any data + - Redirects to `/no-position` if no organization data found + +### Logic Flow + +1. User logs in successfully +2. `MainView.vue` calls `fetchKeycloakPosition()` in `onMounted()` +3. API returns position data from `/org/profile/keycloak/position` +4. System checks if `organization` object has any non-null values (root, child1-4) +5. If no organization data exists: + - User is redirected to `/no-position` + - User sees message explaining they need to contact staff + - User clicks "ตกลง" to logout + - Staff adds them to organization structure + - User can login again + +### Position Data Structure + +```typescript +interface KeycloakPosition { + privacyCheckin: boolean + avatarName?: string + profileId: string + organization?: Organization +} + +interface Organization { + root?: string + child1?: string + child2?: string + child3?: string + child4?: string +} +``` + +A user is considered to have "no position" when all organization fields (root, child1, child2, child3, child4) are null or undefined. + +### Thai UI Messages + +- Page title: "ไม่พบข้อมูลสังกัด" +- Description: "ท่านยังไม่มีสังกัดในโครงสร้างองค์กร กรุณาติดต่อเจ้าหน้าที่เพื่อดำเนินการเพิ่มข้อมูล" +- Confirmation dialog: "ยืนยันการออกจากระบบ" - "ท่านจะถูกนำออกจากระบบเพื่อให้เจ้าหน้าที่ดำเนินการเพิ่มข้อมูลสังกัดในโครงสร้างองค์กร" +- Button: "ตกลง" diff --git a/src/router/index.ts b/src/router/index.ts index 8d85f3e..8346697 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -5,6 +5,7 @@ import MainView from '@/views/MainView.vue' const loginView = () => import('@/views/login.vue') const resetPasswordView = () => import('@/views/ResetPassword.vue') +const noPositionView = () => import('@/views/NoPositionView.vue') import { authenticated, logout } from '@/plugins/auth' @@ -87,19 +88,63 @@ const router = createRouter({ Auth: false, }, }, + { + path: '/no-position', + name: 'no-position', + component: noPositionView, + meta: { + Auth: false, + }, + }, ], }) // authen with keycloak client router.beforeEach(async (to, from, next) => { + // ตรวจสอบเส้นทางพิเศษที่ไม่ต้องตรวจสอบสิทธิ์ + const publicPaths = ['/login', '/auth', '/reset-password', '/no-position'] + if (publicPaths.includes(to.path)) { + next() + return + } + + // ตรวจสอบการ authenticate if (to.meta.Auth) { const checkAuthen = await authenticated() if (!checkAuthen && to.meta.Auth) { logout() + return } - } else { - next() } + + // ตรวจสอบว่าผู้ใช้มีข้อมูลสังกัดหรือไม่ + // ต้องทำการ dynamic import เนื่องจากเป็นการใช้งาน Pinia store ใน router + if (to.path !== '/no-position') { + try { + const { usePositionKeycloakStore } = await import('@/stores/positionKeycloak') + const positionKeycloakStore = usePositionKeycloakStore() + const dataPositionKeycloak = positionKeycloakStore.dataPositionKeycloak + + // ถ้ามีข้อมูล positionKeycloak แล้ว ให้ตรวจสอบว่ามี organization หรือไม่ + if (dataPositionKeycloak) { + const hasOrganization = + dataPositionKeycloak.organization && + (dataPositionKeycloak.organization.root || + dataPositionKeycloak.organization.child1 || + dataPositionKeycloak.organization.child2 || + dataPositionKeycloak.organization.child3 || + dataPositionKeycloak.organization.child4) + + if (!hasOrganization) { + next('/no-position') + return + } + } + } catch (error) { + console.error('Error checking position:', error) + } + } + next() }) diff --git a/src/views/MainView.vue b/src/views/MainView.vue index 7e7d42b..d83192e 100644 --- a/src/views/MainView.vue +++ b/src/views/MainView.vue @@ -182,6 +182,22 @@ async function fetchKeycloakPosition() { if (existingData) { // มีข้อมูลอยู่แล้ว ใช้ข้อมูลเดิม + + // เช็คว่ามีข้อมูลสังกัด (organization) หรือไม่ + const hasOrganization = + existingData.organization && + (existingData.organization.root || + existingData.organization.child1 || + existingData.organization.child2 || + existingData.organization.child3 || + existingData.organization.child4) + + if (!hasOrganization) { + // ไม่มีข้อมูลสังกัด redirect ไปหน้า NoPosition + router.replace('/no-position') + return + } + privacyStore.modalPrivacy = !existingData.privacyCheckin privacyStore.setAccepted(existingData.privacyCheckin) @@ -208,6 +224,24 @@ async function fetchKeycloakPosition() { }, } + // เช็คว่ามีข้อมูลสังกัด (organization) หรือไม่ + // ถ้าไม่มีข้อมูลสังกัด ให้ redirect ไปหน้า NoPosition + const hasOrganization = + keycloakData.organization && + (keycloakData.organization.root || + keycloakData.organization.child1 || + keycloakData.organization.child2 || + keycloakData.organization.child3 || + keycloakData.organization.child4) + + if (!hasOrganization) { + // บันทึกข้อมูลที่ได้รับไว้ก่อน (เผื่อใช้ในอนาคต) + positionKeycloakStore.setPositionKeycloak(keycloakData) + // Redirect ไปหน้า NoPosition + router.replace('/no-position') + return + } + positionKeycloakStore.setPositionKeycloak(keycloakData) privacyStore.modalPrivacy = !keycloakData.privacyCheckin privacyStore.setAccepted(keycloakData.privacyCheckin) diff --git a/src/views/NoPositionView.vue b/src/views/NoPositionView.vue new file mode 100644 index 0000000..691d10a --- /dev/null +++ b/src/views/NoPositionView.vue @@ -0,0 +1,106 @@ + + + + +