diff --git a/Components/Trip/Tripformmodal.tsx b/Components/Trip/Tripformmodal.tsx deleted file mode 100644 index 7f45026..0000000 --- a/Components/Trip/Tripformmodal.tsx +++ /dev/null @@ -1,497 +0,0 @@ -"use client"; - -import { useEffect, useRef, useState } from "react"; -import * as yup from "yup"; -import { Alert, Spinner } from "../UI"; -import { get } from "@/services/api"; -import { getStoredToken } from "@/lib/auth"; -import { createTripSchema, updateTripSchema } from "@/validations/trip.validator"; -import type { TripSchemaErrors } from "@/validations/trip.validator"; -import type { - Trip, - TripStatus, - CreateTripPayload, - UpdateTripPayload, -} from "@/types/trip"; - -// ── Shared styles ──────────────────────────────────────────────────────────── - -const inputBase: React.CSSProperties = { - width: "100%", - height: 40, - padding: "0 0.75rem", - borderRadius: "var(--radius-md)", - border: "1px solid var(--color-border)", - background: "var(--color-surface)", - fontSize: 13, - color: "var(--color-text-primary)", - outline: "none", - fontFamily: "var(--font-sans)", -}; - -const labelStyle: React.CSSProperties = { - display: "flex", - flexDirection: "column", - gap: 6, - fontSize: 12, - fontWeight: 600, - color: "var(--color-text-secondary)", -}; - -const errorTextStyle: React.CSSProperties = { - fontSize: 11, - color: "var(--color-danger)", - fontWeight: 500, -}; - -const sectionHeadingStyle: React.CSSProperties = { - fontSize: 11, - letterSpacing: "0.25em", - textTransform: "uppercase", - color: "var(--color-text-hint)", - fontWeight: 700, - margin: "0.5rem 0 0", -}; - -const optionalLabelStyle: React.CSSProperties = { - fontSize: 10, - fontWeight: 500, - color: "var(--color-text-hint)", - marginRight: 4, -}; - -// ── Driver / Car / Branch minimal types ───────────────────────────────────── - -interface DriverOption { id: string; name: string; phone: string; status: string; } -interface CarOption { id: string; manufacturer: string; model: string; plateNumber: string; status?: string; } -interface BranchOption { id: string; name: string; } - -// ── Props ──────────────────────────────────────────────────────────────────── - -interface TripFormModalProps { - editTrip: Trip | null; - onClose: () => void; - onSubmit: ( - payload: CreateTripPayload | UpdateTripPayload, - isNew: boolean, - ) => Promise; -} - -// ── Component ──────────────────────────────────────────────────────────────── - -export function TripFormModal({ editTrip, onClose, onSubmit }: TripFormModalProps) { - const isNew = editTrip === null; - - // ── Dropdown options ────────────────────────────────────────────────────── - const [drivers, setDrivers] = useState([]); - const [cars, setCars] = useState([]); - const [branches, setBranches] = useState([]); - - useEffect(() => { - const token = getStoredToken(); - // Fetch active drivers - get<{ data: { data: DriverOption[] } }>("driver?limit=100&status=Active", token) - .then(res => setDrivers((res as unknown as { data: { data: DriverOption[] } }).data?.data ?? [])) - .catch(() => {}); - // Fetch active cars - get<{ data: { data: CarOption[] } }>("cars?limit=100&status=Active", token) - .then(res => setCars((res as unknown as { data: { data: CarOption[] } }).data?.data ?? [])) - .catch(() => {}); - // Fetch branches - get<{ data: { data: BranchOption[] } }>("branches?limit=100", token) - .then(res => setBranches((res as unknown as { data: { data: BranchOption[] } }).data?.data ?? [])) - .catch(() => {}); - }, []); - - // ── Form state ──────────────────────────────────────────────────────────── - const [title, setTitle] = useState(editTrip?.title ?? ""); - const [driverId, setDriverId] = useState(editTrip?.driverId ?? ""); - const [carId, setCarId] = useState(editTrip?.carId ?? ""); - const [branchId, setBranchId] = useState(editTrip?.branchId ?? ""); - const [status, setStatus] = useState(editTrip?.status ?? "Scheduled"); - const [startTime, setStartTime] = useState(editTrip?.startTime?.slice(0, 16) ?? ""); - const [endTime, setEndTime] = useState(editTrip?.endTime?.slice(0, 16) ?? ""); - const [collectedCount, setCollectedCount] = useState( - editTrip?.collectedCount != null ? String(editTrip.collectedCount) : "", - ); - const [deliveredCount, setDeliveredCount] = useState( - editTrip?.deliveredCount != null ? String(editTrip.deliveredCount) : "", - ); - const [returnedCount, setReturnedCount] = useState( - editTrip?.returnedCount != null ? String(editTrip.returnedCount) : "", - ); - const [totalCashCollected, setTotalCashCollected] = useState( - editTrip?.totalCashCollected != null ? String(editTrip.totalCashCollected) : "", - ); - const [notes, setNotes] = useState(editTrip?.notes ?? ""); - const [endReason, setEndReason] = useState(editTrip?.endReason ?? ""); - const [reason, setReason] = useState(""); // update-only - - const [errors, setErrors] = useState({}); - const [saving, setSaving] = useState(false); - const [apiError, setApiError] = useState(""); - const firstRef = useRef(null); - - useEffect(() => { firstRef.current?.focus(); }, []); - useEffect(() => { - const h = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; - window.addEventListener("keydown", h); - return () => window.removeEventListener("keydown", h); - }, [onClose]); - - // ── Error style helper ──────────────────────────────────────────────────── - const inputStyle = (field: keyof TripSchemaErrors): React.CSSProperties => ({ - ...inputBase, - ...(errors[field] ? { borderColor: "var(--color-danger)", background: "#FEF2F2" } : {}), - }); - - const clearErr = (field: keyof TripSchemaErrors) => - setErrors(p => ({ ...p, [field]: undefined })); - - // ── Submit ──────────────────────────────────────────────────────────────── - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - const raw: Record = { - title: title || undefined, - driverId: driverId || undefined, - carId: carId || undefined, - branchId: branchId || undefined, - status: status || undefined, - startTime: startTime ? `${startTime}:00.000Z` : undefined, - endTime: endTime ? `${endTime}:00.000Z` : undefined, - collectedCount: collectedCount !== "" ? Number(collectedCount) : undefined, - deliveredCount: deliveredCount !== "" ? Number(deliveredCount) : undefined, - returnedCount: returnedCount !== "" ? Number(returnedCount) : undefined, - totalCashCollected: totalCashCollected !== "" ? Number(totalCashCollected) : undefined, - notes: notes || undefined, - endReason: endReason || undefined, - ...(!isNew && reason ? { reason } : {}), - }; - - try { - const schema = isNew ? createTripSchema : updateTripSchema; - await schema.validate(raw, { abortEarly: false }); - } catch (err) { - if (err instanceof yup.ValidationError) { - const map: TripSchemaErrors = {}; - err.inner.forEach(e => { - if (e.path) (map as Record)[e.path] = e.message; - }); - setErrors(map); - } - return; - } - - setSaving(true); - setApiError(""); - try { - const ok = await onSubmit(raw as CreateTripPayload | UpdateTripPayload, isNew); - if (ok) onClose(); - else setApiError("حدث خطأ أثناء الحفظ. يرجى المحاولة مرة أخرى."); - } catch (err) { - setApiError(err instanceof Error ? err.message : "حدث خطأ غير متوقع."); - } finally { - setSaving(false); - } - }; - - return ( -
{ if (e.target === e.currentTarget) onClose(); }} - style={{ - position: "fixed", inset: 0, zIndex: 60, - background: "rgba(15,23,42,0.55)", backdropFilter: "blur(4px)", - display: "flex", alignItems: "flex-start", justifyContent: "center", - padding: "2rem 1rem", overflowY: "auto", - }} - > -
e.stopPropagation()} - style={{ - width: "100%", maxWidth: 700, - background: "var(--color-surface)", - borderRadius: "var(--radius-xl)", - border: "1px solid var(--color-border)", - boxShadow: "0 24px 56px rgba(0,0,0,.2)", - overflow: "hidden", - }} - > - {/* Header */} -
-

- {isNew ? "إضافة رحلة جديدة" : "تعديل بيانات الرحلة"} -

- -
- - {/* Form */} -
- - {apiError && ( - {apiError} - )} - - {/* ── Section: أساسيات الرحلة ── */} -

أساسيات الرحلة

- - {/* Title */} - - -
- {/* Driver */} - - - {/* Car */} - -
- -
- {/* Branch */} - - - {/* Status */} - -
- - {/* ── Section: التوقيت ── */} -

التوقيت

- -
- - - -
- - {/* ── Section: الأعداد والمبالغ ── */} -

الأعداد والمبالغ

- -
- - - - - - - -
- - {/* ── Section: ملاحظات ── */} -

ملاحظات

- -
-