car_mms/app/components/vehicles/VehicleDetailsView.tsx
2025-09-11 14:22:27 +03:00

388 lines
18 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Link } from "@remix-run/react";
import { Button } from "~/components/ui/Button";
import { Flex } from "~/components/layout/Flex";
import { useSettings } from "~/contexts/SettingsContext";
import { TRANSMISSION_TYPES, FUEL_TYPES, USE_TYPES, BODY_TYPES } from "~/lib/constants";
import type { VehicleWithOwner, VehicleWithRelations } from "~/types/database";
interface VehicleDetailsViewProps {
vehicle: VehicleWithOwner | VehicleWithRelations;
onEdit?: () => void;
onClose?: () => void;
isLoadingVisits?: boolean;
}
export function VehicleDetailsView({ vehicle, onEdit, onClose, isLoadingVisits }: VehicleDetailsViewProps) {
const { formatDate, formatCurrency, formatNumber } = useSettings();
// Helper functions to get display labels
const getTransmissionLabel = (value: string) => {
return TRANSMISSION_TYPES.find(t => t.value === value)?.label || value;
};
const getFuelLabel = (value: string) => {
return FUEL_TYPES.find(f => f.value === value)?.label || value;
};
const getUseTypeLabel = (value: string) => {
return USE_TYPES.find(u => u.value === value)?.label || value;
};
const getBodyTypeLabel = (value: string) => {
return BODY_TYPES.find(b => b.value === value)?.label || value;
};
return (
<div className="space-y-6">
{/* Enhanced Vehicle Information Section */}
<div className="bg-gradient-to-r from-green-50 to-emerald-50 p-6 rounded-xl border border-green-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-green-600 ml-2">🚗</span>
معلومات المركبة
</h3>
<span className="text-sm text-gray-500 bg-white px-3 py-1 rounded-full">
المركبة #{vehicle.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-xl font-bold text-gray-900 font-mono tracking-wider">
{vehicle.plateNumber}
</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-lg font-semibold text-gray-900">{vehicle.manufacturer}</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-lg font-semibold text-gray-900">{vehicle.model}</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-lg font-semibold text-gray-900">{vehicle.year}</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">{getBodyTypeLabel(vehicle.bodyType)}</p>
</div>
{vehicle.trim && (
<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">{vehicle.trim}</p>
</div>
)}
</div>
</div>
{/* Technical Specifications */}
<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">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<span className="text-gray-600 text-xl ml-2"></span>
المواصفات التقنية
</h3>
</div>
<div className="p-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="bg-gray-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">ناقل الحركة</label>
<p className="text-gray-900 font-medium">{getTransmissionLabel(vehicle.transmission)}</p>
</div>
<div className="bg-gray-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">نوع الوقود</label>
<p className="text-gray-900 font-medium">{getFuelLabel(vehicle.fuel)}</p>
</div>
<div className="bg-gray-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">نوع الاستخدام</label>
<p className="text-gray-900 font-medium">{getUseTypeLabel(vehicle.useType)}</p>
</div>
{vehicle.cylinders && (
<div className="bg-gray-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">عدد الأسطوانات</label>
<p className="text-gray-900 font-medium">{vehicle.cylinders}</p>
</div>
)}
{vehicle.engineDisplacement && (
<div className="bg-gray-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">سعة المحرك</label>
<p className="text-gray-900 font-medium">{vehicle.engineDisplacement} لتر</p>
</div>
)}
</div>
</div>
</div>
{/* Owner Information */}
<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">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<span className="text-gray-600 text-xl ml-2">👤</span>
معلومات المالك
</h3>
<Link
to={`/customers?search=${encodeURIComponent(vehicle.owner.name)}`}
target="_blank"
className="inline-flex items-center px-3 py-1 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">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="bg-blue-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">اسم المالك</label>
<p className="text-lg font-semibold text-gray-900">{vehicle.owner.name}</p>
</div>
{vehicle.owner.phone && (
<div className="bg-blue-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">رقم الهاتف</label>
<a
href={`tel:${vehicle.owner.phone}`}
className="text-blue-600 hover:text-blue-800 font-medium"
dir="ltr"
>
📞 {vehicle.owner.phone}
</a>
</div>
)}
{vehicle.owner.email && (
<div className="bg-blue-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">البريد الإلكتروني</label>
<a
href={`mailto:${vehicle.owner.email}`}
className="text-blue-600 hover:text-blue-800 font-medium"
dir="ltr"
>
{vehicle.owner.email}
</a>
</div>
)}
{vehicle.owner.address && (
<div className="bg-blue-50 p-4 rounded-lg md:col-span-2 lg:col-span-3">
<label className="block text-sm font-medium text-gray-600 mb-1">العنوان</label>
<p className="text-gray-900">{vehicle.owner.address}</p>
</div>
)}
</div>
</div>
</div>
{/* Maintenance Status */}
<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">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<span className="text-gray-600 text-xl ml-2">🔧</span>
حالة الصيانة
</h3>
<Link
to={`/maintenance-visits?vehicleId=${vehicle.id}`}
target="_blank"
className="inline-flex items-center px-3 py-1 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">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div className="bg-green-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">آخر زيارة صيانة</label>
<p className="text-gray-900 font-medium">
{vehicle.lastVisitDate
? formatDate(vehicle.lastVisitDate)
: <span className="text-gray-400">لا توجد زيارات</span>
}
</p>
</div>
{vehicle.suggestedNextVisitDate && (
<div className="bg-orange-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">الزيارة المقترحة التالية</label>
<p className="text-orange-600 font-bold">
{formatDate(vehicle.suggestedNextVisitDate)}
</p>
</div>
)}
<div className="bg-gray-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">تاريخ التسجيل</label>
<p className="text-gray-900">
{formatDate(vehicle.createdDate)}
</p>
</div>
<div className="bg-gray-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-1">آخر تحديث</label>
<p className="text-gray-900">
{formatDate(vehicle.updateDate)}
</p>
</div>
</div>
</div>
</div>
{/* Recent Maintenance Visits
<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">
<h3 className="text-lg font-semibold text-gray-900 flex items-center">
<span className="text-gray-600 text-xl ml-2">🔧</span>
آخر زيارات الصيانة
</h3>
</div>
<div className="p-6">
{isLoadingVisits ? (
<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>
<div className="flex items-center justify-center">
<svg className="animate-spin h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</div>
</div>
) : !('maintenanceVisits' in vehicle) || !vehicle.maintenanceVisits || vehicle.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>
<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">
{('maintenanceVisits' in vehicle ? vehicle.maintenanceVisits : []).slice(0, 3).map((visit: any) => (
<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.kilometers ? formatNumber(visit.kilometers) : 'غير محدد'} كم
</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>
))}
{('maintenanceVisits' in vehicle ? vehicle.maintenanceVisits : []).length > 3 && (
<div className="text-center py-4 border-t border-gray-200">
<p className="text-sm text-gray-500 mb-3">
عرض 3 من أصل {('maintenanceVisits' in vehicle ? vehicle.maintenanceVisits : []).length} زيارة صيانة
</p>
<Link
to={`/maintenance-visits?vehicleId=${vehicle.id}`}
target="_blank"
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>
عرض جميع الزيارات ({('maintenanceVisits' in vehicle ? vehicle.maintenanceVisits : []).length})
</Link>
</div>
)}
</div>
)}
</div>
</div> */}
{/* Action Buttons */}
{(onEdit || onClose) && (
<div className="flex flex-col sm:flex-row gap-3 pt-6 border-t border-gray-200">
{onEdit && (
<Button
onClick={onEdit}
className="bg-blue-600 hover:bg-blue-700 flex-1 sm:flex-none"
>
<span className="ml-2"></span>
تعديل المركبة
</Button>
)}
{onClose && (
<Button
variant="outline"
onClick={onClose}
className="flex-1 sm:flex-none"
>
إغلاق
</Button>
)}
</div>
)}
</div>
);
}