import React, { useState, useEffect, useMemo } from "react"; import { View, Text, TouchableOpacity, StyleSheet, Platform, Modal, ScrollView, TextInput, ActivityIndicator, } from "react-native"; import { Ionicons } from "@expo/vector-icons"; import { useI18n } from "@/hooks/use-i18n"; import { useThemeContext } from "@/hooks/use-theme-context"; import { useGroup } from "@/state/use-group"; import { usePort } from "@/state/use-ports"; import { filterPortsByProvinceCode } from "@/utils/tripDataConverters"; interface PortSelectorProps { departurePortId: number; arrivalPortId: number; onDeparturePortChange: (portId: number) => void; onArrivalPortChange: (portId: number) => void; disabled?: boolean; } type PortType = "departure" | "arrival"; export default function PortSelector({ departurePortId, arrivalPortId, onDeparturePortChange, onArrivalPortChange, disabled = false, }: PortSelectorProps) { const { t } = useI18n(); const { colors } = useThemeContext(); // State from zustand stores const { groups, getUserGroups, loading: groupsLoading } = useGroup(); const { ports, getPorts, loading: portsLoading } = usePort(); // Local state - which dropdown is open const [activeDropdown, setActiveDropdown] = useState(null); const [searchText, setSearchText] = useState(""); // Fetch groups and ports if not available useEffect(() => { if (!groups) { getUserGroups(); } }, [groups, getUserGroups]); useEffect(() => { if (!ports) { getPorts(); } }, [ports, getPorts]); // Filter ports by province codes from groups const filteredPorts = useMemo(() => { return filterPortsByProvinceCode(ports, groups); }, [ports, groups]); // Filter by search text const searchFilteredPorts = useMemo(() => { if (!searchText) return filteredPorts; return filteredPorts.filter((port) => port.name?.toLowerCase().includes(searchText.toLowerCase()) ); }, [filteredPorts, searchText]); const isLoading = groupsLoading || portsLoading; const handleSelectPort = (portId: number) => { if (activeDropdown === "departure") { onDeparturePortChange(portId); } else { onArrivalPortChange(portId); } setActiveDropdown(null); setSearchText(""); }; const openDropdown = (type: PortType) => { setActiveDropdown(type); setSearchText(""); }; const closeDropdown = () => { setActiveDropdown(null); setSearchText(""); }; // Get port name by ID const getPortDisplayName = (portId: number): string => { const port = filteredPorts.find((p) => p.id === portId); return port?.name || t("diary.selectPort"); }; const currentSelectedId = activeDropdown === "departure" ? departurePortId : arrivalPortId; const themedStyles = { label: { color: colors.text }, portSelector: { backgroundColor: colors.card, borderColor: colors.border, }, portText: { color: colors.text }, placeholder: { color: colors.textSecondary }, modalOverlay: { backgroundColor: "rgba(0, 0, 0, 0.5)" }, modalContent: { backgroundColor: colors.card }, searchInput: { backgroundColor: colors.backgroundSecondary, color: colors.text, borderColor: colors.border, }, option: { borderBottomColor: colors.separator }, optionText: { color: colors.text }, }; return ( {t("diary.portLabel")} {/* Departure Port */} {t("diary.departurePort")} openDropdown("departure")} activeOpacity={disabled ? 1 : 0.7} disabled={disabled} > {isLoading ? ( ) : ( <> {getPortDisplayName(departurePortId)} {!disabled && ( )} )} {/* Arrival Port */} {t("diary.arrivalPort")} openDropdown("arrival")} activeOpacity={disabled ? 1 : 0.7} disabled={disabled} > {isLoading ? ( ) : ( <> {getPortDisplayName(arrivalPortId)} {!disabled && ( )} )} {/* Port Dropdown Modal - Fade style like MaterialCostList */} {/* Search Input */} {/* Port List */} {isLoading ? ( ) : searchFilteredPorts.length === 0 ? ( {t("diary.noPortsFound")} ) : ( searchFilteredPorts.map((port) => ( handleSelectPort(port.id || 0)} > {port.name} {currentSelectedId === port.id && ( )} )) )} ); } const styles = StyleSheet.create({ container: { marginBottom: 20, }, label: { fontSize: 16, fontWeight: "600", marginBottom: 12, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, subLabel: { fontSize: 14, marginBottom: 6, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, portContainer: { flexDirection: "column", gap: 12, }, portSection: { flex: 1, }, dropdown: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", borderWidth: 1, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 10, minHeight: 44, }, dropdownText: { fontSize: 15, flex: 1, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, // Modal styles - same as MaterialCostList modalOverlay: { flex: 1, justifyContent: "center", alignItems: "center", }, modalContent: { borderRadius: 12, width: "85%", maxHeight: "60%", overflow: "hidden", shadowColor: "#000", shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 8, elevation: 8, }, searchContainer: { flexDirection: "row", alignItems: "center", paddingHorizontal: 12, paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: "#E5E5E7", }, searchIcon: { marginRight: 8, }, searchInput: { flex: 1, fontSize: 15, paddingVertical: 6, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, optionsList: { maxHeight: 300, }, option: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingHorizontal: 20, paddingVertical: 16, borderBottomWidth: 1, }, optionText: { fontSize: 16, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, loadingContainer: { padding: 20, alignItems: "center", }, emptyContainer: { padding: 20, alignItems: "center", }, emptyText: { fontSize: 14, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, });