diff --git a/app/(tabs)/tripInfo.tsx b/app/(tabs)/tripInfo.tsx index f29d659..294e214 100644 --- a/app/(tabs)/tripInfo.tsx +++ b/app/(tabs)/tripInfo.tsx @@ -1,22 +1,31 @@ import { ThemedText } from "@/components/themed-text"; +import CrewListTable from "@/components/tripInfo/CrewListTable"; +import FishingToolsTable from "@/components/tripInfo/FishingToolsList"; import TripCostTable from "@/components/tripInfo/TripCostTable"; -import { StyleSheet, View } from "react-native"; +import { ScrollView, StyleSheet, View } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; export default function TripInfoScreen() { return ( - - - Thông Tin Chuyến Đi - - + + + + Thông Tin Chuyến Đi + + + + + ); } const styles = StyleSheet.create({ + scrollContent: { + flexGrow: 1, + }, container: { alignItems: "center", - padding: 20, + padding: 15, }, }); diff --git a/components/tripInfo/CrewListTable.tsx b/components/tripInfo/CrewListTable.tsx new file mode 100644 index 0000000..3577af0 --- /dev/null +++ b/components/tripInfo/CrewListTable.tsx @@ -0,0 +1,154 @@ +import { IconSymbol } from "@/components/ui/icon-symbol"; +import React, { useRef, useState } from "react"; +import { + Animated, + Platform, + Text, + TouchableOpacity, + UIManager, + View, +} from "react-native"; +import styles from "./style/CrewListTable.styles"; + +// --------------------------- +// 🧩 Interface +// --------------------------- +interface CrewMember { + id: string; + maDinhDanh: string; + ten: string; + chucVu: string; +} + +// --------------------------- +// ⚓ Dữ liệu mẫu +// --------------------------- +const data: CrewMember[] = [ + { + id: "1", + maDinhDanh: "TV001", + ten: "Nguyễn Văn A", + chucVu: "Thuyền trưởng", + }, + { id: "2", maDinhDanh: "TV002", ten: "Trần Văn B", chucVu: "Máy trưởng" }, + { id: "3", maDinhDanh: "TV003", ten: "Lê Văn C", chucVu: "Thủy thủ" }, +]; + +const CrewListTable: React.FC = () => { + if ( + Platform.OS === "android" && + UIManager.setLayoutAnimationEnabledExperimental + ) { + UIManager.setLayoutAnimationEnabledExperimental(true); + } + + const [collapsed, setCollapsed] = useState(true); + const [contentHeight, setContentHeight] = useState(0); + const animatedHeight = useRef(new Animated.Value(0)).current; + const tongThanhVien = data.length; + + const handleToggle = () => { + const toValue = collapsed ? contentHeight : 0; + Animated.timing(animatedHeight, { + toValue, + duration: 300, + useNativeDriver: false, + }).start(); + setCollapsed((prev) => !prev); + }; + + return ( + + {/* Header toggle */} + + Danh sách thuyền viên + {collapsed && ( + {tongThanhVien} + )} + + + + {/* Nội dung ẩn để đo chiều cao */} + { + const height = event.nativeEvent.layout.height; + if (height > 0 && contentHeight === 0) { + setContentHeight(height); + } + }} + > + {/* Header */} + + + Mã định danh + + Tên + + Chức vụ + + + + {/* Body */} + {data.map((item) => ( + + {item.maDinhDanh} + {item.ten} + {item.chucVu} + + ))} + + {/* Footer */} + + + Tổng cộng + + {tongThanhVien} + + + + + {/* Bảng hiển thị với animation */} + + {/* Header */} + + + Mã định danh + + Tên + + Chức vụ + + + + {/* Body */} + {data.map((item) => ( + + {item.maDinhDanh} + {item.ten} + {item.chucVu} + + ))} + + {/* Footer */} + + + Tổng cộng + + {tongThanhVien} + + + + + ); +}; + +export default CrewListTable; diff --git a/components/tripInfo/FishingToolsList.tsx b/components/tripInfo/FishingToolsList.tsx new file mode 100644 index 0000000..6abbe06 --- /dev/null +++ b/components/tripInfo/FishingToolsList.tsx @@ -0,0 +1,141 @@ +import { IconSymbol } from "@/components/ui/icon-symbol"; // biểu tượng giống dự án bạn +import React, { useRef, useState } from "react"; +import { + Animated, + Platform, + Text, + TouchableOpacity, + UIManager, + View, +} from "react-native"; +import styles from "./style/FishingToolsTable.styles"; + +// --------------------------- +// 🧩 Interface +// --------------------------- +interface ToolItem { + id: string; + ten: string; + soLuong: number; +} + +// --------------------------- +// 🎣 Dữ liệu mẫu +// --------------------------- +const data: ToolItem[] = [ + { id: "1", ten: "Lưới kéo", soLuong: 1 }, + { id: "2", ten: "Cần câu", soLuong: 1 }, + { id: "3", ten: "Mồi câu", soLuong: 5 }, +]; + +const FishingToolsTable: React.FC = () => { + // Bật animation trên Android + if ( + Platform.OS === "android" && + UIManager.setLayoutAnimationEnabledExperimental + ) { + UIManager.setLayoutAnimationEnabledExperimental(true); + } + + const [collapsed, setCollapsed] = useState(true); + const [contentHeight, setContentHeight] = useState(0); + const animatedHeight = useRef(new Animated.Value(0)).current; + const tongSoLuong = data.reduce((sum, item) => sum + item.soLuong, 0); + + const handleToggle = () => { + const toValue = collapsed ? contentHeight : 0; + Animated.timing(animatedHeight, { + toValue, + duration: 300, + useNativeDriver: false, + }).start(); + setCollapsed((prev) => !prev); + }; + + return ( + + {/* Header / Toggle */} + + Danh sách ngư cụ + {collapsed && {tongSoLuong}} + + + + {/* Nội dung ẩn để đo chiều cao */} + { + const height = event.nativeEvent.layout.height; + if (height > 0 && contentHeight === 0) { + setContentHeight(height); + } + }} + > + {/* Table Header */} + + Tên + + Số lượng + + + + {/* Body */} + {data.map((item) => ( + + {item.ten} + {item.soLuong} + + ))} + + {/* Footer */} + + + Tổng cộng + + + {tongSoLuong} + + + + + {/* Nội dung mở/đóng */} + + {/* Table Header */} + + Tên + + Số lượng + + + + {/* Body */} + {data.map((item) => ( + + {item.ten} + {item.soLuong} + + ))} + + {/* Footer */} + + + Tổng cộng + + + {tongSoLuong} + + + + + ); +}; + +export default FishingToolsTable; diff --git a/components/tripInfo/TripCostTable.tsx b/components/tripInfo/TripCostTable.tsx index 043d289..b3e4ca8 100644 --- a/components/tripInfo/TripCostTable.tsx +++ b/components/tripInfo/TripCostTable.tsx @@ -2,8 +2,6 @@ import { IconSymbol } from "@/components/ui/icon-symbol"; import React, { useRef, useState } from "react"; import { Animated, - FlatList, - ListRenderItem, Platform, Text, TouchableOpacity, @@ -76,33 +74,17 @@ const TripCostTable: React.FC = () => { } const [collapsed, setCollapsed] = useState(true); + const [contentHeight, setContentHeight] = useState(0); const animatedHeight = useRef(new Animated.Value(0)).current; const tongCong = data.reduce((sum, item) => sum + item.tongChiPhi, 0); - const renderItem: ListRenderItem = ({ item }) => ( - - {item.loai} - - {item.tongChiPhi.toLocaleString()} - - - ); - - const contentHeight = 220; // Chiều cao cố định cho bảng, có thể điều chỉnh const handleToggle = () => { - if (collapsed) { - Animated.timing(animatedHeight, { - toValue: contentHeight, - duration: 350, - useNativeDriver: false, - }).start(); - } else { - Animated.timing(animatedHeight, { - toValue: 0, - duration: 350, - useNativeDriver: false, - }).start(); - } + const toValue = collapsed ? contentHeight : 0; + Animated.timing(animatedHeight, { + toValue, + duration: 300, + useNativeDriver: false, + }).start(); setCollapsed((prev) => !prev); }; @@ -115,7 +97,7 @@ const TripCostTable: React.FC = () => { flexDirection: "row", justifyContent: "space-between", alignItems: "center", - marginBottom: collapsed ? 0 : 12, + // marginBottom: 12, }} > Chi phí chuyến đi @@ -136,6 +118,45 @@ const TripCostTable: React.FC = () => { /> + {/* Nội dung ẩn để đo chiều cao */} + { + const height = event.nativeEvent.layout.height; + if (height > 0 && contentHeight === 0) { + setContentHeight(height); + } + }} + > + {/* Header */} + + + Loại + + Tổng chi phí + + + {/* Body */} + {data.map((item) => ( + + {item.loai} + + {item.tongChiPhi.toLocaleString()} + + + ))} + + {/* Footer */} + + + Tổng cộng + + + {tongCong.toLocaleString()} + + + + {/* Header */} @@ -146,11 +167,14 @@ const TripCostTable: React.FC = () => { {/* Body */} - item.id} - /> + {data.map((item) => ( + + {item.loai} + + {item.tongChiPhi.toLocaleString()} + + + ))} {/* Footer */} diff --git a/components/tripInfo/style/CrewListTable.styles.ts b/components/tripInfo/style/CrewListTable.styles.ts new file mode 100644 index 0000000..907334a --- /dev/null +++ b/components/tripInfo/style/CrewListTable.styles.ts @@ -0,0 +1,67 @@ +import { StyleSheet } from "react-native"; + +export default StyleSheet.create({ + container: { + width: "100%", + backgroundColor: "#fff", + borderRadius: 12, + padding: 16, + marginVertical: 10, + borderWidth: 1, + borderColor: "#fff", + shadowColor: "#000", + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 2, + }, + headerRow: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + }, + title: { + fontSize: 18, + fontWeight: "600", + }, + totalCollapsed: { + color: "#ff6600", + fontSize: 18, + fontWeight: "700", + textAlign: "center", + }, + row: { + flexDirection: "row", + justifyContent: "space-between", + paddingVertical: 8, + borderBottomWidth: 0.5, + borderBottomColor: "#eee", + }, + tableHeader: { + backgroundColor: "#fafafa", + borderRadius: 6, + marginTop: 10, + }, + cell: { + flex: 1, + fontSize: 15, + color: "#111", + textAlign: "center", + }, + left: { + textAlign: "center", + }, + right: { + textAlign: "center", + }, + headerText: { + fontWeight: "600", + }, + footerText: { + color: "#007bff", + fontWeight: "600", + }, + footerTotal: { + color: "#ff6600", + fontWeight: "800", + }, +}); diff --git a/components/tripInfo/style/FishingToolsTable.styles.ts b/components/tripInfo/style/FishingToolsTable.styles.ts new file mode 100644 index 0000000..e720ae5 --- /dev/null +++ b/components/tripInfo/style/FishingToolsTable.styles.ts @@ -0,0 +1,66 @@ +import { StyleSheet } from "react-native"; + +export default StyleSheet.create({ + container: { + width: "100%", + backgroundColor: "#fff", + borderRadius: 12, + padding: 16, + marginVertical: 10, + borderWidth: 1, + borderColor: "#fff", + shadowColor: "#000", + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 2, + }, + headerRow: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + }, + title: { + fontSize: 18, + fontWeight: "700", + }, + totalCollapsed: { + color: "#ff6600", + fontSize: 18, + fontWeight: "700", + textAlign: "center", + }, + row: { + flexDirection: "row", + paddingVertical: 8, + borderBottomWidth: 0.5, + borderBottomColor: "#eee", + paddingLeft: 15, + }, + cell: { + flex: 1, + fontSize: 15, + color: "#111", + }, + left: { + textAlign: "left", + }, + right: { + textAlign: "center", + }, + tableHeader: { + backgroundColor: "#fafafa", + borderRadius: 6, + marginTop: 10, + }, + headerText: { + fontWeight: "600", + }, + footerText: { + color: "#007bff", + fontWeight: "600", + }, + footerTotal: { + color: "#ff6600", + fontWeight: "800", + }, +}); diff --git a/components/tripInfo/style/TripCostTable.styles.ts b/components/tripInfo/style/TripCostTable.styles.ts index 269d3a4..4cdd26d 100644 --- a/components/tripInfo/style/TripCostTable.styles.ts +++ b/components/tripInfo/style/TripCostTable.styles.ts @@ -12,14 +12,10 @@ const styles = StyleSheet.create({ shadowRadius: 4, elevation: 2, }, - icon: { - fontSize: 10, - }, title: { fontSize: 18, fontWeight: "700", textAlign: "center", - // marginBottom: 12, }, row: { flexDirection: "row", @@ -31,23 +27,24 @@ const styles = StyleSheet.create({ cell: { flex: 1, textAlign: "center", - fontSize: 14, + fontSize: 15, }, left: { textAlign: "left", }, + right: { + color: "#ff6600", + fontWeight: "600", + }, header: { backgroundColor: "#f8f8f8", borderTopWidth: 1, borderBottomWidth: 1, + marginTop: 10, }, headerText: { fontWeight: "600", }, - highlight: { - color: "#ff6600", - fontWeight: "600", - }, footer: { marginTop: 6, },