'use client'; import { useState, useEffect, useRef } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Loader2, Check, ArrowUp, ArrowDown } from 'lucide-react'; import { projectService, packageService, chainageService, } from '@/services/api'; import { Project, Package as PackageType, Chainage, SessionContext, } from '@/types'; import { cn } from '@/lib/utils'; type ProjectSelectionSectionProps = { onSelectionComplete?: (session: SessionContext) => void; onSelectionChange?: (session: SessionContext | null) => void; asStep?: boolean; hideButton?: boolean; }; let globalProjectsCache: Project[] | null = null; let projectsPromise: Promise | null = null; export function ProjectSelectionSection({ onSelectionComplete, onSelectionChange, asStep = false, hideButton = false, }: ProjectSelectionSectionProps) { // Data states const [projects, setProjects] = useState( globalProjectsCache || [], ); const [packages, setPackages] = useState([]); const [chainages, setChainages] = useState([]); // Selection states const [selectedProject, setSelectedProject] = useState(null); const [selectedPackage, setSelectedPackage] = useState( null, ); const [selectedChainage, setSelectedChainage] = useState( null, ); // Loading states const [loadingProjects, setLoadingProjects] = useState(!globalProjectsCache); const [loadingPackages, setLoadingPackages] = useState(false); const [loadingChainages, setLoadingChainages] = useState(false); // Error state const [error, setError] = useState(null); // Load projects on mount useEffect(() => { let isMounted = true; const loadProjects = async () => { // Use cache if we already have it if (globalProjectsCache) { if (isMounted) { setProjects(globalProjectsCache); setLoadingProjects(false); } return; } // If a fetch is already in flight, reuse that promise if (!projectsPromise) { projectsPromise = projectService .getProjects() .then((data) => { globalProjectsCache = data.items; return data.items; }) .catch((err) => { projectsPromise = null; // Reset on error to allow retry throw err; }); } try { setLoadingProjects(true); const data = await projectsPromise; if (isMounted) { setProjects(data); } } catch (err) { if (isMounted) { setError('Failed to load projects. Please check connection.'); } } finally { if (isMounted) { setLoadingProjects(false); } } }; loadProjects(); return () => { isMounted = false; }; }, []); // Load packages when project changes useEffect(() => { if (!selectedProject) { setPackages([]); setSelectedPackage(null); return; } let isMounted = true; const loadPackages = async () => { try { setLoadingPackages(true); setError(null); setSelectedPackage(null); setSelectedChainage(null); setChainages([]); const data = await packageService.getPackagesByProject( selectedProject.id, ); if (isMounted) { setPackages(data.items); } } catch (err) { if (isMounted) { console.error('Failed to load packages:', err); setError('Failed to load packages for the selected project.'); } } finally { if (isMounted) { setLoadingPackages(false); } } }; loadPackages(); return () => { isMounted = false; }; }, [selectedProject]); // Load chainages when package changes useEffect(() => { if (!selectedPackage) { setChainages([]); setSelectedChainage(null); return; } let isMounted = true; const loadChainages = async () => { try { setLoadingChainages(true); setError(null); setSelectedChainage(null); const data = await chainageService.getChainagesByPackage( selectedPackage.id, ); if (isMounted) { setChainages(data.items); } } catch (err) { if (isMounted) { console.error('Failed to load chainages:', err); setError('Failed to load segments for the selected package.'); } } finally { if (isMounted) { setLoadingChainages(false); } } }; loadChainages(); return () => { isMounted = false; }; }, [selectedPackage]); const handleProjectChange = (projectId: string) => { const project = projects.find((p) => p.id === projectId) || null; setSelectedProject(project); }; const handlePackageChange = (packageId: string) => { const pkg = packages.find((p) => p.id === packageId) || null; setSelectedPackage(pkg); }; const handleChainageChange = (chainageId: string) => { const chainage = chainages.find((chn) => chn.id === chainageId) || null; setSelectedChainage(chainage); }; // Cache the last reported state to prevent infinite loops const lastReportedIdRef = useRef(null); // Handle change reporting useEffect(() => { if (onSelectionChange) { const currentIds = selectedProject && selectedPackage && selectedChainage ? `${selectedProject.id}-${selectedPackage.id}-${selectedChainage.id}` : null; if (currentIds !== lastReportedIdRef.current) { lastReportedIdRef.current = currentIds; if (selectedProject && selectedPackage && selectedChainage) { onSelectionChange({ projectId: selectedProject.id, projectName: selectedProject.name, packageId: selectedPackage.id, packageName: selectedPackage.name, chainageId: selectedChainage.id, chainageName: selectedChainage.segment_name, chainageDirection: selectedChainage.direction, }); } else { onSelectionChange(null); } } } }, [selectedProject, selectedPackage, selectedChainage, onSelectionChange]); const handleProceed = () => { if ( selectedProject && selectedPackage && selectedChainage && onSelectionComplete ) { onSelectionComplete({ projectId: selectedProject.id, projectName: selectedProject.name, packageId: selectedPackage.id, packageName: selectedPackage.name, chainageId: selectedChainage.id, chainageName: selectedChainage.segment_name, chainageDirection: selectedChainage.direction, }); } }; const isComplete = selectedProject && selectedPackage && selectedChainage; // Step status helpers const getStepStatus = (step: number) => { if (step === 1) return selectedProject ? 'completed' : 'active'; if (step === 2) return selectedPackage ? 'completed' : selectedProject ? 'active' : 'pending'; if (step === 3) return selectedChainage ? 'completed' : selectedPackage ? 'active' : 'pending'; return 'pending'; }; const content = ( <> {/* Error Display */} {error && (
{error}
)}
{/* Project Dropdown */}
{/* Package Dropdown */}
{/* Chainage Dropdown */}
{/* Selected Summary - Only if not asStep or if specifically desired */} {!asStep && isComplete && (
Selection: {selectedProject?.name} / {selectedPackage?.name} / {selectedChainage?.segment_name} {selectedChainage?.direction === 'UP' ? ( ) : ( )} {selectedChainage?.direction}
)} {/* Proceed Button */} {!hideButton && ( )} ); if (asStep) { return content; } return (
Select Project Segment Select Project, Package & Segment to begin intelligent road analysis.
{/* Step Progress Indicator */}
{[1, 2, 3].map((step, index) => { const status = getStepStatus(step); const labels = ['Project', 'Package', 'Segment']; return (
{status === 'completed' ? ( ) : ( step )}
{labels[index]}
{index < 2 && (
)}
); })}
{content} ); }