Files
alla-tms/docs/review/employee-dashboard-audit.md
2026-07-16 09:53:14 +07:00

24 KiB

Employee Dashboard Audit Report

Executive Summary

Overall Status

  • Good

ภาพรวม:

  • หน้า Employee Dashboard ใช้สถาปัตยกรรมที่สอดคล้องกับโปรเจ็กต์ค่อนข้างดี โดยมี route guard, server-side data aggregation และแยก component ตาม feature ชัดเจน
  • ข้อมูลหลักของพนักงาน เช่น ชั่วโมงที่อนุมัติแล้ว ความคืบหน้า K / S / A กิจกรรมล่าสุด และประกาศล่าสุด ถูกดึงจากฝั่ง server ภายใต้ขอบเขตองค์กรและผู้ใช้ที่กำลังล็อกอิน
  • จุดที่ควรปรับหลัก ๆ อยู่ที่ error handling, ความสม่ำเสมอของ navigation/UX, การแสดงผล progress เมื่อเกินเป้า, และการสื่อสารกรณีข้อมูลว่างหรือผู้ใช้ยังไม่ผูก employee profile

Architecture Review

สิ่งที่พบ:

ข้อสรุป:

  • โครงสร้างโดยรวมดี และแยก server responsibility ไว้ถูกที่
  • อย่างไรก็ดี Employee Dashboard ถูกวางชื่อ route เป็น overview มากกว่าจะมี bounded context ชัดเจนแบบ employee-dashboard ทำให้ความหมายของหน้าและความต่างระหว่าง employee กับ HRD พึ่งพา conditional rendering ค่อนข้างมาก

Functional Review

Feature Status Notes
View own dashboard Pass พนักงานเข้า dashboard ได้ผ่าน requireEmployeeDashboardAccess()
Summary cards Pass มี approved, pending/revision, rejected, progress
Required hours resolution Partial fallback ไป policy ล่าสุดหรือ 0 ได้ แต่ไม่มีข้อความอธิบายเมื่อไม่พบ policy/target
K / S / A summary Pass คิดจาก approved records เท่านั้น ซึ่งสอดคล้องกับการวัดความคืบหน้า
Recent activities Partial แสดง 5 รายการล่าสุด แต่เรียงตาม createdAt ไม่ใช่ trainingDate
Announcements widget Pass แสดงประกาศล่าสุดและลิงก์ไปหน้ารายละเอียดได้
Notifications page Pass มีหน้ารายการ notifications แยกต่างหาก
Progress bar Partial รองรับ 0 และ partial ได้ แต่กรณีเกินเป้าถูก cap ที่ 100% ทำให้ไม่เห็นว่าทะลุเป้า
Empty states Partial มี empty state ในหลาย widget แต่ไม่มีคำอธิบายกรณีผู้ใช้ยังไม่ผูก employee profile
Error states Partial มี error boundary ครบหลายจุด แต่แสดง raw error.message ต่อผู้ใช้
Responsive layout Pass layout ใช้ grid ที่ยุบลงได้ทั้ง mobile และ desktop
Dashboard filter Partial employee กรองได้ตามปี, admin กรองได้หลายมิติ แต่ใช้ native select และ submit form แบบพื้นฐาน
Refresh behavior Partial ข้อมูลดึงจาก server render ใหม่ได้ แต่หน้า dashboard ไม่มี evidence ชัดเจนเรื่อง targeted invalidation หลัง action อื่น ๆ

Business Rule Review

สิ่งที่สอดคล้อง:

  • Employee เห็นเฉพาะข้อมูลตนเองผ่าน getEmployeeIdsForUser() และเงื่อนไข scope ใน overview-data.ts
  • ชั่วโมง approved ใช้ approvedHours ถ้ามี ไม่เช่นนั้น fallback ไป hours
  • K / S / A คิดจากรายการที่อนุมัติแล้วเท่านั้น
  • ปีที่ใช้กรองแยกจากค่า default ปีปัจจุบัน

จุดที่ควรระวัง:

  • การใช้ปีอ้างอิงด้วย new Date().getUTCFullYear() และช่วงวันแบบ UTC อาจต่างจากการตีความปีตาม timezone ธุรกิจที่ใช้งานจริงปลายปี/ต้นปี
  • หากพนักงานไม่มี employee profile ที่ผูกกับ user ระบบจะคืนค่าศูนย์เกือบทั้งหมด แต่ไม่ได้บอกผู้ใช้ว่าสาเหตุคือ “ยังไม่พบข้อมูลพนักงาน”
  • ความคืบหน้าถูกจำกัดไม่เกิน 100% ทำให้ business outcome แบบ “เกินเป้า” ไม่ถูกสื่อสารบน UI

