import React, { useState, useRef } from 'react'; import { JobPosition } from '@/data/jobs'; import { JobDataManager } from '@/lib/dataManager'; import { SecurityManager, validateAccessCode } from '@/lib/security'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { useToast } from '@/hooks/use-toast'; import { Shield, Upload, Download, History, Plus, Edit2, Trash2, X, AlertTriangle, FileText, FileSpreadsheet } from 'lucide-react'; interface AdminPanelProps { isOpen: boolean; onClose: () => void; jobs: JobPosition[]; onJobsUpdate: (jobs: JobPosition[]) => void; } export function AdminPanel({ isOpen, onClose, jobs, onJobsUpdate }: AdminPanelProps) { const [isAuthenticated, setIsAuthenticated] = useState(false); const [accessCode, setAccessCode] = useState(''); const [isValidating, setIsValidating] = useState(false); const [editingJob, setEditingJob] = useState(null); const [isJobDialogOpen, setIsJobDialogOpen] = useState(false); const [showDeleteAllDialog, setShowDeleteAllDialog] = useState(false); const [deleteAllConfirmText, setDeleteAllConfirmText] = useState(''); const fileInputRef = useRef(null); const { toast } = useToast(); // Handle authentication const handleLogin = async () => { if (SecurityManager.isLocked()) { const remainingTime = Math.ceil(SecurityManager.getRemainingLockTime() / 60000); toast({ title: "访问被锁定", description: `请 ${remainingTime} 分钟后再试`, variant: "destructive" }); return; } setIsValidating(true); try { const isValid = await validateAccessCode(accessCode); if (isValid) { setIsAuthenticated(true); SecurityManager.resetAttempts(); toast({ title: "认证成功", description: "欢迎进入管理面板" }); } else { SecurityManager.recordFailedAttempt(); const attempts = SecurityManager.getFailedAttempts(); toast({ title: "访问码错误", description: `剩余尝试次数: ${3 - attempts}`, variant: "destructive" }); } } catch (error) { toast({ title: "认证失败", description: "系统错误,请重试", variant: "destructive" }); } setIsValidating(false); setAccessCode(''); }; // Handle file import const handleFileImport = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; try { let importedJobs: JobPosition[]; if (file.name.endsWith('.json')) { importedJobs = await JobDataManager.importFromJSON(file); } else if (file.name.endsWith('.xlsx') || file.name.endsWith('.xls')) { importedJobs = await JobDataManager.importFromXLSX(file); } else { throw new Error('不支持的文件格式,请使用 JSON 或 Excel 文件'); } const updatedJobs = [...jobs, ...importedJobs]; onJobsUpdate(updatedJobs); JobDataManager.saveJobs(updatedJobs); toast({ title: "导入成功", description: `成功导入 ${importedJobs.length} 个职位` }); } catch (error) { toast({ title: "导入失败", description: error.message, variant: "destructive" }); } // Reset file input if (fileInputRef.current) { fileInputRef.current.value = ''; } }; // Handle job operations const handleAddJob = () => { setEditingJob({ id: `job_${Date.now()}`, title: '', department: '', priority: 'normal', url: '', description: [], requirements: [] }); setIsJobDialogOpen(true); }; const handleEditJob = (job: JobPosition) => { setEditingJob({ ...job }); setIsJobDialogOpen(true); }; const handleDeleteJob = (jobId: string) => { const updatedJobs = jobs.filter(job => job.id !== jobId); onJobsUpdate(updatedJobs); JobDataManager.saveJobs(updatedJobs); toast({ title: "删除成功", description: "职位已删除" }); }; const handleDeleteAllJobs = () => { if (deleteAllConfirmText !== 'DELETE ALL') { toast({ title: "确认文本错误", description: "请输入 'DELETE ALL' 来确认删除", variant: "destructive" }); return; } // Save current jobs to history before deleting all JobDataManager.saveJobs(jobs); // Clear all jobs const emptyJobs: JobPosition[] = []; onJobsUpdate(emptyJobs); JobDataManager.saveJobs(emptyJobs); setShowDeleteAllDialog(false); setDeleteAllConfirmText(''); toast({ title: "删除成功", description: `已删除所有 ${jobs.length} 个职位`, variant: "destructive" }); }; const handleSaveJob = (job: JobPosition) => { const existingIndex = jobs.findIndex(j => j.id === job.id); let updatedJobs: JobPosition[]; if (existingIndex >= 0) { updatedJobs = [...jobs]; updatedJobs[existingIndex] = job; } else { updatedJobs = [...jobs, job]; } onJobsUpdate(updatedJobs); JobDataManager.saveJobs(updatedJobs); setIsJobDialogOpen(false); setEditingJob(null); toast({ title: existingIndex >= 0 ? "更新成功" : "添加成功", description: "职位信息已保存" }); }; // Handle history restoration const handleRestoreHistory = (timestamp: number) => { const restoredJobs = JobDataManager.restoreFromHistory(timestamp); if (restoredJobs) { onJobsUpdate(restoredJobs); JobDataManager.saveJobs(restoredJobs); toast({ title: "恢复成功", description: "数据已恢复到选定版本" }); } }; if (!isOpen) return null; return ( onClose()}> MiniMax AI Infra/算法 团队管理系统 数据管理和配置面板 {!isAuthenticated ? (
身份验证 请输入访问码以继续 {SecurityManager.isLocked() && (
访问已被锁定 {Math.ceil(SecurityManager.getRemainingLockTime() / 60000)} 分钟
)}
setAccessCode(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleLogin()} disabled={isValidating || SecurityManager.isLocked()} />
) : ( 职位管理 数据导入 数据导出 历史版本

职位列表 ({jobs.length})

{jobs.length > 0 && ( )}
{jobs.map((job) => (

{job.title}

{job.department} · { job.priority === 'urgent' ? '🔥 紧急' : job.priority === 'high' ? '⚡ 高优' : '📋 普通' }

))}
数据导入 支持 JSON 和 Excel 格式文件导入

支持格式:

  • JSON 文件 (.json) - 标准数据格式
  • Excel 文件 (.xlsx, .xls) - 包含完整职位信息
数据导出 导出当前职位数据用于备份或分享 历史版本 查看和恢复数据的历史版本
{JobDataManager.getHistory().map((entry) => (

{new Date(entry.timestamp).toLocaleString('zh-CN')}

{entry.count} 个职位

))}
)} {/* Job Edit Dialog */} {editingJob && ( { setIsJobDialogOpen(false); setEditingJob(null); }} onSave={handleSaveJob} /> )} {/* Delete All Jobs Confirmation Dialog */} 危险操作确认 此操作将永久删除所有 {jobs.length} 个职位数据。此操作不可逆!

