322 lines
15 KiB
TypeScript
322 lines
15 KiB
TypeScript
import { Link } from "@remix-run/react";
|
||
import { Button } from "~/components/ui/Button";
|
||
import { Flex } from "~/components/layout/Flex";
|
||
import { useSettings } from "~/contexts/SettingsContext";
|
||
import type { CustomerWithVehicles } from "~/types/database";
|
||
|
||
interface CustomerDetailsViewProps {
|
||
customer: CustomerWithVehicles;
|
||
onEdit: () => void;
|
||
onClose: () => void;
|
||
}
|
||
|
||
export function CustomerDetailsView({
|
||
customer,
|
||
onEdit,
|
||
onClose,
|
||
}: CustomerDetailsViewProps) {
|
||
const { formatDate, formatCurrency } = useSettings();
|
||
return (
|
||
<div className="space-y-6">
|
||
{/* Enhanced Basic Information Section */}
|
||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 p-6 rounded-xl border border-blue-100">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h3 className="text-xl font-bold text-gray-900 flex items-center">
|
||
<span className="text-blue-600 ml-2">👤</span>
|
||
المعلومات الأساسية
|
||
</h3>
|
||
<span className="text-sm text-gray-500 bg-white px-3 py-1 rounded-full">
|
||
العميل #{customer.id}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
<div className="bg-white p-4 rounded-lg shadow-sm">
|
||
<label className="block text-sm font-medium text-gray-600 mb-1">اسم العميل</label>
|
||
<p className="text-lg font-semibold text-gray-900">{customer.name}</p>
|
||
</div>
|
||
|
||
<div className="bg-white p-4 rounded-lg shadow-sm">
|
||
<label className="block text-sm font-medium text-gray-600 mb-1">رقم الهاتف</label>
|
||
<p className="text-gray-900" dir="ltr">
|
||
{customer.phone ? (
|
||
<a
|
||
href={`tel:${customer.phone}`}
|
||
className="text-blue-600 hover:text-blue-800 font-medium"
|
||
>
|
||
📞 {customer.phone}
|
||
</a>
|
||
) : (
|
||
<span className="text-gray-400">غير محدد</span>
|
||
)}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="bg-white p-4 rounded-lg shadow-sm">
|
||
<label className="block text-sm font-medium text-gray-600 mb-1">البريد الإلكتروني</label>
|
||
<p className="text-gray-900" dir="ltr">
|
||
{customer.email ? (
|
||
<a
|
||
href={`mailto:${customer.email}`}
|
||
className="text-blue-600 hover:text-blue-800 font-medium"
|
||
>
|
||
✉️ {customer.email}
|
||
</a>
|
||
) : (
|
||
<span className="text-gray-400">غير محدد</span>
|
||
)}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="bg-white p-4 rounded-lg shadow-sm">
|
||
<label className="block text-sm font-medium text-gray-600 mb-1">تاريخ الإنشاء</label>
|
||
<p className="text-gray-900">
|
||
{formatDate(customer.createdDate)}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="bg-white p-4 rounded-lg shadow-sm">
|
||
<label className="block text-sm font-medium text-gray-600 mb-1">آخر تحديث</label>
|
||
<p className="text-gray-900">
|
||
{formatDate(customer.updateDate)}
|
||
</p>
|
||
</div>
|
||
|
||
{customer.address && (
|
||
<div className="bg-white p-4 rounded-lg shadow-sm md:col-span-2 lg:col-span-1">
|
||
<label className="block text-sm font-medium text-gray-600 mb-1">العنوان</label>
|
||
<p className="text-gray-900">{customer.address}</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Customer Vehicles Section */}
|
||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||
<div className="bg-gray-50 px-6 py-4 border-b border-gray-200">
|
||
<Flex justify="between" align="center" className="flex-wrap gap-4">
|
||
<div className="flex items-center">
|
||
<span className="text-gray-600 text-xl ml-2">🚗</span>
|
||
<h3 className="text-lg font-semibold text-gray-900">
|
||
مركبات العميل ({customer.vehicles.length})
|
||
</h3>
|
||
</div>
|
||
|
||
{customer.vehicles.length > 0 && (
|
||
<Link
|
||
to={`/vehicles?customerId=${customer.id}`}
|
||
className="inline-flex items-center px-4 py-2 text-sm font-medium text-blue-600 bg-blue-50 border border-blue-200 rounded-lg hover:bg-blue-100 transition-colors"
|
||
>
|
||
<span className="ml-2">🔍</span>
|
||
عرض جميع المركبات
|
||
</Link>
|
||
)}
|
||
</Flex>
|
||
</div>
|
||
|
||
<div className="p-6">
|
||
{customer.vehicles.length === 0 ? (
|
||
<div className="text-center py-12">
|
||
<div className="text-gray-400 text-4xl mb-4">🚗</div>
|
||
<h4 className="text-lg font-medium text-gray-900 mb-2">لا توجد مركبات مسجلة</h4>
|
||
<p className="text-gray-500 mb-4">لم يتم تسجيل أي مركبات لهذا العميل بعد</p>
|
||
<Link
|
||
to="/vehicles"
|
||
className="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors"
|
||
>
|
||
إضافة مركبة جديدة
|
||
</Link>
|
||
</div>
|
||
) : (
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
{customer.vehicles.map((vehicle) => (
|
||
<Link
|
||
key={vehicle.id}
|
||
to={`/vehicles?plateNumber=${encodeURIComponent(vehicle.plateNumber)}`}
|
||
target="_blank"
|
||
className="block bg-gray-50 rounded-lg p-4 border border-gray-200 hover:border-blue-300 hover:bg-blue-50 transition-all"
|
||
>
|
||
<div className="flex items-start justify-between mb-3">
|
||
<div className="flex-1">
|
||
<h4 className="font-semibold text-gray-900 text-lg">
|
||
{vehicle.plateNumber}
|
||
</h4>
|
||
<p className="text-sm text-gray-500">#{vehicle.id}</p>
|
||
</div>
|
||
<span className="text-xs text-blue-600 bg-blue-100 px-2 py-1 rounded-full">
|
||
انقر للعرض
|
||
</span>
|
||
</div>
|
||
|
||
<div className="space-y-2">
|
||
<div className="flex justify-between">
|
||
<span className="text-sm text-gray-600">الصانع:</span>
|
||
<span className="text-sm font-medium text-gray-900">{vehicle.manufacturer}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-sm text-gray-600">الموديل:</span>
|
||
<span className="text-sm font-medium text-gray-900">{vehicle.model}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-sm text-gray-600">سنة الصنع:</span>
|
||
<span className="text-sm font-medium text-gray-900">{vehicle.year}</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-sm text-gray-600">آخر زيارة:</span>
|
||
<span className="text-sm text-gray-900">
|
||
{vehicle.lastVisitDate
|
||
? formatDate(vehicle.lastVisitDate)
|
||
: <span className="text-gray-400">لا توجد زيارات</span>
|
||
}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</Link>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Latest Maintenance Visits Section */}
|
||
<div className="bg-white rounded-xl border border-gray-200 overflow-hidden">
|
||
<div className="bg-gray-50 px-6 py-4 border-b border-gray-200">
|
||
<Flex justify="between" align="center" className="flex-wrap gap-4">
|
||
<div className="flex items-center">
|
||
<span className="text-gray-600 text-xl ml-2">🔧</span>
|
||
<h3 className="text-lg font-semibold text-gray-900">
|
||
آخر زيارات الصيانة ({customer.maintenanceVisits.length > 3 ? '3 من ' + customer.maintenanceVisits.length : customer.maintenanceVisits.length})
|
||
</h3>
|
||
</div>
|
||
|
||
{customer.maintenanceVisits.length > 0 && (
|
||
<Link
|
||
to={`/maintenance-visits?customerId=${customer.id}`}
|
||
className="inline-flex items-center px-4 py-2 text-sm font-medium text-green-600 bg-green-50 border border-green-200 rounded-lg hover:bg-green-100 transition-colors"
|
||
>
|
||
<span className="ml-2">📋</span>
|
||
عرض جميع الزيارات
|
||
</Link>
|
||
)}
|
||
</Flex>
|
||
</div>
|
||
|
||
<div className="p-6">
|
||
{customer.maintenanceVisits.length === 0 ? (
|
||
<div className="text-center py-12">
|
||
<div className="text-gray-400 text-4xl mb-4">🔧</div>
|
||
<h4 className="text-lg font-medium text-gray-900 mb-2">لا توجد زيارات صيانة</h4>
|
||
<p className="text-gray-500 mb-4">لم يتم تسجيل أي زيارات صيانة لهذا العميل بعد</p>
|
||
<p className="text-sm text-gray-400 mb-4">
|
||
ابدأ بتسجيل أول زيارة صيانة لتتبع تاريخ الخدمات المقدمة
|
||
</p>
|
||
<Link
|
||
to="/maintenance-visits"
|
||
className="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-green-600 rounded-lg hover:bg-green-700 transition-colors"
|
||
>
|
||
تسجيل زيارة صيانة جديدة
|
||
</Link>
|
||
</div>
|
||
) : (
|
||
<div className="space-y-4">
|
||
{customer.maintenanceVisits.slice(0, 3).map((visit) => (
|
||
<div
|
||
key={visit.id}
|
||
className="bg-gray-50 rounded-lg p-4 border border-gray-200 hover:border-green-300 hover:bg-green-50 transition-all"
|
||
>
|
||
<div className="flex items-start justify-between mb-3">
|
||
<div className="flex-1">
|
||
<h4 className="font-semibold text-gray-900 text-lg">
|
||
{(() => {
|
||
try {
|
||
const jobs = JSON.parse(visit.maintenanceJobs);
|
||
return jobs.length > 1
|
||
? `${jobs.length} أعمال صيانة`
|
||
: jobs[0]?.job || 'نوع صيانة غير محدد';
|
||
} catch {
|
||
return 'نوع صيانة غير محدد';
|
||
}
|
||
})()}
|
||
</h4>
|
||
<p className="text-sm text-gray-500">زيارة #{visit.id}</p>
|
||
</div>
|
||
<div className="text-left">
|
||
<div className="text-lg font-bold text-green-600">
|
||
{formatCurrency(visit.cost)}
|
||
</div>
|
||
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||
visit.paymentStatus === "paid"
|
||
? 'bg-green-100 text-green-800'
|
||
: visit.paymentStatus === 'pending'
|
||
? 'bg-yellow-100 text-yellow-800'
|
||
: 'bg-red-100 text-red-800'
|
||
}`}>
|
||
{visit.paymentStatus === 'paid' ? 'مدفوع' :
|
||
visit.paymentStatus === 'pending' ? 'معلق' : 'غير مدفوع'}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
||
<div className="flex justify-between">
|
||
<span className="text-gray-600">تاريخ الزيارة:</span>
|
||
<span className="font-medium text-gray-900">
|
||
{formatDate(visit.visitDate)}
|
||
</span>
|
||
</div>
|
||
<div className="flex justify-between">
|
||
<span className="text-gray-600">المركبة:</span>
|
||
<span className="font-medium text-gray-900">
|
||
{visit.vehicle?.plateNumber || 'غير محدد'}
|
||
</span>
|
||
</div>
|
||
{visit.description && (
|
||
<div className="md:col-span-2">
|
||
<span className="text-gray-600">الوصف:</span>
|
||
<p className="text-gray-900 mt-1">{visit.description}</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
))}
|
||
|
||
{customer.maintenanceVisits.length > 3 && (
|
||
<div className="text-center py-4 border-t border-gray-200">
|
||
<p className="text-sm text-gray-500 mb-3">
|
||
عرض 3 من أصل {customer.maintenanceVisits.length} زيارة صيانة
|
||
</p>
|
||
<Link
|
||
to={`/maintenance-visits?customerId=${customer.id}`}
|
||
className="inline-flex items-center px-4 py-2 text-sm font-medium text-green-600 bg-green-50 border border-green-200 rounded-lg hover:bg-green-100 transition-colors"
|
||
>
|
||
<span className="ml-2">📋</span>
|
||
عرض جميع الزيارات ({customer.maintenanceVisits.length})
|
||
</Link>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Action Buttons */}
|
||
<div className="flex flex-col sm:flex-row gap-3 pt-6 border-t border-gray-200">
|
||
<Button
|
||
onClick={onEdit}
|
||
className="bg-blue-600 hover:bg-blue-700 flex-1 sm:flex-none"
|
||
>
|
||
<span className="ml-2">✏️</span>
|
||
تعديل العميل
|
||
</Button>
|
||
|
||
<Button
|
||
variant="outline"
|
||
onClick={onClose}
|
||
className="flex-1 sm:flex-none"
|
||
>
|
||
إغلاق
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
);
|
||
} |