import { useCallback, useEffect, useRef, useState } from "react"; import { ActivityIndicator, FlatList, Platform, StyleSheet, Text, TouchableOpacity, View, } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { Ionicons } from "@expo/vector-icons"; import FilterButton from "@/components/diary/FilterButton"; import TripCard from "@/components/diary/TripCard"; import FilterModal, { FilterValues } from "@/components/diary/FilterModal"; import AddTripModal from "@/components/diary/addTripModal"; import { useThings } from "@/state/use-thing"; import { useTripsList } from "@/state/use-tripslist"; import dayjs from "dayjs"; import { useI18n } from "@/hooks/use-i18n"; import { useThemeContext } from "@/hooks/use-theme-context"; export default function diary() { const { t } = useI18n(); const { colors } = useThemeContext(); const [showFilterModal, setShowFilterModal] = useState(false); const [showAddTripModal, setShowAddTripModal] = useState(false); const [filters, setFilters] = useState({ status: null, startDate: null, endDate: null, selectedShip: null, // Tàu được chọn }); // State for pagination const [allTrips, setAllTrips] = useState([]); const [isLoadingMore, setIsLoadingMore] = useState(false); const [hasMore, setHasMore] = useState(true); const isInitialLoad = useRef(true); // Body call API things (đang fix cứng) const payloadThings: Model.SearchThingBody = { offset: 0, limit: 200, order: "name", dir: "asc", metadata: { not_empty: "ship_name, ship_reg_number", }, }; // Gọi API things const { getThings } = useThings(); useEffect(() => { getThings(payloadThings); }, []); // State cho payload trips const [payloadTrips, setPayloadTrips] = useState({ name: "", order: "", dir: "desc", offset: 0, limit: 10, metadata: { from: "", to: "", ship_name: "", reg_number: "", province_code: "", owner_id: "", ship_id: "", status: "", }, }); const { tripsList, getTripsList, loading } = useTripsList(); // console.log("Payload trips:", payloadTrips); // Gọi API trips lần đầu useEffect(() => { isInitialLoad.current = true; setAllTrips([]); setHasMore(true); getTripsList(payloadTrips); }, []); // Xử lý khi nhận được dữ liệu mới từ API useEffect(() => { if (tripsList?.trips && tripsList.trips.length > 0) { if (isInitialLoad.current || payloadTrips.offset === 0) { // Load lần đầu hoặc reset do filter setAllTrips(tripsList.trips); isInitialLoad.current = false; } else { // Load more - append data setAllTrips((prevTrips) => { // Tránh duplicate bằng cách check ID const existingIds = new Set(prevTrips.map((trip) => trip.id)); const newTrips = tripsList.trips!.filter( (trip) => !existingIds.has(trip.id) ); return [...prevTrips, ...newTrips]; }); } // Check if có thêm data không const totalFetched = payloadTrips.offset + tripsList.trips.length; setHasMore(totalFetched < (tripsList.total || 0)); setIsLoadingMore(false); } else if (tripsList && payloadTrips.offset === 0) { // Trường hợp API trả về rỗng khi filter setAllTrips([]); setHasMore(false); setIsLoadingMore(false); } }, [tripsList]); const handleFilter = () => { setShowFilterModal(true); }; const handleApplyFilters = (newFilters: FilterValues) => { setFilters(newFilters); // Cập nhật payload với filter mới và reset offset // Lưu ý: status gửi lên server là string const updatedPayload: Model.TripListBody = { ...payloadTrips, offset: 0, // Reset về đầu khi filter metadata: { ...payloadTrips.metadata, from: newFilters.startDate ? dayjs(newFilters.startDate).startOf("day").toISOString() : "", to: newFilters.endDate ? dayjs(newFilters.endDate).endOf("day").toISOString() : "", // Convert number status sang string để gửi lên server status: newFilters.status !== null ? String(newFilters.status) : "", // Thêm ship_id từ tàu đã chọn ship_name: newFilters.selectedShip?.shipName || "", }, }; // Reset trips khi apply filter mới setAllTrips([]); setHasMore(true); setPayloadTrips(updatedPayload); getTripsList(updatedPayload); setShowFilterModal(false); }; // Hàm load more data khi scroll đến cuối const handleLoadMore = useCallback(() => { if (isLoadingMore || !hasMore) { return; } setIsLoadingMore(true); const newOffset = payloadTrips.offset + payloadTrips.limit; const updatedPayload = { ...payloadTrips, offset: newOffset, }; setPayloadTrips(updatedPayload); getTripsList(updatedPayload); }, [isLoadingMore, hasMore, payloadTrips]); const handleTripPress = (tripId: string) => { // TODO: Navigate to trip detail console.log("Trip pressed:", tripId); }; const handleViewTrip = (tripId: string) => { console.log("View trip:", tripId); // TODO: Navigate to trip detail view }; const handleEditTrip = (tripId: string) => { console.log("Edit trip:", tripId); // TODO: Navigate to trip edit screen }; const handleViewTeam = (tripId: string) => { console.log("View team:", tripId); // TODO: Navigate to team management }; const handleSendTrip = (tripId: string) => { console.log("Send trip:", tripId); // TODO: Send trip for approval }; const handleDeleteTrip = (tripId: string) => { console.log("Delete trip:", tripId); // TODO: Show confirmation dialog and delete trip }; // Dynamic styles based on theme const themedStyles = { safeArea: { backgroundColor: colors.background, }, titleText: { color: colors.text, }, countText: { color: colors.textSecondary, }, addButton: { backgroundColor: colors.primary, }, emptyText: { color: colors.textSecondary, }, }; // Render mỗi item trong FlatList const renderTripItem = useCallback( ({ item }: { item: any }) => ( handleTripPress(item.id)} onView={() => handleViewTrip(item.id)} onEdit={() => handleEditTrip(item.id)} onTeam={() => handleViewTeam(item.id)} onSend={() => handleSendTrip(item.id)} onDelete={() => handleDeleteTrip(item.id)} /> ), [] ); // Key extractor cho FlatList const keyExtractor = useCallback((item: any) => item.id, []); // Loading indicator khi load more const renderFooter = () => { // Không hiển thị loading footer nếu không có dữ liệu hoặc không đang load more if (!isLoadingMore || allTrips.length === 0) return null; return ( {t("diary.loadingMore")} ); }; // Empty component const renderEmpty = () => { // Hiển thị loading khi đang load (lần đầu hoặc load more) và chưa có dữ liệu if (loading && allTrips.length === 0) { return ( ); } // Chỉ hiển thị "không có dữ liệu" khi đã load xong và thực sự không có trips return ( {t("diary.noTripsFound")} ); }; return ( {/* Header */} {t("diary.title")} {/* Filter & Add Button Row */} setShowAddTripModal(true)} activeOpacity={0.7} > {t("diary.addTrip")} {/* Trip Count */} {t("diary.tripListCount", { count: tripsList?.total || 0 })} {/* Trip List with FlatList */} {/* Filter Modal */} setShowFilterModal(false)} onApply={handleApplyFilters} /> {/* Add Trip Modal */} setShowAddTripModal(false)} /> ); } const styles = StyleSheet.create({ safeArea: { flex: 1, }, container: { flex: 1, padding: 10, }, titleText: { fontSize: 28, fontWeight: "700", lineHeight: 36, marginBottom: 10, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, actionRow: { flexDirection: "row", justifyContent: "flex-start", alignItems: "center", gap: 12, marginBottom: 12, }, headerRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginTop: 20, marginBottom: 12, }, countText: { fontSize: 16, fontWeight: "600", fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), marginBottom: 10, }, addButton: { flexDirection: "row", alignItems: "center", paddingHorizontal: 16, paddingVertical: 8, borderRadius: 8, gap: 6, }, addButtonText: { fontSize: 14, fontWeight: "600", color: "#FFFFFF", fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, scrollContent: { paddingBottom: 20, }, emptyState: { alignItems: "center", justifyContent: "center", paddingVertical: 60, }, emptyText: { fontSize: 16, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, loadingFooter: { paddingVertical: 20, alignItems: "center", justifyContent: "center", flexDirection: "row", gap: 10, }, loadingText: { fontSize: 14, fontFamily: Platform.select({ ios: "System", android: "Roboto", default: "System", }), }, });