⚠️ 警告:此操作将:

  • • 删除所有 {jobs.length} 个职位
  • • 清空当前数据
  • • 在历史记录中保存删除前的数据
setDeleteAllConfirmText(e.target.value)} placeholder="输入 DELETE ALL" className="mt-2" />
); } // Job editing dialog component interface JobEditDialogProps { job: JobPosition; isOpen: boolean; onClose: () => void; onSave: (job: JobPosition) => void; } function JobEditDialog({ job, isOpen, onClose, onSave }: JobEditDialogProps) { const [editedJob, setEditedJob] = useState(job); const handleSave = () => { if (!editedJob.title || !editedJob.department || !editedJob.url) { return; } onSave(editedJob); }; const updateField = (field: keyof JobPosition, value: any) => { setEditedJob(prev => ({ ...prev, [field]: value })); }; const updateArrayField = (field: 'description' | 'requirements' | 'bonus', value: string) => { const array = value.split('\n').filter(Boolean); setEditedJob(prev => ({ ...prev, [field]: field === 'bonus' ? (array.length > 0 ? array : undefined) : array })); }; return ( {job.title ? '编辑职位' : '添加职位'}
updateField('title', e.target.value)} placeholder="例如:高性能网络专家" />
updateField('department', e.target.value)} placeholder="例如:系统组直招" />
updateField('url', e.target.value)} placeholder="https://..." />