This commit is contained in:
2026-01-23 20:35:40 +03:00
parent cf3b0e48ec
commit 66c151653e
137 changed files with 41495 additions and 0 deletions

View File

@@ -0,0 +1,311 @@
import { Link } from "@remix-run/react";
import { useSettings } from "~/contexts/SettingsContext";
import type { MaintenanceVisitWithRelations } from "~/types/database";
interface MaintenanceVisitDetailsViewProps {
visit: MaintenanceVisitWithRelations;
}
export function MaintenanceVisitDetailsView({ visit }: MaintenanceVisitDetailsViewProps) {
const { formatCurrency, formatDate, formatNumber } = useSettings();
const getPaymentStatusColor = (status: string) => {
switch (status) {
case 'paid':
return 'bg-green-100 text-green-800 border-green-200';
case 'pending':
return 'bg-yellow-100 text-yellow-800 border-yellow-200';
case 'partial':
return 'bg-blue-100 text-blue-800 border-blue-200';
case 'cancelled':
return 'bg-red-100 text-red-800 border-red-200';
default:
return 'bg-gray-100 text-gray-800 border-gray-200';
}
};
const getPaymentStatusLabel = (status: string) => {
switch (status) {
case 'paid': return 'مدفوع';
case 'pending': return 'معلق';
case 'partial': return 'مدفوع جزئياً';
case 'cancelled': return 'ملغي';
default: return status;
}
};
const getDelayLabel = (months: number) => {
const delayOptions = [
{ value: 1, label: 'شهر واحد' },
{ value: 2, label: 'شهرين' },
{ value: 3, label: '3 أشهر' },
{ value: 4, label: '4 أشهر' },
{ value: 6, label: '6 أشهر' },
{ value: 12, label: 'سنة واحدة' },
];
const option = delayOptions.find(opt => opt.value === months);
return option ? option.label : `${months} أشهر`;
};
return (
<div className="space-y-8">
{/* Header Section */}
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl p-6 border border-blue-100">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center">
<span className="text-3xl ml-3">🔧</span>
<div>
<h2 className="text-2xl font-bold text-gray-900">
زيارة صيانة #{visit.id}
</h2>
<p className="text-gray-600 mt-1">
{formatDate(visit.visitDate)}
</p>
</div>
</div>
<div className="text-right">
<div className="text-3xl font-bold text-blue-600 mb-1">
{formatCurrency(visit.cost)}
</div>
<span className={`inline-flex px-3 py-1 text-sm font-semibold rounded-full border ${getPaymentStatusColor(visit.paymentStatus)}`}>
{getPaymentStatusLabel(visit.paymentStatus)}
</span>
</div>
</div>
</div>
{/* Main Details Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Visit 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">
<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 space-y-4">
<div className="bg-blue-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-3">أعمال الصيانة المنجزة</label>
<div className="space-y-3">
{(() => {
try {
const jobs = JSON.parse(visit.maintenanceJobs);
return jobs.map((job: any, index: number) => (
<div key={index} className="bg-white p-3 rounded-lg border border-blue-200">
<div className="flex items-start justify-between">
<div className="flex-1">
<p className="font-semibold text-gray-900 mb-1">{job.job}</p>
{job.notes && (
<p className="text-sm text-gray-600">{job.notes}</p>
)}
</div>
<div className="flex flex-col items-end gap-1">
<span className="inline-flex items-center px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 rounded-full">
#{index + 1}
</span>
{job.cost !== undefined && (
<span className="text-sm font-bold text-gray-700">
{formatCurrency(job.cost)}
</span>
)}
</div>
</div>
</div>
));
} catch {
return (
<div className="bg-white p-3 rounded-lg border border-blue-200">
<p className="text-gray-900">لا توجد تفاصيل أعمال الصيانة</p>
</div>
);
}
})()}
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<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-mono text-lg">
{formatNumber(visit.kilometers)} كم
</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">
{getDelayLabel(visit.nextVisitDelay)}
</p>
</div>
</div>
<div className="bg-gray-50 p-4 rounded-lg">
<label className="block text-sm font-medium text-gray-600 mb-2">وصف الأعمال المنجزة</label>
<div className="bg-white rounded-lg p-4 border">
<p className="text-gray-900 whitespace-pre-wrap leading-relaxed">
{visit.description}
</p>
</div>
</div>
</div>
</div>
{/* Vehicle & Customer Information */}
<div className="space-y-6">
{/* Vehicle Info */}
<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-2 gap-4">
<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-xl font-bold text-gray-900 font-mono">
{visit.vehicle.plateNumber}
</p>
</div>
<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-lg font-semibold text-gray-900">
{visit.vehicle.year}
</p>
</div>
<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">
{visit.vehicle.manufacturer}
</p>
</div>
<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">
{visit.vehicle.model}
</p>
</div>
</div>
</div>
</div>
{/* Customer Info */}
<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="space-y-4">
<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">
{visit.customer.name}
</p>
</div>
{(visit.customer.phone || visit.customer.email) && (
<div className="grid grid-cols-1 gap-4">
{visit.customer.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:${visit.customer.phone}`}
className="text-blue-600 hover:text-blue-800 font-mono font-medium"
dir="ltr"
>
📞 {visit.customer.phone}
</a>
</div>
)}
{visit.customer.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:${visit.customer.email}`}
className="text-blue-600 hover:text-blue-800 font-medium"
dir="ltr"
>
{visit.customer.email}
</a>
</div>
)}
</div>
)}
{visit.customer.address && (
<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-gray-900">
{visit.customer.address}
</p>
</div>
)}
</div>
</div>
</div>
</div>
</div>
{/* Income Information */}
{visit.income && visit.income.length > 0 && (
<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="space-y-3">
{visit.income.map((income) => (
<div key={income.id} className="flex justify-between items-center bg-green-50 p-4 rounded-lg">
<div>
<p className="text-sm text-gray-600">تاريخ الدخل</p>
<p className="font-medium text-gray-900">
{formatDate(income.incomeDate)}
</p>
</div>
<div className="text-right">
<p className="text-sm text-gray-600">المبلغ</p>
<p className="text-xl font-bold text-green-600 font-mono">
{formatCurrency(income.amount)}
</p>
</div>
</div>
))}
</div>
</div>
</div>
)}
{/* Action Buttons */}
<div className="flex flex-wrap gap-3 pt-6 border-t border-gray-200">
<Link
to={`/vehicles?search=${encodeURIComponent(visit.vehicle.plateNumber)}`}
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>
<Link
to={`/customers?search=${encodeURIComponent(visit.customer.name)}`}
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>
<Link
to={`/maintenance-visits?vehicleId=${visit.vehicle.id}`}
className="inline-flex items-center px-4 py-2 text-sm font-medium text-purple-600 bg-purple-50 border border-purple-200 rounded-lg hover:bg-purple-100 transition-colors"
>
<span className="ml-2">📋</span>
جميع زيارات هذه المركبة
</Link>
</div>
</div>
);
}