Initial commit
This commit is contained in:
93
frontend/src/pages/Dashboard.tsx
Normal file
93
frontend/src/pages/Dashboard.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user