doctor-mail/apps/web/src/app/main/letter/write/page.tsx

201 lines
12 KiB
TypeScript
Raw Normal View History

2025-01-22 19:24:52 +08:00
import { useState, useMemo } from 'react';
import { motion } from 'framer-motion';
import { FunnelIcon, MagnifyingGlassIcon, PaperAirplaneIcon } from '@heroicons/react/24/outline';
import { Leader } from './types';
import { leaders } from './mock';
export default function WriteLetterPage() {
const [selectedLeader, setSelectedLeader] = useState<Leader | null>(null);
const [searchQuery, setSearchQuery] = useState('');
const [selectedDivision, setSelectedDivision] = useState<string>('all');
const divisions = useMemo(() => {
return ['all', ...new Set(leaders.map(leader => leader.division))];
}, []);
const filteredLeaders = useMemo(() => {
return leaders.filter(leader => {
const matchesSearch = leader.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
leader.rank.toLowerCase().includes(searchQuery.toLowerCase());
const matchesDivision = selectedDivision === 'all' || leader.division === selectedDivision;
return matchesSearch && matchesDivision;
});
}, [searchQuery, selectedDivision]);
return (
<div className="min-h-screen bg-gradient-to-b from-slate-100 to-slate-200">
{/* Header Banner */}
<div className="bg-gradient-to-r from-[#00308F] to-[#0353A4] text-white py-8">
<div className="container mx-auto px-4">
<div className="flex flex-col space-y-6">
{/* 主标题 */}
<div>
<h1 className="text-3xl font-bold tracking-wider">
</h1>
<p className="mt-2 text-blue-100 text-lg">
</p>
</div>
{/* 隐私保护说明 */}
<div className="flex flex-wrap gap-6 text-sm">
<div className="flex items-center gap-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span></span>
</div>
<div className="flex items-center gap-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
<span></span>
</div>
<div className="flex items-center gap-2">
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z" />
</svg>
<span></span>
</div>
</div>
{/* 隐私承诺 */}
<div className="text-sm text-blue-100 border-t border-blue-400/30 pt-4">
<p></p>
</div>
</div>
</div>
</div>
{/* 搜索和筛选区域 */}
<div className="container mx-auto px-4 py-8">
<div className="flex flex-col md:flex-row gap-4 mb-8">
<div className="relative flex-1">
<MagnifyingGlassIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
<input
type="text"
placeholder="Search by name or rank..."
className="w-full pl-10 pr-4 py-3 rounded-lg border border-gray-200 focus:ring-2 focus:ring-[#00308F] focus:border-transparent"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<div className="relative">
<FunnelIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
<select
className="pl-10 pr-8 py-3 rounded-lg border border-gray-200 focus:ring-2 focus:ring-[#00308F] appearance-none bg-white"
value={selectedDivision}
onChange={(e) => setSelectedDivision(e.target.value)}
>
{divisions.map(division => (
<option key={division} value={division}>
{division === 'all' ? 'All Divisions' : division}
</option>
))}
</select>
</div>
</div>
{/* Modified Leader Cards Grid */}
<div className="grid grid-cols-1 gap-6">
{filteredLeaders.map((leader) => (
<motion.div
key={leader.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
whileHover={{ scale: 1.005 }}
transition={{ duration: 0.2 }}
className={`
bg-white rounded-xl shadow-sm overflow-hidden border border-gray-100
${selectedLeader?.id === leader.id
? 'ring-2 ring-[#00308F]'
: 'hover:shadow-lg hover:border-blue-100'
}
`}
>
<div className="flex flex-col sm:flex-row">
{/* Image Container */}
<div className="sm:w-48 h-64 sm:h-auto flex-shrink-0">
<img
src={leader.imageUrl}
alt={leader.name}
className="w-full h-full object-cover"
/>
</div>
{/* Content Container */}
<div className="flex-1 p-6">
<div className="flex flex-col h-full">
<div className="mb-4">
<div className="flex items-center justify-between mb-3">
<h3 className="text-xl font-semibold text-gray-900">
{leader.name}
</h3>
<span className="px-3 py-1 text-sm font-medium bg-blue-50 text-[#00308F] rounded-full">
{leader.rank}
</span>
</div>
<p className="text-gray-600 mb-4">{leader.division}</p>
{/* Contact Information */}
<div className="space-y-2 text-sm text-gray-600">
<p className="flex items-center">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
{leader.email}
</p>
<p className="flex items-center">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
{leader.phone}
</p>
<p className="flex items-center">
<svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
{leader.office}
</p>
</div>
</div>
<button
onClick={() => setSelectedLeader(leader)}
className="mt-auto w-full sm:w-auto flex items-center justify-center gap-2
bg-[#00308F] text-white py-3 px-6 rounded-lg
hover:bg-[#002070] transition-all duration-300
focus:outline-none focus:ring-2 focus:ring-[#00308F] focus:ring-opacity-50
transform hover:-translate-y-0.5"
>
<PaperAirplaneIcon className="w-5 h-5" />
Compose Letter
</button>
</div>
</div>
</div>
</motion.div>
))}
</div>
{/* 无结果提示 */}
{filteredLeaders.length === 0 && (
<div className="text-center py-12">
<p className="text-gray-600 text-lg">
No leaders found matching your search criteria
</p>
</div>
)}
</div>
</div>
);
}