Add /api/attendance/status_texts endpoint; dynamic status filter in Attendance List (filter by status_text LIKE)

This commit is contained in:
IT狗
2026-07-21 18:02:32 +08:00
parent ef57d7cd59
commit 4c9db01213
2 changed files with 31 additions and 9 deletions
+12
View File
@@ -1098,6 +1098,15 @@ async def dashboard_summary(
"staff_count": len(staff_set),
}
@app.get("/api/attendance/status_texts")
async def attendance_status_texts(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Return distinct status_text values for filter dropdown."""
rows = db.query(AttendanceRecord.status_text).distinct().all()
return [r[0] for r in rows if r[0]]
@app.get("/api/attendance/stats")
async def attendance_stats(
date_from: Optional[str] = None,
@@ -1234,6 +1243,7 @@ async def attendance_records(
date_to: Optional[str] = None,
staff: Optional[str] = None,
status: Optional[str] = None,
status_text: Optional[str] = None,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
@@ -1256,6 +1266,8 @@ async def attendance_records(
q = q.filter(AttendanceRecord.employee_name == staff)
if status:
q = q.filter(AttendanceRecord.status_code == status)
if status_text:
q = q.filter(AttendanceRecord.status_text.like(f"%{status_text}%"))
if search:
like = f"%{search}%"
q = q.filter(or_(
+19 -9
View File
@@ -17,15 +17,24 @@ export default function AttendanceList() {
const [staffFilter, setStaffFilter] = useState('')
const [statusFilter, setStatusFilter] = useState('')
const [page, setPage] = useState(1)
const [statusOptions, setStatusOptions] = useState([])
const perPage = 50
const [staffList, setStaffList] = useState([])
useEffect(() => {
fetchRecords()
fetchStatusTexts()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [page, sortBy, sortOrder, dateFrom, dateTo, staffFilter, statusFilter, search])
const fetchStatusTexts = async () => {
try {
const { data } = await api.get('/api/attendance/status_texts')
setStatusOptions(data || [])
} catch (e) { console.error('Failed to fetch status texts', e) }
}
const fetchRecords = async () => {
setLoading(true)
try {
@@ -39,7 +48,7 @@ export default function AttendanceList() {
if (dateFrom) params.append('date_from', dateFrom)
if (dateTo) params.append('date_to', dateTo)
if (staffFilter) params.append('staff', staffFilter)
if (statusFilter) params.append('status', statusFilter)
if (statusFilter) params.append('status_text', statusFilter)
const { data } = await api.get(`/api/attendance/records?${params}`)
setRecords(data.records || [])
@@ -175,17 +184,18 @@ export default function AttendanceList() {
<div className="flex items-center gap-2">
<span className="text-xs text-slate-500">狀態:</span>
<select
value={statusFilter}
onChange={(e) => { setStatusFilter(e.target.value); setPage(1) }}
value={statusFilter ? statusFilter : (statusTextFilter || '')}
onChange={(e) => {
setStatusFilter(e.target.value)
setStatusTextFilter('')
setPage(1)
}}
className="text-sm border border-slate-300 rounded-md px-2 py-1.5"
>
<option value="">全部</option>
<option value="normal">正常</option>
<option value="late">遲到</option>
<option value="early">早退</option>
<option value="ot">OT</option>
<option value="abnormal">異常</option>
<option value="missing">缺勤</option>
{statusOptions.map(opt => (
<option key={opt} value={opt}>{opt}</option>
))}
</select>
</div>