|
1
|
# สรุปข้อกำหนด: ระบบแสดง Icon Status บนการ์ดพนักงาน
|
|
2
|
|
|
3
|
## ภาพรวม
|
|
4
|
|
|
5
|
พัฒนาระบบแสดงไอคอนและป้ายสถานะ (Status Icon & Label) บนการ์ดพนักงานในหน้าเลือกช่างซ่อมบำรุง (AssignMaintenanceSupportDrawer) แทนที่ placeholder "ICON" และ "STATUS" ที่มีอยู่ใน `EmployeeItem` component
|
|
6
|
|
|
7
|
## ที่มาของข้อมูลสถานะ
|
|
8
|
|
|
9
|
ข้อมูลสถานะมาจาก API (`/call-terminal/employee`) ซึ่ง query จาก DB โดยตรง:
|
|
10
|
|
|
11
|
- **is_available** (boolean): `true` = ไม่มีงาน `during work` ที่ active อยู่ (ว่างรับงานได้)
|
|
12
|
- **is_working** (boolean): `true` = มีงาน `during work` ที่ active และไม่ถูก suspend (กำลังทำงานอยู่)
|
|
13
|
- **Off Shift**: คำนวณจาก `ms_shift` — พนักงานไม่อยู่ในช่วงเวลากะที่ตั้งไว้ (`start_ts` / `end_ts`)
|
|
14
|
|
|
15
|
> ⚠️ ปัจจุบัน API ยังไม่มี field `is_off_shift` — ต้องเพิ่ม logic คำนวณจาก `ms_shift` (เทียบเวลาปัจจุบันกับ `start_ts`/`end_ts` ของกะที่ผูกกับพนักงานผ่าน `work_shift_id`)
|
|
16
|
|
|
17
|
### SQL Query ปัจจุบัน (employee.repository.ts)
|
|
18
|
|
|
19
|
```sql
|
|
20
|
-- is_available: ไม่มีงาน during work
|
|
21
|
NOT EXISTS (
|
|
22
|
SELECT 1 FROM mp_request_work_worker mp
|
|
23
|
JOIN request_work rw ON rw.id = mp.request_work_id
|
|
24
|
WHERE mp.employee_id = me.id
|
|
25
|
AND mp.worker_type = 'employee'
|
|
26
|
AND mp.is_active = TRUE
|
|
27
|
AND rw.current_status = 'during work'
|
|
28
|
) AS is_available
|
|
29
|
|
|
30
|
-- is_working: มีงาน during work ที่ไม่ถูก suspend
|
|
31
|
EXISTS (
|
|
32
|
SELECT 1 FROM mp_request_work_worker mp
|
|
33
|
JOIN request_work rw ON rw.id = mp.request_work_id
|
|
34
|
WHERE mp.employee_id = me.id
|
|
35
|
AND mp.worker_type = 'employee'
|
|
36
|
AND mp.is_active = TRUE
|
|
37
|
AND rw.current_status = 'during work'
|
|
38
|
AND rw.is_suspension = FALSE
|
|
39
|
) AS is_working
|
|
40
|
```
|
|
41
|
|
|
42
|
### ตรรกะ Off Shift (ต้องเพิ่มใหม่)
|
|
43
|
|
|
44
|
```
|
|
45
|
Off Shift = เวลาปัจจุบัน ไม่อยู่ในช่วง ms_shift.start_ts ~ ms_shift.end_ts
|
|
46
|
ของกะที่ผูกกับพนักงาน (ms_employee.work_shift_id → ms_shift.id)
|
|
47
|
```
|
|
48
|
|
|
49
|
## เลย์เอาต์การ์ดพนักงาน
|
|
50
|
|
|
51
|
### โครงสร้างทั่วไป
|
|
52
|
|
|
53
|
```
|
|
54
|
┌─────────────────────────────────┐
|
|
55
|
│ ชื่อ นามสกุล (bold) ● ← จุดสี (Status Dot)
|
|
56
|
│ │
|
|
57
|
│ ⊙ Available │ ← ไอคอน + ข้อความสถานะ
|
|
58
|
└─────────────────────────────────┘
|
|
59
|
```
|
|
60
|
|
|
61
|
- **แถวบน**: ชื่อพนักงาน (ซ้าย) + วงกลมสีทึบ 16x16px (ขวา)
|
|
62
|
- **แถวล่าง**: ไอคอน 16x16px + ข้อความสถานะ (ชิดซ้าย, gap-2)
|
|
63
|
|
|
64
|
### ตัวอย่างการ์ดแต่ละสถานะ
|
|
65
|
|
|
66
|
#### ✅ Available (ว่าง) — สีเขียว
|
|
67
|
|
|
68
|
```
|
|
69
|
┌─────────────────────────────────┐
|
|
70
|
│ │
|
|
71
|
│ Anupong Chaiya 🟢 │ ← bg-green-500
|
|
72
|
│ │
|
|
73
|
│ ✓ Available │ ← CheckCircle + text-green-600
|
|
74
|
│ │
|
|
75
|
└─────────────────────────────────┘
|
|
76
|
border: border-gray-300 (ปกติ)
|
|
77
|
bg: bg-white
|
|
78
|
```
|
|
79
|
|
|
80
|
#### 🔧 Working (กำลังทำงาน) — สีน้ำเงิน
|
|
81
|
|
|
82
|
```
|
|
83
|
┌─────────────────────────────────┐
|
|
84
|
│ │
|
|
85
|
│ Chaiyong Siripong 🔵 │ ← bg-blue-500
|
|
86
|
│ │
|
|
87
|
│ 🕐 Working │ ← Clock + text-blue-600
|
|
88
|
│ │
|
|
89
|
└─────────────────────────────────┘
|
|
90
|
border: border-gray-300 (ปกติ)
|
|
91
|
bg: bg-white
|
|
92
|
```
|
|
93
|
|
|
94
|
#### 🚫 Off Shift (ไม่อยู่กะ) — สีเทา
|
|
95
|
|
|
96
|
```
|
|
97
|
┌─────────────────────────────────┐
|
|
98
|
│ │
|
|
99
|
│ Somchai Three ⚪ │ ← bg-gray-400
|
|
100
|
│ │
|
|
101
|
│ ⊘ Off Shift │ ← MinusCircle + text-gray-400
|
|
102
|
│ │
|
|
103
|
└─────────────────────────────────┘
|
|
104
|
border: border-gray-300 (ปกติ)
|
|
105
|
bg: bg-white
|
|
106
|
```
|
|
107
|
|
|
108
|
#### 🔲 เมื่อถูกเลือก (Selected) — ทุกสถานะเหมือนกัน
|
|
109
|
|
|
110
|
```
|
|
111
|
┌═════════════════════════════════┐
|
|
112
|
│ │ ← border-blue-600 (เส้นขอบน้ำเงิน)
|
|
113
|
│ Sakchai Rattana 🟢 │ bg-blue-50 (พื้นหลังฟ้าอ่อน)
|
|
114
|
│ │
|
|
115
|
│ ✓ Available │
|
|
116
|
│ │
|
|
117
|
└═════════════════════════════════┘
|
|
118
|
border: border-blue-600 (เลือกแล้ว)
|
|
119
|
bg: bg-blue-50
|
|
120
|
```
|
|
121
|
|
|
122
|
### ตัวอย่างหน้าจอรวม (Grid 4 คอลัมน์)
|
|
123
|
|
|
124
|
```
|
|
125
|
⚡ Electrician 12
|
|
126
|
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
127
|
│ Anupong Chaiya 🟢│ │ Ganya Ratchada 🟢│ │ Sakchai Rattan 🟢│ │ Chaiyong Siri 🔵│
|
|
128
|
│ ✓ Available │ │ ✓ Available │ │ ✓ Available │ │ 🕐 Working │
|
|
129
|
└──────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘
|
|
130
|
|
|
131
|
🔧 Mechanic 5
|
|
132
|
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
133
|
│ Somchai Three ⚪│ │ User One 🟢│ │ Prasit Kaew 🔵│ │ Wichai Suk ⚪│
|
|
134
|
│ ⊘ Off Shift │ │ ✓ Available │ │ 🕐 Working │ │ ⊘ Off Shift │
|
|
135
|
└──────────────────┘ └──────────────────┘ └──────────────────┘ └──────────────────┘
|
|
136
|
```
|
|
137
|
|
|
138
|
### ตัวอย่างปุ่มกรองสถานะ
|
|
139
|
|
|
140
|
```
|
|
141
|
┌───────────────────────────────────────────────────────────────────────────────────┐
|
|
142
|
│ 🔍 Search maintenance staff... [ All ] [ Available ] [ Working ] [Off Shift]│
|
|
143
|
│ (red) (green) (blue) (gray) │
|
|
144
|
└───────────────────────────────────────────────────────────────────────────────────┘
|
|
145
|
```
|
|
146
|
|
|
147
|
- **All** (แดง): แสดงทุกคน
|
|
148
|
- **Available** (เขียว): แสดงเฉพาะ `is_available = true` AND `is_off_shift = false`
|
|
149
|
- **Working** (น้ำเงิน): แสดงเฉพาะ `is_working = true` AND `is_off_shift = false`
|
|
150
|
- **Off Shift** (เทา): แสดงเฉพาะ `is_off_shift = true`
|
|
151
|
|
|
152
|
## สถานะทั้ง 3 แบบ (Priority: Off Shift > Working > Available)
|
|
153
|
|
|
154
|
| สถานะ | เงื่อนไข | สี Dot | ไอคอน | ข้อความ | สีข้อความ/ไอคอน |
|
|
155
|
|---|---|---|---|---|---|
|
|
156
|
| Off Shift | ไม่อยู่ในเวลากะ (`is_off_shift = true`) | ⚪ bg-gray-400 | MinusCircle | "Off Shift" | text-gray-400 |
|
|
157
|
| Working | ถูก assign เครื่อง + งานเป็น `during work` (`is_working = true`) | 🔵 bg-blue-500 | Clock | "Working" | text-blue-600 |
|
|
158
|
| Available | อยู่ในกะ + ไม่ได้ทำงาน (`is_available = true` AND `is_off_shift = false`) | 🟢 bg-green-500 | CheckCircle | "Available" | text-green-600 |
|
|
159
|
|
|
160
|
> Priority: ถ้า Off Shift → แสดง Off Shift เสมอ (แม้จะมีงาน assign อยู่)
|
|
161
|
|
|
162
|
## ไฟล์ที่เกี่ยวข้อง
|
|
163
|
|
|
164
|
### Frontend (Web)
|
|
165
|
|
|
166
|
| ไฟล์ | สิ่งที่ต้องทำ |
|
|
167
|
|---|---|
|
|
168
|
| `EmployeeItem.tsx` | แทนที่ placeholder ด้วย status dot, icon, label จริง |
|
|
169
|
| `assign-maintenance.model.ts` | เพิ่ม `is_available`, `is_working`, `is_off_shift` ใน EmployeeRecordData interface |
|
|
170
|
| `useInteralAssignMaintenanceDrawer.ts` | เพิ่ม filter logic สำหรับ "offShift" โดยใช้ `is_off_shift` |
|
|
171
|
|
|
172
|
### Backend (API)
|
|
173
|
|
|
174
|
| ไฟล์ | สิ่งที่ต้องทำ |
|
|
175
|
|---|---|
|
|
176
|
| `employee.repository.ts` | เพิ่ม SQL คำนวณ `is_off_shift` จาก `ms_shift` (เทียบ `start_ts`/`end_ts` กับเวลาปัจจุบัน) |
|
|
177
|
| `employee.model.ts` | เพิ่ม `is_off_shift: boolean` ใน `EmployeeForGroupByWorkType` interface |
|
|
178
|
|
|
179
|
## รายการข้อกำหนด (8 ข้อ)
|
|
180
|
|
|
181
|
### 1. เพิ่ม is_off_shift ใน API (Backend)
|
|
182
|
- เพิ่ม SQL subquery ใน `employee.repository.ts` เพื่อคำนวณ `is_off_shift`
|
|
183
|
- เทียบเวลาปัจจุบันกับ `ms_shift.start_ts` / `ms_shift.end_ts` ผ่าน `ms_employee.work_shift_id`
|
|
184
|
- เพิ่ม `is_off_shift: boolean` ใน `EmployeeForGroupByWorkType` interface (API model)
|
|
185
|
|
|
186
|
### 2. ขยาย EmployeeRecordData Model (Frontend)
|
|
187
|
- เพิ่มฟิลด์ `is_available: boolean`, `is_working: boolean`, `is_off_shift: boolean` ใน interface
|
|
188
|
- ให้ EmployeeItem ใช้งานได้โดยไม่มี type error
|
|
189
|
|
|
190
|
### 3. ตรรกะคำนวณสถานะ (Status Derivation Logic)
|
|
191
|
- `is_off_shift = true` → "offShift" (priority สูงสุด)
|
|
192
|
- `is_working = true` → "working"
|
|
193
|
- `is_available = true` AND `is_off_shift = false` → "available"
|
|
194
|
- Working = ช่างที่ถูก assign เครื่องไว้ + สถานะงาน `during work`
|
|
195
|
- Off Shift = ไม่อยู่ในเวลาที่ตั้งไว้ใน `ms_shift`
|
|
196
|
|
|
197
|
### 4. วงกลมสีสถานะ (Status Dot)
|
|
198
|
- แสดงจุดกลมทึบ 16x16px ที่มุมขวาบนของการ์ด
|
|
199
|
- สี: เขียว (available), น้ำเงิน (working), เทา (offShift)
|
|
200
|
|
|
201
|
### 5. ไอคอนสถานะ (Status Icon)
|
|
202
|
- แสดงใต้ชื่อพนักงาน ฝั่งซ้าย ขนาด 16x16px
|
|
203
|
- ไอคอน: CheckCircle (available), Clock (working), MinusCircle (offShift)
|
|
204
|
- ใช้ lucide-react
|
|
205
|
|
|
206
|
### 6. ข้อความสถานะ (Status Label)
|
|
207
|
- แสดงคู่กับไอคอน: "Available", "Working", "Off Shift"
|
|
208
|
- สีตรงกับสถานะ
|
|
209
|
|
|
210
|
### 7. เลือกได้ทุกสถานะ — ไม่ล็อก
|
|
211
|
- พนักงานทุกสถานะ (available, working, offShift) กดเลือกได้หมด
|
|
212
|
- เมื่อเลือกแล้วแสดง border-blue-600, bg-blue-50 เหมือนกันทุกสถานะ
|
|
213
|
|
|
214
|
### 8. ตัวกรอง Off Shift
|
|
215
|
- เพิ่ม filter logic: เมื่อเลือก "Off Shift" → แสดงเฉพาะพนักงานที่ `is_off_shift = true`
|
|
216
|
- ปุ่มกรองที่มีอยู่แล้ว: All, Available, Working, Off Shift
|