94 lines
3.0 KiB
TypeScript
94 lines
3.0 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { api } from '../lib/api';
|
|
import { AttendanceDto, LeaveDto, NotificationDto } from '../types';
|
|
|
|
export default function Dashboard() {
|
|
const [attendance, setAttendance] = useState<AttendanceDto[]>([]);
|
|
const [leaves, setLeaves] = useState<LeaveDto[]>([]);
|
|
const [notifications, setNotifications] = useState<NotificationDto[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
Promise.all([
|
|
api.get<AttendanceDto[]>('/attendance/me'),
|
|
api.get<LeaveDto[]>('/leave-requests/my'),
|
|
api.get<NotificationDto[]>('/notifications'),
|
|
])
|
|
.then(([a, l, n]) => {
|
|
setAttendance(a);
|
|
setLeaves(l);
|
|
setNotifications(n);
|
|
})
|
|
.finally(() => setLoading(false));
|
|
}, []);
|
|
|
|
return (
|
|
<div className="page animate-in">
|
|
<div className="page-header">
|
|
<h1>概览</h1>
|
|
<p>关键指标一览与近期动态</p>
|
|
</div>
|
|
|
|
<div className="grid stats">
|
|
<div className="card">
|
|
<div className="card-title">本月考勤记录</div>
|
|
<div className="stat">{attendance.length}</div>
|
|
<div className="stat-sub">来自个人记录</div>
|
|
</div>
|
|
<div className="card">
|
|
<div className="card-title">我的请假申请</div>
|
|
<div className="stat">{leaves.length}</div>
|
|
<div className="stat-sub">当前与历史</div>
|
|
</div>
|
|
<div className="card">
|
|
<div className="card-title">通知</div>
|
|
<div className="stat">{notifications.length}</div>
|
|
<div className="stat-sub">含未读与已读</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid cols-2">
|
|
<div className="card">
|
|
<div className="card-title">最近考勤</div>
|
|
{loading ? (
|
|
<div className="muted">加载中...</div>
|
|
) : (
|
|
<table className="table">
|
|
<thead>
|
|
<tr>
|
|
<th>日期</th>
|
|
<th>签到</th>
|
|
<th>签退</th>
|
|
<th>工时</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{attendance.slice(0, 5).map((a) => (
|
|
<tr key={a.id}>
|
|
<td>{a.date}</td>
|
|
<td>{a.checkInTime || '-'}</td>
|
|
<td>{a.checkOutTime || '-'}</td>
|
|
<td>{a.workHours}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
</div>
|
|
|
|
<div className="card">
|
|
<div className="card-title">最近通知</div>
|
|
<div className="list-stagger">
|
|
{notifications.slice(0, 5).map((n) => (
|
|
<div key={n.id} className={`list-item ${n.readAt ? 'read' : ''}`}>
|
|
<div className="list-title">{n.title}</div>
|
|
<div className="list-sub">{n.content}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|