feat: 预入职状态+入职手续待办
1. 花名册动态状态:hireDate > today 显示「预入职」(蓝色标签) 2. RiskType 增加 ONBOARDING 枚举 3. 新增 detectOnboardingRisks:入职日期到但未签合同→生成待办 4. detectContractRisks 排除预入职员工(未到入职日期不检查合同) 5. Dashboard 风险提醒 tab 支持 ONBOARDING 待办类型 6. 前端类型定义同步更新
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react'
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Users, AlertTriangle, CheckSquare, DollarSign, ArrowRight, RefreshCw, FileText, Calendar, TrendingUp, Briefcase, Calculator, Wallet, Building2, Receipt, Check, X, Clock, LayoutDashboard, ListTodo, ShieldAlert } from 'lucide-react'
|
||||
import { Users, AlertTriangle, CheckSquare, DollarSign, ArrowRight, RefreshCw, FileText, Calendar, TrendingUp, Briefcase, Calculator, Wallet, Building2, Receipt, Check, X, Clock, LayoutDashboard, ListTodo, ShieldAlert, UserPlus } from 'lucide-react'
|
||||
import api from '../lib/api'
|
||||
import Card from '../components/ui/Card'
|
||||
import Button from '../components/ui/Button'
|
||||
@@ -18,6 +18,7 @@ const TODO_ICON_CONFIG: Record<string, { icon: typeof FileText; color: string; b
|
||||
SALARY: { icon: DollarSign, color: 'text-amber-600', bg: 'bg-amber-50' },
|
||||
TERMINATION: { icon: ShieldAlert, color: 'text-red-600', bg: 'bg-red-50' },
|
||||
MONTHLY: { icon: Calendar, color: 'text-purple-600', bg: 'bg-purple-50' },
|
||||
ONBOARDING: { icon: UserPlus, color: 'text-cyan-600', bg: 'bg-cyan-50' },
|
||||
}
|
||||
|
||||
function TodoIcon({ type, level }: { type: string; level: string }) {
|
||||
@@ -53,7 +54,7 @@ export default function Dashboard() {
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['dashboard'] }),
|
||||
})
|
||||
|
||||
const riskTodos = data?.todos.filter((t) => t.type === 'CONTRACT' || t.type === 'TERMINATION') || []
|
||||
const riskTodos = data?.todos.filter((t) => t.type === 'CONTRACT' || t.type === 'TERMINATION' || t.type === 'ONBOARDING') || []
|
||||
const taskTodos = data?.todos.filter((t) => t.type === 'MONTHLY' || t.type === 'SALARY') || []
|
||||
const filteredTodos = activeTab === 'risk' ? riskTodos : taskTodos
|
||||
|
||||
|
||||
@@ -133,8 +133,12 @@ export default function Roster() {
|
||||
<td className="py-2 px-3 font-medium">{e.name}</td>
|
||||
<td className="py-2 px-3 text-gray-500">{e.department}</td>
|
||||
<td className="py-2 px-3">
|
||||
<span className={`px-2 py-0.5 rounded text-xs ${e.status === 'ACTIVE' ? 'bg-green-50 text-safe' : 'bg-gray-100 text-gray-500'}`}>
|
||||
{e.status === 'ACTIVE' ? '在职' : '离职'}
|
||||
<span className={`px-2 py-0.5 rounded text-xs ${
|
||||
e.status === 'ACTIVE' ? 'bg-green-50 text-safe'
|
||||
: e.status === 'PRE_HIRE' ? 'bg-blue-50 text-blue-600'
|
||||
: 'bg-gray-100 text-gray-500'
|
||||
}`}>
|
||||
{e.status === 'ACTIVE' ? '在职' : e.status === 'PRE_HIRE' ? '预入职' : '离职'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-2 px-3 text-gray-500">{e.hireDate?.toString().slice(0, 10)}</td>
|
||||
|
||||
@@ -85,7 +85,7 @@ export interface DashboardData {
|
||||
}
|
||||
todos: {
|
||||
id: string
|
||||
type: 'CONTRACT' | 'SALARY' | 'TERMINATION' | 'MONTHLY'
|
||||
type: 'CONTRACT' | 'SALARY' | 'TERMINATION' | 'MONTHLY' | 'ONBOARDING'
|
||||
level: 'high' | 'medium' | 'low'
|
||||
title: string
|
||||
description: string
|
||||
@@ -93,7 +93,7 @@ export interface DashboardData {
|
||||
}[]
|
||||
resolvedTodos: {
|
||||
id: string
|
||||
type: 'CONTRACT' | 'SALARY' | 'TERMINATION' | 'MONTHLY'
|
||||
type: 'CONTRACT' | 'SALARY' | 'TERMINATION' | 'MONTHLY' | 'ONBOARDING'
|
||||
level: 'high' | 'medium' | 'low'
|
||||
title: string
|
||||
description: string
|
||||
|
||||
Reference in New Issue
Block a user