'use client'; import { useState, useEffect } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Loader2, CheckCircle2, MapPin, Navigation, Milestone, ArrowUpCircle, ArrowDownCircle, } from 'lucide-react'; import { DataTable } from '@/components/data-table'; import { PageHeader } from '@/components/page-header'; import { PoweredBy } from '@/components/powered-by'; import { projectService, packageService, chainageService } from '@/services/api'; import { Project, Package as PackageType, Chainage, ChainageCreate, ChainageUpdate } from '@/types'; import { ColumnDef } from '@tanstack/react-table'; import { toast } from 'sonner'; import { Badge } from '@/components/ui/badge'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; export default function ChainagePage() { const [chainages, setChainages] = useState([]); const [totalItems, setTotalItems] = useState(0); const [projects, setProjects] = useState([]); const [packages, setPackages] = useState([]); const [allPackages, setAllPackages] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isModalOpen, setIsModalOpen] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); const [loadingProjects, setLoadingProjects] = useState(false); const [loadingPackages, setLoadingPackages] = useState(false); // Pagination state const [skip, setSkip] = useState(0); const [limit, setLimit] = useState(10); // Editing state const [isEditing, setIsEditing] = useState(false); const [currentChainage, setCurrentChainage] = useState(null); // Form fields const [selectedProjectId, setSelectedProjectId] = useState(''); const [selectedPackageId, setSelectedPackageId] = useState(''); const [segmentName, setSegmentName] = useState(''); const [chainageStartKm, setChainageStartKm] = useState(''); const [chainageEndKm, setChainageEndKm] = useState(''); const [startLat, setStartLat] = useState(''); const [startLng, setStartLng] = useState(''); const [endLat, setEndLat] = useState(''); const [endLng, setEndLng] = useState(''); const [direction, setDirection] = useState<'UP' | 'DOWN'>('UP'); // Load chainages and projects const loadChainages = async (currentSkip = skip, currentLimit = limit) => { try { setIsLoading(true); setError(null); const data = await chainageService.getChainages({ skip: currentSkip, limit: currentLimit }); setChainages(data.items); setTotalItems(data.totalItems); } catch (err) { setError('Failed to load segments. Please check if the backend is running.'); } finally { // Add a small delay for animation stability setTimeout(() => { setIsLoading(false); }, 800); } }; const loadProjects = async () => { try { setLoadingProjects(true); const data = await projectService.getProjects({ skip: 0, limit: 1000 }); setProjects(data.items); } catch (err) { setError('Failed to load projects.'); } finally { setLoadingProjects(false); } }; const loadAllPackages = async () => { try { const data = await packageService.getPackages({ skip: 0, limit: 1000 }); setAllPackages(data.items); } catch (err) { console.error('Failed to load all packages'); } }; useEffect(() => { loadChainages(skip, limit); loadProjects(); loadAllPackages(); }, [skip, limit]); // Load packages when project changes useEffect(() => { if (!selectedProjectId) { setPackages([]); if (!isEditing) setSelectedPackageId(''); return; } const loadPackagesForProject = async () => { try { setLoadingPackages(true); if (!isEditing) setSelectedPackageId(''); const data = await packageService.getPackagesByProject(selectedProjectId, { skip: 0, limit: 1000, }); setPackages(data.items); } catch (err) { setError('Failed to load packages for the selected project.'); } finally { setLoadingPackages(false); } }; loadPackagesForProject(); }, [selectedProjectId, isEditing]); const resetForm = () => { setSelectedProjectId(''); setSelectedPackageId(''); setSegmentName(''); setChainageStartKm(''); setChainageEndKm(''); setStartLat(''); setStartLng(''); setEndLat(''); setEndLng(''); setDirection('UP'); setError(null); setIsEditing(false); setCurrentChainage(null); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedPackageId && !isEditing) { setError('Please select a project and package first'); return; } if (!segmentName.trim()) { setError('Segment name is required'); return; } if (!chainageStartKm || !chainageEndKm) { setError('Segment start and end values are required'); return; } setIsSubmitting(true); setError(null); try { if (isEditing && currentChainage) { const data: ChainageUpdate = { segment_name: segmentName.trim(), chainage_start_km: parseFloat(chainageStartKm), chainage_end_km: parseFloat(chainageEndKm), start_lat: startLat ? parseFloat(startLat) : 0, start_lng: startLng ? parseFloat(startLng) : 0, end_lat: endLat ? parseFloat(endLat) : 0, end_lng: endLng ? parseFloat(endLng) : 0, direction: direction as 'UP' | 'DOWN', }; await chainageService.updateChainage(currentChainage.id, data); toast.success('Segment Updated', { description: `${segmentName} has been updated successfully at ${new Date().toLocaleTimeString()}`, }); } else { const data: ChainageCreate = { package_id: selectedPackageId, segment_name: segmentName.trim(), chainage_start_km: parseFloat(chainageStartKm), chainage_end_km: parseFloat(chainageEndKm), start_lat: startLat ? parseFloat(startLat) : 0, start_lng: startLng ? parseFloat(startLng) : 0, end_lat: endLat ? parseFloat(endLat) : 0, end_lng: endLng ? parseFloat(endLng) : 0, direction: direction, }; await chainageService.createChainage(data); toast.success('Segment Created', { description: `${segmentName} has been established successfully at ${new Date().toLocaleTimeString()}`, }); } // Refresh chainages list await loadChainages(); // Close modal and reset form immediately setIsModalOpen(false); resetForm(); } catch (err) { const message = err instanceof Error ? err.message : `Failed to ${isEditing ? 'update' : 'create'} segment`; setError(message); toast.error('Operation Failed', { description: message, }); } finally { setIsSubmitting(false); } }; const handleEdit = (chainage: Chainage) => { setIsEditing(true); setCurrentChainage(chainage); // Find project for this package const pkg = allPackages.find((p) => p.id === chainage.package_id); if (pkg) { setSelectedProjectId(pkg.project_id); setSelectedPackageId(chainage.package_id); } setSegmentName(chainage.segment_name || ''); setChainageStartKm(chainage.chainage_start_km?.toString() || ''); setChainageEndKm(chainage.chainage_end_km?.toString() || ''); setStartLat(chainage.start_lat.toString()); setStartLng(chainage.start_lng.toString()); setEndLat(chainage.end_lat.toString()); setEndLng(chainage.end_lng.toString()); setDirection(chainage.direction); setIsModalOpen(true); }; const handleDelete = async (chainage: Chainage) => { if (!confirm(`Are you sure you want to delete segment "${chainage.segment_name}"?`)) return; try { setIsLoading(true); await chainageService.deleteChainage(chainage.id); toast.success('Segment Deleted', { description: `${chainage.segment_name} has been removed from the system.`, }); await loadChainages(); } catch (err) { setError('Failed to delete segment'); toast.error('Deletion Failed', { description: 'The segment could not be removed. Please try again.', }); } finally { setIsLoading(false); } }; const getPackageName = (packageId: string) => { return allPackages.find((p) => p.id === packageId)?.name || packageId; }; const isFormComplete = selectedPackageId && segmentName.trim() && chainageStartKm && chainageEndKm; const columns: ColumnDef[] = [ { accessorKey: 'segment_name', header: 'Segment Name', cell: ({ row }) =>
{row.original.segment_name}
, }, { accessorKey: 'package_id', header: 'Package', cell: ({ row }) => getPackageName(row.original.package_id), }, { accessorKey: 'project', header: 'Project', cell: ({ row }) => { const chainage = row.original; const pkg = allPackages.find((p) => p.id === chainage.package_id); const project = projects.find((p) => p.id === pkg?.project_id); return project?.name || '—'; }, }, { id: 'project_state', header: 'Project State', cell: ({ row }) => { const chainage = row.original; const pkg = allPackages.find((p) => p.id === chainage.package_id); const project = projects.find((p) => p.id === pkg?.project_id); if (!project?.state) return ; const states = project.state .split(',') .map((s) => s.trim()) .filter(Boolean); if (states.length === 0) return ; const firstState = states[0]; const remainingStates = states.slice(1); return (
{firstState} {remainingStates.length > 0 && (

Other States

{remainingStates.map((item, idx) => ( {item} ))}
)}
); }, }, { accessorKey: 'chainage', header: 'Segment Range (km)', cell: ({ row }) => { const chainage = row.original; return (
{chainage.chainage_start_km} - {chainage.chainage_end_km} {chainage.direction === 'UP' ? ( ) : ( )} {chainage.direction}
); }, }, { accessorKey: 'start_gps', header: 'Start GPS', cell: ({ row }) => ( {row.original.start_lat.toFixed(4)}, {row.original.start_lng.toFixed(4)} ), }, { accessorKey: 'end_gps', header: 'End GPS', cell: ({ row }) => ( {row.original.end_lat.toFixed(4)}, {row.original.end_lng.toFixed(4)} ), }, ]; return ( <>
{/* Refined Header */}
{ setIsEditing(false); setIsModalOpen(true); }} > Add Segment } />
{/* Data Table */}
{ setLimit(newLimit); setSkip(0); // Reset skip when limit changes }, }} />
{/* Modal Dialog */} { if (!open) { setIsModalOpen(false); resetForm(); } else { setIsModalOpen(true); } }} > e.preventDefault()}>
{isEditing ? 'Edit Segment' : 'Create Segment'}
{isEditing ? 'Update the technical specifications for your road segment.' : 'Select a project & package, then provide the essential segment data.'}
{!isEditing && (
{/* Step 1: Select Project */}
01

Select Project

{/* Step 2: Select Package */}
02

Select Package

)} {/* Step 3: Chainage Details */}
{!isEditing && (
03

Segment Information

)} {/* Segment Name & Chainage Row */}
setSegmentName(e.target.value)} placeholder="e.g. Mumbai to Pune" className="h-11 bg-muted/20 border-border/60" required />
setChainageStartKm(e.target.value)} placeholder="0.0" className="h-11 bg-muted/20 border-border/60 text-sm font-mono" required />
setChainageEndKm(e.target.value)} placeholder="1.0" className="h-11 bg-muted/20 border-border/60 text-sm font-mono" required />
{/* Direction Row */}
{/* GPS Coordinates Grid */}

START COORDINATES

setStartLat(e.target.value)} placeholder="0.0000" className="h-10 bg-background/70 border-border/40 text-xs font-mono" />
setStartLng(e.target.value)} placeholder="0.0000" className="h-10 bg-background/70 border-border/40 text-xs font-mono" />

END COORDINATES

setEndLat(e.target.value)} placeholder="0.0000" className="h-10 bg-background/70 border-border/40 text-xs font-mono" />
setEndLng(e.target.value)} placeholder="0.0000" className="h-10 bg-background/70 border-border/40 text-xs font-mono" />
{/* Submit Button */}
); }