Role & Permission Review

Employee

ข้อสังเกต:

  • ฝั่ง route protection ถือว่าดี
  • แต่ navigation ไม่สม่ำเสมอ: employee เข้า announcements และ notifications ได้โดยตรง แต่เมนูใน src/config/nav-config.ts ไม่ได้เปิดเส้นทางเหล่านี้ให้เห็นอย่างชัดเจนใน sidebar หลัก

UI Review

จุดที่ดี:

  • ใช้ PageContainer, Card, Badge, Progress และ chart shell เดียวกับโปรเจ็กต์
  • spacing โดยรวมสม่ำเสมอ
  • widget หลักแบ่งเป็น summary + content blocks อ่านง่ายบน desktop

จุดที่ควรปรับ:

  • summary cards render Badge ทุกใบ แม้หลายใบส่ง badge: '' มา ทำให้มี badge ว่างหรือเหลือเพียง icon ใน src/app/dashboard/overview/layout.tsx
  • filter panel ใช้ native <select> แทน shared form/select patterns ของโปรเจ็กต์
  • หน้า employee dashboard ไม่มี notification widget โดยตรง ทั้งที่มี notifications page อยู่แล้ว ทำให้ข้อมูลสำคัญถูกแยกบริบทออกจากหน้าหลัก

UX Review

จุดที่ดี:

  • ผู้ใช้เห็นภาพรวมข้อมูลสำคัญได้เร็ว
  • ประกาศล่าสุดและกิจกรรมล่าสุดช่วยสร้างบริบทการใช้งานประจำวัน

จุดที่ควรปรับ:

  • หากเกิด error ผู้ใช้จะเห็นข้อความเชิงเทคนิคเกินไป
  • หากไม่มีข้อมูลเพราะยังไม่ผูก employee profile, ไม่มี policy, หรือยังไม่เคยส่งรายการ ระบบไม่อธิบายสาเหตุให้ชัด
  • notifications แยกไปอีกหน้า แต่ไม่มีทางเข้าที่เด่นพอจาก dashboard/sidebar สำหรับ employee
  • recent activities ใช้วันฝึกอบรมแสดงผล แต่ใช้เวลาสร้างรายการเป็นตัวเรียงลำดับ ซึ่งอาจขัดกับความคาดหวังว่า “ล่าสุด” หมายถึงฝึกเมื่อไร

Accessibility Review

จุดที่ดี:

  • ใช้ semantic button/link/card ของ shared UI หลายจุด
  • chart components เปิด accessibilityLayer ใน Recharts หลายตัว

จุดที่ควรปรับ:

  • error UI หลายจุดพึ่งพาสีและข้อความทั่วไป แต่ไม่มี recovery action ครบทุก slot
  • native chart summary ไม่มี text alternative เชิงสรุปมากนักสำหรับ screen reader
  • badge/progress สื่อสถานะหลักด้วย visual emphasis มากกว่าคำอธิบายเฉพาะกรณี “เกินเป้า”

Code Quality Review

จุดที่ดี:

  • รวม logic ฝั่ง dashboard ไว้ใน data builder เดียว ทำให้ trace business logic ได้ง่าย
  • type ค่อนข้างชัด และ model ที่ส่งไป component แยกตามการใช้งานจริง
  • ใช้ helper role กลาง เช่น isOrganizationAdminRole()

จุดที่ควรปรับ:

  • OverviewFilterPanel เช็ก admin role ด้วย string comparison ตรง ๆ แทน helper กลาง
  • มีการผสม concern ของ employee/admin dashboard ไว้ใน route เดียวค่อนข้างมาก
  • naming บางส่วนยังติด template เดิม เช่น RecentSales ทั้งที่ใช้แสดงกิจกรรมการอบรม

API Review

สิ่งที่พบ:

  • Dashboard data ฝั่ง overview ดึงตรงจาก server helper ไม่ผ่าน client query เป็นหลัก
  • Notifications ใช้ route handlers และ React Query ตาม pattern โปรเจ็กต์
  • Notifications API validate query params ด้วย Zod

