import { type StatusTone } from '@ema-platform/shared'; import { useEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useApiMutation } from '@ema-platform/api'; import { Alert, Box, Button, Card, Divider, FileButton, Group, Modal, Paper, Select, SimpleGrid, Stack, Text, Textarea, TextInput, ThemeIcon, Title, rem, } from '@mantine/core'; import { IconAlertCircle, IconArrowRight, IconCircleCheck, IconFileDescription, IconInfoCircle, IconTransferIn, IconUser, } from '@tabler/icons-react'; import { StatusBadge, notify, PhoneInput } from '@ema-platform/ui'; import { isValidPhoneNumber } from 'libphonenumber-js'; // Minimal vessel type for the approved vessel list interface ApprovedVessel { id: string; vesselName: string; category: string; vesselType: string; ownerName: string; ownerNationalIdOrTin: string; ownerPhone: string; status: string; } const MOCK_APPROVED_VESSELS: ApprovedVessel[] = [ { id: 'VR-2024-001', vesselName: 'Lake Tana Star', category: 'Inland Waterway Vessel', vesselType: 'Passenger Ferry', ownerName: 'Abebe Girma', ownerNationalIdOrTin: 'ET-9812345', ownerPhone: '+251 911 234 567', status: 'Approved', }, ]; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export type TransferStatus = 'Pending' | 'Under Review' | 'Approved' | 'Rejected'; export interface OwnershipTransferRequest { id: string; vesselId: string; vesselName: string; category: string; vesselType: string; currentOwnerName: string; currentOwnerIdOrTin: string; currentOwnerPhone: string; newOwnerName: string; newOwnerIdOrTin: string; newOwnerPhone: string; newOwnerEmail: string; newOwnerAddress: string; transferReason: string; remarks: string; status: TransferStatus; submittedDate: string; approvalDate: string | null; } export const MOCK_TRANSFER_REQUESTS: OwnershipTransferRequest[] = [ { id: 'OT-2024-001', vesselId: 'VR-2024-001', vesselName: 'Lake Tana Star', category: 'Inland Waterway Vessel', vesselType: 'Passenger Ferry', currentOwnerName: 'Abebe Girma', currentOwnerIdOrTin: 'ET-9812345', currentOwnerPhone: '+251 911 234 567', newOwnerName: 'Tigist Haile', newOwnerIdOrTin: 'ET-7743210', newOwnerPhone: '+251 922 876 543', newOwnerEmail: 'tigist.haile@email.com', newOwnerAddress: 'Bahir Dar, Amhara Region', transferReason: 'Sale', remarks: 'Vessel sold to new owner. Bill of sale attached.', status: 'Pending', submittedDate: '2024-06-01', approvalDate: null, }, ]; const TRANSFER_REASONS = [ 'Sale / Purchase', 'Inheritance', 'Gift / Donation', 'Corporate Restructuring', 'Court Order', 'Other', ]; const STATUS_TONE: Record = { Pending: 'neutral', 'Under Review': 'warning', Approved: 'success', Rejected: 'danger', }; // --------------------------------------------------------------------------- // Transfer request card // --------------------------------------------------------------------------- function TransferCard({ req }: { req: OwnershipTransferRequest }) { return (
{req.vesselName} {req.id} · Transfer to {req.newOwnerName}
{[ { label: 'From', value: req.currentOwnerName }, { label: 'To', value: req.newOwnerName }, { label: 'Reason', value: req.transferReason }, { label: 'Submitted', value: req.submittedDate }, ].map((r) => (
{r.label} {r.value}
))}
{req.status === 'Approved' && ( } color="teal" mt="sm" py="xs"> Transfer approved on {req.approvalDate}. New certificates issued to {req.newOwnerName}. )} {req.status === 'Rejected' && req.remarks && ( } color="red" mt="sm" py="xs"> Rejected: {req.remarks} )}
); } // --------------------------------------------------------------------------- // Main page // --------------------------------------------------------------------------- export function OwnershipTransferPage() { const navigate = useNavigate(); const [myVessels, setMyVessels] = useState([]); const [transfers, setTransfers] = useState(MOCK_TRANSFER_REQUESTS); const [modalOpen, setModalOpen] = useState(false); const [submitting, setSubmitting] = useState(false); const [fetchTrigger] = useApiMutation(); const [submitTrigger] = useApiMutation<{ id: string }>(); const fetched = useRef(false); // Form state const [selectedVesselId, setSelectedVesselId] = useState(null); const [newOwnerName, setNewOwnerName] = useState(''); const [newOwnerIdOrTin, setNewOwnerIdOrTin] = useState(''); const [newOwnerPhone, setNewOwnerPhone] = useState(''); const [newOwnerEmail, setNewOwnerEmail] = useState(''); const [newOwnerAddress, setNewOwnerAddress] = useState(''); const [transferReason, setTransferReason] = useState(null); const [notes, setNotes] = useState(''); const [billOfSale, setBillOfSale] = useState(null); useEffect(() => { if (fetched.current) return; fetched.current = true; fetchTrigger({ url: '/vessel-registrations/my', method: 'GET' }) .unwrap() .then((data) => setMyVessels(Array.isArray(data) ? data : [data])) .catch(() => { // Fall back to mock approved vessels setMyVessels(MOCK_APPROVED_VESSELS); }); }, [fetchTrigger]); const selectedVessel = myVessels.find((v) => v.id === selectedVesselId) ?? null; const canSubmit = !!selectedVesselId && !!newOwnerName.trim() && !!newOwnerIdOrTin.trim() && isValidPhoneNumber(newOwnerPhone) && !!transferReason && !!billOfSale; const resetForm = () => { setSelectedVesselId(null); setNewOwnerName(''); setNewOwnerIdOrTin(''); setNewOwnerPhone(''); setNewOwnerEmail(''); setNewOwnerAddress(''); setTransferReason(null); setNotes(''); setBillOfSale(null); }; const handleSubmit = async () => { if (!selectedVessel) return; setSubmitting(true); try { await submitTrigger({ url: '/vessel-ownership-transfers', method: 'POST', body: { vesselId: selectedVessel.id, newOwnerName, newOwnerIdOrTin, newOwnerPhone, newOwnerEmail, newOwnerAddress, transferReason, notes, }, }).unwrap(); // Optimistic local update const newReq: OwnershipTransferRequest = { id: `OT-${Date.now()}`, vesselId: selectedVessel.id, vesselName: selectedVessel.vesselName, category: selectedVessel.category, vesselType: selectedVessel.vesselType, currentOwnerName: selectedVessel.ownerName, currentOwnerIdOrTin: selectedVessel.ownerNationalIdOrTin, currentOwnerPhone: selectedVessel.ownerPhone, newOwnerName, newOwnerIdOrTin, newOwnerPhone, newOwnerEmail, newOwnerAddress, transferReason: transferReason ?? '', remarks: notes, status: 'Pending', submittedDate: new Date().toISOString().split('T')[0], approvalDate: null, }; setTransfers((prev) => [newReq, ...prev]); resetForm(); setModalOpen(false); notify.success('Ownership transfer request submitted successfully.'); } catch { // Still add optimistically on API error (mock mode) const newReq: OwnershipTransferRequest = { id: `OT-${Date.now()}`, vesselId: selectedVessel.id, vesselName: selectedVessel.vesselName, category: selectedVessel.category, vesselType: selectedVessel.vesselType, currentOwnerName: selectedVessel.ownerName, currentOwnerIdOrTin: selectedVessel.ownerNationalIdOrTin, currentOwnerPhone: selectedVessel.ownerPhone, newOwnerName, newOwnerIdOrTin, newOwnerPhone, newOwnerEmail, newOwnerAddress, transferReason: transferReason ?? '', remarks: notes, status: 'Pending', submittedDate: new Date().toISOString().split('T')[0], approvalDate: null, }; setTransfers((prev) => [newReq, ...prev]); resetForm(); setModalOpen(false); notify.success('Ownership transfer request submitted.'); } finally { setSubmitting(false); } }; const vesselOptions = myVessels .filter((v) => v.status === 'Approved') .map((v) => ({ value: v.id, label: `${v.vesselName} (${v.id})` })); return (
Ownership Transfer Request transfer of vessel ownership to another party
{vesselOptions.length === 0 && ( } color="blue" variant="light"> You must have at least one approved vessel registration to request an ownership transfer.{' '} navigate('/vessel-registration')}> View my registrations → )} {/* How it works */} How Ownership Transfer Works {[ "Submit a transfer request with the new owner's details and a Bill of Sale", 'The Maritime Authority reviews and verifies the transfer documents', 'Upon approval, ownership is officially transferred in the registry', 'New certificates are automatically generated for the new owner', 'The new owner receives: Certificate of Nationality, Certificate of Ownership, Certificate of Registration (sea-going) or Inland Registration Certificate (inland)', ].map((step, i) => ( {i + 1} {step} ))} {/* Existing transfer requests */}
My Transfer Requests {transfers.length === 0 ? ( No transfer requests submitted yet. ) : ( {transfers.map((req) => )} )}
{/* Transfer request modal */} { setModalOpen(false); resetForm(); }} title="Request Ownership Transfer" size="lg" > } color="orange" variant="light"> Ownership transfer is permanent. Ensure all details are correct before submitting. {/* Bill of Sale upload */}
Bill of Sale / Transfer Document * Legal document confirming the transfer of ownership
{billOfSale ? ( {billOfSale.name} ) : ( {(props) => } )}