implement TeleEMS platform architecture with centralized API client and master data management system
This commit is contained in:
205
src/pages/fleet/FleetPersonnel.tsx
Normal file
205
src/pages/fleet/FleetPersonnel.tsx
Normal file
@@ -0,0 +1,205 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
UserPlus,
|
||||
Search,
|
||||
Filter,
|
||||
Users,
|
||||
Medal,
|
||||
Clock,
|
||||
ShieldCheck,
|
||||
AlertTriangle,
|
||||
Mail,
|
||||
Phone,
|
||||
Calendar,
|
||||
CheckCircle2,
|
||||
XCircle,
|
||||
MoreVertical
|
||||
} from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Card } from '../../components/Common';
|
||||
|
||||
interface Staff {
|
||||
id: string;
|
||||
name: string;
|
||||
role: 'DRIVER' | 'EMT' | 'DOCTOR' | 'PARAMEDIC';
|
||||
status: 'ON_DUTY' | 'OFF_DUTY' | 'ON_LEAVE';
|
||||
specialization?: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
joinedDate: string;
|
||||
tripsCompleted: number;
|
||||
rating: number;
|
||||
certExpiry: string;
|
||||
}
|
||||
|
||||
const MOCK_STAFF: Staff[] = [
|
||||
{ id: 'S-101', name: 'Vikram Singh', role: 'DRIVER', status: 'ON_DUTY', phone: '+91 98765 43210', email: 'v.singh@teleems.com', joinedDate: '2023-01-15', tripsCompleted: 452, rating: 4.8, certExpiry: '2026-12-01' },
|
||||
{ id: 'S-102', name: 'Dr. Ananya Iyer', role: 'DOCTOR', specialization: 'Critical Care', status: 'ON_DUTY', phone: '+91 98765 43211', email: 'a.iyer@teleems.com', joinedDate: '2023-06-20', tripsCompleted: 128, rating: 4.9, certExpiry: '2026-05-15' },
|
||||
{ id: 'S-103', name: 'Rahul Verma', role: 'EMT', status: 'ON_LEAVE', phone: '+91 98765 43212', email: 'r.verma@teleems.com', joinedDate: '2024-02-10', tripsCompleted: 215, rating: 4.7, certExpiry: '2026-06-01' },
|
||||
{ id: 'S-104', name: 'Suresh Kumar', role: 'DRIVER', status: 'OFF_DUTY', phone: '+91 98765 43213', email: 's.kumar@teleems.com', joinedDate: '2022-11-05', tripsCompleted: 890, rating: 4.6, certExpiry: '2026-08-20' },
|
||||
];
|
||||
|
||||
export const FleetPersonnel: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState<'ALL' | 'DRIVER' | 'EMT' | 'DOCTOR'>('ALL');
|
||||
const [selectedStaff, setSelectedStaff] = useState<Staff | null>(null);
|
||||
|
||||
const filteredStaff = MOCK_STAFF.filter(s => activeTab === 'ALL' || s.role === activeTab);
|
||||
|
||||
return (
|
||||
<div className="fleet-personnel animate-in fade-in duration-500">
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '24px' }}>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
{['ALL', 'DRIVER', 'EMT', 'DOCTOR'].map(t => (
|
||||
<button
|
||||
key={t}
|
||||
onClick={() => setActiveTab(t as any)}
|
||||
style={{
|
||||
padding: '8px 16px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid rgba(255,255,255,0.1)',
|
||||
background: activeTab === t ? 'var(--accent-cyan)' : 'rgba(255,255,255,0.05)',
|
||||
color: activeTab === t ? '#000' : 'var(--text-secondary)',
|
||||
fontSize: '0.75rem',
|
||||
fontWeight: 700,
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s'
|
||||
}}
|
||||
>
|
||||
{t}S
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<button className="btn-primary" style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<UserPlus size={18} /> REGISTER PERSONNEL
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr', gap: '24px' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
||||
<Card style={{ padding: '0', overflow: 'hidden' }}>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: '1px solid rgba(255,255,255,0.1)', textAlign: 'left', background: 'rgba(255,255,255,0.02)' }}>
|
||||
<th style={{ padding: '16px', fontSize: '0.75rem', textTransform: 'uppercase', opacity: 0.5 }}>Personnel</th>
|
||||
<th style={{ padding: '16px', fontSize: '0.75rem', textTransform: 'uppercase', opacity: 0.5 }}>Role / Specialization</th>
|
||||
<th style={{ padding: '16px', fontSize: '0.75rem', textTransform: 'uppercase', opacity: 0.5 }}>Status</th>
|
||||
<th style={{ padding: '16px', fontSize: '0.75rem', textTransform: 'uppercase', opacity: 0.5 }}>Trips</th>
|
||||
<th style={{ padding: '16px', fontSize: '0.75rem', textTransform: 'uppercase', opacity: 0.5 }}>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filteredStaff.map(s => (
|
||||
<tr
|
||||
key={s.id}
|
||||
onClick={() => setSelectedStaff(s)}
|
||||
style={{
|
||||
borderBottom: '1px solid rgba(255,255,255,0.05)',
|
||||
cursor: 'pointer',
|
||||
background: selectedStaff?.id === s.id ? 'rgba(59, 130, 246, 0.05)' : 'transparent'
|
||||
}}
|
||||
className="hover-glow"
|
||||
>
|
||||
<td style={{ padding: '16px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||
<div style={{ width: '36px', height: '36px', borderRadius: '50%', background: 'rgba(59, 130, 246, 0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--accent-cyan)', fontWeight: 700, fontSize: '0.875rem', border: '1px solid rgba(59, 130, 246, 0.2)' }}>
|
||||
{s.name.charAt(0)}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontWeight: 700 }}>{s.name}</div>
|
||||
<div style={{ fontSize: '0.65rem', opacity: 0.5 }}>ID: {s.id}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style={{ padding: '16px' }}>
|
||||
<div style={{ fontSize: '0.8125rem', fontWeight: 600 }}>{s.role}</div>
|
||||
{s.specialization && <div style={{ fontSize: '0.65rem', opacity: 0.5 }}>{s.specialization}</div>}
|
||||
</td>
|
||||
<td style={{ padding: '16px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
||||
<div style={{ width: '6px', height: '6px', borderRadius: '50%', background: s.status === 'ON_DUTY' ? '#22C55E' : s.status === 'ON_LEAVE' ? '#EF4444' : '#94A3B8' }}></div>
|
||||
<span style={{ fontSize: '0.7rem', fontWeight: 700, opacity: 0.8 }}>{s.status.replace('_', ' ')}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td style={{ padding: '16px' }}>
|
||||
<div style={{ fontWeight: 800 }}>{s.tripsCompleted}</div>
|
||||
</td>
|
||||
<td style={{ padding: '16px' }}>
|
||||
<button className="btn-ghost-sm"><MoreVertical size={14} /></button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div style={{ position: 'sticky', top: '0' }}>
|
||||
{selectedStaff ? (
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={selectedStaff.id}
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
>
|
||||
<Card>
|
||||
<div style={{ textAlign: 'center', marginBottom: '24px' }}>
|
||||
<div style={{ width: '80px', height: '80px', borderRadius: '50%', background: 'rgba(59, 130, 246, 0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--accent-cyan)', fontSize: '1.5rem', fontWeight: 900, border: '2px solid var(--accent-cyan)', margin: '0 auto 16px', boxShadow: '0 0 20px rgba(59, 130, 246, 0.2)' }}>
|
||||
{selectedStaff.name.charAt(0)}
|
||||
</div>
|
||||
<h2 style={{ fontSize: '1.25rem', fontWeight: 800 }}>{selectedStaff.name}</h2>
|
||||
<div style={{ fontSize: '0.75rem', color: 'var(--accent-cyan)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.05em' }}>{selectedStaff.role}</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px', marginBottom: '24px' }}>
|
||||
<div style={{ padding: '12px', borderRadius: '12px', background: 'rgba(255,255,255,0.02)', border: '1px solid rgba(255,255,255,0.05)' }}>
|
||||
<div style={{ fontSize: '0.65rem', opacity: 0.5, textTransform: 'uppercase', marginBottom: '4px' }}>Trips Rate</div>
|
||||
<div style={{ fontSize: '1.125rem', fontWeight: 800, color: 'var(--accent-green)' }}>{selectedStaff.rating}/5.0</div>
|
||||
</div>
|
||||
<div style={{ padding: '12px', borderRadius: '12px', background: 'rgba(255,255,255,0.02)', border: '1px solid rgba(255,255,255,0.05)' }}>
|
||||
<div style={{ fontSize: '0.65rem', opacity: 0.5, textTransform: 'uppercase', marginBottom: '4px' }}>SLA Compliance</div>
|
||||
<div style={{ fontSize: '1.125rem', fontWeight: 800 }}>98.4%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
||||
<div>
|
||||
<h4 style={{ fontSize: '0.7rem', fontWeight: 800, textTransform: 'uppercase', opacity: 0.5, marginBottom: '8px' }}>Contact Information</h4>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', fontSize: '0.8125rem' }}>
|
||||
<Phone size={14} style={{ opacity: 0.5 }} /> {selectedStaff.phone}
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', fontSize: '0.8125rem' }}>
|
||||
<Mail size={14} style={{ opacity: 0.5 }} /> {selectedStaff.email}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 style={{ fontSize: '0.7rem', fontWeight: 800, textTransform: 'uppercase', opacity: 0.5, marginBottom: '8px' }}>Certifications</h4>
|
||||
<div style={{ padding: '12px', borderRadius: '12px', background: 'rgba(245, 158, 11, 0.05)', border: '1px solid rgba(245, 158, 11, 0.1)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div>
|
||||
<div style={{ fontSize: '0.75rem', fontWeight: 700 }}>Professional License</div>
|
||||
<div style={{ fontSize: '0.65rem', opacity: 0.6 }}>Expiry: {selectedStaff.certExpiry}</div>
|
||||
</div>
|
||||
<AlertTriangle size={16} color="#F59E0B" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button className="btn-primary" style={{ width: '100%', marginTop: '24px' }}>MANAGE SHIFT SCHEDULE</button>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
) : (
|
||||
<div className="glass" style={{ padding: '40px', borderRadius: '16px', textAlign: 'center', border: '1px dashed rgba(255,255,255,0.1)' }}>
|
||||
<Users size={32} style={{ opacity: 0.2, margin: '0 auto 16px' }} />
|
||||
<h3 style={{ fontSize: '1rem', fontWeight: 700, marginBottom: '8px' }}>Select Personnel</h3>
|
||||
<p style={{ fontSize: '0.875rem', color: 'var(--text-secondary)' }}>View detailed performance metrics, licensing status, and shift history for your fleet crew.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user