จุดที่ควรปรับ:

  • error responses จาก API ถูกส่งกลับไปยัง UI แล้ว render error.message ตรง ๆ ในบางหน้าของ dashboard/notifications
  • dashboard เองยังไม่มี contract ที่แยก concern เช่น announcements กับ notifications ให้ชัดในหน้าหลัก

Performance Review

จุดที่ดี:

  • getOverviewDashboardData() ถูกห่อด้วย cache() ช่วยลดงานซ้ำใน request เดียว
  • widget หลักเป็น server-driven ลด client state ที่ไม่จำเป็น

จุดที่ควรระวัง:

  • หน้า overview มีหลาย parallel route ที่ต่างเรียก data helper เดียวกัน แม้ cache() จะช่วย แต่โครงสร้างยังค่อนข้างซับซ้อนสำหรับ dashboard ที่ข้อมูลสัมพันธ์กันสูง
  • chart และ card ทั้งหมดโหลดพร้อมกัน ไม่มี loading boundary ระดับหน้า root ของ overview

Security Review

จุดที่ดี:

  • route protection ฝั่ง employee/HRD ใช้งานจริง
  • dashboard scope ข้อมูลตาม organization และ user
  • notifications API จำกัดตาม organizationId และ userId

ความเสี่ยง:


Consistency Review

สอดคล้อง:

  • ใช้ shared UI stack และ page guard ตามแนวทางโปรเจ็กต์
  • notifications page ใช้ React Query + HydrationBoundary ตาม pattern กลาง

ไม่สอดคล้อง:

  • dashboard table-like recent activity ใช้ custom list ซึ่งโอเคตามลักษณะข้อมูล แต่ naming ยังโยงกับ template เดิม
  • filter panel ของ overview ยังไม่ใช้ shared select/form wrappers แบบที่หลาย feature ใช้อยู่
  • employee-accessible pages บางหน้าไม่มีเมนูนำทางที่สอดคล้องกับสิทธิ์จริง

Edge Case Review

  • New employee: แสดงค่า 0 ได้ แต่ไม่บอกว่าเพราะยังไม่มีข้อมูลพนักงาน/ประวัติอบรม
  • No training records: รองรับได้
  • All completed: รองรับได้
  • Over target: คำนวณได้ แต่ UI ไม่สื่อส่วนเกิน
  • Missing category: K / S / A จะเหลือค่าตามข้อมูลที่มี แต่ไม่ได้อธิบาย category ที่หาย
  • Missing policy: fallback เป็น 0 ได้ แต่ไม่มี warning/explanation
  • Missing profile: ไม่พัง แต่ silent failure ในเชิง UX
  • API failure: มี error UI แต่ข้อความไม่เหมาะกับผู้ใช้ปลายทาง
  • Slow network: มี loading เฉพาะบาง slot แต่ไม่มี root loading ของ overview

Problems Found

High

  • Description: Error UI หลายจุดแสดง raw error.message ต่อผู้ใช้โดยตรง

  • File: overview/error.tsx, @sales/error.tsx, @area_stats/error.tsx, @pie_stats/error.tsx, notifications-page.tsx

  • Component: OverviewError, SalesError, AreaStatsError, PieStatsError, NotificationsPage

  • Severity: High

  • Impact: เสี่ยงเปิดเผยข้อมูลเชิงเทคนิคจาก backend/runtime และทำให้ UX ของผู้ใช้ปลายทางสับสน

  • Recommendation: แสดงข้อความกลางที่ปลอดภัยต่อผู้ใช้ และ log รายละเอียดไว้ภายในแทน

  • Description: Dashboard ไม่สื่อกรณี “เกินเป้าชั่วโมง” แม้ข้อมูลคำนวณถึงเป้าแล้ว

  • File: overview-data.ts, ksa-progress.tsx

  • Component: buildEmployeeCards, KsaProgress

  • Severity: High

  • Impact: ผู้ใช้ที่ทำเกินเป้าจะเห็นเพียง 100% เท่ากับผู้ใช้ที่เพิ่งแตะเป้า ทำให้สูญเสียข้อมูลเชิงบริหารและแรงจูงใจ

  • Recommendation: แยกสถานะ completed กับ exceeded และสื่อส่วนเกินบน UI

Medium

  • Description: Recent activities เรียงด้วย createdAt แต่แสดงวันที่เป็น trainingDate

  • File: overview-data.ts, recent-sales.tsx

  • Component: getOverviewDashboardData, RecentSales

  • Severity: Medium

  • Impact: ผู้ใช้อาจเข้าใจว่าเป็น “อบรมล่าสุดตามวันที่ฝึก” ทั้งที่จริงคือ “รายการที่สร้างล่าสุด”

  • Recommendation: เลือกให้ชัดว่าจะเรียงตามวันฝึกหรือวันส่ง และทำ label ให้ตรง

  • Description: พนักงานเข้าหน้า notifications และ announcements ได้ แต่ navigation หลักไม่สอดคล้องกับสิทธิ์จริง

  • File: nav-config.ts, notifications/page.tsx, announcements/page.tsx

  • Component: sidebar config, employee-accessible routes

  • Severity: Medium

  • Impact: discoverability ต่ำ ผู้ใช้เข้าฟีเจอร์ที่มีสิทธิ์ได้ยาก และ navigation model ไม่สม่ำเสมอ

  • Recommendation: ทบทวนเมนู employee ให้ตรงกับ route ที่เปิดใช้งานจริง

  • Description: Summary cards แสดง badge แม้ไม่มีข้อความ badge จริง

  • File: overview/layout.tsx

  • Component: OverViewLayout

  • Severity: Medium

  • Impact: UI ดูไม่ตั้งใจและเพิ่ม noise ในส่วนสรุปสำคัญ

  • Recommendation: render badge แบบมีเงื่อนไข หรือกำหนดข้อความ badge ให้ครบ

  • Description: กรณี user ไม่มี employee profile / ไม่มี policy ระบบแสดงศูนย์อย่างเงียบ ๆ

  • File: overview-data.ts

  • Component: getOverviewDashboardData

  • Severity: Medium

  • Impact: ผู้ใช้และทีม support แยกไม่ออกว่า “ไม่มีข้อมูล” หรือ “ข้อมูลเชื่อมไม่ครบ”

  • Recommendation: เพิ่ม state หรือ messaging ที่อธิบายสาเหตุของข้อมูลว่าง

Low

  • Description: OverviewFilterPanel ใช้ string compare role ตรง ๆ แทน helper กลาง

  • File: overview-filter-panel.tsx

  • Component: OverviewFilterPanel

  • Severity: Low

  • Impact: ลดความสม่ำเสมอของโค้ดและเพิ่มโอกาส logic drift

  • Recommendation: ใช้ role helper กลางเดียวกับส่วนอื่น

  • Description: ชื่อ component RecentSales ไม่ตรงกับหน้าที่จริง

  • File: recent-sales.tsx

  • Component: RecentSales

  • Severity: Low

  • Impact: อ่านโค้ดยากขึ้นและสะท้อนร่องรอยจาก template เดิม

  • Recommendation: เปลี่ยนชื่อให้ตรงกับโดเมนเมื่อมีรอบ refactor

  • Description: หน้า overview ไม่มี root loading state ของ route หลัก

  • File: src/app/dashboard/overview

  • Component: overview route

  • Severity: Low

  • Impact: initial load ของทั้งหน้าอาจดูนิ่งเกินไปหาก data ช้า และพึ่ง loading ของบาง slot เท่านั้น

  • Recommendation: เพิ่ม loading ระดับหน้าให้สอดคล้องกับหน้า dashboard สำคัญอื่น ๆ


Missing Features

  • Notification summary/widget บนหน้า dashboard สำหรับ employee
  • ข้อความอธิบายกรณีไม่พบ employee profile หรือ policy เป้าหมาย
  • การสื่อสารสถานะ “เกินเป้า” แยกจาก “ครบเป้า”
  • Navigation entry ที่สอดคล้องกับ employee-accessible notifications/announcements

Improvement Opportunities

  • แยก employee dashboard semantics ให้ชัดกว่าการ reuse overview route ร่วมกับ HRD
  • เพิ่ม domain-specific naming ให้ component/slot เพื่อให้อ่านโค้ดง่ายขึ้น
  • ทำ filter panel ให้ใช้ shared form/select stack ของโปรเจ็กต์
  • เพิ่ม explanatory empty states ตามสาเหตุจริงของข้อมูลว่าง
  • เพิ่ม text summary สำหรับ charts เพื่อช่วยทั้ง accessibility และการอ่านค่าบน mobile

Final Assessment

Architecture: 8/10

Functionality: 7.5/10

UI: 7/10

UX: 6.8/10

Accessibility: 6.8/10

Performance: 7.5/10

Security: 7.5/10

Maintainability: 7.6/10

Overall Score: 7.3/10